pax_global_header00006660000000000000000000000064140112404350014504gustar00rootroot0000000000000052 comment=cc2593524a946e9d3cd73e476b2819ed76d2b133 BambooTracker-0.4.6/000077500000000000000000000000001401124043500142265ustar00rootroot00000000000000BambooTracker-0.4.6/.appveyor.yml000066400000000000000000000116771401124043500167100ustar00rootroot00000000000000#---------------------------------# # general configuration # #---------------------------------# # version format version: 0.4.5.{build} # branches to build branches: # whitelist only: - master - /v\d*\.\d*\.\d*/ # Skipping commits with particular message or from specific user skip_commits: message: /Created.*\.(png|jpg|jpeg|bmp|gif|md)/ # Regex for matching commit message files: - '*.md' - '*.txt' - 'LICENSE' - '.gitignore' #---------------------------------# # environment configuration # #---------------------------------# # set clone depth clone_depth: 3 # clone entire repository history if not defined environment: APPVEYOR_YML_DISABLE_PS_LINUX: true matrix: # Windows 7 or later - APPVEYOR_JOB_NAME: for Windows (7 or later) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 COMPILER: C:\Qt\Tools\mingw530_32\bin QT: C:\Qt\5.10.1\mingw53_32\bin PLATFORM: windows DEST: BambooTracker-v%APPVEYOR_BUILD_VERSION%-dev-windows.zip MAKE: mingw32-make RELEASE_BUILD: false # Windows XP (Debug) - APPVEYOR_JOB_NAME: for Windows XP APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 COMPILER: C:\Qt\Tools\mingw492_32\bin QT: C:\Qt\5.5\mingw492_32\bin PLATFORM: windows-xp DEST: BambooTracker-v%APPVEYOR_BUILD_VERSION%-dev-windows-xp.zip MAKE: mingw32-make RELEASE_BUILD: false # Windows XP (Release) - APPVEYOR_JOB_NAME: for Windows XP (Release) APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 COMPILER: C:\Qt\Tools\mingw492_32\bin QT: C:\Qt\5.5\mingw492_32\bin PLATFORM: windows-xp DEST: BambooTracker-v%APPVEYOR_BUILD_VERSION%-windows-xp.zip MAKE: mingw32-make RELEASE_BUILD: true - APPVEYOR_JOB_NAME: for macOS APPVEYOR_BUILD_WORKER_IMAGE: macOS-Mojave PLATFORM: macos DEST: BambooTracker-v$APPVEYOR_BUILD_VERSION-dev-macos.zip MAKE: make RELEASE_BUILD: false for: - # Debug matrix: only: - RELEASE_BUILD: false skip_tags: true - # Release matrix: only: - RELEASE_BUILD: true skip_non_tags: true # scripts that run after cloning repository install: - git submodule init - git submodule update #---------------------------------# # build configuration # #---------------------------------# # build platform, i.e. x86, x64, Any CPU. This setting is optional. platform: x86 # scripts to run before build before_build: - ps: $env:Path = "$env:QT;$env:COMPILER;$env:Path" - sh: | brew install qt pkg-config jack p7zip export PATH="/usr/local/opt/qt/bin:$PATH" export PKG_CONFIG_PATH="/usr/local/opt/jack/lib/pkgconfig"${PKG_CONFIG_PATH:+':'}$PKG_CONFIG_PATH # to run your custom scripts instead of automatic MSBuild build_script: - ps: | $QMAKE_CONFIGS = if ($env:RELEASE_BUILD -ieq "true") { "CONFIG+=release CONFIG-=debug" } else { "CONFIG-=release CONFIG+=debug CONFIG+=console CONFIG+=nostrip" } echo $QMAKE_CONFIGS Invoke-Expression ("qmake.exe Project.pro PREFIX=$pwd\target " + $QMAKE_CONFIGS) Invoke-Expression ($env:MAKE + " qmake_all") Invoke-Expression ($env:MAKE + " -j2") - sh: | qmake Project.pro PREFIX="$APPVEYOR_BUILD_FOLDER"/target CONFIG-=release CONFIG+=debug CONFIG+=console CONFIG+=nostrip CONFIG+=install_flat CONFIG+=use_jack $MAKE qmake_all $MAKE -j2 # scripts to run after build (working directory and environment changes are persisted from the previous steps) after_build: - ps: Invoke-Expression ($env:MAKE + " install") - sh: $MAKE install - cd target # Port of scripts/package_windows.sh to PowerShell - ps: | Copy-Item -Recurse ..\img,..\*.md . $HELP_OUT = (windeployqt.exe -h) $DEPLOY_OPTS = "-verbose=2" $PLUGIN_OPTS = @("--no-quick-import","--no-system-d3d-compiler","--no-webkit2","--no-opengl-sw","--no-virtualkeyboard","--no-angle") $PLUGIN_OPTS | ForEach{If ($HELP_OUT | Select-String $_) { $DEPLOY_OPTS = $DEPLOY_OPTS + " " + $_ }} $EXLIB_OPTS = @("svg") $EXLIB_OPTS | ForEach{If ($HELP_OUT | Select-String $_) { $DEPLOY_OPTS = $DEPLOY_OPTS + " --no-" + $_ }} Invoke-Expression ("windeployqt.exe BambooTracker.exe " + $DEPLOY_OPTS) Copy-Item -Recurse translations\*.qm lang Remove-Item -Recurse -Force imageformats,translations - sh: bash ../scripts/package_osx.sh - cd .. - ps: | $DEV_LAB = if ($env:RELEASE_BUILD -ieq "true") { "-" } else { "-dev-" } mv target BambooTracker-v"$env:APPVEYOR_BUILD_VERSION$DEV_LAB$env:PLATFORM" 7z a -tzip "$env:DEST" BambooTracker-v"$env:APPVEYOR_BUILD_VERSION$DEV_LAB$env:PLATFORM" - sh: | mv target BambooTracker-v"$APPVEYOR_BUILD_VERSION"-dev-"$PLATFORM" 7z a -tzip "$DEST" BambooTracker-v"$APPVEYOR_BUILD_VERSION"-dev-"$PLATFORM" #---------------------------------# # artifacts configuration # #---------------------------------# artifacts: # pushing a single file - path: $(DEST) BambooTracker-0.4.6/.gitattributes000066400000000000000000000047261401124043500171320ustar00rootroot00000000000000############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain BambooTracker-0.4.6/.github/000077500000000000000000000000001401124043500155665ustar00rootroot00000000000000BambooTracker-0.4.6/.github/ISSUE_TEMPLATE/000077500000000000000000000000001401124043500177515ustar00rootroot00000000000000BambooTracker-0.4.6/.github/ISSUE_TEMPLATE/bug-report.md000066400000000000000000000024741401124043500223700ustar00rootroot00000000000000--- name: Bug Report about: Report a bug in/around BambooTracker title: "[Windows|macOS|Linux|BSD] - Short bug summary" labels: bug assignees: '' --- > #### Checklist > > > > - [ ] I am reporting exactly 1 bug with this issue. > - [ ] This bug hasn't already been reported. > - [ ] This bug hasn't already been fixed in the latest development build. --- ## Bug Description ## How to reproduce 1. … 2. … 3. … ## System Information - **Operating System**: - **BambooTracker Version**: - **Build Type**: BambooTracker-0.4.6/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000000341401124043500217360ustar00rootroot00000000000000blank_issues_enabled: false BambooTracker-0.4.6/.github/ISSUE_TEMPLATE/feature-request.md000066400000000000000000000013601401124043500234140ustar00rootroot00000000000000--- name: Feature Request about: Suggest a feature / QOL improvement title: "[Feature] - Short feature summary" labels: enhancement assignees: '' --- > #### Checklist > > > > - [ ] I'm requesting exactly 1 feature with this issue. > - [ ] This feature hasn't already been requested. > - [ ] This feature hasn't already been implemented in the latest development build. --- ## Requested Feature BambooTracker-0.4.6/.github/workflows/000077500000000000000000000000001401124043500176235ustar00rootroot00000000000000BambooTracker-0.4.6/.github/workflows/macos.yml000066400000000000000000000064311401124043500214540ustar00rootroot00000000000000name: macOS on: push: branches: master tags: 'v*.*.*' paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' pull_request: branches: master paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' defaults: run: shell: bash env: BUILD_TARGET: macos SCRIPT_NAME: osx MAKE: make BT_INSTALLBASE: ${{ github.workspace }}/target/ QMAKE_EXTRA_ARGUMENTS: CONFIG+=install_flat CONFIG+=use_jack jobs: build: runs-on: macos-10.15 steps: - name: Identify build type. id: identify-build run: | case ${GITHUB_REF} in refs/tags/* ) TAG=${GITHUB_REF/refs\/tags\//} echo "Release ${TAG}" echo "::set-output name=build-tag::${TAG}" echo "::set-output name=release::true" ;; refs/heads/* ) BRANCH=${GITHUB_REF/refs\/heads\//} echo "Test ${BRANCH}" echo "::set-output name=build-tag::${BRANCH}" echo "::set-output name=release::false" ;; * ) echo "Test (unknown)" echo "::set-output name=build-tag::unknown" echo "::set-output name=release::false" ;; esac - name: Checking out repository. uses: actions/checkout@v2 with: submodules: recursive ## macOS-specific steps - name: Pin Xcode version run: sudo xcode-select -s "/Applications/Xcode_12.1.1.app" - name: Installing dependencies. run: | brew update # JACK implies brew link python-3.9, fails due to shipped python binaries brew unlink python@3.9 brew link --overwrite python@3.9 brew upgrade --force python@3.9 brew install qt5 pkg-config jack brew link --force qt5 ## End macOS-specific steps - name: Configuring. run: | qmake Project.pro PREFIX=${BT_INSTALLBASE} \ CONFIG+=release CONFIG-=debug ${QMAKE_EXTRA_ARGUMENTS} ${MAKE} qmake_all - name: Building. run: | ${MAKE} -j2 - name: Installing. run: | ${MAKE} -j2 install - name: Test packaging. if: env.DONT_PACKAGE != 'true' run: | pushd ${BT_INSTALLBASE} bash ${GITHUB_WORKSPACE}/scripts/package_${SCRIPT_NAME:-${BUILD_TARGET%%-*}}.sh popd - name: Finalize packaging. id: packaging if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' run: | export package_name="BambooTracker-${{ steps.identify-build.outputs.build-tag }}-${BUILD_TARGET}" echo "::set-output name=package-name::${package_name}" mv -v ${BT_INSTALLBASE} ${package_name} 7z a -tzip ${package_name}{.zip,} - name: Upload release package. if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ${{ steps.packaging.outputs.package-name }}.zip asset_name: ${{ steps.packaging.outputs.package-name }}.zip tag: ${{ github.ref }} BambooTracker-0.4.6/.github/workflows/nixpkgs.yml000066400000000000000000000066401401124043500220370ustar00rootroot00000000000000name: Linux (Nixpkgs) on: push: branches: master tags: 'v*.*.*' paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' pull_request: branches: master paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' defaults: run: shell: bash env: BUILD_TARGET: linux NIXPKGS_CHANNEL: nixos-20.09 BT_INSTALLBASE: ${{ github.workspace }}/target/ jobs: build: runs-on: ubuntu-latest steps: - name: Identify build type. id: identify-build run: | case ${GITHUB_REF} in refs/tags/* ) TAG=${GITHUB_REF/refs\/tags\//} echo "Release ${TAG}" echo "::set-output name=build-tag::${TAG}" echo "::set-output name=release::true" echo "::set-output name=buildVersion::${TAG#v}" # nixpkgs ;; refs/heads/* ) BRANCH=${GITHUB_REF/refs\/heads\//} echo "Test ${BRANCH}" echo "::set-output name=build-tag::${BRANCH}" echo "::set-output name=release::false" echo "::set-output name=buildVersion::${BRANCH}-${GITHUB_SHA}" # nixpkgs ;; * ) echo "Test (unknown)" echo "::set-output name=build-tag::unknown" echo "::set-output name=release::false" echo "::set-output name=buildVersion::unknown-${GITHUB_SHA}" # nixpkgs ;; esac - name: Checking out repository. uses: actions/checkout@v2 with: submodules: recursive ## Nixpkgs-specific steps - name: Installing Nix. uses: cachix/install-nix-action@v12 with: nix_path: "nixpkgs=channel:${{ env.NIXPKGS_CHANNEL }}" - name: Show Nixpkgs version. run: nix-instantiate --eval -E '(import {}).lib.version' - name: Building. run: | nix-build scripts/build_nixpkgs_local.nix --argstr buildVersion "${{ steps.identify-build.outputs.buildVersion }}" -A build --no-out-link > ../outlink - name: Test packaging. if: env.DONT_PACKAGE != 'true' run: | nix-build scripts/build_nixpkgs_local.nix --argstr buildVersion "${{ steps.identify-build.outputs.buildVersion }}" -A bundle install -Dm755 $(realpath result) ${BT_INSTALLBASE}/bin/BambooTracker cp -r --no-preserve=all "$(cat ../outlink)/share" ${BT_INSTALLBASE}/share rm -rf ${BT_INSTALLBASE}/share/BambooTracker/lang # bundled into binary, presumably ## End Nixpkgs-specific steps - name: Finalize packaging. id: packaging if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' run: | export package_name="BambooTracker-${{ steps.identify-build.outputs.build-tag }}-${BUILD_TARGET}" echo "::set-output name=package-name::${package_name}" mv -v ${BT_INSTALLBASE} ${package_name} 7z a -tzip ${package_name}{.zip,} - name: Upload release package. if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ${{ steps.packaging.outputs.package-name }}.zip asset_name: ${{ steps.packaging.outputs.package-name }}.zip tag: ${{ github.ref }} BambooTracker-0.4.6/.github/workflows/ubuntu.yml000066400000000000000000000060541401124043500216750ustar00rootroot00000000000000name: Linux (Ubuntu 16.04) on: push: branches: master tags: 'v*.*.*' paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' pull_request: branches: master paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' defaults: run: shell: bash env: BUILD_TARGET: linux DONT_PACKAGE: true MAKE: make BT_INSTALLBASE: ${{ github.workspace }}/target/ QMAKE_EXTRA_ARGUMENTS: CONFIG+=use_pulse CONFIG+=use_jack jobs: build: runs-on: ubuntu-16.04 steps: - name: Identify build type. id: identify-build run: | case ${GITHUB_REF} in refs/tags/* ) TAG=${GITHUB_REF/refs\/tags\//} echo "Release ${TAG}" echo "::set-output name=build-tag::${TAG}" echo "::set-output name=release::true" ;; refs/heads/* ) BRANCH=${GITHUB_REF/refs\/heads\//} echo "Test ${BRANCH}" echo "::set-output name=build-tag::${BRANCH}" echo "::set-output name=release::false" ;; * ) echo "Test (unknown)" echo "::set-output name=build-tag::unknown" echo "::set-output name=release::false" ;; esac - name: Checking out repository. uses: actions/checkout@v2 with: submodules: recursive ## Ubuntu-specific steps - name: Installing dependencies. run: | sudo apt update sudo apt install qt5-default qttools5-dev-tools \ libasound2-dev libpulse-dev libjack-jackd2-dev ## End Ubuntu-specific steps - name: Configuring. run: | qmake Project.pro PREFIX=${BT_INSTALLBASE} \ CONFIG+=release CONFIG-=debug ${QMAKE_EXTRA_ARGUMENTS} ${MAKE} qmake_all - name: Building. run: | ${MAKE} -j2 - name: Installing. run: | ${MAKE} -j2 install - name: Test packaging. if: env.DONT_PACKAGE != 'true' run: | pushd ${BT_INSTALLBASE} bash ${GITHUB_WORKSPACE}/scripts/package_${SCRIPT_NAME:-${BUILD_TARGET%%-*}}.sh popd - name: Finalize packaging. id: packaging if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' run: | export package_name="BambooTracker-${{ steps.identify-build.outputs.build-tag }}-${BUILD_TARGET}" echo "::set-output name=package-name::${package_name}" mv -v ${BT_INSTALLBASE} ${package_name} 7z a -tzip ${package_name}{.zip,} - name: Upload release package. if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ${{ steps.packaging.outputs.package-name }}.zip asset_name: ${{ steps.packaging.outputs.package-name }}.zip tag: ${{ github.ref }} BambooTracker-0.4.6/.github/workflows/windows.yml000066400000000000000000000116701401124043500220450ustar00rootroot00000000000000name: Windows (7 and up) on: push: branches: master tags: 'v*.*.*' paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' pull_request: branches: master paths-ignore: - '**.md' - '**.txt' - 'LICENSE' - '.gitignore' defaults: run: shell: bash env: BUILD_TARGET: windows MAKE: mingw32-make BT_INSTALLBASE: ${{ github.workspace }}/target/ MINGW_VERSION: 8.1.0 MINGW_CHOCOBASE: /c/ProgramData/chocolatey/lib/mingw/tools/install/mingw32 MINGW_INSTALLBASE: ${{ github.workspace }}/mingw32/ QT_VERSION: 5.15.1 QT_TOOLCHAIN: win32_mingw81 QT_MODULES: qtbase qttools qttranslations QT_INSTALLBASE: ${{ github.workspace }}/Qt5/ jobs: build: runs-on: windows-latest steps: - name: Identify build type. id: identify-build run: | case ${GITHUB_REF} in refs/tags/* ) TAG=${GITHUB_REF/refs\/tags\//} echo "Release ${TAG}" echo "::set-output name=build-tag::${TAG}" echo "::set-output name=release::true" ;; refs/heads/* ) BRANCH=${GITHUB_REF/refs\/heads\//} echo "Test ${BRANCH}" echo "::set-output name=build-tag::${BRANCH}" echo "::set-output name=release::false" ;; * ) echo "Test (unknown)" echo "::set-output name=build-tag::unknown" echo "::set-output name=release::false" ;; esac - name: Checking out repository. uses: actions/checkout@v2 with: submodules: recursive ## Windows-specific steps # Unable to cache system-installed MinGW. Uploaded cache is supposedly an empty tarball # when trying to fetch it. # > Received 30 of 30 (100.0%), 0.0 MBs/sec # > Cache Size: ~0 MB (30 B) # We instead copy & reown the MinGW installation to the GitHub workspace, # ugly but it works. :/ - name: Fetching MinGW cache. id: mingw-cache uses: actions/cache@v2 with: path: ${{ env.MINGW_INSTALLBASE }} key: ${{ env.QT_TOOLCHAIN }} ${{ env.MINGW_VERSION }} - name: Installing MinGW. run: | ./scripts/fetch_mingw.sh ${MINGW_CHOCOBASE} ${MINGW_VERSION} ${MINGW_INSTALLBASE} # "Prepending to PATH" doesn't work properly and inserts the path *close* # to the start of PATH, but *after* the system's MinGW path. # Our MinGW PATH is not searched early enough to work for this build # so we either manually reexport PATH for ever step or fiddle with the PATH even more. :/ # - name: Adding MinGW to PATH. # run: | # echo ${MINGW_CHOCOBASE}/bin >> ${GITHUB_PATH} # Cache Qt5 installations, very costly & flakey to fetch - name: Fetching Qt5 cache. id: qt5-cache uses: actions/cache@v2 with: path: ${{ env.QT_INSTALLBASE }} key: ${{ runner.os }} Qt${{ env.QT_VERSION }} ${{ env.QT_TOOLCHAIN }} ${{ env.QT_MODULES }} - name: Installing Qt5. run: | ./scripts/fetch_qt.sh ${QT_INSTALLBASE} ${QT_VERSION} ${QT_TOOLCHAIN} ${QT_MODULES} - name: Adding Qt5 to PATH. run: | find ${QT_INSTALLBASE} -type d -name bin >> ${GITHUB_PATH} ## End Windows-specific steps - name: Configuring. run: | export PATH="${MINGW_INSTALLBASE}/bin:${PATH}" qmake Project.pro PREFIX=${BT_INSTALLBASE} \ CONFIG+=release CONFIG-=debug ${QMAKE_EXTRA_ARGUMENTS} ${MAKE} qmake_all - name: Building. run: | export PATH="${MINGW_INSTALLBASE}/bin:${PATH}" ${MAKE} -j2 - name: Installing. run: | export PATH="${MINGW_INSTALLBASE}/bin:${PATH}" ${MAKE} -j2 install - name: Test packaging. if: env.DONT_PACKAGE != 'true' run: | export PATH="${MINGW_INSTALLBASE}/bin:${PATH}" pushd ${BT_INSTALLBASE} bash ${GITHUB_WORKSPACE}/scripts/package_${SCRIPT_NAME:-${BUILD_TARGET%%-*}}.sh popd - name: Finalize packaging. id: packaging if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' run: | export package_name="BambooTracker-${{ steps.identify-build.outputs.build-tag }}-${BUILD_TARGET}" echo "::set-output name=package-name::${package_name}" mv -v ${BT_INSTALLBASE} ${package_name} 7z a -tzip ${package_name}{.zip,} - name: Upload release package. if: steps.identify-build.outputs.release == 'true' && env.DONT_PACKAGE != 'true' uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ${{ steps.packaging.outputs.package-name }}.zip asset_name: ${{ steps.packaging.outputs.package-name }}.zip tag: ${{ github.ref }} BambooTracker-0.4.6/.gitignore000066400000000000000000000056701401124043500162260ustar00rootroot00000000000000# Created by https://www.gitignore.io/api/qt,c++,linux,macos,emacs,windows,visualstudiocode # Edit at https://www.gitignore.io/?templates=qt,c++,linux,macos,emacs,windows,visualstudiocode ### C++ ### # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app ### Emacs ### # -*- mode: gitignore; -*- *~ \#*\# /.emacs.desktop /.emacs.desktop.lock *.elc auto-save-list tramp .\#* # Org-mode .org-id-locations *_archive # flymake-mode *_flymake.* # eshell files /eshell/history /eshell/lastdir # elpa packages /elpa/ # reftex files *.rel # AUCTeX auto folder /auto/ # cask packages .cask/ dist/ # Flycheck flycheck_*.el # server auth directory /server/ # projectiles files .projectile # directory configuration .dir-locals.el # network security /network-security.data ### Linux ### # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* # KDE directory preferences .directory # Linux trash folder which might appear on any partition or disk .Trash-* # .nfs files are created when an open file is removed but is still being accessed .nfs* ### macOS ### # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### Qt ### # C++ objects and libs # Qt-es object_script.*.Release object_script.*.Debug *_plugin_import.cpp .qmake.cache .qmake.stash *.pro.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp moc_*.h qrc_*.cpp ui_*.h *.qmlc *.jsc Makefile* *build-* .qm config.log *.prl # Qt unit tests target_wrapper.* # QtCreator *.autosave # QtCreator Qml *.qmlproject.user *.qmlproject.user.* # QtCreator CMake CMakeLists.txt.user* # QtCreator 4.8< compilation database compile_commands.json # QtCreator local machine specific files for imported projects *creator.user* ### VisualStudioCode ### .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json ### VisualStudioCode Patch ### # Ignore all local history of files .history ### Windows ### # Windows thumbnail cache files Thumbs.db Thumbs.db:encryptable ehthumbs.db ehthumbs_vista.db # Dump file *.stackdump # Folder config file [Dd]esktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msix *.msm *.msp # Windows shortcuts *.lnk # End of https://www.gitignore.io/api/qt,c++,linux,macos,emacs,windows,visualstudiocode ########## !/BambooTracker/res/icon BambooTracker/BambooTracker submodules/config.tests/*/test BambooTracker-0.4.6/.gitmodules000066400000000000000000000003271401124043500164050ustar00rootroot00000000000000[submodule "submodules/RtAudio/src"] path = submodules/RtAudio/src url = https://github.com/thestk/rtaudio [submodule "submodules/RtMidi/src"] path = submodules/RtMidi/src url = https://github.com/thestk/rtmidi BambooTracker-0.4.6/BambooTracker/000077500000000000000000000000001401124043500167415ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/BambooTracker.pro000066400000000000000000000447501401124043500222100ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2018-06-09T16:20:11 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = BambooTracker TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 # This produces the installation rule for the program and resources. # Use a default destination prefix if none is given. isEmpty(PREFIX) { win32:PREFIX = C:/BambooTracker else:PREFIX = /usr/local } INSTALLS += target win32|install_flat { target.path = $$PREFIX } else { target.path = $$PREFIX/bin } CONFIG += c++14 # C/C++ compiler flags message("Qt is version" $$QT_VERSION) msvc { message("Configured compiler is MSVC") message("Compiler is version" $$QT_MSC_FULL_VER) CPP_WARNING_FLAGS += /Wall /Wp64 /WX CPP_WARNING_FLAGS += /source-charset:utf-8 } else:clang|if(gcc:!intel_icc) { # Pedantic settings, warning -> error escalation and C standard specification CPP_WARNING_FLAGS += -Wall -Wextra -Werror -pedantic -pedantic-errors QMAKE_CFLAGS += -std=gnu11 # Get the compiler version for version-specific handling clang { defined(QMAKE_APPLE_CLANG_MAJOR_VERSION, var) { message("Configured compiler is Apple LLVM") CONFIG += clang-apple COMPILER_MAJOR_VERSION = $$QT_APPLE_CLANG_MAJOR_VERSION COMPILER_MINOR_VERSION = $$QT_APPLE_CLANG_MINOR_VERSION COMPILER_PATCH_VERSION = $$QT_APPLE_CLANG_PATCH_VERSION } else { message("Configured compiler is LLVM") CONFIG += clang-normal COMPILER_MAJOR_VERSION = $$QT_CLANG_MAJOR_VERSION COMPILER_MINOR_VERSION = $$QT_CLANG_MINOR_VERSION COMPILER_PATCH_VERSION = $$QT_CLANG_PATCH_VERSION } } else { message("Configured compiler is GCC") COMPILER_MAJOR_VERSION = $$QT_GCC_MAJOR_VERSION COMPILER_MINOR_VERSION = $$QT_GCC_MINOR_VERSION COMPILER_PATCH_VERSION = $$QT_GCC_PATCH_VERSION } COMPILER_VERSION = $${COMPILER_MAJOR_VERSION}.$${COMPILER_MINOR_VERSION}.$${COMPILER_PATCH_VERSION} message("Compiler is version" $$COMPILER_VERSION) # Temporary known-error downgrades here } else { message("Configured compiler is unknown, no attempt to add warning & pedantic compiler switches") } QMAKE_CFLAGS_WARN_ON += $$CPP_WARNING_FLAGS QMAKE_CXXFLAGS_WARN_ON += $$CPP_WARNING_FLAGS SOURCES += \ chip/c86ctl/c86ctl_wrapper.cpp \ chip/register_write_logger.cpp \ command/instrument/swap_instruments_command.cpp \ command/pattern/change_values_in_pattern_command.cpp \ command/pattern/paste_insert_copied_data_to_pattern_command.cpp \ command/pattern/pattern_command_utils.cpp \ command/pattern/transpose_note_in_pattern_command.cpp \ gui/bookmark_manager_form.cpp \ gui/color_palette_handler.cpp \ gui/command/instrument/instrument_command_qt_utils.cpp \ gui/command/instrument/swap_instruments_qt_command.cpp \ gui/command/order/order_list_common_qt_command.cpp \ gui/command/pattern/pattern_editor_common_qt_command.cpp \ gui/drop_detect_list_widget.cpp \ gui/effect_description.cpp \ gui/effect_list_dialog.cpp \ gui/file_io_error_message_box.cpp \ gui/go_to_dialog.cpp \ gui/gui_utils.cpp \ gui/hide_tracks_dialog.cpp \ gui/instrument_editor/adpcm_sample_editor.cpp \ gui/instrument_editor/arpeggio_macro_editor.cpp \ gui/instrument_editor/grid_settings_dialog.cpp \ gui/instrument_editor/instrument_editor_adpcm_form.cpp \ gui/instrument_editor/instrument_editor_drumkit_form.cpp \ gui/instrument_editor/instrument_editor_utils.cpp \ gui/instrument_editor/sample_length_dialog.cpp \ gui/instrument_editor/tone_noise_macro_editor.cpp \ gui/keyboard_shortcut_list_dialog.cpp \ gui/swap_tracks_dialog.cpp \ gui/track_visibility_memory_handler.cpp \ gui/transpose_song_dialog.cpp \ instrument/sample_adpcm.cpp \ instrument/sequence_property.cpp \ io/btb_io.cpp \ io/bti_io.cpp \ io/btm_io.cpp \ io/dat_io.cpp \ io/dmp_io.cpp \ io/export_io.cpp \ io/ff_io.cpp \ io/ins_io.cpp \ io/io_utils.cpp \ io/opni_io.cpp \ io/p86_io.cpp \ io/ppc_io.cpp \ io/pps_io.cpp \ io/pvi_io.cpp \ io/pzi_io.cpp \ io/tfi_io.cpp \ io/vgi_io.cpp \ io/wav_container.cpp \ io/wopn_io.cpp \ io/y12_io.cpp \ jamming.cpp \ main.cpp \ gui/mainwindow.cpp \ chip/chip.cpp \ chip/opna.cpp \ chip/resampler.cpp \ chip/mame/2608intf.c \ chip/mame/emu2149.c \ chip/mame/fm.c \ chip/mame/ymdeltat.c \ chip/nuked/nuke2608intf.c \ chip/nuked/ym3438.c \ bamboo_tracker.cpp \ module/effect.cpp \ note.cpp \ playback.cpp \ song_length_calculator.cpp \ audio/audio_stream.cpp \ instrument/instruments_manager.cpp \ command/command_manager.cpp \ command/instrument/add_instrument_command.cpp \ command/instrument/remove_instrument_command.cpp \ gui/command/instrument/add_instrument_qt_command.cpp \ gui/command/instrument/remove_instrument_qt_command.cpp \ gui/instrument_editor/instrument_editor_fm_form.cpp \ gui/instrument_editor/fm_operator_table.cpp \ gui/labeled_vertical_slider.cpp \ gui/labeled_horizontal_slider.cpp \ gui/slider_style.cpp \ gui/command/instrument/change_instrument_name_qt_command.cpp \ command/instrument/change_instrument_name_command.cpp \ opna_controller.cpp \ instrument/instrument.cpp \ instrument/envelope_fm.cpp \ gui/event_guard.cpp \ audio/audio_stream_rtaudio.cpp \ tick_counter.cpp \ module/module.cpp \ module/song.cpp \ module/pattern.cpp \ module/track.cpp \ module/step.cpp \ gui/order_list_editor/order_list_panel.cpp \ gui/order_list_editor/order_list_editor.cpp \ gui/pattern_editor/pattern_editor_panel.cpp \ gui/pattern_editor/pattern_editor.cpp \ gui/instrument_editor/instrument_editor_ssg_form.cpp \ command/pattern/set_key_off_to_step_command.cpp \ command/pattern/set_key_on_to_step_command.cpp \ command/pattern/set_instrument_to_step_command.cpp \ command/pattern/erase_instrument_in_step_command.cpp \ command/pattern/set_volume_to_step_command.cpp \ command/pattern/erase_volume_in_step_command.cpp \ command/pattern/set_effect_id_to_step_command.cpp \ command/pattern/erase_effect_in_step_command.cpp \ command/pattern/set_effect_value_to_step_command.cpp \ command/pattern/erase_effect_value_in_step_command.cpp \ command/pattern/insert_step_command.cpp \ command/pattern/delete_previous_step_command.cpp \ command/pattern/erase_step_command.cpp \ gui/command/instrument/deep_clone_instrument_qt_command.cpp \ command/instrument/deep_clone_instrument_command.cpp \ command/instrument/clone_instrument_command.cpp \ gui/command/instrument/clone_instrument_qt_command.cpp \ command/order/set_pattern_to_order_command.cpp \ command/order/insert_order_below_command.cpp \ command/order/delete_order_command.cpp \ command/pattern/paste_copied_data_to_pattern_command.cpp \ command/pattern/erase_cells_in_pattern_command.cpp \ command/order/paste_copied_data_to_order_command.cpp \ gui/instrument_editor/instrument_form_manager.cpp \ instrument/lfo_fm.cpp \ gui/instrument_editor/visualized_instrument_macro_editor.cpp \ instrument/effect_iterator.cpp \ command/pattern/paste_mix_copied_data_to_pattern_command.cpp \ gui/module_properties_dialog.cpp \ gui/groove_settings_dialog.cpp \ gui/configuration_dialog.cpp \ command/pattern/expand_pattern_command.cpp \ command/pattern/shrink_pattern_command.cpp \ instrument/abstract_instrument_property.cpp \ command/order/duplicate_order_command.cpp \ command/order/move_order_command.cpp \ command/order/clone_patterns_command.cpp \ command/order/clone_order_command.cpp \ command/pattern/set_echo_buffer_access_command.cpp \ gui/comment_edit_dialog.cpp \ io/binary_container.cpp \ command/pattern/interpolate_pattern_command.cpp \ command/pattern/reverse_pattern_command.cpp \ command/pattern/replace_instrument_in_pattern_command.cpp \ gui/vgm_export_settings_dialog.cpp \ gui/wave_export_settings_dialog.cpp \ configuration.cpp \ gui/configuration_handler.cpp \ gui/color_palette.cpp \ command/pattern/paste_overwrite_copied_data_to_pattern_command.cpp \ format/wopn_file.c \ instrument/bank.cpp \ gui/instrument_selection_dialog.cpp \ gui/s98_export_settings_dialog.cpp \ precise_timer.cpp \ io/module_io.cpp \ io/instrument_io.cpp \ io/bank_io.cpp \ gui/fm_envelope_set_edit_dialog.cpp \ gui/file_history_handler.cpp \ gui/file_history.cpp \ midi/midi.cpp \ gui/q_application_wrapper.cpp \ gui/wave_visual.cpp HEADERS += \ bamboo_tracker_defs.hpp \ chip/codec/ymb_codec.hpp \ chip/c86ctl/c86ctl.h \ chip/c86ctl/c86ctl_wrapper.hpp \ chip/register_write_logger.hpp \ chip/scci/SCCIDefines.hpp \ chip/scci/scci.hpp \ command/command_id.hpp \ command/instrument/swap_instruments_command.hpp \ command/pattern/change_values_in_pattern_command.hpp \ command/pattern/paste_insert_copied_data_to_pattern_command.hpp \ command/pattern/pattern_command_utils.hpp \ command/pattern/transpose_note_in_pattern_command.hpp \ echo_buffer.hpp \ enum_hash.hpp \ gui/bookmark_manager_form.hpp \ gui/color_palette_handler.hpp \ gui/command/instrument/instrument_command_qt_utils.hpp \ gui/command/instrument/instrument_commands_qt.hpp \ gui/command/instrument/swap_instruments_qt_command.hpp \ gui/command/order/order_commands_qt.hpp \ gui/command/order/order_list_common_qt_command.hpp \ gui/command/pattern/pattern_editor_common_qt_command.hpp \ gui/drop_detect_list_widget.hpp \ gui/effect_description.hpp \ gui/effect_list_dialog.hpp \ gui/file_io_error_message_box.hpp \ gui/go_to_dialog.hpp \ gui/gui_utils.hpp \ gui/hide_tracks_dialog.hpp \ gui/instrument_editor/adpcm_sample_editor.hpp \ gui/instrument_editor/arpeggio_macro_editor.hpp \ gui/instrument_editor/grid_settings_dialog.hpp \ gui/instrument_editor/instrument_editor_adpcm_form.hpp \ gui/instrument_editor/instrument_editor_drumkit_form.hpp \ gui/instrument_editor/instrument_editor_utils.hpp \ gui/instrument_editor/sample_length_dialog.hpp \ gui/instrument_editor/tone_noise_macro_editor.hpp \ gui/jam_layout.hpp \ gui/keyboard_shortcut_list_dialog.hpp \ gui/mainwindow.hpp \ chip/mame/2608intf.h \ chip/mame/emu2149.h \ chip/mame/emutypes.h \ chip/mame/fm.h \ chip/mame/mamedef.h \ chip/mame/ymdeltat.h \ chip/nuked/nuke2608intf.h \ chip/nuked/ym3438.h \ chip/chip.hpp \ chip/opna.hpp \ chip/resampler.hpp \ bamboo_tracker.hpp \ gui/swap_tracks_dialog.hpp \ gui/track_visibility_memory_handler.hpp \ gui/transpose_song_dialog.hpp \ instrument/instrument_property_defs.hpp \ instrument/sample_adpcm.hpp \ instrument/sequence_property.hpp \ io/btb_io.hpp \ io/bti_io.hpp \ io/btm_io.hpp \ io/dat_io.hpp \ io/dmp_io.hpp \ io/export_io.hpp \ io/ff_io.hpp \ io/ins_io.hpp \ io/io_file_type.hpp \ io/io_utils.hpp \ io/opni_io.hpp \ io/p86_io.hpp \ io/ppc_io.hpp \ io/pps_io.hpp \ io/pvi_io.hpp \ io/pzi_io.hpp \ io/tfi_io.hpp \ io/vgi_io.hpp \ io/wav_container.hpp \ io/wopn_io.hpp \ io/y12_io.hpp \ jamming.hpp \ module/effect.hpp \ note.hpp \ playback.hpp \ song_length_calculator.hpp \ audio/audio_stream.hpp \ chip/chip_def.h \ instrument/instruments_manager.hpp \ command/command_manager.hpp \ command/instrument/add_instrument_command.hpp \ command/instrument/remove_instrument_command.hpp \ command/commands.hpp \ gui/command/instrument/add_instrument_qt_command.hpp \ gui/command/instrument/remove_instrument_qt_command.hpp \ gui/instrument_editor/instrument_editor_fm_form.hpp \ gui/instrument_editor/fm_operator_table.hpp \ gui/labeled_vertical_slider.hpp \ gui/labeled_horizontal_slider.hpp \ gui/slider_style.hpp \ gui/command/instrument/change_instrument_name_qt_command.hpp \ command/instrument/change_instrument_name_command.hpp \ opna_controller.hpp \ instrument/instrument.hpp \ instrument/envelope_fm.hpp \ gui/event_guard.hpp \ audio/audio_stream_rtaudio.hpp \ tick_counter.hpp \ module/module.hpp \ module/song.hpp \ module/pattern.hpp \ module/track.hpp \ module/step.hpp \ gui/order_list_editor/order_list_panel.hpp \ gui/order_list_editor/order_list_editor.hpp \ gui/pattern_editor/pattern_editor_panel.hpp \ gui/pattern_editor/pattern_editor.hpp \ gui/instrument_editor/instrument_editor_ssg_form.hpp \ command/pattern/set_key_off_to_step_command.hpp \ gui/command/pattern/pattern_commands_qt.hpp \ command/pattern/set_key_on_to_step_command.hpp \ gui/pattern_editor/pattern_position.hpp \ command/pattern/set_instrument_to_step_command.hpp \ command/pattern/erase_instrument_in_step_command.hpp \ command/pattern/set_volume_to_step_command.hpp \ command/pattern/erase_volume_in_step_command.hpp \ command/pattern/set_effect_id_to_step_command.hpp \ command/pattern/erase_effect_in_step_command.hpp \ command/pattern/set_effect_value_to_step_command.hpp \ command/pattern/erase_effect_value_in_step_command.hpp \ command/pattern/insert_step_command.hpp \ command/pattern/delete_previous_step_command.hpp \ command/pattern/erase_step_command.hpp \ gui/command/instrument/deep_clone_instrument_qt_command.hpp \ command/instrument/deep_clone_instrument_command.hpp \ command/instrument/clone_instrument_command.hpp \ gui/command/instrument/clone_instrument_qt_command.hpp \ gui/order_list_editor/order_position.hpp \ command/order/set_pattern_to_order_command.hpp \ command/order/insert_order_below_command.hpp \ command/order/delete_order_command.hpp \ command/pattern/paste_copied_data_to_pattern_command.hpp \ command/pattern/erase_cells_in_pattern_command.hpp \ command/order/paste_copied_data_to_order_command.hpp \ gui/instrument_editor/instrument_form_manager.hpp \ instrument/lfo_fm.hpp \ gui/instrument_editor/visualized_instrument_macro_editor.hpp \ instrument/sequence_iterator_interface.hpp \ instrument/effect_iterator.hpp \ command/pattern/paste_mix_copied_data_to_pattern_command.hpp \ gui/module_properties_dialog.hpp \ gui/groove_settings_dialog.hpp \ gui/configuration_dialog.hpp \ command/pattern/expand_pattern_command.hpp \ command/pattern/shrink_pattern_command.hpp \ command/abstract_command.hpp \ instrument/abstract_instrument_property.hpp \ command/order/duplicate_order_command.hpp \ command/order/move_order_command.hpp \ command/order/clone_patterns_command.hpp \ command/order/clone_order_command.hpp \ command/pattern/set_echo_buffer_access_command.hpp \ gui/comment_edit_dialog.hpp \ io/binary_container.hpp \ utils.hpp \ version.hpp \ command/pattern/interpolate_pattern_command.hpp \ command/pattern/reverse_pattern_command.hpp \ command/pattern/replace_instrument_in_pattern_command.hpp \ gui/vgm_export_settings_dialog.hpp \ gui/wave_export_settings_dialog.hpp \ configuration.hpp \ gui/configuration_handler.hpp \ gui/color_palette.hpp \ command/pattern/paste_overwrite_copied_data_to_pattern_command.hpp \ io/file_io_error.hpp \ format/wopn_file.h \ instrument/bank.hpp \ gui/instrument_selection_dialog.hpp \ gui/s98_export_settings_dialog.hpp \ precise_timer.hpp \ io/module_io.hpp \ io/instrument_io.hpp \ io/bank_io.hpp \ gui/fm_envelope_set_edit_dialog.hpp \ gui/file_history_handler.hpp \ gui/file_history.hpp \ midi/midi.hpp \ gui/q_application_wrapper.hpp \ gui/wave_visual.hpp FORMS += \ gui/bookmark_manager_form.ui \ gui/effect_list_dialog.ui \ gui/go_to_dialog.ui \ gui/hide_tracks_dialog.ui \ gui/instrument_editor/adpcm_sample_editor.ui \ gui/instrument_editor/grid_settings_dialog.ui \ gui/instrument_editor/instrument_editor_adpcm_form.ui \ gui/instrument_editor/instrument_editor_drumkit_form.ui \ gui/instrument_editor/sample_length_dialog.ui \ gui/keyboard_shortcut_list_dialog.ui \ gui/mainwindow.ui \ gui/instrument_editor/instrument_editor_fm_form.ui \ gui/instrument_editor/fm_operator_table.ui \ gui/labeled_vertical_slider.ui \ gui/labeled_horizontal_slider.ui \ gui/order_list_editor/order_list_editor.ui \ gui/pattern_editor/pattern_editor.ui \ gui/instrument_editor/instrument_editor_ssg_form.ui \ gui/instrument_editor/visualized_instrument_macro_editor.ui \ gui/module_properties_dialog.ui \ gui/groove_settings_dialog.ui \ gui/configuration_dialog.ui \ gui/comment_edit_dialog.ui \ gui/swap_tracks_dialog.ui \ gui/transpose_song_dialog.ui \ gui/vgm_export_settings_dialog.ui \ gui/wave_export_settings_dialog.ui \ gui/instrument_selection_dialog.ui \ gui/s98_export_settings_dialog.ui \ gui/fm_envelope_set_edit_dialog.ui INCLUDEPATH += \ $$PWD/instrument \ $$PWD/module # In-app resource bundle. Needs to be handled here because it generates an object file to link against include("resources/resources.pri") !system_rtaudio|!system_rtmidi { CONFIG += link_prl } system_* { CONFIG += link_pkgconfig } system_rtaudio { PKGCONFIG += rtaudio } else { INCLUDEPATH += $$PWD/../submodules/RtAudio/src LIBS += -L$$OUT_PWD/../submodules/RtAudio CONFIG(debug, debug|release):LIBS += -lrtaudiod else:CONFIG(release, debug|release):LIBS += -lrtaudio } system_rtmidi { PKGCONFIG += rtmidi } else { INCLUDEPATH += $$PWD/../submodules/RtMidi/src LIBS += -L$$OUT_PWD/../submodules/RtMidi CONFIG(debug, debug|release):LIBS += -lrtmidid else:CONFIG(release, debug|release):LIBS += -lrtmidi } BambooTracker-0.4.6/BambooTracker/audio/000077500000000000000000000000001401124043500200425ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/audio/audio_stream.cpp000066400000000000000000000072771401124043500232370ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "audio_stream.hpp" #include const std::string AudioStream::AUDIO_OUT_CLIENT_NAME = "BambooTracker"; AudioStream::AudioStream(QObject *parent) : QObject(parent), rate_(0), intrRate_(0), intrCount_(0), intrCountRest_(0), gcb_(nullptr), gcbPtr_(nullptr), tuState_(-1), started_(false), quitNotify_(false), tickNotifier_([this]() { tickNotifierRun(); }) { } AudioStream::~AudioStream() { quitNotify_.store(true); tickNotifierSem_.release(); tickNotifier_.join(); } void AudioStream::setGenerateCallback(GenerateCallback* cb, void* cbPtr) { std::lock_guard lock(mutex_); gcb_ = cb; gcbPtr_ = cbPtr; } void AudioStream::setTickUpdateCallback(TickUpdateCallback* cb, void* cbPtr) { std::lock_guard lock(mutex_); tucb_ = cb; tucbPtr_ = cbPtr; } bool AudioStream::initialize(uint32_t rate, uint32_t duration, uint32_t intrRate, const QString& backend, const QString& device, QString* errDetail) { Q_UNUSED(duration) Q_UNUSED(backend) Q_UNUSED(device) Q_UNUSED(errDetail) started_ = false; rate_ = rate; setInterruption(intrRate); return true; } void AudioStream::setInterruption(uint32_t intrRate) { std::lock_guard lock(mutex_); intrRate_ = intrRate; intrCount_ = rate_ / intrRate_; } uint32_t AudioStream::getStreamRate() const noexcept { return rate_; } void AudioStream::start() { std::lock_guard lock(mutex_); started_ = true; } void AudioStream::stop() { std::lock_guard lock(mutex_); started_ = false; } void AudioStream::generate(int16_t* container, uint32_t nSamples) { GenerateCallback* gcb = nullptr; void* gcbPtr = nullptr; TickUpdateCallback* tucb = nullptr; bool started = false; std::unique_lock lock(mutex_, std::try_to_lock); if (lock.owns_lock()) { gcb = gcb_; gcbPtr = gcbPtr_; tucb = tucb_; started = started_; } if (!gcb || !tucb || !started) { std::fill(container, container + (nSamples << 1), 0); return; } int16_t* destPtr = container; while (nSamples) { if (!intrCountRest_) { // Interruption intrCountRest_ = intrCount_; // Set counts to next interruption generateTick(); } size_t count = std::min(intrCountRest_, nSamples); nSamples -= count; intrCountRest_ -= count; gcb(destPtr, count, gcbPtr); destPtr += (count << 1); // Move head } } void AudioStream::generateTick() { tuState_.store(tucb_(tucbPtr_)); tickNotifierSem_.release(); } void AudioStream::tickNotifierRun() { while (true) { tickNotifierSem_.acquire(); if (quitNotify_.load()) return; emit streamInterrupted(tuState_.load()); } } BambooTracker-0.4.6/BambooTracker/audio/audio_stream.hpp000066400000000000000000000056151401124043500232360ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include #include #include class AudioStream : public QObject { Q_OBJECT public: explicit AudioStream(QObject* parent = nullptr); virtual ~AudioStream() override; using GenerateCallback = void (int16_t*, size_t, void*); void setGenerateCallback(GenerateCallback* cb, void* cbPtr); using TickUpdateCallback = int (void*); void setTickUpdateCallback(TickUpdateCallback* cb, void* cbPtr); // duration: miliseconds virtual bool initialize(uint32_t rate, uint32_t duration, uint32_t intrRate, const QString& backend, const QString& device, QString* errDetail = nullptr); virtual void shutdown() = 0; virtual std::vector getAvailableBackends() const = 0; virtual QString getCurrentBackend() const = 0; virtual std::vector getAvailableDevices() const = 0; virtual std::vector getAvailableDevices(const QString& backend) const = 0; virtual QString getDefaultOutputDevice() const = 0; virtual QString getDefaultOutputDevice(const QString& backend) const = 0; void setInterruption(uint32_t inrtRate); uint32_t getStreamRate() const noexcept; virtual void start(); virtual void stop(); signals: void streamInterrupted(int state); protected: static const std::string AUDIO_OUT_CLIENT_NAME; void generate(int16_t* container, uint32_t nSamples); private: uint32_t rate_; uint32_t intrRate_; uint32_t intrCount_; uint32_t intrCountRest_; std::mutex mutex_; GenerateCallback* gcb_; void* gcbPtr_; TickUpdateCallback* tucb_; void* tucbPtr_; std::atomic_int tuState_; bool started_; std::atomic_bool quitNotify_; QSemaphore tickNotifierSem_; std::thread tickNotifier_; void generateTick(); void tickNotifierRun(); }; BambooTracker-0.4.6/BambooTracker/audio/audio_stream_rtaudio.cpp000066400000000000000000000134431401124043500247560ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "audio_stream_rtaudio.hpp" #include #include #include "RtAudio.h" AudioStreamRtAudio::AudioStreamRtAudio(QObject* parent) : AudioStream(parent) { audio_.reset(new RtAudio); } AudioStreamRtAudio::~AudioStreamRtAudio() = default; bool AudioStreamRtAudio::initialize(uint32_t rate, uint32_t duration, uint32_t intrRate, const QString& backend, const QString& device, QString* errDetail) { shutdown(); setBackend(backend); RtAudio* audio = audio_.get(); const std::string deviceUtf8 = device.toStdString(); RtAudio::StreamParameters param; param.nChannels = 2; param.deviceId = ~0u; for (unsigned int i = 0, n = audio->getDeviceCount(); i < n && param.deviceId == ~0u; ++i) { RtAudio::DeviceInfo info = audio->getDeviceInfo(i); if (info.outputChannels >= 2 && info.name == deviceUtf8) param.deviceId = i; } if (param.deviceId == ~0u) param.deviceId = audio->getDefaultOutputDevice(); RtAudio::StreamOptions opts; opts.flags = RTAUDIO_SCHEDULE_REALTIME; opts.streamName = AUDIO_OUT_CLIENT_NAME; auto callback = +[](void* outputBuffer, void*, unsigned int nFrames, double, RtAudioStreamStatus, void* userData) -> int { auto stream = reinterpret_cast(userData); stream->generate(static_cast(outputBuffer), nFrames); return 0; }; unsigned int bufferSize = rate * duration / 1000; bool isSuccessed = false; try { audio->openStream(¶m, nullptr, RTAUDIO_SINT16, rate, &bufferSize, callback, this, &opts); if (errDetail) *errDetail = ""; isSuccessed = true; rate = audio->getStreamSampleRate(); // Match to real rate (for ALSA) } catch (RtAudioError& error) { error.printMessage(); if (errDetail) *errDetail = QString::fromStdString(error.getMessage()); } AudioStream::initialize(rate, duration, intrRate, backend, device); return isSuccessed; } void AudioStreamRtAudio::shutdown() { if (audio_->isStreamOpen()) audio_->closeStream(); } void AudioStreamRtAudio::setBackend(const QString& backend) { std::vector apis; RtAudio::getCompiledApi(apis); size_t i = 0; for (const auto& api : apis) { if (backend == QString::fromStdString(RtAudio::getApiDisplayName(api))) { audio_.reset(new RtAudio(apis[i])); return; } ++i; } audio_.reset(new RtAudio); } std::vector AudioStreamRtAudio::getAvailableBackends() const { std::vector apis; std::vector names; RtAudio::getCompiledApi(apis); for (const auto& api : apis) names.push_back(QString::fromStdString(RtAudio::getApiDisplayName(api))); return names; } QString AudioStreamRtAudio::getCurrentBackend() const { return QString::fromStdString(RtAudio::getApiDisplayName(audio_->getCurrentApi())); } std::vector AudioStreamRtAudio::getAvailableDevices() const { RtAudio* audio = audio_.get(); std::vector devices; for (unsigned int i = 0, n = audio->getDeviceCount(); i < n; ++i) { RtAudio::DeviceInfo info = audio->getDeviceInfo(i); if (info.outputChannels >= 2) devices.push_back(QString::fromStdString(info.name)); } return devices; } std::vector AudioStreamRtAudio::getAvailableDevices(const QString& backend) const { std::vector apis; RtAudio::getCompiledApi(apis); RtAudio::Api api = RtAudio::RTAUDIO_DUMMY; for (const auto& apiAvailable : apis) { if (backend == QString::fromStdString(RtAudio::getApiDisplayName(apiAvailable))) { api = apiAvailable; break; } } std::vector list; auto a = std::make_unique(api); unsigned int deviceCnt = a->getDeviceCount(); list.reserve(deviceCnt); for (unsigned int i = 0; i < deviceCnt; ++i) { RtAudio::DeviceInfo info = a->getDeviceInfo(i); if (info.outputChannels >= 2) list.push_back(QString::fromStdString(info.name)); } return list; } QString AudioStreamRtAudio::getDefaultOutputDevice() const { return QString::fromStdString(audio_->getDeviceInfo(audio_->getDefaultOutputDevice()).name); } QString AudioStreamRtAudio::getDefaultOutputDevice(const QString& backend) const { std::vector apis; RtAudio::getCompiledApi(apis); RtAudio::Api api = RtAudio::RTAUDIO_DUMMY; for (const auto& apiAvailable : apis) { if (backend == QString::fromStdString(RtAudio::getApiDisplayName(apiAvailable))) { api = apiAvailable; break; } } std::vector list; auto a = std::make_unique(api); return QString::fromStdString(a->getDeviceInfo(a->getDefaultOutputDevice()).name); } void AudioStreamRtAudio::start() { AudioStream::start(); if (audio_->isStreamOpen() && !audio_->isStreamRunning()) audio_->startStream(); } void AudioStreamRtAudio::stop() { AudioStream::stop(); if (audio_->isStreamOpen() && audio_->isStreamRunning()) audio_->stopStream(); } BambooTracker-0.4.6/BambooTracker/audio/audio_stream_rtaudio.hpp000066400000000000000000000040111401124043500247520ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "audio_stream.hpp" #include class RtAudio; class AudioStreamRtAudio final : public AudioStream { public: explicit AudioStreamRtAudio(QObject* parent = nullptr); ~AudioStreamRtAudio() override; bool initialize(uint32_t rate, uint32_t duration, uint32_t intrRate, const QString& backend, const QString& device, QString* errDetail) override; void shutdown() override; std::vector getAvailableBackends() const override; QString getCurrentBackend() const override; std::vector getAvailableDevices() const override; virtual std::vector getAvailableDevices(const QString& backend) const override; QString getDefaultOutputDevice() const override; QString getDefaultOutputDevice(const QString& backend) const override; void start() override; void stop() override; private: std::unique_ptr audio_; void setBackend(const QString& backend); }; BambooTracker-0.4.6/BambooTracker/bamboo_tracker.cpp000066400000000000000000002230371401124043500224260ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "bamboo_tracker.hpp" #include #include #include #include #include #include "configuration.hpp" #include "opna_controller.hpp" #include "playback.hpp" #include "tick_counter.hpp" #include "command/commands.hpp" #include "chip/register_write_logger.hpp" #include "io/module_io.hpp" #include "io/instrument_io.hpp" #include "io/bank_io.hpp" #include "bank.hpp" #include "note.hpp" #include "song_length_calculator.hpp" #include "utils.hpp" namespace { const uint32_t CHIP_CLOCK = 3993600 * 2; } BambooTracker::BambooTracker(std::weak_ptr config) : instMan_(std::make_shared(config.lock()->getOverwriteUnusedUneditedPropety())), jamMan_(std::make_unique()), tickCounter_(std::make_shared()), mod_(std::make_shared()), curOctave_(Note::DEFAULT_OCTAVE), curSongNum_(0), curTrackNum_(0), curOrderNum_(0), curStepNum_(0), curInstNum_(-1), curVolume_(127), mkOrder_(-1), mkStep_(-1), isFollowPlay_(true) { opnaCtrl_ = std::make_shared( static_cast(config.lock()->getEmulator()), CHIP_CLOCK, config.lock()->getSampleRate(), config.lock()->getBufferLength()); setMasterVolume(config.lock()->getMixerVolumeMaster()); setMasterVolumeFM(config.lock()->getMixerVolumeFM()); setMasterVolumeSSG(config.lock()->getMixerVolumeSSG()); songStyle_ = mod_->getSong(curSongNum_).getStyle(); playback_ = std::make_unique( opnaCtrl_, instMan_, tickCounter_, mod_, config.lock()->getRetrieveChannelState()); storeOnlyUsedSamples_ = config.lock()->getWriteOnlyUsedSamples(); volFMReversed_ = config.lock()->getReverseFMVolumeOrder(); } BambooTracker::~BambooTracker() = default; /********** Change configuration **********/ void BambooTracker::changeConfiguration(std::weak_ptr config) { setStreamRate(static_cast(config.lock()->getSampleRate())); setStreamDuration(static_cast(config.lock()->getBufferLength())); setMasterVolume(config.lock()->getMixerVolumeMaster()); if (mod_->getMixerType() == MixerType::UNSPECIFIED) { setMasterVolumeFM(config.lock()->getMixerVolumeFM()); setMasterVolumeSSG(config.lock()->getMixerVolumeSSG()); } playback_->setChannelRetrieving(config.lock()->getRetrieveChannelState()); instMan_->setPropertyFindMode(config.lock()->getOverwriteUnusedUneditedPropety()); storeOnlyUsedSamples_ = config.lock()->getWriteOnlyUsedSamples(); volFMReversed_ = config.lock()->getReverseFMVolumeOrder(); } /********** Current octave **********/ void BambooTracker::setCurrentOctave(int octave) { curOctave_ = octave; } int BambooTracker::getCurrentOctave() const { return curOctave_; } /********** Current volume **********/ void BambooTracker::setCurrentVolume(int volume) { curVolume_ = volume; } int BambooTracker::getCurrentVolume() const { return curVolume_; } /********** Current track **********/ void BambooTracker::setCurrentTrack(int num) { curTrackNum_ = num; } TrackAttribute BambooTracker::getCurrentTrackAttribute() const { return songStyle_.trackAttribs.at(static_cast(curTrackNum_)); } /********** Current instrument **********/ void BambooTracker::setCurrentInstrument(int n) { curInstNum_ = n; } int BambooTracker::getCurrentInstrumentNumber() const { return curInstNum_; } /********** Instrument edit **********/ void BambooTracker::addInstrument(int num, InstrumentType type, const std::string& name) { comMan_.invoke(std::make_unique(instMan_, num, type, name)); } void BambooTracker::removeInstrument(int num) { comMan_.invoke(std::make_unique(instMan_, num)); } std::unique_ptr BambooTracker::getInstrument(int num) { std::shared_ptr inst = instMan_->getInstrumentSharedPtr(num); if (inst == nullptr) return std::unique_ptr(); else return std::unique_ptr(inst->clone()); } void BambooTracker::cloneInstrument(int num, int refNum) { comMan_.invoke(std::make_unique(instMan_, num, refNum)); } void BambooTracker::deepCloneInstrument(int num, int refNum) { comMan_.invoke(std::make_unique(instMan_, num, refNum)); } void BambooTracker::swapInstruments(int a, int b, bool patternChange) { comMan_.invoke(std::make_unique(instMan_, mod_, a, b, curSongNum_, patternChange)); } void BambooTracker::loadInstrument(io::BinaryContainer& container, const std::string& path, int instNum) { AbstractInstrument* inst = io::InstrumentIO::getInstance().loadInstrument(container, path, instMan_, instNum); comMan_.invoke(std::make_unique(instMan_, std::unique_ptr(inst))); } void BambooTracker::saveInstrument(io::BinaryContainer& container, int instNum) { io::InstrumentIO::getInstance().saveInstrument(container, instMan_, instNum); } void BambooTracker::importInstrument(const AbstractBank &bank, size_t index, int instNum) { AbstractInstrument* inst = bank.loadInstrument(index, instMan_, instNum); comMan_.invoke(std::make_unique( instMan_, std::unique_ptr(inst))); } void BambooTracker::exportInstruments(io::BinaryContainer& container, const std::vector& instNums) { io::BankIO::getInstance().saveBank(container, instMan_, instNums); } int BambooTracker::findFirstFreeInstrumentNumber() const { return instMan_->findFirstFreeInstrument(); } void BambooTracker::setInstrumentName(int num, const std::string& name) { comMan_.invoke(std::make_unique(instMan_, num, name)); } void BambooTracker::clearAllInstrument() { instMan_->clearAll(); } std::vector BambooTracker::getInstrumentIndices() const { return instMan_->getInstrumentIndices(); } std::vector BambooTracker::getUnusedInstrumentIndices() const { std::vector instIdcs = instMan_->getInstrumentIndices(); std::set regdInsts = mod_->getRegisterdInstruments(); std::vector unused; std::set_difference(instIdcs.begin(), instIdcs.end(), regdInsts.begin(), regdInsts.end(), std::back_inserter(unused)); return unused; } void BambooTracker::clearUnusedInstrumentProperties() { instMan_->clearUnusedInstrumentProperties(); } std::vector BambooTracker::getInstrumentNames() const { return instMan_->getInstrumentNameList(); } //--- FM void BambooTracker::setEnvelopeFMParameter(int envNum, FMEnvelopeParameter param, int value) { instMan_->setEnvelopeFMParameter(envNum, param, value); opnaCtrl_->updateInstrumentFMEnvelopeParameter(envNum, param); } void BambooTracker::setEnvelopeFMOperatorEnable(int envNum, int opNum, bool enable) { instMan_->setEnvelopeFMOperatorEnabled(envNum, opNum, enable); opnaCtrl_->setInstrumentFMOperatorEnabled(envNum, opNum); } void BambooTracker::setInstrumentFMEnvelope(int instNum, int envNum) { instMan_->setInstrumentFMEnvelope(instNum, envNum); opnaCtrl_->updateInstrumentFM(instNum); } std::multiset BambooTracker::getEnvelopeFMUsers(int envNum) const { return instMan_->getEnvelopeFMUsers(envNum); } void BambooTracker::setLFOFMParameter(int lfoNum, FMLFOParameter param, int value) { instMan_->setLFOFMParameter(lfoNum, param, value); opnaCtrl_->updateInstrumentFMLFOParameter(lfoNum, param); } void BambooTracker::setInstrumentFMLFOEnabled(int instNum, bool enabled) { instMan_->setInstrumentFMLFOEnabled(instNum, enabled); opnaCtrl_->updateInstrumentFM(instNum); } void BambooTracker::setInstrumentFMLFO(int instNum, int lfoNum) { instMan_->setInstrumentFMLFO(instNum, lfoNum); opnaCtrl_->updateInstrumentFM(instNum); } std::multiset BambooTracker::getLFOFMUsers(int lfoNum) const { return instMan_->getLFOFMUsers(lfoNum); } void BambooTracker::addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data) { instMan_->addOperatorSequenceFMSequenceData(param, opSeqNum, data); } void BambooTracker::removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum) { instMan_->removeOperatorSequenceFMSequenceData(param, opSeqNum); } void BambooTracker::setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data) { instMan_->setOperatorSequenceFMSequenceData(param, opSeqNum, cnt, data); } void BambooTracker::addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop) { instMan_->addOperatorSequenceFMLoop(param, opSeqNum, loop); } void BambooTracker::removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end) { instMan_->removeOperatorSequenceFMLoop(param, opSeqNum, begin, end); } void BambooTracker::changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeOperatorSequenceFMLoop(param, opSeqNum, prevBegin, prevEnd, loop); } void BambooTracker::clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum) { instMan_->clearOperatorSequenceFMLoops(param, opSeqNum); } void BambooTracker::setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release) { instMan_->setOperatorSequenceFMRelease(param, opSeqNum, release); } void BambooTracker::setInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param, int opSeqNum) { instMan_->setInstrumentFMOperatorSequence(instNum, param, opSeqNum); opnaCtrl_->updateInstrumentFM(instNum); } void BambooTracker::setInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param, bool enabled) { instMan_->setInstrumentFMOperatorSequenceEnabled(instNum, param, enabled); opnaCtrl_->updateInstrumentFM(instNum); } std::multiset BambooTracker::getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const { return instMan_->getOperatorSequenceFMUsers(param, opSeqNum); } void BambooTracker::setArpeggioFMType(int arpNum, SequenceType type) { instMan_->setArpeggioFMType(arpNum, type); } void BambooTracker::addArpeggioFMSequenceData(int arpNum, int data) { instMan_->addArpeggioFMSequenceData(arpNum, data); } void BambooTracker::removeArpeggioFMSequenceData(int arpNum) { instMan_->removeArpeggioFMSequenceData(arpNum); } void BambooTracker::setArpeggioFMSequenceData(int arpNum, int cnt, int data) { instMan_->setArpeggioFMSequenceData(arpNum, cnt, data); } void BambooTracker::addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop) { instMan_->addArpeggioFMLoop(arpNum, loop); } void BambooTracker::removeArpeggioFMLoop(int arpNum, int begin, int end) { instMan_->removeArpeggioFMLoop(arpNum, begin, end); } void BambooTracker::changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeArpeggioFMLoop(arpNum, prevBegin, prevEnd, loop); } void BambooTracker::clearArpeggioFMLoops(int arpNum) { instMan_->clearArpeggioFMLoops(arpNum); } void BambooTracker::setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release) { instMan_->setArpeggioFMRelease(arpNum, release); } void BambooTracker::setInstrumentFMArpeggio(int instNum, FMOperatorType op, int arpNum) { instMan_->setInstrumentFMArpeggio(instNum, op, arpNum); opnaCtrl_->updateInstrumentFM(instNum); } void BambooTracker::setInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op, bool enabled) { instMan_->setInstrumentFMArpeggioEnabled(instNum, op, enabled); opnaCtrl_->updateInstrumentFM(instNum); } std::multiset BambooTracker::getArpeggioFMUsers(int arpNum) const { return instMan_->getArpeggioFMUsers(arpNum); } void BambooTracker::setPitchFMType(int ptNum, SequenceType type) { instMan_->setPitchFMType(ptNum, type); } void BambooTracker::addPitchFMSequenceData(int ptNum, int data) { instMan_->addPitchFMSequenceData(ptNum, data); } void BambooTracker::removePitchFMSequenceData(int ptNum) { instMan_->removePitchFMSequenceData(ptNum); } void BambooTracker::setPitchFMSequenceData(int ptNum, int cnt, int data) { instMan_->setPitchFMSequenceData(ptNum, cnt, data); } void BambooTracker::addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop) { instMan_->addPitchFMLoop(ptNum, loop); } void BambooTracker::removePitchFMLoop(int ptNum, int begin, int end) { instMan_->removePitchFMLoop(ptNum, begin, end); } void BambooTracker::changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changePitchFMLoop(ptNum, prevBegin, prevEnd, loop); } void BambooTracker::clearPitchFMLoops(int ptNum) { instMan_->clearPitchFMLoops(ptNum); } void BambooTracker::setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release) { instMan_->setPitchFMRelease(ptNum, release); } void BambooTracker::setInstrumentFMPitch(int instNum, FMOperatorType op, int ptNum) { instMan_->setInstrumentFMPitch(instNum, op, ptNum); opnaCtrl_->updateInstrumentFM(instNum); } void BambooTracker::setInstrumentFMPitchEnabled(int instNum, FMOperatorType op, bool enabled) { instMan_->setInstrumentFMPitchEnabled(instNum, op, enabled); opnaCtrl_->updateInstrumentFM(instNum); } std::multiset BambooTracker::getPitchFMUsers(int ptNum) const { return instMan_->getPitchFMUsers(ptNum); } void BambooTracker::setInstrumentFMEnvelopeResetEnabled(int instNum, FMOperatorType op, bool enabled) { instMan_->setInstrumentFMEnvelopeResetEnabled(instNum, op, enabled); opnaCtrl_->updateInstrumentFM(instNum); } //--- SSG void BambooTracker::addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data) { instMan_->addWaveformSSGSequenceData(wfNum, data); } void BambooTracker::removeWaveformSSGSequenceData(int wfNum) { instMan_->removeWaveformSSGSequenceData(wfNum); } void BambooTracker::setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data) { instMan_->setWaveformSSGSequenceData(wfNum, cnt, data); } void BambooTracker::addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop) { instMan_->addWaveformSSGLoop(wfNum, loop); } void BambooTracker::removeWaveformSSGLoop(int wfNum, int begin, int end) { instMan_->removeWaveformSSGLoop(wfNum, begin, end); } void BambooTracker::changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeWaveformSSGLoop(wfNum, prevBegin, prevEnd, loop); } void BambooTracker::clearWaveformSSGLoops(int wfNum) { instMan_->clearWaveformSSGLoops(wfNum); } void BambooTracker::setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release) { instMan_->setWaveformSSGRelease(wfNum, release); } void BambooTracker::setInstrumentSSGWaveform(int instNum, int wfNum) { instMan_->setInstrumentSSGWaveform(instNum, wfNum); opnaCtrl_->updateInstrumentSSG(instNum); } void BambooTracker::setInstrumentSSGWaveformEnabled(int instNum, bool enabled) { instMan_->setInstrumentSSGWaveformEnabled(instNum, enabled); opnaCtrl_->updateInstrumentSSG(instNum); } std::multiset BambooTracker::getWaveformSSGUsers(int wfNum) const { return instMan_->getWaveformSSGUsers(wfNum); } void BambooTracker::addToneNoiseSSGSequenceData(int tnNum, int data) { instMan_->addToneNoiseSSGSequenceData(tnNum, data); } void BambooTracker::removeToneNoiseSSGSequenceData(int tnNum) { instMan_->removeToneNoiseSSGSequenceData(tnNum); } void BambooTracker::setToneNoiseSSGSequenceData(int tnNum, int cnt, int data) { instMan_->setToneNoiseSSGSequenceData(tnNum, cnt, data); } void BambooTracker::addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop) { instMan_->addToneNoiseSSGLoop(tnNum, loop); } void BambooTracker::removeToneNoiseSSGLoop(int tnNum, int begin, int end) { instMan_->removeToneNoiseSSGLoop(tnNum, begin, end); } void BambooTracker::changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeToneNoiseSSGLoop(tnNum, prevBegin, prevEnd, loop); } void BambooTracker::clearToneNoiseSSGLoops(int tnNum) { instMan_->clearToneNoiseSSGLoops(tnNum); } void BambooTracker::setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release) { instMan_->setToneNoiseSSGRelease(tnNum, release); } void BambooTracker::setInstrumentSSGToneNoise(int instNum, int tnNum) { instMan_->setInstrumentSSGToneNoise(instNum, tnNum); opnaCtrl_->updateInstrumentSSG(instNum); } void BambooTracker::setInstrumentSSGToneNoiseEnabled(int instNum, bool enabled) { instMan_->setInstrumentSSGToneNoiseEnabled(instNum, enabled); opnaCtrl_->updateInstrumentSSG(instNum); } std::multiset BambooTracker::getToneNoiseSSGUsers(int tnNum) const { return instMan_->getToneNoiseSSGUsers(tnNum); } void BambooTracker::addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data) { instMan_->addEnvelopeSSGSequenceData(envNum, data); } void BambooTracker::removeEnvelopeSSGSequenceData(int envNum) { instMan_->removeEnvelopeSSGSequenceData(envNum); } void BambooTracker::setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data) { instMan_->setEnvelopeSSGSequenceData(envNum, cnt, data); } void BambooTracker::addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop) { instMan_->addEnvelopeSSGLoop(envNum, loop); } void BambooTracker::removeEnvelopeSSGLoop(int envNum, int begin, int end) { instMan_->removeEnvelopeSSGLoop(envNum, begin, end); } void BambooTracker::changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeEnvelopeSSGLoop(envNum, prevBegin, prevEnd, loop); } void BambooTracker::clearEnvelopeSSGLoops(int envNum) { instMan_->clearEnvelopeSSGLoops(envNum); } void BambooTracker::setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release) { instMan_->setEnvelopeSSGRelease(envNum, release); } void BambooTracker::setInstrumentSSGEnvelope(int instNum, int envNum) { instMan_->setInstrumentSSGEnvelope(instNum, envNum); opnaCtrl_->updateInstrumentSSG(instNum); } void BambooTracker::setInstrumentSSGEnvelopeEnabled(int instNum, bool enabled) { instMan_->setInstrumentSSGEnvelopeEnabled(instNum, enabled); opnaCtrl_->updateInstrumentSSG(instNum); } std::multiset BambooTracker::getEnvelopeSSGUsers(int envNum) const { return instMan_->getEnvelopeSSGUsers(envNum); } void BambooTracker::setArpeggioSSGType(int arpNum, SequenceType type) { instMan_->setArpeggioSSGType(arpNum, type); } void BambooTracker::addArpeggioSSGSequenceData(int arpNum, int data) { instMan_->addArpeggioSSGSequenceData(arpNum, data); } void BambooTracker::removeArpeggioSSGSequenceData(int arpNum) { instMan_->removeArpeggioSSGSequenceData(arpNum); } void BambooTracker::setArpeggioSSGSequenceData(int arpNum, int cnt, int data) { instMan_->setArpeggioSSGSequenceData(arpNum, cnt, data); } void BambooTracker::addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop) { instMan_->addArpeggioSSGLoop(arpNum, loop); } void BambooTracker::removeArpeggioSSGLoop(int arpNum, int begin, int end) { instMan_->removeArpeggioSSGLoop(arpNum, begin, end); } void BambooTracker::changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeArpeggioSSGLoop(arpNum, prevBegin, prevEnd, loop); } void BambooTracker::clearArpeggioSSGLoops(int arpNum) { instMan_->clearArpeggioSSGLoops(arpNum); } void BambooTracker::setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release) { instMan_->setArpeggioSSGRelease(arpNum, release); } void BambooTracker::setInstrumentSSGArpeggio(int instNum, int arpNum) { instMan_->setInstrumentSSGArpeggio(instNum, arpNum); opnaCtrl_->updateInstrumentSSG(instNum); } void BambooTracker::setInstrumentSSGArpeggioEnabled(int instNum, bool enabled) { instMan_->setInstrumentSSGArpeggioEnabled(instNum, enabled); opnaCtrl_->updateInstrumentSSG(instNum); } std::multiset BambooTracker::getArpeggioSSGUsers(int arpNum) const { return instMan_->getArpeggioSSGUsers(arpNum); } void BambooTracker::setPitchSSGType(int ptNum, SequenceType type) { instMan_->setPitchSSGType(ptNum, type); } void BambooTracker::addPitchSSGSequenceData(int ptNum, int data) { instMan_->addPitchSSGSequenceData(ptNum, data); } void BambooTracker::removePitchSSGSequenceData(int ptNum) { instMan_->removePitchSSGSequenceData(ptNum); } void BambooTracker::setPitchSSGSequenceData(int ptNum, int cnt, int data) { instMan_->setPitchSSGSequenceData(ptNum, cnt, data); } void BambooTracker::addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop) { instMan_->addPitchSSGLoop(ptNum, loop); } void BambooTracker::removePitchSSGLoop(int ptNum, int begin, int end) { instMan_->removePitchSSGLoop(ptNum, begin, end); } void BambooTracker::changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changePitchSSGLoop(ptNum, prevBegin, prevEnd, loop); } void BambooTracker::clearPitchSSGLoops(int ptNum) { instMan_->clearPitchSSGLoops(ptNum); } void BambooTracker::setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release) { instMan_->setPitchSSGRelease(ptNum, release); } void BambooTracker::setInstrumentSSGPitch(int instNum, int ptNum) { instMan_->setInstrumentSSGPitch(instNum, ptNum); opnaCtrl_->updateInstrumentSSG(instNum); } void BambooTracker::setInstrumentSSGPitchEnabled(int instNum, bool enabled) { instMan_->setInstrumentSSGPitchEnabled(instNum, enabled); opnaCtrl_->updateInstrumentSSG(instNum); } std::multiset BambooTracker::getPitchSSGUsers(int ptNum) const { return instMan_->getPitchSSGUsers(ptNum); } //--- ADPCM size_t BambooTracker::getADPCMLimit() const { return opnaCtrl_->getDRAMSize(); } size_t BambooTracker::getADPCMStoredSize() const { return opnaCtrl_->getADPCMStoredSize(); } void BambooTracker::setSampleADPCMRootKeyNumber(int sampNum, int n) { instMan_->setSampleADPCMRootKeyNumber(sampNum, n); // opnaCtrl is changed through refInstADPCM (shared_ptr) } int BambooTracker::getSampleADPCMRootKeyNumber(int sampNum) const { return instMan_->getSampleADPCMRootKeyNumber(sampNum); } void BambooTracker::setSampleADPCMRootDeltaN(int sampNum, int dn) { instMan_->setSampleADPCMRootDeltaN(sampNum, dn); // opnaCtrl is changed through refInstADPCM (shared_ptr) } int BambooTracker::getSampleADPCMRootDeltaN(int sampNum) const { return instMan_->getSampleADPCMRootDeltaN(sampNum); } void BambooTracker::setSampleADPCMRepeatEnabled(int sampNum, bool enabled) { instMan_->setSampleADPCMRepeatEnabled(sampNum, enabled); // opnaCtrl is changed through refInstADPCM (shared_ptr) } bool BambooTracker::getSampleADPCMRepeatEnabled(int sampNum) const { return instMan_->isSampleADPCMRepeatable(sampNum); } void BambooTracker::storeSampleADPCMRawSample(int sampNum, const std::vector& sample) { instMan_->storeSampleADPCMRawSample(sampNum, sample); } void BambooTracker::storeSampleADPCMRawSample(int sampNum, std::vector&& sample) { instMan_->storeSampleADPCMRawSample(sampNum, std::move(sample)); } std::vector BambooTracker::getSampleADPCMRawSample(int sampNum) const { return instMan_->getSampleADPCMRawSample(sampNum); } void BambooTracker::clearSampleADPCMRawSample(int sampNum) { instMan_->clearSampleADPCMRawSample(sampNum); } bool BambooTracker::assignSampleADPCMRawSamples() { opnaCtrl_->clearSamplesADPCM(); std::vector idcs = storeOnlyUsedSamples_ ? instMan_->getSampleADPCMValidIndices() : instMan_->getSampleADPCMEntriedIndices(); bool storedAll = true; for (auto sampNum : idcs) { size_t startAddr, stopAddr; if (opnaCtrl_->storeSampleADPCM(instMan_->getSampleADPCMRawSample(sampNum), startAddr, stopAddr)) { instMan_->setSampleADPCMStartAddress(sampNum, startAddr); instMan_->setSampleADPCMStopAddress(sampNum, stopAddr); } else { storedAll = false; } } return storedAll; } size_t BambooTracker::getSampleADPCMStartAddress(int sampNum) const { return instMan_->getSampleADPCMStartAddress(sampNum); } size_t BambooTracker::getSampleADPCMStopAddress(int sampNum) const { return instMan_->getSampleADPCMStopAddress(sampNum); } void BambooTracker::setInstrumentADPCMSample(int instNum, int sampNum) { instMan_->setInstrumentADPCMSample(instNum, sampNum); opnaCtrl_->updateInstrumentADPCM(instNum); } std::multiset BambooTracker::getSampleADPCMUsers(int sampNum) const { return instMan_->getSampleADPCMUsers(sampNum); } void BambooTracker::addEnvelopeADPCMSequenceData(int envNum, int data) { instMan_->addEnvelopeADPCMSequenceData(envNum, data); } void BambooTracker::removeEnvelopeADPCMSequenceData(int envNum) { instMan_->removeEnvelopeADPCMSequenceData(envNum); } void BambooTracker::setEnvelopeADPCMSequenceData(int envNum, int cnt, int data) { instMan_->setEnvelopeADPCMSequenceData(envNum, cnt, data); } void BambooTracker::addEnvelopeADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop) { instMan_->addEnvelopeADPCMLoop(arpNum, loop); } void BambooTracker::removeEnvelopeADPCMLoop(int envNum, int begin, int end) { instMan_->removeEnvelopeADPCMLoop(envNum, begin, end); } void BambooTracker::changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeEnvelopeADPCMLoop(envNum, prevBegin, prevEnd, loop); } void BambooTracker::clearEnvelopeADPCMLoops(int envNum) { instMan_->clearEnvelopeADPCMLoops(envNum); } void BambooTracker::setEnvelopeADPCMRelease(int arpNum, const InstrumentSequenceRelease& release) { instMan_->setEnvelopeADPCMRelease(arpNum, release); } void BambooTracker::setInstrumentADPCMEnvelope(int instNum, int envNum) { instMan_->setInstrumentADPCMEnvelope(instNum, envNum); opnaCtrl_->updateInstrumentADPCM(instNum); } void BambooTracker::setInstrumentADPCMEnvelopeEnabled(int instNum, bool enabled) { instMan_->setInstrumentADPCMEnvelopeEnabled(instNum, enabled); opnaCtrl_->updateInstrumentADPCM(instNum); } std::multiset BambooTracker::getEnvelopeADPCMUsers(int envNum) const { return instMan_->getEnvelopeADPCMUsers(envNum); } void BambooTracker::setArpeggioADPCMType(int arpNum, SequenceType type) { instMan_->setArpeggioADPCMType(arpNum, type); } void BambooTracker::addArpeggioADPCMSequenceData(int arpNum, int data) { instMan_->addArpeggioADPCMSequenceData(arpNum, data); } void BambooTracker::removeArpeggioADPCMSequenceData(int arpNum) { instMan_->removeArpeggioADPCMSequenceData(arpNum); } void BambooTracker::setArpeggioADPCMSequenceData(int arpNum, int cnt, int data) { instMan_->setArpeggioADPCMSequenceData(arpNum, cnt, data); } void BambooTracker::addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop) { instMan_->addArpeggioADPCMLoop(arpNum, loop); } void BambooTracker::removeArpeggioADPCMLoop(int arpNum, int begin, int end) { instMan_->removeArpeggioADPCMLoop(arpNum, begin, end); } void BambooTracker::changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changeArpeggioADPCMLoop(arpNum, prevBegin, prevEnd, loop); } void BambooTracker::clearArpeggioADPCMLoops(int arpNum) { instMan_->clearArpeggioADPCMLoops(arpNum); } void BambooTracker::setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release) { instMan_->setArpeggioADPCMRelease(arpNum, release); } void BambooTracker::setInstrumentADPCMArpeggio(int instNum, int arpNum) { instMan_->setInstrumentADPCMArpeggio(instNum, arpNum); opnaCtrl_->updateInstrumentADPCM(instNum); } void BambooTracker::setInstrumentADPCMArpeggioEnabled(int instNum, bool enabled) { instMan_->setInstrumentADPCMArpeggioEnabled(instNum, enabled); opnaCtrl_->updateInstrumentADPCM(instNum); } std::multiset BambooTracker::getArpeggioADPCMUsers(int arpNum) const { return instMan_->getArpeggioADPCMUsers(arpNum); } void BambooTracker::setPitchADPCMType(int ptNum, SequenceType type) { instMan_->setPitchADPCMType(ptNum, type); } void BambooTracker::addPitchADPCMSequenceData(int ptNum, int data) { instMan_->addPitchADPCMSequenceData(ptNum, data); } void BambooTracker::removePitchADPCMSequenceData(int ptNum) { instMan_->removePitchADPCMSequenceData(ptNum); } void BambooTracker::setPitchADPCMSequenceData(int ptNum, int cnt, int data) { instMan_->setPitchADPCMSequenceData(ptNum, cnt, data); } void BambooTracker::addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop) { instMan_->addPitchADPCMLoop(ptNum, loop); } void BambooTracker::removePitchADPCMLoop(int ptNum, int begin, int end) { instMan_->removePitchADPCMLoop(ptNum, begin, end); } void BambooTracker::changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { instMan_->changePitchADPCMLoop(ptNum, prevBegin, prevEnd, loop); } void BambooTracker::clearPitchADPCMLoops(int ptNum) { instMan_->clearPitchADPCMLoops(ptNum); } void BambooTracker::setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release) { instMan_->setPitchADPCMRelease(ptNum, release); } void BambooTracker::setInstrumentADPCMPitch(int instNum, int ptNum) { instMan_->setInstrumentADPCMPitch(instNum, ptNum); opnaCtrl_->updateInstrumentADPCM(instNum); } void BambooTracker::setInstrumentADPCMPitchEnabled(int instNum, bool enabled) { instMan_->setInstrumentADPCMPitchEnabled(instNum, enabled); opnaCtrl_->updateInstrumentADPCM(instNum); } std::multiset BambooTracker::getPitchADPCMUsers(int ptNum) const { return instMan_->getPitchADPCMUsers(ptNum); } //--- Drumkit void BambooTracker::setInstrumentDrumkitSample(int instNum, int key, int sampNum) { instMan_->setInstrumentDrumkitSamples(instNum, key, sampNum); opnaCtrl_->updateInstrumentDrumkit(instNum, key); } void BambooTracker::setInstrumentDrumkitSampleEnabled(int instNum, int key, bool enabled) { instMan_->setInstrumentDrumkitSamplesEnabled(instNum, key, enabled); opnaCtrl_->updateInstrumentADPCM(instNum); } void BambooTracker::setInstrumentDrumkitPitch(int instNum, int key, int pitch) { instMan_->setInstrumentDrumkitPitch(instNum, key, pitch); opnaCtrl_->updateInstrumentDrumkit(instNum, key); } /********** Song edit **********/ int BambooTracker::getCurrentSongNumber() const { return curSongNum_; } void BambooTracker::setCurrentSongNumber(int num) { curSongNum_ = num; curTrackNum_ = 0; curOrderNum_ = 0; curStepNum_ = 0; mkOrder_ = -1; mkStep_ = -1; auto& song = mod_->getSong(curSongNum_); songStyle_ = song.getStyle(); playback_->setSong(mod_, curSongNum_); /*jamMan_->clear();*/ // Reset opnaCtrl_->reset(); opnaCtrl_->setMode(songStyle_.type); tickCounter_->resetCount(); tickCounter_->setTempo(song.getTempo()); tickCounter_->setSpeed(song.getSpeed()); tickCounter_->setGroove(mod_->getGroove(song.getGroove())); tickCounter_->setGrooveState(song.isUsedTempo() ? GrooveState::Invalid : GrooveState::ValidByGlobal); std::unordered_map pairs = { { SoundSource::FM, Song::getFMChannelCount(songStyle_.type) }, { SoundSource::SSG, 3 }, { SoundSource::RHYTHM, 6 }, { SoundSource::ADPCM, 1 }, }; for (auto& pair : pairs) { muteState_[pair.first] = std::vector(pair.second, false); for (int ch = 0; ch < pair.second; ++ch) opnaCtrl_->setMuteState(pair.first, ch, false); } } /********** Order edit **********/ int BambooTracker::getCurrentOrderNumber() const { return curOrderNum_; } void BambooTracker::setCurrentOrderNumber(int num) { curOrderNum_ = num; } /********** Pattern edit **********/ int BambooTracker::getCurrentStepNumber() const { return curStepNum_; } void BambooTracker::setCurrentStepNumber(int num) { curStepNum_ = num; } /********** Undo-Redo **********/ void BambooTracker::undo() { comMan_.undo(); } void BambooTracker::redo() { comMan_.redo(); } void BambooTracker::clearCommandHistory() { comMan_.clear(); } /********** Jam mode **********/ void BambooTracker::toggleJamMode() { if (jamMan_->toggleJamMode() && !isPlaySong()) { jamMan_->polyphonic(true); } else { jamMan_->polyphonic(false); } } bool BambooTracker::isJamMode() const { return jamMan_->isJamMode(); } void BambooTracker::jamKeyOn(JamKey key, bool volumeSet) { int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; funcJamKeyOn(key, keyNum, attrib, volumeSet); } void BambooTracker::jamKeyOn(int keyNum, bool volumeSet) { const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; funcJamKeyOn(JamKey::MidiKey, keyNum, attrib, volumeSet); } void BambooTracker::jamKeyOnForced(JamKey key, SoundSource src, bool volumeSet, std::shared_ptr inst) { int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; if (attrib.source == src) { funcJamKeyOn(key, keyNum, attrib, volumeSet, inst); } else { auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOn(key, keyNum, *it, volumeSet, inst); } } void BambooTracker::jamKeyOnForced(int keyNum, SoundSource src, bool volumeSet, std::shared_ptr inst) { const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; if (attrib.source == src) { funcJamKeyOn(JamKey::MidiKey, keyNum, attrib, volumeSet, inst); } else { auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOn(JamKey::MidiKey, keyNum, *it, volumeSet, inst); } } void BambooTracker::funcJamKeyOn(JamKey key, int keyNum, const TrackAttribute& attrib, bool volumeSet, std::shared_ptr inst) { if (playback_->isPlayingStep()) playback_->stopPlaySong(); // Reset if (attrib.source == SoundSource::RHYTHM) { if (volumeSet) opnaCtrl_->setVolumeRhythm(attrib.channelInSource, std::min(curVolume_, bt_defs::NSTEP_RHYTHM_VOLUME - 1)); opnaCtrl_->setKeyOnFlagRhythm(attrib.channelInSource); opnaCtrl_->updateRegisterStates(); } else { std::vector&& list = jamMan_->keyOn(key, attrib.channelInSource, attrib.source, keyNum); if (list.size() == 2) { // Key off JamKeyInfo& offInfo = list[1]; switch (offInfo.source) { case SoundSource::FM: if (songStyle_.type == SongType::FM3chExpanded && offInfo.channelInSource == 2) { opnaCtrl_->keyOffFM(2, true); opnaCtrl_->keyOffFM(6, true); opnaCtrl_->keyOffFM(7, true); opnaCtrl_->keyOffFM(8, true); } else { opnaCtrl_->keyOffFM(offInfo.channelInSource, true); } break; case SoundSource::SSG: opnaCtrl_->keyOffSSG(offInfo.channelInSource, true); break; case SoundSource::ADPCM: opnaCtrl_->keyOffADPCM(true); break; default: break; } } if (!inst) { // Use current instrument if not specified inst = instMan_->getInstrumentSharedPtr(curInstNum_); } JamKeyInfo& onInfo = list.front(); Note&& note = jam_utils::makeNote(onInfo, curOctave_); switch (onInfo.source) { case SoundSource::FM: if (auto fm = std::dynamic_pointer_cast(inst)) opnaCtrl_->setInstrumentFM(onInfo.channelInSource, fm); if (volumeSet) { int vol; if (volFMReversed_) vol = effect_utils::reverseFmVolume(curVolume_, true); else vol = std::min(curVolume_, bt_defs::NSTEP_FM_VOLUME - 1); opnaCtrl_->setVolumeFM(onInfo.channelInSource, vol); } if (songStyle_.type == SongType::FM3chExpanded && onInfo.channelInSource == 2) { opnaCtrl_->keyOnFM(2, note, true); opnaCtrl_->keyOnFM(6, note, true); opnaCtrl_->keyOnFM(7, note, true); opnaCtrl_->keyOnFM(8, note, true); } else { opnaCtrl_->keyOnFM(onInfo.channelInSource, note, true); } break; case SoundSource::SSG: if (auto ssg = std::dynamic_pointer_cast(inst)) opnaCtrl_->setInstrumentSSG(onInfo.channelInSource, ssg); if (volumeSet) opnaCtrl_->setVolumeSSG(onInfo.channelInSource, std::min(curVolume_, 0xf)); opnaCtrl_->keyOnSSG(onInfo.channelInSource, note, true); break; case SoundSource::ADPCM: if (auto adpcm = std::dynamic_pointer_cast(inst)) opnaCtrl_->setInstrumentADPCM(adpcm); else if (auto kit = std::dynamic_pointer_cast(inst)) opnaCtrl_->setInstrumentDrumkit(kit); if (volumeSet) opnaCtrl_->setVolumeADPCM(curVolume_); opnaCtrl_->keyOnADPCM(note, true); break; default: break; } } } void BambooTracker::jamKeyOff(JamKey key) { int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; funcJamKeyOff(key, keyNum, attrib); } void BambooTracker::jamKeyOff(int keyNum) { const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; funcJamKeyOff(JamKey::MidiKey, keyNum, attrib); } void BambooTracker::jamKeyOffForced(JamKey key, SoundSource src) { int keyNum = jam_utils::makeNote(curOctave_, key).getNoteNumber(); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; if (attrib.source == src) { funcJamKeyOff(key, keyNum, attrib); } else { auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOff(key, keyNum, *it); } } void BambooTracker::jamKeyOffForced(int keyNum, SoundSource src) { const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(curTrackNum_)]; if (attrib.source == src) { funcJamKeyOff(JamKey::MidiKey, keyNum, attrib); } else { auto it = utils::findIf(songStyle_.trackAttribs, [src](const TrackAttribute& attrib) { return attrib.source == src; }); funcJamKeyOff(JamKey::MidiKey, keyNum, *it); } } void BambooTracker::funcJamKeyOff(JamKey key, int keyNum, const TrackAttribute& attrib) { if (attrib.source == SoundSource::RHYTHM) { opnaCtrl_->setKeyOffFlagRhythm(attrib.channelInSource); opnaCtrl_->updateRegisterStates(); } else { JamKeyInfo&& info = jamMan_->keyOff(key, keyNum); if (info.channelInSource > -1) { // Key still sound switch (info.source) { case SoundSource::FM: if (songStyle_.type == SongType::FM3chExpanded && info.channelInSource == 2) { opnaCtrl_->keyOffFM(2, true); opnaCtrl_->keyOffFM(6, true); opnaCtrl_->keyOffFM(7, true); opnaCtrl_->keyOffFM(8, true); } else { opnaCtrl_->keyOffFM(info.channelInSource, true); } break; case SoundSource::SSG: opnaCtrl_->keyOffSSG(info.channelInSource, true); break; case SoundSource::ADPCM: opnaCtrl_->keyOffADPCM(true); break; default: break; } } } } bool BambooTracker::assignADPCMBeforeForcedJamKeyOn( std::shared_ptr inst, std::unordered_map>& sampAddrs) { size_t start, stop; bool isAssignedAll = false; switch (inst->getType()) { case InstrumentType::ADPCM: { opnaCtrl_->clearSamplesADPCM(); if (opnaCtrl_->storeSampleADPCM( std::dynamic_pointer_cast(inst)->getRawSample(), start, stop)) { sampAddrs[0] = { start, stop }; isAssignedAll = true; } break; } case InstrumentType::Drumkit: { opnaCtrl_->clearSamplesADPCM(); std::vector> addrs; auto kit = std::dynamic_pointer_cast(inst); for (const int& key : kit->getAssignedKeys()) { int n = kit->getSampleNumber(key); if (!sampAddrs.count(n)) { bool assigned = opnaCtrl_->storeSampleADPCM(kit->getRawSample(key), start, stop); if (assigned) sampAddrs[n] = { start, stop }; isAssignedAll &= assigned; } } break; } default: break; } return isAssignedAll; } /********** Play song **********/ void BambooTracker::startPlaySong() { playback_->startPlaySong(curOrderNum_); startPlay(); if (isFollowPlay_) curStepNum_ = 0; } void BambooTracker::startPlayFromStart() { playback_->startPlayFromStart(); startPlay(); if (isFollowPlay_) { curOrderNum_ = 0; curStepNum_ = 0; } } void BambooTracker::startPlayPattern() { playback_->startPlayPattern(curOrderNum_); startPlay(); if (isFollowPlay_) curStepNum_ = 0; } void BambooTracker::startPlayFromCurrentStep() { playback_->startPlayFromPosition(curOrderNum_, curStepNum_); startPlay(); } bool BambooTracker::startPlayFromMarker() { Song& song = mod_->getSong(curSongNum_); if (mkOrder_ != -1 && mkOrder_ < static_cast(song.getOrderSize()) && mkStep_ != -1 && mkStep_ < static_cast(song.getPatternSizeFromOrderNumber(mkOrder_))) { playback_->startPlayFromPosition(mkOrder_, mkStep_); startPlay(); return true; } return false; } void BambooTracker::playStep() { playback_->playStep(curOrderNum_, curStepNum_); for (auto& pair : muteState_) { for (size_t i = 0; i < pair.second.size(); ++i) { opnaCtrl_->setMuteState(pair.first, static_cast(i), pair.second[i]); } } } void BambooTracker::startPlay() { jamMan_->polyphonic(false); for (auto& pair : muteState_) { for (size_t i = 0; i < pair.second.size(); ++i) { opnaCtrl_->setMuteState(pair.first, static_cast(i), pair.second[i]); } } } void BambooTracker::stopPlaySong() { playback_->stopPlaySong(); jamMan_->polyphonic(true); for (auto& pair : muteState_) { for (size_t i = 0; i < pair.second.size(); ++i) { opnaCtrl_->setMuteState(pair.first, static_cast(i), false); } } } bool BambooTracker::isPlaySong() const { return playback_->isPlaySong(); } void BambooTracker::setTrackMuteState(int trackNum, bool isMute) { auto& ta = songStyle_.trackAttribs[static_cast(trackNum)]; muteState_.at(ta.source).at(static_cast(ta.channelInSource)) = isMute; if (isPlaySong()) opnaCtrl_->setMuteState(ta.source, ta.channelInSource, isMute); } bool BambooTracker::isMute(int trackNum) { auto& ta = songStyle_.trackAttribs[static_cast(trackNum)]; return muteState_.at(ta.source).at(ta.channelInSource); } void BambooTracker::setFollowPlay(bool isFollowed) { isFollowPlay_ = isFollowed; if (isFollowed) { int odr = playback_->getPlayingOrderNumber(); if (odr >= 0) { curOrderNum_ = odr; curStepNum_ = playback_->getPlayingStepNumber(); } } } bool BambooTracker::isFollowPlay() const { return isFollowPlay_; } int BambooTracker::getPlayingOrderNumber() const { return playback_->getPlayingOrderNumber(); } int BambooTracker::getPlayingStepNumber() const { return playback_->getPlayingStepNumber(); } void BambooTracker::setMarker(int order, int step) { if (mkOrder_ == order && mkStep_ == step) { mkOrder_ = -1; mkStep_ = -1; } else { mkOrder_ = order; mkStep_ = step; } } int BambooTracker::getMarkerOrder() const { return mkOrder_; } int BambooTracker::getMarkerStep() const { return mkStep_; } /********** Export **********/ namespace { void checkNextPositionOfLastStepAndStepSize(Song& song, int& endOrder, int& endStep) { endOrder = 0; endStep = 0; std::vector attribs = song.getTrackAttributes(); std::unordered_set orderStepMap; int lastOrder = static_cast(song.getOrderSize()) - 1; for (int curOrder = 0; !orderStepMap.count(curOrder); curOrder = endOrder) { orderStepMap.insert(curOrder); // Arrived flag // Default next order endOrder = (endOrder + 1) % (lastOrder + 1); endStep = 0; // Check jump effect for (auto attrib : attribs) { Step& step = song.getTrack(attrib.number).getPatternFromOrderNumber(curOrder) .getStep(static_cast(song.getPatternSizeFromOrderNumber(curOrder)) - 1); for (int i = 0; i < Step::N_EFFECT; ++i) { Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); switch (eff.type) { case EffectType::PositionJump: if (eff.value <= lastOrder) { endOrder = eff.value; endStep = 0; } break; case EffectType::SongEnd: endOrder = -1; endStep = -1; return; case EffectType::PatternBreak: if (curOrder == lastOrder && eff.value < static_cast(song.getPatternSizeFromOrderNumber(0))) { endOrder = 0; endStep = eff.value; } else if (eff.value < static_cast(song.getPatternSizeFromOrderNumber(curOrder + 1))) { endOrder = curOrder + 1; endStep = eff.value; } break; default: break; } } } } } } bool BambooTracker::exportToWav(io::WavContainer& container, int loopCnt, ExportCancellCallback checkFunc) { int tmpRate = opnaCtrl_->getRate(); opnaCtrl_->setRate(static_cast(container.getSampleRate())); size_t sampCnt = static_cast(opnaCtrl_->getRate() * opnaCtrl_->getDuration() / 1000); size_t intrCnt = static_cast(opnaCtrl_->getRate()) / mod_->getTickFrequency(); size_t intrCntRest = 0; std::vector buf(sampCnt << 1); int endOrder, endStep; checkNextPositionOfLastStepAndStepSize(mod_->getSong(curSongNum_), endOrder, endStep); bool endFlag = false; bool tmpFollow = std::exchange(isFollowPlay_, false); startPlayFromStart(); while (true) { size_t sampCntRest = sampCnt; while (sampCntRest) { if (!intrCntRest) { // Interruption intrCntRest = intrCnt; // Set counts to next interruption if (!streamCountUp()) { if (checkFunc()) { // Update lambda function stopPlaySong(); isFollowPlay_ = tmpFollow; return false; } int playOrder = playback_->getPlayingOrderNumber(); int playStep = playback_->getPlayingStepNumber(); if ((playOrder == -1 && playStep == -1) || (playOrder == endOrder && playStep == endStep && !(loopCnt--))){ endFlag = true; break; } } } size_t count = std::min(intrCntRest, sampCntRest); sampCntRest -= count; intrCntRest -= count; opnaCtrl_->getStreamSamples(buf.data(), count); container.appendSample(buf.data(), count); } if (endFlag) break; } stopPlaySong(); isFollowPlay_ = tmpFollow; opnaCtrl_->setRate(tmpRate); return true; } bool BambooTracker::exportToVgm(io::BinaryContainer& container, int target, bool gd3TagEnabled, const io::GD3Tag& tag, ExportCancellCallback checkFunc) { int tmpRate = opnaCtrl_->getRate(); opnaCtrl_->setRate(44100); double dblIntrCnt = 44100.0 / static_cast(mod_->getTickFrequency()); size_t intrCnt = static_cast(dblIntrCnt); double intrCntDiff = dblIntrCnt - intrCnt; double intrCntRest = 0; int loopOrder, loopStep; checkNextPositionOfLastStepAndStepSize(mod_->getSong(curSongNum_), loopOrder, loopStep); bool loopFlag = (loopOrder != -1); int endCnt = (loopOrder == -1) ? 0 : 1; bool tmpFollow = std::exchange(isFollowPlay_, false); uint32_t loopPoint = 0; uint32_t loopPointSamples = 0; auto exCntr = std::make_shared(target, mod_->getTickFrequency()); // Set ADPCM opnaCtrl_->clearSamplesADPCM(); std::vector rom; for (auto sampNum : instMan_->getSampleADPCMValidIndices()) { std::vector&& sample = instMan_->getSampleADPCMRawSample(sampNum); size_t startAddr, stopAddr; if (opnaCtrl_->storeSampleADPCM(sample, startAddr, stopAddr)) { instMan_->setSampleADPCMStartAddress(sampNum, startAddr); instMan_->setSampleADPCMStopAddress(sampNum, stopAddr); rom.resize((stopAddr + 1) << 5); std::copy(sample.begin(), sample.end(), rom.begin() + static_cast(startAddr << 5)); } } exCntr->setDataBlock(std::move(rom)); opnaCtrl_->setExportContainer(exCntr); startPlayFromStart(); exCntr->forceMoveLoopPoint(); while (true) { if (!streamCountUp()) { if (checkFunc()) { // Update lambda function stopPlaySong(); isFollowPlay_ = tmpFollow; return false; } int playOrder = playback_->getPlayingOrderNumber(); int playStep = playback_->getPlayingStepNumber(); if (playOrder == loopOrder && playStep == loopStep && !(endCnt--)) break; if (loopFlag && loopOrder == playOrder && loopStep == playStep) { loopPoint = exCntr->setLoopPoint(); loopPointSamples = exCntr->getSampleLength(); } } intrCntRest += intrCntDiff; size_t extraIntrCnt = static_cast(intrCntRest); intrCntRest -= extraIntrCnt; exCntr->elapse(intrCnt + extraIntrCnt); } opnaCtrl_->setExportContainer(); stopPlaySong(); isFollowPlay_ = tmpFollow; opnaCtrl_->setRate(tmpRate); try { io::writeVgm(container, target, exCntr->getData(), CHIP_CLOCK, mod_->getTickFrequency(), loopFlag, loopPoint, exCntr->getSampleLength() - loopPointSamples, exCntr->getSampleLength(), gd3TagEnabled, tag); return true; } catch (...) { throw; } } bool BambooTracker::exportToS98(io::BinaryContainer& container, int target, bool tagEnabled, const io::S98Tag& tag, int rate, ExportCancellCallback checkFunc) { int tmpRate = opnaCtrl_->getRate(); opnaCtrl_->setRate(rate); double dblIntrCnt = static_cast(rate) / static_cast(mod_->getTickFrequency()); size_t intrCnt = static_cast(dblIntrCnt); double intrCntDiff = dblIntrCnt - intrCnt; double intrCntRest = 0; int loopOrder, loopStep; checkNextPositionOfLastStepAndStepSize(mod_->getSong(curSongNum_), loopOrder, loopStep); bool loopFlag = (loopOrder != -1); int endCnt = (loopOrder == -1) ? 0 : 1; bool tmpFollow = std::exchange(isFollowPlay_, false); uint32_t loopPoint = 0; auto exCntr = std::make_shared(target); opnaCtrl_->setExportContainer(exCntr); startPlayFromStart(); assignSampleADPCMRawSamples(); exCntr->forceMoveLoopPoint(); while (true) { exCntr->getData(); // Set wait counts if (!streamCountUp()) { if (checkFunc()) { // Update lambda function stopPlaySong(); isFollowPlay_ = tmpFollow; return false; } int playOrder = playback_->getPlayingOrderNumber(); int playStep = playback_->getPlayingStepNumber(); if (playOrder == loopOrder && playStep == loopStep && !(endCnt--)) break; if (loopFlag && loopOrder == playOrder && loopStep == playStep) { loopPoint = exCntr->setLoopPoint(); } } intrCntRest += intrCntDiff; size_t extraIntrCnt = static_cast(intrCntRest); intrCntRest -= extraIntrCnt; exCntr->elapse(intrCnt + extraIntrCnt); } opnaCtrl_->setExportContainer(); stopPlaySong(); isFollowPlay_ = tmpFollow; opnaCtrl_->setRate(tmpRate); try { io::writeS98(container, target, exCntr->getData(), CHIP_CLOCK, static_cast(rate), loopFlag, loopPoint, tagEnabled, tag); return true; } catch (...) { throw; } } /********** Real chip interface **********/ void BambooTracker::useSCCI(scci::SoundInterfaceManager* manager) { opnaCtrl_->useSCCI(manager); } void BambooTracker::useC86CTL(C86ctlBase* base) { opnaCtrl_->useC86CTL(base); } RealChipInterface BambooTracker::getRealChipinterface() const { if (opnaCtrl_->isUsedSCCI()) return RealChipInterface::SCCI; else if (opnaCtrl_->isUsedC86CTL()) return RealChipInterface::C86CTL; else return RealChipInterface::NONE; } /********** Stream events **********/ int BambooTracker::streamCountUp() { int state = playback_->streamCountUp(); if (!state && isFollowPlay_ && !playback_->isPlayingStep()) { // Step int odr = playback_->getPlayingOrderNumber(); if (odr >= 0) { curOrderNum_ = odr; curStepNum_ = playback_->getPlayingStepNumber(); } } return state; } void BambooTracker::getStreamSamples(int16_t *container, size_t nSamples) { opnaCtrl_->getStreamSamples(container, nSamples); } void BambooTracker::killSound() { jamMan_->reset(); opnaCtrl_->reset(); } /********** Stream details **********/ int BambooTracker::getStreamRate() const { return opnaCtrl_->getRate(); } void BambooTracker::setStreamRate(int rate) { opnaCtrl_->setRate(rate); } int BambooTracker::getStreamDuration() const { return opnaCtrl_->getDuration(); } void BambooTracker::setStreamDuration(int duration) { opnaCtrl_->setDuration(duration); } int BambooTracker::getStreamTempo() const { return tickCounter_->getTempo(); } int BambooTracker::getStreamSpeed() const { return tickCounter_->getSpeed(); } bool BambooTracker::getStreamGrooveEnabled() const { return tickCounter_->getGrooveEnabled(); } void BambooTracker::setMasterVolume(int percentage) { opnaCtrl_->setMasterVolume(percentage); } void BambooTracker::setMasterVolumeFM(double dB) { opnaCtrl_->setMasterVolumeFM(dB); } void BambooTracker::setMasterVolumeSSG(double dB) { opnaCtrl_->setMasterVolumeSSG(dB); } /********** Module details **********/ /*----- Module -----*/ void BambooTracker::makeNewModule() { stopPlaySong(); clearAllInstrument(); opnaCtrl_->reset(); mod_ = std::make_shared(); tickCounter_->setInterruptRate(mod_->getTickFrequency()); setCurrentSongNumber(0); curInstNum_ = -1; clearCommandHistory(); } void BambooTracker::loadModule(io::BinaryContainer& container) { makeNewModule(); std::exception_ptr ep; try { io::ModuleIO::getInstance().loadModule(container, mod_, instMan_); } catch (...) { ep = std::current_exception(); } tickCounter_->setInterruptRate(mod_->getTickFrequency()); setCurrentSongNumber(0); clearCommandHistory(); if (ep) std::rethrow_exception(ep); } void BambooTracker::saveModule(io::BinaryContainer& container) { io::ModuleIO::getInstance().saveModule(container, mod_, instMan_); } void BambooTracker::setModulePath(const std::string& path) { mod_->setFilePath(path); } std::string BambooTracker::getModulePath() const { return mod_->getFilePath(); } void BambooTracker::setModuleTitle(const std::string& title) { mod_->setTitle(title); } std::string BambooTracker::getModuleTitle() const { return mod_->getTitle(); } void BambooTracker::setModuleAuthor(const std::string& author) { mod_->setAuthor(author); } std::string BambooTracker::getModuleAuthor() const { return mod_->getAuthor(); } void BambooTracker::setModuleCopyright(const std::string& copyright) { mod_->setCopyright(copyright); } std::string BambooTracker::getModuleCopyright() const { return mod_->getCopyright(); } void BambooTracker::setModuleComment(const std::string& comment) { mod_->setComment(comment); } std::string BambooTracker::getModuleComment() const { return mod_->getComment(); } void BambooTracker::setModuleTickFrequency(unsigned int freq) { mod_->setTickFrequency(freq); tickCounter_->setInterruptRate(freq); } unsigned int BambooTracker::getModuleTickFrequency() const { return mod_->getTickFrequency(); } void BambooTracker::setModuleStepHighlight1Distance(size_t dist) { mod_->setStepHighlight1Distance(dist); } size_t BambooTracker::getModuleStepHighlight1Distance() const { return mod_->getStepHighlight1Distance(); } void BambooTracker::setModuleStepHighlight2Distance(size_t dist) { mod_->setStepHighlight2Distance(dist); } size_t BambooTracker::getModuleStepHighlight2Distance() const { return mod_->getStepHighlight2Distance(); } void BambooTracker::setModuleMixerType(MixerType type) { mod_->setMixerType(type); } MixerType BambooTracker::getModuleMixerType() const { return mod_->getMixerType(); } void BambooTracker::setModuleCustomMixerFMLevel(double level) { mod_->setCustomMixerFMLevel(level); } double BambooTracker::getModuleCustomMixerFMLevel() const { return mod_->getCustomMixerFMLevel(); } void BambooTracker::setModuleCustomMixerSSGLevel(double level) { mod_->setCustomMixerSSGLevel(level); } double BambooTracker::getModuleCustomMixerSSGLevel() const { return mod_->getCustomMixerSSGLevel(); } size_t BambooTracker::getGrooveCount() const { return mod_->getGrooveCount(); } void BambooTracker::setGroove(int num, const std::vector& seq) { mod_->setGroove(num, seq); } void BambooTracker::setGrooves(const std::vector>& seqs) { mod_->setGrooves(seqs); } std::vector BambooTracker::getGroove(int num) const { return mod_->getGroove(num); } void BambooTracker::clearUnusedPatterns() { mod_->clearUnusedPatterns(); } std::unordered_map BambooTracker::replaceDuplicateInstrumentsInPatterns() { std::unordered_map map = instMan_->getDuplicateInstrumentMap(); mod_->replaceDuplicateInstrumentsInPatterns(map); return map; } void BambooTracker::clearUnusedADPCMSamples() { instMan_->clearUnusedSamplesADPCM(); } /*----- Song -----*/ void BambooTracker::setSongTitle(int songNum, const std::string& title) { mod_->getSong(songNum).setTitle(title); } std::string BambooTracker::getSongTitle(int songNum) const { return mod_->getSong(songNum).getTitle(); } void BambooTracker::setSongTempo(int songNum, int tempo) { mod_->getSong(songNum).setTempo(tempo); if (curSongNum_ == songNum) tickCounter_->setTempo(tempo); } int BambooTracker::getSongTempo(int songNum) const { return mod_->getSong(songNum).getTempo(); } void BambooTracker::setSongGroove(int songNum, int groove) { mod_->getSong(songNum).setGroove(groove); tickCounter_->setGroove(mod_->getGroove(groove)); } int BambooTracker::getSongGroove(int songNum) const { return mod_->getSong(songNum).getGroove(); } void BambooTracker::toggleTempoOrGrooveInSong(int songNum, bool isTempo) { mod_->getSong(songNum).toggleTempoOrGroove(isTempo); tickCounter_->setGrooveState(isTempo ? GrooveState::Invalid : GrooveState::ValidByGlobal); } bool BambooTracker::isUsedTempoInSong(int songNum) const { return mod_->getSong(songNum).isUsedTempo(); } SongStyle BambooTracker::getSongStyle(int songNum) const { return mod_->getSong(songNum).getStyle(); } void BambooTracker::changeSongType(int songNum, SongType type) { mod_->getSong(songNum).changeType(type); } void BambooTracker::setSongSpeed(int songNum, int speed) { mod_->getSong(songNum).setSpeed(speed); if (curSongNum_ == songNum) tickCounter_->setSpeed(speed); } int BambooTracker::getSongSpeed(int songNum) const { return mod_->getSong(songNum).getSpeed(); } size_t BambooTracker::getSongCount() const { return mod_->getSongCount(); } void BambooTracker::addSong(SongType songType, const std::string& title) { mod_->addSong(songType, title); } void BambooTracker::sortSongs(const std::vector& numbers) { mod_->sortSongs(std::move(numbers)); } void BambooTracker::transposeSong(int songNum, int seminotes, const std::vector& excludeInsts) { mod_->getSong(songNum).transpose(seminotes, excludeInsts); } void BambooTracker::swapTracks(int songNum, int track1, int track2) { mod_->getSong(songNum).swapTracks(track1, track2); } double BambooTracker::getApproximateSongLength(int songNum) const { SongLengthCalculator calc(*mod_.get(), songNum); return calc.approximateLengthBySecond(); } size_t BambooTracker::getTotalStepCount(int songNum, size_t loopCnt) const { size_t introSize, loopSize; SongLengthCalculator calc(*mod_.get(), songNum); calc.totalStepCount(introSize, loopSize); return introSize + loopSize * loopCnt; } /*----- Bookmark -----*/ void BambooTracker::addBookmark(int songNum, const std::string& name, int order, int step) { mod_->getSong(songNum).addBookmark(name, order, step); } void BambooTracker::changeBookmark(int songNum, int i, const std::string& name, int order, int step) { mod_->getSong(songNum).changeBookmark(i, name, order, step); } void BambooTracker::removeBookmark(int songNum, int i) { mod_->getSong(songNum).removeBookmark(i); } void BambooTracker::clearBookmark(int songNum) { mod_->getSong(songNum).clearBookmark(); } void BambooTracker::swapBookmarks(int songNum, int a, int b) { mod_->getSong(songNum).swapBookmarks(a, b); } void BambooTracker::sortBookmarkByPosition(int songNum) { mod_->getSong(songNum).sortBookmarkByPosition(); } void BambooTracker::sortBookmarkByName(int songNum) { mod_->getSong(songNum).sortBookmarkByName(); } Bookmark BambooTracker::getBookmark(int songNum, int i) const { return mod_->getSong(songNum).getBookmark(i); } std::vector BambooTracker::findBookmarks(int songNum, int order, int step) { return mod_->getSong(songNum).findBookmarks(order, step); } Bookmark BambooTracker::getPreviousBookmark(int songNum, int order, int step) { return mod_->getSong(songNum).getPreviousBookmark(order, step); } Bookmark BambooTracker::getNextBookmark(int songNum, int order, int step) { return mod_->getSong(songNum).getNextBookmark(order, step); } size_t BambooTracker::getBookmarkSize(int songNum) const { return mod_->getSong(songNum).getBookmarkSize(); } /*----- Track -----*/ void BambooTracker::setEffectDisplayWidth(int songNum, int trackNum, size_t w) { mod_->getSong(songNum).getTrack(trackNum).setEffectDisplayWidth(w); } size_t BambooTracker::getEffectDisplayWidth(int songNum, int trackNum) const { return mod_->getSong(songNum).getTrack(trackNum).getEffectDisplayWidth(); } /*----- Order -----*/ std::vector BambooTracker::getOrderData(int songNum, int orderNum) const { return mod_->getSong(songNum).getOrderData(orderNum); } void BambooTracker::setOrderPatternDigit(int songNum, int trackNum, int orderNum, int patternNum, bool secondEntry) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, patternNum, secondEntry)); } void BambooTracker::insertOrderBelow(int songNum, int orderNum) { comMan_.invoke(std::make_unique(mod_, songNum, orderNum)); } void BambooTracker::deleteOrder(int songNum, int orderNum) { comMan_.invoke(std::make_unique(mod_, songNum, orderNum)); } void BambooTracker::pasteOrderCells(int songNum, int beginTrack, int beginOrder, const std::vector>& cells) { // Arrange data size_t w = std::min(cells[0].size(), songStyle_.trackAttribs.size() - static_cast(beginTrack)); size_t h = std::min(cells.size(), getOrderSize(songNum) - static_cast(beginOrder)); std::vector> d(h); for (size_t i = 0; i < h; ++i) d[i].assign(cells[i].begin(), cells[i].begin() + w); comMan_.invoke(std::make_unique(mod_, songNum, beginTrack, beginOrder, std::move(d))); } void BambooTracker::duplicateOrder(int songNum, int orderNum) { comMan_.invoke(std::make_unique(mod_, songNum, orderNum)); } void BambooTracker::MoveOrder(int songNum, int orderNum, bool isUp) { comMan_.invoke(std::make_unique(mod_, songNum, orderNum, isUp)); } void BambooTracker::clonePatterns(int songNum, int beginOrder, int beginTrack, int endOrder, int endTrack) { comMan_.invoke(std::make_unique(mod_, songNum, beginOrder, beginTrack, endOrder, endTrack)); } void BambooTracker::cloneOrder(int songNum, int orderNum) { comMan_.invoke(std::make_unique(mod_, songNum, orderNum)); } size_t BambooTracker::getOrderSize(int songNum) const { return mod_->getSong(songNum).getOrderSize(); } bool BambooTracker::canAddNewOrder(int songNum) const { return mod_->getSong(songNum).canAddNewOrder(); } /*----- Pattern -----*/ int BambooTracker::getStepNoteNumber(int songNum, int trackNum, int orderNum, int stepNum) const { return mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum) .getStep(stepNum).getNoteNumber(); } void BambooTracker::setStepNote(int songNum, int trackNum, int orderNum, int stepNum, const Note& note, bool instMask, bool volMask) { int nn = Note(note).getNoteNumber(); SoundSource src = songStyle_.trackAttribs.at(static_cast(trackNum)).source; int in = -1; if (!instMask && curInstNum_ != -1 && (src == instMan_->getInstrumentSharedPtr(curInstNum_)->getSoundSource())) { in = curInstNum_; } bool fmReversed = (volFMReversed_ && src == SoundSource::FM); comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, nn, instMask, in, volMask, curVolume_, fmReversed)); } void BambooTracker::setStepKeyOff(int songNum, int trackNum, int orderNum, int stepNum) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum)); } void BambooTracker::setEchoBufferAccess(int songNum, int trackNum, int orderNum, int stepNum, int bufNum) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, bufNum)); } void BambooTracker::eraseStepNote(int songNum, int trackNum, int orderNum, int stepNum) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum)); } int BambooTracker::getStepInstrument(int songNum, int trackNum, int orderNum, int stepNum) const { return mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum) .getStep(stepNum).getInstrumentNumber(); } void BambooTracker::setStepInstrumentDigit(int songNum, int trackNum, int orderNum, int stepNum, int instNum, bool secondEntry) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, instNum, secondEntry)); } void BambooTracker::eraseStepInstrument(int songNum, int trackNum, int orderNum, int stepNum) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum)); } int BambooTracker::getStepVolume(int songNum, int trackNum, int orderNum, int stepNum) const { return mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum) .getStep(stepNum).getVolume(); } int BambooTracker::setStepVolumeDigit(int songNum, int trackNum, int orderNum, int stepNum, int volume, bool secondEntry) { bool fmReversed = (volFMReversed_ && songStyle_.trackAttribs.at(static_cast(trackNum)).source == SoundSource::FM); comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, volume, fmReversed, secondEntry)); curVolume_ = mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum).getStep(stepNum).getVolume(); if (fmReversed) curVolume_ = effect_utils::reverseFmVolume(curVolume_); return curVolume_; } void BambooTracker::eraseStepVolume(int songNum, int trackNum, int orderNum, int stepNum) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum)); } std::string BambooTracker::getStepEffectID(int songNum, int trackNum, int orderNum, int stepNum, int n) const { return mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum) .getStep(stepNum).getEffectId(n); } void BambooTracker::setStepEffectIDCharacter(int songNum, int trackNum, int orderNum, int stepNum, int n, const std::string& id, bool fillValue00, bool secondEntry) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, n, id, fillValue00, secondEntry)); } int BambooTracker::getStepEffectValue(int songNum, int trackNum, int orderNum, int stepNum, int n) const { return mod_->getSong(songNum).getTrack(trackNum).getPatternFromOrderNumber(orderNum) .getStep(stepNum).getEffectValue(n); } void BambooTracker::setStepEffectValueDigit(int songNum, int trackNum, int orderNum, int stepNum, int n, int value, EffectDisplayControl ctrl, bool secondEntry) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, n, value, ctrl, secondEntry)); } void BambooTracker::eraseStepEffect(int songNum, int trackNum, int orderNum, int stepNum, int n) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, n)); } void BambooTracker::eraseStepEffectValue(int songNum, int trackNum, int orderNum, int stepNum, int n) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum, n)); } void BambooTracker::insertStep(int songNum, int trackNum, int orderNum, int stepNum) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum)); } void BambooTracker::deletePreviousStep(int songNum, int trackNum, int orderNum, int stepNum) { comMan_.invoke(std::make_unique(mod_, songNum, trackNum, orderNum, stepNum)); } namespace { std::vector> arrangePatternDataCells( size_t trackCnt, size_t ptnSize, int beginTrack, int beginColmn, int beginStep, const std::vector>& cells) { size_t w = (trackCnt - static_cast(beginTrack) - 1) * 11 + (11 - static_cast(beginColmn)); size_t h = ptnSize - static_cast(beginStep); size_t width = std::min(cells.at(0).size(), w); size_t height = std::min(cells.size(), h); std::vector> d(height); for (size_t i = 0; i < height; ++i) { d[i].assign(cells[i].begin(), cells[i].begin() + width); } return d; } } void BambooTracker::pastePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector>& cells) { auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } void BambooTracker::pasteMixPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector >& cells) { auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } void BambooTracker::pasteOverwritePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector >& cells) { auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } void BambooTracker::pasteInsertPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector >& cells) { auto d = arrangePatternDataCells(songStyle_.trackAttribs.size(), getPatternSizeFromOrderNumber(songNum, beginOrder), beginTrack, beginColmn, beginStep, cells); comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, std::move(d))); } void BambooTracker::erasePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, endTrack, endColmn, endStep)); } void BambooTracker::transposeNoteInPattern(int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int seminote) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginOrder, beginStep, endTrack, endStep, seminote)); } void BambooTracker::changeValuesInPattern(int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep, int value) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColumn, beginOrder, beginStep, endTrack, endColumn, endStep, value, volFMReversed_)); } void BambooTracker::expandPattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, endTrack, endColmn, endStep)); } void BambooTracker::shrinkPattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, endTrack, endColmn, endStep)); } void BambooTracker::interpolatePattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, endTrack, endColmn, endStep)); } void BambooTracker::reversePattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginColmn, beginOrder, beginStep, endTrack, endColmn, endStep)); } void BambooTracker::replaceInstrumentInPattern(int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int newInstNum) { comMan_.invoke(std::make_unique( mod_, songNum, beginTrack, beginOrder, beginStep, endTrack, endStep, newInstNum)); } size_t BambooTracker::getPatternSizeFromOrderNumber(int songNum, int orderNum) const { return mod_->getSong(songNum).getPatternSizeFromOrderNumber(orderNum); } void BambooTracker::setDefaultPatternSize(int songNum, size_t size) { mod_->getSong(songNum).setDefaultPatternSize(size); playback_->checkPlayPosition(static_cast(size)); } size_t BambooTracker::getDefaultPatternSize(int songNum) const { return mod_->getSong(songNum).getDefaultPatternSize(); } void BambooTracker::getOutputHistory(int16_t* container) { opnaCtrl_->getOutputHistory(container); } BambooTracker-0.4.6/BambooTracker/bamboo_tracker.hpp000066400000000000000000000617131401124043500224340ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include #include #include #include "jamming.hpp" #include "instrument.hpp" #include "module.hpp" #include "command/command_manager.hpp" #include "chip/c86ctl/c86ctl_wrapper.hpp" #include "chip/scci/scci.hpp" #include "io/binary_container.hpp" #include "io/export_io.hpp" #include "io/wav_container.hpp" #include "bamboo_tracker_defs.hpp" #include "enum_hash.hpp" class Configuration; enum class EffectDisplayControl; enum class RealChipInterface; class AbstractBank; class OPNAController; class PlaybackManager; class TickCounter; class BambooTracker { public: explicit BambooTracker(std::weak_ptr config); ~BambooTracker(); // Change confuguration void changeConfiguration(std::weak_ptr config); // Current octave void setCurrentOctave(int octave); int getCurrentOctave() const; // Current volume void setCurrentVolume(int volume); int getCurrentVolume() const; // Current track void setCurrentTrack(int num); TrackAttribute getCurrentTrackAttribute() const; // Current instrument void setCurrentInstrument(int n); int getCurrentInstrumentNumber() const; // Instrument edit void addInstrument(int num, InstrumentType type, const std::string& name); void removeInstrument(int num); std::unique_ptr getInstrument(int num); void cloneInstrument(int num, int refNum); void deepCloneInstrument(int num, int refNum); void swapInstruments(int a, int b, bool patternChange); void loadInstrument(io::BinaryContainer& container, const std::string& path, int instNum); void saveInstrument(io::BinaryContainer& container, int instNum); void importInstrument(const AbstractBank &bank, size_t index, int instNum); void exportInstruments(io::BinaryContainer& container, const std::vector& instNums); int findFirstFreeInstrumentNumber() const; void setInstrumentName(int num, const std::string& name); void clearAllInstrument(); std::vector getInstrumentIndices() const; std::vector getUnusedInstrumentIndices() const; void clearUnusedInstrumentProperties(); std::vector getInstrumentNames() const; //--- FM void setEnvelopeFMParameter(int envNum, FMEnvelopeParameter param, int value); void setEnvelopeFMOperatorEnable(int envNum, int opNum, bool enable); void setInstrumentFMEnvelope(int instNum, int envNum); std::multiset getEnvelopeFMUsers(int envNum) const; void setLFOFMParameter(int lfoNum, FMLFOParameter param, int value); void setInstrumentFMLFOEnabled(int instNum, bool enabled); void setInstrumentFMLFO(int instNum, int lfoNum); std::multiset getLFOFMUsers(int lfoNum) const; void addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data); void removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum); void setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data); void addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop); void removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end); void changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum); void setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release); void setInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param, int opSeqNum); void setInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param, bool enabled); std::multiset getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const; void setArpeggioFMType(int arpNum, SequenceType type); void addArpeggioFMSequenceData(int arpNum, int data); void removeArpeggioFMSequenceData(int arpNum); void setArpeggioFMSequenceData(int arpNum, int cnt, int data); void addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop); void removeArpeggioFMLoop(int arpNum, int begin, int end); void changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearArpeggioFMLoops(int arpNum); void setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentFMArpeggio(int instNum, FMOperatorType op, int arpNum); void setInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op, bool enabled); std::multiset getArpeggioFMUsers(int arpNum) const; void setPitchFMType(int ptNum, SequenceType type); void addPitchFMSequenceData(int ptNum, int data); void removePitchFMSequenceData(int ptNum); void setPitchFMSequenceData(int ptNum, int cnt, int data); void addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop); void removePitchFMLoop(int ptNum, int begin, int end); void changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearPitchFMLoops(int ptNum); void setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release); void setInstrumentFMPitch(int instNum, FMOperatorType op, int ptNum); void setInstrumentFMPitchEnabled(int instNum, FMOperatorType op, bool enabled); std::multiset getPitchFMUsers(int ptNum) const; void setInstrumentFMEnvelopeResetEnabled(int instNum, FMOperatorType op, bool enabled); //--- SSG void addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data); void removeWaveformSSGSequenceData(int wfNum); void setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data); void addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop); void removeWaveformSSGLoop(int wfNum, int begin, int end); void changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearWaveformSSGLoops(int wfNum); void setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release); void setInstrumentSSGWaveform(int instNum, int wfNum); void setInstrumentSSGWaveformEnabled(int instNum, bool enabled); std::multiset getWaveformSSGUsers(int wfNum) const; void addToneNoiseSSGSequenceData(int tnNum, int data); void removeToneNoiseSSGSequenceData(int tnNum); void setToneNoiseSSGSequenceData(int tnNum, int cnt, int data); void addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop); void removeToneNoiseSSGLoop(int tnNum, int begin, int end); void changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearToneNoiseSSGLoops(int tnNum); void setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release); void setInstrumentSSGToneNoise(int instNum, int tnNum); void setInstrumentSSGToneNoiseEnabled(int instNum, bool enabled); std::multiset getToneNoiseSSGUsers(int tnNum) const; void addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data); void removeEnvelopeSSGSequenceData(int envNum); void setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data); void addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop); void removeEnvelopeSSGLoop(int envNum, int begin, int end); void changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearEnvelopeSSGLoops(int envNum); void setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release); void setInstrumentSSGEnvelope(int instNum, int envNum); void setInstrumentSSGEnvelopeEnabled(int instNum, bool enabled); std::multiset getEnvelopeSSGUsers(int envNum) const; void setArpeggioSSGType(int arpNum, SequenceType type); void addArpeggioSSGSequenceData(int arpNum, int data); void removeArpeggioSSGSequenceData(int arpNum); void setArpeggioSSGSequenceData(int arpNum, int cnt, int data); void addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop); void removeArpeggioSSGLoop(int arpNum, int begin, int end); void changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearArpeggioSSGLoops(int arpNum); void setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentSSGArpeggio(int instNum, int arpNum); void setInstrumentSSGArpeggioEnabled(int instNum, bool enabled); std::multiset getArpeggioSSGUsers(int arpNum) const; void setPitchSSGType(int ptNum, SequenceType type); void addPitchSSGSequenceData(int ptNum, int data); void removePitchSSGSequenceData(int ptNum); void setPitchSSGSequenceData(int ptNum, int cnt, int data); void addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop); void removePitchSSGLoop(int ptNum, int begin, int end); void changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearPitchSSGLoops(int ptNum); void setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release); void setInstrumentSSGPitch(int instNum, int ptNum); void setInstrumentSSGPitchEnabled(int instNum, bool enabled); std::multiset getPitchSSGUsers(int ptNum) const; //--- ADPCM size_t getADPCMLimit() const; size_t getADPCMStoredSize() const; void setSampleADPCMRootKeyNumber(int sampNum, int n); int getSampleADPCMRootKeyNumber(int sampNum) const; void setSampleADPCMRootDeltaN(int sampNum, int dn); int getSampleADPCMRootDeltaN(int sampNum) const; void setSampleADPCMRepeatEnabled(int sampNum, bool enabled); bool getSampleADPCMRepeatEnabled(int sampNum) const; void storeSampleADPCMRawSample(int sampNum, const std::vector& sample); void storeSampleADPCMRawSample(int sampNum, std::vector&& sample); std::vector getSampleADPCMRawSample(int sampNum) const; void clearSampleADPCMRawSample(int sampNum); bool assignSampleADPCMRawSamples(); size_t getSampleADPCMStartAddress(int sampNum) const; size_t getSampleADPCMStopAddress(int sampNum) const; void setInstrumentADPCMSample(int instNum, int sampNum); std::multiset getSampleADPCMUsers(int sampNum) const; void addEnvelopeADPCMSequenceData(int envNum, int data); void removeEnvelopeADPCMSequenceData(int envNum); void setEnvelopeADPCMSequenceData(int envNum, int cnt, int data); void addEnvelopeADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop); void removeEnvelopeADPCMLoop(int envNum, int begin, int end); void changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearEnvelopeADPCMLoops(int envNum); void setEnvelopeADPCMRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentADPCMEnvelope(int instNum, int envNum); void setInstrumentADPCMEnvelopeEnabled(int instNum, bool enabled); std::multiset getEnvelopeADPCMUsers(int envNum) const; void setArpeggioADPCMType(int arpNum, SequenceType type); void addArpeggioADPCMSequenceData(int arpNum, int data); void removeArpeggioADPCMSequenceData(int arpNum); void setArpeggioADPCMSequenceData(int arpNum, int cnt, int data); void addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop); void removeArpeggioADPCMLoop(int arpNum, int begin, int end); void changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearArpeggioADPCMLoops(int arpNum); void setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release); void setInstrumentADPCMArpeggio(int instNum, int arpNum); void setInstrumentADPCMArpeggioEnabled(int instNum, bool enabled); std::multiset getArpeggioADPCMUsers(int arpNum) const; void setPitchADPCMType(int ptNum, SequenceType type); void addPitchADPCMSequenceData(int ptNum, int data); void removePitchADPCMSequenceData(int ptNum); void setPitchADPCMSequenceData(int ptNum, int cnt, int data); void addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop); void removePitchADPCMLoop(int ptNum, int begin, int end); void changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearPitchADPCMLoops(int ptNum); void setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release); void setInstrumentADPCMPitch(int instNum, int ptNum); void setInstrumentADPCMPitchEnabled(int instNum, bool enabled); std::multiset getPitchADPCMUsers(int ptNum) const; //--- Drumkit void setInstrumentDrumkitSample(int instNum, int key, int sampNum); void setInstrumentDrumkitSampleEnabled(int instNum, int key, bool enabled); void setInstrumentDrumkitPitch(int instNum, int key, int pitch); // Song edit void setCurrentSongNumber(int num); int getCurrentSongNumber() const; // Order edit int getCurrentOrderNumber() const; void setCurrentOrderNumber(int num); // Pattern edit int getCurrentStepNumber() const; void setCurrentStepNumber(int num); // Undo-Redo void undo(); void redo(); void clearCommandHistory(); // Jam mode void toggleJamMode(); bool isJamMode() const; void jamKeyOn(JamKey key, bool volumeSet); void jamKeyOn(int keyNum, bool volumeSet); void jamKeyOff(JamKey key); void jamKeyOff(int keyNum); void jamKeyOnForced(JamKey key, SoundSource src, bool volumeSet, std::shared_ptr inst = nullptr); void jamKeyOnForced(int keyNum, SoundSource src, bool volumeSet, std::shared_ptr inst = nullptr); void jamKeyOffForced(JamKey key, SoundSource src); void jamKeyOffForced(int keyNum, SoundSource src); bool assignADPCMBeforeForcedJamKeyOn(std::shared_ptr inst, std::unordered_map>& sampAddrs); // Play song void startPlaySong(); void startPlayFromStart(); void startPlayPattern(); void startPlayFromCurrentStep(); bool startPlayFromMarker(); void playStep(); void stopPlaySong(); bool isPlaySong() const; void setTrackMuteState(int trackNum, bool isMute); bool isMute(int trackNum); void setFollowPlay(bool isFollowed); bool isFollowPlay() const; int getPlayingOrderNumber() const; int getPlayingStepNumber() const; void setMarker(int order, int step); int getMarkerOrder() const; int getMarkerStep() const; // Export using ExportCancellCallback = std::function; bool exportToWav(io::WavContainer& container, int loopCnt, ExportCancellCallback checkFunc); bool exportToVgm(io::BinaryContainer& container, int target, bool gd3TagEnabled, const io::GD3Tag& tag, ExportCancellCallback checkFunc); bool exportToS98(io::BinaryContainer& container, int target, bool tagEnabled, const io::S98Tag& tag, int rate, ExportCancellCallback checkFunc); // Real chip interface void useSCCI(scci::SoundInterfaceManager* manager); void useC86CTL(C86ctlBase* base); RealChipInterface getRealChipinterface() const; // Stream events /// 0<: Tick /// 0: Step /// -1: Stop int streamCountUp(); void getStreamSamples(int16_t *container, size_t nSamples); void killSound(); // Stream details int getStreamRate() const; void setStreamRate(int rate); int getStreamDuration() const; void setStreamDuration(int duration); int getStreamTempo() const; int getStreamSpeed() const; bool getStreamGrooveEnabled() const; void setMasterVolume(int percentage); void setMasterVolumeFM(double dB); void setMasterVolumeSSG(double dB); // Module details /*----- Module -----*/ void makeNewModule(); void loadModule(io::BinaryContainer& container); void saveModule(io::BinaryContainer& container); void setModulePath(const std::string& path); std::string getModulePath() const; void setModuleTitle(const std::string& title); std::string getModuleTitle() const; void setModuleAuthor(const std::string& author); std::string getModuleAuthor() const; void setModuleCopyright(const std::string& copyright); std::string getModuleCopyright() const; void setModuleComment(const std::string& comment); std::string getModuleComment() const; void setModuleTickFrequency(unsigned int freq); unsigned int getModuleTickFrequency() const; void setModuleStepHighlight1Distance(size_t dist); size_t getModuleStepHighlight1Distance() const; void setModuleStepHighlight2Distance(size_t dist); size_t getModuleStepHighlight2Distance() const; void setModuleMixerType(MixerType type); MixerType getModuleMixerType() const; void setModuleCustomMixerFMLevel(double level); double getModuleCustomMixerFMLevel() const; void setModuleCustomMixerSSGLevel(double level); double getModuleCustomMixerSSGLevel() const; size_t getGrooveCount() const; void setGroove(int num, const std::vector& seq); void setGrooves(const std::vector>& seqs); std::vector getGroove(int num) const; void clearUnusedPatterns(); std::unordered_map replaceDuplicateInstrumentsInPatterns(); void clearUnusedADPCMSamples(); /*----- Song -----*/ void setSongTitle(int songNum, const std::string& title); std::string getSongTitle(int songNum) const; void setSongTempo(int songNum, int tempo); int getSongTempo(int songNum) const; void setSongGroove(int songNum, int groove); int getSongGroove(int songNum) const; void toggleTempoOrGrooveInSong(int songNum, bool isTempo); bool isUsedTempoInSong(int songNum) const; SongStyle getSongStyle(int songNum) const; void changeSongType(int songNum, SongType type); void setSongSpeed(int songNum, int speed); int getSongSpeed(int songNum) const; size_t getSongCount() const; void addSong(SongType songType, const std::string& title); void sortSongs(const std::vector& numbers); void transposeSong(int songNum, int seminotes, const std::vector& excludeInsts); void swapTracks(int songNum, int track1, int track2); double getApproximateSongLength(int songNum) const; size_t getTotalStepCount(int songNum, size_t loopCnt) const; /*----- Bookmark -----*/ void addBookmark(int songNum, const std::string& name, int order, int step); void changeBookmark(int songNum, int i, const std::string& name, int order, int step); void removeBookmark(int songNum, int i); void clearBookmark(int songNum); void swapBookmarks(int songNum, int a, int b); void sortBookmarkByPosition(int songNum); void sortBookmarkByName(int songNum); Bookmark getBookmark(int songNum, int i) const; std::vector findBookmarks(int songNum, int order, int step); Bookmark getPreviousBookmark(int songNum, int order, int step); Bookmark getNextBookmark(int songNum, int order, int step); size_t getBookmarkSize(int songNum) const; /*----- Track -----*/ void setEffectDisplayWidth(int songNum, int trackNum, size_t w); size_t getEffectDisplayWidth(int songNum, int trackNum) const; /*----- Order -----*/ std::vector getOrderData(int songNum, int orderNum) const; void setOrderPatternDigit(int songNum, int trackNum, int orderNum, int patternNum, bool secondEntry); void insertOrderBelow(int songNum, int orderNum); void deleteOrder(int songNum, int orderNum); void pasteOrderCells(int songNum, int beginTrack, int beginOrder, const std::vector>& cells); void duplicateOrder(int songNum, int orderNum); void MoveOrder(int songNum, int orderNum, bool isUp); void clonePatterns(int songNum, int beginOrder, int beginTrack, int endOrder, int endTrack); void cloneOrder(int songNum, int orderNum); size_t getOrderSize(int songNum) const; bool canAddNewOrder(int songNum) const; /*----- Pattern -----*/ int getStepNoteNumber(int songNum, int trackNum, int orderNum, int stepNum) const; void setStepNote(int songNum, int trackNum, int orderNum, int stepNum, const Note& note, bool instMask, bool volMask); void setStepKeyOff(int songNum, int trackNum, int orderNum, int stepNum); void setEchoBufferAccess(int songNum, int trackNum, int orderNum, int stepNum, int bufNum); void eraseStepNote(int songNum, int trackNum, int orderNum, int stepNum); int getStepInstrument(int songNum, int trackNum, int orderNum, int stepNum) const; void setStepInstrumentDigit(int songNum, int trackNum, int orderNum, int stepNum, int instNum, bool secondEntry); void eraseStepInstrument(int songNum, int trackNum, int orderNum, int stepNum); int getStepVolume(int songNum, int trackNum, int orderNum, int stepNum) const; int setStepVolumeDigit(int songNum, int trackNum, int orderNum, int stepNum, int volume, bool secondEntry); void eraseStepVolume(int songNum, int trackNum, int orderNum, int stepNum); std::string getStepEffectID(int songNum, int trackNum, int orderNum, int stepNum, int n) const; void setStepEffectIDCharacter(int songNum, int trackNum, int orderNum, int stepNum, int n, const std::string& id, bool fillValue00, bool secondEntry); int getStepEffectValue(int songNum, int trackNum, int orderNum, int stepNum, int n) const; void setStepEffectValueDigit(int songNum, int trackNum, int orderNum, int stepNum, int n, int value, EffectDisplayControl ctrl, bool secondEntry); void eraseStepEffect(int songNum, int trackNum, int orderNum, int stepNum, int n); void eraseStepEffectValue(int songNum, int trackNum, int orderNum, int stepNum, int n); void deletePreviousStep(int songNum, int trackNum, int orderNum, int stepNum); void insertStep(int songNum, int trackNum, int orderNum, int stepNum); /// beginColumn /// 0: note /// 1: instrument /// 2: volume /// 3: effect id /// 4: effect value void pastePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector>& cells); void pasteMixPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector>& cells); void pasteOverwritePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector>& cells); void pasteInsertPatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector>& cells); void erasePatternCells(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep); void transposeNoteInPattern(int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int seminote); void changeValuesInPattern(int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep, int value); void expandPattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep); void shrinkPattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep); void interpolatePattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep); void reversePattern(int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, int endTrack, int endColmn, int endStep); void replaceInstrumentInPattern(int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int newInstNum); size_t getPatternSizeFromOrderNumber(int songNum, int orderNum) const; void setDefaultPatternSize(int songNum, size_t size); size_t getDefaultPatternSize(int songNum) const; /*----- Visual -----*/ void getOutputHistory(int16_t* container); private: CommandManager comMan_; std::shared_ptr instMan_; std::unique_ptr jamMan_; std::shared_ptr opnaCtrl_; std::shared_ptr tickCounter_; std::unique_ptr playback_; std::shared_ptr mod_; // Current status int curOctave_; // 0-7 int curSongNum_; SongStyle songStyle_; int curTrackNum_; int curOrderNum_, curStepNum_; /// -1: not set int curInstNum_; int curVolume_; bool volFMReversed_; std::unordered_map> muteState_; int mkOrder_, mkStep_; bool isFollowPlay_; bool storeOnlyUsedSamples_; // Jam mode void funcJamKeyOn(JamKey key, int keyNum, const TrackAttribute& attrib, bool volumeSet, std::shared_ptr inst = nullptr); void funcJamKeyOff(JamKey key, int keyNum, const TrackAttribute& attrib); // Play song void startPlay(); }; BambooTracker-0.4.6/BambooTracker/bamboo_tracker_defs.hpp000066400000000000000000000026441401124043500234330ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once enum class SoundSource : int { FM = 1, SSG = 2, RHYTHM = 4, ADPCM = 8 }; namespace bt_defs { constexpr int OUTPUT_HISTORY_SIZE = 1024; constexpr int NSTEP_FM_VOLUME = 0x80; constexpr int NSTEP_SSG_VOLUME = 0x10; constexpr int NSTEP_RHYTHM_VOLUME = 0x20; constexpr int NSTEP_ADPCM_VOLUME = 0x100; } BambooTracker-0.4.6/BambooTracker/chip/000077500000000000000000000000001401124043500176645ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/chip/c86ctl/000077500000000000000000000000001401124043500207675ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/chip/c86ctl/c86ctl.h000066400000000000000000000101121401124043500222360ustar00rootroot00000000000000/*** c86ctl Copyright (c) 2009-2012, honet. All rights reserved. This software is licensed under the BSD license. honet.kk(at)gmail.com */ #ifdef _WIN32 #ifndef _C86CTL_H #define _C86CTL_H #include #ifdef __cplusplus namespace c86ctl{ #endif /*----------------------------------------------------------------------------*/ /* 定数定義 */ /*----------------------------------------------------------------------------*/ #define C86CTL_ERR_NONE 0 #define C86CTL_ERR_UNKNOWN -1 #define C86CTL_ERR_INVALID_PARAM -2 #define C86CTL_ERR_UNSUPPORTED -3 #define C86CTL_ERR_NODEVICE -1000 #define C86CTL_ERR_NOT_IMPLEMENTED -9999 enum ChipType { CHIP_UNKNOWN = 0, CHIP_OPNA, CHIP_OPM, CHIP_OPN3L, CHIP_OPL3 }; /*----------------------------------------------------------------------------*/ /* 構造体定義 */ /*----------------------------------------------------------------------------*/ struct Devinfo{ char Devname[16]; char Rev; char Serial[15]; }; /*----------------------------------------------------------------------------*/ /* Interface定義 */ /*----------------------------------------------------------------------------*/ /* IRealChipBase {5C457918-E66D-4AC1-8CB5-B91C4704DF79} */ static const GUID IID_IRealChipBase = { 0x5c457918, 0xe66d, 0x4ac1, { 0x8c, 0xb5, 0xb9, 0x1c, 0x47, 0x4, 0xdf, 0x79 } }; interface IRealChipBase : public IUnknown { virtual int __stdcall initialize(void) = 0; virtual int __stdcall deinitialize(void) = 0; virtual int __stdcall getNumberOfChip(void) = 0; virtual HRESULT __stdcall getChipInterface( int id, REFIID riid, void** ppi ) = 0; }; /* IRealChip {F959C007-6B4D-46F3-BB60-9B0897C7E642} */ static const GUID IID_IRealChip = { 0xf959c007, 0x6b4d, 0x46f3, { 0xbb, 0x60, 0x9b, 0x8, 0x97, 0xc7, 0xe6, 0x42 } }; interface IRealChip : public IUnknown { public: virtual int __stdcall reset(void) = 0; virtual void __stdcall out( UINT addr, UCHAR data ) = 0; virtual UCHAR __stdcall in( UINT addr ) = 0; }; /* IRealChip2 {BEFA830A-0DF3-46E4-A79E-FABB78E80357} */ static const GUID IID_IRealChip2 = { 0xbefa830a, 0xdf3, 0x46e4, { 0xa7, 0x9e, 0xfa, 0xbb, 0x78, 0xe8, 0x3, 0x57 } }; interface IRealChip2 : public IRealChip { virtual int __stdcall getChipStatus( UINT addr, UCHAR *status ) = 0; virtual void __stdcall directOut(UINT addr, UCHAR data) = 0; }; /* IGimic {175C7DA0-8AA5-4173-96DA-BB43B8EB8F17} */ static const GUID IID_IGimic = { 0x175c7da0, 0x8aa5, 0x4173, { 0x96, 0xda, 0xbb, 0x43, 0xb8, 0xeb, 0x8f, 0x17 } }; interface IGimic : public IUnknown { virtual int __stdcall getFWVer( UINT *major, UINT *minor, UINT *revision, UINT *build ) = 0; virtual int __stdcall getMBInfo( struct Devinfo *info ) = 0; virtual int __stdcall getModuleInfo( struct Devinfo *info ) = 0; virtual int __stdcall setSSGVolume(UCHAR vol) = 0; virtual int __stdcall getSSGVolume(UCHAR *vol) = 0; virtual int __stdcall setPLLClock(UINT clock) = 0; virtual int __stdcall getPLLClock(UINT *clock) = 0; }; /* IGimic2 {47141A01-15F5-4BF5-9554-CA7AACD54BB8} */ static const GUID IID_IGimic2 = { 0x47141a01, 0x15f5, 0x4bf5, { 0x95, 0x54, 0xca, 0x7a, 0xac, 0xd5, 0x4b, 0xb8 } }; interface IGimic2 : public IGimic { virtual int __stdcall getModuleType( enum ChipType *type ) = 0; }; /*----------------------------------------------------------------------------*/ /* 公開関数定義 */ /*----------------------------------------------------------------------------*/ #ifdef __cplusplus extern "C" { #endif HRESULT WINAPI CreateInstance( REFIID riid, void** ppi ); int WINAPI c86ctl_initialize(void); /* DEPRECATED */ int WINAPI c86ctl_deinitialize(void); /* DEPRECATED */ int WINAPI c86ctl_reset(void); /* DEPRECATED */ void WINAPI c86ctl_out( UINT addr, UCHAR data ); /* DEPRECATED */ UCHAR WINAPI c86ctl_in( UINT addr ); /* DEPRECATED */ #ifdef __cplusplus } } #endif #endif #endif BambooTracker-0.4.6/BambooTracker/chip/c86ctl/c86ctl_wrapper.cpp000066400000000000000000000040711401124043500243400ustar00rootroot00000000000000#include "c86ctl_wrapper.hpp" #include "c86ctl.h" C86ctlBase::C86ctlBase(void (*func)()) : base_(nullptr) { #ifdef _C86CTL_H auto tmpFunc = reinterpret_cast(func); // Avoid MSVC C4191 if (auto createInstance = reinterpret_cast(tmpFunc)) createInstance(c86ctl::IID_IRealChipBase, reinterpret_cast(&base_)); #else (void)func; #endif } bool C86ctlBase::isEmpty() const noexcept { return (base_ == nullptr); } void C86ctlBase::initialize() { #ifdef _C86CTL_H if (base_) base_->initialize(); #endif } void C86ctlBase::deinitialize() { #ifdef _C86CTL_H if (base_) base_->deinitialize(); #endif } int C86ctlBase::getNumberOfChip() { #ifdef _C86CTL_H if (base_) return base_->getNumberOfChip(); #endif return 0; } C86ctlRealChip* C86ctlBase::getChipInterface(int id) { c86ctl::IRealChip2* rc = nullptr; #ifdef _C86CTL_H if (base_) base_->getChipInterface(id, c86ctl::IID_IRealChip2, reinterpret_cast(&rc)); #else (void)id; #endif return (rc ? new C86ctlRealChip(rc) : nullptr); } C86ctlRealChip::C86ctlRealChip(c86ctl::IRealChip2* rc) { #ifdef _C86CTL_H rc_ = rc; #else (void)rc; #endif } C86ctlRealChip::~C86ctlRealChip() { #ifdef _C86CTL_H rc_->Release(); #endif } void C86ctlRealChip::resetChip() { #ifdef _C86CTL_H rc_->reset(); #endif } void C86ctlRealChip::out(uint32_t addr, uint8_t data) { #ifdef _C86CTL_H rc_->out(addr, data); #else (void)addr; (void)data; #endif } C86ctlGimic* C86ctlRealChip::queryInterface() { #ifdef _C86CTL_H c86ctl::IGimic2* gm = nullptr; if (rc_->QueryInterface(c86ctl::IID_IGimic2, reinterpret_cast(&gm)) == S_OK) { c86ctl::ChipType type; gm->getModuleType(&type); if (type == c86ctl::CHIP_OPNA) { return new C86ctlGimic(gm); } gm->Release(); } #endif return nullptr; } C86ctlGimic::C86ctlGimic(c86ctl::IGimic2* gm) { #ifdef _C86CTL_H gm_ = gm; #else (void)gm; #endif } C86ctlGimic::~C86ctlGimic() { #ifdef _C86CTL_H gm_->Release(); #endif } void C86ctlGimic::setSSGVolume(uint8_t vol) { #ifdef _C86CTL_H gm_->setSSGVolume(vol); #else (void)vol; #endif } BambooTracker-0.4.6/BambooTracker/chip/c86ctl/c86ctl_wrapper.hpp000066400000000000000000000015121401124043500243420ustar00rootroot00000000000000#pragma once #include namespace c86ctl { struct IRealChipBase; struct IRealChip2; struct IGimic2; } class C86ctlRealChip; class C86ctlGimic; class C86ctlBase { public: explicit C86ctlBase(void (*func)()); bool isEmpty() const noexcept; void initialize(); void deinitialize(); int getNumberOfChip(); C86ctlRealChip* getChipInterface(int id); private: c86ctl::IRealChipBase* base_; }; class C86ctlRealChip { public: explicit C86ctlRealChip(c86ctl::IRealChip2* rc); ~C86ctlRealChip(); void resetChip(); void out(uint32_t addr, uint8_t data); C86ctlGimic* queryInterface(); #ifdef _WIN32 private: c86ctl::IRealChip2* rc_; #endif }; class C86ctlGimic { public: explicit C86ctlGimic(c86ctl::IGimic2* gm); ~C86ctlGimic(); void setSSGVolume(uint8_t vol); #ifdef _WIN32 private: c86ctl::IGimic2* gm_; #endif }; BambooTracker-0.4.6/BambooTracker/chip/chip.cpp000066400000000000000000000060671401124043500213240ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "chip.hpp" #include #include "resampler.hpp" #include "register_write_logger.hpp" extern "C" { #include "mame/mamedef.h" UINT8 CHIP_SAMPLING_MODE = 0x00; INT32 CHIP_SAMPLE_RATE; stream_sample_t* DUMMYBUF[] = { nullptr, nullptr }; } namespace chip { Chip::Chip(int id, int clock, int rate, int autoRate, size_t maxDuration, std::unique_ptr resampler1, std::unique_ptr resampler2, std::shared_ptr logger) : id_(id), rate_(rate), // Dummy set clock_(clock), autoRate_(autoRate), maxDuration_(maxDuration), masterVolumeRatio_(100), logger_(logger) { resampler_[0] = std::move(resampler1); resampler_[1] = std::move(resampler2); for (int pan = STEREO_LEFT; pan <= STEREO_RIGHT; ++pan) { for (auto& buf : buffer_) { buf[pan] = new stream_sample_t[CHIP_SMPL_BUF_SIZE_]; } } } Chip::~Chip() { for (int pan = STEREO_LEFT; pan <= STEREO_RIGHT; ++pan) { for (auto& buf : buffer_) { delete[] buf[pan]; } } } void Chip::initResampler() { for (int snd = 0; snd < 2; ++snd) { resampler_[snd]->init(internalRate_[snd], rate_, maxDuration_); } } void Chip::setRate(int rate) { std::lock_guard lg(mutex_); funcSetRate(rate); for (auto& rsmp : resampler_) { rsmp->setDestributionRate(rate); } } void Chip::funcSetRate(int rate) noexcept { rate_ = CHIP_SAMPLE_RATE = ((rate) ? rate : autoRate_); } int Chip::getClock() const noexcept { return clock_; } int Chip::getRate() const noexcept { return rate_; } void Chip::setMaxDuration(size_t maxDuration) { maxDuration_ = maxDuration; for (int snd = 0; snd < 2; ++snd) { resampler_[snd]->setMaxDuration(maxDuration); } } size_t Chip::getMaxDuration() const noexcept { return maxDuration_; } void Chip::setRegisterWriteLogger(std::shared_ptr logger) { logger_ = logger; } void Chip::setMasterVolume(int percentage) { masterVolumeRatio_ = percentage / 100.0; } } BambooTracker-0.4.6/BambooTracker/chip/chip.hpp000066400000000000000000000047371401124043500213330ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "chip_def.h" #include #include #include namespace chip { class AbstractResampler; class AbstractRegisterWriteLogger; class Chip { public: // [rate] // 0 = auto-set mode (set internal chip rate) Chip(int id, int clock, int rate, int autoRate, size_t maxDuration, std::unique_ptr resampler1, std::unique_ptr resampler2, std::shared_ptr logger); virtual ~Chip(); virtual void reset() = 0; virtual void setRegister(uint32_t offset, uint8_t value) = 0; virtual uint8_t getRegister(uint32_t offset) const = 0; virtual void setRate(int rate); int getRate() const noexcept; int getClock() const noexcept; void setMaxDuration(size_t maxDuration); size_t getMaxDuration() const noexcept; void setRegisterWriteLogger(std::shared_ptr logger = nullptr); void setMasterVolume(int percentage); virtual void mix(int16_t* stream, size_t nSamples) = 0; protected: const int id_; std::mutex mutex_; int rate_, clock_; const int autoRate_; int internalRate_[2]; size_t maxDuration_; double masterVolumeRatio_; double volumeRatio_[2]; sample* buffer_[2][2]; std::unique_ptr resampler_[2]; std::shared_ptr logger_; void initResampler(); void funcSetRate(int rate) noexcept; }; } BambooTracker-0.4.6/BambooTracker/chip/chip_def.h000066400000000000000000000040701401124043500215770ustar00rootroot00000000000000#pragma once #include typedef int32_t sample; enum { CHIP_SMPL_BUF_SIZE_ = 0x10000 }; enum Stereo { STEREO_LEFT, STEREO_RIGHT }; struct intf2608 { void (*set_ay_emu_core)(uint8_t Emulator); int (*device_start)(uint8_t ChipID, int clock, uint8_t AYDisable, uint8_t AYFlags, int* AYrate, uint32_t dramSize); void (*device_stop)(uint8_t ChipID); void (*device_reset)(uint8_t ChipID); void (*control_port_a_w)(uint8_t ChipID, uint32_t offset, uint8_t data); void (*control_port_b_w)(uint8_t ChipID, uint32_t offset, uint8_t data); void (*data_port_a_w)(uint8_t ChipID, uint32_t offset, uint8_t data); void (*data_port_b_w)(uint8_t ChipID, uint32_t offset, uint8_t data); uint8_t (*read_port_r)(uint8_t ChipID, uint32_t offset); void (*stream_update)(uint8_t ChipID, sample **outputs, int samples); void (*stream_update_ay)(uint8_t ChipID, sample **outputs, int samples); }; #ifndef INCLUDE_AY8910_H /* Copied from VGMPlay/chips/ay8910.h */ /* * Default values for resistor loads. * The macro should be used in AY8910interface if * the real values are unknown. */ #define AY8910_DEFAULT_LOADS {1000, 1000, 1000} /* * The following is used by all drivers not reviewed yet. * This will like the old behaviour, output between * 0 and 7FFF */ #define AY8910_LEGACY_OUTPUT (1) /* * Specifing the next define will simulate the special * cross channel mixing if outputs are tied together. * The driver will only provide one stream in this case. */ #define AY8910_SINGLE_OUTPUT (2) typedef struct _ay8910_interface ay8910_interface; struct _ay8910_interface { int flags; /* Flags */ int res_load[3]; /* Load on channel in ohms */ /*devcb_read8 portAread;*/ /*devcb_read8 portBread;*/ /*devcb_write8 portAwrite;*/ /*devcb_write8 portBwrite;*/ }; #endif typedef struct _ym2608_interface ym2608_interface; struct _ym2608_interface { ay8910_interface ay8910_intf; /*void ( *handler )( const device_config *device, int irq );*/ /* IRQ handler for the YM2608 */ void ( *handler )( int irq ); /* IRQ handler for the YM2608 */ }; BambooTracker-0.4.6/BambooTracker/chip/codec/000077500000000000000000000000001401124043500207415ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/chip/codec/ymb_codec.hpp000066400000000000000000000035741401124043500234070ustar00rootroot00000000000000/* Encode and decode algorithms for Y8950/YM2608/YM2610 ADPCM-B 2019 by superctr. */ #pragma once #include #include #include #include #include #define CD_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) #define CD_CLAMP_ZERO(x, high) (((x) > (high)) ? (high) : (x)) namespace codec { inline int16_t ymb_step(uint8_t step, int16_t* history, int16_t* step_size) { static const int step_table[8] = { 57, 57, 57, 57, 77, 102, 128, 153 }; int sign = step & 8; int delta = step & 7; int diff = ((1+(delta<<1)) * *step_size) >> 3; int newval = *history; int nstep = (step_table[delta] * *step_size) >> 6; if (sign > 0) newval -= diff; else newval += diff; //*step_size = CLAMP(nstep, 511, 32767); *step_size = CD_CLAMP(nstep, 127, 24576); *history = newval = CD_CLAMP(newval, -32768, 32767); return newval; } inline void ymb_encode(int16_t *buffer,uint8_t *outbuffer,long len) { long i; int16_t step_size = 127; int16_t history = 0; uint8_t buf_sample = 0, nibble = 0; unsigned int adpcm_sample; for(i=0;i>= 4; if(nibble) buffer++; nibble^=4; *outbuffer++ = ymb_step(step, &history, &step_size); } } } #undef CD_CLAMP #undef CD_CLAMP_ZERO BambooTracker-0.4.6/BambooTracker/chip/mame/000077500000000000000000000000001401124043500206035ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/chip/mame/2608intf.c000066400000000000000000000312671401124043500222400ustar00rootroot00000000000000/*************************************************************************** 2608intf.c The YM2608 emulator supports up to 2 chips. Each chip has the following connections: - Status Read / Control Write A - Port Read / Data Write A - Control Write B - Data Write B ***************************************************************************/ #include /* for free */ #include /* for memset */ #include /* for NULL */ /*#include "mamedef.h" #include "sndintrf.h" #include "streams.h"*/ #include "2608intf.h" /*#include "fm.h"*/ /*// Only use EC_EMU2149 #define ENABLE_ALL_CORES*/ #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 /* AY8910 core from MAME */ #endif #define EC_EMU2149 0x00 typedef struct _ym2608_state ym2608_state; struct _ym2608_state { /*sound_stream * stream; emu_timer * timer[2];*/ void * chip; void * psg; ym2608_interface intf; /*const device_config *device;*/ }; #define CHTYPE_YM2608 0x21 extern UINT8 CHIP_SAMPLING_MODE; extern INT32 CHIP_SAMPLE_RATE; static UINT8 AY_EMU_CORE = 0x00; /*extern UINT32 SampleRate;*/ #define MAX_CHIPS 0x02 static ym2608_state YM2608Data[MAX_CHIPS]; /*INLINE ym2608_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_YM2608); return (ym2608_state *)device->token; }*/ static void psg_set_clock(void *param, int clock) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_clock_ym(info->psg, clock); break; #endif case EC_EMU2149: PSG_set_clock((PSG*)info->psg, clock); break; } } } static void psg_write(void *param, int address, int data) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_write_ym(info->psg, address, data); break; #endif case EC_EMU2149: PSG_writeIO((PSG*)info->psg, address, data); break; } } } static int psg_read(void *param) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return ay8910_read_ym(info->psg); #endif case EC_EMU2149: return PSG_readIO((PSG*)info->psg); } } return 0x00; } static void psg_reset(void *param) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_reset_ym(info->psg); break; #endif case EC_EMU2149: PSG_reset((PSG*)info->psg); break; } } } static const ssg_callbacks psgintf = { psg_set_clock, psg_write, psg_read, psg_reset }; /* IRQ Handler */ /*static void IRQHandler(void *param,int irq) { ym2608_state *info = (ym2608_state *)param; //if(info->intf->handler) info->intf->handler(info->device, irq); if(info->intf.handler) info->intf.handler(irq); }*/ /* Timer overflow callback from timer.c */ /*static TIMER_CALLBACK( timer_callback_2608_0 ) { ym2608_state *info = (ym2608_state *)ptr; ym2608_timer_over(info->chip,0); } static TIMER_CALLBACK( timer_callback_2608_1 ) { ym2608_state *info = (ym2608_state *)ptr; ym2608_timer_over(info->chip,1); }*/ /*static void timer_handler(void *param,int c,int count,int clock) { ym2608_state *info = (ym2608_state *)param; if( count == 0 ) { // Reset FM Timer //timer_enable(info->timer[c], 0); } else { // Start FM Timer //attotime period = attotime_mul(ATTOTIME_IN_HZ(clock), count); //if (!timer_enable(info->timer[c], 1)) // timer_adjust_oneshot(info->timer[c], period, 0); } }*/ /* update request from fm.c */ void ym2608_update_request(void *param) { ym2608_state *info = (ym2608_state *)param; /*stream_update(info->stream);*/ ym2608_update_one(info->chip, DUMMYBUF, 0); /*// Not necessary. //if (info->psg != NULL) // ay8910_update_one(info->psg, DUMMYBUF, 0);*/ } /*static STREAM_UPDATE( ym2608_stream_update )*/ void ym2608_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples) { /*ym2608_state *info = (ym2608_state *)param;*/ ym2608_state *info = &YM2608Data[ChipID]; ym2608_update_one(info->chip, outputs, samples); } void ym2608_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples) { /*ym2608_state *info = (ym2608_state *)param;*/ ym2608_state *info = &YM2608Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_update_one(info->psg, outputs, samples); break; #endif case EC_EMU2149: PSG_calc_stereo((PSG*)info->psg, outputs, samples); break; } } else { memset(outputs[0], 0x00, samples * sizeof(stream_sample_t)); memset(outputs[1], 0x00, samples * sizeof(stream_sample_t)); } } /*static STATE_POSTLOAD( ym2608_intf_postload )*/ /*static void ym2608_intf_postload(UINT8 ChipID) { //ym2608_state *info = (ym2608_state *)param; ym2608_state *info = &YM2608Data[ChipID]; ym2608_postload(info->chip); }*/ /*static DEVICE_START( ym2608 )*/ int device_start_ym2608(UINT8 ChipID, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate, offs_t dramSize) { static const ym2608_interface generic_2608 = { { AY8910_LEGACY_OUTPUT | AY8910_SINGLE_OUTPUT, AY8910_DEFAULT_LOADS /*DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL*/ }, NULL }; /*const ym2608_interface *intf = device->static_config ? (const ym2608_interface *)device->static_config : &generic_2608;*/ ym2608_interface *intf; int rate; int ay_clock; /*void *pcmbufa; int pcmsizea;*/ /*ym2608_state *info = get_safe_token(device);*/ ym2608_state *info; if (ChipID >= MAX_CHIPS) return 0; info = &YM2608Data[ChipID]; rate = clock / 144; /* FM synthesis rate is clock / 2 / 72 */ /*rate = clock/72;*/ if ((CHIP_SAMPLING_MODE == 0x01 && rate < CHIP_SAMPLE_RATE) || CHIP_SAMPLING_MODE == 0x02) rate = CHIP_SAMPLE_RATE; info->intf = generic_2608; intf = &info->intf; if (AYFlags) intf->ay8910_intf.flags = AYFlags; /*info->device = device;*/ /* FIXME: Force to use single output */ /*info->psg = ay8910_start_ym(NULL, SOUND_YM2608, clock, &intf->ay8910_intf);*/ if (! AYDisable) { ay_clock = clock / 4; *AYrate = ay_clock / 8; switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->psg = ay8910_start_ym(NULL, CHTYPE_YM2608, ay_clock, &intf->ay8910_intf); break; #endif case EC_EMU2149: info->psg = PSG_new(ay_clock, *AYrate); if (info->psg == NULL) return 0; PSG_setVolumeMode((PSG*)info->psg, 1); /* YM2149 volume mode */ break; } } else { info->psg = NULL; *AYrate = 0; } /*assert_always(info->psg != NULL, "Error creating YM2608/AY8910 chip");*/ /* Timer Handler set */ /*info->timer[0] = timer_alloc(device->machine, timer_callback_2608_0, info); info->timer[1] = timer_alloc(device->machine, timer_callback_2608_1, info);*/ /* stream system initialize */ /*info->stream = stream_create(device,0,2,rate,info,ym2608_stream_update);*/ /* setup adpcm buffers */ /*pcmbufa = device->region; pcmsizea = device->regionbytes;*/ /* initialize YM2608 */ /*//info->chip = ym2608_init(info,device,device->clock,rate, // pcmbufa,pcmsizea, // timer_handler,IRQHandler,&psgintf);*/ /*info->chip = ym2608_init(info, clock, rate, NULL, NULL, &psgintf); assert_always(info->chip != NULL, "Error creating YM2608 chip");*/ info->chip = ym2608_init(info, clock, rate, dramSize, NULL, NULL, &psgintf); /*state_save_register_postload(device->machine, ym2608_intf_postload, info);*/ return rate; } /*static DEVICE_STOP( ym2608 )*/ void device_stop_ym2608(UINT8 ChipID) { /*ym2608_state *info = get_safe_token(device);*/ ym2608_state *info = &YM2608Data[ChipID]; ym2608_shutdown(info->chip); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_stop_ym(info->psg); break; #endif case EC_EMU2149: PSG_delete((PSG*)info->psg); break; } info->psg = NULL; } } /*static DEVICE_RESET( ym2608 )*/ void device_reset_ym2608(UINT8 ChipID) { /*ym2608_state *info = get_safe_token(device);*/ ym2608_state *info = &YM2608Data[ChipID]; ym2608_reset_chip(info->chip); /* also resets the AY clock */ /*//psg_reset(info); // already done as a callback in ym2608_reset_chip*/ } /*READ8_DEVICE_HANDLER( ym2608_r )*/ UINT8 ym2608_r(UINT8 ChipID, offs_t offset) { /*ym2608_state *info = get_safe_token(device);*/ ym2608_state *info = &YM2608Data[ChipID]; return ym2608_read(info->chip, offset & 3); } /*WRITE8_DEVICE_HANDLER( ym2608_w )*/ void ym2608_w(UINT8 ChipID, offs_t offset, UINT8 data) { /*ym2608_state *info = get_safe_token(device);*/ ym2608_state *info = &YM2608Data[ChipID]; ym2608_write(info->chip, offset & 3, data); } /*READ8_DEVICE_HANDLER( ym2608_read_port_r )*/ UINT8 ym2608_read_port_r(UINT8 ChipID, offs_t offset) { (void)offset; return ym2608_r(ChipID, 1); } /*//READ8_DEVICE_HANDLER( ym2608_status_port_a_r ) //UINT8 ym2608_status_port_a_r(UINT8 ChipID, offs_t offset) //{ // return ym2608_r(ChipID, 0); //} //READ8_DEVICE_HANDLER( ym2608_status_port_b_r ) //UINT8 ym2608_status_port_b_r(UINT8 ChipID, offs_t offset) //{ // return ym2608_r(ChipID, 2); //}*/ /*WRITE8_DEVICE_HANDLER( ym2608_control_port_a_w )*/ void ym2608_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { (void)offset; ym2608_w(ChipID, 0, data); } /*WRITE8_DEVICE_HANDLER( ym2608_control_port_b_w )*/ void ym2608_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { (void)offset; ym2608_w(ChipID, 2, data); } /*WRITE8_DEVICE_HANDLER( ym2608_data_port_a_w )*/ void ym2608_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data) { (void)offset; ym2608_w(ChipID, 1, data); } /*WRITE8_DEVICE_HANDLER( ym2608_data_port_b_w )*/ void ym2608_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data) { (void)offset; ym2608_w(ChipID, 3, data); } void ym2608_set_ay_emu_core(UINT8 Emulator) { #ifdef ENABLE_ALL_CORES AY_EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else (void)Emulator; AY_EMU_CORE = EC_EMU2149; #endif return; } /*void ym2608_write_data_pcmrom(UINT8 ChipID, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { ym2608_state* info = &YM2608Data[ChipID]; ym2608_write_pcmrom(info->chip, rom_id, ROMSize, DataStart, DataLength, ROMData); }*/ void ym2608_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY) { ym2608_state* info = &YM2608Data[ChipID]; ym2608_set_mutemask(info->chip, MuteMaskFM); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_mute_mask_ym(info->psg, MuteMaskAY); break; #endif case EC_EMU2149: PSG_setMask((PSG*)info->psg, MuteMaskAY); break; } } } /*void ym2608_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr) { ym2608_state* info = &YM2608Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_srchg_cb_ym(info->psg, CallbackFunc, AYDataPtr); break; #endif case EC_EMU2149: break; } } return; }*/ /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( ym2608 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ym2608_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ym2608 ); break; case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME( ym2608 ); break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ym2608 ); break; // --- the following bits of info are returned as NULL-terminated strings --- case DEVINFO_STR_NAME: strcpy(info->s, "YM2608"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); break; case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }*/ struct intf2608 mame_intf2608 = { /*.set_ay_emu_core =*/ &ym2608_set_ay_emu_core, /*.device_start =*/ &device_start_ym2608, /*.device_stop =*/ &device_stop_ym2608, /*.device_reset =*/ &device_reset_ym2608, /*.control_port_a_w =*/ &ym2608_control_port_a_w, /*.control_port_b_w =*/ &ym2608_control_port_b_w, /*.data_port_a_w =*/ &ym2608_data_port_a_w, /*.data_port_b_w =*/ &ym2608_data_port_b_w, /*.read_port_r =*/ &ym2608_read_port_r, /*.stream_update =*/ &ym2608_stream_update, /*.stream_update_ay =*/ &ym2608_stream_update_ay, }; BambooTracker-0.4.6/BambooTracker/chip/mame/2608intf.h000066400000000000000000000037051401124043500222410ustar00rootroot00000000000000#pragma once #include "mamedef.h" #include "fm.h" #include "emu2149.h" #ifdef INCLUDE_AY8910_H #include "ay8910.h" #endif void ym2608_update_request(void *param); /*READ8_DEVICE_HANDLER( ym2608_r ); WRITE8_DEVICE_HANDLER( ym2608_w ); READ8_DEVICE_HANDLER( ym2608_read_port_r ); READ8_DEVICE_HANDLER( ym2608_status_port_a_r ); READ8_DEVICE_HANDLER( ym2608_status_port_b_r ); WRITE8_DEVICE_HANDLER( ym2608_control_port_a_w ); WRITE8_DEVICE_HANDLER( ym2608_control_port_b_w ); WRITE8_DEVICE_HANDLER( ym2608_data_port_a_w ); WRITE8_DEVICE_HANDLER( ym2608_data_port_b_w ); DEVICE_GET_INFO( ym2608 ); #define SOUND_YM2608 DEVICE_GET_INFO_NAME( ym2608 )*/ void ym2608_stream_update(UINT8 ChipID, stream_sample_t **outputs, int samples); void ym2608_stream_update_ay(UINT8 ChipID, stream_sample_t **outputs, int samples); int device_start_ym2608(UINT8 ChipID, int clock, UINT8 AYDisable, UINT8 AYFlags, int* AYrate, offs_t dramSize); void device_stop_ym2608(UINT8 ChipID); void device_reset_ym2608(UINT8 ChipID); UINT8 ym2608_r(UINT8 ChipID, offs_t offset); void ym2608_w(UINT8 ChipID, offs_t offset, UINT8 data); UINT8 ym2608_read_port_r(UINT8 ChipID, offs_t offset); /*UINT8 ym2608_status_port_a_r(UINT8 ChipID, offs_t offset); UINT8 ym2608_status_port_b_r(UINT8 ChipID, offs_t offset);*/ void ym2608_control_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_control_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_data_port_a_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_data_port_b_w(UINT8 ChipID, offs_t offset, UINT8 data); void ym2608_set_ay_emu_core(UINT8 Emulator); /*void ym2608_write_data_pcmrom(UINT8 ChipID, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData);*/ void ym2608_set_mute_mask(UINT8 ChipID, UINT32 MuteMaskFM, UINT32 MuteMaskAY); /*void ym2608_set_srchg_cb(UINT8 ChipID, SRATE_CALLBACK CallbackFunc, void* DataPtr, void* AYDataPtr);*/ extern struct intf2608 mame_intf2608; BambooTracker-0.4.6/BambooTracker/chip/mame/emu2149.c000066400000000000000000000254201401124043500220600ustar00rootroot00000000000000/**************************************************************************** emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 2001 04-28 : Version 1.00beta -- 1st Beta Release. 2001 08-14 : Version 1.10 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. 2002 10-13 : Version 1.14 -- Fixed the envelope unit. 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope frequency register is written before key-on. References: psg.vhd -- 2000 written by Kazuhiro Tsujikawa. s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). ay8910.c -- 1998-2001 Author unknown (MAME). MSX-Datapack -- 1991 ASCII Corp. AY-3-8910 data sheet *****************************************************************************/ #include #include #include #include "emu2149.h" static e_uint32 voltbl[2][32] = { {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0B, 0x0D, 0x0F, 0x12, 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, 0xB4, 0xD6, 0xEB, 0xFF}, {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, 0x0B, 0x0B, 0x0F, 0x0F, 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, 0xB4, 0xB4, 0xFF, 0xFF} }; #define GETA_BITS 24 static void internal_refresh (PSG * psg) { if (psg->quality) { psg->base_incr = 1 << GETA_BITS; psg->realstep = (e_uint32) ((1 << 31) / psg->rate); psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 8)); psg->psgtime = 0; } else { psg->base_incr = (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (8.0 * psg->rate)); } } EMU2149_API void PSG_set_clock(PSG * psg, e_uint32 c) { psg->clk = c; internal_refresh(psg); } EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r) { psg->rate = r ? r : 44100; internal_refresh (psg); } EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q) { psg->quality = q; internal_refresh (psg); } EMU2149_API PSG * PSG_new (e_uint32 c, e_uint32 r) { PSG *psg; psg = (PSG *) malloc (sizeof (PSG)); if (psg == NULL) return NULL; memset(psg, 0x00, sizeof(PSG)); PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); psg->clk = c; psg->rate = r ? r : 44100; PSG_set_quality (psg, 0); psg->stereo_mask[0] = 0x03; psg->stereo_mask[1] = 0x03; psg->stereo_mask[2] = 0x03; return psg; } EMU2149_API void PSG_setFlags (PSG * psg, e_uint8 flags) { if (flags & EMU2149_ZX_STEREO) { /* ABC Stereo */ psg->stereo_mask[0] = 0x01; psg->stereo_mask[1] = 0x03; psg->stereo_mask[2] = 0x02; } else { psg->stereo_mask[0] = 0x03; psg->stereo_mask[1] = 0x03; psg->stereo_mask[2] = 0x03; } return; } EMU2149_API void PSG_setVolumeMode (PSG * psg, int type) { switch (type) { case 1: psg->voltbl = voltbl[EMU2149_VOL_YM2149]; break; case 2: psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; break; default: psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; break; } } EMU2149_API e_uint32 PSG_setMask (PSG *psg, e_uint32 mask) { e_uint32 ret = 0; if(psg) { ret = psg->mask; psg->mask = mask; } return ret; } EMU2149_API void PSG_setStereoMask (PSG *psg, e_uint32 mask) { /* e_uint32 ret = 0; */ if(psg) { psg->stereo_mask[0] = (mask >>0) &3; psg->stereo_mask[1] = (mask >>2) &3; psg->stereo_mask[2] = (mask >>4) &3; } } EMU2149_API e_uint32 PSG_toggleMask (PSG *psg, e_uint32 mask) { e_uint32 ret = 0; if(psg) { ret = psg->mask; psg->mask ^= mask; } return ret; } EMU2149_API void PSG_reset (PSG * psg) { int i; psg->base_count = 0; for (i = 0; i < 3; i++) { psg->cout[i] = 0; psg->count[i] = 0x1000; psg->freq[i] = 0; psg->edge[i] = 0; psg->volume[i] = 0; } psg->mask = 0; for (i = 0; i < 16; i++) psg->reg[i] = 0; psg->adr = 0; psg->noise_seed = 0xffff; psg->noise_count = 0x40; psg->noise_freq = 0; psg->env_volume = 0; psg->env_ptr = 0; psg->env_freq = 0; psg->env_count = 0; psg->env_pause = 1; psg->out = 0; } EMU2149_API void PSG_delete (PSG * psg) { free (psg); } EMU2149_API e_uint8 PSG_readIO (PSG * psg) { return (e_uint8) (psg->reg[psg->adr]); } EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg) { return (e_uint8) (psg->reg[reg & 0x1f]); } EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) { if (adr & 1) PSG_writeReg (psg, psg->adr, val); else psg->adr = val & 0x1f; } INLINE static e_int16 calc (PSG * psg) { int i, noise; e_uint32 incr; e_int32 mix = 0; psg->base_count += psg->base_incr; incr = (psg->base_count >> GETA_BITS); psg->base_count &= (1 << GETA_BITS) - 1; /* Envelope */ psg->env_count += incr; while (psg->env_count>=0x10000 && psg->env_freq!=0) { if (!psg->env_pause) { if(psg->env_face) psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; else psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; } if (psg->env_ptr & 0x20) /* if carry or borrow */ { if (psg->env_continue) { if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; if (psg->env_hold) psg->env_pause = 1; psg->env_ptr = psg->env_face?0:0x1f; } else { psg->env_pause = 1; psg->env_ptr = 0; } } psg->env_count -= psg->env_freq; } /* Noise */ psg->noise_count += incr; if (psg->noise_count & 0x40) { if (psg->noise_seed & 1) psg->noise_seed ^= 0x24000; psg->noise_seed >>= 1; psg->noise_count -= psg->noise_freq; } noise = psg->noise_seed & 1; /* Tone */ for (i = 0; i < 3; i++) { psg->count[i] += incr; if (psg->count[i] & 0x1000) { if (psg->freq[i] > 1) { psg->edge[i] = !psg->edge[i]; psg->count[i] -= psg->freq[i]; } else { psg->edge[i] = 1; } } psg->cout[i] = 0; /* BS maintaining cout for stereo mix */ if (psg->mask&PSG_MASK_CH(i)) continue; if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) { if (!(psg->volume[i] & 32)) psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; else psg->cout[i] = psg->voltbl[psg->env_ptr]; mix += psg->cout[i]; } } return (e_int16) mix; } EMU2149_API e_int16 PSG_calc (PSG * psg) { if (!psg->quality) return (e_int16) (calc (psg) << 4); /* Simple rate converter */ while (psg->realstep > psg->psgtime) { psg->psgtime += psg->psgstep; psg->out += calc (psg); psg->out >>= 1; } psg->psgtime = psg->psgtime - psg->realstep; return (e_int16) (psg->out << 4); } INLINE static void calc_stereo (PSG * psg, e_int32 out[2]) { int i, noise; e_uint32 incr; e_int32 l = 0, r = 0; psg->base_count += psg->base_incr; incr = (psg->base_count >> GETA_BITS); psg->base_count &= (1 << GETA_BITS) - 1; /* Envelope */ psg->env_count += incr; while (psg->env_count>=0x10000 && psg->env_freq!=0) { if (!psg->env_pause) { if(psg->env_face) psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; else psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; } if (psg->env_ptr & 0x20) /* if carry or borrow */ { if (psg->env_continue) { if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; if (psg->env_hold) psg->env_pause = 1; psg->env_ptr = psg->env_face?0:0x1f; } else { psg->env_pause = 1; psg->env_ptr = 0; } } psg->env_count -= psg->env_freq; } /* Noise */ psg->noise_count += incr; if (psg->noise_count & 0x40) { if (psg->noise_seed & 1) psg->noise_seed ^= 0x24000; psg->noise_seed >>= 1; psg->noise_count -= psg->noise_freq; } noise = psg->noise_seed & 1; /* Tone */ for (i = 0; i < 3; i++) { psg->count[i] += incr; if (psg->count[i] & 0x1000) { if (psg->freq[i] > 1) { psg->edge[i] = !psg->edge[i]; psg->count[i] -= psg->freq[i]; } else { psg->edge[i] = 1; } } psg->cout[i] = 0; /* BS maintaining cout for stereo mix */ if (psg->mask&PSG_MASK_CH(i)) continue; if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) { if (!(psg->volume[i] & 32)) psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; else psg->cout[i] = psg->voltbl[psg->env_ptr]; if (psg->stereo_mask[i] & 0x01) l += psg->cout[i]; if (psg->stereo_mask[i] & 0x02) r += psg->cout[i]; } } out[0] = l << 5; out[1] = r << 5; return; } EMU2149_API void PSG_calc_stereo (PSG * psg, e_int32 **out, e_int32 samples) { e_int32 *bufMO = out[0]; e_int32 *bufRO = out[1]; e_int32 buffers[2]; int i; for (i = 0; i < samples; i ++) { if (!psg->quality) { calc_stereo (psg, buffers); bufMO[i] = buffers[0]; bufRO[i] = buffers[1]; } else { while (psg->realstep > psg->psgtime) { psg->psgtime += psg->psgstep; psg->sprev[0] = psg->snext[0]; psg->sprev[1] = psg->snext[1]; calc_stereo (psg, psg->snext); } psg->psgtime -= psg->realstep; bufMO[i] = (e_int32) (((double) psg->snext[0] * (psg->psgstep - psg->psgtime) + (double) psg->sprev[0] * psg->psgtime) / psg->psgstep); bufRO[i] = (e_int32) (((double) psg->snext[1] * (psg->psgstep - psg->psgtime) + (double) psg->sprev[1] * psg->psgtime) / psg->psgstep); } } } EMU2149_API void PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) { int c; if (reg > 15) return; psg->reg[reg] = (e_uint8) (val & 0xff); switch (reg) { case 0: case 2: case 4: case 1: case 3: case 5: c = reg >> 1; psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; break; case 6: psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); break; case 7: psg->tmask[0] = (val & 1); psg->tmask[1] = (val & 2); psg->tmask[2] = (val & 4); psg->nmask[0] = (val & 8); psg->nmask[1] = (val & 16); psg->nmask[2] = (val & 32); break; case 8: case 9: case 10: psg->volume[reg - 8] = val << 1; break; case 11: case 12: psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; break; case 13: psg->env_continue = (val >> 3) & 1; psg->env_attack = (val >> 2) & 1; psg->env_alternate = (val >> 1) & 1; psg->env_hold = val & 1; psg->env_face = psg->env_attack; psg->env_pause = 0; psg->env_count = 0x10000 - psg->env_freq; psg->env_ptr = psg->env_face?0:0x1f; break; case 14: case 15: default: break; } return; } BambooTracker-0.4.6/BambooTracker/chip/mame/emu2149.h000066400000000000000000000045751401124043500220750ustar00rootroot00000000000000/* emu2149.h */ #ifndef _EMU2149_H_ #define _EMU2149_H_ #include "emutypes.h" /*#ifdef EMU2149_DLL_EXPORTS #define EMU2149_API __declspec(dllexport) #elif EMU2149_DLL_IMPORTS #define EMU2149_API __declspec(dllimport) #else*/ #define EMU2149_API /*#endif*/ #define EMU2149_VOL_DEFAULT 1 #define EMU2149_VOL_YM2149 0 #define EMU2149_VOL_AY_3_8910 1 #define EMU2149_ZX_STEREO 0x80 #define PSG_MASK_CH(x) (1<<(x)) /*#ifdef __cplusplus extern "C" { #endif*/ typedef struct __PSG { /* Volume Table */ e_uint32 *voltbl; e_uint8 reg[0x20]; e_int32 out; e_int32 cout[3]; e_uint32 clk, rate, base_incr, quality; e_uint32 count[3]; e_uint32 volume[3]; e_uint32 freq[3]; e_uint32 edge[3]; e_uint32 tmask[3]; e_uint32 nmask[3]; e_uint32 mask; e_uint32 stereo_mask[3]; e_uint32 base_count; e_uint32 env_volume; e_uint32 env_ptr; e_uint32 env_face; e_uint32 env_continue; e_uint32 env_attack; e_uint32 env_alternate; e_uint32 env_hold; e_uint32 env_pause; e_uint32 env_reset; e_uint32 env_freq; e_uint32 env_count; e_uint32 noise_seed; e_uint32 noise_count; e_uint32 noise_freq; /* rate converter */ e_uint32 realstep; e_uint32 psgtime; e_uint32 psgstep; e_int32 prev, next; e_int32 sprev[2], snext[2]; /* I/O Ctrl */ e_uint32 adr; } PSG; EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); EMU2149_API void PSG_set_clock(PSG * psg, e_uint32 c); EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); EMU2149_API void PSG_reset (PSG *); EMU2149_API void PSG_delete (PSG *); EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); EMU2149_API e_uint8 PSG_readIO (PSG * psg); EMU2149_API e_int16 PSG_calc (PSG *); EMU2149_API void PSG_calc_stereo (PSG * psg, e_int32 **out, e_int32 samples); EMU2149_API void PSG_setFlags (PSG * psg, e_uint8 flags); EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); EMU2149_API void PSG_setStereoMask (PSG *psg, e_uint32 mask); /*#ifdef __cplusplus } #endif*/ #endif BambooTracker-0.4.6/BambooTracker/chip/mame/emutypes.h000066400000000000000000000010141401124043500226230ustar00rootroot00000000000000#ifndef _EMUTYPES_H_ #define _EMUTYPES_H_ #ifndef INLINE #if defined(_MSC_VER) /*#define INLINE __forceinline*/ #define INLINE __inline #elif defined(__GNUC__) #define INLINE __inline__ #elif defined(_MWERKS_) #define INLINE inline #else #define INLINE #endif #endif typedef unsigned int e_uint; typedef signed int e_int; typedef unsigned char e_uint8 ; typedef signed char e_int8 ; typedef unsigned short e_uint16 ; typedef signed short e_int16 ; typedef unsigned int e_uint32 ; typedef signed int e_int32 ; #endif BambooTracker-0.4.6/BambooTracker/chip/mame/fm.c000066400000000000000000004605141401124043500213630ustar00rootroot00000000000000#define YM2610B_WARNING /* ** ** File: fm.c -- software implementation of Yamaha FM sound generator ** ** Copyright Jarek Burczynski (bujar at mame dot net) ** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development ** ** Version 1.4.2 (final beta) ** */ /* ** History: ** ** 2006-2008 Eke-Eke (Genesis Plus GX), MAME backport by R. Belmont. ** - implemented PG overflow, aka "detune bug" (Ariel, Comix Zone, Shaq Fu, Spiderman,...), credits to Nemesis ** - fixed SSG-EG support, credits to Nemesis and additional fixes from Alone Coder ** - modified EG rates and frequency, tested by Nemesis on real hardware ** - implemented LFO phase update for CH3 special mode (Warlock birds, Alladin bug sound) ** - fixed Attack Rate update (Batman & Robin intro) ** - fixed attenuation level at the start of Substain (Gynoug explosions) ** - fixed EG decay->substain transition to handle special cases, like SL=0 and Decay rate is very slow (Mega Turrican tracks 03,09...) ** ** 06-23-2007 Zsolt Vasvari: ** - changed the timing not to require the use of floating point calculations ** ** 03-08-2003 Jarek Burczynski: ** - fixed YM2608 initial values (after the reset) ** - fixed flag and irqmask handling (YM2608) ** - fixed BUFRDY flag handling (YM2608) ** ** 14-06-2003 Jarek Burczynski: ** - implemented all of the YM2608 status register flags ** - implemented support for external memory read/write via YM2608 ** - implemented support for deltat memory limit register in YM2608 emulation ** ** 22-05-2003 Jarek Burczynski: ** - fixed LFO PM calculations (copy&paste bugfix) ** ** 08-05-2003 Jarek Burczynski: ** - fixed SSG support ** ** 22-04-2003 Jarek Burczynski: ** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) ** ** 15-04-2003 Jarek Burczynski: ** - added support for YM2608's register 0x110 - status mask ** ** 01-12-2002 Jarek Burczynski: ** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) ** The addressing patch used for early Neo-Geo games can be removed now. ** ** 26-11-2002 Jarek Burczynski, Nicola Salmoria: ** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: ** - added emulation of YM2608 drums. ** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) ** ** 16-08-2002 Jarek Burczynski: ** - binary exact Envelope Generator (verified on real YM2203); ** identical to YM2151 ** - corrected 'off by one' error in feedback calculations (when feedback is off) ** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) ** ** 18-12-2001 Jarek Burczynski: ** - added SSG-EG support (verified on real YM2203) ** ** 12-08-2001 Jarek Burczynski: ** - corrected sin_tab and tl_tab data (verified on real chip) ** - corrected feedback calculations (verified on real chip) ** - corrected phase generator calculations (verified on real chip) ** - corrected envelope generator calculations (verified on real chip) ** - corrected FM volume level (YM2610 and YM2610B). ** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : ** this was needed to calculate YM2610 FM channels output correctly. ** (Each FM channel is calculated as in other chips, but the output of the channel ** gets shifted right by one *before* sending to accumulator. That was impossible to do ** with previous implementation). ** ** 23-07-2001 Jarek Burczynski, Nicola Salmoria: ** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) ** ** 11-06-2001 Jarek Burczynski: ** - corrected end of sample bug in ADPCMA_calc_cha(). ** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). ** ** 08-12-98 hiro-shi: ** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA ** move ROM limit check.(CALC_CH? -> 2610Write1/2) ** test program (ADPCMB_TEST) ** move ADPCM A/B end check. ** ADPCMB repeat flag(no check) ** change ADPCM volume rate (8->16) (32->48). ** ** 09-12-98 hiro-shi: ** change ADPCM volume. (8->16, 48->64) ** replace ym2610 ch0/3 (YM-2610B) ** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. ** add ADPCM_SHIFT_MASK ** change ADPCMA_DECODE_MIN/MAX. */ /************************************************************************/ /* comment of hiro-shi(Hiromitsu Shioya) */ /* YM2610(B) = OPN-B */ /* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ /* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ /************************************************************************/ #include #include #include #include #include #include "mamedef.h" /*#ifndef __RAINE__*/ /*#include "sndintrf.h"*/ /* use M.A.M.E. */ /*#else*/ /*#include "deftypes.h"*/ /* use RAINE */ /*#include "support.h"*/ /* use RAINE */ /*#endif*/ #include "fm.h" /* include external DELTA-T unit (when needed) */ #if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) #include "ymdeltat.h" #endif /* shared function building option */ #define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) #define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) /* globals */ #define TYPE_SSG 0x01 /* SSG support */ #define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ #define TYPE_6CH 0x04 /* FM 6CH / 3CH */ #define TYPE_DAC 0x08 /* YM2612's DAC device */ #define TYPE_ADPCM 0x10 /* two ADPCM units */ #define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ #define TYPE_YM2203 (TYPE_SSG) #define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) #define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ #define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ #define FREQ_MASK ((1<>3) /* sin waveform table in 'decibel' scale */ static unsigned int sin_tab[SIN_LEN]; /* sustain level table (3dB per step) */ /* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ /* 1, 2, 4, 8, 16, 32, 64 (value)*/ /* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ #define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) static const UINT32 sl_table[16]={ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) }; #undef SC #define RATE_STEPS (8) static const UINT8 eg_inc[19*RATE_STEPS]={ /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ /* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ /* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ /* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ /* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ /* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ /* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ /* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ /* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ /* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ /*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ /*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ /*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ /*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ /*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ /*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ /*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ /*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ /*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ }; #define O(a) (a*RATE_STEPS) /*note that there is no O(17) in this table - it's directly in the code */ static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ /* 32 infinite time rates */ O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), /* rates 00-11 */ O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), /* rate 12 */ O( 4),O( 5),O( 6),O( 7), /* rate 13 */ O( 8),O( 9),O(10),O(11), /* rate 14 */ O(12),O(13),O(14),O(15), /* rate 15 */ O(16),O(16),O(16),O(16), /* 32 dummy rates (same as 15 3) */ O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) }; #undef O /*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ /*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ /*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ #define O(a) (a*1) static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ /* 32 infinite time rates */ O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), /* rates 00-11 */ O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), /* rate 12 */ O( 0),O( 0),O( 0),O( 0), /* rate 13 */ O( 0),O( 0),O( 0),O( 0), /* rate 14 */ O( 0),O( 0),O( 0),O( 0), /* rate 15 */ O( 0),O( 0),O( 0),O( 0), /* 32 dummy rates (same as 15 3) */ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) }; #undef O static const UINT8 dt_tab[4 * 32]={ /* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ /* FD=0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* FD=1 */ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, /* FD=2 */ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, /* FD=3 */ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 }; /* OPN key frequency number -> key code follow table */ /* fnum higher 4bit -> keycode lower 2bit */ static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; /* 8 LFO speed parameters */ /* each value represents number of samples that one LFO level will last for */ static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; /*There are 4 different LFO AM depths available, they are: 0 dB, 1.4 dB, 5.9 dB, 11.8 dB Here is how it is generated (in EG steps): 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 (1.4 dB is losing precision as you can see) It's implemented as generator from 0..126 with step 2 then a shift right N times, where N is: 8 for 0 dB 3 for 1.4 dB 1 for 5.9 dB 0 for 11.8 dB */ static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; /*There are 8 different LFO PM depths available, they are: 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 (bits 8,9,10 = FNUM MSB from OCT/FNUM register) Here we store only first quarter (positive one) of full waveform. Full table (lfo_pm_table) containing all 128 waveforms is build at run (init) time. One value in table below represents 4 (four) basic LFO steps (1 PM step = 4 AM steps). For example: at LFO SPEED=0 (which is 108 samples per basic LFO step) one value from "lfo_pm_output" table lasts for 432 consecutive samples (4*108=432) and one full LFO waveform cycle lasts for 13824 samples (32*432=13824; 32 because we store only a quarter of whole waveform in the table below) */ static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ /* FNUM BIT 4: 000 0001xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* FNUM BIT 5: 000 0010xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* FNUM BIT 6: 000 0100xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, /* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* FNUM BIT 7: 000 1000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, /* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, /* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, /* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* FNUM BIT 8: 001 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, /* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, /* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, /* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, /* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, /* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* FNUM BIT 9: 010 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, /* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, /* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, /* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, /* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, /* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, /* FNUM BIT10: 100 0000xxxx */ /* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, /* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, /* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, /* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, /* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, /* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, /* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, /* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, }; /* all 128 LFO PM waveforms */ static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ /* register number to channel number , slot offset */ #define OPN_CHAN(N) (N&3) #define OPN_SLOT(N) ((N>>2)&3) /* slot number */ #define SLOT1 0 #define SLOT2 2 #define SLOT3 1 #define SLOT4 3 /* bit0 = Right enable , bit1 = Left enable */ #define OUTD_RIGHT 1 #define OUTD_LEFT 2 #define OUTD_CENTER 3 /* save output as raw 16-bit sample */ /* #define SAVE_SAMPLE */ #ifdef SAVE_SAMPLE static FILE *sample[1]; #if 1 /*save to MONO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = lt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #else /*save to STEREO file */ #define SAVE_ALL_CHANNELS \ { signed int pom = lt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ pom = rt; \ fputc((unsigned short)pom&0xff,sample[0]); \ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ } #endif #endif /* struct describing a single operator (SLOT) */ typedef struct { INT32 *DT; /* detune :dt_tab[DT] */ UINT8 KSR; /* key scale rate :3-KSR */ UINT32 ar; /* attack rate */ UINT32 d1r; /* decay rate */ UINT32 d2r; /* sustain rate */ UINT32 rr; /* release rate */ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ UINT32 mul; /* multiple :ML_TABLE[ML] */ /* Phase Generator */ UINT32 phase; /* phase counter */ INT32 Incr; /* phase step */ /* Envelope Generator */ UINT8 state; /* phase type */ UINT32 tl; /* total level: TL << 3 */ INT32 volume; /* envelope counter */ UINT32 sl; /* sustain level:sl_table[SL] */ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ UINT8 eg_sh_ar; /* (attack state) */ UINT8 eg_sel_ar; /* (attack state) */ UINT8 eg_sh_d1r; /* (decay state) */ UINT8 eg_sel_d1r; /* (decay state) */ UINT8 eg_sh_d2r; /* (sustain state) */ UINT8 eg_sel_d2r; /* (sustain state) */ UINT8 eg_sh_rr; /* (release state) */ UINT8 eg_sel_rr; /* (release state) */ UINT8 ssg; /* SSG-EG waveform */ UINT8 ssgn; /* SSG-EG negated output */ UINT32 key; /* 0=last key was KEY OFF, 1=KEY ON */ /* LFO */ UINT32 AMmask; /* AM enable flag */ } FM_SLOT; typedef struct { FM_SLOT SLOT[4]; /* four SLOTs (operators) */ UINT8 ALGO; /* algorithm */ UINT8 FB; /* feedback shift */ INT32 op1_out[2]; /* op1 output for feedback */ INT32 *connect1; /* SLOT1 output pointer */ INT32 *connect3; /* SLOT3 output pointer */ INT32 *connect2; /* SLOT2 output pointer */ INT32 *connect4; /* SLOT4 output pointer */ INT32 *mem_connect;/* where to put the delayed sample (MEM) */ INT32 mem_value; /* delayed sample (MEM) value */ INT32 pms; /* channel PMS */ UINT8 ams; /* channel AMS */ UINT32 fc; /* fnum,blk:adjusted to sample rate */ UINT8 kcode; /* key code: */ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ UINT8 Muted; } FM_CH; typedef struct { /*const device_config *device;*/ void * param; /* this chip parameter */ int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ int timer_prescaler; /* timer prescaler */ #if FM_BUSY_FLAG_SUPPORT TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ #endif UINT8 address; /* address register */ UINT8 irq; /* interrupt level */ UINT8 irqmask; /* irq mask */ UINT8 status; /* status flag */ UINT32 mode; /* mode CSM / 3SLOT */ UINT8 prescaler_sel; /* prescaler selector */ UINT8 fn_h; /* freq latch */ INT32 TA; /* timer a */ INT32 TAC; /* timer a counter */ UINT8 TB; /* timer b */ INT32 TBC; /* timer b counter */ /* local time tables */ INT32 dt_tab[8][32]; /* DeTune table */ /* Extention Timer and IRQ handler */ FM_TIMERHANDLER timer_handler; FM_IRQHANDLER IRQ_Handler; const ssg_callbacks *SSG; } FM_ST; /***********************************************************/ /* OPN unit */ /***********************************************************/ /* OPN 3slot struct */ typedef struct { UINT32 fc[3]; /* fnum3,blk3: calculated */ UINT8 fn_h; /* freq3 latch */ UINT8 kcode[3]; /* key code */ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ } FM_3SLOT; /* OPN/A/B common state */ typedef struct { UINT8 type; /* chip type */ FM_ST ST; /* general state */ FM_3SLOT SL3; /* 3 slot mode state */ FM_CH *P_CH; /* pointer of CH */ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ UINT32 eg_cnt; /* global envelope generator counter */ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */ UINT32 eg_timer_add; /* step of eg_timer */ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers but LFO works with one more bit of a precision so we really need 4096 elements */ UINT32 fn_table[4096]; /* fnumber->increment counter */ UINT32 fn_max; /* maximal phase increment (used for phase overflow) */ /* LFO */ UINT32 LFO_AM; /* runtime LFO calculations helper */ INT32 LFO_PM; /* runtime LFO calculations helper */ UINT32 lfo_cnt; UINT32 lfo_inc; UINT32 lfo_freq[8]; /* LFO FREQ table */ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ INT32 mem; /* one sample delay memory */ INT32 out_fm[8]; /* outputs of working channels */ #if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) INT32 out_adpcm[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 ADPCM */ INT32 out_delta[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 DELTAT*/ #endif } FM_OPN; /* log output level */ #define LOG_ERR 3 /* ERROR */ #define LOG_WAR 2 /* WARNING */ #define LOG_INF 1 /* INFORMATION */ #define LOG_LEVEL LOG_INF #ifndef __RAINE__ #define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0) #endif /* limitter */ #define Limit(val, max,min) { \ if ( val > max ) val = max; \ else if ( val < min ) val = min; \ } /* status set and IRQ handling */ INLINE void FM_STATUS_SET(FM_ST *ST,int flag) { /* set status flag */ ST->status |= flag; if ( !(ST->irq) && (ST->status & ST->irqmask) ) { ST->irq = 1; /* callback user interrupt handler (IRQ is OFF to ON) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); } } /* status reset and IRQ handling */ INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) { /* reset status flag */ ST->status &=~flag; if ( (ST->irq) && !(ST->status & ST->irqmask) ) { ST->irq = 0; /* callback user interrupt handler (IRQ is ON to OFF) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); } } /* IRQ mask set */ INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag) { ST->irqmask = flag; /* IRQ handling check */ FM_STATUS_SET(ST,0); FM_STATUS_RESET(ST,0); } /* OPN Mode Register Write */ INLINE void set_timers( FM_ST *ST, void *n, int v ) { /* b7 = CSM MODE */ /* b6 = 3 slot mode */ /* b5 = reset b */ /* b4 = reset a */ /* b3 = timer enable b */ /* b2 = timer enable a */ /* b1 = load b */ /* b0 = load a */ ST->mode = v; /* reset Timer b flag */ if( v & 0x20 ) FM_STATUS_RESET(ST,0x02); /* reset Timer a flag */ if( v & 0x10 ) FM_STATUS_RESET(ST,0x01); /* load b */ if( v & 0x02 ) { if( ST->TBC == 0 ) { ST->TBC = ( 256-ST->TB)<<4; /* External timer handler */ if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,ST->clock); } } else { /* stop timer b */ if( ST->TBC != 0 ) { ST->TBC = 0; if (ST->timer_handler) (ST->timer_handler)(n,1,0,ST->clock); } } /* load a */ if( v & 0x01 ) { if( ST->TAC == 0 ) { ST->TAC = (1024-ST->TA); /* External timer handler */ if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,ST->clock); } } else { /* stop timer a */ if( ST->TAC != 0 ) { ST->TAC = 0; if (ST->timer_handler) (ST->timer_handler)(n,0,0,ST->clock); } } } /* Timer A Overflow */ INLINE void TimerAOver(FM_ST *ST) { /* set status (if enabled) */ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); /* clear or reload the counter */ ST->TAC = (1024-ST->TA); if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,ST->clock); } /* Timer B Overflow */ INLINE void TimerBOver(FM_ST *ST) { /* set status (if enabled) */ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); /* clear or reload the counter */ ST->TBC = ( 256-ST->TB)<<4; if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,ST->clock); } #if FM_INTERNAL_TIMER /* ----- internal timer mode , update timer */ /* ---------- calculate timer A ---------- */ #define INTERNAL_TIMER_A(ST,CSM_CH) \ { \ if( (ST)->TAC && ((ST)->timer_handler==0) ) \ if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ { \ TimerAOver( ST ); \ /* CSM mode total level latch and auto key on */ \ if( (ST)->mode & 0x80 ) \ CSMKeyControll( OPN->type, CSM_CH ); \ } \ } /* ---------- calculate timer B ---------- */ #define INTERNAL_TIMER_B(ST,step) \ { \ if( (ST)->TBC && ((ST)->timer_handler==0) ) \ if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ TimerBOver( ST ); \ } #else /* FM_INTERNAL_TIMER */ /* external timer mode */ #define INTERNAL_TIMER_A(ST,CSM_CH) #define INTERNAL_TIMER_B(ST,step) #endif /* FM_INTERNAL_TIMER */ #if FM_BUSY_FLAG_SUPPORT #define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST) { if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) { if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0) return ST->status | 0x80; /* with busy */ /* expire */ FM_BUSY_CLEAR(ST); } return ST->status; } INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock ) { TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler); ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period); } #else #define FM_STATUS_FLAG(ST) ((ST)->status) #define FM_BUSY_SET(ST,bclock) {} #define FM_BUSY_CLEAR(ST) {} #endif INLINE void FM_KEYON(UINT8 type, FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; (void)type; if( !SLOT->key ) { SLOT->key = 1; SLOT->phase = 0; /* restart Phase Generator */ SLOT->ssgn = (SLOT->ssg & 0x04) >> 1; SLOT->state = EG_ATT; } } INLINE void FM_KEYOFF(FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if( SLOT->key ) { SLOT->key = 0; if (SLOT->state>EG_REL) SLOT->state = EG_REL;/* phase -> Release */ } } /* set algorithm connection */ static void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) { INT32 *carrier = &OPN->out_fm[ch]; INT32 **om1 = &CH->connect1; INT32 **om2 = &CH->connect3; INT32 **oc1 = &CH->connect2; INT32 **memc = &CH->mem_connect; switch( CH->ALGO ) { case 0: /* M1---C1---MEM---M2---C2---OUT */ *om1 = &OPN->c1; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 1: /* M1------+-MEM---M2---C2---OUT */ /* C1-+ */ *om1 = &OPN->mem; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 2: /* M1-----------------+-C2---OUT */ /* C1---MEM---M2-+ */ *om1 = &OPN->c2; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->m2; break; case 3: /* M1---C1---MEM------+-C2---OUT */ /* M2-+ */ *om1 = &OPN->c1; *oc1 = &OPN->mem; *om2 = &OPN->c2; *memc= &OPN->c2; break; case 4: /* M1---C1-+-OUT */ /* M2---C2-+ */ /* MEM: not used */ *om1 = &OPN->c1; *oc1 = carrier; *om2 = &OPN->c2; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; case 5: /* +----C1----+ */ /* M1-+-MEM---M2-+-OUT */ /* +----C2----+ */ *om1 = 0; /* special mark */ *oc1 = carrier; *om2 = carrier; *memc= &OPN->m2; break; case 6: /* M1---C1-+ */ /* M2-+-OUT */ /* C2-+ */ /* MEM: not used */ *om1 = &OPN->c1; *oc1 = carrier; *om2 = carrier; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; case 7: /* M1-+ */ /* C1-+-OUT */ /* M2-+ */ /* C2-+ */ /* MEM: not used*/ *om1 = carrier; *oc1 = carrier; *om2 = carrier; *memc= &OPN->mem; /* store it anywhere where it will not be used */ break; } CH->connect4 = carrier; } /* set detune & multiple */ INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) { SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; SLOT->DT = ST->dt_tab[(v>>4)&7]; CH->SLOT[SLOT1].Incr=-1; } /* set total level */ INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) { (void)CH; SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ } /* set attack rate & key scale */ INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v) { UINT8 old_KSR = SLOT->KSR; (void)type; SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->KSR = 3-(v>>6); if (SLOT->KSR != old_KSR) { CH->SLOT[SLOT1].Incr=-1; } /* refresh Attack rate */ if ((SLOT->ar + SLOT->ksr) < 32+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 17*RATE_STEPS; } } /* set decay rate */ INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v) { (void)type; SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; } /* set sustain rate */ INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v) { (void)type; SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; } /* set release rate */ INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v) { (void)type; SLOT->sl = sl_table[ v>>4 ]; SLOT->rr = 34 + ((v&0x0f)<<2); SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; } INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) { UINT32 p; p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) { UINT32 p; p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } /* advance LFO to next sample */ INLINE void advance_lfo(FM_OPN *OPN) { UINT8 pos; if (OPN->lfo_inc) /* LFO enabled ? */ { OPN->lfo_cnt += OPN->lfo_inc; pos = (OPN->lfo_cnt >> LFO_SH) & 127; /* update AM when LFO output changes */ /* actually I can't optimize is this way without rewriting chan_calc() to use chip->lfo_am instead of global lfo_am */ { /* triangle */ /* AM: 0 to 126 step +2, 126 to 0 step -2 */ if (pos<64) OPN->LFO_AM = (pos&63) * 2; else OPN->LFO_AM = 126 - ((pos&63) * 2); } /* PM works with 4 times slower clock */ pos >>= 2; /* update PM when LFO output changes */ /*if (prev_pos != pos)*/ /* can't use global lfo_pm for this optimization, must be chip->lfo_pm instead*/ { OPN->LFO_PM = pos; } } else { OPN->LFO_AM = 0; OPN->LFO_PM = 0; } } /* changed from INLINE to static here to work around gcc 4.2.1 codegen bug */ static void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) { unsigned int out; unsigned int swap_flag = 0; unsigned int i; i = 4; /* four operators per channel */ do { /* reset SSG-EG swap flag */ swap_flag = 0; switch(SLOT->state) { case EG_ATT: /* attack phase */ if ( !(OPN->eg_cnt & ((1<eg_sh_ar)-1) ) ) { SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]) ) >>4; if (SLOT->volume <= MIN_ATT_INDEX) { SLOT->volume = MIN_ATT_INDEX; SLOT->state = EG_DEC; } } break; case EG_DEC: /* decay phase */ { if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ { if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) ) { SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; if ( SLOT->volume >= (INT32)(SLOT->sl) ) SLOT->state = EG_SUS; } } else { if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) ) { SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; if ( SLOT->volume >= (INT32)(SLOT->sl) ) SLOT->state = EG_SUS; } } } break; case EG_SUS: /* sustain phase */ if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ { if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) ) { SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; if ( SLOT->volume >= ENV_QUIET ) { SLOT->volume = MAX_ATT_INDEX; if (SLOT->ssg&0x01) /* bit 0 = hold */ { if (SLOT->ssgn&1) /* have we swapped once ??? */ { /* yes, so do nothing, just hold current level */ } else swap_flag = (SLOT->ssg&0x02) | 1 ; /* bit 1 = alternate */ } else { /* same as KEY-ON operation */ /* restart of the Phase Generator should be here */ SLOT->phase = 0; { /* phase -> Attack */ SLOT->volume = 511; SLOT->state = EG_ATT; } swap_flag = (SLOT->ssg&0x02); /* bit 1 = alternate */ } } } } else { if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) ) { SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; if ( SLOT->volume >= MAX_ATT_INDEX ) { SLOT->volume = MAX_ATT_INDEX; /* do not change SLOT->state (verified on real chip) */ } } } break; case EG_REL: /* release phase */ if ( !(OPN->eg_cnt & ((1<eg_sh_rr)-1) ) ) { /* SSG-EG affects Release phase also (Nemesis) */ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; if ( SLOT->volume >= MAX_ATT_INDEX ) { SLOT->volume = MAX_ATT_INDEX; SLOT->state = EG_OFF; } } break; } out = ((UINT32)SLOT->volume); /* negate output (changes come from alternate bit, init comes from attack bit) */ if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) out ^= MAX_ATT_INDEX; /* we need to store the result here because we are going to change ssgn in next instruction */ SLOT->vol_out = out + SLOT->tl; /* reverse SLOT inversion flag */ SLOT->ssgn ^= swap_flag; SLOT++; i--; }while (i); } #define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum) { UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { UINT8 blk; UINT32 fn; int kc, fc; block_fnum = block_fnum*2 + lfo_fn_table_index_offset; blk = (block_fnum&0x7000) >> 12; fn = block_fnum & 0xfff; /* keyscale code */ kc = (blk<<2) | opn_fktable[fn >> 8]; /* phase increment counter */ fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; /* detects frequency overflow (credits to Nemesis) */ if (fc < 0) fc += OPN->fn_max; /* update phase */ SLOT->phase += (fc * SLOT->mul) >> 1; } else /* LFO phase modulation = zero */ { SLOT->phase += SLOT->Incr; } } INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) { UINT32 block_fnum = CH->block_fnum; UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; if (lfo_fn_table_index_offset) /* LFO phase modulation active */ { UINT8 blk; UINT32 fn; int kc, fc, finc; block_fnum = block_fnum*2 + lfo_fn_table_index_offset; blk = (block_fnum&0x7000) >> 12; fn = block_fnum & 0xfff; /* keyscale code */ kc = (blk<<2) | opn_fktable[fn >> 8]; /* phase increment counter */ fc = (OPN->fn_table[fn]>>(7-blk)); /* detects frequency overflow (credits to Nemesis) */ finc = fc + CH->SLOT[SLOT1].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; finc = fc + CH->SLOT[SLOT2].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; finc = fc + CH->SLOT[SLOT3].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; finc = fc + CH->SLOT[SLOT4].DT[kc]; if (finc < 0) finc += OPN->fn_max; CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; } else /* LFO phase modulation = zero */ { CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; } } INLINE void chan_calc(FM_OPN *OPN, FM_CH *CH, int chnum) { unsigned int eg_out; UINT32 AM = OPN->LFO_AM >> CH->ams; if (CH->Muted) return; OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ eg_out = volume_calc(&CH->SLOT[SLOT1]); { INT32 out = CH->op1_out[0] + CH->op1_out[1]; CH->op1_out[0] = CH->op1_out[1]; if( !CH->connect1 ) { /* algorithm 5 */ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; } else { /* other algorithms */ *CH->connect1 += CH->op1_out[0]; } CH->op1_out[1] = 0; if( eg_out < ENV_QUIET ) /* SLOT 1 */ { if (!CH->FB) out=0; CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); } } eg_out = volume_calc(&CH->SLOT[SLOT3]); if( eg_out < ENV_QUIET ) /* SLOT 3 */ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); eg_out = volume_calc(&CH->SLOT[SLOT2]); if( eg_out < ENV_QUIET ) /* SLOT 2 */ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); eg_out = volume_calc(&CH->SLOT[SLOT4]); if( eg_out < ENV_QUIET ) /* SLOT 4 */ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); /* store current MEM */ CH->mem_value = OPN->mem; /* update phase counters AFTER output calculations */ if(CH->pms) { /* add support for 3 slot mode */ if ((OPN->ST.mode & 0xC0) && (chnum == 2)) { update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); } else update_phase_lfo_channel(OPN, CH); } else /* no LFO phase modulation */ { CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; } } /* update phase increment and envelope generator */ INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) { int ksr = kc >> SLOT->KSR; fc += SLOT->DT[kc]; /* detects frequency overflow (credits to Nemesis) */ if (fc < 0) fc += OPN->fn_max; /* (frequency) phase increment counter */ SLOT->Incr = (fc * SLOT->mul) >> 1; if( SLOT->ksr != ksr ) { SLOT->ksr = ksr; /* calculate envelope generator rates */ if ((SLOT->ar + SLOT->ksr) < 32+62) { SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } else { SLOT->eg_sh_ar = 0; SLOT->eg_sel_ar = 17*RATE_STEPS; } SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr]; SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr]; SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; } } /* update phase increment counters */ /* Changed from INLINE to static to work around gcc 4.2.1 codegen bug */ static void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) { if( CH->SLOT[SLOT1].Incr==-1) { int fc = CH->fc; int kc = CH->kcode; refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); } } /* initialize time tables */ static void init_timetables( FM_ST *ST , const UINT8 *dttable ) { int i,d; double rate; #if 0 logerror("FM.C: samplerate=%8i chip clock=%8i freqbase=%f \n", ST->rate, ST->clock, ST->freqbase ); #endif /* DeTune table */ for (d = 0;d <= 3;d++) { for (i = 0;i <= 31;i++) { rate = ((double)dttable[d*32 + i]) * SIN_LEN * ST->freqbase * (1<dt_tab[d][i] = (INT32) rate; ST->dt_tab[d+4][i] = -ST->dt_tab[d][i]; #if 0 logerror("FM.C: DT [%2i %2i] = %8x \n", d, i, ST->dt_tab[d][i] ); #endif } } } static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) { int c,s; ST->mode = 0; /* normal mode */ ST->TA = 0; ST->TAC = 0; ST->TB = 0; ST->TBC = 0; for( c = 0 ; c < num ; c++ ) { /*memset(&CH[c], 0x00, sizeof(FM_CH));*/ CH[c].mem_value = 0; CH[c].op1_out[0] = 0; CH[c].op1_out[1] = 0; CH[c].fc = 0; for(s = 0 ; s < 4 ; s++ ) { /*memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT));*/ CH[c].SLOT[s].Incr = -1; CH[c].SLOT[s].key = 0; CH[c].SLOT[s].phase = 0; CH[c].SLOT[s].ssg = 0; CH[c].SLOT[s].ssgn = 0; CH[c].SLOT[s].state= EG_OFF; CH[c].SLOT[s].volume = MAX_ATT_INDEX; CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; } } } /* initialize generic tables */ static int init_tables(void) { signed int i,x; signed int n; double o,m; for (x=0; x>= 4; /* 12 bits here */ if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; /* 11 bits here (rounded) */ n <<= 2; /* 13 bits here (as in real chip) */ tl_tab[ x*2 + 0 ] = n; tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; for (i=1; i<13; i++) { tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; } #if 0 logerror("tl %04i", x); for (i=0; i<13; i++) logerror(", [%02i] %4x", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]); logerror("\n"); #endif } /*logerror("FM.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/ for (i=0; i0.0) o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ else o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ o = o / (ENV_STEP/4); n = (int)(2.0*o); if (n&1) /* round to nearest */ n = (n>>1)+1; else n = n>>1; sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); /*logerror("FM.C: sin [%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/ } /*logerror("FM.C: ENV_QUIET= %08x\n",ENV_QUIET );*/ /* build LFO PM modulation table */ for(i = 0; i < 8; i++) /* 8 PM depths */ { UINT8 fnum; for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ { UINT8 value; UINT8 step; UINT32 offset_depth = i; UINT32 offset_fnum_bit; UINT32 bit_tmp; for (step=0; step<8; step++) { value = 0; for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ { if (fnum & (1<SLOT[SLOT1].key) { FM_KEYON(type, CH,SLOT1); FM_KEYOFF(CH, SLOT1); } if (!CH->SLOT[SLOT2].key) { FM_KEYON(type, CH,SLOT2); FM_KEYOFF(CH, SLOT2); } if (!CH->SLOT[SLOT3].key) { FM_KEYON(type, CH,SLOT3); FM_KEYOFF(CH, SLOT3); } if (!CH->SLOT[SLOT4].key) { FM_KEYON(type, CH,SLOT4); FM_KEYOFF(CH, SLOT4); } } #ifdef __STATE_H__ /* FM channel save , internal state only */ static void FMsave_state_channel(const device_config *device,FM_CH *CH,int num_ch) { int slot , ch; for(ch=0;chop1_out); state_save_register_device_item(device, ch, CH->fc); /* slots */ for(slot=0;slot<4;slot++) { FM_SLOT *SLOT = &CH->SLOT[slot]; state_save_register_device_item(device, ch * 4 + slot, SLOT->phase); state_save_register_device_item(device, ch * 4 + slot, SLOT->state); state_save_register_device_item(device, ch * 4 + slot, SLOT->volume); } } } static void FMsave_state_st(const device_config *device,FM_ST *ST) { #if FM_BUSY_FLAG_SUPPORT state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds ); state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds ); #endif state_save_register_device_item(device, 0, ST->address ); state_save_register_device_item(device, 0, ST->irq ); state_save_register_device_item(device, 0, ST->irqmask ); state_save_register_device_item(device, 0, ST->status ); state_save_register_device_item(device, 0, ST->mode ); state_save_register_device_item(device, 0, ST->prescaler_sel ); state_save_register_device_item(device, 0, ST->fn_h ); state_save_register_device_item(device, 0, ST->TA ); state_save_register_device_item(device, 0, ST->TAC ); state_save_register_device_item(device, 0, ST->TB ); state_save_register_device_item(device, 0, ST->TBC ); } #endif /* _STATE_H */ #if BUILD_OPN /* prescaler set (and make time tables) */ static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) { int i; /* frequency base */ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; #if 0 OPN->ST.rate = (double)OPN->ST.clock / pres; OPN->ST.freqbase = 1.0; #endif /*OPN->eg_timer_add = (1<ST.freqbase;*/ OPN->eg_timer_add = (UINT32)((1 << EG_SH) * OPN->ST.freqbase); OPN->eg_timer_overflow = ( 3 ) * (1<ST.timer_prescaler = timer_prescaler; /* SSG part prescaler set */ if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); /* make time tables */ init_timetables( &OPN->ST, dt_tab ); /* there are 2048 FNUMs that can be generated using FNUM/BLK registers but LFO works with one more bit of a precision so we really need 4096 elements */ /* calculate fnumber -> increment counter table */ for(i = 0; i < 4096; i++) { /* freq table for octave 7 */ /* OPN phase increment counter = 20bit */ OPN->fn_table[i] = (UINT32)( (double)i * 32 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ #if 0 logerror("FM.C: fn_table[%4i] = %08x (dec=%8i)\n", i, OPN->fn_table[i]>>6,OPN->fn_table[i]>>6 ); #endif } /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ OPN->fn_max = (UINT32)( (double)0x20000 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* LFO freq. table */ for(i = 0; i < 8; i++) { /* Amplitude modulation: 64 output levels (triangle waveform); 1 level lasts for one of "lfo_samples_per_step" samples */ /* Phase modulation: one entry from lfo_pm_output lasts for one of 4 * "lfo_samples_per_step" samples */ /*OPN->lfo_freq[i] = (1.0 / lfo_samples_per_step[i]) * (1<ST.freqbase;*/ OPN->lfo_freq[i] = (UINT32)((1.0 / lfo_samples_per_step[i]) * (1 << LFO_SH) * OPN->ST.freqbase); #if 0 logerror("FM.C: lfo_freq[%i] = %08x (dec=%8i)\n", i, OPN->lfo_freq[i],OPN->lfo_freq[i] ); #endif } } /* write a OPN mode register 0x20-0x2f */ static void OPNWriteMode(FM_OPN *OPN, int r, int v) { UINT8 c; FM_CH *CH; switch(r) { case 0x21: /* Test */ break; case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ if( OPN->type & TYPE_LFOPAN ) { if (v&0x08) /* LFO enabled ? */ { OPN->lfo_inc = OPN->lfo_freq[v&7]; } else { OPN->lfo_inc = 0; } } break; case 0x24: /* timer A High 8*/ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); break; case 0x25: /* timer A Low 2*/ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); break; case 0x26: /* timer B */ OPN->ST.TB = v; break; case 0x27: /* mode, timer control */ set_timers( &(OPN->ST),OPN->ST.param,v ); break; case 0x28: /* key on / off */ c = v & 0x03; if( c == 3 ) break; if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; CH = OPN->P_CH; CH = &CH[c]; if(v&0x10) FM_KEYON(OPN->type,CH,SLOT1); else FM_KEYOFF(CH,SLOT1); if(v&0x20) FM_KEYON(OPN->type,CH,SLOT2); else FM_KEYOFF(CH,SLOT2); if(v&0x40) FM_KEYON(OPN->type,CH,SLOT3); else FM_KEYOFF(CH,SLOT3); if(v&0x80) FM_KEYON(OPN->type,CH,SLOT4); else FM_KEYOFF(CH,SLOT4); break; } } /* write a OPN register (0x30-0xff) */ static void OPNWriteReg(FM_OPN *OPN, int r, int v) { FM_CH *CH; FM_SLOT *SLOT; UINT8 c = OPN_CHAN(r); if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ if (r >= 0x100) c+=3; CH = OPN->P_CH; CH = &CH[c]; SLOT = &(CH->SLOT[OPN_SLOT(r)]); switch( r & 0xf0 ) { case 0x30: /* DET , MUL */ set_det_mul(&OPN->ST,CH,SLOT,v); break; case 0x40: /* TL */ set_tl(CH,SLOT,v); break; case 0x50: /* KS, AR */ set_ar_ksr(OPN->type,CH,SLOT,v); break; case 0x60: /* bit7 = AM ENABLE, DR */ set_dr(OPN->type, SLOT,v); if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ { SLOT->AMmask = (v&0x80) ? ~0 : 0; } break; case 0x70: /* SR */ set_sr(OPN->type,SLOT,v); break; case 0x80: /* SL, RR */ set_sl_rr(OPN->type,SLOT,v); break; case 0x90: /* SSG-EG */ SLOT->ssg = v&0x0f; SLOT->ssgn = (v&0x04)>>1; /* bit 1 in ssgn = attack */ /* SSG-EG envelope shapes : E AtAlH 1 0 0 0 \\\\ 1 0 0 1 \___ 1 0 1 0 \/\/ ___ 1 0 1 1 \ 1 1 0 0 //// ___ 1 1 0 1 / 1 1 1 0 /\/\ 1 1 1 1 /___ E = SSG-EG enable The shapes are generated using Attack, Decay and Sustain phases. Each single character in the diagrams above represents this whole sequence: - when KEY-ON = 1, normal Attack phase is generated (*without* any difference when compared to normal mode), - later, when envelope level reaches minimum level (max volume), the EG switches to Decay phase (which works with bigger steps when compared to normal mode - see below), - later when envelope level passes the SL level, the EG swithes to Sustain phase (which works with bigger steps when compared to normal mode - see below), - finally when envelope level reaches maximum level (min volume), the EG switches to Attack phase again (depends on actual waveform). Important is that when switch to Attack phase occurs, the phase counter of that operator will be zeroed-out (as in normal KEY-ON) but not always. (I havent found the rule for that - perhaps only when the output level is low) The difference (when compared to normal Envelope Generator mode) is that the resolution in Decay and Sustain phases is 4 times lower; this results in only 256 steps instead of normal 1024. In other words: when SSG-EG is disabled, the step inside of the EG is one, when SSG-EG is enabled, the step is four (in Decay and Sustain phases). Times between the level changes are the same in both modes. Important: Decay 1 Level (so called SL) is compared to actual SSG-EG output, so it is the same in both SSG and no-SSG modes, with this exception: when the SSG-EG is enabled and is generating raising levels (when the EG output is inverted) the SL will be found at wrong level !!! For example, when SL=02: 0 -6 = -6dB in non-inverted EG output 96-6 = -90dB in inverted EG output Which means that EG compares its level to SL as usual, and that the output is simply inverted afterall. The Yamaha's manuals say that AR should be set to 0x1f (max speed). That is not necessary, but then EG will be generating Attack phase. */ break; case 0xa0: switch( OPN_SLOT(r) ) { case 0: /* 0xa0-0xa2 : FNUM1 */ { UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v; UINT8 blk = OPN->ST.fn_h>>3; /* keyscale code */ CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; /* phase increment counter */ CH->fc = OPN->fn_table[fn*2]>>(7-blk); /* store fnum in clear form for LFO PM calculations */ CH->block_fnum = (blk<<11) | fn; CH->SLOT[SLOT1].Incr=-1; } break; case 1: /* 0xa4-0xa6 : FNUM2,BLK */ OPN->ST.fn_h = v&0x3f; break; case 2: /* 0xa8-0xaa : 3CH FNUM1 */ if(r < 0x100) { UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v; UINT8 blk = OPN->SL3.fn_h>>3; /* keyscale code */ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; /* phase increment counter */ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); OPN->SL3.block_fnum[c] = (blk<<11) | fn; (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; } break; case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ if(r < 0x100) OPN->SL3.fn_h = v&0x3f; break; } break; case 0xb0: switch( OPN_SLOT(r) ) { case 0: /* 0xb0-0xb2 : FB,ALGO */ { int feedback = (v>>3)&7; CH->ALGO = v&7; CH->FB = feedback ? feedback+6 : 0; setup_connection( OPN, CH, c ); } break; case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ if( OPN->type & TYPE_LFOPAN) { /* b0-2 PMS */ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ /* b4-5 AMS */ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; /* PAN : b7 = L, b6 = R */ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; } break; } break; } } #endif /* BUILD_OPN */ #if BUILD_OPN_PRESCALER /* prescaler circuit (best guess to verified chip behaviour) +--------------+ +-sel2-+ | +--|in20 | +---+ | +-sel1-+ | | M-CLK -+-|1/2|-+--|in10 | +---+ | out|--INT_CLOCK | +---+ | out|-|1/3|-|in21 | +----------|in11 | +---+ +------+ +------+ reg.2d : sel2 = in21 (select sel2) reg.2e : sel1 = in11 (select sel1) reg.2f : sel1 = in10 , sel2 = in20 (clear selector) reset : sel1 = in11 , sel2 = in21 (clear both) */ static void OPNPrescaler_w(FM_OPN *OPN , int addr, int pre_divider) { static const int opn_pres[4] = { 2*12 , 2*12 , 6*12 , 3*12 }; static const int ssg_pres[4] = { 1 , 1 , 4 , 2 }; int sel; switch(addr) { case 0: /* when reset */ OPN->ST.prescaler_sel = 2; break; case 1: /* when postload */ break; case 0x2d: /* divider sel : select 1/1 for 1/3line */ OPN->ST.prescaler_sel |= 0x02; break; case 0x2e: /* divider sel , select 1/3line for output */ OPN->ST.prescaler_sel |= 0x01; break; case 0x2f: /* divider sel , clear both selector to 1/2,1/2 */ OPN->ST.prescaler_sel = 0; break; } sel = OPN->ST.prescaler_sel & 3; /* update prescaler */ OPNSetPres( OPN, opn_pres[sel]*pre_divider, opn_pres[sel]*pre_divider, ssg_pres[sel]*pre_divider ); } #endif /* BUILD_OPN_PRESCALER */ #if BUILD_YM2203 /*****************************************************************************/ /* YM2203 local section */ /*****************************************************************************/ /* here's the virtual YM2203(OPN) */ typedef struct { UINT8 REGS[256]; /* registers */ FM_OPN OPN; /* OPN state */ FM_CH CH[3]; /* channel state */ } YM2203; /* Generate samples for one of the YM2203s */ void ym2203_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2203 *F2203 = (YM2203 *)chip; FM_OPN *OPN = &F2203->OPN; int i; FMSAMPLE *bufL = buffer[0]; FMSAMPLE *bufR = buffer[1]; FM_CH *cch[3]; cch[0] = &F2203->CH[0]; cch[1] = &F2203->CH[1]; cch[2] = &F2203->CH[2]; /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); refresh_fc_eg_chan( OPN, cch[1] ); if( (F2203->OPN.ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[2]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[2] ); /* YM2203 doesn't have LFO so we must keep these globals at 0 level */ OPN->LFO_AM = 0; OPN->LFO_PM = 0; /* buffering */ for (i=0; i < length ; i++) { /* clear outputs */ OPN->out_fm[0] = 0; OPN->out_fm[1] = 0; OPN->out_fm[2] = 0; /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); } /* calculate FM */ chan_calc(OPN, cch[0], 0 ); chan_calc(OPN, cch[1], 1 ); chan_calc(OPN, cch[2], 2 ); /* buffering */ { int lt; lt = OPN->out_fm[0] + OPN->out_fm[1] + OPN->out_fm[2]; lt >>= FINAL_SH; /*Limit( lt , MAXOUT, MINOUT );*/ #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif /* buffering */ bufL[i] = lt; bufR[i] = lt; } /* timer A control */ INTERNAL_TIMER_A( &F2203->OPN.ST , cch[2] ) } INTERNAL_TIMER_B(&F2203->OPN.ST,length) } /* ---------- reset one of chip ---------- */ void ym2203_reset_chip(void *chip) { int i; YM2203 *F2203 = (YM2203 *)chip; FM_OPN *OPN = &F2203->OPN; /* Reset Prescaler */ OPNPrescaler_w(OPN, 0 , 1 ); /* reset SSG section */ (*OPN->ST.SSG->reset)(OPN->ST.param); /* status clear */ FM_IRQMASK_SET(&OPN->ST,0x03); FM_BUSY_CLEAR(&OPN->ST); OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ OPN->eg_timer = 0; OPN->eg_cnt = 0; FM_STATUS_RESET(&OPN->ST, 0xff); reset_channels( &OPN->ST , F2203->CH , 3 ); /* reset OPerator paramater */ for(i = 0xb2 ; i >= 0x30 ; i-- ) OPNWriteReg(OPN,i,0); for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); } #ifdef __STATE_H__ void ym2203_postload(void *chip) { if (chip) { YM2203 *F2203 = (YM2203 *)chip; int r; /* prescaler */ OPNPrescaler_w(&F2203->OPN,1,1); /* SSG registers */ for(r=0;r<16;r++) { (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,0,r); (*F2203->OPN.ST.SSG->write)(F2203->OPN.ST.param,1,F2203->REGS[r]); } /* OPN registers */ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ for(r=0x30;r<0x9e;r++) if((r&3) != 3) OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); /* FB / CONNECT , L / R / AMS / PMS */ for(r=0xb0;r<0xb6;r++) if((r&3) != 3) OPNWriteReg(&F2203->OPN,r,F2203->REGS[r]); /* channels */ /*FM_channel_postload(F2203->CH,3);*/ } } static void YM2203_save_state(YM2203 *F2203, const device_config *device) { state_save_register_device_item_array(device, 0, F2203->REGS); FMsave_state_st(device,&F2203->OPN.ST); FMsave_state_channel(device,F2203->CH,3); /* 3slots */ state_save_register_device_item_array (device, 0, F2203->OPN.SL3.fc); state_save_register_device_item (device, 0, F2203->OPN.SL3.fn_h); state_save_register_device_item_array (device, 0, F2203->OPN.SL3.kcode); } #endif /* _STATE_H */ /* ---------- Initialize YM2203 emulator(s) ---------- 'num' is the number of virtual YM2203s to allocate 'clock' is the chip clock in Hz 'rate' is sampling rate */ /*//void * ym2203_init(void *param, const device_config *device, int clock, int rate, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)*/ void * ym2203_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2203 *F2203; /* allocate ym2203 state space */ if( (F2203 = (YM2203 *)malloc(sizeof(YM2203)))==NULL) return NULL; /* clear */ memset(F2203,0,sizeof(YM2203)); if( !init_tables() ) { free( F2203 ); return NULL; } F2203->OPN.ST.param = param; F2203->OPN.type = TYPE_YM2203; F2203->OPN.P_CH = F2203->CH; /*F2203->OPN.ST.device = device;*/ F2203->OPN.ST.clock = clock; F2203->OPN.ST.rate = rate; F2203->OPN.ST.timer_handler = timer_handler; F2203->OPN.ST.IRQ_Handler = IRQHandler; F2203->OPN.ST.SSG = ssg; #ifdef __STATE_H__ YM2203_save_state(F2203, device); #endif return F2203; } /* shut down emulator */ void ym2203_shutdown(void *chip) { YM2203 *FM2203 = (YM2203 *)chip; FMCloseTable(); free(FM2203); } /* YM2203 I/O interface */ int ym2203_write(void *chip,int a,UINT8 v) { YM2203 *F2203 = (YM2203 *)chip; FM_OPN *OPN = &F2203->OPN; if( !(a&1) ) { /* address port */ OPN->ST.address = (v &= 0xff); /* Write register to SSG emulator */ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v); /* prescaler select : 2d,2e,2f */ if( v >= 0x2d && v <= 0x2f ) OPNPrescaler_w(OPN , v , 1); } else { /* data port */ int addr = OPN->ST.address; F2203->REGS[addr] = v; switch( addr & 0xf0 ) { case 0x00: /* 0x00-0x0f : SSG section */ /* Write data to SSG emulator */ (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x20: /* 0x20-0x2f : Mode section */ ym2203_update_req(OPN->ST.param); /* write register */ OPNWriteMode(OPN,addr,v); break; default: /* 0x30-0xff : OPN section */ ym2203_update_req(OPN->ST.param); /* write register */ OPNWriteReg(OPN,addr,v); } FM_BUSY_SET(&OPN->ST,1); } return OPN->ST.irq; } UINT8 ym2203_read(void *chip,int a) { YM2203 *F2203 = (YM2203 *)chip; int addr = F2203->OPN.ST.address; UINT8 ret = 0; if( !(a&1) ) { /* status port */ ret = FM_STATUS_FLAG(&F2203->OPN.ST); } else { /* data port (only SSG) */ if( addr < 16 ) ret = (*F2203->OPN.ST.SSG->read)(F2203->OPN.ST.param); } return ret; } int ym2203_timer_over(void *chip,int c) { YM2203 *F2203 = (YM2203 *)chip; if( c ) { /* Timer B */ TimerBOver( &(F2203->OPN.ST) ); } else { /* Timer A */ ym2203_update_req(F2203->OPN.ST.param); /* timer update */ TimerAOver( &(F2203->OPN.ST) ); /* CSM mode key,TL control */ if( F2203->OPN.ST.mode & 0x80 ) { /* CSM mode auto key on */ CSMKeyControll( F2203->OPN.type, &(F2203->CH[2]) ); } } return F2203->OPN.ST.irq; } void ym2203_set_mutemask(void *chip, UINT32 MuteMask) { YM2203 *F2203 = (YM2203 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 3; CurChn ++) F2203->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } #endif /* BUILD_YM2203 */ #if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) /* ADPCM type A channel struct */ typedef struct { UINT8 flag; /* port state */ UINT8 flagMask; /* arrived flag mask */ UINT8 now_data; /* current ROM data */ UINT32 now_addr; /* current ROM address */ UINT32 now_step; UINT32 step; UINT32 start; /* sample data start address*/ UINT32 end; /* sample data end address */ UINT8 IL; /* Instrument Level */ INT32 adpcm_acc; /* accumulator */ INT32 adpcm_step; /* step */ INT32 adpcm_out; /* (speedup) hiro-shi!! */ INT8 vol_mul; /* volume in "0.75dB" steps */ UINT8 vol_shift; /* volume in "-6dB" steps */ INT32 *pan; /* &out_adpcm[OPN_xxxx] */ UINT8 Muted; } ADPCM_CH; /* here's the virtual YM2610 */ typedef struct { UINT8 REGS[512]; /* registers */ FM_OPN OPN; /* OPN state */ FM_CH CH[6]; /* channel state */ UINT8 addr_A1; /* address line A1 */ /* ADPCM-A unit */ /*const UINT8 *pcmbuf;*/ /* pcm rom buffer */ UINT8 *pcmbuf; /* pcm rom buffer */ UINT32 pcm_size; /* size of pcm rom */ UINT8 adpcmTL; /* adpcmA total level */ ADPCM_CH adpcm[6]; /* adpcm channels */ UINT32 adpcmreg[0x30]; /* registers */ UINT8 adpcm_arrivedEndAddress; YM_DELTAT deltaT; /* Delta-T ADPCM unit */ UINT8 MuteDeltaT; UINT8 flagmask; /* YM2608 only */ UINT8 irqmask; /* YM2608 only */ } YM2610; /* here is the virtual YM2608 */ typedef YM2610 YM2608; /**** YM2610 ADPCM defines ****/ #define ADPCM_SHIFT (16) /* frequency step rate */ #define ADPCMA_ADDRESS_SHIFT 8 /* adpcm A address shift */ /* Algorithm and tables verified on real YM2608 and YM2610 */ /* usual ADPCM table (16 * 1.1^N) */ static const int steps[49] = { 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 }; /* different from the usual ADPCM table */ static const int step_inc[8] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 }; /* speedup purposes only */ static int jedi_table[ 49*16 ]; static void Init_ADPCMATable(void) { int step, nib; for (step = 0; step < 49; step++) { /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { int value = (2*(nib & 0x07) + 1) * steps[step] / 8; jedi_table[step*16 + nib] = (nib&0x08) ? -value : value; } } } /* ADPCM A (Non control type) : calculate one channel output */ INLINE void ADPCMA_calc_chan( YM2610 *F2610, ADPCM_CH *ch ) { UINT32 step; UINT8 data; if (ch->Muted) return; ch->now_step += ch->step; if ( ch->now_step >= (1<now_step >> ADPCM_SHIFT; ch->now_step &= (1< instead of == */ /* YM2610 checks lower 20 bits only, the 4 MSB bits are sample bank */ /* Here we use 1<<21 to compensate for nibble calculations */ if ( (ch->now_addr & ((1<<21)-1)) == ((ch->end<<1) & ((1<<21)-1)) ) { ch->flag = 0; F2610->adpcm_arrivedEndAddress |= ch->flagMask; return; } #if 0 if ( ch->now_addr > (F2610->pcmsizeA<<1) ) { #ifdef _DEBUG LOG(LOG_WAR,("YM2610: Attempting to play past adpcm rom size!\n" )); #endif return; } #endif if ( ch->now_addr&1 ) data = ch->now_data & 0x0f; else { ch->now_data = *(F2610->pcmbuf+(ch->now_addr>>1)); data = (ch->now_data >> 4) & 0x0f; } ch->now_addr++; ch->adpcm_acc += jedi_table[ch->adpcm_step + data]; /* the 12-bit accumulator wraps on the ym2610 and ym2608 (like the msm5205), it does not saturate (like the msm5218) */ ch->adpcm_acc &= 0xfff; /* extend 12-bit signed int */ if (ch->adpcm_acc & 0x800) ch->adpcm_acc |= ~0xfff; ch->adpcm_step += step_inc[data & 7]; Limit( ch->adpcm_step, 48*16, 0*16 ); }while(--step); /* calc pcm * volume data */ ch->adpcm_out = ((ch->adpcm_acc * ch->vol_mul) >> ch->vol_shift) & ~3; /* multiply, shift and mask out 2 LSB bits */ } /* output for work of output channels (out_adpcm[OPNxxxx])*/ *(ch->pan) += ch->adpcm_out; } /* ADPCM type A Write */ static void FM_ADPCMAWrite(YM2610 *F2610,int r,int v) { ADPCM_CH *adpcm = F2610->adpcm; UINT8 c = r&0x07; F2610->adpcmreg[r] = v&0xff; /* stock data */ switch( r ) { case 0x00: /* DM,--,C5,C4,C3,C2,C1,C0 */ if( !(v&0x80) ) { /* KEY ON */ for( c = 0; c < 6; c++ ) { if( (v>>c)&1 ) { /**** start adpcm ****/ /* The .step variable is already set and for the YM2608 it is different on channels 4 and 5. */ /*adpcm[c].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0);*/ adpcm[c].now_addr = adpcm[c].start<<1; adpcm[c].now_step = 0; adpcm[c].adpcm_acc = 0; adpcm[c].adpcm_step= 0; adpcm[c].adpcm_out = 0; adpcm[c].flag = 1; if(F2610->pcmbuf==NULL) { /* Check ROM Mapped */ #ifdef _DEBUG logerror("YM2608-YM2610: ADPCM-A rom not mapped\n"); #endif adpcm[c].flag = 0; } else { if(adpcm[c].end >= F2610->pcm_size) { /* Check End in Range */ #ifdef _DEBUG logerror("YM2610: ADPCM-A end out of range: $%08x\n",adpcm[c].end); #endif /*adpcm[c].end = F2610->pcm_size-1;*/ /* JB: DO NOT uncomment this, otherwise you will break the comparison in the ADPCM_CALC_CHA() */ } if(adpcm[c].start >= F2610->pcm_size) /* Check Start in Range */ { #ifdef _DEBUG logerror("YM2608-YM2610: ADPCM-A start out of range: $%08x\n",adpcm[c].start); #endif adpcm[c].flag = 0; } } } } } else { /* KEY OFF */ for( c = 0; c < 6; c++ ) if( (v>>c)&1 ) adpcm[c].flag = 0; } break; case 0x01: /* B0-5 = TL */ F2610->adpcmTL = (v & 0x3f) ^ 0x3f; for( c = 0; c < 6; c++ ) { int volume = F2610->adpcmTL + adpcm[c].IL; if ( volume >= 63 ) /* This is correct, 63 = quiet */ { adpcm[c].vol_mul = 0; adpcm[c].vol_shift = 0; } else { adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */ } /* calc pcm * volume data */ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */ } break; default: c = r&0x07; if( c >= 0x06 ) return; switch( r&0x38 ) { case 0x08: /* B7=L,B6=R, B4-0=IL */ { int volume; adpcm[c].IL = (v & 0x1f) ^ 0x1f; volume = F2610->adpcmTL + adpcm[c].IL; if ( volume >= 63 ) /* This is correct, 63 = quiet */ { adpcm[c].vol_mul = 0; adpcm[c].vol_shift = 0; } else { adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */ } adpcm[c].pan = &F2610->OPN.out_adpcm[(v>>6)&0x03]; /* calc pcm * volume data */ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */ } break; case 0x10: case 0x18: adpcm[c].start = ( (F2610->adpcmreg[0x18 + c]*0x0100 | F2610->adpcmreg[0x10 + c]) << ADPCMA_ADDRESS_SHIFT); break; case 0x20: case 0x28: adpcm[c].end = ( (F2610->adpcmreg[0x28 + c]*0x0100 | F2610->adpcmreg[0x20 + c]) << ADPCMA_ADDRESS_SHIFT); adpcm[c].end += (1<flag); state_save_register_device_item(device, ch, adpcm->now_data); state_save_register_device_item(device, ch, adpcm->now_addr); state_save_register_device_item(device, ch, adpcm->now_step); state_save_register_device_item(device, ch, adpcm->adpcm_acc); state_save_register_device_item(device, ch, adpcm->adpcm_step); state_save_register_device_item(device, ch, adpcm->adpcm_out); } } #endif /* _STATE_H */ #endif /* (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) */ #if BUILD_YM2608 /*****************************************************************************/ /* YM2608 local section */ /*****************************************************************************/ const unsigned int YM2608_ADPCM_ROM_addr[2*6] = { 0x0000, 0x01bf, /* bass drum */ 0x01c0, 0x043f, /* snare drum */ 0x0440, 0x1b7f, /* top cymbal */ 0x1b80, 0x1cff, /* high hat */ 0x1d00, 0x1f7f, /* tom tom */ 0x1f80, 0x1fff /* rim shot */ }; /* This data is derived from the chip's output - internal ROM can't be read. It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal. */ const unsigned char YM2608_ADPCM_ROM[0x2000] = { /* Source: 01BD.ROM */ /* Length: 448 / 0x000001C0 */ 0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54, 0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97, 0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0, 0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1, 0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24, 0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99, 0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99, 0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09, 0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8, 0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39, 0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9, 0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A, 0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40, 0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00, 0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12, 0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18, 0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52, 0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02, 0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B, 0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC, 0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A, 0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98, 0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A, 0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99, 0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99, 0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11, 0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31, 0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80, /* Source: 02SD.ROM */ /* Length: 640 / 0x00000280 */ 0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1, 0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41, 0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03, 0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18, 0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02, 0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92, 0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28, 0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62, 0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E, 0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09, 0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB, 0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83, 0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85, 0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21, 0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0, 0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80, 0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21, 0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8, 0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F, 0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A, 0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12, 0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36, 0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80, 0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90, 0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99, 0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03, 0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89, 0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18, 0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27, 0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9, 0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39, 0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23, 0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1, 0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3, 0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9, 0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71, 0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A, 0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88, 0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C, 0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10, /* Source: 04TOP.ROM */ /* Length: 5952 / 0x00001740 */ 0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C, 0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00, 0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E, 0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C, 0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38, 0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81, 0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A, 0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81, 0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4, 0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8, 0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10, 0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29, 0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98, 0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59, 0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3, 0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28, 0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B, 0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B, 0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11, 0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0, 0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90, 0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00, 0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8, 0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29, 0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F, 0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12, 0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8, 0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4, 0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82, 0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11, 0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09, 0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A, 0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3, 0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85, 0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88, 0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80, 0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C, 0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10, 0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81, 0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81, 0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8, 0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A, 0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79, 0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49, 0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA, 0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99, 0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24, 0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7, 0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8, 0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0, 0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15, 0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18, 0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B, 0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C, 0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B, 0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7, 0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14, 0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02, 0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98, 0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88, 0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01, 0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2, 0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D, 0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19, 0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28, 0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21, 0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A, 0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2, 0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87, 0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11, 0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C, 0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A, 0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0, 0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1, 0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11, 0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90, 0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00, 0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2, 0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3, 0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82, 0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00, 0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B, 0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28, 0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79, 0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90, 0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1, 0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05, 0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10, 0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19, 0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D, 0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5, 0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90, 0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49, 0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C, 0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C, 0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0, 0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3, 0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4, 0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4, 0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9, 0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01, 0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88, 0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12, 0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D, 0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02, 0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09, 0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89, 0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A, 0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2, 0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49, 0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50, 0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80, 0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C, 0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C, 0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13, 0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01, 0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18, 0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C, 0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1, 0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3, 0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B, 0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E, 0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90, 0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28, 0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28, 0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E, 0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91, 0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00, 0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71, 0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20, 0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8, 0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03, 0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7, 0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C, 0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09, 0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12, 0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2, 0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6, 0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02, 0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B, 0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC, 0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0, 0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00, 0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A, 0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2, 0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3, 0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F, 0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A, 0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0, 0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84, 0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10, 0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21, 0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F, 0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B, 0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24, 0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3, 0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00, 0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93, 0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10, 0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30, 0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0, 0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7, 0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1, 0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19, 0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B, 0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99, 0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A, 0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C, 0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03, 0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1, 0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A, 0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19, 0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8, 0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11, 0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A, 0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59, 0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F, 0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C, 0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80, 0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5, 0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08, 0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29, 0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1, 0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28, 0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D, 0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A, 0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D, 0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48, 0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A, 0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3, 0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1, 0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18, 0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C, 0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A, 0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19, 0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91, 0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04, 0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29, 0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10, 0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1, 0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0, 0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84, 0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95, 0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11, 0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B, 0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19, 0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10, 0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1, 0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17, 0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A, 0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08, 0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C, 0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1, 0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2, 0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09, 0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20, 0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A, 0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D, 0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82, 0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1, 0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2, 0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99, 0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18, 0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1, 0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1, 0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01, 0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40, 0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A, 0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C, 0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83, 0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12, 0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09, 0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68, 0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92, 0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A, 0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80, 0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82, 0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80, 0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B, 0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B, 0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18, 0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91, 0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1, 0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6, 0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B, 0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89, 0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90, 0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A, 0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01, 0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98, 0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29, 0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D, 0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92, 0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93, 0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88, 0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79, 0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20, 0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59, 0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9, 0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8, 0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07, 0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13, 0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80, 0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB, 0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A, 0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20, 0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0, 0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20, 0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39, 0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92, 0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1, 0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0, 0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41, 0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC, 0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30, 0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18, 0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B, 0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A, 0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09, 0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10, 0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1, 0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96, 0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09, 0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09, 0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A, 0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B, 0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90, 0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0, 0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1, 0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28, 0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18, 0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF, 0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0, 0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02, 0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08, 0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96, 0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88, 0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28, 0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D, 0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0, 0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83, 0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3, 0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04, 0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2, 0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21, 0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98, 0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8, 0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23, 0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39, 0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08, 0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E, 0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14, 0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4, 0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0, 0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88, 0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0, 0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69, 0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08, 0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13, 0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0, 0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA, 0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81, 0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01, 0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B, 0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B, 0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4, 0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18, 0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01, 0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0, 0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5, 0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94, 0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A, 0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A, 0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88, 0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B, 0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01, 0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12, 0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12, 0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D, 0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02, 0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1, 0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82, 0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1, 0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A, 0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A, 0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F, 0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98, 0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1, 0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3, 0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68, 0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91, 0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A, 0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C, 0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90, 0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6, 0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89, 0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B, 0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA, 0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1, 0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93, 0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93, 0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00, 0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B, 0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2, 0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92, 0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89, 0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48, 0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10, 0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29, 0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91, 0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3, 0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90, /* Source: 08HH.ROM */ /* Length: 384 / 0x00000180 */ 0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3, 0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7, 0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D, 0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80, 0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B, 0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2, 0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91, 0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D, 0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C, 0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5, 0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B, 0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01, 0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92, 0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93, 0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4, 0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3, 0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08, 0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3, 0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88, 0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92, 0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00, 0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4, 0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4, 0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0, /* Source: 10TOM.ROM */ /* Length: 640 / 0x00000280 */ 0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2, 0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11, 0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1, 0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32, 0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9, 0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01, 0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14, 0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA, 0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81, 0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00, 0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63, 0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB, 0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34, 0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB, 0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31, 0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99, 0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A, 0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30, 0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02, 0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98, 0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11, 0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11, 0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A, 0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35, 0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11, 0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB, 0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42, 0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99, 0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20, 0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81, 0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31, 0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC, 0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33, 0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB, 0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31, 0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99, 0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99, 0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88, 0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12, 0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* Source: 20RIM.ROM */ /* Length: 128 / 0x00000080 */ 0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A, 0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2, 0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C, 0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91, 0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59, 0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92, 0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6, 0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80, }; /* flag enable control 0x110 */ INLINE void YM2608IRQFlagWrite(FM_OPN *OPN, YM2608 *F2608, int v) { if( v & 0x80 ) { /* Reset IRQ flag */ FM_STATUS_RESET(&OPN->ST, 0xf7); /* don't touch BUFRDY flag otherwise we'd have to call ymdeltat module to set the flag back */ } else { /* Set status flag mask */ F2608->flagmask = (~(v&0x1f)); FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) ); } } /* compatible mode & IRQ enable control 0x29 */ INLINE void YM2608IRQMaskWrite(FM_OPN *OPN, YM2608 *F2608, int v) { /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */ /* extend 3ch. enable/disable */ if(v&0x80) OPN->type |= TYPE_6CH; /* OPNA mode - 6 FM channels */ else OPN->type &= ~TYPE_6CH; /* OPN mode - 3 FM channels */ /* IRQ MASK store and set */ F2608->irqmask = v&0x1f; FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) ); } /* Generate samples for one of the YM2608s */ void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2608 *F2608 = (YM2608 *)chip; FM_OPN *OPN = &F2608->OPN; YM_DELTAT *DELTAT = &F2608->deltaT; int i,j; FMSAMPLE *bufL,*bufR; FM_CH *cch[6]; INT32 *out_fm = OPN->out_fm; /* set bufer */ bufL = buffer[0]; bufR = buffer[1]; cch[0] = &F2608->CH[0]; cch[1] = &F2608->CH[1]; cch[2] = &F2608->CH[2]; cch[3] = &F2608->CH[3]; cch[4] = &F2608->CH[4]; cch[5] = &F2608->CH[5]; /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); refresh_fc_eg_chan( OPN, cch[1] ); if( (OPN->ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[2]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[2] ); refresh_fc_eg_chan( OPN, cch[3] ); refresh_fc_eg_chan( OPN, cch[4] ); refresh_fc_eg_chan( OPN, cch[5] ); /* buffering */ for(i=0; i < length ; i++) { advance_lfo(OPN); /* clear output acc. */ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; /* clear outputs */ out_fm[0] = 0; out_fm[1] = 0; out_fm[2] = 0; out_fm[3] = 0; out_fm[4] = 0; out_fm[5] = 0; /* calculate FM */ chan_calc(OPN, cch[0], 0 ); chan_calc(OPN, cch[1], 1 ); chan_calc(OPN, cch[2], 2 ); chan_calc(OPN, cch[3], 3 ); chan_calc(OPN, cch[4], 4 ); chan_calc(OPN, cch[5], 5 ); /* deltaT ADPCM */ if( DELTAT->portstate&0x80 && ! F2608->MuteDeltaT ) YM_DELTAT_ADPCM_CALC(DELTAT); /* ADPCMA */ for( j = 0; j < 6; j++ ) { if( F2608->adpcm[j].flag ) ADPCMA_calc_chan( F2608, &F2608->adpcm[j]); } /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]); } /* buffering */ { int lt,rt; /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; lt += ((out_fm[0]>>1) & OPN->pan[0]); // shift right verified on real YM2608 rt += ((out_fm[0]>>1) & OPN->pan[1]); lt += ((out_fm[1]>>1) & OPN->pan[2]); rt += ((out_fm[1]>>1) & OPN->pan[3]); lt += ((out_fm[2]>>1) & OPN->pan[4]); rt += ((out_fm[2]>>1) & OPN->pan[5]); lt += ((out_fm[3]>>1) & OPN->pan[6]); rt += ((out_fm[3]>>1) & OPN->pan[7]); lt += ((out_fm[4]>>1) & OPN->pan[8]); rt += ((out_fm[4]>>1) & OPN->pan[9]); lt += ((out_fm[5]>>1) & OPN->pan[10]); rt += ((out_fm[5]>>1) & OPN->pan[11]);*/ /* this way it's louder (and more accurate) */ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1; rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8; lt += (out_fm[0] & OPN->pan[0]); rt += (out_fm[0] & OPN->pan[1]); lt += (out_fm[1] & OPN->pan[2]); rt += (out_fm[1] & OPN->pan[3]); lt += (out_fm[2] & OPN->pan[4]); rt += (out_fm[2] & OPN->pan[5]); lt += (out_fm[3] & OPN->pan[6]); rt += (out_fm[3] & OPN->pan[7]); lt += (out_fm[4] & OPN->pan[8]); rt += (out_fm[4] & OPN->pan[9]); lt += (out_fm[5] & OPN->pan[10]); rt += (out_fm[5] & OPN->pan[11]); lt >>= FINAL_SH; rt >>= FINAL_SH; /*Limit( lt, MAXOUT, MINOUT );*/ /*Limit( rt, MAXOUT, MINOUT );*/ /* buffering */ bufL[i] = lt; bufR[i] = rt; #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif } /* timer A control */ INTERNAL_TIMER_A( &OPN->ST , cch[2] ) } INTERNAL_TIMER_B(&OPN->ST,length) /* check IRQ for DELTA-T EOS */ FM_STATUS_SET(&OPN->ST, 0); } #ifdef __STATE_H__ void ym2608_postload(void *chip) { if (chip) { YM2608 *F2608 = (YM2608 *)chip; int r; /* prescaler */ OPNPrescaler_w(&F2608->OPN,1,2); F2608->deltaT.freqbase = F2608->OPN.ST.freqbase; /* IRQ mask / mode */ YM2608IRQMaskWrite(&F2608->OPN, F2608, F2608->REGS[0x29]); /* SSG registers */ for(r=0;r<16;r++) { (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,0,r); (*F2608->OPN.ST.SSG->write)(F2608->OPN.ST.param,1,F2608->REGS[r]); } /* OPN registers */ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ for(r=0x30;r<0x9e;r++) if((r&3) != 3) { OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); } /* FB / CONNECT , L / R / AMS / PMS */ for(r=0xb0;r<0xb6;r++) if((r&3) != 3) { OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]); OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]); } /* FM channels */ /*FM_channel_postload(F2608->CH,6);*/ /* rhythm(ADPCMA) */ FM_ADPCMAWrite(F2608,1,F2608->REGS[0x111]); for( r=0x08 ; r<0x0c ; r++) FM_ADPCMAWrite(F2608,r,F2608->REGS[r+0x110]); /* Delta-T ADPCM unit */ YM_DELTAT_postload(&F2608->deltaT , &F2608->REGS[0x100] ); } } static void YM2608_save_state(YM2608 *F2608, const device_config *device) { state_save_register_device_item_array(device, 0, F2608->REGS); FMsave_state_st(device,&F2608->OPN.ST); FMsave_state_channel(device,F2608->CH,6); /* 3slots */ state_save_register_device_item_array(device, 0, F2608->OPN.SL3.fc); state_save_register_device_item(device, 0, F2608->OPN.SL3.fn_h); state_save_register_device_item_array(device, 0, F2608->OPN.SL3.kcode); /* address register1 */ state_save_register_device_item(device, 0, F2608->addr_A1); /* rythm(ADPCMA) */ FMsave_state_adpcma(device,F2608->adpcm); /* Delta-T ADPCM unit */ YM_DELTAT_savestate(device,&F2608->deltaT); } #endif /* _STATE_H */ static void YM2608_deltat_status_set(void *chip, UINT8 changebits) { YM2608 *F2608 = (YM2608 *)chip; FM_STATUS_SET(&(F2608->OPN.ST), changebits); } static void YM2608_deltat_status_reset(void *chip, UINT8 changebits) { YM2608 *F2608 = (YM2608 *)chip; FM_STATUS_RESET(&(F2608->OPN.ST), changebits); } /* YM2608(OPNA) */ /*//void * ym2608_init(void *param, const device_config *device, int clock, int rate, // void *pcmrom,int pcmsize, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)*/ /*void * ym2608_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)*/ void * ym2608_init(void *param, int clock, int rate, offs_t dram_size, FM_TIMERHANDLER timer_handler, FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2608 *F2608; /* allocate extend state space */ if( (F2608 = (YM2608 *)malloc(sizeof(YM2608)))==NULL) return NULL; /* clear */ memset(F2608,0,sizeof(YM2608)); /* allocate total level table (128kb space) */ if( !init_tables() ) { free( F2608 ); return NULL; } F2608->OPN.ST.param = param; F2608->OPN.type = TYPE_YM2608; F2608->OPN.P_CH = F2608->CH; /*F2608->OPN.ST.device = device;*/ F2608->OPN.ST.clock = clock; F2608->OPN.ST.rate = rate; /* External handlers */ F2608->OPN.ST.timer_handler = timer_handler; F2608->OPN.ST.IRQ_Handler = IRQHandler; F2608->OPN.ST.SSG = ssg; /* DELTA-T */ /*//F2608->deltaT.memory = (UINT8 *)pcmrom; //F2608->deltaT.memory_size = pcmsize; //F2608->deltaT.memory = NULL; //F2608->deltaT.memory_size = 0x00; //F2608->deltaT.memory_mask = 0x00;*/ F2608->deltaT.memory = (UINT8*)realloc(F2608->deltaT.memory, dram_size); F2608->deltaT.memory_size = dram_size; YM_DELTAT_calc_mem_mask(&F2608->deltaT); /*F2608->deltaT.write_time = 20.0 / clock;*/ /* a single byte write takes 20 cycles of main clock */ /*F2608->deltaT.read_time = 18.0 / clock;*/ /* a single byte read takes 18 cycles of main clock */ F2608->deltaT.status_set_handler = YM2608_deltat_status_set; F2608->deltaT.status_reset_handler = YM2608_deltat_status_reset; F2608->deltaT.status_change_which_chip = F2608; F2608->deltaT.status_change_EOS_bit = 0x04; /* status flag: set bit2 on End Of Sample */ F2608->deltaT.status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY */ F2608->deltaT.status_change_ZERO_bit = 0x10; /* status flag: set bit4 if silence continues for more than 290 miliseconds while recording the ADPCM */ /* ADPCM Rhythm */ F2608->pcmbuf = (UINT8*)YM2608_ADPCM_ROM; F2608->pcm_size = 0x2000; Init_ADPCMATable(); #ifdef __STATE_H__ YM2608_save_state(F2608, device); #endif return F2608; } /* shut down emulator */ void ym2608_shutdown(void *chip) { YM2608 *F2608 = (YM2608 *)chip; free(F2608->deltaT.memory); F2608->deltaT.memory = NULL; FMCloseTable(); free(F2608); } /* reset one of chips */ void ym2608_reset_chip(void *chip) { int i; YM2608 *F2608 = (YM2608 *)chip; FM_OPN *OPN = &F2608->OPN; YM_DELTAT *DELTAT = &F2608->deltaT; /* Reset Prescaler */ OPNPrescaler_w(OPN , 0 , 2); F2608->deltaT.freqbase = OPN->ST.freqbase; /* reset SSG section */ (*OPN->ST.SSG->reset)(OPN->ST.param); /* status clear */ FM_BUSY_CLEAR(&OPN->ST); /* register 0x29 - default value after reset is: enable only 3 FM channels and enable all the status flags */ YM2608IRQMaskWrite(OPN, F2608, 0x1f ); /* default value for D4-D0 is 1 */ /* register 0x10, A1=1 - default value is 1 for D4, D3, D2, 0 for the rest */ YM2608IRQFlagWrite(OPN, F2608, 0x1c ); /* default: enable timer A and B, disable EOS, BRDY and ZERO */ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ OPN->eg_timer = 0; OPN->eg_cnt = 0; FM_STATUS_RESET(&OPN->ST, 0xff); reset_channels( &OPN->ST , F2608->CH , 6 ); /* reset OPerator paramater */ for(i = 0xb6 ; i >= 0xb4 ; i-- ) { OPNWriteReg(OPN,i ,0xc0); OPNWriteReg(OPN,i|0x100,0xc0); } for(i = 0xb2 ; i >= 0x30 ; i-- ) { OPNWriteReg(OPN,i ,0); OPNWriteReg(OPN,i|0x100,0); } for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); /* ADPCM - percussion sounds */ for( i = 0; i < 6; i++ ) { if (i<=3) /* channels 0,1,2,3 */ F2608->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0); else /* channels 4 and 5 work with slower clock */ F2608->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/6.0); F2608->adpcm[i].start = YM2608_ADPCM_ROM_addr[i*2]; F2608->adpcm[i].end = YM2608_ADPCM_ROM_addr[i*2+1]; F2608->adpcm[i].now_addr = 0; F2608->adpcm[i].now_step = 0; /* F2608->adpcm[i].delta = 21866; */ F2608->adpcm[i].vol_mul = 0; F2608->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */ F2608->adpcm[i].flagMask = 0; F2608->adpcm[i].flag = 0; F2608->adpcm[i].adpcm_acc = 0; F2608->adpcm[i].adpcm_step= 0; F2608->adpcm[i].adpcm_out = 0; } F2608->adpcmTL = 0x3f; F2608->adpcm_arrivedEndAddress = 0; /* not used */ /* DELTA-T unit */ DELTAT->freqbase = OPN->ST.freqbase; DELTAT->output_pointer = OPN->out_delta; DELTAT->portshift = 5; /* always 5bits shift */ /* ASG */ DELTAT->output_range = 1<<23; YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_NORMAL); } /* YM2608 write */ /* n = number */ /* a = address */ /* v = value */ int ym2608_write(void *chip, int a,UINT8 v) { YM2608 *F2608 = (YM2608 *)chip; FM_OPN *OPN = &F2608->OPN; int addr; v &= 0xff; /*adjust to 8 bit bus */ switch(a&3) { case 0: /* address port 0 */ OPN->ST.address = v; F2608->addr_A1 = 0; /* Write register to SSG emulator */ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v); /* prescaler selecter : 2d,2e,2f */ if( v >= 0x2d && v <= 0x2f ) { OPNPrescaler_w(OPN , v , 2); /*TODO: set ADPCM[c].step*/ F2608->deltaT.freqbase = OPN->ST.freqbase; } break; case 1: /* data port 0 */ if (F2608->addr_A1 != 0) break; /* verified on real YM2608 */ addr = OPN->ST.address; F2608->REGS[addr] = v; switch(addr & 0xf0) { case 0x00: /* SSG section */ /* Write data to SSG emulator */ (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x10: /* 0x10-0x1f : Rhythm section */ ym2608_update_req(OPN->ST.param); FM_ADPCMAWrite(F2608,addr-0x10,v); break; case 0x20: /* Mode Register */ switch(addr) { case 0x29: /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */ YM2608IRQMaskWrite(OPN, F2608, v); break; default: ym2608_update_req(OPN->ST.param); OPNWriteMode(OPN,addr,v); } break; default: /* OPN section */ ym2608_update_req(OPN->ST.param); OPNWriteReg(OPN,addr,v); } break; case 2: /* address port 1 */ OPN->ST.address = v; F2608->addr_A1 = 1; break; case 3: /* data port 1 */ if (F2608->addr_A1 != 1) break; /* verified on real YM2608 */ addr = OPN->ST.address; F2608->REGS[addr | 0x100] = v; ym2608_update_req(OPN->ST.param); switch( addr & 0xf0 ) { case 0x00: /* DELTAT PORT */ switch( addr ) { case 0x0e: /* DAC data */ #ifdef _DEBUG logerror("YM2608: write to DAC data (unimplemented) value=%02x\n",v); #endif break; default: /* 0x00-0x0d */ YM_DELTAT_ADPCM_Write(&F2608->deltaT,addr,v); } break; case 0x10: /* IRQ Flag control */ if( addr == 0x10 ) { YM2608IRQFlagWrite(OPN, F2608, v); } break; default: OPNWriteReg(OPN,addr | 0x100,v); } } return OPN->ST.irq; } UINT8 ym2608_read(void *chip,int a) { YM2608 *F2608 = (YM2608 *)chip; int addr = F2608->OPN.ST.address; UINT8 ret = 0; switch( a&3 ) { case 0: /* status 0 : YM2203 compatible */ /* BUSY:x:x:x:x:x:FLAGB:FLAGA */ ret = FM_STATUS_FLAG(&F2608->OPN.ST) & 0x83; break; case 1: /* status 0, ID */ if( addr < 16 ) ret = (*F2608->OPN.ST.SSG->read)(F2608->OPN.ST.param); else if(addr == 0xff) ret = 0x01; /* ID code */ break; case 2: /* status 1 : status 0 + ADPCM status */ /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */ ret = (FM_STATUS_FLAG(&F2608->OPN.ST) & (F2608->flagmask|0x80)) | ((F2608->deltaT.PCM_BSY & 1)<<5) ; break; case 3: if(addr == 0x08) { ret = YM_DELTAT_ADPCM_Read(&F2608->deltaT); } else { if(addr == 0x0f) { #ifdef _DEBUG logerror("YM2608 A/D conversion is accessed but not implemented !\n"); #endif ret = 0x80; /* 2's complement PCM data - result from A/D conversion */ } } break; } return ret; } int ym2608_timer_over(void *chip,int c) { YM2608 *F2608 = (YM2608 *)chip; switch(c) { #if 0 case 2: { /* BUFRDY flag */ YM_DELTAT_BRDY_callback( &F2608->deltaT ); } break; #endif case 1: { /* Timer B */ TimerBOver( &(F2608->OPN.ST) ); } break; case 0: { /* Timer A */ ym2608_update_req(F2608->OPN.ST.param); /* timer update */ TimerAOver( &(F2608->OPN.ST) ); /* CSM mode key,TL controll */ if( F2608->OPN.ST.mode & 0x80 ) { /* CSM mode total level latch and auto key on */ CSMKeyControll( F2608->OPN.type, &(F2608->CH[2]) ); } } break; default: break; } return F2608->OPN.ST.irq; } void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { YM2608 *F2608 = (YM2608 *)chip; switch(rom_id) { case 0x01: /* ADPCM */ /* unused, it's constant */ break; case 0x02: /* DELTA-T */ if (F2608->deltaT.memory_size != ROMSize) { F2608->deltaT.memory = (UINT8*)realloc(F2608->deltaT.memory, ROMSize); F2608->deltaT.memory_size = ROMSize; memset(F2608->deltaT.memory, 0xFF, ROMSize); YM_DELTAT_calc_mem_mask(&F2608->deltaT); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(F2608->deltaT.memory + DataStart, ROMData, DataLength); break; } return; } void ym2608_set_mutemask(void *chip, UINT32 MuteMask) { YM2608 *F2608 = (YM2608 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) F2608->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; for (CurChn = 0; CurChn < 6; CurChn ++) F2608->adpcm[CurChn].Muted = (MuteMask >> (CurChn + 6)) & 0x01; F2608->MuteDeltaT = (MuteMask >> 12) & 0x01; return; } #endif /* BUILD_YM2608 */ #if (BUILD_YM2610||BUILD_YM2610B) /* YM2610(OPNB) */ /* Generate samples for one of the YM2610s */ void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; YM_DELTAT *DELTAT = &F2610->deltaT; int i,j; FMSAMPLE *bufL,*bufR; FM_CH *cch[4]; INT32 *out_fm = OPN->out_fm; /* buffer setup */ bufL = buffer[0]; bufR = buffer[1]; cch[0] = &F2610->CH[1]; cch[1] = &F2610->CH[2]; cch[2] = &F2610->CH[4]; cch[3] = &F2610->CH[5]; #ifdef YM2610B_WARNING #define FM_KEY_IS(SLOT) ((SLOT)->key) #define FM_MSG_YM2610B "YM2610-%p.CH%d is playing,Check whether the type of the chip is YM2610B\n" /* Check YM2610B warning message */ if( FM_KEY_IS(&F2610->CH[0].SLOT[3]) ) { LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,0)); FM_KEY_IS(&F2610->CH[0].SLOT[3]) = 0; } if( FM_KEY_IS(&F2610->CH[3].SLOT[3]) ) { LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,3)); FM_KEY_IS(&F2610->CH[3].SLOT[3]) = 0; } #endif /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); if( (OPN->ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[1]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT4] , cch[1]->fc , cch[1]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[1] ); refresh_fc_eg_chan( OPN, cch[2] ); refresh_fc_eg_chan( OPN, cch[3] ); /* buffering */ for(i=0; i < length ; i++) { advance_lfo(OPN); /* clear output acc. */ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; /* clear outputs */ out_fm[1] = 0; out_fm[2] = 0; out_fm[4] = 0; out_fm[5] = 0; /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); } /* calculate FM */ chan_calc(OPN, cch[0], 1 ); /*remapped to 1*/ chan_calc(OPN, cch[1], 2 ); /*remapped to 2*/ chan_calc(OPN, cch[2], 4 ); /*remapped to 4*/ chan_calc(OPN, cch[3], 5 ); /*remapped to 5*/ /* deltaT ADPCM */ if( DELTAT->portstate&0x80 && ! F2610->MuteDeltaT ) YM_DELTAT_ADPCM_CALC(DELTAT); /* ADPCMA */ for( j = 0; j < 6; j++ ) { if( F2610->adpcm[j].flag ) ADPCMA_calc_chan( F2610, &F2610->adpcm[j]); } /* buffering */ { int lt,rt; /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; lt += ((out_fm[1]>>1) & OPN->pan[2]); // the shift right was verified on real chip rt += ((out_fm[1]>>1) & OPN->pan[3]); lt += ((out_fm[2]>>1) & OPN->pan[4]); rt += ((out_fm[2]>>1) & OPN->pan[5]); lt += ((out_fm[4]>>1) & OPN->pan[8]); rt += ((out_fm[4]>>1) & OPN->pan[9]); lt += ((out_fm[5]>>1) & OPN->pan[10]); rt += ((out_fm[5]>>1) & OPN->pan[11]);*/ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1; rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8; lt += (out_fm[1] & OPN->pan[2]); rt += (out_fm[1] & OPN->pan[3]); lt += (out_fm[2] & OPN->pan[4]); rt += (out_fm[2] & OPN->pan[5]); lt += (out_fm[4] & OPN->pan[8]); rt += (out_fm[4] & OPN->pan[9]); lt += (out_fm[5] & OPN->pan[10]); rt += (out_fm[5] & OPN->pan[11]); lt >>= FINAL_SH; rt >>= FINAL_SH; /*//Limit( lt, MAXOUT, MINOUT ); //Limit( rt, MAXOUT, MINOUT );*/ #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif /* buffering */ bufL[i] = lt; bufR[i] = rt; } /* timer A control */ INTERNAL_TIMER_A( &OPN->ST , cch[1] ) } INTERNAL_TIMER_B(&OPN->ST,length) } #if BUILD_YM2610B /* Generate samples for one of the YM2610Bs */ void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length) { YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; YM_DELTAT *DELTAT = &F2610->deltaT; int i,j; FMSAMPLE *bufL,*bufR; FM_CH *cch[6]; INT32 *out_fm = OPN->out_fm; /* buffer setup */ bufL = buffer[0]; bufR = buffer[1]; cch[0] = &F2610->CH[0]; cch[1] = &F2610->CH[1]; cch[2] = &F2610->CH[2]; cch[3] = &F2610->CH[3]; cch[4] = &F2610->CH[4]; cch[5] = &F2610->CH[5]; /* refresh PG and EG */ refresh_fc_eg_chan( OPN, cch[0] ); refresh_fc_eg_chan( OPN, cch[1] ); if( (OPN->ST.mode & 0xc0) ) { /* 3SLOT MODE */ if( cch[2]->SLOT[SLOT1].Incr==-1) { refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode ); } } else refresh_fc_eg_chan( OPN, cch[2] ); refresh_fc_eg_chan( OPN, cch[3] ); refresh_fc_eg_chan( OPN, cch[4] ); refresh_fc_eg_chan( OPN, cch[5] ); /* buffering */ for(i=0; i < length ; i++) { advance_lfo(OPN); /* clear output acc. */ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0; OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0; /* clear outputs */ out_fm[0] = 0; out_fm[1] = 0; out_fm[2] = 0; out_fm[3] = 0; out_fm[4] = 0; out_fm[5] = 0; /* advance envelope generator */ OPN->eg_timer += OPN->eg_timer_add; while (OPN->eg_timer >= OPN->eg_timer_overflow) { OPN->eg_timer -= OPN->eg_timer_overflow; OPN->eg_cnt++; advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]); advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]); } /* calculate FM */ chan_calc(OPN, cch[0], 0 ); chan_calc(OPN, cch[1], 1 ); chan_calc(OPN, cch[2], 2 ); chan_calc(OPN, cch[3], 3 ); chan_calc(OPN, cch[4], 4 ); chan_calc(OPN, cch[5], 5 ); /* deltaT ADPCM */ if( DELTAT->portstate&0x80 && ! F2610->MuteDeltaT ) YM_DELTAT_ADPCM_CALC(DELTAT); /* ADPCMA */ for( j = 0; j < 6; j++ ) { if( F2610->adpcm[j].flag ) ADPCMA_calc_chan( F2610, &F2610->adpcm[j]); } /* buffering */ { int lt,rt; /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]; rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9; lt += ((out_fm[0]>>1) & OPN->pan[0]); // the shift right is verified on YM2610 rt += ((out_fm[0]>>1) & OPN->pan[1]); lt += ((out_fm[1]>>1) & OPN->pan[2]); rt += ((out_fm[1]>>1) & OPN->pan[3]); lt += ((out_fm[2]>>1) & OPN->pan[4]); rt += ((out_fm[2]>>1) & OPN->pan[5]); lt += ((out_fm[3]>>1) & OPN->pan[6]); rt += ((out_fm[3]>>1) & OPN->pan[7]); lt += ((out_fm[4]>>1) & OPN->pan[8]); rt += ((out_fm[4]>>1) & OPN->pan[9]); lt += ((out_fm[5]>>1) & OPN->pan[10]); rt += ((out_fm[5]>>1) & OPN->pan[11]);*/ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1; rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1; lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8; rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8; lt += (out_fm[0] & OPN->pan[0]); rt += (out_fm[0] & OPN->pan[1]); lt += (out_fm[1] & OPN->pan[2]); rt += (out_fm[1] & OPN->pan[3]); lt += (out_fm[2] & OPN->pan[4]); rt += (out_fm[2] & OPN->pan[5]); lt += (out_fm[3] & OPN->pan[6]); rt += (out_fm[3] & OPN->pan[7]); lt += (out_fm[4] & OPN->pan[8]); rt += (out_fm[4] & OPN->pan[9]); lt += (out_fm[5] & OPN->pan[10]); rt += (out_fm[5] & OPN->pan[11]); lt >>= FINAL_SH; rt >>= FINAL_SH; /*//Limit( lt, MAXOUT, MINOUT ); //Limit( rt, MAXOUT, MINOUT );*/ #ifdef SAVE_SAMPLE SAVE_ALL_CHANNELS #endif /* buffering */ bufL[i] = lt; bufR[i] = rt; } /* timer A control */ INTERNAL_TIMER_A( &OPN->ST , cch[2] ) } INTERNAL_TIMER_B(&OPN->ST,length) } #endif /* BUILD_YM2610B */ #ifdef __STATE_H__ void ym2610_postload(void *chip) { if (chip) { YM2610 *F2610 = (YM2610 *)chip; int r; /* SSG registers */ for(r=0;r<16;r++) { (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,0,r); (*F2610->OPN.ST.SSG->write)(F2610->OPN.ST.param,1,F2610->REGS[r]); } /* OPN registers */ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ for(r=0x30;r<0x9e;r++) if((r&3) != 3) { OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); } /* FB / CONNECT , L / R / AMS / PMS */ for(r=0xb0;r<0xb6;r++) if((r&3) != 3) { OPNWriteReg(&F2610->OPN,r,F2610->REGS[r]); OPNWriteReg(&F2610->OPN,r|0x100,F2610->REGS[r|0x100]); } /* FM channels */ /*FM_channel_postload(F2610->CH,6);*/ /* rhythm(ADPCMA) */ FM_ADPCMAWrite(F2610,1,F2610->REGS[0x101]); for( r=0 ; r<6 ; r++) { FM_ADPCMAWrite(F2610,r+0x08,F2610->REGS[r+0x108]); FM_ADPCMAWrite(F2610,r+0x10,F2610->REGS[r+0x110]); FM_ADPCMAWrite(F2610,r+0x18,F2610->REGS[r+0x118]); FM_ADPCMAWrite(F2610,r+0x20,F2610->REGS[r+0x120]); FM_ADPCMAWrite(F2610,r+0x28,F2610->REGS[r+0x128]); } /* Delta-T ADPCM unit */ YM_DELTAT_postload(&F2610->deltaT , &F2610->REGS[0x010] ); } } static void YM2610_save_state(YM2610 *F2610, const device_config *device) { state_save_register_device_item_array(device, 0, F2610->REGS); FMsave_state_st(device,&F2610->OPN.ST); FMsave_state_channel(device,F2610->CH,6); /* 3slots */ state_save_register_device_item_array(device, 0, F2610->OPN.SL3.fc); state_save_register_device_item(device, 0, F2610->OPN.SL3.fn_h); state_save_register_device_item_array(device, 0, F2610->OPN.SL3.kcode); /* address register1 */ state_save_register_device_item(device, 0, F2610->addr_A1); state_save_register_device_item(device, 0, F2610->adpcm_arrivedEndAddress); /* rythm(ADPCMA) */ FMsave_state_adpcma(device,F2610->adpcm); /* Delta-T ADPCM unit */ YM_DELTAT_savestate(device,&F2610->deltaT); } #endif /* _STATE_H */ static void YM2610_deltat_status_set(void *chip, UINT8 changebits) { YM2610 *F2610 = (YM2610 *)chip; F2610->adpcm_arrivedEndAddress |= changebits; } static void YM2610_deltat_status_reset(void *chip, UINT8 changebits) { YM2610 *F2610 = (YM2610 *)chip; F2610->adpcm_arrivedEndAddress &= (~changebits); } /*//void *ym2610_init(void *param, const device_config *device, int clock, int rate, // void *pcmroma,int pcmsizea,void *pcmromb,int pcmsizeb, // FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)*/ void *ym2610_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) { YM2610 *F2610; /* allocate extend state space */ if( (F2610 = (YM2610 *)malloc(sizeof(YM2610)))==NULL) return NULL; /* clear */ memset(F2610,0,sizeof(YM2610)); /* allocate total level table (128kb space) */ if( !init_tables() ) { free( F2610 ); return NULL; } /* FM */ F2610->OPN.ST.param = param; F2610->OPN.type = TYPE_YM2610; F2610->OPN.P_CH = F2610->CH; /*F2610->OPN.ST.device = device;*/ F2610->OPN.ST.clock = clock; F2610->OPN.ST.rate = rate; /* Extend handler */ F2610->OPN.ST.timer_handler = timer_handler; F2610->OPN.ST.IRQ_Handler = IRQHandler; F2610->OPN.ST.SSG = ssg; /* ADPCM */ /*//F2610->pcmbuf = (const UINT8 *)pcmroma; //F2610->pcm_size = pcmsizea;*/ F2610->pcmbuf = NULL; F2610->pcm_size = 0x00; /* DELTA-T */ /*//F2610->deltaT.memory = (UINT8 *)pcmromb; //F2610->deltaT.memory_size = pcmsizeb;*/ F2610->deltaT.memory = NULL; F2610->deltaT.memory_size = 0x00; F2610->deltaT.memory_mask = 0x00; F2610->deltaT.status_set_handler = YM2610_deltat_status_set; F2610->deltaT.status_reset_handler = YM2610_deltat_status_reset; F2610->deltaT.status_change_which_chip = F2610; F2610->deltaT.status_change_EOS_bit = 0x80; /* status flag: set bit7 on End Of Sample */ Init_ADPCMATable(); #ifdef __STATE_H__ YM2610_save_state(F2610, device); #endif return F2610; } /* shut down emulator */ void ym2610_shutdown(void *chip) { YM2610 *F2610 = (YM2610 *)chip; free(F2610->pcmbuf); F2610->pcmbuf = NULL; free(F2610->deltaT.memory); F2610->deltaT.memory = NULL; FMCloseTable(); free(F2610); } /* reset one of chip */ void ym2610_reset_chip(void *chip) { int i; YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; YM_DELTAT *DELTAT = &F2610->deltaT; /*astring name; device_t* dev = F2610->OPN.ST.device;*/ /* setup PCM buffers again */ /*name.printf("%s",dev->tag()); F2610->pcmbuf = (const UINT8 *)dev->machine->region(name)->base(); F2610->pcm_size = dev->machine->region(name)->bytes(); name.printf("%s.deltat",dev->tag()); F2610->deltaT.memory = (UINT8 *)dev->machine->region(name)->base(); if(F2610->deltaT.memory == NULL) { F2610->deltaT.memory = (UINT8*)F2610->pcmbuf; F2610->deltaT.memory_size = F2610->pcm_size; } else F2610->deltaT.memory_size = dev->machine->region(name)->bytes();*/ /* Reset Prescaler */ OPNSetPres( OPN, 6*24, 6*24, 4*2); /* OPN 1/6 , SSG 1/4 */ /* reset SSG section */ (*OPN->ST.SSG->reset)(OPN->ST.param); /* status clear */ FM_IRQMASK_SET(&OPN->ST,0x03); FM_BUSY_CLEAR(&OPN->ST); OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ OPN->eg_timer = 0; OPN->eg_cnt = 0; FM_STATUS_RESET(&OPN->ST, 0xff); reset_channels( &OPN->ST , F2610->CH , 6 ); /* reset OPerator paramater */ for(i = 0xb6 ; i >= 0xb4 ; i-- ) { OPNWriteReg(OPN,i ,0xc0); OPNWriteReg(OPN,i|0x100,0xc0); } for(i = 0xb2 ; i >= 0x30 ; i-- ) { OPNWriteReg(OPN,i ,0); OPNWriteReg(OPN,i|0x100,0); } for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); /**** ADPCM work initial ****/ for( i = 0; i < 6 ; i++ ) { F2610->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0); F2610->adpcm[i].now_addr = 0; F2610->adpcm[i].now_step = 0; F2610->adpcm[i].start = 0; F2610->adpcm[i].end = 0; /* F2610->adpcm[i].delta = 21866; */ F2610->adpcm[i].vol_mul = 0; F2610->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */ F2610->adpcm[i].flagMask = 1<adpcm[i].flag = 0; F2610->adpcm[i].adpcm_acc = 0; F2610->adpcm[i].adpcm_step= 0; F2610->adpcm[i].adpcm_out = 0; } F2610->adpcmTL = 0x3f; F2610->adpcm_arrivedEndAddress = 0; /* DELTA-T unit */ DELTAT->freqbase = OPN->ST.freqbase; DELTAT->output_pointer = OPN->out_delta; DELTAT->portshift = 8; /* allways 8bits shift */ DELTAT->output_range = 1<<23; YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_YM2610); } /* YM2610 write */ /* n = number */ /* a = address */ /* v = value */ int ym2610_write(void *chip, int a, UINT8 v) { YM2610 *F2610 = (YM2610 *)chip; FM_OPN *OPN = &F2610->OPN; int addr; int ch; v &= 0xff; /* adjust to 8 bit bus */ switch( a&3 ) { case 0: /* address port 0 */ OPN->ST.address = v; F2610->addr_A1 = 0; /* Write register to SSG emulator */ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v); break; case 1: /* data port 0 */ if (F2610->addr_A1 != 0) break; /* verified on real YM2608 */ addr = OPN->ST.address; F2610->REGS[addr] = v; switch(addr & 0xf0) { case 0x00: /* SSG section */ /* Write data to SSG emulator */ (*OPN->ST.SSG->write)(OPN->ST.param,a,v); break; case 0x10: /* DeltaT ADPCM */ ym2610_update_req(OPN->ST.param); switch(addr) { case 0x10: /* control 1 */ case 0x11: /* control 2 */ case 0x12: /* start address L */ case 0x13: /* start address H */ case 0x14: /* stop address L */ case 0x15: /* stop address H */ case 0x19: /* delta-n L */ case 0x1a: /* delta-n H */ case 0x1b: /* volume */ { YM_DELTAT_ADPCM_Write(&F2610->deltaT,addr-0x10,v); } break; case 0x1c: /* FLAG CONTROL : Extend Status Clear/Mask */ { UINT8 statusmask = ~v; /* set arrived flag mask */ for(ch=0;ch<6;ch++) F2610->adpcm[ch].flagMask = statusmask&(1<deltaT.status_change_EOS_bit = statusmask & 0x80; /* status flag: set bit7 on End Of Sample */ /* clear arrived flag */ F2610->adpcm_arrivedEndAddress &= statusmask; } break; default: #ifdef _DEBUG logerror("YM2610: write to unknown deltat register %02x val=%02x\n",addr,v); #endif break; } break; case 0x20: /* Mode Register */ ym2610_update_req(OPN->ST.param); OPNWriteMode(OPN,addr,v); break; default: /* OPN section */ ym2610_update_req(OPN->ST.param); /* write register */ OPNWriteReg(OPN,addr,v); } break; case 2: /* address port 1 */ OPN->ST.address = v; F2610->addr_A1 = 1; break; case 3: /* data port 1 */ if (F2610->addr_A1 != 1) break; /* verified on real YM2608 */ ym2610_update_req(OPN->ST.param); addr = OPN->ST.address; F2610->REGS[addr | 0x100] = v; if( addr < 0x30 ) /* 100-12f : ADPCM A section */ FM_ADPCMAWrite(F2610,addr,v); else OPNWriteReg(OPN,addr | 0x100,v); } return OPN->ST.irq; } UINT8 ym2610_read(void *chip,int a) { YM2610 *F2610 = (YM2610 *)chip; int addr = F2610->OPN.ST.address; UINT8 ret = 0; switch( a&3) { case 0: /* status 0 : YM2203 compatible */ ret = FM_STATUS_FLAG(&F2610->OPN.ST) & 0x83; break; case 1: /* data 0 */ if( addr < 16 ) ret = (*F2610->OPN.ST.SSG->read)(F2610->OPN.ST.param); if( addr == 0xff ) ret = 0x01; break; case 2: /* status 1 : ADPCM status */ /* ADPCM STATUS (arrived End Address) */ /* B,--,A5,A4,A3,A2,A1,A0 */ /* B = ADPCM-B(DELTA-T) arrived end address */ /* A0-A5 = ADPCM-A arrived end address */ ret = F2610->adpcm_arrivedEndAddress; break; case 3: ret = 0; break; } return ret; } int ym2610_timer_over(void *chip,int c) { YM2610 *F2610 = (YM2610 *)chip; if( c ) { /* Timer B */ TimerBOver( &(F2610->OPN.ST) ); } else { /* Timer A */ ym2610_update_req(F2610->OPN.ST.param); /* timer update */ TimerAOver( &(F2610->OPN.ST) ); /* CSM mode key,TL controll */ if( F2610->OPN.ST.mode & 0x80 ) { /* CSM mode total level latch and auto key on */ CSMKeyControll( F2610->OPN.type, &(F2610->CH[2]) ); } } return F2610->OPN.ST.irq; } void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { YM2610 *F2610 = (YM2610 *)chip; switch(rom_id) { case 0x01: /* ADPCM */ if (F2610->pcm_size != ROMSize) { F2610->pcmbuf = (UINT8*)realloc(F2610->pcmbuf, ROMSize); F2610->pcm_size = ROMSize; memset(F2610->pcmbuf, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(F2610->pcmbuf + DataStart, ROMData, DataLength); break; case 0x02: /* DELTA-T */ if (F2610->deltaT.memory_size != ROMSize) { F2610->deltaT.memory = (UINT8*)realloc(F2610->deltaT.memory, ROMSize); F2610->deltaT.memory_size = ROMSize; memset(F2610->deltaT.memory, 0xFF, ROMSize); YM_DELTAT_calc_mem_mask(&F2610->deltaT); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(F2610->deltaT.memory + DataStart, ROMData, DataLength); break; } return; } void ym2610_set_mutemask(void *chip, UINT32 MuteMask) { YM2610 *F2610 = (YM2610 *)chip; UINT8 CurChn; for (CurChn = 0; CurChn < 6; CurChn ++) F2610->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; for (CurChn = 0; CurChn < 6; CurChn ++) F2610->adpcm[CurChn].Muted = (MuteMask >> (CurChn + 6)) & 0x01; F2610->MuteDeltaT = (MuteMask >> 12) & 0x01; return; } #endif /* (BUILD_YM2610||BUILD_YM2610B) */ BambooTracker-0.4.6/BambooTracker/chip/mame/fm.h000066400000000000000000000176641401124043500213740ustar00rootroot00000000000000/* File: fm.h -- header file for software emulation for FM sound generator */ #pragma once #include "mamedef.h" /* --- select emulation chips --- */ /* #define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator #define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator #define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator #define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator #define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator #define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator */ /*#define BUILD_YM2203 1*/ #define BUILD_YM2608 1 /*#define BUILD_YM2610 1 #define BUILD_YM2610B 1 #define BUILD_YM2612 1 #define BUILD_YM3438 1*/ /* select bit size of output : 8 or 16 */ #define FM_SAMPLE_BITS 16 /* select timer system internal or external */ #define FM_INTERNAL_TIMER 1 /* --- speedup optimize --- */ /* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */ /*#define FM_BUSY_FLAG_SUPPORT 1*/ /* --- external SSG(YM2149/AY-3-8910)emulator interface port */ /* used by YM2203,YM2608,and YM2610 */ typedef struct _ssg_callbacks ssg_callbacks; struct _ssg_callbacks { void (*set_clock)(void *param, int clock); void (*write)(void *param, int address, int data); int (*read)(void *param); void (*reset)(void *param); }; /* --- external callback funstions for realtime update --- */ #if FM_BUSY_FLAG_SUPPORT #define TIME_TYPE attotime #define UNDEFINED_TIME attotime_zero #define FM_GET_TIME_NOW(machine) timer_get_time(machine) #define ADD_TIMES(t1, t2) attotime_add((t1), (t2)) #define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2)) #define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i) #endif #if BUILD_YM2203 /* in 2203intf.c */ void ym2203_update_request(void *param); #define ym2203_update_req(chip) ym2203_update_request(chip) #endif /* BUILD_YM2203 */ #if BUILD_YM2608 /* in 2608intf.c */ void ym2608_update_request(void *param); #define ym2608_update_req(chip) ym2608_update_request(chip); #endif /* BUILD_YM2608 */ #if (BUILD_YM2610||BUILD_YM2610B) /* in 2610intf.c */ void ym2610_update_request(void *param); #define ym2610_update_req(chip) ym2610_update_request(chip); #endif /* (BUILD_YM2610||BUILD_YM2610B) */ #if (BUILD_YM2612||BUILD_YM3438) /* in 2612intf.c */ void ym2612_update_request(void *param); #define ym2612_update_req(chip) ym2612_update_request(chip); #endif /* (BUILD_YM2612||BUILD_YM3438) */ /* compiler dependence */ #if 0 #ifndef OSD_CPU_H #define OSD_CPU_H typedef unsigned char UINT8; /* unsigned 8bit */ typedef unsigned short UINT16; /* unsigned 16bit */ typedef unsigned int UINT32; /* unsigned 32bit */ typedef signed char INT8; /* signed 8bit */ typedef signed short INT16; /* signed 16bit */ typedef signed int INT32; /* signed 32bit */ #endif /* OSD_CPU_H */ #endif typedef stream_sample_t FMSAMPLE; /* #if (FM_SAMPLE_BITS==16) typedef INT16 FMSAMPLE; #endif #if (FM_SAMPLE_BITS==8) typedef unsigned char FMSAMPLE; #endif */ typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock); typedef void (*FM_IRQHANDLER)(void *param,int irq); /* FM_TIMERHANDLER : Stop or Start timer */ /* int n = chip number */ /* int c = Channel 0=TimerA,1=TimerB */ /* int count = timer count (0=stop) */ /* doube stepTime = step time of one count (sec.)*/ /* FM_IRQHHANDLER : IRQ level changing sense */ /* int n = chip number */ /* int irq = IRQ level 0=OFF,1=ON */ #if BUILD_YM2203 /* -------------------- YM2203(OPN) Interface -------------------- */ /* ** Initialize YM2203 emulator(s). ** ** 'num' is the number of virtual YM2203's to allocate ** 'baseclock' ** 'rate' is sampling rate ** 'TimerHandler' timer callback handler when timer start and clear ** 'IRQHandler' IRQ callback handler when changed IRQ level ** return 0 = success */ /*//void * ym2203_init(void *param, const device_config *device, int baseclock, int rate, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);*/ void * ym2203_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); /* ** shutdown the YM2203 emulators */ void ym2203_shutdown(void *chip); /* ** reset all chip registers for YM2203 number 'num' */ void ym2203_reset_chip(void *chip); /* ** update one of chip */ void ym2203_update_one(void *chip, FMSAMPLE **buffer, int length); /* ** Write ** return : InterruptLevel */ int ym2203_write(void *chip,int a,unsigned char v); /* ** Read ** return : InterruptLevel */ unsigned char ym2203_read(void *chip,int a); /* ** Timer OverFlow */ int ym2203_timer_over(void *chip, int c); /* ** State Save */ void ym2203_postload(void *chip); void ym2203_set_mutemask(void *chip, UINT32 MuteMask); #endif /* BUILD_YM2203 */ #if BUILD_YM2608 /* -------------------- YM2608(OPNA) Interface -------------------- */ /*//void * ym2608_init(void *param, const device_config *device, int baseclock, int rate, // void *pcmroma,int pcmsizea, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void * ym2608_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler, FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);*/ void * ym2608_init(void *param, int baseclock, int rate, offs_t dram_size, FM_TIMERHANDLER TimerHandler, FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void ym2608_shutdown(void *chip); void ym2608_reset_chip(void *chip); void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length); int ym2608_write(void *chip, int a, UINT8 v); unsigned char ym2608_read(void *chip,int a); int ym2608_timer_over(void *chip, int c ); void ym2608_postload(void *chip); void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ym2608_set_mutemask(void *chip, UINT32 MuteMask); #endif /* BUILD_YM2608 */ #if (BUILD_YM2610||BUILD_YM2610B) /* -------------------- YM2610(OPNB) Interface -------------------- */ /*//void * ym2610_init(void *param, const device_config *device, int baseclock, int rate, // void *pcmroma,int pcmasize,void *pcmromb,int pcmbsize, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);*/ void * ym2610_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); void ym2610_shutdown(void *chip); void ym2610_reset_chip(void *chip); void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length); #if BUILD_YM2610B void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length); #endif /* BUILD_YM2610B */ int ym2610_write(void *chip, int a,unsigned char v); unsigned char ym2610_read(void *chip,int a); int ym2610_timer_over(void *chip, int c ); void ym2610_postload(void *chip); void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData); void ym2610_set_mutemask(void *chip, UINT32 MuteMask); #endif /* (BUILD_YM2610||BUILD_YM2610B) */ #if (BUILD_YM2612||BUILD_YM3438) /*//void * ym2612_init(void *param, const device_config *device, int baseclock, int rate, // FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler);*/ void * ym2612_init(void *param, int baseclock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); void ym2612_shutdown(void *chip); void ym2612_reset_chip(void *chip); void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length); int ym2612_write(void *chip, int a,unsigned char v); unsigned char ym2612_read(void *chip,int a); int ym2612_timer_over(void *chip, int c ); void ym2612_postload(void *chip); void ym2612_set_mutemask(void *chip, UINT32 MuteMask); void ym2612_setoptions(UINT8 Flags); #endif /* (BUILD_YM2612||BUILD_YM3438) */ BambooTracker-0.4.6/BambooTracker/chip/mame/mamedef.h000066400000000000000000000033271401124043500223570ustar00rootroot00000000000000#ifndef __MAMEDEF_H__ #define __MAMEDEF_H__ #include #include "../chip_def.h" /* typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) */ /* 8-bit values */ /*typedef unsigned char UINT8; typedef signed char INT8;*/ typedef uint8_t UINT8; typedef int8_t INT8; /* 16-bit values */ /*typedef unsigned short UINT16; typedef signed short INT16;*/ typedef uint16_t UINT16; typedef int16_t INT16; /* 32-bit values */ #ifndef _WINDOWS_H /*typedef unsigned int UINT32; typedef signed int INT32;*/ typedef uint32_t UINT32; typedef int32_t INT32; #endif /* 64-bit values */ #ifndef _WINDOWS_H #ifdef _MSC_VER typedef signed __int64 INT64; typedef unsigned __int64 UINT64; #else __extension__ typedef unsigned long long UINT64; __extension__ typedef signed long long INT64; #endif #endif /* offsets and addresses are 32-bit (for now...) */ typedef UINT32 offs_t; /* stream_sample_t is used to represent a single sample in a sound stream */ /*typedef INT32 stream_sample_t;*/ typedef sample stream_sample_t; #if defined(VGM_BIG_ENDIAN) #define BYTE_XOR_BE(x) (x) #elif defined(VGM_LITTLE_ENDIAN) #define BYTE_XOR_BE(x) ((x) ^ 0x01) #else /* don't define BYTE_XOR_BE so that it throws an error when compiling */ #endif #if defined(_MSC_VER) /*#define INLINE static __forceinline*/ #define INLINE static __inline #elif defined(__GNUC__) #define INLINE static __inline__ #else #define INLINE static inline #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifdef _DEBUG #define logerror printf #else #define logerror #endif extern stream_sample_t* DUMMYBUF[]; typedef void (*SRATE_CALLBACK)(void*, UINT32); #endif /* __MAMEDEF_H__ */ BambooTracker-0.4.6/BambooTracker/chip/mame/ymdeltat.c000066400000000000000000000551071401124043500226020ustar00rootroot00000000000000/* ** ** File: ymdeltat.c ** ** YAMAHA DELTA-T adpcm sound emulation subroutine ** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B) ** ** Base program is YM2610 emulator by Hiromitsu Shioya. ** Written by Tatsuyuki Satoh ** Improvements by Jarek Burczynski (bujar at mame dot net) ** ** ** History: ** ** 03-08-2003 Jarek Burczynski: ** - fixed BRDY flag implementation. ** ** 24-07-2003 Jarek Burczynski, Frits Hilderink: ** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset ** ** 22-07-2003 Jarek Burczynski, Frits Hilderink: ** - fixed external memory support ** ** 15-06-2003 Jarek Burczynski: ** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08) ** - implemented support for the Limit address register ** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM ** - implemented external memory access (read/write) via the ADPCM data reg reads/writes ** Thanks go to Frits Hilderink for the example code. ** ** 14-06-2003 Jarek Burczynski: ** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO ** - modified EOS handling ** ** 05-04-2003 Jarek Burczynski: ** - implemented partial support for external/processor memory on sample replay ** ** 01-12-2002 Jarek Burczynski: ** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi ** - renamed/removed some YM_DELTAT struct fields ** ** 28-12-2001 Acho A. Tang ** - added EOS status report on ADPCM playback. ** ** 05-08-2001 Jarek Burczynski: ** - now_step is initialized with 0 at the start of play. ** ** 12-06-2001 Jarek Burczynski: ** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC. ** Checked on real YM2610 chip - address register is 24 bits wide. ** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem. ** ** TO DO: ** Check size of the address register on the other chips.... ** ** Version 0.72 ** ** sound chips that have this unit: ** YM2608 OPNA ** YM2610/B OPNB ** Y8950 MSX AUDIO ** */ #include "mamedef.h" #include /*#include "sndintrf.h"*/ #include "ymdeltat.h" #define YM_DELTAT_DELTA_MAX (24576) #define YM_DELTAT_DELTA_MIN (127) #define YM_DELTAT_DELTA_DEF (127) #define YM_DELTAT_DECODE_RANGE 32768 #define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) #define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) /* Forecast to next Forecast (rate = *8) */ /* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ static const INT32 ym_deltat_decode_tableB1[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15, }; /* delta to next delta (rate= *64) */ /* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ static const INT32 ym_deltat_decode_tableB2[16] = { 57, 57, 57, 57, 77, 102, 128, 153, 57, 57, 57, 57, 77, 102, 128, 153 }; #if 0 void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT) { logerror("BRDY_callback reached (flag set) !\n"); /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } #endif UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT) { UINT8 v = 0; /* external memory read */ if ( (DELTAT->portstate & 0xe0)==0x20 ) { /* two dummy reads */ if (DELTAT->memread) { DELTAT->now_addr = DELTAT->start << 1; DELTAT->memread--; return 0; } if ( DELTAT->now_addr != (DELTAT->end<<1) ) { v = DELTAT->memory[DELTAT->now_addr>>1]; /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ DELTAT->now_addr+=2; /* two nibbles at a time */ /* reset BRDY bit in status register, which means we are reading the memory now */ if(DELTAT->status_reset_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); /* setup a timer that will callback us in 10 master clock cycles for Y8950 * in the callback set the BRDY flag to 1 , which means we have another data ready. * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. */ /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } else { /* set EOS bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_EOS_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); } } return v; } /* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ static const UINT8 dram_rightshift[4]={3,0,0,0}; /* DELTA-T ADPCM write register */ void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v) { if(r>=0x10) return; DELTAT->reg[r] = v; /* stock data */ switch( r ) { case 0x00: /* START: Accessing *external* memory is started when START bit (D7) is set to "1", so you must set all conditions needed for recording/playback before starting. If you access *CPU-managed* memory, recording/playback starts after read/write of ADPCM data register $08. REC: 0 = ADPCM synthesis (playback) 1 = ADPCM analysis (record) MEMDATA: 0 = processor (*CPU-managed*) memory (means: using register $08) 1 = external memory (using start/end/limit registers to access memory: RAM or ROM) SPOFF: controls output pin that should disable the speaker while ADPCM analysis RESET and REPEAT only work with external memory. some examples: value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 */ /* handle emulation mode */ if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) { v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */ } DELTAT->portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */ if( DELTAT->portstate&0x80 )/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ { /* set PCM BUSY bit */ DELTAT->PCM_BSY = 1; /* start ADPCM */ DELTAT->now_step = 0; DELTAT->acc = 0; DELTAT->prev_acc = 0; DELTAT->adpcml = 0; DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; DELTAT->now_data = 0; if (DELTAT->start > DELTAT->end) { /*logerror("DeltaT-Warning: Start: %06X, End: %06X\n", DELTAT->start, DELTAT->end);*/ } } if( DELTAT->portstate&0x20 ) /* do we access external memory? */ { DELTAT->now_addr = DELTAT->start << 1; DELTAT->memread = 2; /* two dummy reads needed before accesing external memory via register $08*/ /* if yes, then let's check if ADPCM memory is mapped and big enough */ if(DELTAT->memory == 0) { #ifdef _DEBUG logerror("YM Delta-T ADPCM rom not mapped\n"); #endif DELTAT->portstate = 0x00; DELTAT->PCM_BSY = 0; } else { if( DELTAT->end >= DELTAT->memory_size ) /* Check End in Range */ { #ifdef _DEBUG logerror("YM Delta-T ADPCM end out of range: $%08x\n", DELTAT->end); #endif DELTAT->end = DELTAT->memory_size - 1; } if( DELTAT->start >= DELTAT->memory_size ) /* Check Start in Range */ { #ifdef _DEBUG logerror("YM Delta-T ADPCM start out of range: $%08x\n", DELTAT->start); #endif DELTAT->portstate = 0x00; DELTAT->PCM_BSY = 0; } } } else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */ { DELTAT->now_addr = 0; } if( DELTAT->portstate&0x01 ) { DELTAT->portstate = 0x00; /* clear PCM BUSY bit (in status register) */ DELTAT->PCM_BSY = 0; /* set BRDY flag */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } break; case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ /* handle emulation mode */ if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) { v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't have ROM/RAM memory flag bit. */ } DELTAT->pan = &DELTAT->output_pointer[(v>>6)&0x03]; if ((DELTAT->control2 & 3) != (v & 3)) { /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */ if (DELTAT->DRAMportshift != dram_rightshift[v&3]) { DELTAT->DRAMportshift = dram_rightshift[v&3]; /* final shift value depends on chip type and memory type selected: 8 for YM2610 (ROM only), 5 for ROM for Y8950 and YM2608, 5 for x8bit DRAMs for Y8950 and YM2608, 2 for x1bit DRAMs for Y8950 and YM2608. */ /* refresh addresses */ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); } } DELTAT->control2 = v; break; case 0x02: /* Start Address L */ case 0x03: /* Start Address H */ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift); /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",DELTAT->reg[0x2], DELTAT->reg[0x3],DELTAT->start );*/ break; case 0x04: /* Stop Address L */ case 0x05: /* Stop Address H */ DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift); DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1; /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",DELTAT->reg[0x4], DELTAT->reg[0x5],DELTAT->end );*/ break; case 0x06: /* Prescale L (ADPCM and Record frq) */ case 0x07: /* Prescale H */ break; case 0x08: /* ADPCM data */ /* some examples: value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 */ /* external memory write */ if ( (DELTAT->portstate & 0xe0)==0x60 ) { if (DELTAT->memread) { DELTAT->now_addr = DELTAT->start << 1; DELTAT->memread = 0; } /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/ if ( DELTAT->now_addr != (DELTAT->end<<1) ) { DELTAT->memory[DELTAT->now_addr>>1] = v; DELTAT->now_addr+=2; /* two nibbles at a time */ /* reset BRDY bit in status register, which means we are processing the write */ if(DELTAT->status_reset_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); /* setup a timer that will callback us in 10 master clock cycles for Y8950 * in the callback set the BRDY flag to 1 , which means we have written the data. * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work. */ /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } else { /* set EOS bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_EOS_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); } return; } /* ADPCM synthesis from CPU */ if ( (DELTAT->portstate & 0xe0)==0x80 ) { DELTAT->CPU_data = v; /* Reset BRDY bit in status register, which means we are full of data */ if(DELTAT->status_reset_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); return; } break; case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ case 0x0a: /* DELTA-N H */ DELTAT->delta = (DELTAT->reg[0xa]*0x0100 | DELTAT->reg[0x9]); DELTAT->step = (UINT32)( (double)(DELTAT->delta /* *(1<<(YM_DELTAT_SHIFT-16)) */ ) * (DELTAT->freqbase) ); /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",DELTAT->reg[0x9], DELTAT->reg[0xa]);*/ break; case 0x0b: /* Output level control (volume, linear) */ { INT32 oldvol = DELTAT->volume; DELTAT->volume = (v&0xff) * (DELTAT->output_range/256) / YM_DELTAT_DECODE_RANGE; /* v * ((1<<16)>>8) >> 15; * thus: v * (1<<8) >> 15; * thus: output_range must be (1 << (15+8)) at least * v * ((1<<23)>>8) >> 15; * v * (1<<15) >> 15; */ /*logerror("DELTAT vol = %2x\n",v&0xff);*/ if( oldvol != 0 ) { DELTAT->adpcml = (int)((double)DELTAT->adpcml / (double)oldvol * (double)DELTAT->volume); } } break; case 0x0c: /* Limit Address L */ case 0x0d: /* Limit Address H */ DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift); /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",DELTAT->reg[0xc], DELTAT->reg[0xd],DELTAT->limit );*/ break; } } void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode) { DELTAT->now_addr = 0; DELTAT->now_step = 0; DELTAT->step = 0; DELTAT->start = 0; DELTAT->end = 0; DELTAT->limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */ DELTAT->volume = 0; DELTAT->pan = &DELTAT->output_pointer[pan]; DELTAT->acc = 0; DELTAT->prev_acc = 0; DELTAT->adpcmd = 127; DELTAT->adpcml = 0; DELTAT->emulation_mode = (UINT8)emulation_mode; DELTAT->portstate = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x20 : 0; DELTAT->control2 = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */ DELTAT->DRAMportshift = dram_rightshift[DELTAT->control2 & 3]; /* The flag mask register disables the BRDY after the reset, however ** as soon as the mask is enabled the flag needs to be set. */ /* set BRDY bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } /*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs) { int r; // to keep adpcml DELTAT->volume = 0; // update for(r=1;r<16;r++) YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]); DELTAT->reg[0] = regs[0]; // current rom data if (DELTAT->memory) DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1) ); } //void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT) void YM_DELTAT_savestate(YM_DELTAT *DELTAT) { #ifdef __STATE_H__ state_save_register_device_item(device, 0, DELTAT->portstate); state_save_register_device_item(device, 0, DELTAT->now_addr); state_save_register_device_item(device, 0, DELTAT->now_step); state_save_register_device_item(device, 0, DELTAT->acc); state_save_register_device_item(device, 0, DELTAT->prev_acc); state_save_register_device_item(device, 0, DELTAT->adpcmd); state_save_register_device_item(device, 0, DELTAT->adpcml); #endif }*/ #define YM_DELTAT_Limit(val,max,min) \ { \ if ( val > max ) val = max; \ else if ( val < min ) val = min; \ } INLINE void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT) { UINT32 step; int data; DELTAT->now_step += DELTAT->step; if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; DELTAT->now_step &= (1<now_addr == (DELTAT->limit<<1) ) DELTAT->now_addr = 0; if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */ if( DELTAT->portstate&0x10 ){ /* repeat start */ DELTAT->now_addr = DELTAT->start<<1; DELTAT->acc = 0; DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; DELTAT->prev_acc = 0; }else{ /* set EOS bit in status register */ if(DELTAT->status_set_handler) if(DELTAT->status_change_EOS_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit); /* clear PCM BUSY bit (reflected in status register) */ DELTAT->PCM_BSY = 0; DELTAT->portstate = 0; DELTAT->adpcml = 0; DELTAT->prev_acc = 0; return; } } if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f; else { DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1)); data = DELTAT->now_data >> 4; } DELTAT->now_addr++; /* 12-06-2001 JB: */ /* YM2610 address register is 24 bits wide.*/ /* The "+1" is there because we use 1 bit more for nibble calculations.*/ /* WARNING: */ /* Side effect: we should take the size of the mapped ROM into account */ /*DELTAT->now_addr &= ( (1<<(24+1))-1);*/ DELTAT->now_addr &= DELTAT->memory_mask; /* store accumulator value */ DELTAT->prev_acc = DELTAT->acc; /* Forecast to next Forecast */ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); /* delta to next delta */ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); /* ElSemi: Fix interpolator. */ /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/ }while(--step); } /* ElSemi: Fix interpolator. */ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; /* output for work of output channels (outd[OPNxxxx])*/ *(DELTAT->pan) += DELTAT->adpcml; } INLINE void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT) { UINT32 step; int data; DELTAT->now_step += DELTAT->step; if ( DELTAT->now_step >= (1<now_step >> YM_DELTAT_SHIFT; DELTAT->now_step &= (1<now_addr&1 ) { data = DELTAT->now_data & 0x0f; DELTAT->now_data = DELTAT->CPU_data; /* after we used CPU_data, we set BRDY bit in status register, * which means we are ready to accept another byte of data */ if(DELTAT->status_set_handler) if(DELTAT->status_change_BRDY_bit) (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit); } else { data = DELTAT->now_data >> 4; } DELTAT->now_addr++; /* store accumulator value */ DELTAT->prev_acc = DELTAT->acc; /* Forecast to next Forecast */ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); /* delta to next delta */ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64; YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN ); }while(--step); } /* ElSemi: Fix interpolator. */ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<now_step); DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step); DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume; /* output for work of output channels (outd[OPNxxxx])*/ *(DELTAT->pan) += DELTAT->adpcml; } /* ADPCM B (Delta-T control type) */ void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT) { /* some examples: value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning: 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08 */ if ( (DELTAT->portstate & 0xe0)==0xa0 ) { YM_DELTAT_synthesis_from_external_memory(DELTAT); return; } if ( (DELTAT->portstate & 0xe0)==0x80 ) { /* ADPCM synthesis from CPU-managed memory (from reg $08) */ YM_DELTAT_synthesis_from_CPU_memory(DELTAT); /* change output based on data in ADPCM data reg ($08) */ return; } /*//todo: ADPCM analysis // if ( (DELTAT->portstate & 0xe0)==0xc0 ) // if ( (DELTAT->portstate & 0xe0)==0xe0 )*/ return; } void YM_DELTAT_calc_mem_mask(YM_DELTAT* DELTAT) { UINT32 MaskSize; MaskSize = 0x01; while(MaskSize < DELTAT->memory_size) MaskSize <<= 1; DELTAT->memory_mask = (MaskSize << 1) - 1; /* it's Mask<<1 because of the nibbles */ return; } BambooTracker-0.4.6/BambooTracker/chip/mame/ymdeltat.h000066400000000000000000000065111401124043500226020ustar00rootroot00000000000000#pragma once #include "mamedef.h" #define YM_DELTAT_SHIFT (16) #define YM_DELTAT_EMULATION_MODE_NORMAL 0 #define YM_DELTAT_EMULATION_MODE_YM2610 1 typedef void (*STATUS_CHANGE_HANDLER)(void *chip, UINT8 status_bits); /* DELTA-T (adpcm type B) struct */ typedef struct deltat_adpcm_state { /* AT: rearranged and tigntened structure */ UINT8 *memory; INT32 *output_pointer;/* pointer of output pointers */ INT32 *pan; /* pan : &output_pointer[pan] */ double freqbase; #if 0 double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */ double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */ #endif UINT32 memory_size; UINT32 memory_mask; int output_range; UINT32 now_addr; /* current address */ UINT32 now_step; /* currect step */ UINT32 step; /* step */ UINT32 start; /* start address */ UINT32 limit; /* limit address */ UINT32 end; /* end address */ UINT32 delta; /* delta scale */ INT32 volume; /* current volume */ INT32 acc; /* shift Measurement value*/ INT32 adpcmd; /* next Forecast */ INT32 adpcml; /* current value */ INT32 prev_acc; /* leveling value */ UINT8 now_data; /* current rom data */ UINT8 CPU_data; /* current data from reg 08 */ UINT8 portstate; /* port status */ UINT8 control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */ UINT8 portshift; /* address bits shift-left: ** 8 for YM2610, ** 5 for Y8950 and YM2608 */ UINT8 DRAMportshift; /* address bits shift-right: ** 0 for ROM and x8bit DRAMs, ** 3 for x1 DRAMs */ UINT8 memread; /* needed for reading/writing external memory */ /* handlers and parameters for the status flags support */ STATUS_CHANGE_HANDLER status_set_handler; STATUS_CHANGE_HANDLER status_reset_handler; /* note that different chips have these flags on different ** bits of the status register */ void * status_change_which_chip; /* this chip id */ UINT8 status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/ UINT8 status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */ UINT8 status_change_ZERO_bit; /* 1 if silence lasts for more than 290 miliseconds on ADPCM recording */ /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above, ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608) */ UINT8 PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */ UINT8 reg[16]; /* adpcm registers */ UINT8 emulation_mode; /* which chip we're emulating */ }YM_DELTAT; /*void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT);*/ UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT); void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v); void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode); void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT); /*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs); //void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT); void YM_DELTAT_savestate(YM_DELTAT *DELTAT);*/ void YM_DELTAT_calc_mem_mask(YM_DELTAT* DELTAT); BambooTracker-0.4.6/BambooTracker/chip/nuked/000077500000000000000000000000001401124043500207725ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/chip/nuked/nuke2608intf.c000066400000000000000000000144001401124043500233000ustar00rootroot00000000000000#include /* for free */ #include /* for memset */ #include /* for NULL */ #include "nuke2608intf.h" #include "ym3438.h" /*// Only use EC_EMU2149 //#define ENABLE_ALL_CORES*/ #ifdef ENABLE_ALL_CORES #define EC_MAME 0x01 /* AY8910 core from MAME */ #endif #define EC_EMU2149 0x00 typedef struct _ym2608_state ym2608_state; struct _ym2608_state { ym3438_t * chip; void * psg; int clock; ym2608_interface intf; uint32_t dramSize; }; static uint8_t AY_EMU_CORE = 0x00; #define MAX_CHIPS 0x02 static ym2608_state YM2608Data[MAX_CHIPS]; static void psg_set_clock(void *param, int clock) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_set_clock_ym(info->psg, clock); break; #endif case EC_EMU2149: PSG_set_clock((PSG*)info->psg, clock); break; } } } static void psg_write(void *param, int address, int data) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_write_ym(info->psg, address, data); break; #endif case EC_EMU2149: PSG_writeIO((PSG*)info->psg, address, data); break; } } } static int psg_read(void *param) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: return ay8910_read_ym(info->psg); #endif case EC_EMU2149: return PSG_readIO((PSG*)info->psg); } } return 0x00; } static void psg_reset(void *param) { ym2608_state *info = (ym2608_state *)param; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_reset_ym(info->psg); break; #endif case EC_EMU2149: PSG_reset((PSG*)info->psg); break; } } } static const struct OPN2mod_psg_callbacks psgintf = { psg_set_clock, psg_write, psg_read, psg_reset }; void nuke2608_set_ay_emu_core(uint8_t Emulator) { #ifdef ENABLE_ALL_CORES AY_EMU_CORE = (Emulator < 0x02) ? Emulator : 0x00; #else (void)Emulator; AY_EMU_CORE = EC_EMU2149; #endif return; } int device_start_nuke2608(uint8_t ChipID, int clock, uint8_t AYDisable, uint8_t AYFlags, int *AYrate, uint32_t dramSize) { static const ym2608_interface generic_2608 = { { AY8910_LEGACY_OUTPUT | AY8910_SINGLE_OUTPUT, AY8910_DEFAULT_LOADS /*DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL*/ }, NULL }; ym2608_interface *intf; ym2608_state *info; int rate; int ay_clock; if (ChipID >= MAX_CHIPS) return 0; info = &YM2608Data[ChipID]; info->clock = clock; rate = clock / 144; /* FM synthesis rate is clock / 2 / 72 */ info->intf = generic_2608; intf = &info->intf; if (AYFlags) intf->ay8910_intf.flags = AYFlags; info->chip = (ym3438_t *)calloc(1, sizeof(ym3438_t)); if (!info->chip) return 0; /* FIXME: Force to use single output */ /*info->psg = ay8910_start_ym(NULL, SOUND_YM2608, clock, &intf->ay8910_intf);*/ if (! AYDisable) { ay_clock = clock / 4; *AYrate = ay_clock / 8; switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: info->psg = ay8910_start_ym(NULL, CHTYPE_YM2608, ay_clock, &intf->ay8910_intf); break; #endif case EC_EMU2149: info->psg = PSG_new(ay_clock, *AYrate); if (info->psg == NULL) return 0; PSG_setVolumeMode((PSG*)info->psg, 1); /* YM2149 volume mode */ break; } } else { info->psg = NULL; *AYrate = 0; } info->dramSize = dramSize; OPN2_Reset(info->chip, clock, &psgintf, info, dramSize); return rate; } void device_stop_nuke2608(uint8_t ChipID) { ym2608_state *info = &YM2608Data[ChipID]; OPN2_Destroy(info->chip); free(info->chip); if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_stop_ym(info->psg); break; #endif case EC_EMU2149: PSG_delete((PSG*)info->psg); break; } info->psg = NULL; } } void device_reset_nuke2608(uint8_t ChipID) { ym2608_state *info = &YM2608Data[ChipID]; OPN2_FlushBuffer(info->chip); OPN2_Reset(info->chip, info->clock, &psgintf, info, info->dramSize); } void nuke2608_control_port_a_w(uint8_t ChipID, uint32_t offset, uint8_t data) { ym2608_state *info = &YM2608Data[ChipID]; (void)offset; OPN2_WriteBuffered(info->chip, 0, data); } void nuke2608_control_port_b_w(uint8_t ChipID, uint32_t offset, uint8_t data) { ym2608_state *info = &YM2608Data[ChipID]; (void)offset; OPN2_WriteBuffered(info->chip, 2, data); } void nuke2608_data_port_a_w(uint8_t ChipID, uint32_t offset, uint8_t data) { ym2608_state *info = &YM2608Data[ChipID]; (void)offset; OPN2_WriteBuffered(info->chip, 1, data); } void nuke2608_data_port_b_w(uint8_t ChipID, uint32_t offset, uint8_t data) { ym2608_state *info = &YM2608Data[ChipID]; (void)offset; OPN2_WriteBuffered(info->chip, 3, data); } uint8_t nuke2608_read_port_r(uint8_t ChipID, uint32_t offset) { ym2608_state *info = &YM2608Data[ChipID]; (void)offset; return OPN2_Read(info->chip, 1); } void nuke2608_stream_update(uint8_t ChipID, sample **outputs, int samples) { int i; ym2608_state *info = &YM2608Data[ChipID]; sample *bufl = outputs[0]; sample *bufr = outputs[1]; for (i = 0; i < samples; ++i) { sample lr[2]; OPN2_Generate(info->chip, lr); *bufl++ = lr[0]; *bufr++ = lr[1]; } } void nuke2608_stream_update_ay(uint8_t ChipID, sample **outputs, int samples) { ym2608_state *info = &YM2608Data[ChipID]; if (info->psg != NULL) { switch(AY_EMU_CORE) { #ifdef ENABLE_ALL_CORES case EC_MAME: ay8910_update_one(info->psg, outputs, samples); break; #endif case EC_EMU2149: PSG_calc_stereo((PSG*)info->psg, outputs, samples); break; } } else { memset(outputs[0], 0x00, samples * sizeof(sample)); memset(outputs[1], 0x00, samples * sizeof(sample)); } } struct intf2608 nuked_intf2608 = { /*.set_ay_emu_core =*/ &nuke2608_set_ay_emu_core, /*.device_start =*/ &device_start_nuke2608, /*.device_stop =*/ &device_stop_nuke2608, /*.device_reset =*/ &device_reset_nuke2608, /*.control_port_a_w =*/ &nuke2608_control_port_a_w, /*.control_port_b_w =*/ &nuke2608_control_port_b_w, /*.data_port_a_w =*/ &nuke2608_data_port_a_w, /*.data_port_b_w =*/ &nuke2608_data_port_b_w, /*.read_port_r =*/ &nuke2608_read_port_r, /*.stream_update =*/ &nuke2608_stream_update, /*.stream_update_ay =*/ &nuke2608_stream_update_ay, }; BambooTracker-0.4.6/BambooTracker/chip/nuked/nuke2608intf.h000066400000000000000000000002431401124043500233050ustar00rootroot00000000000000#pragma once #include "ym3438.h" #include "../mame/emu2149.h" #ifdef INCLUDE_AY8910_H #include "../mame/ay8910.h" #endif extern struct intf2608 nuked_intf2608; BambooTracker-0.4.6/BambooTracker/chip/nuked/ym3438.c000066400000000000000000001654521401124043500221220ustar00rootroot00000000000000/* * Copyright (C) 2017-2018 Alexey Khokholov (Nuke.YKT) * Copyright (C) 2019 Jean Pierre Cimalando * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Nuked OPN2-MOD emulator, with OPNA functionality added. * Thanks: * Silicon Pr0n: * Yamaha YM3438 decap and die shot(digshadow). * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): * OPL2 ROMs. * * version: 1.0.9 * * OPN-MOD additions: * - Jean Pierre Cimalando 2019-04-06: add SSG control interface * - Jean Pierre Cimalando 2019-04-06: add 6-channel FM flag * - Jean Pierre Cimalando 2019-04-06: add ADPCM rhythm channels * - Jean Pierre Cimalando 2019-04-07: raise the channel clipping threshold * - Jean Pierre Cimalando 2020-02-23: add the ADPCM Delta-T */ #include #include #include "ym3438.h" /*OPN-MOD: define the quantization in bits at which channels clip*/ static const unsigned channel_clipbits = 11; /*YM2612 has 9 bits*/ enum { eg_num_attack = 0, eg_num_decay = 1, eg_num_sustain = 2, eg_num_release = 3 }; /* logsin table */ static const Bit16u logsinrom[256] = { 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 }; /* exp table */ static const Bit16u exprom[256] = { 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa }; /* Note table */ static const Bit32u fn_note[16] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 }; /* Envelope generator */ static const Bit32u eg_stephi[4][4] = { { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 1, 1, 0 } }; static const Bit8u eg_am_shift[4] = { 7, 3, 1, 0 }; /* Phase generator */ static const Bit32u pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; static const Bit32u pg_lfo_sh1[8][8] = { { 7, 7, 7, 7, 7, 7, 7, 7 }, { 7, 7, 7, 7, 7, 7, 7, 7 }, { 7, 7, 7, 7, 7, 7, 1, 1 }, { 7, 7, 7, 7, 1, 1, 1, 1 }, { 7, 7, 7, 1, 1, 1, 1, 0 }, { 7, 7, 1, 1, 0, 0, 0, 0 }, { 7, 7, 1, 1, 0, 0, 0, 0 }, { 7, 7, 1, 1, 0, 0, 0, 0 } }; static const Bit32u pg_lfo_sh2[8][8] = { { 7, 7, 7, 7, 7, 7, 7, 7 }, { 7, 7, 7, 7, 2, 2, 2, 2 }, { 7, 7, 7, 2, 2, 2, 7, 7 }, { 7, 7, 2, 2, 7, 7, 2, 2 }, { 7, 7, 2, 7, 7, 7, 2, 7 }, { 7, 7, 7, 2, 7, 7, 2, 1 }, { 7, 7, 7, 2, 7, 7, 2, 1 }, { 7, 7, 7, 2, 7, 7, 2, 1 } }; /* Address decoder */ static const Bit32u op_offset[12] = { 0x000, /* Ch1 OP1/OP2 */ 0x001, /* Ch2 OP1/OP2 */ 0x002, /* Ch3 OP1/OP2 */ 0x100, /* Ch4 OP1/OP2 */ 0x101, /* Ch5 OP1/OP2 */ 0x102, /* Ch6 OP1/OP2 */ 0x004, /* Ch1 OP3/OP4 */ 0x005, /* Ch2 OP3/OP4 */ 0x006, /* Ch3 OP3/OP4 */ 0x104, /* Ch4 OP3/OP4 */ 0x105, /* Ch5 OP3/OP4 */ 0x106 /* Ch6 OP3/OP4 */ }; static const Bit32u ch_offset[6] = { 0x000, /* Ch1 */ 0x001, /* Ch2 */ 0x002, /* Ch3 */ 0x100, /* Ch4 */ 0x101, /* Ch5 */ 0x102 /* Ch6 */ }; /* LFO */ static const Bit32u lfo_cycles[8] = { 108, 77, 71, 67, 62, 44, 8, 5 }; /* FM algorithm */ static const Bit32u fm_algorithm[4][6][8] = { { { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_0 */ { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ }, { { 0, 1, 0, 0, 0, 1, 0, 0 }, /* OP1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ { 1, 1, 1, 0, 0, 0, 0, 0 }, /* OP2 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ }, { { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ }, { { 0, 0, 1, 0, 0, 1, 0, 0 }, /* OP1_0 */ { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ { 0, 0, 0, 1, 0, 0, 0, 0 }, /* OP2 */ { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ } }; /*OPN-MOD: ADPCM jedi table*/ static const Bit16s adpcm_jedi_table[49 * 16] = { 2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30, 2, 6, 10, 14, 19, 23, 27, 31, -2, -6, -10, -14, -19, -23, -27, -31, 2, 7, 11, 16, 21, 26, 30, 35, -2, -7, -11, -16, -21, -26, -30, -35, 2, 7, 13, 18, 23, 28, 34, 39, -2, -7, -13, -18, -23, -28, -34, -39, 2, 8, 14, 20, 25, 31, 37, 43, -2, -8, -14, -20, -25, -31, -37, -43, 3, 9, 15, 21, 28, 34, 40, 46, -3, -9, -15, -21, -28, -34, -40, -46, 3, 10, 17, 24, 31, 38, 45, 52, -3, -10, -17, -24, -31, -38, -45, -52, 3, 11, 19, 27, 34, 42, 50, 58, -3, -11, -19, -27, -34, -42, -50, -58, 4, 12, 21, 29, 38, 46, 55, 63, -4, -12, -21, -29, -38, -46, -55, -63, 4, 13, 23, 32, 41, 50, 60, 69, -4, -13, -23, -32, -41, -50, -60, -69, 5, 15, 25, 35, 46, 56, 66, 76, -5, -15, -25, -35, -46, -56, -66, -76, 5, 16, 28, 39, 50, 61, 73, 84, -5, -16, -28, -39, -50, -61, -73, -84, 6, 18, 31, 43, 56, 68, 81, 93, -6, -18, -31, -43, -56, -68, -81, -93, 6, 20, 34, 48, 61, 75, 89, 103, -6, -20, -34, -48, -61, -75, -89, -103, 7, 22, 37, 52, 67, 82, 97, 112, -7, -22, -37, -52, -67, -82, -97, -112, 8, 24, 41, 57, 74, 90, 107, 123, -8, -24, -41, -57, -74, -90, -107, -123, 9, 27, 45, 63, 82, 100, 118, 136, -9, -27, -45, -63, -82, -100, -118, -136, 10, 30, 50, 70, 90, 110, 130, 150, -10, -30, -50, -70, -90, -110, -130, -150, 11, 33, 55, 77, 99, 121, 143, 165, -11, -33, -55, -77, -99, -121, -143, -165, 12, 36, 60, 84, 109, 133, 157, 181, -12, -36, -60, -84, -109, -133, -157, -181, 13, 40, 66, 93, 120, 147, 173, 200, -13, -40, -66, -93, -120, -147, -173, -200, 14, 44, 73, 103, 132, 162, 191, 221, -14, -44, -73, -103, -132, -162, -191, -221, 16, 48, 81, 113, 146, 178, 211, 243, -16, -48, -81, -113, -146, -178, -211, -243, 17, 53, 89, 125, 160, 196, 232, 268, -17, -53, -89, -125, -160, -196, -232, -268, 19, 58, 98, 137, 176, 215, 255, 294, -19, -58, -98, -137, -176, -215, -255, -294, 21, 64, 108, 151, 194, 237, 281, 324, -21, -64, -108, -151, -194, -237, -281, -324, 23, 71, 118, 166, 213, 261, 308, 356, -23, -71, -118, -166, -213, -261, -308, -356, 26, 78, 130, 182, 235, 287, 339, 391, -26, -78, -130, -182, -235, -287, -339, -391, 28, 86, 143, 201, 258, 316, 373, 431, -28, -86, -143, -201, -258, -316, -373, -431, 31, 94, 158, 221, 284, 347, 411, 474, -31, -94, -158, -221, -284, -347, -411, -474, 34, 104, 174, 244, 313, 383, 453, 523, -34, -104, -174, -244, -313, -383, -453, -523, 38, 115, 191, 268, 345, 422, 498, 575, -38, -115, -191, -268, -345, -422, -498, -575, 42, 126, 210, 294, 379, 463, 547, 631, -42, -126, -210, -294, -379, -463, -547, -631, 46, 139, 231, 324, 417, 510, 602, 695, -46, -139, -231, -324, -417, -510, -602, -695, 51, 153, 255, 357, 459, 561, 663, 765, -51, -153, -255, -357, -459, -561, -663, -765, 56, 168, 280, 392, 505, 617, 729, 841, -56, -168, -280, -392, -505, -617, -729, -841, 61, 185, 308, 432, 555, 679, 802, 926, -61, -185, -308, -432, -555, -679, -802, -926, 68, 204, 340, 476, 612, 748, 884, 1020, -68, -204, -340, -476, -612, -748, -884, -1020, 74, 224, 373, 523, 672, 822, 971, 1121, -74, -224, -373, -523, -672, -822, -971, -1121, 82, 246, 411, 575, 740, 904, 1069, 1233, -82, -246, -411, -575, -740, -904, -1069, -1233, 90, 271, 452, 633, 814, 995, 1176, 1357, -90, -271, -452, -633, -814, -995, -1176, -1357, 99, 298, 497, 696, 895, 1094, 1293, 1492, -99, -298, -497, -696, -895, -1094, -1293, -1492, 109, 328, 547, 766, 985, 1204, 1423, 1642, -109, -328, -547, -766, -985, -1204, -1423, -1642, 120, 361, 601, 842, 1083, 1324, 1564, 1805, -120, -361, -601, -842, -1083, -1324, -1564, -1805, 132, 397, 662, 927, 1192, 1457, 1722, 1987, -132, -397, -662, -927, -1192, -1457, -1722, -1987, 145, 437, 728, 1020, 1311, 1603, 1894, 2186, -145, -437, -728, -1020, -1311, -1603, -1894, -2186, 160, 480, 801, 1121, 1442, 1762, 2083, 2403, -160, -480, -801, -1121, -1442, -1762, -2083, -2403, 176, 529, 881, 1234, 1587, 1940, 2292, 2645, -176, -529, -881, -1234, -1587, -1940, -2292, -2645, 194, 582, 970, 1358, 1746, 2134, 2522, 2910, -194, -582, -970, -1358, -1746, -2134, -2522, -2910 }; /*OPN-MOD: ADPCM table*/ extern const unsigned int YM2608_ADPCM_ROM_addr[2*6]; extern const unsigned char YM2608_ADPCM_ROM[0x2000]; /*OPN-MOD: ADPCM constants*/ static const Bitu adpcm_shift = 16; static const int adpcm_step_inc[8] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 }; static Bit32u chip_type = ym3438_mode_readmode; /*OPN-MOD: update ADPCM volume*/ void OPNmod_RhythmUpdateVolume(ym3438_t *chip, Bit32u channel) { Bit8u volume = chip->rhythm_tl + chip->rhythm_level[channel]; /* volume formula from MAME OPN */ if (volume >= 63) /* This is correct, 63 = quiet */ { chip->rhythm_vol_mul[channel] = 0; chip->rhythm_vol_shift[channel] = 0; } else { chip->rhythm_vol_mul[channel] = 15 - (volume & 7); /* so called 0.75 dB */ chip->rhythm_vol_shift[channel] = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */ } } void OPN2_DoIO(ym3438_t *chip) { /* Write signal check */ chip->write_a_en = (chip->write_a & 0x03) == 0x01; chip->write_d_en = (chip->write_d & 0x03) == 0x01; chip->write_a <<= 1; chip->write_d <<= 1; /* Busy counter */ chip->busy = chip->write_busy; chip->write_busy_cnt += chip->write_busy; chip->write_busy = (chip->write_busy && !(chip->write_busy_cnt >> 5)) || chip->write_d_en; chip->write_busy_cnt &= 0x1f; } void OPN2_DoRegWrite(ym3438_t *chip) { Bit32u i; Bit32u slot = chip->cycles % 12; Bit32u address; Bit32u channel = chip->channel; /* Update registers */ if (chip->write_fm_data) { /* Slot */ if (op_offset[slot] == (chip->address & 0x107)) { if (chip->address & 0x08) { /* OP2, OP4 */ slot += 12; } address = chip->address & 0xf0; switch (address) { case 0x30: /* DT, MULTI */ chip->multi[slot] = chip->data & 0x0f; if (!chip->multi[slot]) { chip->multi[slot] = 1; } else { chip->multi[slot] <<= 1; } chip->dt[slot] = (chip->data >> 4) & 0x07; break; case 0x40: /* TL */ chip->tl[slot] = chip->data & 0x7f; break; case 0x50: /* KS, AR */ chip->ar[slot] = chip->data & 0x1f; chip->ks[slot] = (chip->data >> 6) & 0x03; break; case 0x60: /* AM, DR */ chip->dr[slot] = chip->data & 0x1f; chip->am[slot] = (chip->data >> 7) & 0x01; break; case 0x70: /* SR */ chip->sr[slot] = chip->data & 0x1f; break; case 0x80: /* SL, RR */ chip->rr[slot] = chip->data & 0x0f; chip->sl[slot] = (chip->data >> 4) & 0x0f; chip->sl[slot] |= (chip->sl[slot] + 1) & 0x10; break; case 0x90: /* SSG-EG */ chip->ssg_eg[slot] = chip->data & 0x0f; break; default: break; } } /* Channel */ if (ch_offset[channel] == (chip->address & 0x103)) { address = chip->address & 0xfc; switch (address) { case 0xa0: chip->fnum[channel] = (chip->data & 0xff) | ((chip->reg_a4 & 0x07) << 8); chip->block[channel] = (chip->reg_a4 >> 3) & 0x07; chip->kcode[channel] = (chip->block[channel] << 2) | fn_note[chip->fnum[channel] >> 7]; break; case 0xa4: chip->reg_a4 = chip->data & 0xff; break; case 0xa8: chip->fnum_3ch[channel] = (chip->data & 0xff) | ((chip->reg_ac & 0x07) << 8); chip->block_3ch[channel] = (chip->reg_ac >> 3) & 0x07; chip->kcode_3ch[channel] = (chip->block_3ch[channel] << 2) | fn_note[chip->fnum_3ch[channel] >> 7]; break; case 0xac: chip->reg_ac = chip->data & 0xff; break; case 0xb0: chip->connect[channel] = chip->data & 0x07; chip->fb[channel] = (chip->data >> 3) & 0x07; break; case 0xb4: chip->pms[channel] = chip->data & 0x07; chip->ams[channel] = (chip->data >> 4) & 0x03; chip->pan_l[channel] = (chip->data >> 7) & 0x01; chip->pan_r[channel] = (chip->data >> 6) & 0x01; break; default: break; } } } if (chip->write_a_en || chip->write_d_en) { /* Data */ if (chip->write_a_en) { chip->write_fm_data = 0; } if (chip->write_d_en) { chip->write_fm_data = 1; } /* Address */ if (chip->write_a_en) { /*OPN-MOD: store address for both FM/SSG modes (for PSG read) */ chip->address = chip->write_data; if ((chip->write_data & 0xf0) != 0x00) { /* FM Write */ chip->write_fm_address = 1; } else { /* SSG write */ chip->write_fm_address = 0; } } /* FM Mode */ /* Data */ if (chip->write_d_en && (chip->write_data & 0x100) == 0) { switch (chip->write_fm_mode_a) { /*OPN-MOD: Rhythm key on/off*/ case 0x10: if ((chip->write_data & 0x80) == 0) { for (i = 0; i < 6; ++i) { if (chip->write_data & (1 << i)) { chip->rhythm_key[i] = 1; chip->rhythm_addr[i] = YM2608_ADPCM_ROM_addr[2 * i] << 1; chip->rhythm_now_step[i] = 0; chip->rhythm_adpcm_step[i] = 0; chip->rhythm_adpcm_acc[i] = 0; } } } else { for (i = 0; i < 6; ++i) { if (chip->write_data & (1 << i)) { chip->rhythm_key[i] = 0; chip->rhythml[i] = 0; chip->rhythmr[i] = 0; } } } break; /*OPN-MOD: Rhythm total level*/ case 0x11: chip->rhythm_tl = ~chip->write_data & 63; for (i = 0; i < 6; ++i) { OPNmod_RhythmUpdateVolume(chip, i); } break; /*OPN-MOD: Rhythm instrument pan/level*/ case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: i = chip->write_fm_mode_a - 0x18; chip->rhythm_pan[i] = (chip->write_data >> 6) & 3; chip->rhythm_level[i] = ~chip->write_data & 31; OPNmod_RhythmUpdateVolume(chip, i); break; case 0x21: /* LSI test 1 */ for (i = 0; i < 8; i++) { chip->mode_test_21[i] = (chip->write_data >> i) & 0x01; } break; case 0x22: /* LFO control */ if ((chip->write_data >> 3) & 0x01) { chip->lfo_en = 0x7f; } else { chip->lfo_en = 0; } chip->lfo_freq = chip->write_data & 0x07; break; case 0x24: /* Timer A */ chip->timer_a_reg &= 0x03; chip->timer_a_reg |= (chip->write_data & 0xff) << 2; break; case 0x25: chip->timer_a_reg &= 0x3fc; chip->timer_a_reg |= chip->write_data & 0x03; break; case 0x26: /* Timer B */ chip->timer_b_reg = chip->write_data & 0xff; break; case 0x27: /* CSM, Timer control */ chip->mode_ch3 = (chip->write_data & 0xc0) >> 6; chip->mode_csm = chip->mode_ch3 == 2; chip->timer_a_load = chip->write_data & 0x01; chip->timer_a_enable = (chip->write_data >> 2) & 0x01; chip->timer_a_reset = (chip->write_data >> 4) & 0x01; chip->timer_b_load = (chip->write_data >> 1) & 0x01; chip->timer_b_enable = (chip->write_data >> 3) & 0x01; chip->timer_b_reset = (chip->write_data >> 5) & 0x01; break; case 0x28: /* Key on/off */ for (i = 0; i < 4; i++) { chip->mode_kon_operator[i] = (chip->write_data >> (4 + i)) & 0x01; } if ((chip->write_data & 0x03) == 0x03) { /* Invalid address */ chip->mode_kon_channel = 0xff; } else { /*OPN-MOD: select according to 6 FM channel flag*/ chip->mode_kon_channel = chip->write_data & 0x03; if (chip->mode_fm6ch && (chip->write_data & 0x04)) { chip->mode_kon_channel += 3; } } break; case 0x29: /*OPN-MOD: set 6 FM channel flag*/ chip->mode_fm6ch = chip->write_data & 0x80; break; case 0x2a: /* DAC data */ chip->dacdata &= 0x01; chip->dacdata |= (chip->write_data ^ 0x80) << 1; break; case 0x2b: /* DAC enable */ chip->dacen = chip->write_data >> 7; break; case 0x2c: /* LSI test 2 */ for (i = 0; i < 8; i++) { chip->mode_test_2c[i] = (chip->write_data >> i) & 0x01; } chip->dacdata &= 0x1fe; chip->dacdata |= chip->mode_test_2c[3]; chip->eg_custom_timer = !chip->mode_test_2c[7] && chip->mode_test_2c[6]; break; default: /*OPN-MOD: write $00-$0F to PSG*/ if (chip->write_fm_mode_a < 0x10) { chip->psg->Write(chip->psgdata, 0, chip->write_fm_mode_a); chip->psg->Write(chip->psgdata, 1, chip->write_data); } break; } } else if (chip->write_d_en && (chip->write_data & 0x100) == 0x100) { /*OPN-Mod*/ switch (chip->write_fm_mode_a & 0xf0) { case 0x00: if (chip->write_fm_mode_a == 0x0e) { /* write to DAC data (unimplemented) */ } else if (chip->write_fm_mode_a != 0x0f) { YM_DELTAT_ADPCM_Write(&chip->deltaT, chip->write_fm_mode_a, chip->write_data & 0xff); } break; case 0x10: /* IRQ flag control (unimplemented) */ break; } } /* Address */ if (chip->write_a_en) { chip->write_fm_mode_a = chip->write_data & 0x1ff; } } if (chip->write_fm_data) { chip->data = chip->write_data & 0xff; } } void OPN2_PhaseCalcIncrement(ym3438_t *chip) { Bit32u chan = chip->channel; Bit32u slot = chip->cycles; Bit32u fnum = chip->pg_fnum; Bit32u fnum_h = fnum >> 4; Bit32u fm; Bit32u basefreq; Bit8u lfo = chip->lfo_pm; Bit8u lfo_l = lfo & 0x0f; Bit8u pms = chip->pms[chan]; Bit8u dt = chip->dt[slot]; Bit8u dt_l = dt & 0x03; Bit8u detune = 0; Bit8u block, note; Bit8u sum, sum_h, sum_l; Bit8u kcode = chip->pg_kcode; fnum <<= 1; /* Apply LFO */ if (lfo_l & 0x08) { lfo_l ^= 0x0f; } fm = (fnum_h >> pg_lfo_sh1[pms][lfo_l]) + (fnum_h >> pg_lfo_sh2[pms][lfo_l]); if (pms > 5) { fm <<= pms - 5; } fm >>= 2; if (lfo & 0x10) { fnum -= fm; } else { fnum += fm; } fnum &= 0xfff; basefreq = (fnum << chip->pg_block) >> 2; /* Apply detune */ if (dt_l) { if (kcode > 0x1c) { kcode = 0x1c; } block = kcode >> 2; note = kcode & 0x03; sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); sum_h = sum >> 1; sum_l = sum & 0x01; detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); } if (dt & 0x04) { basefreq -= detune; } else { basefreq += detune; } basefreq &= 0x1ffff; chip->pg_inc[slot] = (basefreq * chip->multi[slot]) >> 1; chip->pg_inc[slot] &= 0xfffff; } void OPN2_PhaseGenerate(ym3438_t *chip) { Bit32u slot; /* Mask increment */ slot = (chip->cycles + 20) % 24; if (chip->pg_reset[slot]) { chip->pg_inc[slot] = 0; } /* Phase step */ slot = (chip->cycles + 19) % 24; chip->pg_phase[slot] += chip->pg_inc[slot]; chip->pg_phase[slot] &= 0xfffff; if (chip->pg_reset[slot] || chip->mode_test_21[3]) { chip->pg_phase[slot] = 0; } } void OPN2_EnvelopeSSGEG(ym3438_t *chip) { Bit32u slot = chip->cycles; Bit8u direction = 0; chip->eg_ssg_pgrst_latch[slot] = 0; chip->eg_ssg_repeat_latch[slot] = 0; chip->eg_ssg_hold_up_latch[slot] = 0; chip->eg_ssg_inv[slot] = 0; if (chip->ssg_eg[slot] & 0x08) { direction = chip->eg_ssg_dir[slot]; if (chip->eg_level[slot] & 0x200) { /* Reset */ if ((chip->ssg_eg[slot] & 0x03) == 0x00) { chip->eg_ssg_pgrst_latch[slot] = 1; } /* Repeat */ if ((chip->ssg_eg[slot] & 0x01) == 0x00) { chip->eg_ssg_repeat_latch[slot] = 1; } /* Inverse */ if ((chip->ssg_eg[slot] & 0x03) == 0x02) { direction ^= 1; } if ((chip->ssg_eg[slot] & 0x03) == 0x03) { direction = 1; } } /* Hold up */ if (chip->eg_kon_latch[slot] && ((chip->ssg_eg[slot] & 0x07) == 0x05 || (chip->ssg_eg[slot] & 0x07) == 0x03)) { chip->eg_ssg_hold_up_latch[slot] = 1; } direction &= chip->eg_kon[slot]; chip->eg_ssg_inv[slot] = (chip->eg_ssg_dir[slot] ^ ((chip->ssg_eg[slot] >> 2) & 0x01)) & chip->eg_kon[slot]; } chip->eg_ssg_dir[slot] = direction; chip->eg_ssg_enable[slot] = (chip->ssg_eg[slot] >> 3) & 0x01; } void OPN2_EnvelopeADSR(ym3438_t *chip) { Bit32u slot = (chip->cycles + 22) % 24; Bit8u nkon = chip->eg_kon_latch[slot]; Bit8u okon = chip->eg_kon[slot]; Bit8u kon_event; Bit8u koff_event; Bit8u eg_off; Bit16s level; Bit16s nextlevel = 0; Bit16s ssg_level; Bit8u nextstate = chip->eg_state[slot]; Bit16s inc = 0; chip->eg_read[0] = chip->eg_read_inc; chip->eg_read_inc = chip->eg_inc > 0; /* Reset phase generator */ chip->pg_reset[slot] = (nkon && !okon) || chip->eg_ssg_pgrst_latch[slot]; /* KeyOn/Off */ kon_event = (nkon && !okon) || (okon && chip->eg_ssg_repeat_latch[slot]); koff_event = okon && !nkon; ssg_level = level = (Bit16s)chip->eg_level[slot]; if (chip->eg_ssg_inv[slot]) { /* Inverse */ ssg_level = 512 - level; ssg_level &= 0x3ff; } if (koff_event) { level = ssg_level; } if (chip->eg_ssg_enable[slot]) { eg_off = level >> 9; } else { eg_off = (level & 0x3f0) == 0x3f0; } nextlevel = level; if (kon_event) { nextstate = eg_num_attack; /* Instant attack */ if (chip->eg_ratemax) { nextlevel = 0; } else if (chip->eg_state[slot] == eg_num_attack && level != 0 && chip->eg_inc && nkon) { inc = (~level << chip->eg_inc) >> 5; } } else { switch (chip->eg_state[slot]) { case eg_num_attack: if (level == 0) { nextstate = eg_num_decay; } else if(chip->eg_inc && !chip->eg_ratemax && nkon) { inc = (~level << chip->eg_inc) >> 5; } break; case eg_num_decay: if ((level >> 5) == chip->eg_sl[1]) { nextstate = eg_num_sustain; } else if (!eg_off && chip->eg_inc) { inc = 1 << (chip->eg_inc - 1); if (chip->eg_ssg_enable[slot]) { inc <<= 2; } } break; case eg_num_sustain: case eg_num_release: if (!eg_off && chip->eg_inc) { inc = 1 << (chip->eg_inc - 1); if (chip->eg_ssg_enable[slot]) { inc <<= 2; } } break; default: break; } if (!nkon) { nextstate = eg_num_release; } } if (chip->eg_kon_csm[slot]) { nextlevel |= chip->eg_tl[1] << 3; } /* Envelope off */ if (!kon_event && !chip->eg_ssg_hold_up_latch[slot] && chip->eg_state[slot] != eg_num_attack && eg_off) { nextstate = eg_num_release; nextlevel = 0x3ff; } nextlevel += inc; chip->eg_kon[slot] = chip->eg_kon_latch[slot]; chip->eg_level[slot] = (Bit16u)nextlevel & 0x3ff; chip->eg_state[slot] = nextstate; } void OPN2_EnvelopePrepare(ym3438_t *chip) { Bit8u rate; Bit8u sum; Bit8u inc = 0; Bit32u slot = chip->cycles; Bit8u rate_sel; /* Prepare increment */ rate = (chip->eg_rate << 1) + chip->eg_ksv; if (rate > 0x3f) { rate = 0x3f; } sum = ((rate >> 2) + chip->eg_shift_lock) & 0x0f; if (chip->eg_rate != 0 && chip->eg_quotient == 2) { if (rate < 48) { switch (sum) { case 12: inc = 1; break; case 13: inc = (rate >> 1) & 0x01; break; case 14: inc = rate & 0x01; break; default: break; } } else { inc = eg_stephi[rate & 0x03][chip->eg_timer_low_lock] + (rate >> 2) - 11; if (inc > 4) { inc = 4; } } } chip->eg_inc = inc; chip->eg_ratemax = (rate >> 1) == 0x1f; /* Prepare rate & ksv */ rate_sel = chip->eg_state[slot]; if ((chip->eg_kon[slot] && chip->eg_ssg_repeat_latch[slot]) || (!chip->eg_kon[slot] && chip->eg_kon_latch[slot])) { rate_sel = eg_num_attack; } switch (rate_sel) { case eg_num_attack: chip->eg_rate = chip->ar[slot]; break; case eg_num_decay: chip->eg_rate = chip->dr[slot]; break; case eg_num_sustain: chip->eg_rate = chip->sr[slot]; break; case eg_num_release: chip->eg_rate = (chip->rr[slot] << 1) | 0x01; break; default: break; } chip->eg_ksv = chip->pg_kcode >> (chip->ks[slot] ^ 0x03); if (chip->am[slot]) { chip->eg_lfo_am = chip->lfo_am >> eg_am_shift[chip->ams[chip->channel]]; } else { chip->eg_lfo_am = 0; } /* Delay TL & SL value */ chip->eg_tl[1] = chip->eg_tl[0]; chip->eg_tl[0] = chip->tl[slot]; chip->eg_sl[1] = chip->eg_sl[0]; chip->eg_sl[0] = chip->sl[slot]; } void OPN2_EnvelopeGenerate(ym3438_t *chip) { Bit32u slot = (chip->cycles + 23) % 24; Bit16u level; level = chip->eg_level[slot]; if (chip->eg_ssg_inv[slot]) { /* Inverse */ level = 512 - level; } if (chip->mode_test_21[5]) { level = 0; } level &= 0x3ff; /* Apply AM LFO */ level += chip->eg_lfo_am; /* Apply TL */ if (!(chip->mode_csm && chip->channel == 2 + 1)) { level += chip->eg_tl[0] << 3; } if (level > 0x3ff) { level = 0x3ff; } chip->eg_out[slot] = level; } void OPN2_UpdateLFO(ym3438_t *chip) { if ((chip->lfo_quotient & lfo_cycles[chip->lfo_freq]) == lfo_cycles[chip->lfo_freq]) { chip->lfo_quotient = 0; chip->lfo_cnt++; } else { chip->lfo_quotient += chip->lfo_inc; } chip->lfo_cnt &= chip->lfo_en; } void OPN2_FMPrepare(ym3438_t *chip) { Bit32u slot = (chip->cycles + 6) % 24; Bit32u channel = chip->channel; Bit16s mod, mod1, mod2; Bit32u op = slot / 6; Bit8u connect = chip->connect[channel]; Bit32u prevslot = (chip->cycles + 18) % 24; /* Calculate modulation */ mod1 = mod2 = 0; if (fm_algorithm[op][0][connect]) { mod2 |= chip->fm_op1[channel][0]; } if (fm_algorithm[op][1][connect]) { mod1 |= chip->fm_op1[channel][1]; } if (fm_algorithm[op][2][connect]) { mod1 |= chip->fm_op2[channel]; } if (fm_algorithm[op][3][connect]) { mod2 |= chip->fm_out[prevslot]; } if (fm_algorithm[op][4][connect]) { mod1 |= chip->fm_out[prevslot]; } mod = mod1 + mod2; if (op == 0) { /* Feedback */ mod = mod >> (10 - chip->fb[channel]); if (!chip->fb[channel]) { mod = 0; } } else { mod >>= 1; } chip->fm_mod[slot] = mod; slot = (chip->cycles + 18) % 24; /* OP1 */ if (slot / 6 == 0) { chip->fm_op1[channel][1] = chip->fm_op1[channel][0]; chip->fm_op1[channel][0] = chip->fm_out[slot]; } /* OP2 */ if (slot / 6 == 2) { chip->fm_op2[channel] = chip->fm_out[slot]; } } void OPN2_ChGenerate(ym3438_t *chip) { Bit32u slot = (chip->cycles + 18) % 24; Bit32u channel = chip->channel; Bit32u op = slot / 6; Bit32u test_dac = chip->mode_test_2c[5]; Bit16s acc = chip->ch_acc[channel]; Bit16s add = test_dac; Bit16s sum = 0; Bit16s channel_maxsample = (1 << (channel_clipbits - 1)) - 1; Bit16s channel_minsample = -(1 << (channel_clipbits - 1)); if (op == 0 && !test_dac) { acc = 0; } if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) { add += chip->fm_out[slot] >> 5; } sum = acc + add; /* Clamp */ /*Bit16s channel_maxsample = (1 << (channel_clipbits - 1)) - 1; Bit16s channel_minsample = -(1 << (channel_clipbits - 1));*/ if (sum > channel_maxsample) { sum = channel_maxsample; } else if(sum < channel_minsample) { sum = channel_minsample; } if (op == 0 || test_dac) { chip->ch_out[channel] = chip->ch_acc[channel]; } chip->ch_acc[channel] = sum; } void OPN2_ChOutput(ym3438_t *chip) { Bit32u cycles = chip->cycles; Bit32u slot = chip->cycles; Bit32u channel = chip->channel; Bit32u test_dac = chip->mode_test_2c[5]; Bit16s out; Bit16s sign; Bit32u out_en; chip->ch_read = chip->ch_lock; if (slot < 12) { /* Ch 4,5,6 */ channel++; } if ((cycles & 3) == 0) { if (!test_dac) { /* Lock value */ chip->ch_lock = chip->ch_out[channel]; } chip->ch_lock_l = chip->pan_l[channel]; chip->ch_lock_r = chip->pan_r[channel]; } /* Ch 6 */ if (((cycles >> 2) == 1 && chip->dacen) || test_dac) { out = (Bit16s)chip->dacdata; out <<= 7; out >>= 7; } else { out = chip->ch_lock; } chip->mol = 0; chip->mor = 0; if (chip_type & ym3438_mode_ym2612) { out_en = ((cycles & 3) == 3) || test_dac; /* YM2612 DAC emulation(not verified) */ sign = out >> 8; if (out >= 0) { out++; sign++; } if (chip->ch_lock_l && out_en) { chip->mol = out; } else { chip->mol = sign; } if (chip->ch_lock_r && out_en) { chip->mor = out; } else { chip->mor = sign; } /* Amplify signal */ chip->mol *= 3; chip->mor *= 3; } else { out_en = ((cycles & 3) != 0) || test_dac; if (chip->ch_lock_l && out_en) { chip->mol = out; } if (chip->ch_lock_r && out_en) { chip->mor = out; } } } void OPN2_FMGenerate(ym3438_t *chip) { Bit32u slot = (chip->cycles + 19) % 24; /* Calculate phase */ Bit16u phase = (chip->fm_mod[slot] + (chip->pg_phase[slot] >> 10)) & 0x3ff; Bit16u quarter; Bit16u level; Bit16s output; if (phase & 0x100) { quarter = (phase ^ 0xff) & 0xff; } else { quarter = phase & 0xff; } level = logsinrom[quarter]; /* Apply envelope */ level += chip->eg_out[slot] << 2; /* Transform */ if (level > 0x1fff) { level = 0x1fff; } output = ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 2) >> (level >> 8); if (phase & 0x200) { output = ((~output) ^ (chip->mode_test_21[4] << 13)) + 1; } else { output = output ^ (chip->mode_test_21[4] << 13); } output <<= 2; output >>= 2; chip->fm_out[slot] = output; } /*OPN-MOD: generate ADPCM rhythm*/ void OPNmod_RhythmGenerate(ym3438_t *chip) { Bit32u channel = chip->channel; Bit32s out = 0; Bit8u panl = 0; Bit8u panr = 0; Bit32u step; Bit8u data; Bit16u end = YM2608_ADPCM_ROM_addr[2 * channel + 1] << 1; if (chip->cycles < 6 && chip->rhythm_key[channel]) { /*Bit32u step; Bit8u data; Bit16u end = YM2608_ADPCM_ROM_addr[2 * channel + 1] << 1;*/ panl = chip->rhythm_pan[channel] & 2; panr = chip->rhythm_pan[channel] & 1; /*from MAME*/ chip->rhythm_now_step[channel] += chip->rhythm_step[channel]; if (chip->rhythm_now_step[channel] >= (1u << adpcm_shift)) { step = chip->rhythm_now_step[channel] >> adpcm_shift; chip->rhythm_now_step[channel] &= (1 << adpcm_shift) - 1; do { /* end check */ /* 11-06-2001 JB: corrected comparison. Was > instead of == */ /* YM2610 checks lower 20 bits only, the 4 MSB bits are sample bank */ /* Here we use 1<<21 to compensate for nibble calculations */ if ((chip->rhythm_addr[channel] & ((1 << 21) - 1)) == (end & ((1 << 21) - 1))) { chip->rhythm_key[channel] = 0; goto end; } if (chip->rhythm_addr[channel] & 1) { data = chip->rhythm_data[channel] & 0x0f; } else { chip->rhythm_data[channel] = YM2608_ADPCM_ROM[chip->rhythm_addr[channel] >> 1]; data = (chip->rhythm_data[channel] >> 4) & 0x0f; } chip->rhythm_addr[channel]++; chip->rhythm_adpcm_acc[channel] += adpcm_jedi_table[chip->rhythm_adpcm_step[channel] + data]; /* the 12-bit accumulator wraps on the ym2610 and ym2608 (like the msm5205), it does not saturate (like the msm5218) */ chip->rhythm_adpcm_acc[channel] &= 0xfff; /* extend 12-bit signed int */ if (chip->rhythm_adpcm_acc[channel] & 0x800) { chip->rhythm_adpcm_acc[channel] |= ~0xfff; } chip->rhythm_adpcm_step[channel] += adpcm_step_inc[data & 7]; if (chip->rhythm_adpcm_step[channel] > 48 * 16) { chip->rhythm_adpcm_step[channel] = 48 * 16; } if (chip->rhythm_adpcm_step[channel] < 0) { chip->rhythm_adpcm_step[channel] = 0; } } while (--step); /* calc pcm * volume data */ out = ((chip->rhythm_adpcm_acc[channel] * chip->rhythm_vol_mul[channel]) >> chip->rhythm_vol_shift[channel]) & ~3; /* multiply, shift and mask out 2 LSB bits */ end: chip->rhythml[channel] = panl ? (out >> 1) : 0; chip->rhythmr[channel] = panr ? (out >> 1) : 0; } } } /*OPN-MOD: generate ADPCM DeltaT*/ void OPNmod_DeltaTGenerate(ym3438_t *chip) { Bit32u channel = chip->channel; Bit32s *out = chip->out_deltaT; out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0; if (chip->cycles == 0) { if (chip->deltaT.portstate & 0x80 /* && !chip->mute_deltaT */) YM_DELTAT_ADPCM_CALC(&chip->deltaT); chip->deltaTl[channel] = (out[2/*LEFT*/] + out[3/*CENTER*/]) / (1 << 8); chip->deltaTr[channel] = (out[1/*RIGHT*/] + out[3/*CENTER*/]) / (1 << 8); } else { chip->deltaTl[channel] = 0; chip->deltaTr[channel] = 0; } } void OPN2_DoTimerA(ym3438_t *chip) { Bit16u time; Bit8u load; load = chip->timer_a_overflow; if (chip->cycles == 2) { /* Lock load value */ load |= (!chip->timer_a_load_lock && chip->timer_a_load); chip->timer_a_load_lock = chip->timer_a_load; if (chip->mode_csm) { /* CSM KeyOn */ chip->mode_kon_csm = load; } else { chip->mode_kon_csm = 0; } } /* Load counter */ if (chip->timer_a_load_latch) { time = chip->timer_a_reg; } else { time = chip->timer_a_cnt; } chip->timer_a_load_latch = load; /* Increase counter */ if ((chip->cycles == 1 && chip->timer_a_load_lock) || chip->mode_test_21[2]) { time++; } /* Set overflow flag */ if (chip->timer_a_reset) { chip->timer_a_reset = 0; chip->timer_a_overflow_flag = 0; } else { chip->timer_a_overflow_flag |= chip->timer_a_overflow & chip->timer_a_enable; } chip->timer_a_overflow = (time >> 10); chip->timer_a_cnt = time & 0x3ff; } void OPN2_DoTimerB(ym3438_t *chip) { Bit16u time; Bit8u load; load = chip->timer_b_overflow; if (chip->cycles == 2) { /* Lock load value */ load |= (!chip->timer_b_load_lock && chip->timer_b_load); chip->timer_b_load_lock = chip->timer_b_load; } /* Load counter */ if (chip->timer_b_load_latch) { time = chip->timer_b_reg; } else { time = chip->timer_b_cnt; } chip->timer_b_load_latch = load; /* Increase counter */ if (chip->cycles == 1) { chip->timer_b_subcnt++; } if ((chip->timer_b_subcnt == 0x10 && chip->timer_b_load_lock) || chip->mode_test_21[2]) { time++; } chip->timer_b_subcnt &= 0x0f; /* Set overflow flag */ if (chip->timer_b_reset) { chip->timer_b_reset = 0; chip->timer_b_overflow_flag = 0; } else { chip->timer_b_overflow_flag |= chip->timer_b_overflow & chip->timer_b_enable; } chip->timer_b_overflow = (time >> 8); chip->timer_b_cnt = time & 0xff; } void OPN2_KeyOn(ym3438_t*chip) { Bit32u slot = chip->cycles; Bit32u chan = chip->channel; /* Key On */ chip->eg_kon_latch[slot] = chip->mode_kon[slot]; chip->eg_kon_csm[slot] = 0; if (chip->channel == 2 && chip->mode_kon_csm) { /* CSM Key On */ chip->eg_kon_latch[slot] = 1; chip->eg_kon_csm[slot] = 1; } if (chip->cycles == chip->mode_kon_channel) { /* OP1 */ chip->mode_kon[chan] = chip->mode_kon_operator[0]; /* OP2 */ chip->mode_kon[chan + 12] = chip->mode_kon_operator[1]; /* OP3 */ chip->mode_kon[chan + 6] = chip->mode_kon_operator[2]; /* OP4 */ chip->mode_kon[chan + 18] = chip->mode_kon_operator[3]; } } static void OPNmod_deltat_status_set(void *userdata, Bit8u changebits) { ym3438_t *chip = (ym3438_t *)userdata; chip->status |= ~changebits; } static void OPNmod_deltat_status_reset(void *userdata, Bit8u changebits) { ym3438_t *chip = (ym3438_t *)userdata; chip->status &= ~changebits; } void OPN2_Reset(ym3438_t *chip, Bit32u clock, const struct OPN2mod_psg_callbacks *psg, void *psgdata, Bit32u dramsize) { Bit32u i; Bit8u *pcmmem = chip->deltaT.memory; Bit32u pcmoldsize = chip->deltaT.memory_size; memset(chip, 0, sizeof(ym3438_t)); chip->deltaT.memory = pcmmem; for (i = 0; i < 24; i++) { chip->eg_out[i] = 0x3ff; chip->eg_level[i] = 0x3ff; chip->eg_state[i] = eg_num_release; chip->multi[i] = 1; } for (i = 0; i < 6; i++) { chip->pan_l[i] = 1; chip->pan_r[i] = 1; } /*OPN-MOD: initialize and connect PSG*/ chip->psg = psg; chip->psgdata = psgdata; psg->Reset(psgdata); psg->SetClock(psgdata, clock / 4); /*OPN-MOD: initialize rhythm*/ for (i = 0; i < 6; i++) { chip->rhythm_pan[i] = 3; chip->rhythm_step[i] = (Bit32u)((1 << adpcm_shift) / ((i < 4) ? 3.0 : 6.0)); OPNmod_RhythmUpdateVolume(chip, i); } /*OPN-MOD: initialize ADPCM*/ /*//chip->deltaT.memory = (UINT8 *)pcmrom; //chip->deltaT.memory_size = pcmsize; //chip->deltaT.memory = NULL; //chip->deltaT.memory_size = 0x00; //chip->deltaT.memory_mask = 0x00;*/ chip->deltaT.memory = pcmmem = (Bit8u *)realloc(pcmmem, dramsize); chip->deltaT.memory_size = dramsize; if (!pcmmem) abort(); if (pcmoldsize < dramsize) memset(pcmmem + pcmoldsize, 0, dramsize - pcmoldsize); YM_DELTAT_calc_mem_mask(&chip->deltaT); /*chip->deltaT.write_time = 20.0 / clock;*/ /* a single byte write takes 20 cycles of main clock */ /*chip->deltaT.read_time = 18.0 / clock;*/ /* a single byte read takes 18 cycles of main clock */ chip->deltaT.status_set_handler = &OPNmod_deltat_status_set; chip->deltaT.status_reset_handler = &OPNmod_deltat_status_reset; chip->deltaT.status_change_which_chip = chip; chip->deltaT.status_change_EOS_bit = 0x04; /* status flag: set bit2 on End Of Sample */ chip->deltaT.status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY */ chip->deltaT.status_change_ZERO_bit = 0x10; /* status flag: set bit4 if silence continues for more than 290 miliseconds while recording the ADPCM */ chip->deltaT.freqbase = 1.0; /* TODO(jpc) set the freqbase value to fixed */ chip->deltaT.output_pointer = chip->out_deltaT; chip->deltaT.portshift = 5; /* always 5bits shift */ /* ASG */ chip->deltaT.output_range = 1<<23; YM_DELTAT_ADPCM_Reset(&chip->deltaT, 3/*CENTER*/, YM_DELTAT_EMULATION_MODE_NORMAL); } void OPN2_Destroy(ym3438_t *chip) { if (!chip) return; free(chip->deltaT.memory); chip->deltaT.memory = NULL; } void OPN2_SetChipType(Bit32u type) { chip_type = type; } void OPN2_Clock(ym3438_t *chip, Bit16s *buffer) { Bit32u slot = chip->cycles; Bit16s *rhythml = &chip->rhythml[chip->channel]; Bit16s *rhythmr = &chip->rhythmr[chip->channel]; Bit16s *deltaTl = &chip->deltaTl[chip->channel]; Bit16s *deltaTr = &chip->deltaTr[chip->channel]; chip->lfo_inc = chip->mode_test_21[1]; chip->pg_read >>= 1; chip->eg_read[1] >>= 1; chip->eg_cycle++; /* Lock envelope generator timer value */ if (chip->cycles == 1 && chip->eg_quotient == 2) { if (chip->eg_cycle_stop) { chip->eg_shift_lock = 0; } else { chip->eg_shift_lock = chip->eg_shift + 1; } chip->eg_timer_low_lock = chip->eg_timer & 0x03; } /* Cycle specific functions */ switch (chip->cycles) { case 0: chip->lfo_pm = chip->lfo_cnt >> 2; if (chip->lfo_cnt & 0x40) { chip->lfo_am = chip->lfo_cnt & 0x3f; } else { chip->lfo_am = chip->lfo_cnt ^ 0x3f; } chip->lfo_am <<= 1; break; case 1: chip->eg_quotient++; chip->eg_quotient %= 3; chip->eg_cycle = 0; chip->eg_cycle_stop = 1; chip->eg_shift = 0; chip->eg_timer_inc |= chip->eg_quotient >> 1; chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; chip->eg_timer_inc = chip->eg_timer >> 12; chip->eg_timer &= 0xfff; break; case 2: chip->pg_read = chip->pg_phase[21] & 0x3ff; chip->eg_read[1] = chip->eg_out[0]; break; case 13: chip->eg_cycle = 0; chip->eg_cycle_stop = 1; chip->eg_shift = 0; chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; chip->eg_timer_inc = chip->eg_timer >> 12; chip->eg_timer &= 0xfff; break; case 23: chip->lfo_inc |= 1; break; } chip->eg_timer &= ~(chip->mode_test_21[5] << chip->eg_cycle); if (((chip->eg_timer >> chip->eg_cycle) | (chip->pin_test_in & chip->eg_custom_timer)) & chip->eg_cycle_stop) { chip->eg_shift = chip->eg_cycle; chip->eg_cycle_stop = 0; } OPN2_DoIO(chip); OPN2_DoTimerA(chip); OPN2_DoTimerB(chip); OPN2_KeyOn(chip); OPN2_ChOutput(chip); OPN2_ChGenerate(chip); OPN2_FMPrepare(chip); OPN2_FMGenerate(chip); OPNmod_RhythmGenerate(chip); OPNmod_DeltaTGenerate(chip); OPN2_PhaseGenerate(chip); OPN2_PhaseCalcIncrement(chip); OPN2_EnvelopeADSR(chip); OPN2_EnvelopeGenerate(chip); OPN2_EnvelopeSSGEG(chip); OPN2_EnvelopePrepare(chip); /* Prepare fnum & block */ if (chip->mode_ch3) { /* Channel 3 special mode */ switch (slot) { case 1: /* OP1 */ chip->pg_fnum = chip->fnum_3ch[1]; chip->pg_block = chip->block_3ch[1]; chip->pg_kcode = chip->kcode_3ch[1]; break; case 7: /* OP3 */ chip->pg_fnum = chip->fnum_3ch[0]; chip->pg_block = chip->block_3ch[0]; chip->pg_kcode = chip->kcode_3ch[0]; break; case 13: /* OP2 */ chip->pg_fnum = chip->fnum_3ch[2]; chip->pg_block = chip->block_3ch[2]; chip->pg_kcode = chip->kcode_3ch[2]; break; case 19: /* OP4 */ default: chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; chip->pg_block = chip->block[(chip->channel + 1) % 6]; chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; break; } } else { chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; chip->pg_block = chip->block[(chip->channel + 1) % 6]; chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; } OPN2_UpdateLFO(chip); OPN2_DoRegWrite(chip); chip->cycles = (chip->cycles + 1) % 24; chip->channel = chip->cycles % 6; buffer[0] = chip->mol; buffer[1] = chip->mor; /*OPN-MOD: Rhythm output*/ buffer[2] = *rhythml; buffer[3] = *rhythmr; /*OPN-MOD: DeltaT output*/ buffer[4] = *deltaTl; buffer[5] = *deltaTr; if (chip->status_time) chip->status_time--; } void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) { port &= 3; chip->write_data = ((port << 7) & 0x100) | data; if (port & 1) { /* Data */ chip->write_d |= 1; } else { /* Address */ chip->write_a |= 1; } } void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) { chip->pin_test_in = value & 1; } Bit32u OPN2_ReadTestPin(ym3438_t *chip) { if (!chip->mode_test_2c[7]) { return 0; } return chip->cycles == 23; } Bit32u OPN2_ReadIRQPin(ym3438_t *chip) { return chip->timer_a_overflow_flag | chip->timer_b_overflow_flag; } Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) { Bit32u slot; Bit16u testdata; if ((port & 3) == 0 || (chip_type & ym3438_mode_readmode)) { if (chip->mode_test_21[6]) { /* Read test data */ /*Bit32u slot = (chip->cycles + 18) % 24; Bit16u testdata = ((chip->pg_read & 0x01) << 15) | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14);*/ slot = (chip->cycles + 18) % 24; testdata = ((chip->pg_read & 0x01) << 15) | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14); if (chip->mode_test_2c[4]) { testdata |= chip->ch_read & 0x1ff; } else { testdata |= chip->fm_out[slot] & 0x3fff; } if (chip->mode_test_21[7]) { chip->status = testdata & 0xff; } else { chip->status = testdata >> 8; } } else { chip->status = (chip->busy << 7) | (chip->timer_b_overflow_flag << 1) | chip->timer_a_overflow_flag; } /*OPN-MOD: status of DeltaT */ chip->status |= (chip->deltaT.PCM_BSY & 1) << 5; if (chip_type & ym3438_mode_ym2612) { chip->status_time = 300000; } else { chip->status_time = 40000000; } } /*OPN-MOD: read from PSG*/ if ((port & 3) == 1) { if (chip->address < 16) { return chip->psg->Read(chip->psgdata); } else if(chip->address == 0xff) { return 1; /* ID code */ } } /*OPN-MOD: read from DeltaT*/ if ((port & 3) == 3) { if (chip->address == 0x08) { return YM_DELTAT_ADPCM_Read(&chip->deltaT); } } if (chip->status_time) { return chip->status; } return 0; } void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data) { Bit64u time1, time2; Bit16s buffer[6]; Bit64u skip; if (chip->writebuf[chip->writebuf_last].port & 0x04) { OPN2_Write(chip, chip->writebuf[chip->writebuf_last].port & 0X03, chip->writebuf[chip->writebuf_last].data); chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; while (skip--) { OPN2_Clock(chip, buffer); } } chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; chip->writebuf[chip->writebuf_last].data = data; time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; time2 = chip->writebuf_samplecnt; if (time1 < time2) { time1 = time2; } chip->writebuf[chip->writebuf_last].time = time1; chip->writebuf_lasttime = time1; chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; } void OPN2_FlushBuffer(ym3438_t *chip) { Bit16s buffer[6]; Bit64u skip; while (chip->writebuf[chip->writebuf_cur].port & 0x04) { chip->writebuf[chip->writebuf_cur].port &= 0x03; OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, chip->writebuf[chip->writebuf_cur].data); skip = chip->writebuf[chip->writebuf_cur].time - chip->writebuf_samplecnt; chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_cur].time; chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; while (skip--) { OPN2_Clock(chip, buffer); } } } void OPN2_Generate(ym3438_t *chip, sample *samples) { Bit32u i; Bit16s buffer[6]; samples[0] = 0; samples[1] = 0; for (i = 0; i < 24; i++) { OPN2_Clock(chip, buffer); samples[0] += buffer[0] * 11; samples[1] += buffer[1] * 11; /*OPN-MOD: mix rhythm samples*/ samples[0] += buffer[2]; samples[1] += buffer[3]; /*OPN-MOD: mix deltaT samples*/ samples[0] += buffer[4]; samples[1] += buffer[5]; while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) { if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) { break; } chip->writebuf[chip->writebuf_cur].port &= 0x03; OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, chip->writebuf[chip->writebuf_cur].data); chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; } chip->writebuf_samplecnt++; } } BambooTracker-0.4.6/BambooTracker/chip/nuked/ym3438.h000066400000000000000000000161231401124043500221150ustar00rootroot00000000000000/* * Copyright (C) 2017-2018 Alexey Khokholov (Nuke.YKT) * Copyright (C) 2019 Jean Pierre Cimalando * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Nuked OPN2-MOD emulator, with OPNA functionality added. * Thanks: * Silicon Pr0n: * Yamaha YM3438 decap and die shot(digshadow). * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): * OPL2 ROMs. * * version: 1.0.9 * * OPN-MOD additions: * - Jean Pierre Cimalando 2019-04-06: add SSG control interface * - Jean Pierre Cimalando 2019-04-06: add 6-channel FM flag * - Jean Pierre Cimalando 2019-04-06: add ADPCM rhythm channels * - Jean Pierre Cimalando 2019-04-07: raise the channel clipping threshold */ #ifndef YM3438_H #define YM3438_H #include "../chip_def.h" #include "../mame/mamedef.h" #include "../mame/ymdeltat.h" #ifdef __cplusplus extern "C" { #endif /*EXTRA*/ #define RSM_FRAC 10 #define OPN_WRITEBUF_SIZE 2048 #define OPN_WRITEBUF_DELAY 15 enum { ym3438_mode_ym2612 = 0x01, /* Enables YM2612 emulation (MD1, MD2 VA2) */ ym3438_mode_readmode = 0x02 /* Enables status read on any port (TeraDrive, MD1 VA7, MD2, etc) */ }; #include typedef uintptr_t Bitu; typedef intptr_t Bits; typedef uint64_t Bit64u; typedef int64_t Bit64s; typedef uint32_t Bit32u; typedef int32_t Bit32s; typedef uint16_t Bit16u; typedef int16_t Bit16s; typedef uint8_t Bit8u; typedef int8_t Bit8s; /*EXTRA*/ typedef struct _opn2_writebuf { Bit64u time; Bit8u port; Bit8u data; Bit8u reserved[6]; } opn2_writebuf; typedef struct { Bit32u cycles; Bit32u channel; Bit16s mol, mor; /*OPN-MOD: Rhythm channel outputs*/ Bit16s rhythml[6]; Bit16s rhythmr[6]; /*OPN-MOD: DeltaT channel outputs*/ Bit16s deltaTl[6]; Bit16s deltaTr[6]; /* IO */ Bit16u write_data; Bit8u write_a; Bit8u write_d; Bit8u write_a_en; Bit8u write_d_en; Bit8u write_busy; Bit8u write_busy_cnt; Bit8u write_fm_address; Bit8u write_fm_data; Bit8u write_fm_mode_a; Bit16u address; Bit8u data; Bit8u pin_test_in; Bit8u pin_irq; Bit8u busy; /* LFO */ Bit8u lfo_en; Bit8u lfo_freq; Bit8u lfo_pm; Bit8u lfo_am; Bit8u lfo_cnt; Bit8u lfo_inc; Bit8u lfo_quotient; /* Phase generator */ Bit16u pg_fnum; Bit8u pg_block; Bit8u pg_kcode; Bit32u pg_inc[24]; Bit32u pg_phase[24]; Bit8u pg_reset[24]; Bit32u pg_read; /* Envelope generator */ Bit8u eg_cycle; Bit8u eg_cycle_stop; Bit8u eg_shift; Bit8u eg_shift_lock; Bit8u eg_timer_low_lock; Bit16u eg_timer; Bit8u eg_timer_inc; Bit16u eg_quotient; Bit8u eg_custom_timer; Bit8u eg_rate; Bit8u eg_ksv; Bit8u eg_inc; Bit8u eg_ratemax; Bit8u eg_sl[2]; Bit8u eg_lfo_am; Bit8u eg_tl[2]; Bit8u eg_state[24]; Bit16u eg_level[24]; Bit16u eg_out[24]; Bit8u eg_kon[24]; Bit8u eg_kon_csm[24]; Bit8u eg_kon_latch[24]; Bit8u eg_csm_mode[24]; Bit8u eg_ssg_enable[24]; Bit8u eg_ssg_pgrst_latch[24]; Bit8u eg_ssg_repeat_latch[24]; Bit8u eg_ssg_hold_up_latch[24]; Bit8u eg_ssg_dir[24]; Bit8u eg_ssg_inv[24]; Bit32u eg_read[2]; Bit8u eg_read_inc; /* FM */ Bit16s fm_op1[6][2]; Bit16s fm_op2[6]; Bit16s fm_out[24]; Bit16u fm_mod[24]; /* Channel */ Bit16s ch_acc[6]; Bit16s ch_out[6]; Bit16s ch_lock; Bit8u ch_lock_l; Bit8u ch_lock_r; Bit16s ch_read; /*OPNA: Rhythm channel*/ Bit8u rhythm_tl; Bit8u rhythm_key[6]; Bit8u rhythm_pan[6]; Bit8u rhythm_level[6]; Bit16u rhythm_addr[6]; Bit8u rhythm_data[6]; Bit32u rhythm_step[6]; Bit32u rhythm_now_step[6]; Bit32s rhythm_adpcm_step[6]; Bit32s rhythm_adpcm_acc[6]; Bit8s rhythm_vol_mul[6]; Bit8u rhythm_vol_shift[6]; /* Timer */ Bit16u timer_a_cnt; Bit16u timer_a_reg; Bit8u timer_a_load_lock; Bit8u timer_a_load; Bit8u timer_a_enable; Bit8u timer_a_reset; Bit8u timer_a_load_latch; Bit8u timer_a_overflow_flag; Bit8u timer_a_overflow; Bit16u timer_b_cnt; Bit8u timer_b_subcnt; Bit16u timer_b_reg; Bit8u timer_b_load_lock; Bit8u timer_b_load; Bit8u timer_b_enable; Bit8u timer_b_reset; Bit8u timer_b_load_latch; Bit8u timer_b_overflow_flag; Bit8u timer_b_overflow; /* Register set */ Bit8u mode_test_21[8]; Bit8u mode_test_2c[8]; Bit8u mode_ch3; Bit8u mode_kon_channel; Bit8u mode_kon_operator[4]; Bit8u mode_kon[24]; Bit8u mode_csm; Bit8u mode_kon_csm; Bit8u dacen; Bit16s dacdata; /*OPN-MOD: 6 FM channel flag*/ Bit8u mode_fm6ch; Bit8u ks[24]; Bit8u ar[24]; Bit8u sr[24]; Bit8u dt[24]; Bit8u multi[24]; Bit8u sl[24]; Bit8u rr[24]; Bit8u dr[24]; Bit8u am[24]; Bit8u tl[24]; Bit8u ssg_eg[24]; Bit16u fnum[6]; Bit8u block[6]; Bit8u kcode[6]; Bit16u fnum_3ch[6]; Bit8u block_3ch[6]; Bit8u kcode_3ch[6]; Bit8u reg_a4; Bit8u reg_ac; Bit8u connect[6]; Bit8u fb[6]; Bit8u pan_l[6], pan_r[6]; Bit8u ams[6]; Bit8u pms[6]; Bit8u status; Bit32u status_time; /*EXTRA*/ Bit64u writebuf_samplecnt; Bit32u writebuf_cur; Bit32u writebuf_last; Bit64u writebuf_lasttime; opn2_writebuf writebuf[OPN_WRITEBUF_SIZE]; /*OPN-MOD*/ const struct OPN2mod_psg_callbacks *psg; void *psgdata; /*OPN-MOD*/ YM_DELTAT deltaT; Bit32s out_deltaT[4]; } ym3438_t; void OPN2_Reset(ym3438_t *chip, Bit32u clock, const struct OPN2mod_psg_callbacks *psg, void *psgdata, Bit32u dramsize); void OPN2_Destroy(ym3438_t *chip); void OPN2_SetChipType(Bit32u type); void OPN2_Clock(ym3438_t *chip, Bit16s *buffer); void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); Bit32u OPN2_ReadTestPin(ym3438_t *chip); Bit32u OPN2_ReadIRQPin(ym3438_t *chip); Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); /*EXTRA*/ void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data); void OPN2_FlushBuffer(ym3438_t *chip); void OPN2_Generate(ym3438_t *chip, sample *samples); /*OPN-MOD*/ struct OPN2mod_psg_callbacks { void (*SetClock)(void *param, int clock); void (*Write)(void *param, int address, int data); int (*Read)(void *param); void (*Reset)(void *param); }; #ifdef __cplusplus } #endif #endif BambooTracker-0.4.6/BambooTracker/chip/opna.cpp000066400000000000000000000154711401124043500213350ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "opna.hpp" #include #include #include #include "scci/SCCIDefines.hpp" #include "scci/scci.hpp" #include "c86ctl/c86ctl_wrapper.hpp" #include "register_write_logger.hpp" extern "C" { #include "mame/2608intf.h" #include "nuked/nuke2608intf.h" } namespace chip { namespace { constexpr double VOL_REDUC_ = 7.5; enum SoundSourceIndex : int { FM = 0, SSG = 1 }; inline double clamp(double value, double low, double high) { return std::min(std::max(value, low), high); } } size_t OPNA::count_ = 0; OPNA::OPNA(OpnaEmulator emu, int clock, int rate, size_t maxDuration, size_t dramSize, std::unique_ptr fmResampler, std::unique_ptr ssgResampler, std::shared_ptr logger) : Chip(count_++, clock, rate, 110933, maxDuration, std::move(fmResampler), std::move(ssgResampler), // autoRate = 110933: FM internal rate logger), dramSize_(dramSize), scciManager_(nullptr), scciChip_(nullptr), c86ctlBase_(nullptr), c86ctlRC_(nullptr), c86ctlGm_(nullptr) { switch (emu) { default: fprintf(stderr, "Unknown emulator choice. Using the default.\n"); /* fall through */ case OpnaEmulator::Mame: fprintf(stderr, "Using emulator: MAME YM2608\n"); intf_ = &mame_intf2608; break; case OpnaEmulator::Nuked: fprintf(stderr, "Using emulator: Nuked OPN-Mod\n"); intf_ = &nuked_intf2608; break; } funcSetRate(rate); uint8_t EmuCore = 0; intf_->set_ay_emu_core(EmuCore); uint8_t AYDisable = 0; // Enable uint8_t AYFlags = 0; // None internalRate_[FM] = intf_->device_start( id_, clock, AYDisable, AYFlags, reinterpret_cast(&internalRate_[SSG]), dramSize); initResampler(); setVolumeFM(0); setVolumeSSG(0); reset(); } OPNA::~OPNA() { intf_->device_stop(id_); --count_; useSCCI(nullptr); useC86CTL(nullptr); } void OPNA::reset() { std::lock_guard lg(mutex_); intf_->device_reset(id_); if (scciChip_) scciChip_->init(); if (c86ctlRC_) c86ctlRC_->resetChip(); } void OPNA::setRegister(uint32_t offset, uint8_t value) { std::lock_guard lg(mutex_); if (logger_) { logger_->recordRegisterChange(offset, value); } else { if (offset & 0x100) { intf_->control_port_b_w(id_, 2, offset & 0xff); intf_->data_port_b_w(id_, 3, value & 0xff); } else { intf_->control_port_a_w(id_, 0, offset & 0xff); intf_->data_port_a_w(id_, 1, value & 0xff); } } if (scciChip_) scciChip_->setRegister(offset, value); if (c86ctlRC_) c86ctlRC_->out(offset, value); } uint8_t OPNA::getRegister(uint32_t offset) const { if (offset & 0x100) { intf_->control_port_b_w(id_, 2, offset & 0xff); } else { intf_->control_port_a_w(id_, 0, offset & 0xff); } return intf_->read_port_r(id_, 1); } void OPNA::setVolumeFM(double dB) { std::lock_guard lg(mutex_); volumeRatio_[FM] = std::pow(10.0, (dB - VOL_REDUC_) / 20.0); } void OPNA::setVolumeSSG(double dB) { std::lock_guard lg(mutex_); volumeRatio_[SSG] = std::pow(10.0, (dB - VOL_REDUC_) / 20.0); if (c86ctlGm_) { // NOTE: estimate SSG volume roughly uint8_t vol = static_cast(std::round((dB < -3.0) ? (2.5 * dB + 45.5) : (7. * dB + 59.))); c86ctlGm_->setSSGVolume(vol); } } size_t OPNA::getDRAMSize() const noexcept { return dramSize_; } void OPNA::mix(int16_t* stream, size_t nSamples) { std::lock_guard lg(mutex_); sample **bufFM, **bufSSG; // Set FM buffer if (internalRate_[FM] == rate_) { intf_->stream_update(id_, buffer_[FM], nSamples); bufFM = buffer_[FM]; } else { size_t intrSize = resampler_[FM]->calculateInternalSampleSize(nSamples); intf_->stream_update(id_, buffer_[FM], intrSize); bufFM = resampler_[FM]->interpolate(buffer_[FM], nSamples, intrSize); } // Set SSG buffer if (internalRate_[SSG] == rate_) { intf_->stream_update_ay(id_, buffer_[SSG], nSamples); bufSSG = buffer_[SSG]; } else { size_t intrSize = resampler_[SSG]->calculateInternalSampleSize(nSamples); intf_->stream_update_ay(id_, buffer_[SSG], intrSize); bufSSG = resampler_[SSG]->interpolate(buffer_[SSG], nSamples, intrSize); } int16_t* p = stream; for (size_t i = 0; i < nSamples; ++i) { for (int pan = STEREO_LEFT; pan <= STEREO_RIGHT; ++pan) { double s = volumeRatio_[FM] * bufFM[pan][i] + volumeRatio_[SSG] * bufSSG[pan][i]; *p++ = static_cast(clamp(s * masterVolumeRatio_, -32768.0, 32767.0)); } } } void OPNA::useSCCI(scci::SoundInterfaceManager* manager) { if (manager) { scciManager_ = manager; scciManager_->initializeInstance(); scciManager_->reset(); scciChip_ = scciManager_->getSoundChip(scci::SC_TYPE_YM2608, scci::SC_CLOCK_7987200); if (!scciChip_) { scciManager_->releaseInstance(); scciManager_ = nullptr; } } else { if (!scciChip_) return; scciManager_->releaseSoundChip(scciChip_); scciChip_ = nullptr; scciManager_->releaseInstance(); scciManager_ = nullptr; } } bool OPNA::isUsedSCCI() const noexcept { return (scciManager_ != nullptr); } void OPNA::useC86CTL(C86ctlBase* base) { if (!base || base->isEmpty()) { if (!c86ctlBase_) return; c86ctlRC_->resetChip(); c86ctlGm_.reset(); c86ctlRC_.reset(); c86ctlBase_->deinitialize(); } else { c86ctlBase_.reset(base); c86ctlBase_->initialize(); int nChip = c86ctlBase_->getNumberOfChip(); for (int i = 0; i < nChip; ++i) { C86ctlRealChip* rc = c86ctlBase_->getChipInterface(i); if (rc) { c86ctlRC_.reset(rc); c86ctlRC_->resetChip(); if (C86ctlGimic* gm = c86ctlRC_->queryInterface()) { c86ctlGm_.reset(gm); return; } c86ctlRC_.reset(); } } base->deinitialize(); } c86ctlBase_.reset(); } bool OPNA::isUsedC86CTL() const noexcept { return (c86ctlBase_ != nullptr); } } BambooTracker-0.4.6/BambooTracker/chip/opna.hpp000066400000000000000000000051271401124043500213370ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "chip.hpp" #include #include "resampler.hpp" namespace scci { class SoundInterfaceManager; class SoundChip; } class C86ctlBase; class C86ctlRealChip; class C86ctlGimic; namespace chip { enum class OpnaEmulator { Mame, Nuked, First = Mame, Last = Nuked, }; class OPNA final : public Chip { public: // [rate] // 0 = rate is 55466 (FM synthesis rate when clock is 3993600 * 2) OPNA(OpnaEmulator emu, int clock, int rate, size_t maxDuration, size_t dramSize, std::unique_ptr fmResampler = std::make_unique(), std::unique_ptr ssgResampler = std::make_unique(), std::shared_ptr logger = nullptr); ~OPNA() override; void reset() override; void setRegister(uint32_t offset, uint8_t value) override; uint8_t getRegister(uint32_t offset) const override; void setVolumeFM(double dB); void setVolumeSSG(double dB); size_t getDRAMSize() const noexcept; void mix(int16_t* stream, size_t nSamples) override; void useSCCI(scci::SoundInterfaceManager* manager); bool isUsedSCCI() const noexcept; void useC86CTL(C86ctlBase* base); bool isUsedC86CTL() const noexcept; private: static size_t count_; intf2608* intf_; size_t dramSize_; // For SCCI scci::SoundInterfaceManager* scciManager_; scci::SoundChip* scciChip_; // For C86CTL std::unique_ptr c86ctlBase_; std::unique_ptr c86ctlRC_; std::unique_ptr c86ctlGm_; }; } BambooTracker-0.4.6/BambooTracker/chip/register_write_logger.cpp000066400000000000000000000226621401124043500247750ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "register_write_logger.hpp" #include "io/export_io.hpp" #include namespace chip { AbstractRegisterWriteLogger::AbstractRegisterWriteLogger(int target) : target_(target), lastWait_(0), isSetLoop_(false), loopPoint_(0) { } void AbstractRegisterWriteLogger::elapse(size_t count) noexcept { lastWait_ += count; totalSampCnt_ += count; } bool AbstractRegisterWriteLogger::empty() const noexcept { return (buf_.empty() || lastWait_ != 0); } void AbstractRegisterWriteLogger::clear() noexcept { buf_.clear(); lastWait_ = 0; totalSampCnt_ = 0; isSetLoop_ = false; loopPoint_ = 0; } std::vector AbstractRegisterWriteLogger::getData() { if (lastWait_) setWait(); return buf_; } size_t AbstractRegisterWriteLogger::getSampleLength() const noexcept { return totalSampCnt_; } size_t AbstractRegisterWriteLogger::setLoopPoint() { if (lastWait_) setWait(); isSetLoop_ = true; return loopPoint_; } size_t AbstractRegisterWriteLogger::forceMoveLoopPoint() noexcept { loopPoint_ = buf_.size(); return loopPoint_; } //******************************// VgmLogger::VgmLogger(int target, uint32_t intrRate) : AbstractRegisterWriteLogger(target), intrRate_(intrRate) {} void VgmLogger::recordRegisterChange(uint32_t offset, uint8_t value) { if (lastWait_) setWait(); const int fm = target_ & io::Export_FmMask; const int ssg = target_ & io::Export_SsgMask; const uint8_t cmdSsg = (ssg != io::Export_InternalSsg) ? 0xa0 : (fm == io::Export_YM2608) ? 0x56 : (fm == io::Export_YM2203) ? 0x55 : 0x00; const uint8_t cmdFmPortA = (fm == io::Export_YM2608) ? 0x56 : (fm == io::Export_YM2612) ? 0x52 : (fm == io::Export_YM2203) ? 0x55 : 0x00; const uint8_t cmdFmPortB = (fm == io::Export_YM2608) ? 0x57 : (fm == io::Export_YM2612) ? 0x53 : 0x00; if (cmdSsg && offset < 0x10) { buf_.push_back(cmdSsg); buf_.push_back(offset); buf_.push_back(value); } else if (cmdFmPortA && (offset & 0x100) == 0) { bool compatible = true; if (offset == 0x28) { // Key register if (fm == io::Export_YM2203 && (value & 7) >= 3) compatible = false; } else if (offset == 0x29) // Mode register compatible = fm == io::Export_YM2608; else if ((offset & 0xf0) == 0x10) // Rhythm section compatible = fm == io::Export_YM2608; if (compatible) { buf_.push_back(cmdFmPortA); buf_.push_back(offset & 0xff); buf_.push_back(value); } } else if (cmdFmPortB && (offset & 0x100) != 0) { bool compatible = true; if (offset < 0x10) // ADPCM section compatible = fm == io::Export_YM2608; if (compatible) { buf_.push_back(cmdFmPortB); buf_.push_back(offset & 0xff); buf_.push_back(value); } } } void VgmLogger::setDataBlock(std::vector data) { buf_.push_back(0x67); buf_.push_back(0x66); buf_.push_back(0x81); size_t blockSize = data.size() + 8; buf_.push_back(blockSize & 0xff); buf_.push_back((blockSize >> 8) & 0xff); buf_.push_back((blockSize >> 16) & 0xff); buf_.push_back(blockSize >> 24); buf_.push_back(data.size() & 0xff); buf_.push_back((data.size() >> 8) & 0xff); buf_.push_back((data.size() >> 16) & 0xff); buf_.push_back(data.size() >> 24); buf_.resize(buf_.size() + 4); // Start address is 0 std::copy(data.begin(), data.end(), std::back_inserter(buf_)); } void VgmLogger::setWait() { while (lastWait_) { uint32_t sub; if (intrRate_ == 50) { if (lastWait_ > 65535) { uint32_t tmp = lastWait_ - 65535; if (tmp <= 882) { //65535 - (882 - tmp) sub = 64653 + tmp; } else if (tmp <= 1764) { //65535 - (1764 - tmp) sub = 63771 + tmp; } else if (tmp <= 2646) { //65535 - (2646 - tmp) sub = 62889 + tmp; } else { sub = 65535; } buf_.push_back(0x61); buf_.push_back(sub & 0x00ff); buf_.push_back(sub >> 8); } else { if (lastWait_ <= 16) { buf_.push_back(0x70 | (lastWait_ - 1)); } else if (lastWait_ > 2646) { buf_.push_back(0x61); buf_.push_back(lastWait_ & 0x00ff); buf_.push_back(lastWait_ >> 8); } else if (lastWait_ == 2646) { buf_.push_back(0x63); buf_.push_back(0x63); buf_.push_back(0x63); } else if (1764 <= lastWait_ && lastWait_ <= 1780) { uint32_t tmp = lastWait_ - 1764; buf_.push_back(0x63); buf_.push_back(0x63); if (tmp) buf_.push_back(0x70 | (tmp - 1)); } else if (882 <= lastWait_ && lastWait_ <= 898) { uint32_t tmp = lastWait_ - 882; buf_.push_back(0x63); if (tmp) buf_.push_back(0x70 | (tmp - 1)); } else { buf_.push_back(0x61); buf_.push_back(lastWait_ & 0x00ff); buf_.push_back(lastWait_ >> 8); } sub = lastWait_; } } else if (intrRate_ == 60) { if (lastWait_ > 65535) { uint32_t tmp = lastWait_ - 65535; if (tmp <= 735) { //65535 - (735 - tmp) sub = 64800 + tmp; } else if (tmp <= 1470) { //65535 - (1470 - tmp) sub = 64065 + tmp; } else if (tmp <= 2205) { //65535 - (2205 - tmp) sub = 63330 + tmp; } else { sub = 65535; } buf_.push_back(0x61); buf_.push_back(sub & 0x00ff); buf_.push_back(sub >> 8); } else { if (lastWait_ <= 16) { buf_.push_back(0x70 | (lastWait_ - 1)); } else if (lastWait_ > 2205) { buf_.push_back(0x61); buf_.push_back(lastWait_ & 0x00ff); buf_.push_back(lastWait_ >> 8); } else if (lastWait_ == 2205) { buf_.push_back(0x62); buf_.push_back(0x62); buf_.push_back(0x62); } else if (1470 <= lastWait_ && lastWait_ <= 1486) { uint32_t tmp = lastWait_ - 1470; buf_.push_back(0x62); buf_.push_back(0x62); if (tmp) buf_.push_back(0x70 | (tmp - 1)); } else if (735 <= lastWait_ && lastWait_ <= 751) { uint32_t tmp = lastWait_ - 735; buf_.push_back(0x62); if (tmp) buf_.push_back(0x70 | (tmp - 1)); } else { buf_.push_back(0x61); buf_.push_back(lastWait_ & 0x00ff); buf_.push_back(lastWait_ >> 8); } sub = lastWait_; } } else { if (lastWait_ > 65535) { sub = 65535; buf_.push_back(0x61); buf_.push_back(sub & 0x00ff); buf_.push_back(sub >> 8); } else { buf_.push_back(0x61); buf_.push_back(lastWait_ & 0x00ff); buf_.push_back(lastWait_ >> 8); } sub = lastWait_; } lastWait_ -= sub; } if (!isSetLoop_) loopPoint_ = buf_.size(); } //******************************// S98Logger::S98Logger(int target) : AbstractRegisterWriteLogger(target) {} void S98Logger::recordRegisterChange(uint32_t offset, uint8_t value) { if (lastWait_) setWait(); const int fm = target_ & io::Export_FmMask; const int ssg = target_ & io::Export_SsgMask; const uint8_t cmdSsg = (ssg != io::Export_InternalSsg) ? (fm == io::Export_NoneFm) ? 0x01 : 0x02 : (fm == io::Export_YM2608) ? 0x00 : (fm == io::Export_YM2203) ? 0x00 : 0xff; const uint8_t cmdFmPortA = (fm != io::Export_NoneFm) ? 0x00 : 0xff; const uint8_t cmdFmPortB = (fm == io::Export_YM2608 || fm == io::Export_YM2612) ? 0x01 : 0xff; if (cmdSsg != 0xff && offset < 0x10) { buf_.push_back(cmdSsg); buf_.push_back(offset); buf_.push_back(value); } else if (cmdFmPortA != 0xff && (offset & 0x100) == 0) { bool compatible = true; if (offset == 0x28) { // Key register if (fm == io::Export_YM2203 && (value & 7) >= 3) compatible = false; } else if (offset == 0x29) // Mode register compatible = fm == io::Export_YM2608; else if ((offset & 0xf0) == 0x10) // Rhythm section compatible = fm == io::Export_YM2608; if (compatible) { buf_.push_back(cmdFmPortA); buf_.push_back(offset & 0xff); buf_.push_back(value); } } else if (cmdFmPortB != 0xff && (offset & 0x100) != 0) { bool compatible = true; if (offset < 0x10) // ADPCM section compatible = fm == io::Export_YM2608; if (compatible) { buf_.push_back(cmdFmPortB); buf_.push_back(offset & 0xff); buf_.push_back(value); } } } void S98Logger::setWait() { if (lastWait_ == 1) { buf_.push_back(0xff); } else { buf_.push_back(0xfe); lastWait_ -= 2; do { uint8_t b = lastWait_ & 0x7f; lastWait_ >>= 7; if (lastWait_ > 0) b |= 0x80; buf_.push_back(b); } while (lastWait_ > 0); } if (!isSetLoop_) loopPoint_ = buf_.size(); lastWait_ = 0; } } BambooTracker-0.4.6/BambooTracker/chip/register_write_logger.hpp000066400000000000000000000044531401124043500250000ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include namespace chip { class AbstractRegisterWriteLogger { public: explicit AbstractRegisterWriteLogger(int target); virtual ~AbstractRegisterWriteLogger() = default; virtual void recordRegisterChange(uint32_t offset, uint8_t value) = 0; void elapse(size_t count) noexcept; bool empty() const noexcept; void clear() noexcept; std::vector getData(); size_t getSampleLength() const noexcept; size_t setLoopPoint(); size_t forceMoveLoopPoint() noexcept; protected: int target_; std::vector buf_; uint64_t lastWait_; bool isSetLoop_; uint32_t loopPoint_; virtual void setWait() = 0; private: uint64_t totalSampCnt_; }; class VgmLogger final : public AbstractRegisterWriteLogger { public: VgmLogger(int target, uint32_t intrRate); void recordRegisterChange(uint32_t offset, uint8_t value) override; void setDataBlock(std::vector data); private: uint32_t intrRate_; void setWait() override; }; class S98Logger final : public AbstractRegisterWriteLogger { public: explicit S98Logger(int target); void recordRegisterChange(uint32_t offset, uint8_t value) override; private: void setWait() override; }; } BambooTracker-0.4.6/BambooTracker/chip/resampler.cpp000066400000000000000000000046251401124043500223710ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "resampler.hpp" namespace chip { AbstractResampler::AbstractResampler() { for (int pan = STEREO_LEFT; pan <= STEREO_RIGHT; ++pan) { destBuf_[pan] = new sample[CHIP_SMPL_BUF_SIZE_](); } } AbstractResampler::~AbstractResampler() { for (int pan = STEREO_LEFT; pan <= STEREO_RIGHT; ++pan) { delete[] destBuf_[pan]; } } void AbstractResampler::init(int srcRate, int destRate, size_t maxDuration) { srcRate_ = srcRate; maxDuration_ = maxDuration; destRate_ = destRate; updateRateRatio(); } void AbstractResampler::setDestributionRate(int destRate) { destRate_ = destRate; updateRateRatio(); } void AbstractResampler::setMaxDuration(size_t maxDuration) noexcept { maxDuration_ = maxDuration; } /****************************************/ sample** LinearResampler::interpolate(sample** src, size_t nSamples, size_t intrSize) { (void)intrSize; // Linear interplation for (int pan = STEREO_LEFT; pan <= STEREO_RIGHT; ++pan) { for (size_t n = 0; n < nSamples; ++n) { float curnf = n * rateRatio_; int curni = static_cast(curnf); float sub = curnf - curni; if (sub) { destBuf_[pan][n] = static_cast(src[pan][curni] + (src[pan][curni + 1] - src[pan][curni]) * sub); } else /* if (sub == 0) */ { destBuf_[pan][n] = src[pan][curni]; } } } return destBuf_; } } BambooTracker-0.4.6/BambooTracker/chip/resampler.hpp000066400000000000000000000037651401124043500224020ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "chip_def.h" #include #include namespace chip { class AbstractResampler { public: AbstractResampler(); virtual ~AbstractResampler(); virtual void init(int srcRate, int destRate, size_t maxDuration); virtual void setDestributionRate(int destRate); virtual void setMaxDuration(size_t maxDuration) noexcept; virtual sample** interpolate(sample** src, size_t nSamples, size_t intrSize) = 0; inline size_t calculateInternalSampleSize(size_t nSamples) { return static_cast(std::ceil(nSamples * rateRatio_)); } protected: int srcRate_, destRate_; size_t maxDuration_; float rateRatio_; sample* destBuf_[2]; inline void updateRateRatio() { rateRatio_ = static_cast(srcRate_) / destRate_; } }; class LinearResampler : public AbstractResampler { public: sample** interpolate(sample** src, size_t nSamples, size_t intrSize) override; }; } BambooTracker-0.4.6/BambooTracker/chip/scci/000077500000000000000000000000001401124043500206055ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/chip/scci/SCCIDefines.hpp000066400000000000000000000100401401124043500233300ustar00rootroot00000000000000//---------------------------------------------------------------------- // SCCI Sound Interfaces defines //---------------------------------------------------------------------- #pragma once namespace scci { // Sound chip list enum SC_CHIP_TYPE { SC_TYPE_NONE = 0, SC_TYPE_YM2608, SC_TYPE_YM2151, SC_TYPE_YM2610, SC_TYPE_YM2203, SC_TYPE_YM2612, SC_TYPE_AY8910, SC_TYPE_SN76489, SC_TYPE_YM3812, SC_TYPE_YMF262, SC_TYPE_YM2413, SC_TYPE_YM3526, SC_TYPE_YMF288, SC_TYPE_SCC, SC_TYPE_SCCS, SC_TYPE_Y8950, SC_TYPE_YM2164, // OPP:OPMとはハードウェアLFOの制御が違う SC_TYPE_YM2414, // OPZ:OPMとピンコンパチ SC_TYPE_AY8930, // APSG:拡張PSG SC_TYPE_YM2149, // SSG:PSGとはDACが違う(YM3439とは同一とみていいと思う) SC_TYPE_YMZ294, // SSGL:SSGとはDACが違う(YMZ284とは同一とみていいと思う) SC_TYPE_SN76496, // DCSG:76489とはノイズジェネレータの生成式が違う SC_TYPE_YM2420, // OPLL2:OPLLとはFnumの設定方法が違う。音は同じ。 SC_TYPE_YMF281, // OPLLP:OPLLとは内蔵ROM音色が違う。制御は同じ。 SC_TYPE_YMF276, // OPN2L:OPN2/OPN2CとはDACが違う SC_TYPE_YM2610B, // OPNB-B:OPNBとはFM部のch数が違う。 SC_TYPE_YMF286, // OPNB-C:OPNBとはDACが違う。 SC_TYPE_YM2602, // 315-5124: 76489/76496とはノイズジェネレータの生成式が違う。POWON時に発振しない。 SC_TYPE_UM3567, // OPLLのコピー品(だけどDIP24なのでそのままリプレースできない) SC_TYPE_YMF274, // OPL4:試作未定 SC_TYPE_YM3806, // OPQ:試作予定 SC_TYPE_YM2163, // DSG:試作中 SC_TYPE_YM7129, // OPK2:試作中 SC_TYPE_YMZ280, // PCM8:ADPCM8ch:試作予定 SC_TYPE_YMZ705, // SSGS:SSG*2set+ADPCM8ch:試作中 SC_TYPE_YMZ735, // FMS:FM8ch+ADPCM8ch:試作中 SC_TYPE_YM2423, // YM2413の音色違い SC_TYPE_SPC700, // SPC700 SC_TYPE_NBV4, // NBV4用 SC_TYPE_AYB02, // AYB02用 SC_TYPE_8253, // i8253(及び互換チップ用) SC_TYPE_315_5124, // DCSG互換チップ SC_TYPE_SPPCM, // SPPCM SC_TYPE_C140, // NAMCO C140(SPPCMデバイス) SC_TYPE_SEGAPCM, // SEGAPCM(SPPCMデバイス) SC_TYPE_SPW, // SPW SC_TYPE_SAM2695, // SAM2695 SC_TYPE_MIDI, // MIDIインターフェース SC_TYPE_MAX, // 使用可能デバイスMAX値 // 以降は、専用ハード用 // 実験ハード用 SC_TYPE_OTHER = 1000, // その他デバイス用、アドレスがA0-A3で動作する SC_TYPE_UNKNOWN, // 開発デバイス向け SC_TYPE_YMF825, // YMF825(暫定) }; // Sound chip clock list enum SC_CHIP_CLOCK { SC_CLOCK_NONE = 0, SC_CLOCK_1789773 = 1789773, // SSG,OPN,OPM,SN76489 etc SC_CLOCK_1996800 = 1996800, // SSG,OPN,OPM,SN76489 etc SC_CLOCK_2000000 = 2000000, // SSG,OPN,OPM,SN76489 etc SC_CLOCK_2048000 = 2048000, // SSGLP(4096/2|6144/3) SC_CLOCK_3579545 = 3579545, // SSG,OPN,OPM,SN76489 etc SC_CLOCK_3993600 = 3993600, // OPN(88) SC_CLOCK_4000000 = 4000000, // SSF,OPN,OPM etc SC_CLOCK_7159090 = 7159090, // OPN,OPNA,OPNB,OPN2,OPN3L etc SC_CLOCK_7670454 = 7670454, // YM-2612 etc SC_CLOCK_7987200 = 7987200, // OPNA(88) SC_CLOCK_8000000 = 8000000, // OPNB etc SC_CLOCK_10738635 = 10738635, // 315-5124 SC_CLOCK_12500000 = 12500000, // RF5C164 SC_CLOCK_14318180 = 14318180, // OPL2 SC_CLOCK_16934400 = 16934400, // YMF271 SC_CLOCK_23011361 = 23011361, // PWM }; // Sound chip location enum SC_CHIP_LOCATION { SC_LOCATION_MONO = 0, SC_LOCATION_LEFT = 1, SC_LOCATION_RIGHT = 2, SC_LOCATION_STEREO = 3 }; // mode defines #define SC_MODE_ASYNC (0x00000000) #define SC_MODE_SYNC (0x00000001) // sound chip Acquisition mode defines #define SC_ACQUISITION_MODE_NEAR (0x00000000) #define SC_ACQUISITION_MODE_MATCH (0x00000001) #define SC_WAIT_REG (0xffffffff) // ウェイとコマンド送信(データは送信するコマンド数) #define SC_FLUSH_REG (0xfffffffe) // 書き込みデータフラッシュ待ち #define SC_DIRECT_BUS (0x80000000) // アドレスバスダイレクトモード } BambooTracker-0.4.6/BambooTracker/chip/scci/scci.hpp000066400000000000000000000131731401124043500222440ustar00rootroot00000000000000//---------------------------------------------------------------------- // Sound Chip common Interface //---------------------------------------------------------------------- #pragma once /*#include */ #include #ifndef _WIN32 #define __stdcall #endif namespace scci { using DWORD = uint32_t; using BOOL = bool; using BYTE = uint8_t; // Sound Interface Infomation typedef struct { char cInterfaceName[64]; // Interface Name int iSoundChipCount; // Sound Chip Count; } SCCI_INTERFACE_INFO; // Sound Chip Infomation typedef struct { char cSoundChipName[64]; // Sound Chip Name int iSoundChip; // Sound Chip ID int iCompatibleSoundChip[2]; // Compatible Sound Chip ID DWORD dClock; // Sound Chip clock DWORD dCompatibleClock[2]; // Sound Chip clock BOOL bIsUsed; // Sound Chip Used Check DWORD dBusID; // 接続バスID DWORD dSoundLocation; // サウンドロケーション } SCCI_SOUND_CHIP_INFO; class SoundInterfaceManager; class SoundInterface; class SoundChip; //---------------------------------------- // Sound Interface Manager //---------------------------------------- class SoundInterfaceManager{ public: // ---------- LOW LEVEL APIs ---------- // get interface count virtual int __stdcall getInterfaceCount() = 0; // get interface information virtual SCCI_INTERFACE_INFO* __stdcall getInterfaceInfo(int iInterfaceNo) = 0; // get interface instance virtual SoundInterface* __stdcall getInterface(int iInterfaceNo) = 0; // release interface instance virtual BOOL __stdcall releaseInterface(SoundInterface* pSoundInterface) = 0; // release all interface instance virtual BOOL __stdcall releaseAllInterface() = 0; // ---------- HI LEVEL APIs ---------- // get sound chip instance virtual SoundChip* __stdcall getSoundChip(int iSoundChipType,DWORD dClock) = 0; // release sound chip instance virtual BOOL __stdcall releaseSoundChip(SoundChip* pSoundChip) = 0; // release all sound chip instance virtual BOOL __stdcall releaseAllSoundChip() = 0; // set delay time virtual BOOL __stdcall setDelay(DWORD dMSec) = 0; // get delay time virtual DWORD __stdcall getDelay() = 0; // reset interfaces(A sound chips initialize after interface reset) virtual BOOL __stdcall reset() = 0; // initialize sound chips virtual BOOL __stdcall init() = 0; // Sound Interface instance initialize virtual BOOL __stdcall initializeInstance() = 0; // Sound Interface instance release virtual BOOL __stdcall releaseInstance() = 0; // config scci // !!!this function is scciconfig exclusive use!!! virtual BOOL __stdcall config() = 0; // get version info /*virtual DWORD __stdcall getVersion(DWORD *pMVersion = NULL) = 0;*/ virtual DWORD __stdcall getVersion(DWORD *pMVersion = nullptr) = 0; // get Level mater disp valid virtual BOOL __stdcall isValidLevelDisp() = 0; // get Level mater disp visible virtual BOOL __stdcall isLevelDisp() = 0; // set Level mater disp visible virtual void __stdcall setLevelDisp(BOOL bDisp) = 0; // set mode virtual void __stdcall setMode(int iMode) = 0; // send datas virtual void __stdcall sendData() = 0; // clear buffer virtual void __stdcall clearBuff() = 0; // set Acquisition Mode(Sound Chip) virtual void __stdcall setAcquisitionMode(int iMode) = 0; // set Acquisition Mode clock renge virtual void __stdcall setAcquisitionModeClockRenge(DWORD dClock) = 0; // set command buffer size virtual BOOL __stdcall setCommandBuffetSize(DWORD dBuffSize) = 0; // buffer check virtual BOOL __stdcall isBufferEmpty() = 0; }; //---------------------------------------- // Sound Interface(LOW level APIs) //---------------------------------------- class SoundInterface{ public: // support low level API check virtual BOOL __stdcall isSupportLowLevelApi() = 0; // send data to interface virtual BOOL __stdcall setData(BYTE *pData,DWORD dSendDataLen) = 0; // get data from interface virtual DWORD __stdcall getData(BYTE *pData,DWORD dGetDataLen) = 0; // set delay time virtual BOOL __stdcall setDelay(DWORD dDelay) = 0; // get delay time virtual DWORD __stdcall getDelay() = 0; // reset interface virtual BOOL __stdcall reset() = 0; // initialize sound chips virtual BOOL __stdcall init() = 0; // サウンドチップ数取得 virtual DWORD __stdcall getSoundChipCount() = 0; // サウンドチップ取得 virtual SoundChip* __stdcall getSoundChip(DWORD dNum) = 0; }; //---------------------------------------- // Sound Chip //---------------------------------------- class SoundChip{ public: // get sound chip information virtual SCCI_SOUND_CHIP_INFO* __stdcall getSoundChipInfo() = 0; // get sound chip type virtual int __stdcall getSoundChipType() = 0; // set Register data virtual BOOL __stdcall setRegister(DWORD dAddr,DWORD dData) = 0; // get Register data(It may not be supported) virtual DWORD __stdcall getRegister(DWORD dAddr) = 0; // initialize sound chip(clear registers) virtual BOOL __stdcall init() = 0; // get sound chip clock virtual DWORD __stdcall getSoundChipClock() = 0; // get writed register data virtual DWORD __stdcall getWrittenRegisterData(DWORD addr) = 0; // buffer check virtual BOOL __stdcall isBufferEmpty() = 0; }; //---------------------------------------- // get sound interface manager function //---------------------------------------- typedef SoundInterfaceManager* (__stdcall *SCCIFUNC)(void); //---------------------------------------- // pcm callback function // void callback(SCCIPCMDATA *pPcm,DWORD dSize) //---------------------------------------- typedef struct { int iL; int iR; } SCCIPCMDATA; } BambooTracker-0.4.6/BambooTracker/command/000077500000000000000000000000001401124043500203575ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/command/abstract_command.hpp000066400000000000000000000027561401124043500244030ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "command_id.hpp" class AbstractCommand { public: AbstractCommand(CommandId id) : id_(id) {} virtual ~AbstractCommand() = default; virtual void redo() = 0; virtual void undo() = 0; inline CommandId getID() const noexcept { return id_; }; virtual bool mergeWith(const AbstractCommand* other) { (void)other; return false; } private: const CommandId id_; }; BambooTracker-0.4.6/BambooTracker/command/command_id.hpp000066400000000000000000000046431401124043500231710ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once enum CommandId : int { // 0x1*: Instrument list AddInstrument = 0x10, RemoveInstrument = 0x11, ChangeInstrumentName = 0x12, CloneInstrument = 0x13, DeepCloneInstrument = 0x14, SwapInstruments = 0x15, // 0x2*, 0x3*: Pattern editor SetKeyOnToStep = 0x20, SetKeyOffToStep = 0x21, EraseStep = 0x22, SetInstrumentInStep = 0x23, EraseInstrumentInStep = 0x24, SetVolumeToStep = 0x25, EraseVolumeInStep = 0x26, SetEffectIDToStep = 0x27, EraseEffectInStep = 0x28, SetEffectValueToStep = 0x29, EraseEffectValueInStep = 0x2a, InsertStep = 0x2b, DeletePreviousStep = 0x2c, PasteCopiedDataToPattern = 0x2d, EraseCellsInPattern = 0x2e, PasteMixCopiedDataToPattern = 0x2f, TransposeNoteInPattern = 0x30, ChangeValuesInPattern = 0x31, ExpandPattern = 0x34, ShrinkPattern = 0x35, SetEchoBufferAccess = 0x36, InterpolatePattern = 0x37, ReversePattern = 0x38, ReplaceInstrumentInPattern = 0x39, PasteOverwriteCopiedDataToPattern = 0x3a, PasteInsertCopiedDataToPattern = 0x3b, // 0x4*: Order list SetPatternToOrder = 0x40, InsertOrderBelow = 0x41, DeleteOrder = 0x42, PasteCopiedDataToOrder = 0x43, DuplicateOrder = 0x44, MoveOrder = 0x45, ClonePatterns = 0x46, CloneOrder = 0x47 }; BambooTracker-0.4.6/BambooTracker/command/command_manager.cpp000066400000000000000000000035641401124043500242030ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "command_manager.hpp" #include void CommandManager::invoke(CommandIPtr command) { command->redo(); redoStack_ = std::stack(); if (undoStack_.empty() || !undoStack_.top()->mergeWith(command.get())) { undoStack_.push(std::move(command)); } } void CommandManager::undo() { if (undoStack_.empty()) return; CommandIPtr command = std::move(undoStack_.top()); command->undo(); undoStack_.pop(); redoStack_.push(std::move(command)); } void CommandManager::redo() { if (redoStack_.empty()) return; CommandIPtr command = std::move(redoStack_.top()); command->redo(); redoStack_.pop(); undoStack_.push(std::move(command)); } void CommandManager::clear() { redoStack_ = std::stack(); undoStack_ = std::stack(); } BambooTracker-0.4.6/BambooTracker/command/command_manager.hpp000066400000000000000000000026331401124043500242040ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "abstract_command.hpp" class CommandManager { public: using CommandIPtr = std::unique_ptr; void invoke(CommandIPtr command); void undo(); void redo(); void clear(); private: std::stack undoStack_, redoStack_; }; BambooTracker-0.4.6/BambooTracker/command/commands.hpp000066400000000000000000000065321401124043500226770ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once /********** Instrument edit **********/ #include "./instrument/add_instrument_command.hpp" #include "./instrument/remove_instrument_command.hpp" #include "./instrument/change_instrument_name_command.hpp" #include "./instrument/clone_instrument_command.hpp" #include "./instrument/deep_clone_instrument_command.hpp" #include "./instrument/swap_instruments_command.hpp" /********** Pattern edit **********/ #include "./pattern/set_key_on_to_step_command.hpp" #include "./pattern/set_key_off_to_step_command.hpp" #include "./pattern/erase_step_command.hpp" #include "./pattern/set_instrument_to_step_command.hpp" #include "./pattern/erase_instrument_in_step_command.hpp" #include "./pattern/set_volume_to_step_command.hpp" #include "./pattern/erase_volume_in_step_command.hpp" #include "./pattern/set_effect_id_to_step_command.hpp" #include "./pattern/erase_effect_in_step_command.hpp" #include "./pattern/set_effect_value_to_step_command.hpp" #include "./pattern/erase_effect_value_in_step_command.hpp" #include "./pattern/insert_step_command.hpp" #include "./pattern/delete_previous_step_command.hpp" #include "./pattern/paste_copied_data_to_pattern_command.hpp" #include "./pattern/erase_cells_in_pattern_command.hpp" #include "./pattern/paste_mix_copied_data_to_pattern_command.hpp" #include "./pattern/transpose_note_in_pattern_command.hpp" #include "./pattern/expand_pattern_command.hpp" #include "./pattern/shrink_pattern_command.hpp" #include "./pattern/set_echo_buffer_access_command.hpp" #include "./pattern/interpolate_pattern_command.hpp" #include "./pattern/reverse_pattern_command.hpp" #include "./pattern/replace_instrument_in_pattern_command.hpp" #include "./pattern/paste_overwrite_copied_data_to_pattern_command.hpp" #include "./pattern/change_values_in_pattern_command.hpp" #include "./pattern/paste_insert_copied_data_to_pattern_command.hpp" /********** Order edit **********/ #include "./order/set_pattern_to_order_command.hpp" #include "./order/insert_order_below_command.hpp" #include "./order/delete_order_command.hpp" #include "./order/paste_copied_data_to_order_command.hpp" #include "./order/duplicate_order_command.hpp" #include "./order/move_order_command.hpp" #include "./order/clone_patterns_command.hpp" #include "./order/clone_order_command.hpp" BambooTracker-0.4.6/BambooTracker/command/instrument/000077500000000000000000000000001401124043500225675ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/command/instrument/add_instrument_command.cpp000066400000000000000000000036511401124043500300160ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "add_instrument_command.hpp" #include AddInstrumentCommand::AddInstrumentCommand(std::weak_ptr manager, int num, InstrumentType type, const std::string& name) : AbstractCommand(CommandId::AddInstrument), manager_(manager), num_(num), type_(type), name_(name) { } AddInstrumentCommand::AddInstrumentCommand(std::weak_ptr manager, std::unique_ptr inst) : AbstractCommand(CommandId::AddInstrument), manager_(manager), num_(inst->getNumber()), inst_(std::move(inst)) { } void AddInstrumentCommand::redo() { if (inst_) manager_.lock()->addInstrument(inst_->clone()); else manager_.lock()->addInstrument(num_, type_, name_); } void AddInstrumentCommand::undo() { manager_.lock()->removeInstrument(num_); } BambooTracker-0.4.6/BambooTracker/command/instrument/add_instrument_command.hpp000066400000000000000000000033721401124043500300230ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "instrument.hpp" #include "instruments_manager.hpp" class AddInstrumentCommand final : public AbstractCommand { public: AddInstrumentCommand(std::weak_ptr manager, int num, InstrumentType type, const std::string& name); AddInstrumentCommand(std::weak_ptr manager, std::unique_ptr inst); void redo() override; void undo() override; private: std::weak_ptr manager_; int num_; InstrumentType type_; std::string name_; std::unique_ptr inst_; }; BambooTracker-0.4.6/BambooTracker/command/instrument/change_instrument_name_command.cpp000066400000000000000000000032451401124043500315120ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "change_instrument_name_command.hpp" ChangeInstrumentNameCommand::ChangeInstrumentNameCommand(std::weak_ptr manager, int num, const std::string& name) : AbstractCommand(CommandId::ChangeInstrumentName), manager_(manager), instNum_(num), newName_(name) { oldName_ = manager_.lock()->getInstrumentName(instNum_); } void ChangeInstrumentNameCommand::redo() { manager_.lock()->setInstrumentName(instNum_, newName_); } void ChangeInstrumentNameCommand::undo() { manager_.lock()->setInstrumentName(instNum_, oldName_); } BambooTracker-0.4.6/BambooTracker/command/instrument/change_instrument_name_command.hpp000066400000000000000000000030651401124043500315170ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "instruments_manager.hpp" class ChangeInstrumentNameCommand final : public AbstractCommand { public: ChangeInstrumentNameCommand(std::weak_ptr manager, int num, const std::string& name); void redo() override; void undo() override; private: std::weak_ptr manager_; int instNum_; std::string oldName_, newName_; }; BambooTracker-0.4.6/BambooTracker/command/instrument/clone_instrument_command.cpp000066400000000000000000000031101401124043500303540ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "clone_instrument_command.hpp" cloneInstrumentCommand::cloneInstrumentCommand(std::weak_ptr manager, int num, int refNum) : AbstractCommand(CommandId::CloneInstrument), manager_(manager), cloneInstNum_(num), refInstNum_(refNum) { } void cloneInstrumentCommand::redo() { manager_.lock()->cloneInstrument(cloneInstNum_, refInstNum_); } void cloneInstrumentCommand::undo() { manager_.lock()->removeInstrument(cloneInstNum_); } BambooTracker-0.4.6/BambooTracker/command/instrument/clone_instrument_command.hpp000066400000000000000000000027651401124043500304000ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "instruments_manager.hpp" class cloneInstrumentCommand final : public AbstractCommand { public: cloneInstrumentCommand(std::weak_ptr manager, int num, int refNum); void redo() override; void undo() override; private: std::weak_ptr manager_; int cloneInstNum_, refInstNum_; }; BambooTracker-0.4.6/BambooTracker/command/instrument/deep_clone_instrument_command.cpp000066400000000000000000000031471401124043500313630ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "deep_clone_instrument_command.hpp" DeepCloneInstrumentCommand::DeepCloneInstrumentCommand(std::weak_ptr manager, int num, int refNum) : AbstractCommand(CommandId::DeepCloneInstrument), manager_(manager), cloneInstNum_(num), refInstNum_(refNum) { } void DeepCloneInstrumentCommand::redo() { manager_.lock()->deepCloneInstrument(cloneInstNum_, refInstNum_); } void DeepCloneInstrumentCommand::undo() { manager_.lock()->removeInstrument(cloneInstNum_); } BambooTracker-0.4.6/BambooTracker/command/instrument/deep_clone_instrument_command.hpp000066400000000000000000000027751401124043500313760ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "instruments_manager.hpp" class DeepCloneInstrumentCommand final : public AbstractCommand { public: DeepCloneInstrumentCommand(std::weak_ptr manager, int num, int refNum); void redo() override; void undo() override; private: std::weak_ptr manager_; int cloneInstNum_, refInstNum_; }; BambooTracker-0.4.6/BambooTracker/command/instrument/remove_instrument_command.cpp000066400000000000000000000030531401124043500305570ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "remove_instrument_command.hpp" #include RemoveInstrumentCommand::RemoveInstrumentCommand(std::weak_ptr manager, int number) : AbstractCommand(CommandId::RemoveInstrument), manager_(manager), number_(number) { } void RemoveInstrumentCommand::redo() { inst_ = manager_.lock()->removeInstrument(number_); } void RemoveInstrumentCommand::undo() { manager_.lock()->addInstrument(inst_.release()); } BambooTracker-0.4.6/BambooTracker/command/instrument/remove_instrument_command.hpp000066400000000000000000000030411401124043500305610ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "instruments_manager.hpp" #include "instrument.hpp" class RemoveInstrumentCommand final : public AbstractCommand { public: RemoveInstrumentCommand(std::weak_ptr manager, int number); void redo() override; void undo() override; private: std::weak_ptr manager_; int number_; std::unique_ptr inst_; }; BambooTracker-0.4.6/BambooTracker/command/instrument/swap_instruments_command.cpp000066400000000000000000000047521401124043500304260ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "swap_instruments_command.hpp" SwapInstrumentsCommand::SwapInstrumentsCommand(std::weak_ptr manager, std::weak_ptr mod, int inst1, int inst2, int song, bool patternChange) : AbstractCommand(CommandId::SwapInstruments), manager_(manager), mod_(mod), inst1Num_(inst1), inst2Num_(inst2), songNum_(song), patternChange_(patternChange) { } void SwapInstrumentsCommand::redo() { manager_.lock()->swapInstruments(inst1Num_, inst2Num_); if (patternChange_) swapInstrumentsInPatterns(); } void SwapInstrumentsCommand::undo() { manager_.lock()->swapInstruments(inst1Num_, inst2Num_); if (patternChange_) swapInstrumentsInPatterns(); } void SwapInstrumentsCommand::swapInstrumentsInPatterns() { // NOTE: Is it better to execute this as the method of Song to use global replace action? // Too slow... Song& song = mod_.lock()->getSong(songNum_); for (const auto& attrib : song.getStyle().trackAttribs) { Track& track = song.getTrack(attrib.number); for (int i = 0; i < 256; ++i) { // Used track size Pattern& pat = track.getPattern(i); for (size_t j = 0; j < pat.getSize(); ++j) { Step& step = pat.getStep(static_cast(j)); if (step.getInstrumentNumber() == inst1Num_) step.setInstrumentNumber(inst2Num_); else if (step.getInstrumentNumber() == inst2Num_) step.setInstrumentNumber(inst1Num_); } } } } BambooTracker-0.4.6/BambooTracker/command/instrument/swap_instruments_command.hpp000066400000000000000000000032511401124043500304240ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "instruments_manager.hpp" #include "module.hpp" class SwapInstrumentsCommand final : public AbstractCommand { public: SwapInstrumentsCommand(std::weak_ptr manager, std::weak_ptr mod, int inst1, int inst2, int song, bool patternChange); void redo() override; void undo() override; private: std::weak_ptr manager_; std::weak_ptr mod_; int inst1Num_, inst2Num_; int songNum_; bool patternChange_; void swapInstrumentsInPatterns(); }; BambooTracker-0.4.6/BambooTracker/command/order/000077500000000000000000000000001401124043500214725ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/command/order/clone_order_command.cpp000066400000000000000000000040171401124043500261710ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "clone_order_command.hpp" CloneOrderCommand::CloneOrderCommand(std::weak_ptr mod, int songNum, int orderNum) : AbstractCommand(CommandId::CloneOrder), mod_(mod), song_(songNum), order_(orderNum) { } void CloneOrderCommand::redo() { auto& sng = mod_.lock()->getSong(song_); sng.insertOrderBelow(order_); for (auto& t : sng.getTrackAttributes()) { auto& track = sng.getTrack(t.number); // Set previous pattern to avoid leaving unused pattern track.registerPatternToOrder(order_ + 1, track.getPatternFromOrderNumber(order_).getNumber()); track.registerPatternToOrder(order_ + 1, track.clonePattern(track.getOrderInfo(order_).patten)); } } void CloneOrderCommand::undo() { auto& sng = mod_.lock()->getSong(song_); for (auto& t : sng.getTrackAttributes()) { auto& p = sng.getTrack(t.number).getPatternFromOrderNumber(order_ + 1); if (p.getUsedCount() == 1) p.clear(); } sng.deleteOrder(order_ + 1); } BambooTracker-0.4.6/BambooTracker/command/order/clone_order_command.hpp000066400000000000000000000027531401124043500262030ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class CloneOrderCommand final : public AbstractCommand { public: CloneOrderCommand(std::weak_ptr mod, int songNum, int orderNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, order_; std::vector prevOdr_; }; BambooTracker-0.4.6/BambooTracker/command/order/clone_patterns_command.cpp000066400000000000000000000047001401124043500267150ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "clone_patterns_command.hpp" ClonePatternsCommand::ClonePatternsCommand(std::weak_ptr mod, int songNum, int beginOrder, int beginTrack, int endOrder, int endTrack) : AbstractCommand(CommandId::ClonePatterns), mod_(mod), song_(songNum), bOrder_(beginOrder), bTrack_(beginTrack), eOrder_(endOrder), eTrack_(endTrack) { for (int o = beginOrder; o <= endOrder; ++o) { prevOdrs_.emplace_back(); for (int t = beginTrack; t <= endTrack; ++t) { prevOdrs_.at(static_cast(o - beginOrder)).push_back( mod_.lock()->getSong(songNum).getTrack(t).getOrderInfo(o)); } } } void ClonePatternsCommand::redo() { auto& sng = mod_.lock()->getSong(song_); for (int o = bOrder_; o <= eOrder_; ++o) { for (int t = bTrack_; t <= eTrack_; ++t) { auto& track = sng.getTrack(t); track.registerPatternToOrder(o, track.clonePattern(track.getOrderInfo(o).patten)); } } } void ClonePatternsCommand::undo() { auto& sng = mod_.lock()->getSong(song_); for (int o = bOrder_; o <= eOrder_; ++o) { for (int t = bTrack_; t <= eTrack_; ++t) { auto& track = sng.getTrack(t); auto& p = track.getPatternFromOrderNumber(o); if (p.getUsedCount() == 1) p.clear(); track.registerPatternToOrder( o, prevOdrs_.at(static_cast(o - bOrder_)).at(static_cast(t - bTrack_)).patten); } } } BambooTracker-0.4.6/BambooTracker/command/order/clone_patterns_command.hpp000066400000000000000000000031201401124043500267150ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class ClonePatternsCommand final : public AbstractCommand { public: ClonePatternsCommand(std::weak_ptr mod, int songNum, int beginOrder, int beginTrack, int endOrder, int endTrack); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, bOrder_, bTrack_, eOrder_, eTrack_; std::vector> prevOdrs_; }; BambooTracker-0.4.6/BambooTracker/command/order/delete_order_command.cpp000066400000000000000000000033071401124043500263340ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "delete_order_command.hpp" DeleteOrderCommand::DeleteOrderCommand(std::weak_ptr mod, int songNum, int orderNum) : AbstractCommand(CommandId::DeleteOrder), mod_(mod), song_(songNum), order_(orderNum) { prevOdr_ = mod_.lock()->getSong(songNum).getOrderData(orderNum); } void DeleteOrderCommand::redo() { mod_.lock()->getSong(song_).deleteOrder(order_); } void DeleteOrderCommand::undo() { auto& sng = mod_.lock()->getSong(song_); sng.insertOrderBelow(order_ - 1); for (const auto& t : prevOdr_) { sng.getTrack(t.trackAttribute.number).registerPatternToOrder(t.order, t.patten); } } BambooTracker-0.4.6/BambooTracker/command/order/delete_order_command.hpp000066400000000000000000000027551401124043500263470ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class DeleteOrderCommand final : public AbstractCommand { public: DeleteOrderCommand(std::weak_ptr mod, int songNum, int orderNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, order_; std::vector prevOdr_; }; BambooTracker-0.4.6/BambooTracker/command/order/duplicate_order_command.cpp000066400000000000000000000033051401124043500270420ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "duplicate_order_command.hpp" DuplicateOrderCommand::DuplicateOrderCommand(std::weak_ptr mod, int songNum, int orderNum) : AbstractCommand(CommandId::DuplicateOrder), mod_(mod), song_(songNum), order_(orderNum) { } void DuplicateOrderCommand::redo() { auto& sng = mod_.lock()->getSong(song_); sng.insertOrderBelow(order_); for (auto& t : sng.getTrackAttributes()) { auto& track = sng.getTrack(t.number); track.registerPatternToOrder(order_ + 1, track.getOrderInfo(order_).patten); } } void DuplicateOrderCommand::undo() { mod_.lock()->getSong(song_).deleteOrder(order_ + 1); } BambooTracker-0.4.6/BambooTracker/command/order/duplicate_order_command.hpp000066400000000000000000000026711401124043500270540ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class DuplicateOrderCommand : public AbstractCommand { public: DuplicateOrderCommand(std::weak_ptr mod, int songNum, int orderNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, order_; }; BambooTracker-0.4.6/BambooTracker/command/order/insert_order_below_command.cpp000066400000000000000000000030321401124043500275610ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "insert_order_below_command.hpp" InsertOrderBelowCommand::InsertOrderBelowCommand(std::weak_ptr mod, int songNum, int orderNum) : AbstractCommand(CommandId::InsertOrderBelow), mod_(mod), song_(songNum), order_(orderNum) { } void InsertOrderBelowCommand::redo() { mod_.lock()->getSong(song_).insertOrderBelow(order_); } void InsertOrderBelowCommand::undo() { mod_.lock()->getSong(song_).deleteOrder(order_ + 1); } BambooTracker-0.4.6/BambooTracker/command/order/insert_order_below_command.hpp000066400000000000000000000027031401124043500275720ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class InsertOrderBelowCommand final : public AbstractCommand { public: InsertOrderBelowCommand(std::weak_ptr mod, int songNum, int orderNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, order_; }; BambooTracker-0.4.6/BambooTracker/command/order/move_order_command.cpp000066400000000000000000000031211401124043500260320ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "move_order_command.hpp" MoveOrderCommand::MoveOrderCommand(std::weak_ptr mod, int songNum, int orderNum, bool isUp) : AbstractCommand(CommandId::MoveOrder), mod_(mod), song_(songNum), order_(orderNum), isUp_(isUp) { } void MoveOrderCommand::redo() { swap(); } void MoveOrderCommand::undo() { swap(); } void MoveOrderCommand::swap() { auto& sng = mod_.lock()->getSong(song_); if (isUp_) sng.swapOrder(order_ - 1, order_); else sng.swapOrder(order_, order_ + 1); } BambooTracker-0.4.6/BambooTracker/command/order/move_order_command.hpp000066400000000000000000000027341401124043500260500ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class MoveOrderCommand final : public AbstractCommand { public: MoveOrderCommand(std::weak_ptr mod, int songNum, int orderNum, bool isUp); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, order_; bool isUp_; void swap(); }; BambooTracker-0.4.6/BambooTracker/command/order/paste_copied_data_to_order_command.cpp000066400000000000000000000045721401124043500312310ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "paste_copied_data_to_order_command.hpp" #include "track.hpp" PasteCopiedDataToOrderCommand::PasteCopiedDataToOrderCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, const std::vector>& cells) : AbstractCommand(CommandId::PasteCopiedDataToOrder), mod_(mod), song_(songNum), track_(beginTrack), order_(beginOrder), cells_(cells) { auto& sng = mod.lock()->getSong(songNum); for (size_t i = 0; i < cells.size(); ++i) { prevCells_.emplace_back(); std::vector odrs = sng.getOrderData(beginOrder + static_cast(i)); for (size_t j = 0; j < cells.at(i).size(); ++j) { prevCells_.at(i).push_back(std::to_string(odrs.at(static_cast(beginTrack) + j).patten)); } } } void PasteCopiedDataToOrderCommand::redo() { setCells(cells_); } void PasteCopiedDataToOrderCommand::undo() { setCells(prevCells_); } void PasteCopiedDataToOrderCommand::setCells(const std::vector>& cells) { auto& sng = mod_.lock()->getSong(song_); for (size_t i = 0; i < cells.size(); ++i) { for (size_t j = 0; j < cells.at(i).size(); ++j) { sng.getTrack(track_ + static_cast(j)) .registerPatternToOrder(order_ + static_cast(i), std::stoi(cells.at(i).at(j))); } } } BambooTracker-0.4.6/BambooTracker/command/order/paste_copied_data_to_order_command.hpp000066400000000000000000000033101401124043500312230ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class PasteCopiedDataToOrderCommand final : public AbstractCommand { public: PasteCopiedDataToOrderCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, const std::vector>& cells); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_; std::vector> cells_, prevCells_; void setCells(const std::vector>& cells); }; BambooTracker-0.4.6/BambooTracker/command/order/set_pattern_to_order_command.cpp000066400000000000000000000045641401124043500301320ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_pattern_to_order_command.hpp" SetPatternToOrderCommand::SetPatternToOrderCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int patternNum, bool secondEntry) : AbstractCommand(CommandId::SetPatternToOrder), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), pattern_(patternNum), prevPattern_(mod_.lock()->getSong(songNum).getTrack(trackNum) .getPatternFromOrderNumber(orderNum).getNumber()), isSecondEntry_(secondEntry) { } void SetPatternToOrderCommand::redo() { mod_.lock()->getSong(song_).getTrack(track_).registerPatternToOrder(order_, pattern_); } void SetPatternToOrderCommand::undo() { mod_.lock()->getSong(song_).getTrack(track_).registerPatternToOrder(order_, prevPattern_); isSecondEntry_ = true; // Forced complete } bool SetPatternToOrderCommand::mergeWith(const AbstractCommand* other) { if (other->getID() == getID() && !isSecondEntry_) { auto com = dynamic_cast(other); if (com->song_ == song_ && com->track_ == track_ && com->order_ == order_ && com->isSecondEntry_) { pattern_ = (pattern_ << 4) + com->pattern_; redo(); isSecondEntry_ = true; return true; } } isSecondEntry_ = true; return false; } BambooTracker-0.4.6/BambooTracker/command/order/set_pattern_to_order_command.hpp000066400000000000000000000032011401124043500301220ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class SetPatternToOrderCommand final : public AbstractCommand { public: SetPatternToOrderCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int patternNum, bool secondEntry); void redo() override; void undo() override; bool mergeWith(const AbstractCommand* other) override; private: std::weak_ptr mod_; const int song_, track_, order_; int pattern_; const int prevPattern_; bool isSecondEntry_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/000077500000000000000000000000001401124043500220345ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/command/pattern/change_values_in_pattern_command.cpp000066400000000000000000000105301401124043500312640ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "change_values_in_pattern_command.hpp" #include "pattern_command_utils.hpp" #include "bamboo_tracker_defs.hpp" #include "utils.hpp" ChangeValuesInPatternCommand::ChangeValuesInPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep, int value, bool isFMReversed) : AbstractCommand(CommandId::ChangeValuesInPattern), mod_(mod), song_(songNum), bTrack_(beginTrack), bCol_(beginColumn), order_(beginOrder), bStep_(beginStep), eTrack_(endTrack), eCol_(endColumn), eStep_(endStep), diff_(value), fmReverse_(isFMReversed) { auto& sng = mod.lock()->getSong(songNum); for (int step = beginStep; step <= endStep; ++step) { int track = beginTrack; int col = beginColumn; std::vector vals; while (true) { Step& st = command_utils::getStep(sng, track, beginOrder, step); switch (col) { case 1: if (st.hasInstrument()) vals.push_back(st.getInstrumentNumber()); break; case 2: if (st.hasVolume()) vals.push_back(st.getVolume()); break; default: { if (col) { int ec = col - 3; int ei = ec / 2; if (ec % 2 && st.hasEffectValue(ei)) vals.push_back(st.getEffectValue(ei)); } break; } } if (track == endTrack && col == endColumn) break; track += (++col / Step::N_COLUMN); col %= Step::N_COLUMN; } prevVals_.push_back(vals); } } void ChangeValuesInPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); auto it = prevVals_.begin(); for (int step = bStep_; step <= eStep_; ++step, ++it) { int track = bTrack_; int col = bCol_; auto valit = it->begin(); while (true) { Track& tr = sng.getTrack(track); Step& st = tr.getPatternFromOrderNumber(order_).getStep(step); switch (col) { case 1: if (st.hasInstrument()) st.setInstrumentNumber(utils::clamp(diff_ + *valit++, 0, 127)); break; case 2: if (st.hasVolume()) { int d = (tr.getAttribute().source == SoundSource::FM && fmReverse_) ? -diff_ : diff_; st.setVolume(utils::clamp(d + *valit++, 0, 255)); } break; default: { if (col) { int ec = col - 3; int ei = ec / 2; if (ec % 2 && st.hasEffectValue(ei)) st.setEffectValue(ei, utils::clamp(diff_ + *valit++, 0, 255)); } break; } } if (track == eTrack_ && col == eCol_) break; track += (++col / Step::N_COLUMN); col %= Step::N_COLUMN; } } } void ChangeValuesInPatternCommand::undo() { auto& sng = mod_.lock()->getSong(song_); auto it = prevVals_.begin(); for (int step = bStep_; step <= eStep_; ++step, ++it) { int track = bTrack_; int col = bCol_; auto valit = it->begin(); while (true) { Step& st = command_utils::getStep(sng, track, order_, step); switch (col) { case 1: if (st.hasInstrument()) st.setInstrumentNumber(*valit++); break; case 2: if (st.hasVolume()) st.setVolume(*valit++); break; default: { if (col) { int ec = col - 3; int ei = ec / 2; if (ec % 2 && st.hasEffectValue(ei)) st.setEffectValue(ei, *valit++); } break; } } if (track == eTrack_ && col == eCol_) break; track += (++col / Step::N_COLUMN); col %= Step::N_COLUMN; } } } BambooTracker-0.4.6/BambooTracker/command/pattern/change_values_in_pattern_command.hpp000066400000000000000000000033501401124043500312730ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class ChangeValuesInPatternCommand final : public AbstractCommand { public: ChangeValuesInPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep, int value, bool isFMReversed); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_; int bTrack_, bCol_, order_, bStep_; int eTrack_, eCol_, eStep_; int diff_; bool fmReverse_; std::vector> prevVals_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/delete_previous_step_command.cpp000066400000000000000000000043131401124043500304700ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "delete_previous_step_command.hpp" #include "pattern_command_utils.hpp" DeletePreviousStepCommand::DeletePreviousStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::DeletePreviousStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum) { Step& st = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum - 1); prevNote_ = st.getNoteNumber(); prevInst_ = st.getInstrumentNumber(); prevVol_ = st.getVolume(); for (int i = 0; i < Step::N_EFFECT; ++i) { prevEff_[i] = st.getEffect(i); } } void DeletePreviousStepCommand::redo() { command_utils::getPattern(mod_, song_, track_, order_).deletePreviousStep(step_); } void DeletePreviousStepCommand::undo() { auto& pt = command_utils::getPattern(mod_, song_, track_, order_); pt.insertStep(step_ - 1); // Insert previous step auto& st = pt.getStep(step_ - 1); st.setNoteNumber(prevNote_); st.setInstrumentNumber(prevInst_); st.setVolume(prevVol_); for (int i = 0; i < Step::N_EFFECT; ++i) { st.setEffect(i, prevEff_[i]); } } BambooTracker-0.4.6/BambooTracker/command/pattern/delete_previous_step_command.hpp000066400000000000000000000031361401124043500304770ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class DeletePreviousStepCommand final : public AbstractCommand { public: DeletePreviousStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_; int prevNote_, prevInst_, prevVol_; Step::PlainEffect prevEff_[Step::N_EFFECT]; }; BambooTracker-0.4.6/BambooTracker/command/pattern/erase_cells_in_pattern_command.cpp000066400000000000000000000052411401124043500307440ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "erase_cells_in_pattern_command.hpp" #include "pattern_command_utils.hpp" EraseCellsInPatternCommand::EraseCellsInPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::EraseCellsInPattern), mod_(mod), song_(songNum), bTrack_(beginTrack), bCol_(beginColumn), order_(beginOrder), bStep_(beginStep) { auto& song = mod.lock()->getSong(songNum); size_t h = static_cast(endStep - beginStep + 1); size_t w = command_utils::calculateColumnSize(beginTrack, beginColumn, endTrack, endColumn); prevCells_ = command_utils::getPreviousCells(song, w, h, beginTrack, beginColumn, beginOrder, beginStep); } void EraseCellsInPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); int s = bStep_; for (size_t i = 0; i < prevCells_.size(); ++i) { int t = bTrack_; int c = bCol_; for (size_t j = 0; j < prevCells_.at(i).size(); ++j) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { case 0: st.clearNoteNumber(); break; case 1: st.clearInstrumentNumber(); break; case 2: st.clearVolume(); break; default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) st.clearEffectValue(ei); else st.clearEffectId(ei); break; } } t += (++c / Step::N_COLUMN); c %= Step::N_COLUMN; } ++s; } } void EraseCellsInPatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, bTrack_, bCol_, order_, bStep_); } BambooTracker-0.4.6/BambooTracker/command/pattern/erase_cells_in_pattern_command.hpp000066400000000000000000000032501401124043500307470ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class EraseCellsInPatternCommand final : public AbstractCommand { public: EraseCellsInPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, bTrack_, bCol_, order_, bStep_; std::vector> prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/erase_effect_in_step_command.cpp000066400000000000000000000034671401124043500304040ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "erase_effect_in_step_command.hpp" #include "pattern_command_utils.hpp" EraseEffectInStepCommand::EraseEffectInStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n) : AbstractCommand(CommandId::EraseEffectInStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), n_(n) { prevEff_ = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getEffect(n); } void EraseEffectInStepCommand::redo() { command_utils::getStep(mod_, song_, track_, order_, step_).clearEffect(n_); } void EraseEffectInStepCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setEffect(n_, prevEff_); } BambooTracker-0.4.6/BambooTracker/command/pattern/erase_effect_in_step_command.hpp000066400000000000000000000030611401124043500303770ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class EraseEffectInStepCommand final : public AbstractCommand { public: EraseEffectInStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_, n_; Step::PlainEffect prevEff_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/erase_effect_value_in_step_command.cpp000066400000000000000000000035451401124043500315750ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "erase_effect_value_in_step_command.hpp" #include "pattern_command_utils.hpp" EraseEffectValueInStepCommand::EraseEffectValueInStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n) : AbstractCommand(CommandId::EraseEffectValueInStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), n_(n) { prevVal_ = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getEffectValue(n); } void EraseEffectValueInStepCommand::redo() { command_utils::getStep(mod_, song_, track_, order_, step_).clearEffectValue(n_); } void EraseEffectValueInStepCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setEffectValue(n_, prevVal_); } BambooTracker-0.4.6/BambooTracker/command/pattern/erase_effect_value_in_step_command.hpp000066400000000000000000000030351401124043500315740ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class EraseEffectValueInStepCommand final : public AbstractCommand { public: EraseEffectValueInStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_, n_; int prevVal_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/erase_instrument_in_step_command.cpp000066400000000000000000000035341401124043500313530ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "erase_instrument_in_step_command.hpp" #include "pattern_command_utils.hpp" EraseInstrumentInStepCommand::EraseInstrumentInStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::EraseInstrumentInStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum) { prevInst_ = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum) .getInstrumentNumber(); } void EraseInstrumentInStepCommand::redo() { command_utils::getStep(mod_, song_, track_, order_, step_).clearInstrumentNumber(); } void EraseInstrumentInStepCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setInstrumentNumber(prevInst_); } BambooTracker-0.4.6/BambooTracker/command/pattern/erase_instrument_in_step_command.hpp000066400000000000000000000030201401124043500313460ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class EraseInstrumentInStepCommand final : public AbstractCommand { public: EraseInstrumentInStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_; int prevInst_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/erase_step_command.cpp000066400000000000000000000040601401124043500263700ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "erase_step_command.hpp" #include "pattern_command_utils.hpp" EraseStepCommand::EraseStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::EraseStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum) { Step& st = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum); prevNote_ = st.getNoteNumber(); prevInst_ = st.getInstrumentNumber(); prevVol_ = st.getVolume(); for (int i = 0; i < Step::N_EFFECT; ++i) { prevEff_[i] = st.getEffect(i); } } void EraseStepCommand::redo() { command_utils::getStep(mod_, song_, track_, order_, step_).clear(); } void EraseStepCommand::undo() { Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); st.setNoteNumber(prevNote_); st.setInstrumentNumber(prevInst_); st.setVolume(prevVol_); for (int i = 0; i < Step::N_EFFECT; ++i) { st.setEffect(i, prevEff_[i]); } } BambooTracker-0.4.6/BambooTracker/command/pattern/erase_step_command.hpp000066400000000000000000000031031401124043500263720ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class EraseStepCommand final : public AbstractCommand { public: EraseStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_; int prevNote_, prevInst_, prevVol_; Step::PlainEffect prevEff_[Step::N_EFFECT]; }; BambooTracker-0.4.6/BambooTracker/command/pattern/erase_volume_in_step_command.cpp000066400000000000000000000034371401124043500304540ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "erase_volume_in_step_command.hpp" #include "pattern_command_utils.hpp" EraseVolumeInStepCommand::EraseVolumeInStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::EraseVolumeInStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum) { prevVol_ = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getVolume(); } void EraseVolumeInStepCommand::redo() { command_utils::getStep(mod_, song_, track_, order_, step_).clearVolume(); } void EraseVolumeInStepCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setVolume(prevVol_); } BambooTracker-0.4.6/BambooTracker/command/pattern/erase_volume_in_step_command.hpp000066400000000000000000000030061401124043500304510ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class EraseVolumeInStepCommand final : public AbstractCommand { public: EraseVolumeInStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_; int prevVol_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/expand_pattern_command.cpp000066400000000000000000000061061401124043500272550ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "expand_pattern_command.hpp" #include "pattern_command_utils.hpp" ExpandPatternCommand::ExpandPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::ExpandPattern), mod_(mod), song_(songNum), bTrack_(beginTrack), bCol_(beginColumn), order_(beginOrder), bStep_(beginStep) { auto& song = mod.lock()->getSong(songNum); size_t h = static_cast(endStep - beginStep + 1); size_t w = command_utils::calculateColumnSize(beginTrack, beginColumn, endTrack, endColumn); prevCells_ = command_utils::getPreviousCells(song, w, h, beginTrack, beginColumn, beginOrder, beginStep); } void ExpandPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); int s = bStep_; for (size_t i = 0; i < prevCells_.size(); ++i) { int t = bTrack_; int c = bCol_; for (size_t j = 0; j < prevCells_.at(i).size(); ++j) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { case 0: { if (i % 2) st.clearNoteNumber(); else st.setNoteNumber(std::stoi(prevCells_.at(i / 2).at(j))); break; } case 1: { if (i % 2) st.clearInstrumentNumber(); else st.setInstrumentNumber(std::stoi(prevCells_.at(i / 2).at(j))); break; } case 2: { if (i % 2) st.clearVolume(); else st.setVolume(std::stoi(prevCells_.at(i / 2).at(j))); break; } default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) { if (i % 2) st.clearEffectValue(ei); else st.setEffectValue(ei, std::stoi(prevCells_.at(i / 2).at(j))); } else { if (i % 2) st.clearEffectId(ei); else st.setEffectId(ei, prevCells_.at(i / 2).at(j)); } break; } } t += (++c / Step::N_COLUMN); c %= Step::N_COLUMN; } ++s; } } void ExpandPatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, bTrack_, bCol_, order_, bStep_); } BambooTracker-0.4.6/BambooTracker/command/pattern/expand_pattern_command.hpp000066400000000000000000000032261401124043500272620ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class ExpandPatternCommand final : public AbstractCommand { public: ExpandPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, bTrack_, bCol_, order_, bStep_; std::vector> prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/insert_step_command.cpp000066400000000000000000000032361401124043500266010ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "insert_step_command.hpp" #include "pattern_command_utils.hpp" InsertStepCommand::InsertStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::InsertStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum) { } void InsertStepCommand::redo() { command_utils::getPattern(mod_, song_, track_, order_).insertStep(step_); } void InsertStepCommand::undo() { command_utils::getPattern(mod_, song_, track_, order_).deletePreviousStep(step_ + 1); } BambooTracker-0.4.6/BambooTracker/command/pattern/insert_step_command.hpp000066400000000000000000000027411401124043500266060ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class InsertStepCommand final : public AbstractCommand { public: InsertStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/interpolate_pattern_command.cpp000066400000000000000000000072061401124043500303260ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "interpolate_pattern_command.hpp" #include "pattern_command_utils.hpp" namespace { inline int interp(int a, int b, size_t t, int div) { return a + (b - a) * static_cast(t) / div; } } InterpolatePatternCommand::InterpolatePatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::InterpolatePattern), mod_(mod), song_(songNum), bTrack_(beginTrack), bCol_(beginColumn), order_(beginOrder), bStep_(beginStep), eStep_(endStep) { auto& song = mod.lock()->getSong(songNum); size_t h = static_cast(endStep - beginStep + 1); size_t w = command_utils::calculateColumnSize(beginTrack, beginColumn, endTrack, endColumn); prevCells_ = command_utils::getPreviousCells(song, w, h, beginTrack, beginColumn, beginOrder, beginStep); } void InterpolatePatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); int div = static_cast(prevCells_.size()) - 1; if (!div) div = 1; int t = bTrack_; int c = bCol_; for (size_t i = 0; i < prevCells_.front().size(); ++i) { int s = bStep_; for (size_t j = 0; j < prevCells_.size(); ++j) { Pattern& pattern = command_utils::getPattern(sng, t, order_); Step& sa = pattern.getStep(bStep_); Step& sb = pattern.getStep(eStep_); switch (c) { case 0: { if (sa.hasGeneralNote() && sb.hasGeneralNote()) pattern.getStep(s).setNoteNumber( interp(sa.getNoteNumber(), sb.getNoteNumber(), j, div)); break; } case 1: { if (sa.hasInstrument() && sb.hasInstrument()) pattern.getStep(s).setInstrumentNumber(interp(sa.getInstrumentNumber(), sb.getInstrumentNumber(), j, div)); break; } case 2: { if (sa.hasVolume() && sb.hasVolume()) pattern.getStep(s).setVolume(interp(sa.getVolume(), sb.getVolume(), j, div)); break; } default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) { // Value if (sa.hasEffectValue(ei) && sb.hasEffectValue(ei)) pattern.getStep(s).setEffectValue(ei, interp(sa.getEffectValue(ei), sb.getEffectValue(ei), j, div)); } else { // ID std::string a = sa.getEffectId(ei); std::string b = sb.getEffectId(ei); if (a == b) pattern.getStep(s).setEffectId(ei, a); } break; } } ++s; } ++c; t += (c / Step::N_COLUMN); c %= Step::N_COLUMN; } } void InterpolatePatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, bTrack_, bCol_, order_, bStep_); } BambooTracker-0.4.6/BambooTracker/command/pattern/interpolate_pattern_command.hpp000066400000000000000000000032611401124043500303300ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class InterpolatePatternCommand final : public AbstractCommand { public: InterpolatePatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, bTrack_, bCol_, order_, bStep_; int eStep_; std::vector> prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.cpp000066400000000000000000000041251401124043500321270ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "paste_copied_data_to_pattern_command.hpp" #include "pattern_command_utils.hpp" PasteCopiedDataToPatternCommand::PasteCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteCopiedDataToPattern), mod_(mod), song_(songNum), track_(beginTrack), col_(beginColmn), order_(beginOrder), step_(beginStep), cells_(cells) { auto& song = mod.lock()->getSong(songNum); prevCells_ = command_utils::getPreviousCells(song, cells.front().size(), cells.size(), beginTrack, beginColmn, beginOrder, beginStep); } void PasteCopiedDataToPatternCommand::redo() { command_utils::restorePattern(mod_.lock()->getSong(song_), cells_, track_, col_, order_, step_); } void PasteCopiedDataToPatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, track_, col_, order_, step_); } BambooTracker-0.4.6/BambooTracker/command/pattern/paste_copied_data_to_pattern_command.hpp000066400000000000000000000032631401124043500321360ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class PasteCopiedDataToPatternCommand final : public AbstractCommand { public: PasteCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColmn, int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, col_, order_, step_; std::vector> cells_, prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.cpp000066400000000000000000000045611401124043500335170ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "paste_insert_copied_data_to_pattern_command.hpp" #include #include "pattern_command_utils.hpp" PasteInsertCopiedDataToPatternCommand::PasteInsertCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteInsertCopiedDataToPattern), mod_(mod), song_(songNum), track_(beginTrack), col_(beginColumn), order_(beginOrder), step_(beginStep), cells_(cells) { auto& song = mod.lock()->getSong(songNum); size_t newStepSize = song.getTrack(track_).getPatternFromOrderNumber(order_).getSize() - step_; prevCells_ = command_utils::getPreviousCells(song, cells.front().size(), newStepSize, beginTrack, beginColumn, beginOrder, beginStep); if (cells.size() < newStepSize) { std::copy(prevCells_.begin(), prevCells_.end() - cells.size(), std::back_inserter(cells_)); } } void PasteInsertCopiedDataToPatternCommand::redo() { command_utils::restorePattern(mod_.lock()->getSong(song_), cells_, track_, col_, order_, step_); } void PasteInsertCopiedDataToPatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, track_, col_, order_, step_); } BambooTracker-0.4.6/BambooTracker/command/pattern/paste_insert_copied_data_to_pattern_command.hpp000066400000000000000000000032731401124043500335230ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class PasteInsertCopiedDataToPatternCommand final : public AbstractCommand { public: PasteInsertCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, col_, order_, step_; std::vector> cells_, prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.cpp000066400000000000000000000061461401124043500330110ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "paste_mix_copied_data_to_pattern_command.hpp" #include "pattern_command_utils.hpp" PasteMixCopiedDataToPatternCommand::PasteMixCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteMixCopiedDataToPattern), mod_(mod), song_(songNum), track_(beginTrack), col_(beginColumn), order_(beginOrder), step_(beginStep), cells_(cells) { auto& song = mod.lock()->getSong(songNum); prevCells_ = command_utils::getPreviousCells(song, cells.front().size(), cells.size(), beginTrack, beginColumn, beginOrder, beginStep); } void PasteMixCopiedDataToPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); int s = step_; for (const auto& row : cells_) { int t = track_; int c = col_; for (const std::string& cell : row) { Step& step = command_utils::getStep(sng, t, order_, s); switch (c) { case 0: { int n = std::stoi(cell); if (!Step::testEmptyNote(n) && step.isEmptyNote()) step.setNoteNumber(n); break; } case 1: { int n = std::stoi(cell); if (!Step::testEmptyInstrument(n) && !step.hasInstrument()) step.setInstrumentNumber(n); break; } case 2: { int vol = std::stoi(cell); if (!Step::testEmptyVolume(vol) && !step.hasVolume()) step.setVolume(vol); break; } default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) { // Value int val = std::stoi(cell); if (!Step::testEmptyEffectValue(val) && !step.hasEffectValue(ei)) step.setEffectValue(ei, val); } else { // ID if (!Step::testEmptyEffectId(cell) && !step.hasEffectId(ei)) step.setEffectId(ei, cell); } break; } } ++c; t += (c / Step::N_COLUMN); c %= Step::N_COLUMN; } ++s; } } void PasteMixCopiedDataToPatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, track_, col_, order_, step_); } BambooTracker-0.4.6/BambooTracker/command/pattern/paste_mix_copied_data_to_pattern_command.hpp000066400000000000000000000032721401124043500330130ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class PasteMixCopiedDataToPatternCommand final : public AbstractCommand { public: PasteMixCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, col_, order_, step_; std::vector> cells_, prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.cpp000066400000000000000000000057601401124043500342430ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "paste_overwrite_copied_data_to_pattern_command.hpp" #include "pattern_command_utils.hpp" PasteOverwriteCopiedDataToPatternCommand::PasteOverwriteCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, const std::vector>& cells) : AbstractCommand(CommandId::PasteOverwriteCopiedDataToPattern), mod_(mod), song_(songNum), track_(beginTrack), col_(beginColumn), order_(beginOrder), step_(beginStep), cells_(cells) { auto& song = mod.lock()->getSong(songNum); prevCells_ = command_utils::getPreviousCells(song, cells.front().size(), cells.size(), beginTrack, beginColumn, beginOrder, beginStep); } void PasteOverwriteCopiedDataToPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); int s = step_; for (const auto& row : cells_) { int t = track_; int c = col_; for (const std::string& cell : row) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { case 0: { int n = std::stoi(cell); if (!Step::testEmptyNote(n)) st.setNoteNumber(n); break; } case 1: { int n = std::stoi(cell); if (!Step::testEmptyInstrument(n)) st.setInstrumentNumber(n); break; } case 2: { int vol = std::stoi(cell); if (!Step::testEmptyVolume(vol)) st.setVolume(vol); break; } default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) { int val = std::stoi(cell); if (!Step::testEmptyEffectValue(val)) st.setEffectValue(ei, val); } else { if (!Step::testEmptyEffectId(cell)) st.setEffectId(ei, cell); } break; } } t += (++c / Step::N_COLUMN); c %= Step::N_COLUMN; } ++s; } } void PasteOverwriteCopiedDataToPatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, track_, col_, order_, step_); } BambooTracker-0.4.6/BambooTracker/command/pattern/paste_overwrite_copied_data_to_pattern_command.hpp000066400000000000000000000033061401124043500342420ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class PasteOverwriteCopiedDataToPatternCommand final : public AbstractCommand { public: PasteOverwriteCopiedDataToPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, const std::vector>& cells); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, col_, order_, step_; std::vector> cells_, prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/pattern_command_utils.cpp000066400000000000000000000064771401124043500271510ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pattern_command_utils.hpp" namespace command_utils { size_t calculateColumnSize(int beginTrack, int beginColumn, int endTrack, int endColumn) { constexpr int WCOL = Step::N_COLUMN - 1; int w = 0; int tr = endTrack; int cl = endColumn; while (true) { if (tr == beginTrack) { w += (cl - beginColumn + 1); break; } else { w += (cl + 1); cl = WCOL; --tr; } } return static_cast(w); } std::vector> getPreviousCells(Song& song, size_t w, size_t h, int beginTrack, int beginColumn, int beginOrder, int beginStep) { std::vector> cells(h); int s = beginStep; for (size_t i = 0; i < h; ++i) { int t = beginTrack; int c = beginColumn; cells[i].resize(w); for (size_t j = 0; j < w; ++j) { Step& st = song.getTrack(t).getPatternFromOrderNumber(beginOrder).getStep(s); std::string val; switch (c) { case 0: val = std::to_string(st.getNoteNumber()); break; case 1: val = std::to_string(st.getInstrumentNumber()); break; case 2: val = std::to_string(st.getVolume()); break; default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) { // Value val = std::to_string(st.getEffectValue(ei)); } else { // ID val = st.getEffectId(ei); } break; } } cells[i][j] = val; t += (++c / Step::N_COLUMN); c %= 11; } ++s; } return cells; } void restorePattern(Song& song, const std::vector>& cells, int beginTrack, int beginColumn, int beginOrder, int beginStep) { int s = beginStep; for (const auto& row : cells) { int t = beginTrack; int c = beginColumn; for (const std::string& cell : row) { Step& st = song.getTrack(t).getPatternFromOrderNumber(beginOrder).getStep(s); switch (c) { case 0: st.setNoteNumber(std::stoi(cell)); break; case 1: st.setInstrumentNumber(std::stoi(cell)); break; case 2: st.setVolume(std::stoi(cell)); break; default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) { // Value st.setEffectValue(ei, std::stoi(cell)); } else { // ID st.setEffectId(ei, cell); } break; } } t += (++c / Step::N_COLUMN); c %= Step::N_COLUMN; } ++s; } } } BambooTracker-0.4.6/BambooTracker/command/pattern/pattern_command_utils.hpp000066400000000000000000000043761401124043500271520ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "module.hpp" namespace command_utils { inline Step& getStep(std::weak_ptr mod, int song, int track, int order, int step) { return mod.lock()->getSong(song).getTrack(track) .getPatternFromOrderNumber(order).getStep(step); } inline Step& getStep(Song& song, int track, int order, int step) { return song.getTrack(track).getPatternFromOrderNumber(order).getStep(step); } inline Pattern& getPattern(std::weak_ptr mod, int song, int track, int order) { return mod.lock()->getSong(song).getTrack(track).getPatternFromOrderNumber(order); } inline Pattern& getPattern(Song& song, int track, int order) { return song.getTrack(track).getPatternFromOrderNumber(order); } size_t calculateColumnSize(int beginTrack, int beginColumn, int endTrack, int endColumn); std::vector> getPreviousCells(Song& song, size_t w, size_t h, int beginTrack, int beginColumn, int beginOrder, int beginStep); void restorePattern(Song& song, const std::vector>& cells, int beginTrack, int beginColumn, int beginOrder, int beginStep); } BambooTracker-0.4.6/BambooTracker/command/pattern/replace_instrument_in_pattern_command.cpp000066400000000000000000000051231401124043500323650ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "replace_instrument_in_pattern_command.hpp" #include "pattern_command_utils.hpp" ReplaceInstrumentInPatternCommand::ReplaceInstrumentInPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int newInst) : AbstractCommand(CommandId::ReplaceInstrumentInPattern), mod_(mod), song_(songNum), bTrack_(beginTrack), order_(beginOrder), bStep_(beginStep), eTrack_(endTrack), eStep_(endStep), inst_(newInst) { auto& sng = mod.lock()->getSong(songNum); for (int step = beginStep; step <= endStep; ++step) { for (int track = beginTrack; track <= endTrack; ++track) { Step& st = command_utils::getStep(sng, track, beginOrder, step); if (st.hasInstrument()) prevInsts_.push_back(st.getInstrumentNumber()); } } } void ReplaceInstrumentInPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { Step& st = command_utils::getStep(sng, track, order_, step); if (st.hasInstrument()) st.setInstrumentNumber(inst_); } } } void ReplaceInstrumentInPatternCommand::undo() { auto& sng = mod_.lock()->getSong(song_); size_t i = 0; for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { Step& st = command_utils::getStep(sng, track, order_, step); if (st.hasInstrument()) st.setInstrumentNumber(prevInsts_.at(i)); } } } BambooTracker-0.4.6/BambooTracker/command/pattern/replace_instrument_in_pattern_command.hpp000066400000000000000000000032361401124043500323750ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class ReplaceInstrumentInPatternCommand final : public AbstractCommand { public: ReplaceInstrumentInPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int newInst); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_; int bTrack_, order_, bStep_; int eTrack_, eStep_; int inst_; std::vector prevInsts_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/reverse_pattern_command.cpp000066400000000000000000000055141401124043500274530ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "reverse_pattern_command.hpp" #include "pattern_command_utils.hpp" ReversePatternCommand::ReversePatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::ReversePattern), mod_(mod), song_(songNum), bTrack_(beginTrack), bCol_(beginColumn), order_(beginOrder), bStep_(beginStep) { auto& song = mod.lock()->getSong(songNum); size_t h = static_cast(endStep - beginStep + 1); size_t w = command_utils::calculateColumnSize(beginTrack, beginColumn, endTrack, endColumn); prevCells_ = command_utils::getPreviousCells(song, w, h, beginTrack, beginColumn, beginOrder, beginStep); } void ReversePatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); size_t l = prevCells_.size() - 1; int s = bStep_; for (size_t i = 0; i < prevCells_.size(); ++i) { int t = bTrack_; int c = bCol_; for (size_t j = 0; j < prevCells_.at(i).size(); ++j) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { case 0: st.setNoteNumber(std::stoi(prevCells_.at(l - i).at(j))); break; case 1: st.setInstrumentNumber(std::stoi(prevCells_.at(l - i).at(j))); break; case 2: st.setVolume(std::stoi(prevCells_.at(l - i).at(j))); break; default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) st.setEffectValue(ei, std::stoi(prevCells_.at(l - i).at(j))); else st.setEffectId(ei, prevCells_.at(l - i).at(j)); break; } } t += (++c / Step::N_COLUMN); c %= Step::N_COLUMN; } ++s; } } void ReversePatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, bTrack_, bCol_, order_, bStep_); } BambooTracker-0.4.6/BambooTracker/command/pattern/reverse_pattern_command.hpp000066400000000000000000000032321401124043500274530ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class ReversePatternCommand final : public AbstractCommand { public: ReversePatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, bTrack_, bCol_, order_, bStep_; std::vector> prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/set_echo_buffer_access_command.cpp000066400000000000000000000035301401124043500307020ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_echo_buffer_access_command.hpp" #include "pattern_command_utils.hpp" SetEchoBufferAccessCommand::SetEchoBufferAccessCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int bufNum) : AbstractCommand(CommandId::SetEchoBufferAccess), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), buf_(bufNum) { prevNote_ = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getNoteNumber(); } void SetEchoBufferAccessCommand::redo() { command_utils::getStep(mod_, song_, track_, order_, step_).setEchoBuffer(buf_); } void SetEchoBufferAccessCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setNoteNumber(prevNote_); } BambooTracker-0.4.6/BambooTracker/command/pattern/set_echo_buffer_access_command.hpp000066400000000000000000000030611401124043500307060ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class SetEchoBufferAccessCommand final : public AbstractCommand { public: SetEchoBufferAccessCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int bufNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_, buf_; int prevNote_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/set_effect_id_to_step_command.cpp000066400000000000000000000055211401124043500305610ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_effect_id_to_step_command.hpp" #include "pattern_command_utils.hpp" SetEffectIDToStepCommand::SetEffectIDToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n, std::string id, bool fillValue00, bool secondEntry) : AbstractCommand(CommandId::SetEffectIDToStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), n_(n), effID_(id), isSecondEntry_(secondEntry) { Step& step = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum); prevEffID_ = step.getEffectId(n); filledValue00_ = fillValue00 && !step.hasEffectValue(n); } void SetEffectIDToStepCommand::redo() { std::string str = isSecondEntry_ ? effID_ : ("0" + effID_); Step& step = command_utils::getStep(mod_, song_, track_, order_, step_); step.setEffectId(n_, str); if (filledValue00_) step.setEffectValue(n_, 0); } void SetEffectIDToStepCommand::undo() { Step& step = command_utils::getStep(mod_, song_, track_, order_, step_); step.setEffectId(n_, prevEffID_); if (filledValue00_) step.clearEffectValue(n_); if (!isSecondEntry_) { // Forced complete effID_ = "0" + effID_; isSecondEntry_ = true; } } bool SetEffectIDToStepCommand::mergeWith(const AbstractCommand* other) { if (other->getID() == getID() && !isSecondEntry_) { auto com = dynamic_cast(other); if (com->song_ == song_ && com->track_ == track_ && com->order_ == order_ && com->step_ == step_ && com->n_ == n_ && com->isSecondEntry_) { effID_ = effID_ + com->effID_; isSecondEntry_ = true; redo(); return true; } } // Enterd only 1 character if (!isSecondEntry_) { effID_ = "0" + effID_; isSecondEntry_ = true; } return false; } BambooTracker-0.4.6/BambooTracker/command/pattern/set_effect_id_to_step_command.hpp000066400000000000000000000033221401124043500305630ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class SetEffectIDToStepCommand final : public AbstractCommand { public: SetEffectIDToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n, std::string id, bool fillValue00, bool secondEntry); void redo() override; void undo() override; bool mergeWith(const AbstractCommand* other) override; private: std::weak_ptr mod_; const int song_, track_, order_, step_, n_; std::string effID_, prevEffID_; bool filledValue00_; bool isSecondEntry_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/set_effect_value_to_step_command.cpp000066400000000000000000000054761401124043500313120ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_effect_value_to_step_command.hpp" #include "pattern_command_utils.hpp" #include "effect.hpp" SetEffectValueToStepCommand::SetEffectValueToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n, int value, EffectDisplayControl ctrl, bool secondEntry) : AbstractCommand(CommandId::SetEffectValueToStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), n_(n), val_(value), ctrl_(ctrl), isSecondEntry_(secondEntry) { prevVal_ = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getEffectValue(n); } void SetEffectValueToStepCommand::redo() { int value; switch (ctrl_) { default: case EffectDisplayControl::Unset: value = val_; break; case EffectDisplayControl::ReverseFMVolumeDelay: value = effect_utils::reverseFmVolume(val_); break; case EffectDisplayControl::ReverseFMBrightness: value = effect_utils::reverseFmBrightness(val_); break; } command_utils::getStep(mod_, song_, track_, order_, step_).setEffectValue(n_, value); } void SetEffectValueToStepCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setEffectValue(n_, prevVal_); isSecondEntry_ = true; // Forced complete } bool SetEffectValueToStepCommand::mergeWith(const AbstractCommand* other) { if (other->getID() == getID() && !isSecondEntry_) { auto com = dynamic_cast(other); if (com->song_ == song_ && com->track_ == track_ && com->order_ == order_ && com->step_ == step_ && com->n_ == n_ && com->isSecondEntry_) { val_ = (val_ << 4) + com->val_; redo(); isSecondEntry_ = true; return true; } } isSecondEntry_ = true; return false; } BambooTracker-0.4.6/BambooTracker/command/pattern/set_effect_value_to_step_command.hpp000066400000000000000000000034541401124043500313110ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" enum class EffectDisplayControl { Unset, ReverseFMVolumeDelay, ReverseFMBrightness }; class SetEffectValueToStepCommand final : public AbstractCommand { public: SetEffectValueToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int n, int value, EffectDisplayControl ctrl, bool secondEntry); void redo() override; void undo() override; bool mergeWith(const AbstractCommand* other) override; private: std::weak_ptr mod_; const int song_, track_, order_, step_, n_; int val_, prevVal_; const EffectDisplayControl ctrl_; bool isSecondEntry_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/set_instrument_to_step_command.cpp000066400000000000000000000046451401124043500310670ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_instrument_to_step_command.hpp" #include "pattern_command_utils.hpp" SetInstrumentToStepCommand::SetInstrumentToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int instNum, bool secondEntry) : AbstractCommand(CommandId::SetInstrumentInStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), inst_(instNum), prevInst_(command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getInstrumentNumber()), isSecondEntry_(secondEntry) { } void SetInstrumentToStepCommand::redo() { command_utils::getStep(mod_, song_, track_, order_, step_).setInstrumentNumber(inst_); } void SetInstrumentToStepCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setInstrumentNumber(prevInst_); isSecondEntry_ = true; // Forced complete } bool SetInstrumentToStepCommand::mergeWith(const AbstractCommand* other) { if (other->getID() == getID() && !isSecondEntry_) { auto com = dynamic_cast(other); if (com->song_ == song_ && com->track_ == track_ && com->order_ == order_ && com->step_ == step_ && com->isSecondEntry_) { inst_ = (inst_ << 4) + com->inst_; redo(); isSecondEntry_ = true; return true; } } isSecondEntry_ = true; return false; } BambooTracker-0.4.6/BambooTracker/command/pattern/set_instrument_to_step_command.hpp000066400000000000000000000032221401124043500310620ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class SetInstrumentToStepCommand final : public AbstractCommand { public: SetInstrumentToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int instNum, bool secondEntry); void redo() override; void undo() override; bool mergeWith(const AbstractCommand* other) override; private: std::weak_ptr mod_; const int song_, track_, order_, step_; int inst_; const int prevInst_; bool isSecondEntry_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/set_key_off_to_step_command.cpp000066400000000000000000000043371401124043500302770ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_key_off_to_step_command.hpp" #include "pattern_command_utils.hpp" SetKeyOffToStepCommand::SetKeyOffToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum) : AbstractCommand(CommandId::SetKeyOffToStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum) { Step& st = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum); prevNote_ = st.getNoteNumber(); prevInst_ = st.getInstrumentNumber(); prevVol_ = st.getVolume(); for (int i = 0; i < Step::N_EFFECT; ++i) { prevEff_[i] = st.getEffect(i); } } void SetKeyOffToStepCommand::redo() { Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); st.setKeyOff(); st.clearInstrumentNumber(); st.clearVolume(); for (int i = 0; i < Step::N_EFFECT; ++i) { st.clearEffect(i); } } void SetKeyOffToStepCommand::undo() { Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); st.setNoteNumber(prevNote_); st.setInstrumentNumber(prevInst_); st.setVolume(prevVol_); for (int i = 0; i < Step::N_EFFECT; ++i) { st.setEffect(i, prevEff_[i]); } } BambooTracker-0.4.6/BambooTracker/command/pattern/set_key_off_to_step_command.hpp000066400000000000000000000031301401124043500302720ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class SetKeyOffToStepCommand final : public AbstractCommand { public: SetKeyOffToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_; int prevNote_, prevInst_, prevVol_; Step::PlainEffect prevEff_[Step::N_EFFECT]; }; BambooTracker-0.4.6/BambooTracker/command/pattern/set_key_on_to_step_command.cpp000066400000000000000000000046021401124043500301340ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_key_on_to_step_command.hpp" #include "pattern_command_utils.hpp" #include "effect.hpp" SetKeyOnToStepCommand::SetKeyOnToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int noteNum, bool instMask, int instNum, bool volMask, int vol, bool isFMReversed) : AbstractCommand(CommandId::SetKeyOnToStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), note_(noteNum), inst_(instNum), vol_(vol), instMask_(instMask), volMask_(volMask), isFMReversed_(isFMReversed) { Step& st = command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum); prevNote_ = st.getNoteNumber(); if (!instMask) prevInst_ = st.getInstrumentNumber(); if (!volMask) prevVol_ = st.getVolume(); } void SetKeyOnToStepCommand::redo() { Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); st.setNoteNumber(note_); if (!instMask_) st.setInstrumentNumber(inst_); if (!volMask_) st.setVolume(isFMReversed_ ? effect_utils::reverseFmVolume(vol_) : vol_); } void SetKeyOnToStepCommand::undo() { Step& st = command_utils::getStep(mod_, song_, track_, order_, step_); st.setNoteNumber(prevNote_); if (!instMask_) st.setInstrumentNumber(prevInst_); if (!volMask_) st.setVolume(prevVol_); } BambooTracker-0.4.6/BambooTracker/command/pattern/set_key_on_to_step_command.hpp000066400000000000000000000032541401124043500301430ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class SetKeyOnToStepCommand final : public AbstractCommand { public: SetKeyOnToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int noteNum, bool instMask, int instNum, bool volMask, int vol, bool isFMReversed); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, track_, order_, step_, note_, inst_, vol_; int prevNote_, prevInst_, prevVol_; bool instMask_, volMask_; bool isFMReversed_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/set_volume_to_step_command.cpp000066400000000000000000000047631401124043500301670ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "set_volume_to_step_command.hpp" #include "pattern_command_utils.hpp" #include "effect.hpp" SetVolumeToStepCommand::SetVolumeToStepCommand( std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int volume, bool isFMReversed, bool secondEntry) : AbstractCommand(CommandId::SetVolumeToStep), mod_(mod), song_(songNum), track_(trackNum), order_(orderNum), step_(stepNum), vol_(volume), prevVol_(command_utils::getStep(mod, songNum, trackNum, orderNum, stepNum).getVolume()), isFMReversed_(isFMReversed), isSecondEntry_(secondEntry) { } void SetVolumeToStepCommand::redo() { int volume = isFMReversed_ ? effect_utils::reverseFmVolume(vol_) : vol_; command_utils::getStep(mod_, song_, track_, order_, step_).setVolume(volume); } void SetVolumeToStepCommand::undo() { command_utils::getStep(mod_, song_, track_, order_, step_).setVolume(prevVol_); isSecondEntry_ = true; // Forced complete } bool SetVolumeToStepCommand::mergeWith(const AbstractCommand* other) { if (other->getID() == getID() && !isSecondEntry_) { auto com = dynamic_cast(other); if (com->song_ == song_ && com->track_ == track_ && com->order_ == order_ && com->step_ == step_ && com->isSecondEntry_) { vol_ = (vol_ << 4) + com->vol_; redo(); isSecondEntry_ = true; return true; } } isSecondEntry_ = true; return false; } BambooTracker-0.4.6/BambooTracker/command/pattern/set_volume_to_step_command.hpp000066400000000000000000000032641401124043500301670ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "../abstract_command.hpp" #include "module.hpp" class SetVolumeToStepCommand final : public AbstractCommand { public: SetVolumeToStepCommand(std::weak_ptr mod, int songNum, int trackNum, int orderNum, int stepNum, int volume, bool isFMReversed, bool secondEntry); void redo() override; void undo() override; bool mergeWith(const AbstractCommand* other) override; private: std::weak_ptr mod_; const int song_, track_, order_, step_; int vol_; const int prevVol_; const bool isFMReversed_; bool isSecondEntry_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/shrink_pattern_command.cpp000066400000000000000000000065001401124043500272720ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "shrink_pattern_command.hpp" #include "pattern_command_utils.hpp" ShrinkPatternCommand::ShrinkPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep) : AbstractCommand(CommandId::ShrinkPattern), mod_(mod), song_(songNum), bTrack_(beginTrack), bCol_(beginColumn), order_(beginOrder), bStep_(beginStep), eStep_(endStep) { auto& song = mod.lock()->getSong(songNum); size_t h = static_cast(endStep - beginStep + 1); size_t w = command_utils::calculateColumnSize(beginTrack, beginColumn, endTrack, endColumn); prevCells_ = command_utils::getPreviousCells(song, w, h, beginTrack, beginColumn, beginOrder, beginStep); } void ShrinkPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); int s = bStep_; for (size_t i = 0; i < prevCells_.size(); i += 2) { int t = bTrack_; int c = bCol_; for (size_t j = 0; j < prevCells_.at(i).size(); ++j) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { case 0: st.setNoteNumber(std::stoi(prevCells_.at(i).at(j))); break; case 1: st.setInstrumentNumber(std::stoi(prevCells_.at(i).at(j))); break; case 2: st.setVolume(std::stoi(prevCells_.at(i).at(j))); break; default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) st.setEffectValue(ei, std::stoi(prevCells_.at(i).at(j))); else st.setEffectId(ei, prevCells_.at(i).at(j)); break; } } t += (++c / Step::N_COLUMN); c %= Step::N_COLUMN; } ++s; } for (; s <= eStep_; ++s) { int t = bTrack_; int c = bCol_; for (size_t j = 0; j < prevCells_.at(0).size(); ++j) { Step& st = command_utils::getStep(sng, t, order_, s); switch (c) { case 0: st.clearNoteNumber(); break; case 1: st.clearInstrumentNumber(); break; case 2: st.clearVolume(); break; default: { int ec = c - 3; int ei = ec / 2; if (ec % 2) st.clearEffectValue(ei); else st.clearEffectId(ei); break; } } t += (++c / Step::N_COLUMN); c %= Step::N_COLUMN; } } } void ShrinkPatternCommand::undo() { command_utils::restorePattern(mod_.lock()->getSong(song_), prevCells_, bTrack_, bCol_, order_, bStep_); } BambooTracker-0.4.6/BambooTracker/command/pattern/shrink_pattern_command.hpp000066400000000000000000000032431401124043500273000ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "../abstract_command.hpp" #include "module.hpp" class ShrinkPatternCommand final : public AbstractCommand { public: ShrinkPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginColumn, int beginOrder, int beginStep, int endTrack, int endColumn, int endStep); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_, bTrack_, bCol_, order_, bStep_; int eStep_; std::vector> prevCells_; }; BambooTracker-0.4.6/BambooTracker/command/pattern/transpose_note_in_pattern_command.cpp000066400000000000000000000052731401124043500315330ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "transpose_note_in_pattern_command.hpp" #include "pattern_command_utils.hpp" #include "note.hpp" #include "utils.hpp" TransposeNoteInPatternCommand::TransposeNoteInPatternCommand( std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int seminote) : AbstractCommand(CommandId::TransposeNoteInPattern), mod_(mod), song_(songNum), bTrack_(beginTrack), order_(beginOrder), bStep_(beginStep), eTrack_(endTrack), eStep_(endStep), seminote_(seminote) { auto& sng = mod.lock()->getSong(songNum); for (int step = beginStep; step <= endStep; ++step) { for (int track = beginTrack; track <= endTrack; ++track) { Step& st = command_utils::getStep(sng, track, beginOrder, step); if (st.hasGeneralNote()) prevKeys_.push_back(st.getNoteNumber()); } } } void TransposeNoteInPatternCommand::redo() { auto& sng = mod_.lock()->getSong(song_); for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { Step& st = command_utils::getStep(sng, track, order_, step); int n = st.getNoteNumber(); if (st.hasGeneralNote()) { st.setNoteNumber(utils::clamp(n + seminote_, 0, Note::NOTE_NUMBER_RANGE - 1)); } } } } void TransposeNoteInPatternCommand::undo() { auto& sng = mod_.lock()->getSong(song_); size_t i = 0; for (int step = bStep_; step <= eStep_; ++step) { for (int track = bTrack_; track <= eTrack_; ++track) { Step& st = command_utils::getStep(sng, track, order_, step); if (st.hasGeneralNote()) st.setNoteNumber(prevKeys_.at(i++)); } } } BambooTracker-0.4.6/BambooTracker/command/pattern/transpose_note_in_pattern_command.hpp000066400000000000000000000032231401124043500315310ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "../abstract_command.hpp" #include "module.hpp" class TransposeNoteInPatternCommand final : public AbstractCommand { public: TransposeNoteInPatternCommand(std::weak_ptr mod, int songNum, int beginTrack, int beginOrder, int beginStep, int endTrack, int endStep, int seminote); void redo() override; void undo() override; private: std::weak_ptr mod_; int song_; int bTrack_, order_, bStep_; int eTrack_, eStep_; int seminote_; std::vector prevKeys_; }; BambooTracker-0.4.6/BambooTracker/configuration.cpp000066400000000000000000000523121401124043500223170ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "configuration.hpp" #include "jamming.hpp" namespace { const std::unordered_map KEY_MAP_QWERTY = { {u8"Z", JamKey::LowC}, {u8"S", JamKey::LowCS}, {u8"X", JamKey::LowD}, {u8"D", JamKey::LowDS}, {u8"C", JamKey::LowE}, {u8"V", JamKey::LowF}, {u8"G", JamKey::LowFS}, {u8"B", JamKey::LowG}, {u8"H", JamKey::LowGS}, {u8"N", JamKey::LowA}, {u8"J", JamKey::LowAS}, {u8"M", JamKey::LowB}, {u8",", JamKey::LowC2}, {u8"L", JamKey::LowCS2}, {u8".", JamKey::LowD2}, {u8"Q", JamKey::HighC}, {u8"2", JamKey::HighCS}, {u8"W", JamKey::HighD}, {u8"3", JamKey::HighDS}, {u8"E", JamKey::HighE}, {u8"R", JamKey::HighF}, {u8"5", JamKey::HighFS}, {u8"T", JamKey::HighG}, {u8"6", JamKey::HighGS}, {u8"Y", JamKey::HighA}, {u8"7", JamKey::HighAS}, {u8"U", JamKey::HighB}, {u8"I", JamKey::HighC2}, {u8"9", JamKey::HighCS2}, {u8"O", JamKey::HighD2}, }; const std::unordered_map KEY_MAP_QWERTZ = { {u8"Y", JamKey::LowC}, {u8"S", JamKey::LowCS}, {u8"X", JamKey::LowD}, {u8"D", JamKey::LowDS}, {u8"C", JamKey::LowE}, {u8"V", JamKey::LowF}, {u8"G", JamKey::LowFS}, {u8"B", JamKey::LowG}, {u8"H", JamKey::LowGS}, {u8"N", JamKey::LowA}, {u8"J", JamKey::LowAS}, {u8"M", JamKey::LowB}, {u8",", JamKey::LowC2}, {u8"L", JamKey::LowCS2}, {u8".", JamKey::LowD2}, {u8"Q", JamKey::HighC}, {u8"2", JamKey::HighCS}, {u8"W", JamKey::HighD}, {u8"3", JamKey::HighDS}, {u8"E", JamKey::HighE}, {u8"R", JamKey::HighF}, {u8"5", JamKey::HighFS}, {u8"T", JamKey::HighG}, {u8"6", JamKey::HighGS}, {u8"Z", JamKey::HighA}, {u8"7", JamKey::HighAS}, {u8"U", JamKey::HighB}, {u8"I", JamKey::HighC2}, {u8"9", JamKey::HighCS2}, {u8"O", JamKey::HighD2}, }; const std::unordered_map KEY_MAP_AZERTY = { {u8"W", JamKey::LowC}, {u8"S", JamKey::LowCS}, {u8"X", JamKey::LowD}, {u8"D", JamKey::LowDS}, {u8"C", JamKey::LowE}, {u8"V", JamKey::LowF}, {u8"G", JamKey::LowFS}, {u8"B", JamKey::LowG}, {u8"H", JamKey::LowGS}, {u8"N", JamKey::LowA}, {u8"J", JamKey::LowAS}, {u8",", JamKey::LowB}, {u8";", JamKey::LowC2}, {u8"L", JamKey::LowCS2}, {u8".", JamKey::LowD2}, {u8"A", JamKey::HighC}, {u8"É", JamKey::HighCS}, //é - \xc9 {u8"Z", JamKey::HighD}, {u8"\"", JamKey::HighDS}, {u8"E", JamKey::HighE}, {u8"R", JamKey::HighF}, {u8"(", JamKey::HighFS}, {u8"T", JamKey::HighG}, {u8"-", JamKey::HighGS}, {u8"Y", JamKey::HighA}, {u8"È", JamKey::HighAS}, //è - \xc8 {u8"U", JamKey::HighB}, {u8"I", JamKey::HighC2}, {u8"Ç", JamKey::HighCS2}, //ç - \xc7 {u8"O", JamKey::HighD2}, }; } Configuration::Configuration() { // Internal // followMode_ = true; workDir_ = ""; instOpenFormat_ = 0; bankOpenFormat_ = 0; instMask_ = false; volMask_ = true; visibleToolbar_ = true; visibleStatusBar_ = true; visibleWaveView_ = true; pasteMode_ = PasteMode::Cursor; // Mainwindow state mainW_ = 930; mainH_= 780; mainMax_ = false; mainX_ = -1; // Dummy mainY_ = -1; // Dummy mainVSplit_ = -1; // Dummy // Instrument editor state instFMW_ = 570; instFMH_ = 750; instSSGW_ = 510; instSSGH_ = 390; instADPCMW_ = 510; instADPCMH_ = 430; instKitW_ = 590; instKitH_ = 430; // Toolbar state mainTb_.setPosition(ToolbarPosition::TopPosition); mainTb_.setNumber(0); mainTb_.setBreakBefore(false); mainTb_.setX(-1); // Dummy mainTb_.setY(-1); // Dummy subTb_.setPosition(ToolbarPosition::TopPosition); subTb_.setNumber(1); subTb_.setBreakBefore(false); subTb_.setX(-1); // Dummy subTb_.setY(-1); // Dummy // General // // General settings warpCursor_ = true; warpAcrossOrders_ = true; showRowNumHex_ = true; showPrevNextOrders_ = true; backupModules_ = true; dontSelectOnDoubleClick_ = false; reverseFMVolumeOrder_ = true; moveCursorToRight_ = false; retrieveChannelState_ = false; enableTranslation_ = true; showFMDetuneSigned_ = false; fill00ToEffectValue_ = true; moveCursorHScroll_ = true; overwriteUnusedUnedited_ = false; writeOnlyUsedSamples_ = false; reflectInstNumChange_ = false; fixJamVol_ = true; muteHiddenTracks_ = true; restoreTrackVis_ = false; // Edit settings pageJumpLength_ = 4; editableStep_ = 1; keyRepetision_ = true; // Wave view waveViewFps_ = 30; // Keys shortcuts_ = { { ShortcutAction::KeyOff, u8"-" }, { ShortcutAction::OctaveUp, u8"Num+*" }, { ShortcutAction::OctaveDown, u8"Num+/" }, { ShortcutAction::EchoBuffer, u8"^" }, { ShortcutAction::PlayAndStop, u8"Return" }, { ShortcutAction::Play, u8"" }, { ShortcutAction::PlayFromStart, u8"F5" }, { ShortcutAction::PlayPattern, u8"F6" }, { ShortcutAction::PlayFromCursor, u8"F7" }, { ShortcutAction::PlayFromMarker, u8"Ctrl+F7" }, { ShortcutAction::PlayStep, u8"Ctrl+Return" }, { ShortcutAction::Stop, u8"F8" }, { ShortcutAction::FocusOnPattern, u8"F2" }, { ShortcutAction::FocusOnOrder, u8"F3" }, { ShortcutAction::FocusOnInstrument, u8"F4" }, { ShortcutAction::ToggleEditJam, u8"Space" }, { ShortcutAction::SetMarker, u8"Ctrl+B" }, { ShortcutAction::PasteMix, u8"Ctrl+M" }, { ShortcutAction::PasteOverwrite, u8"" }, { ShortcutAction::PasteInsert, u8"" }, { ShortcutAction::SelectAll, u8"Ctrl+A" }, { ShortcutAction::Deselect, u8"Esc" }, { ShortcutAction::SelectRow, u8"" }, { ShortcutAction::SelectColumn, u8"" }, { ShortcutAction::SelectPattern, u8"" }, { ShortcutAction::SelectOrder, u8"" }, { ShortcutAction::GoToStep, u8"Alt+G" }, { ShortcutAction::ToggleTrack, u8"Alt+F9" }, { ShortcutAction::SoloTrack, u8"Alt+F10" }, { ShortcutAction::Interpolate, u8"Ctrl+G" }, { ShortcutAction::Reverse, u8"Ctrl+R" }, { ShortcutAction::GoToPrevOrder, u8"Ctrl+Left" }, { ShortcutAction::GoToNextOrder, u8"Ctrl+Right" }, { ShortcutAction::ToggleBookmark, u8"Ctrl+K" }, { ShortcutAction::PrevBookmark, u8"Ctrl+PgUp" }, { ShortcutAction::NextBookmark, u8"Ctrl+PgDown" }, { ShortcutAction::DecreaseNote, u8"Ctrl+F1" }, { ShortcutAction::IncreaseNote, u8"Ctrl+F2" }, { ShortcutAction::DecreaseOctave, u8"Ctrl+F3" }, { ShortcutAction::IncreaseOctave, u8"Ctrl+F4" }, { ShortcutAction::PrevInstrument, u8"Alt+Left" }, { ShortcutAction::NextInstrument, u8"Alt+Right" }, { ShortcutAction::MaskInstrument, u8"" }, { ShortcutAction::MaskVolume, u8"" }, { ShortcutAction::EditInstrument, u8"Ctrl+I" }, { ShortcutAction::FollowMode, u8"ScrollLock" }, { ShortcutAction::DuplicateOrder, u8"Ctrl+D" }, { ShortcutAction::ClonePatterns, u8"Alt+D" }, { ShortcutAction::CloneOrder, u8"" }, { ShortcutAction::ReplaceInstrument, u8"Alt+S" }, { ShortcutAction::ExpandPattern, u8"" }, { ShortcutAction::ShrinkPattern, u8"" }, { ShortcutAction::FineDecreaseValues, u8"Shift+F1" }, { ShortcutAction::FineIncreaseValues, u8"Shift+F2" }, { ShortcutAction::CoarseDecreaseValues, u8"Shift+F3" }, { ShortcutAction::CoarseIncreaseValuse, u8"Shift+F4" }, { ShortcutAction::ExpandEffect, u8"Alt+L" }, { ShortcutAction::ShrinkEffect, u8"Alt+K" }, { ShortcutAction::PrevHighlighted, u8"Ctrl+Up" }, { ShortcutAction::NextHighlighted, u8"Ctrl+Down" }, { ShortcutAction::IncreasePatternSize, u8"" }, { ShortcutAction::DecreasePatternSize, u8"" }, { ShortcutAction::IncreaseEditStep, u8"" }, { ShortcutAction::DecreaseEditStep, u8"" }, { ShortcutAction::DisplayEffectList, u8"F1" }, { ShortcutAction::PreviousSong, u8"" }, { ShortcutAction::NextSong, u8"" }, { ShortcutAction::JamVolumeUp, u8"" }, { ShortcutAction::JamVolumeDown, u8"" } }; noteEntryLayout_ = KeyboardLayout::QWERTY; // Sound // sndAPI_ = u8""; sndDevice_ = u8""; realChip_ = RealChipInterface::NONE; emulator_ = 1; sampleRate_ = 44100; bufferLength_ = 40; // Midi // midiEnabled_ = false; midiAPI_ = u8""; midiInPort_ = u8""; // Mixer // mixerVolumeMaster_ = 100; mixerVolumeFM_ = 0; mixerVolumeSSG_ = 0; // Input // fmEnvelopeTexts_ = { { "PMD", std::vector({ FMEnvelopeTextType::Skip, FMEnvelopeTextType::AL, FMEnvelopeTextType::FB, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4, FMEnvelopeTextType::Skip }) }, { "FMP", std::vector({ FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4, FMEnvelopeTextType::AL, FMEnvelopeTextType::FB }) }, { "FMP7", std::vector({ FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AL, FMEnvelopeTextType::FB }) }, { "VOPM", std::vector({ // Number FMEnvelopeTextType::Skip, // LFO FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, // CH FMEnvelopeTextType::Skip, FMEnvelopeTextType::FB, FMEnvelopeTextType::AL, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, // Op FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip }) }, { "NRTDRV", // For VOICE_MODE=0 std::vector({ FMEnvelopeTextType::Skip, FMEnvelopeTextType::AL, FMEnvelopeTextType::FB, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip }) }, { "MXDRV", std::vector({ FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AL, FMEnvelopeTextType::FB, FMEnvelopeTextType::Skip }) }, { "MMLDRV", std::vector({ FMEnvelopeTextType::Skip, FMEnvelopeTextType::AL, FMEnvelopeTextType::FB, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4, FMEnvelopeTextType::Skip, FMEnvelopeTextType::Skip }) }, { "MUCOM88", std::vector({ FMEnvelopeTextType::Skip, FMEnvelopeTextType::FB, FMEnvelopeTextType::AL, FMEnvelopeTextType::AR1, FMEnvelopeTextType::DR1, FMEnvelopeTextType::SR1, FMEnvelopeTextType::RR1, FMEnvelopeTextType::SL1, FMEnvelopeTextType::TL1, FMEnvelopeTextType::KS1, FMEnvelopeTextType::ML1, FMEnvelopeTextType::DT1, FMEnvelopeTextType::AR2, FMEnvelopeTextType::DR2, FMEnvelopeTextType::SR2, FMEnvelopeTextType::RR2, FMEnvelopeTextType::SL2, FMEnvelopeTextType::TL2, FMEnvelopeTextType::KS2, FMEnvelopeTextType::ML2, FMEnvelopeTextType::DT2, FMEnvelopeTextType::AR3, FMEnvelopeTextType::DR3, FMEnvelopeTextType::SR3, FMEnvelopeTextType::RR3, FMEnvelopeTextType::SL3, FMEnvelopeTextType::TL3, FMEnvelopeTextType::KS3, FMEnvelopeTextType::ML3, FMEnvelopeTextType::DT3, FMEnvelopeTextType::AR4, FMEnvelopeTextType::DR4, FMEnvelopeTextType::SR4, FMEnvelopeTextType::RR4, FMEnvelopeTextType::SL4, FMEnvelopeTextType::TL4, FMEnvelopeTextType::KS4, FMEnvelopeTextType::ML4, FMEnvelopeTextType::DT4 }) } }; // Layouts mappingCustom_ = {}; mappingLayouts = { { KeyboardLayout::Custom, mappingCustom_ }, { KeyboardLayout::QWERTY, KEY_MAP_QWERTY }, { KeyboardLayout::QWERTZ, KEY_MAP_QWERTZ }, { KeyboardLayout::AZERTY, KEY_MAP_AZERTY } }; // Appearance ptnHdFont_ = u8""; ptnHdFontSize_ = 10; ptnRowFont_ = u8""; ptnRowFontSize_ = 10; odrHdFont_ = u8""; odrHdFontSize_ = 10; odrRowFont_ = u8""; odrRowFontSize_ = 10; } // Keys void Configuration::setCustomLayoutKeys(const std::unordered_map& mapping) { mappingLayouts[KeyboardLayout::Custom] = mapping; } std::unordered_map Configuration::getCustomLayoutKeys() const { return mappingLayouts.at(KeyboardLayout::Custom); } BambooTracker-0.4.6/BambooTracker/configuration.hpp000066400000000000000000000361321401124043500223260ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include "enum_hash.hpp" enum class JamKey : int; enum class FMEnvelopeTextType : int { Skip, AL, FB, AR1, DR1, SR1, RR1, SL1, TL1, KS1, ML1, DT1, AR2, DR2, SR2, RR2, SL2, TL2, KS2, ML2, DT2, AR3, DR3, SR3, RR3, SL3, TL3, KS3, ML3, DT3, AR4, DR4, SR4, RR4, SL4, TL4, KS4, ML4, DT4 }; struct FMEnvelopeText { std::string name; std::vector texts; }; enum class RealChipInterface : int { NONE = 0, SCCI = 1, C86CTL = 2 }; class Configuration { public: Configuration(); // Internal // public: void setFollowMode(bool enabled) { followMode_ = enabled; } bool getFollowMode() const { return followMode_; } void setWorkingDirectory(const std::string& path) { workDir_ = path; } std::string getWorkingDirectory() const { return workDir_; } void setInstrumentOpenFormat(int i) { instOpenFormat_ = i; } int getInstrumentOpenFormat() const { return instOpenFormat_; } void setBankOpenFormat(int i) { bankOpenFormat_ = i; } int getBankOpenFormat() const { return bankOpenFormat_; } void setInstrumentMask(bool enabled) { instMask_ = enabled; } bool getInstrumentMask() const { return instMask_; } void setVolumeMask(bool enabled) { volMask_ = enabled; } bool getVolumeMask() const { return volMask_; } void setVisibleToolbar(bool visible) { visibleToolbar_ = visible; } bool getVisibleToolbar() const { return visibleToolbar_; } void setVisibleStatusBar(bool visible) { visibleStatusBar_ = visible; } bool getVisibleStatusBar() const { return visibleStatusBar_; } void setVisibleWaveView(bool visible) { visibleWaveView_ = visible; } bool getVisibleWaveView() const { return visibleWaveView_; } enum class PasteMode : int { Cursor, Selection, Fill }; void setPasteMode(PasteMode mode) { pasteMode_ = mode; } PasteMode getPasteMode() const { return pasteMode_; } private: bool followMode_; std::string workDir_; int instOpenFormat_, bankOpenFormat_; bool instMask_, volMask_; bool visibleToolbar_, visibleStatusBar_, visibleWaveView_; PasteMode pasteMode_; // Mainwindow state public: void setMainWindowWidth(int w) { mainW_ = w; } int getMainWindowWidth() const { return mainW_; } void setMainWindowHeight(int h) { mainH_ = h; } int getMainWindowHeight() const { return mainH_; } void setMainWindowMaximized(bool isMax) { mainMax_ = isMax; } bool getMainWindowMaximized() const { return mainMax_; } void setMainWindowX(int x) { mainX_ = x; } int getMainWindowX() const { return mainX_; } void setMainWindowY(int y) { mainY_ = y; } int getMainWindowY() const { return mainY_; } void setMainWindowVerticalSplit(int y) { mainVSplit_ = y; } int getMainWindowVerticalSplit() const { return mainVSplit_; } private: int mainW_, mainH_; bool mainMax_; int mainX_, mainY_; int mainVSplit_; // Instrument editor state public: void setInstrumentFMWindowWidth(int w) { instFMW_ = w; } int getInstrumentFMWindowWidth() const { return instFMW_; } void setInstrumentFMWindowHeight(int h) { instFMH_ = h; } int getInstrumentFMWindowHeight() const { return instFMH_; } void setInstrumentSSGWindowWidth(int w) { instSSGW_ = w; } int getInstrumentSSGWindowWidth() const { return instSSGW_; } void setInstrumentSSGWindowHeight(int h) { instSSGH_ = h; } int getInstrumentSSGWindowHeight() const { return instSSGH_; } void setInstrumentADPCMWindowWidth(int w) { instADPCMW_ = w; } int getInstrumentADPCMWindowWidth() const { return instADPCMW_; } void setInstrumentADPCMWindowHeight(int h) { instADPCMH_ = h; } int getInstrumentADPCMWindowHeight() const { return instADPCMH_; } void setInstrumentDrumkitWindowWidth(int w) { instKitW_ = w; } int getInstrumentDrumkitWindowWidth() const { return instKitW_; } void setInstrumentDrumkitWindowHeight(int h) { instKitH_ = h; } int getInstrumentDrumkitWindowHeight() const { return instKitH_; } private: int instFMW_, instFMH_; int instSSGW_, instSSGH_; int instADPCMW_, instADPCMH_; int instKitW_, instKitH_; // Toolbar state public: class ToolbarConfiguration { public: enum class Position : int { TopPosition = 0, BottomPosition, LeftPosition, RightPosition, FloatPorition }; void setPosition(Position pos) { pos_ = pos; } Position getPosition() const { return pos_; } void setNumber(int n) { num_ = n; } int getNumber() const { return num_; } void setBreakBefore(bool enabled) { hasBreakBefore_ = enabled; } bool hasBreakBefore() const { return hasBreakBefore_; } void setX(int x) { x_ = x; } int getX() const { return x_; } void setY(int y) { y_ = y; } int getY() const { return y_; } private: Position pos_; int num_; bool hasBreakBefore_; int x_, y_; }; using ToolbarPosition = ToolbarConfiguration::Position; ToolbarConfiguration& getMainToolbarConfiguration() { return mainTb_; } ToolbarConfiguration& getSubToolbarConfiguration() { return subTb_; } private: ToolbarConfiguration mainTb_, subTb_; // General // // General settings public: void setWarpCursor(bool enabled) { warpCursor_ = enabled; } bool getWarpCursor() const { return warpCursor_; } void setWarpAcrossOrders(bool enabled) { warpAcrossOrders_ = enabled; } bool getWarpAcrossOrders() const { return warpAcrossOrders_; } void setShowRowNumberInHex(bool enabled) { showRowNumHex_ = enabled; } bool getShowRowNumberInHex() const { return showRowNumHex_; } void setShowPreviousNextOrders(bool enabled) { showPrevNextOrders_ = enabled; } bool getShowPreviousNextOrders() const { return showPrevNextOrders_; } void setBackupModules(bool enabled) { backupModules_ = enabled; } bool getBackupModules() const { return backupModules_; } void setDontSelectOnDoubleClick(bool enabled) { dontSelectOnDoubleClick_ = enabled; } bool getDontSelectOnDoubleClick() const { return dontSelectOnDoubleClick_; } void setReverseFMVolumeOrder(bool enabled) { reverseFMVolumeOrder_= enabled; } bool getReverseFMVolumeOrder() const { return reverseFMVolumeOrder_; } void setMoveCursorToRight(bool enabled) { moveCursorToRight_ = enabled; } bool getMoveCursorToRight() const { return moveCursorToRight_; } void setRetrieveChannelState(bool enabled) { retrieveChannelState_ = enabled; } bool getRetrieveChannelState() const { return retrieveChannelState_; } void setEnableTranslation(bool enabled) { enableTranslation_ = enabled; } bool getEnableTranslation() const { return enableTranslation_; } void setShowFMDetuneAsSigned(bool enabled) { showFMDetuneSigned_ = enabled; } bool getShowFMDetuneAsSigned() const { return showFMDetuneSigned_; } void setFill00ToEffectValue(bool enabled) { fill00ToEffectValue_ = enabled; } bool getFill00ToEffectValue() const { return fill00ToEffectValue_; } void setMoveCursorByHorizontalScroll(bool enabled) { moveCursorHScroll_ = enabled; } bool getMoveCursorByHorizontalScroll() const { return moveCursorHScroll_; } void setOverwriteUnusedUneditedPropety(bool enabled) { overwriteUnusedUnedited_ = enabled; } bool getOverwriteUnusedUneditedPropety() const { return overwriteUnusedUnedited_; } void setWriteOnlyUsedSamples(bool enabled) { writeOnlyUsedSamples_ = enabled; } bool getWriteOnlyUsedSamples() const { return writeOnlyUsedSamples_; } void setReflectInstrumentNumberChange(bool enabled) { reflectInstNumChange_ = enabled; } bool getReflectInstrumentNumberChange() const { return reflectInstNumChange_; } void setFixJammingVolume(bool enabled) { fixJamVol_ = enabled; } bool getFixJammingVolume() const { return fixJamVol_; } void setMuteHiddenTracks(bool enabled) { muteHiddenTracks_ = enabled; } bool getMuteHiddenTracks() const { return muteHiddenTracks_; } void setRestoreTrackVisibility(bool enabled) { restoreTrackVis_ = enabled; } bool getRestoreTrackVisibility() const { return restoreTrackVis_; } private: bool warpCursor_, warpAcrossOrders_, showRowNumHex_, showPrevNextOrders_, backupModules_; bool dontSelectOnDoubleClick_, reverseFMVolumeOrder_, moveCursorToRight_, retrieveChannelState_; bool enableTranslation_, showFMDetuneSigned_, fill00ToEffectValue_, moveCursorHScroll_; bool overwriteUnusedUnedited_, writeOnlyUsedSamples_, reflectInstNumChange_, fixJamVol_; bool muteHiddenTracks_, restoreTrackVis_; // Edit settings public: void setPageJumpLength(size_t length) { pageJumpLength_ = length; } size_t getPageJumpLength() const { return pageJumpLength_; } void setEditableStep(size_t step) { editableStep_ = step; } size_t getEditableStep() const { return editableStep_; } void setKeyRepetition(bool enabled) { keyRepetision_ = enabled; } bool getKeyRepetition() const { return keyRepetision_; } private: size_t pageJumpLength_, editableStep_; bool keyRepetision_; // Wave view public: void setWaveViewFrameRate(int rate) { waveViewFps_ = rate; } int getWaveViewFrameRate() const { return waveViewFps_; } private: int waveViewFps_; // Keys public: enum class ShortcutAction : int { KeyOff, OctaveUp, OctaveDown, EchoBuffer, PlayAndStop, Play, PlayFromStart, PlayPattern, PlayFromCursor, PlayFromMarker, PlayStep, Stop, FocusOnPattern, FocusOnOrder, FocusOnInstrument, ToggleEditJam, SetMarker, PasteMix, PasteOverwrite, PasteInsert, SelectAll, Deselect, SelectRow, SelectColumn, SelectPattern, SelectOrder, GoToStep, ToggleTrack, SoloTrack, Interpolate, Reverse, GoToPrevOrder, GoToNextOrder, ToggleBookmark, PrevBookmark, NextBookmark, DecreaseNote, IncreaseNote, DecreaseOctave, IncreaseOctave, PrevInstrument, NextInstrument, MaskInstrument, MaskVolume, EditInstrument, FollowMode, DuplicateOrder, ClonePatterns, CloneOrder, ReplaceInstrument, ExpandPattern, ShrinkPattern, FineDecreaseValues, FineIncreaseValues, CoarseDecreaseValues, CoarseIncreaseValuse, ExpandEffect, ShrinkEffect, PrevHighlighted, NextHighlighted, IncreasePatternSize, DecreasePatternSize, IncreaseEditStep, DecreaseEditStep, DisplayEffectList, PreviousSong, NextSong, JamVolumeUp, JamVolumeDown }; void setShortcuts(std::unordered_map shortcuts) { shortcuts_ = shortcuts; } std::unordered_map getShortcuts() const { return shortcuts_; } enum class KeyboardLayout : int { // at the top, so new layouts can easily be added in after it // and it's always easy to find no matter how many layouts we add Custom = 0, QWERTY, QWERTZ, AZERTY }; std::unordered_map> mappingLayouts; void setNoteEntryLayout(KeyboardLayout layout) { noteEntryLayout_ = layout; } KeyboardLayout getNoteEntryLayout() const { return noteEntryLayout_; } void setCustomLayoutKeys(const std::unordered_map& mapping); std::unordered_map getCustomLayoutKeys() const; private: std::unordered_map shortcuts_; KeyboardLayout noteEntryLayout_; std::unordered_map mappingCustom_; // Sound // public: void setSoundAPI(const std::string& api) { sndAPI_ = api; } std::string getSoundAPI() const { return sndAPI_; } void setSoundDevice(const std::string& device) { sndDevice_ = device; } std::string getSoundDevice() const { return sndDevice_; } void setRealChipInterface(RealChipInterface type) { realChip_ = type; } RealChipInterface getRealChipInterface() const { return realChip_; } void setEmulator(int emulator) { emulator_ = emulator; } int getEmulator() const { return emulator_; } void setSampleRate(uint32_t rate) { sampleRate_ = rate; } uint32_t getSampleRate() const { return sampleRate_; } void setBufferLength(size_t length) { bufferLength_ = length; } size_t getBufferLength() const { return bufferLength_; } private: std::string sndAPI_, sndDevice_; RealChipInterface realChip_; int emulator_; uint32_t sampleRate_; size_t bufferLength_; // Midi // public: void setMidiEnabled(const bool enabled) { midiEnabled_ = enabled; } bool getMidiEnabled() const { return midiEnabled_; } void setMidiAPI(const std::string& api) { midiAPI_ = api; } std::string getMidiAPI() const { return midiAPI_; } void setMidiInputPort(const std::string& port) { midiInPort_ = port; } std::string getMidiInputPort() const { return midiInPort_; } private: bool midiEnabled_; std::string midiAPI_, midiInPort_; // Mixer // public: void setMixerVolumeMaster(int percentage) { mixerVolumeMaster_ = percentage; } int getMixerVolumeMaster() const { return mixerVolumeMaster_; } void setMixerVolumeFM(double dB) { mixerVolumeFM_ = dB; } double getMixerVolumeFM() const { return mixerVolumeFM_; } void setMixerVolumeSSG(double dB) { mixerVolumeSSG_ = dB; } double getMixerVolumeSSG() const { return mixerVolumeSSG_; } private: int mixerVolumeMaster_; double mixerVolumeFM_, mixerVolumeSSG_; // Input // public: void setFMEnvelopeTexts(const std::vector& texts) { fmEnvelopeTexts_ = texts; } std::vector getFMEnvelopeTexts() const { return fmEnvelopeTexts_; } // Appearance // public: void setPatternEditorHeaderFont(const std::string& font) { ptnHdFont_ = font; } std::string getPatternEditorHeaderFont() const { return ptnHdFont_; } void setPatternEditorHeaderFontSize(int size) { ptnHdFontSize_ = size; } int getPatternEditorHeaderFontSize() const { return ptnHdFontSize_; } void setPatternEditorRowsFont(const std::string& font) { ptnRowFont_ = font; } std::string getPatternEditorRowsFont() const { return ptnRowFont_; } void setPatternEditorRowsFontSize(int size) { ptnRowFontSize_ = size; } int getPatternEditorRowsFontSize() const { return ptnRowFontSize_; } void setOrderListHeaderFont(const std::string& font) { odrHdFont_ = font; } std::string getOrderListHeaderFont() const { return odrHdFont_; } void setOrderListHeaderFontSize(int size) { odrHdFontSize_ = size; } int getOrderListHeaderFontSize() const { return odrHdFontSize_; } void setOrderListRowsFont(const std::string& font) { odrRowFont_ = font; } std::string getOrderListRowsFont() const { return odrRowFont_; } void setOrderListRowsFontSize(int size) { odrRowFontSize_ = size; } int getOrderListRowsFontSize() const { return odrRowFontSize_; } private: std::string ptnHdFont_, ptnRowFont_, odrHdFont_, odrRowFont_; int ptnHdFontSize_, ptnRowFontSize_, odrHdFontSize_, odrRowFontSize_; private: std::vector fmEnvelopeTexts_; }; BambooTracker-0.4.6/BambooTracker/echo_buffer.hpp000066400000000000000000000037301401124043500217240ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "note.hpp" class EchoBuffer { using DequeType = std::deque; public: using reference = DequeType::reference; using const_reference = DequeType::const_reference; using size_type = DequeType::size_type; reference at(size_type n) { return deque_.at(n); } const_reference at(size_type n) const { return deque_.at(n); } reference operator[](size_type n) { return deque_[n]; } const_reference operator[](size_type n) const { return deque_[n]; } reference latest() { return deque_.front(); } const_reference latest() const { return deque_.front(); } size_type size() const noexcept { return deque_.size(); } void clear() noexcept { deque_.clear(); } void push(const Note& y) { deque_.push_front(y); if (MAX_ <= deque_.size()) deque_.pop_back(); } private: std::deque deque_; static constexpr size_type MAX_ = 4; }; BambooTracker-0.4.6/BambooTracker/enum_hash.hpp000066400000000000000000000007771401124043500214340ustar00rootroot00000000000000#pragma once #if (defined __GNUC__) && (!defined __clang__) #if (__GNUC__ < 6) || ((__GNUC__ == 6) && (__GNUC_MINOR__ < 1)) // Unsupport std::hash with enum types before gcc 6.1. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970 #include namespace std { template struct hash { static_assert(is_enum::value, "..."); size_t operator()(T x) const noexcept { using type = typename underlying_type::type; return hash{}(static_cast(x)); } }; } #endif #endif BambooTracker-0.4.6/BambooTracker/format/000077500000000000000000000000001401124043500202315ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/format/wopn_file.c000066400000000000000000000432711401124043500223660ustar00rootroot00000000000000/* * Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup * * Copyright (c) 2018 Vitaly Novichkov * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "wopn_file.h" #include #include static const char *wopn2_magic1 = "WOPN2-BANK\0"; static const char *wopn2_magic2 = "WOPN2-B2NK\0"; static const char *opni_magic1 = "WOPN2-INST\0"; static const char *opni_magic2 = "WOPN2-IN2T\0"; static const uint16_t wopn_latest_version = 2; enum { WOPN_INST_SIZE_V1 = 65, WOPN_INST_SIZE_V2 = 69 }; static uint16_t toUint16LE(const uint8_t *arr) { uint16_t num = arr[0]; num |= ((arr[1] << 8) & 0xFF00); return num; } static uint16_t toUint16BE(const uint8_t *arr) { uint16_t num = arr[1]; num |= ((arr[0] << 8) & 0xFF00); return num; } static int16_t toSint16BE(const uint8_t *arr) { int16_t num = *(const int8_t *)(&arr[0]); num *= 1 << 8; num |= arr[1]; return num; } static void fromUint16LE(uint16_t in, uint8_t *arr) { arr[0] = in & 0x00FF; arr[1] = (in >> 8) & 0x00FF; } static void fromUint16BE(uint16_t in, uint8_t *arr) { arr[1] = in & 0x00FF; arr[0] = (in >> 8) & 0x00FF; } static void fromSint16BE(int16_t in, uint8_t *arr) { arr[1] = in & 0x00FF; arr[0] = ((uint16_t)in >> 8) & 0x00FF; } WOPNFile *WOPN_Init(uint16_t melodic_banks, uint16_t percussive_banks) { WOPNFile *file = (WOPNFile*)calloc(1, sizeof(WOPNFile)); if(!file) return NULL; file->banks_count_melodic = (melodic_banks != 0) ? melodic_banks : 1; file->banks_melodic = (WOPNBank*)calloc(file->banks_count_melodic, sizeof(WOPNBank)); if(melodic_banks == 0) { unsigned i; for(i = 0; i < 128; ++i) file->banks_melodic[0].ins[i].inst_flags = WOPN_Ins_IsBlank; } file->banks_count_percussion = (percussive_banks != 0) ? percussive_banks : 1; file->banks_percussive = (WOPNBank*)calloc(file->banks_count_percussion, sizeof(WOPNBank)); if(percussive_banks == 0) { unsigned i; for(i = 0; i < 128; ++i) file->banks_percussive[0].ins[i].inst_flags = WOPN_Ins_IsBlank; } return file; } void WOPN_Free(WOPNFile *file) { if(file) { if(file->banks_melodic) free(file->banks_melodic); if(file->banks_percussive) free(file->banks_percussive); free(file); } } int WOPN_BanksCmp(const WOPNFile *bank1, const WOPNFile *bank2) { int res = 1; res &= (bank1->version == bank2->version); res &= (bank1->lfo_freq == bank2->lfo_freq); res &= (bank1->chip_type == bank2->chip_type); res &= (bank1->volume_model == bank2->volume_model); res &= (bank1->banks_count_melodic == bank2->banks_count_melodic); res &= (bank1->banks_count_percussion == bank2->banks_count_percussion); if(res) { int i; for(i = 0; i < bank1->banks_count_melodic; i++) res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPNBank)) == 0); if(res) { for(i = 0; i < bank1->banks_count_percussion; i++) res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPNBank)) == 0); } } return res; } static void WOPN_parseInstrument(WOPNInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) { int l; strncpy(ins->inst_name, (const char*)cursor, 32); ins->inst_name[32] = '\0'; ins->note_offset = toSint16BE(cursor + 32); ins->midi_velocity_offset = 0; /* TODO: for future version > 2 */ ins->percussion_key_number = cursor[34]; ins->inst_flags = 0; /* TODO: for future version > 2 */ ins->fbalg = cursor[35]; ins->lfosens = cursor[36]; for(l = 0; l < 4; l++) { size_t off = 37 + (size_t)(l) * 7; ins->operators[l].dtfm_30 = cursor[off + 0]; ins->operators[l].level_40 = cursor[off + 1]; ins->operators[l].rsatk_50 = cursor[off + 2]; ins->operators[l].amdecay1_60 = cursor[off + 3]; ins->operators[l].decay2_70 = cursor[off + 4]; ins->operators[l].susrel_80 = cursor[off + 5]; ins->operators[l].ssgeg_90 = cursor[off + 6]; } if((version >= 2) && has_sounding_delays) { ins->delay_on_ms = toUint16BE(cursor + 65); ins->delay_off_ms = toUint16BE(cursor + 67); /* Null delays indicate the blank instrument in version 2 */ if((version < 3) && ins->delay_on_ms == 0 && ins->delay_off_ms == 0) ins->inst_flags |= WOPN_Ins_IsBlank; } } static void WOPN_writeInstrument(WOPNInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) { int l; unsigned int nameLen = strlen(ins->inst_name) + 1; if (32 < nameLen) nameLen = 32; memcpy(cursor, ins->inst_name, nameLen); /*strncpy((char*)cursor, ins->inst_name, 32);*/ fromSint16BE(ins->note_offset, cursor + 32); cursor[34] = ins->percussion_key_number; cursor[35] = ins->fbalg; cursor[36] = ins->lfosens; for(l = 0; l < 4; l++) { size_t off = 37 + (size_t)(l) * 7; cursor[off + 0] = ins->operators[l].dtfm_30; cursor[off + 1] = ins->operators[l].level_40; cursor[off + 2] = ins->operators[l].rsatk_50; cursor[off + 3] = ins->operators[l].amdecay1_60; cursor[off + 4] = ins->operators[l].decay2_70; cursor[off + 5] = ins->operators[l].susrel_80; cursor[off + 6] = ins->operators[l].ssgeg_90; } if((version >= 2) && has_sounding_delays) { if((version < 3) && (ins->inst_flags & WOPN_Ins_IsBlank) != 0) { /* Null delays indicate the blank instrument in version 2 */ fromUint16BE(0, cursor + 65); fromUint16BE(0, cursor + 67); } else { fromUint16BE(ins->delay_on_ms, cursor + 65); fromUint16BE(ins->delay_off_ms, cursor + 67); } } } WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error) { WOPNFile *outFile = NULL; uint16_t i = 0, j = 0, k = 0; uint16_t version = 0; uint16_t count_melodic_banks = 1; uint16_t count_percussive_banks = 1; uint8_t *cursor = (uint8_t *)mem; WOPNBank *bankslots[2]; uint16_t bankslots_sizes[2]; #define SET_ERROR(err) \ {\ WOPN_Free(outFile);\ if(error)\ {\ *error = err;\ }\ } #define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } if(!cursor) { SET_ERROR(WOPN_ERR_NULL_POINTER); return NULL; } {/* Magic number */ if(length < 11) { SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); return NULL; } if(memcmp(cursor, wopn2_magic1, 11) == 0) { version = 1; } else if(memcmp(cursor, wopn2_magic2, 11) != 0) { SET_ERROR(WOPN_ERR_BAD_MAGIC); return NULL; } GO_FORWARD(11); } if (version == 0) {/* Version code */ if(length < 2) { SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); return NULL; } version = toUint16LE(cursor); if(version > wopn_latest_version) { SET_ERROR(WOPN_ERR_NEWER_VERSION); return NULL; } GO_FORWARD(2); } {/* Header of WOPN */ uint8_t head[5]; if(length < 5) { SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); return NULL; } memcpy(head, cursor, 5); count_melodic_banks = toUint16BE(head); count_percussive_banks = toUint16BE(head + 2); GO_FORWARD(5); outFile = WOPN_Init(count_melodic_banks, count_percussive_banks); if(!outFile) { SET_ERROR(WOPN_ERR_OUT_OF_MEMORY); return NULL; } outFile->version = version; outFile->lfo_freq = head[4] & 0xf; if(version >= 2) outFile->chip_type = (head[4] >> 4) & 1; outFile->volume_model = 0; } bankslots_sizes[0] = count_melodic_banks; bankslots[0] = outFile->banks_melodic; bankslots_sizes[1] = count_percussive_banks; bankslots[1] = outFile->banks_percussive; if(version >= 2) /* Bank names and LSB/MSB titles */ { for(i = 0; i < 2; i++) { for(j = 0; j < bankslots_sizes[i]; j++) { if(length < 34) { SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); return NULL; } strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32); bankslots[i][j].bank_name[32] = '\0'; bankslots[i][j].bank_midi_lsb = cursor[32]; bankslots[i][j].bank_midi_msb = cursor[33]; GO_FORWARD(34); } } } {/* Read instruments data */ uint16_t insSize = 0; if(version > 1) insSize = WOPN_INST_SIZE_V2; else insSize = WOPN_INST_SIZE_V1; for(i = 0; i < 2; i++) { if(length < (insSize * 128) * (size_t)bankslots_sizes[i]) { SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); return NULL; } for(j = 0; j < bankslots_sizes[i]; j++) { for(k = 0; k < 128; k++) { WOPNInstrument *ins = &bankslots[i][j].ins[k]; WOPN_parseInstrument(ins, cursor, version, 1); GO_FORWARD(insSize); } } } } #undef GO_FORWARD #undef SET_ERROR return outFile; } int WOPN_LoadInstFromMem(OPNIFile *file, void *mem, size_t length) { uint16_t version = 0; uint8_t *cursor = (uint8_t *)mem; uint16_t ins_size; if(!cursor) return WOPN_ERR_NULL_POINTER; #define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } {/* Magic number */ if(length < 11) return WOPN_ERR_UNEXPECTED_ENDING; if(memcmp(cursor, opni_magic1, 11) == 0) version = 1; else if(memcmp(cursor, opni_magic2, 11) != 0) return WOPN_ERR_BAD_MAGIC; GO_FORWARD(11); } if (version == 0) {/* Version code */ if(length < 2) return WOPN_ERR_UNEXPECTED_ENDING; version = toUint16LE(cursor); if(version > wopn_latest_version) return WOPN_ERR_NEWER_VERSION; GO_FORWARD(2); } file->version = version; {/* is drum flag */ if(length < 1) return WOPN_ERR_UNEXPECTED_ENDING; file->is_drum = *cursor; GO_FORWARD(1); } if(version > 1) /* Skip sounding delays are not part of single-instrument file * two sizes of uint16_t will be subtracted */ ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); else ins_size = WOPN_INST_SIZE_V1; if(length < ins_size) return WOPN_ERR_UNEXPECTED_ENDING; WOPN_parseInstrument(&file->inst, cursor, version, 0); GO_FORWARD(ins_size); return WOPN_ERR_OK; #undef GO_FORWARD } size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version) { size_t final_size = 0; size_t ins_size = 0; if(version == 0) version = wopn_latest_version; if(!file) return 0; final_size += 11 + 2 + 2 + 2 + 1; /* * Magic number, * Version, * Count of melodic banks, * Count of percussive banks, * Chip specific flags */ if(version >= 2) { /* Melodic banks meta-data */ final_size += (32 + 1 + 1) * file->banks_count_melodic; /* Percussive banks meta-data */ final_size += (32 + 1 + 1) * file->banks_count_percussion; } if(version >= 2) ins_size = WOPN_INST_SIZE_V2; else ins_size = WOPN_INST_SIZE_V1; /* Melodic instruments */ final_size += (ins_size * 128) * file->banks_count_melodic; /* Percussive instruments */ final_size += (ins_size * 128) * file->banks_count_percussion; return final_size; } size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version) { size_t final_size = 0; size_t ins_size = 0; if(version == 0) version = wopn_latest_version; if(!file) return 0; final_size += 11 + 1; /* * Magic number, * is percussive instrument */ /* Version */ if (version > 1) final_size += 2; if(version > 1) /* Skip sounding delays are not part of single-instrument file * two sizes of uint16_t will be subtracted */ ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); else ins_size = WOPN_INST_SIZE_V1; final_size += ins_size; return final_size; } int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm) { uint8_t *cursor = (uint8_t *)dest_mem; uint16_t ins_size = 0; uint16_t i, j, k; uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic; uint16_t banks_percussive = force_gm ? 1 : file->banks_count_percussion; WOPNBank *bankslots[2]; uint16_t bankslots_sizes[2]; if(version == 0) version = wopn_latest_version; #define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } if(length < 11) return WOPN_ERR_UNEXPECTED_ENDING; if(version > 1) memcpy(cursor, wopn2_magic2, 11); else memcpy(cursor, wopn2_magic1, 11); GO_FORWARD(11); if(version > 1) { if(length < 2) return WOPN_ERR_UNEXPECTED_ENDING; fromUint16LE(version, cursor); GO_FORWARD(2); } if(length < 2) return WOPN_ERR_UNEXPECTED_ENDING; fromUint16BE(banks_melodic, cursor); GO_FORWARD(2); if(length < 2) return WOPN_ERR_UNEXPECTED_ENDING; fromUint16BE(banks_percussive, cursor); GO_FORWARD(2); if(length < 1) return WOPN_ERR_UNEXPECTED_ENDING; cursor[0] = file->lfo_freq & 0xf; if (version >= 2) cursor[0] |= (file->chip_type & 1) << 4; GO_FORWARD(1); bankslots[0] = file->banks_melodic; bankslots_sizes[0] = banks_melodic; bankslots[1] = file->banks_percussive; bankslots_sizes[1] = banks_percussive; if(version >= 2) { for(i = 0; i < 2; i++) { for(j = 0; j < bankslots_sizes[i]; j++) { if(length < 34) return WOPN_ERR_UNEXPECTED_ENDING; strncpy((char*)cursor, bankslots[i][j].bank_name, 32); cursor[32] = bankslots[i][j].bank_midi_lsb; cursor[33] = bankslots[i][j].bank_midi_msb; GO_FORWARD(34); } } } {/* Write instruments data */ if(version >= 2) ins_size = WOPN_INST_SIZE_V2; else ins_size = WOPN_INST_SIZE_V1; for(i = 0; i < 2; i++) { if(length < (ins_size * 128) * (size_t)bankslots_sizes[i]) return WOPN_ERR_UNEXPECTED_ENDING; for(j = 0; j < bankslots_sizes[i]; j++) { for(k = 0; k < 128; k++) { WOPNInstrument *ins = &bankslots[i][j].ins[k]; WOPN_writeInstrument(ins, cursor, version, 1); GO_FORWARD(ins_size); } } } } return WOPN_ERR_OK; #undef GO_FORWARD } int WOPN_SaveInstToMem(OPNIFile *file, void *dest_mem, size_t length, uint16_t version) { uint8_t *cursor = (uint8_t *)dest_mem; uint16_t ins_size; if(!cursor) return WOPN_ERR_NULL_POINTER; if(version == 0) version = wopn_latest_version; #define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } {/* Magic number */ if(length < 11) return WOPN_ERR_UNEXPECTED_ENDING; if(version > 1) memcpy(cursor, opni_magic2, 11); else memcpy(cursor, opni_magic1, 11); GO_FORWARD(11); } if (version > 1) {/* Version code */ if(length < 2) return WOPN_ERR_UNEXPECTED_ENDING; fromUint16LE(version, cursor); GO_FORWARD(2); } {/* is drum flag */ if(length < 1) return WOPN_ERR_UNEXPECTED_ENDING; *cursor = file->is_drum; GO_FORWARD(1); } if(version > 1) /* Skip sounding delays are not part of single-instrument file * two sizes of uint16_t will be subtracted */ ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); else ins_size = WOPN_INST_SIZE_V1; if(length < ins_size) return WOPN_ERR_UNEXPECTED_ENDING; WOPN_writeInstrument(&file->inst, cursor, version, 0); GO_FORWARD(ins_size); return WOPN_ERR_OK; #undef GO_FORWARD } BambooTracker-0.4.6/BambooTracker/format/wopn_file.h000066400000000000000000000210451401124043500223660ustar00rootroot00000000000000/* * Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup * * Copyright (c) 2018 Vitaly Novichkov * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef WOPN_FILE_H #define WOPN_FILE_H #include #include #ifdef __cplusplus extern "C" { #endif #if !defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) \ || defined(__STRICT_ANSI__) || !defined(__cplusplus) typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed short int int16_t; typedef unsigned short int uint16_t; #endif /* Type of chip for which a bank has been designed */ typedef enum WOPN_ChipType { /* The Yamaha OPN2 chip, alias YM2612 YM3438 */ WOPN_Chip_OPN2 = 0, /* The Yamaha OPNA chip, alias YM2608 */ WOPN_Chip_OPNA = 1 } WOPN_ChipType; /* Volume scaling model implemented in the libOPNMIDI */ typedef enum WOPN_VolumeModel { WOPN_VM_Generic = 0 } WOPN_VolumeModel; typedef enum WOPN_InstrumentFlags { /* Is pseudo eight-operator (two 4-operator voices) instrument */ WOPN_Ins_Pseudo8op = 0x01, /* Is a blank instrument entry */ WOPN_Ins_IsBlank = 0x02, /* Mask of the flags range */ WOPN_Ins_ALL_MASK = 0x03 } WOPN_InstrumentFlags; /* Error codes */ typedef enum WOPN_ErrorCodes { WOPN_ERR_OK = 0, /* Magic number is not maching */ WOPN_ERR_BAD_MAGIC, /* Too short file */ WOPN_ERR_UNEXPECTED_ENDING, /* Zero banks count */ WOPN_ERR_INVALID_BANKS_COUNT, /* Version of file is newer than supported by current version of library */ WOPN_ERR_NEWER_VERSION, /* Out of memory */ WOPN_ERR_OUT_OF_MEMORY, /* Given null pointer memory data */ WOPN_ERR_NULL_POINTER } WOPN_ErrorCodes; /* OPN2 Oerators data */ typedef struct WOPNOperator { /* Detune and frequency multiplication register data */ uint8_t dtfm_30; /* Total level register data */ uint8_t level_40; /* Rate scale and attack register data */ uint8_t rsatk_50; /* Amplitude modulation enable and Decay-1 register data */ uint8_t amdecay1_60; /* Decay-2 register data */ uint8_t decay2_70; /* Sustain and Release register data */ uint8_t susrel_80; /* SSG-EG register data */ uint8_t ssgeg_90; } WOPNOperator; /* Instrument entry */ typedef struct WOPNInstrument { /* Title of the instrument */ char inst_name[34]; /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ int16_t note_offset; /* Reserved */ int8_t midi_velocity_offset; /* Percussion MIDI base tone number at which this drum will be played */ uint8_t percussion_key_number; /* Enum WOPN_InstrumentFlags */ uint8_t inst_flags; /* Feedback and Algorithm register data */ uint8_t fbalg; /* LFO Sensitivity register data */ uint8_t lfosens; /* Operators register data */ WOPNOperator operators[4]; /* Millisecond delay of sounding while key is on */ uint16_t delay_on_ms; /* Millisecond delay of sounding after key off */ uint16_t delay_off_ms; } WOPNInstrument; /* Bank entry */ typedef struct WOPNBank { /* Name of bank */ char bank_name[33]; /* MIDI Bank LSB code */ uint8_t bank_midi_lsb; /* MIDI Bank MSB code */ uint8_t bank_midi_msb; /* Instruments data of this bank */ WOPNInstrument ins[128]; } WOPNBank; /* Instrument data file */ typedef struct OPNIFile { /* Version of instrument file */ uint16_t version; /* Is this a percussion instrument */ uint8_t is_drum; /* Instrument data */ WOPNInstrument inst; } OPNIFile; /* Bank data file */ typedef struct WOPNFile { /* Version of bank file */ uint16_t version; /* Count of melodic banks in this file */ uint16_t banks_count_melodic; /* Count of percussion banks in this file */ uint16_t banks_count_percussion; /* Chip global LFO enable flag and frequency register data */ uint8_t lfo_freq; /* Chip type this bank is designed for */ uint8_t chip_type; /* Reserved (Enum WOPN_VolumeModel) */ uint8_t volume_model; /* dynamically allocated data Melodic banks array */ WOPNBank *banks_melodic; /* dynamically allocated data Percussive banks array */ WOPNBank *banks_percussive; } WOPNFile; /** * @brief Initialize blank WOPN data structure with allocated bank data * @param melodic_banks Count of melodic banks * @param percussive_banks Count of percussive banks * @return pointer to heap-allocated WOPN data structure or NULL when out of memory or incorrectly given banks counts */ extern WOPNFile *WOPN_Init(uint16_t melodic_banks, uint16_t percussive_banks); /** * @brief Clean up WOPN data file (all allocated bank arrays will be fried too) * @param file pointer to heap-allocated WOPN data structure */ extern void WOPN_Free(WOPNFile *file); /** * @brief Compare two bank entries * @param bank1 First bank * @param bank2 Second bank * @return 1 if banks are equal or 0 if there are different */ extern int WOPN_BanksCmp(const WOPNFile *bank1, const WOPNFile *bank2); /** * @brief Load WOPN bank file from the memory. * WOPN data structure will be allocated. (don't forget to clear it with WOPN_Free() after use!) * @param mem Pointer to memory block contains raw WOPN bank file data * @param length Length of given memory block * @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes. * @return Heap-allocated WOPN file data structure or NULL if any error has occouped */ extern WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error); /** * @brief Load WOPI instrument file from the memory. * You must allocate OPNIFile structure by yourself and give the pointer to it. * @param file Pointer to destination OPNIFile structure to fill it with parsed data. * @param mem Pointer to memory block contains raw WOPI instrument file data * @param length Length of given memory block * @return 0 if no errors occouped, or an error code of WOPN_ErrorCodes enumeration */ extern int WOPN_LoadInstFromMem(OPNIFile *file, void *mem, size_t length); /** * @brief Calculate the size of the output memory block * @param file Heap-allocated WOPN file data structure * @param version Destination version of the file * @return Size of the raw WOPN file data */ extern size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version); /** * @brief Calculate the size of the output memory block * @param file Pointer to WOPI file data structure * @param version Destination version of the file * @return Size of the raw WOPI file data */ extern size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version); /** * @brief Write raw WOPN into given memory block * @param file Heap-allocated WOPN file data structure * @param dest_mem Destination memory block pointer * @param length Length of destination memory block * @param version Wanted WOPN version * @param force_gm Force GM set in saved bank file * @return Error code or 0 on success */ extern int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm); /** * @brief Write raw WOPI into given memory block * @param file Pointer to WOPI file data structure * @param dest_mem Destination memory block pointer * @param length Length of destination memory block * @param version Wanted WOPI version * @return Error code or 0 on success */ extern int WOPN_SaveInstToMem(OPNIFile *file, void *dest_mem, size_t length, uint16_t version); #ifdef __cplusplus } #endif #endif /* WOPN_FILE_H */ BambooTracker-0.4.6/BambooTracker/gui/000077500000000000000000000000001401124043500175255ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/bookmark_manager_form.cpp000066400000000000000000000177141401124043500245650ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "bookmark_manager_form.hpp" #include "ui_bookmark_manager_form.h" #include #include #include "song.hpp" #include "gui/gui_utils.hpp" BookmarkManagerForm::BookmarkManagerForm(std::weak_ptr core, bool showHex, QWidget *parent) : QWidget(parent), ui(new Ui::BookmarkManagerForm), bt_(core), curSong_(core.lock()->getCurrentSongNumber()) { ui->setupUi(this); setWindowFlags(windowFlags() & ~(Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint)); setNumberSettings(showHex); initList(); ui->orderSpinBox->setDisplayIntegerBase(numBase_); ui->stepSpinBox->setDisplayIntegerBase(numBase_); insSc_ = std::make_unique(Qt::Key_Insert, ui->listWidget, nullptr, nullptr, Qt::WidgetShortcut); QObject::connect(insSc_.get(), &QShortcut::activated, this, &BookmarkManagerForm::on_createPushButton_clicked); delSc_ = std::make_unique(Qt::Key_Delete, ui->listWidget, nullptr, nullptr, Qt::WidgetShortcut); QObject::connect(delSc_.get(), &QShortcut::activated, this, &BookmarkManagerForm::on_removePushButton_clicked); mvUpSc_ = std::make_unique(Qt::CTRL + Qt::Key_Up, ui->listWidget, nullptr, nullptr, Qt::WidgetShortcut); QObject::connect(mvUpSc_.get(), &QShortcut::activated, this, &BookmarkManagerForm::on_upToolButton_clicked); mvDnSc_ = std::make_unique(Qt::CTRL + Qt::Key_Down, ui->listWidget, nullptr, nullptr, Qt::WidgetShortcut); QObject::connect(mvDnSc_.get(), &QShortcut::activated, this, &BookmarkManagerForm::on_downToolButton_clicked); } BookmarkManagerForm::~BookmarkManagerForm() { delete ui; } void BookmarkManagerForm::initList() { int size = static_cast(bt_.lock()->getBookmarkSize(curSong_)); for (int i = 0; i < size; ++i) { Bookmark bm = bt_.lock()->getBookmark(curSong_, i); addBookmark(gui_utils::utf8ToQString(bm.name), bm.order, bm.step, true); } } void BookmarkManagerForm::addBookmark(QString name, int order, int step, bool onlyUi) { ui->listWidget->addItem(createText(name, order, step)); if (!onlyUi) { bt_.lock()->addBookmark(curSong_, name.toUtf8().toStdString(), order, step); } } void BookmarkManagerForm::removeBookmark(int i) { delete ui->listWidget->takeItem(i); bt_.lock()->removeBookmark(curSong_, i); } QString BookmarkManagerForm::createText(QString name, int order, int step) { auto pos = QString("(%1,%2)").arg(order, numWidth_, numBase_, QChar('0')) .arg(step, numWidth_, numBase_, QChar('0')).toUpper(); return ((name.isEmpty() ? tr("Bookmark") : name) + " " + pos); } void BookmarkManagerForm::sortList(bool byPos) { int row = ui->listWidget->currentRow(); QString text = (row == -1) ? "" : ui->listWidget->currentItem()->text(); ui->listWidget->clear(); if (byPos) bt_.lock()->sortBookmarkByPosition(curSong_); else bt_.lock()->sortBookmarkByName(curSong_); initList(); for (int i = 0; i < ui->listWidget->count(); ++i) { QListWidgetItem* item = ui->listWidget->item(i); if (item->text() == text) { ui->listWidget->setCurrentRow(i); break; } } emit modified(); } void BookmarkManagerForm::onCurrentSongNumberChanged() { curSong_ = bt_.lock()->getCurrentSongNumber(); ui->listWidget->clear(); initList(); } void BookmarkManagerForm::onConfigurationChanged(bool showHex) { if ((showHex && numBase_ == 16) || (!showHex && numBase_ == 10)) return; setNumberSettings(showHex); for (int i = 0; i < ui->listWidget->count(); ++i) { Bookmark bm = bt_.lock()->getBookmark(curSong_, i); ui->listWidget->item(i)->setText(createText(gui_utils::utf8ToQString(bm.name), bm.order, bm.step)); } ui->orderSpinBox->setDisplayIntegerBase(numBase_); ui->stepSpinBox->setDisplayIntegerBase(numBase_); } void BookmarkManagerForm::onBookmarkToggleRequested(int order, int step) { std::vector idcs = bt_.lock()->findBookmarks(curSong_, order, step); if (idcs.empty()) { if (bt_.lock()->getBookmarkSize(curSong_) == 127) return; // Maximum size int i = static_cast(bt_.lock()->getBookmarkSize(curSong_)); addBookmark(tr("Bookmark %1").arg(i), order, step); } else { for (auto&& it = idcs.rbegin(); it != idcs.rend(); ++it) { removeBookmark(*it); // Remove from back to remain the position } } emit modified(); } void BookmarkManagerForm::onBookmarkJumpRequested(bool toNext, int order, int step) { if (!bt_.lock()->getBookmarkSize(curSong_)) return; Bookmark bm = toNext ? bt_.lock()->getNextBookmark(curSong_, order, step) : bt_.lock()->getPreviousBookmark(curSong_, order, step); ui->listWidget->setCurrentRow(bt_.lock()->findBookmarks(curSong_, bm.order, bm.step).front()); emit positionJumpRequested(bm.order, bm.step); } void BookmarkManagerForm::on_createPushButton_clicked() { if (bt_.lock()->getBookmarkSize(curSong_) == 127) return; // Maximum size addBookmark(ui->nameLineEdit->text(), ui->orderSpinBox->value(), ui->stepSpinBox->value()); emit modified(); } void BookmarkManagerForm::on_removePushButton_clicked() { int row = ui->listWidget->currentRow(); if (row == -1) return; removeBookmark(row); emit modified(); } void BookmarkManagerForm::on_clearPushButton_clicked() { if (!ui->listWidget->count()) return; ui->listWidget->clear(); bt_.lock()->clearBookmark(curSong_); emit modified(); } void BookmarkManagerForm::on_upToolButton_clicked() { int row = ui->listWidget->currentRow(); if (row < 1) return; bt_.lock()->swapBookmarks(curSong_, row, row - 1); ui->listWidget->clear(); initList(); ui->listWidget->setCurrentRow(row - 1); emit modified(); } void BookmarkManagerForm::on_downToolButton_clicked() { int row = ui->listWidget->currentRow(); if (row == -1 || ui->listWidget->count() - 2 < row) return; bt_.lock()->swapBookmarks(curSong_, row, row + 1); ui->listWidget->clear(); initList(); ui->listWidget->setCurrentRow(row + 1); emit modified(); } void BookmarkManagerForm::on_positionPushButton_clicked() { sortList(true); } void BookmarkManagerForm::on_namePushButton_clicked() { sortList(false); } void BookmarkManagerForm::on_listWidget_currentRowChanged(int currentRow) { if (currentRow == -1) return; Bookmark bm = bt_.lock()->getBookmark(curSong_, currentRow); ui->nameLineEdit->setText(gui_utils::utf8ToQString(bm.name)); ui->orderSpinBox->setValue(bm.order); ui->stepSpinBox->setValue(bm.step); } void BookmarkManagerForm::on_updatePushButton_clicked() { int row = ui->listWidget->currentRow(); if (row == -1) return; QString name = ui->nameLineEdit->text(); int order = ui->orderSpinBox->value(); int step = ui->stepSpinBox->value(); ui->listWidget->item(row)->setText(createText(name, order, step)); bt_.lock()->changeBookmark(curSong_, row, name.toUtf8().toStdString(), order, step); emit modified(); } void BookmarkManagerForm::on_listWidget_itemDoubleClicked(QListWidgetItem *item) { int row = ui->listWidget->row(item); Bookmark bm = bt_.lock()->getBookmark(curSong_, row); emit positionJumpRequested(bm.order, bm.step); } BambooTracker-0.4.6/BambooTracker/gui/bookmark_manager_form.hpp000066400000000000000000000054701401124043500245660ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef BOOKMARK_MANAGER_FORM_HPP #define BOOKMARK_MANAGER_FORM_HPP #include #include #include #include #include #include "bamboo_tracker.hpp" namespace Ui { class BookmarkManagerForm; } class BookmarkManagerForm : public QWidget { Q_OBJECT public: BookmarkManagerForm(std::weak_ptr core, bool showHex, QWidget *parent = nullptr); ~BookmarkManagerForm() override; signals: void positionJumpRequested(int order, int step); void modified(); public slots: void onCurrentSongNumberChanged(); void onConfigurationChanged(bool showHex); void onBookmarkToggleRequested(int order, int step); void onBookmarkJumpRequested(bool toNext, int order, int step); private slots: void on_createPushButton_clicked(); void on_removePushButton_clicked(); void on_clearPushButton_clicked(); void on_upToolButton_clicked(); void on_downToolButton_clicked(); void on_positionPushButton_clicked(); void on_namePushButton_clicked(); void on_listWidget_currentRowChanged(int currentRow); void on_updatePushButton_clicked(); void on_listWidget_itemDoubleClicked(QListWidgetItem *item); private: Ui::BookmarkManagerForm *ui; std::weak_ptr bt_; int curSong_; int numWidth_, numBase_; std::unique_ptr insSc_, delSc_, mvUpSc_, mvDnSc_; void initList(); inline void setNumberSettings(bool showHex) { if (showHex) { numWidth_ = 2; numBase_ = 16; } else { numWidth_ = 3; numBase_ = 10; } } void addBookmark(QString name, int order, int step, bool onlyUi = false); void removeBookmark(int i); QString createText(QString name, int order, int step); void sortList(bool byPos); }; #endif // BOOKMARK_MANAGER_FORM_HPP BambooTracker-0.4.6/BambooTracker/gui/bookmark_manager_form.ui000066400000000000000000000154761401124043500244230ustar00rootroot00000000000000 BookmarkManagerForm 0 0 300 320 Bookmark Manager Sort by 9 9 9 9 Position Name 9 9 0 0 ... Qt::UpArrow 0 0 ... Qt::DownArrow Remove Clear All 0 0 Qt::Vertical 20 40 0 0 Bookmark editor 255 Order Name 127 Step Qt::Horizontal 40 20 0 0 Create New Update listWidget upToolButton downToolButton removePushButton clearPushButton positionPushButton namePushButton nameLineEdit orderSpinBox stepSpinBox createPushButton updatePushButton BambooTracker-0.4.6/BambooTracker/gui/color_palette.cpp000066400000000000000000000143431401124043500230720ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "color_palette.hpp" ColorPalette::ColorPalette() { // Instrument list ilistTextColor = QColor::fromRgb(255, 255, 255, 255); ilistBackColor = QColor::fromRgb(0, 0, 0, 255); ilistSelBackColor = QColor::fromRgb(110, 90, 140, 255); ilistHovBackColor = QColor::fromRgb(255, 255, 255, 75); ilistHovSelBackColor = QColor::fromRgb(140, 120, 170, 255); // Instrument editor instFMEnvLine1Color = QColor::fromRgb(242, 38, 19, 255); instFMEnvLine2Color = QColor::fromRgb(46, 204, 113, 255); instFMEnvLine3Color = QColor::fromRgb(38, 38, 255, 255); instFMEnvGridColor = QColor::fromRgb(40, 40, 40, 63); instFMEnvBackColor = QColor::fromRgb(255, 255, 255, 0); instFMEnvBorderColor = QColor::fromRgb(125, 125, 125, 255); instFMAlForeColor = QColor::fromRgb(107, 185, 240, 255); instFMAlBackColor = QColor::fromRgb(255, 255, 255, 0); instSeqLoopBackColor = QColor::fromRgb(25, 25, 25, 255); instSeqReleaseBackColor = QColor::fromRgb(0, 0, 0, 255); instSeqLoopColor = QColor::fromRgb(210, 40, 180, 127); instSeqReleaseColor = QColor::fromRgb(40, 170, 200, 127); instSeqLoopEdgeColor = QColor::fromRgb(180, 20, 180, 127); instSeqReleaseEdgeColor = QColor::fromRgb(40, 170, 150, 127); instSeqTagColor = QColor::fromRgb(255, 255, 255, 255); instSeqHovColor = QColor::fromRgb(255, 255, 255, 63); instSeqLoopTextColor = QColor::fromRgb(24, 223, 172, 255); instSeqReleaseTextColor = QColor::fromRgb(24, 223, 172, 255); instSeqCellColor = QColor::fromRgb(38, 183, 173, 255); instSeqCellTextColor = QColor::fromRgb(255, 255, 255); instSeqBorderColor = QColor::fromRgb(50, 50, 50, 255); instSeqMaskColor = QColor::fromRgb(0, 0, 0, 128); instSeqOddColColor = QColor::fromRgb(255, 255, 255, 31); // ADPCM sample editor instADPCMMemAllColor = QColor::fromRgb(210, 40, 180, 150); instADPCMMemCurColor = QColor::fromRgb(210, 40, 180, 255); instADPCMMemBackColor = QColor::fromRgb(0, 0, 0, 255); instADPCMSampViewForeColor = QColor::fromRgb(38, 183, 173, 255); instADPCMSampViewBackColor = QColor::fromRgb(0, 0, 0, 255); instADPCMSampViewCenterColor = QColor::fromRgb(63, 63, 63, 255); instADPCMSampViewGridColor = QColor::fromRgb(63, 63, 63, 170); instADPCMSampViewDrawColor = QColor::fromRgb(255, 0, 0, 255); instADPCMSampViewDirectDrawColor = QColor::fromRgb(255, 150, 0, 255); // Tone/Noise editor tnToneCellColor = QColor::fromRgb(225, 209, 47, 255); tnToneTextColor = QColor::fromRgb(255, 255, 126, 255); tnNoiseCellColor = QColor::fromRgb(210, 40, 180, 255); tnNoiseTextColor = QColor::fromRgb(240, 110, 220, 255); tnToneBackColor = QColor::fromRgb(0, 0, 0, 255); tnNoiseBackColor = QColor::fromRgb(25, 25, 25, 255); // Order list odrDefTextColor = QColor::fromRgb(180, 180, 180, 255); odrDefRowColor = QColor::fromRgb(40, 40, 80, 255); odrCurTextColor = QColor::fromRgb(255, 255, 255, 255); odrCurRowColor = QColor::fromRgb(110, 90, 140, 255); odrCurEditRowColor = QColor::fromRgb(140, 90, 110, 255); odrCurCellColor = QColor::fromRgb(255, 255, 255, 127); odrPlayRowColor = QColor::fromRgb(90, 90, 140, 255); odrSelCellColor = QColor::fromRgb(100, 100, 200, 192); odrHovCellColor = QColor::fromRgb(255, 255, 255, 64); odrRowNumColor = QColor::fromRgb(255, 200, 180, 255); odrHeaderTextColor = QColor::fromRgb(240, 240, 200, 255); odrHeaderRowColor = QColor::fromRgb(60, 60, 60, 255); odrBorderColor = QColor::fromRgb(0, 0, 0, 255); odrHeaderBorderColor = QColor::fromRgb(0, 0, 0, 255); odrBackColor = QColor::fromRgb(0, 0, 0, 255); odrUnfocusedShadowColor = QColor::fromRgb(0, 0, 0, 47); // Pattern editor ptnDefTextColor = QColor::fromRgb(180, 180, 180, 255); ptnDefStepColor = QColor::fromRgb(0, 0, 40, 255); ptnHl1StepColor = QColor::fromRgb(30, 40, 70, 255); ptnHl2StepColor = QColor::fromRgb(60, 60, 100, 255); ptnCurTextColor = QColor::fromRgb(255, 255, 255, 255); ptnCurStepColor = QColor::fromRgb(110, 90, 140, 255); ptnCurEditStepColor = QColor::fromRgb(140, 90, 110, 255); ptnCurCellColor = QColor::fromRgb(255, 255, 255, 127); ptnPlayStepColor = QColor::fromRgb(90, 90, 140, 255); ptnSelCellColor = QColor::fromRgb(100, 100, 200, 192); ptnHovCellColor = QColor::fromRgb(255, 255, 255, 64); ptnDefStepNumColor = QColor::fromRgb(255, 200, 180, 255); ptnHl1StepNumColor = QColor::fromRgb(255, 140, 160, 255); ptnHl2StepNumColor = QColor::fromRgb(255, 140, 160, 255); ptnNoteColor = QColor::fromRgb(210, 230, 64, 255); ptnInstColor = QColor::fromRgb(82, 179, 217, 255); ptnVolColor = QColor::fromRgb(226, 156, 80, 255); ptnEffColor = QColor::fromRgb(42, 187, 155, 255); ptnErrorColor = QColor::fromRgb(255, 0, 0, 255); ptnHeaderTextColor = QColor::fromRgb(240, 240, 200, 255); ptnHeaderRowColor = QColor::fromRgb(60, 60, 60, 255); ptnMaskColor = QColor::fromRgb(0, 0, 0, 127); ptnBorderColor = QColor::fromRgb(0, 0, 0, 255); ptnHeaderBorderColor = QColor::fromRgb(0, 0, 0, 255); ptnMuteColor = QColor::fromRgb(255, 0, 0, 255); ptnUnmuteColor = QColor::fromRgb(0, 255, 0, 255); ptnBackColor = QColor::fromRgb(0, 0, 0, 255); ptnMarkerColor = QColor::fromRgb(255, 255, 255, 128); ptnUnfocusedShadowColor = QColor::fromRgb(0, 0, 0, 47); // Wave visual wavBackColor = QColor::fromRgb(0, 0, 33, 255); wavDrawColor = QColor::fromRgb(82, 179, 217, 255); } BambooTracker-0.4.6/BambooTracker/gui/color_palette.hpp000066400000000000000000000067531401124043500231050ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef COLOR_PALETTE_HPP #define COLOR_PALETTE_HPP #include class ColorPalette { public: ColorPalette(); // Instrument list QColor ilistTextColor, ilistBackColor; QColor ilistSelBackColor; QColor ilistHovBackColor; QColor ilistHovSelBackColor; // Instrument editor QColor instFMEnvLine1Color, instFMEnvLine2Color, instFMEnvLine3Color; QColor instFMEnvGridColor; QColor instFMEnvBackColor, instFMEnvBorderColor; QColor instFMAlForeColor, instFMAlBackColor; QColor instSeqTagColor; QColor instSeqHovColor; QColor instSeqLoopBackColor, instSeqLoopColor, instSeqLoopEdgeColor; QColor instSeqReleaseBackColor, instSeqReleaseColor, instSeqReleaseEdgeColor; QColor instSeqLoopTextColor, instSeqReleaseTextColor; QColor instSeqCellColor, instSeqCellTextColor; QColor instSeqBorderColor; QColor instSeqMaskColor; QColor instSeqOddColColor; // ADPCM sample editor QColor instADPCMMemAllColor, instADPCMMemCurColor, instADPCMMemBackColor; QColor instADPCMSampViewForeColor, instADPCMSampViewBackColor, instADPCMSampViewCenterColor; QColor instADPCMSampViewGridColor, instADPCMSampViewDrawColor, instADPCMSampViewDirectDrawColor; // Tone/Noise editor QColor tnToneCellColor, tnToneTextColor; QColor tnNoiseCellColor, tnNoiseTextColor; QColor tnToneBackColor, tnNoiseBackColor; // Order list QColor odrDefTextColor, odrDefRowColor; QColor odrCurTextColor, odrCurRowColor; QColor odrCurEditRowColor; QColor odrCurCellColor; QColor odrPlayRowColor; QColor odrSelCellColor; QColor odrHovCellColor; QColor odrRowNumColor; QColor odrHeaderTextColor, odrHeaderRowColor; QColor odrBorderColor, odrHeaderBorderColor; QColor odrBackColor; QColor odrUnfocusedShadowColor; // Pattern editor QColor ptnDefTextColor, ptnDefStepColor, ptnHl1StepColor, ptnHl2StepColor; QColor ptnCurTextColor, ptnCurStepColor, ptnCurEditStepColor, ptnCurCellColor; QColor ptnPlayStepColor; QColor ptnSelCellColor; QColor ptnHovCellColor; QColor ptnDefStepNumColor, ptnHl1StepNumColor, ptnHl2StepNumColor; QColor ptnNoteColor, ptnInstColor, ptnVolColor, ptnEffColor; QColor ptnErrorColor; QColor ptnHeaderTextColor, ptnHeaderRowColor; QColor ptnMaskColor; QColor ptnBorderColor, ptnHeaderBorderColor; QColor ptnMuteColor, ptnUnmuteColor; QColor ptnBackColor; QColor ptnMarkerColor; QColor ptnUnfocusedShadowColor; // Wave visual QColor wavBackColor; QColor wavDrawColor; }; #endif // COLOR_PALETTE_HPP BambooTracker-0.4.6/BambooTracker/gui/color_palette_handler.cpp000066400000000000000000000316701401124043500245710ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "color_palette_handler.hpp" #include #include // config path (*nix): ~/.config//.ini const QString ColorPaletteHandler::organization = "BambooTracker"; const QString ColorPaletteHandler::application = "BambooTracker"; ColorPaletteHandler::ColorPaletteHandler() {} bool ColorPaletteHandler::savePalette(const ColorPalette* const palette) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ColorPaletteHandler::organization, ColorPaletteHandler::application); save(settings, palette); return true; } catch (...) { return false; } } bool ColorPaletteHandler::savePalette(QString file, const ColorPalette* const palette) { try { QSettings settings(file, QSettings::IniFormat); save(settings, palette); return true; } catch (...) { return false; } } void ColorPaletteHandler::save(QSettings& settings, const ColorPalette* const palette) { settings.beginGroup("PatternEditor"); settings.setValue("defaultStepText", palette->ptnDefTextColor.name(QColor::HexArgb)); settings.setValue("defaultStepBackground", palette->ptnDefStepColor.name(QColor::HexArgb)); settings.setValue("highlightedStep1Background", palette->ptnHl1StepColor.name(QColor::HexArgb)); settings.setValue("highlightedStep2Background", palette->ptnHl2StepColor.name(QColor::HexArgb)); settings.setValue("currentStepText", palette->ptnCurTextColor.name(QColor::HexArgb)); settings.setValue("currentStepBackground", palette->ptnCurStepColor.name(QColor::HexArgb)); settings.setValue("currentEditingStepBackground", palette->ptnCurEditStepColor.name(QColor::HexArgb)); settings.setValue("currentCellBackground", palette->ptnCurCellColor.name(QColor::HexArgb)); settings.setValue("currentPlayingStepBackground", palette->ptnPlayStepColor.name(QColor::HexArgb)); settings.setValue("selectionBackground", palette->ptnSelCellColor.name(QColor::HexArgb)); settings.setValue("hoveredCellBackground", palette->ptnHovCellColor.name(QColor::HexArgb)); settings.setValue("defaultStepNumber", palette->ptnDefStepNumColor.name(QColor::HexArgb)); settings.setValue("highlightedStep1Number", palette->ptnHl1StepNumColor.name(QColor::HexArgb)); settings.setValue("highlightedStep2Number", palette->ptnHl2StepNumColor.name(QColor::HexArgb)); settings.setValue("noteText", palette->ptnNoteColor.name(QColor::HexArgb)); settings.setValue("instrumentText", palette->ptnInstColor.name(QColor::HexArgb)); settings.setValue("volumeText", palette->ptnVolColor.name(QColor::HexArgb)); settings.setValue("effectText", palette->ptnEffColor.name(QColor::HexArgb)); settings.setValue("errorText", palette->ptnErrorColor.name(QColor::HexArgb)); settings.setValue("headerText", palette->ptnHeaderTextColor.name(QColor::HexArgb)); settings.setValue("headerBackground", palette->ptnHeaderRowColor.name(QColor::HexArgb)); settings.setValue("mask", palette->ptnMaskColor.name(QColor::HexArgb)); settings.setValue("border", palette->ptnBorderColor.name(QColor::HexArgb)); settings.setValue("headerBorder", palette->ptnHeaderBorderColor.name(QColor::HexArgb)); settings.setValue("mute", palette->ptnMuteColor.name(QColor::HexArgb)); settings.setValue("unmute", palette->ptnUnmuteColor.name(QColor::HexArgb)); settings.setValue("background", palette->ptnBackColor.name(QColor::HexArgb)); settings.setValue("marker", palette->ptnMarkerColor.name(QColor::HexArgb)); settings.setValue("unfocusedShadow", palette->ptnUnfocusedShadowColor.name(QColor::HexArgb)); settings.endGroup(); settings.beginGroup("OrderList"); settings.setValue("defaultRowText", palette->odrDefTextColor.name(QColor::HexArgb)); settings.setValue("defaultRowBackground", palette->odrDefRowColor.name(QColor::HexArgb)); settings.setValue("currentRowText", palette->odrCurTextColor.name(QColor::HexArgb)); settings.setValue("currentRowBackground", palette->odrCurRowColor.name(QColor::HexArgb)); settings.setValue("currentEditingRowBackground", palette->odrCurEditRowColor.name(QColor::HexArgb)); settings.setValue("currentCellBackground", palette->odrCurCellColor.name(QColor::HexArgb)); settings.setValue("currentPlayingRowBackground", palette->odrPlayRowColor.name(QColor::HexArgb)); settings.setValue("selectionBackground", palette->odrSelCellColor.name(QColor::HexArgb)); settings.setValue("hoveredCellBackground", palette->odrHovCellColor.name(QColor::HexArgb)); settings.setValue("rowNumber", palette->odrRowNumColor.name(QColor::HexArgb)); settings.setValue("headerText", palette->odrHeaderTextColor.name(QColor::HexArgb)); settings.setValue("headerBackground", palette->odrHeaderRowColor.name(QColor::HexArgb)); settings.setValue("border", palette->odrBorderColor.name(QColor::HexArgb)); settings.setValue("headerBorder", palette->odrHeaderBorderColor.name(QColor::HexArgb)); settings.setValue("background", palette->odrBackColor.name(QColor::HexArgb)); settings.setValue("unfocusedShadow", palette->odrUnfocusedShadowColor.name(QColor::HexArgb)); settings.endGroup(); settings.beginGroup("InstrumentList"); settings.setValue("defaultText", palette->ilistTextColor.name(QColor::HexArgb)); settings.setValue("background", palette->ilistBackColor.name(QColor::HexArgb)); settings.setValue("selectedBackground", palette->ilistSelBackColor.name(QColor::HexArgb)); settings.setValue("hoveredBackground", palette->ilistHovBackColor.name(QColor::HexArgb)); settings.setValue("selectedHoveredBackground", palette->ilistHovSelBackColor.name(QColor::HexArgb)); settings.endGroup(); settings.beginGroup("Oscilloscope"); settings.setValue("background", palette->wavBackColor.name(QColor::HexArgb)); settings.setValue("foreground", palette->wavDrawColor.name(QColor::HexArgb)); settings.endGroup(); } bool ColorPaletteHandler::loadPalette(ColorPalette* const palette) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ColorPaletteHandler::organization, ColorPaletteHandler::application); load(settings, palette); return true; } catch (...) { return false; } } bool ColorPaletteHandler::loadPalette(QString file, ColorPalette* const palette) { try { QSettings settings(file, QSettings::IniFormat); load(settings, palette); return true; } catch (...) { return false; } } void ColorPaletteHandler::load(QSettings& settings, ColorPalette* const palette) { settings.beginGroup("PatternEditor"); palette->ptnDefTextColor = settings.value("defaultStepText", palette->ptnDefTextColor).value(); palette->ptnDefStepColor = settings.value("defaultStepBackground", palette->ptnDefStepColor).value(); palette->ptnHl1StepColor = settings.value("highlightedStep1Background", palette->ptnHl1StepColor).value(); palette->ptnHl2StepColor = settings.value("highlightedStep2Background", palette->ptnHl2StepColor).value(); palette->ptnCurTextColor = settings.value("currentStepText", palette->ptnCurTextColor).value(); palette->ptnCurStepColor = settings.value("currentStepBackground", palette->ptnCurStepColor).value(); palette->ptnCurEditStepColor = settings.value("currentEditingStepBackground", palette->ptnCurEditStepColor).value(); palette->ptnCurCellColor = settings.value("currentCellBackground", palette->ptnCurCellColor).value(); palette->ptnPlayStepColor = settings.value("currentPlayingStepBackground", palette->ptnPlayStepColor).value(); palette->ptnSelCellColor = settings.value("selectionBackground", palette->ptnSelCellColor).value(); palette->ptnHovCellColor = settings.value("hoveredCellBackground", palette->ptnHovCellColor).value(); palette->ptnDefStepNumColor = settings.value("defaultStepNumber", palette->ptnDefStepNumColor).value(); palette->ptnHl1StepNumColor = settings.value("highlightedStep1Number", palette->ptnHl1StepNumColor).value(); palette->ptnHl2StepNumColor = settings.value("highlightedStep2Number", palette->ptnHl2StepNumColor).value(); palette->ptnNoteColor = settings.value("noteText", palette->ptnNoteColor).value(); palette->ptnInstColor = settings.value("instrumentText", palette->ptnInstColor).value(); palette->ptnVolColor = settings.value("volumeText", palette->ptnVolColor).value(); palette->ptnEffColor = settings.value("effectText", palette->ptnEffColor).value(); palette->ptnErrorColor = settings.value("errorText", palette->ptnErrorColor).value(); palette->ptnHeaderTextColor = settings.value("headerText", palette->ptnHeaderTextColor).value(); palette->ptnHeaderRowColor = settings.value("headerBackground", palette->ptnHeaderRowColor).value(); palette->ptnMaskColor = settings.value("mask", palette->ptnMaskColor).value(); palette->ptnBorderColor = settings.value("border", palette->ptnBorderColor).value(); palette->ptnHeaderBorderColor = settings.value("headerBorder", palette->ptnHeaderBorderColor).value(); palette->ptnMuteColor = settings.value("mute", palette->ptnMuteColor).value(); palette->ptnUnmuteColor = settings.value("unmute", palette->ptnUnmuteColor).value(); palette->ptnBackColor = settings.value("background", palette->ptnBackColor).value(); palette->ptnMarkerColor = settings.value("marker", palette->ptnMarkerColor).value(); palette->ptnUnfocusedShadowColor = settings.value("unfocusedShadow", palette->ptnUnfocusedShadowColor).value(); settings.endGroup(); settings.beginGroup("OrderList"); palette->odrDefTextColor = settings.value("defaultRowText", palette->odrDefTextColor).value(); palette->odrDefRowColor = settings.value("defaultRowBackground", palette->odrDefRowColor).value(); palette->odrCurTextColor = settings.value("currentRowText", palette->odrCurTextColor).value(); palette->odrCurRowColor = settings.value("currentRowBackground", palette->odrCurRowColor).value(); palette->odrCurEditRowColor = settings.value("currentEditingRowBackground", palette->odrCurEditRowColor).value(); palette->odrCurCellColor = settings.value("currentCellBackground", palette->odrCurCellColor).value(); palette->odrPlayRowColor = settings.value("currentPlayingRowBackground", palette->odrPlayRowColor).value(); palette->odrSelCellColor = settings.value("selectionBackground", palette->odrSelCellColor).value(); palette->odrHovCellColor = settings.value("hoveredCellBackground", palette->odrHovCellColor).value(); palette->odrRowNumColor = settings.value("rowNumber", palette->odrRowNumColor).value(); palette->odrHeaderTextColor = settings.value("headerText", palette->odrHeaderTextColor).value(); palette->odrHeaderRowColor = settings.value("headerBackground", palette->odrHeaderRowColor).value(); palette->odrBorderColor = settings.value("border", palette->odrBorderColor).value(); palette->odrHeaderBorderColor = settings.value("headerBorder", palette->odrHeaderBorderColor).value(); palette->odrBackColor = settings.value("background", palette->odrBackColor).value(); palette->odrUnfocusedShadowColor = settings.value("unfocusedShadow", palette->odrUnfocusedShadowColor).value(); settings.endGroup(); settings.beginGroup("InstrumentList"); palette->ilistTextColor = settings.value("defaultText", palette->ilistTextColor).value(); palette->ilistBackColor = settings.value("background", palette->ilistBackColor).value(); palette->ilistSelBackColor = settings.value("selectedBackground", palette->ilistSelBackColor).value(); palette->ilistHovBackColor = settings.value("hoveredBackground", palette->ilistHovBackColor).value(); palette->ilistHovSelBackColor = settings.value("selectedHoveredBackground", palette->ilistHovSelBackColor).value(); settings.endGroup(); settings.beginGroup("Oscilloscope"); palette->wavBackColor = settings.value("background", palette->wavBackColor).value(); palette->wavDrawColor = settings.value("foreground", palette->wavDrawColor).value(); settings.endGroup(); } BambooTracker-0.4.6/BambooTracker/gui/color_palette_handler.hpp000066400000000000000000000034621401124043500245740ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef COLORPALETTEHANDLER_HPP #define COLORPALETTEHANDLER_HPP #include #include "color_palette.hpp" class QSettings; class ColorPaletteHandler { public: static bool savePalette(const ColorPalette* const palette); static bool savePalette(QString file, const ColorPalette* const palette); static bool loadPalette(ColorPalette* const palette); static bool loadPalette(QString file, ColorPalette* const palette); private: ColorPaletteHandler(); const static QString organization; const static QString application; static void save(QSettings& settings, const ColorPalette* const palette); static void load(QSettings& settings, ColorPalette* const palette); }; #endif // COLORPALETTEHANDLER_HPP BambooTracker-0.4.6/BambooTracker/gui/command/000077500000000000000000000000001401124043500211435ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/command/instrument/000077500000000000000000000000001401124043500233535ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/command/instrument/add_instrument_qt_command.cpp000066400000000000000000000044141401124043500313040ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "add_instrument_qt_command.hpp" #include #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" AddInstrumentQtCommand::AddInstrumentQtCommand( QListWidget *list, int num, const QString& name, InstrumentType type, std::weak_ptr formMan, MainWindow* mainwin, bool onlyUsed, bool preventFirstStore, QUndoCommand *parent) : QUndoCommand(parent), list_(list), num_(num), name_(name), type_(type), formMan_(formMan), mainwin_(mainwin), onlyUsed_(onlyUsed), hasDone_(!preventFirstStore) { } void AddInstrumentQtCommand::undo() { auto&& item = list_->takeItem(num_); delete item; formMan_.lock()->remove(num_); if ((type_ == InstrumentType::ADPCM || type_ == InstrumentType::Drumkit) && onlyUsed_) { mainwin_->assignADPCMSamples(); } } void AddInstrumentQtCommand::redo() { list_->insertItem(num_, gui_command_utils::createInstrumentListItem(num_, type_, name_)); if (hasDone_ && (type_ == InstrumentType::ADPCM || type_ == InstrumentType::Drumkit)) { mainwin_->assignADPCMSamples(); } hasDone_ = true; } int AddInstrumentQtCommand::id() const { return CommandId::AddInstrument; } BambooTracker-0.4.6/BambooTracker/gui/command/instrument/add_instrument_qt_command.hpp000066400000000000000000000040461401124043500313120ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ADD_INSTRUMENT_QT_COMMAND_HPP #define ADD_INSTRUMENT_QT_COMMAND_HPP #include #include #include #include #include #include "gui/mainwindow.hpp" #include "gui/instrument_editor/instrument_form_manager.hpp" enum class InstrumentType; class AddInstrumentQtCommand final : public QUndoCommand { public: AddInstrumentQtCommand(QListWidget *list, int num, const QString& name, InstrumentType type, std::weak_ptr formMan, MainWindow* mainwin, bool onlyUsed, bool preventFirstStore = false, QUndoCommand *parent = nullptr); void undo() override; void redo() override; int id() const override; private: QListWidget *list_; const int num_; const QString name_; const InstrumentType type_; std::weak_ptr formMan_; MainWindow* mainwin_; const bool onlyUsed_; bool hasDone_; }; #endif // ADD_INSTRUMENT_QT_COMMAND_HPP BambooTracker-0.4.6/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.cpp000066400000000000000000000042761401124043500330070ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "change_instrument_name_qt_command.hpp" #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" ChangeInstrumentNameQtCommand::ChangeInstrumentNameQtCommand( QListWidget *list, int num, int row, std::weak_ptr formMan, const QString& oldName, const QString& newName, QUndoCommand *parent) : QUndoCommand(parent), list_(list), num_(num), row_(row), formMan_(formMan), oldName_(oldName), newName_(newName) { } void ChangeInstrumentNameQtCommand::redo() { auto item = list_->item(row_); auto title = gui_command_utils::makeInstrumentListText(num_, newName_); item->setText(title); if (auto form = formMan_.lock()->getForm(num_).get()) { form->setWindowTitle(title); } } void ChangeInstrumentNameQtCommand::undo() { auto item = list_->item(row_); auto title = gui_command_utils::makeInstrumentListText(num_, oldName_); item->setText(title); if (auto form = formMan_.lock()->getForm(num_).get()) { form->setWindowTitle(title); } } int ChangeInstrumentNameQtCommand::id() const { return CommandId::ChangeInstrumentName; } BambooTracker-0.4.6/BambooTracker/gui/command/instrument/change_instrument_name_qt_command.hpp000066400000000000000000000036201401124043500330040ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CHANGE_INSTRUMENT_NAME_QT_COMMAND_HPP #define CHANGE_INSTRUMENT_NAME_QT_COMMAND_HPP #include #include #include #include #include "gui/instrument_editor/instrument_form_manager.hpp" class ChangeInstrumentNameQtCommand final : public QUndoCommand { public: ChangeInstrumentNameQtCommand(QListWidget *list, int num, int row, std::weak_ptr formMan, const QString& oldName, const QString& newName, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; private: QListWidget *list_; const int num_; const int row_; std::weak_ptr formMan_; const QString oldName_, newName_; }; #endif // CHANGE_INSTRUMENT_NAME_QT_COMMAND_HPP BambooTracker-0.4.6/BambooTracker/gui/command/instrument/clone_instrument_qt_command.cpp000066400000000000000000000036341401124043500316570ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "clone_instrument_qt_command.hpp" #include "instrument.hpp" #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" CloneInstrumentQtCommand::CloneInstrumentQtCommand( QListWidget *list, int num, InstrumentType type, const QString& name, std::weak_ptr formMan, QUndoCommand *parent) : QUndoCommand(parent), list_(list), cloneNum_(num), formMan_(formMan), type_(type), name_(name) { } void CloneInstrumentQtCommand::redo() { list_->insertItem(cloneNum_, gui_command_utils::createInstrumentListItem(cloneNum_, type_, name_)); } void CloneInstrumentQtCommand::undo() { auto&& item = list_->takeItem(cloneNum_); delete item; formMan_.lock()->remove(cloneNum_); } int CloneInstrumentQtCommand::id() const { return CommandId::CloneInstrument; } BambooTracker-0.4.6/BambooTracker/gui/command/instrument/clone_instrument_qt_command.hpp000066400000000000000000000035361401124043500316650ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CLONE_INSTRUMENT_QT_COMMAND_H #define CLONE_INSTRUMENT_QT_COMMAND_H #include #include #include #include "gui/instrument_editor/instrument_form_manager.hpp" enum class InstrumentType; class CloneInstrumentQtCommand final : public QUndoCommand { public: CloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, const QString& name, std::weak_ptr formMan, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; private: QListWidget* list_; const int cloneNum_; std::weak_ptr formMan_; const InstrumentType type_; const QString name_; }; #endif // CLONE_INSTRUMENT_QT_COMMAND_H BambooTracker-0.4.6/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.cpp000066400000000000000000000043711401124043500326530ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "deep_clone_instrument_qt_command.hpp" #include "instrument.hpp" #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" DeepCloneInstrumentQtCommand::DeepCloneInstrumentQtCommand( QListWidget *list, int num, InstrumentType type, const QString& name, std::weak_ptr formMan, MainWindow* mainwin, bool onlyUsed, QUndoCommand *parent) : QUndoCommand(parent), list_(list), cloneNum_(num), formMan_(formMan), type_(type), name_(name), mainwin_(mainwin), onlyUsed_(onlyUsed) { } void DeepCloneInstrumentQtCommand::redo() { list_->insertItem(cloneNum_, gui_command_utils::createInstrumentListItem(cloneNum_, type_, name_)); if (type_ == InstrumentType::ADPCM || type_ == InstrumentType::Drumkit) mainwin_->assignADPCMSamples(); } void DeepCloneInstrumentQtCommand::undo() { auto&& item = list_->takeItem(cloneNum_); delete item; formMan_.lock()->remove(cloneNum_); if ((type_ == InstrumentType::ADPCM || type_ == InstrumentType::Drumkit) && onlyUsed_) { mainwin_->assignADPCMSamples(); } } int DeepCloneInstrumentQtCommand::id() const { return CommandId::DeepCloneInstrument; } BambooTracker-0.4.6/BambooTracker/gui/command/instrument/deep_clone_instrument_qt_command.hpp000066400000000000000000000037561401124043500326660ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef DEEP_CLONE_INSTRUMENT_QT_COMMAND_HPP #define DEEP_CLONE_INSTRUMENT_QT_COMMAND_HPP #include #include #include #include "gui/mainwindow.hpp" #include "gui/instrument_editor/instrument_form_manager.hpp" enum class InstrumentType; class DeepCloneInstrumentQtCommand final : public QUndoCommand { public: DeepCloneInstrumentQtCommand(QListWidget *list, int num, InstrumentType type, const QString& name, std::weak_ptr formMan, MainWindow* mainwin, bool onlyUsed, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; private: QListWidget* list_; const int cloneNum_; std::weak_ptr formMan_; const InstrumentType type_; const QString name_; MainWindow* mainwin_; const bool onlyUsed_; }; #endif // DEEP_CLONE_INSTRUMENT_QT_COMMAND_HPP BambooTracker-0.4.6/BambooTracker/gui/command/instrument/instrument_command_qt_utils.cpp000066400000000000000000000034711401124043500317160ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_command_qt_utils.hpp" #include #include #include "instrument.hpp" #include "enum_hash.hpp" namespace gui_command_utils { namespace { const std::unordered_map ICON_SRC = { { InstrumentType::FM, ":/icon/inst_fm" }, { InstrumentType::SSG, ":/icon/inst_ssg" }, { InstrumentType::ADPCM, ":/icon/inst_adpcm" }, { InstrumentType::Drumkit, ":/icon/inst_kit" } }; } QListWidgetItem* createInstrumentListItem(int num, InstrumentType type, const QString& name) { QListWidgetItem *item = new QListWidgetItem(QIcon(ICON_SRC.at(type)), makeInstrumentListText(num, name)); item->setSizeHint(QSize(130, 17)); item->setData(Qt::UserRole, num); return item; } } BambooTracker-0.4.6/BambooTracker/gui/command/instrument/instrument_command_qt_utils.hpp000066400000000000000000000030721401124043500317200ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_COMMAND_QT_UTILS_HPP #define INSTRUMENT_COMMAND_QT_UTILS_HPP #include #include enum class InstrumentType; namespace gui_command_utils { inline QString makeInstrumentListText(int num, const QString& name) { return QString("%1: %2").arg(num, 2, 16, QChar('0')).toUpper().arg(name); } QListWidgetItem* createInstrumentListItem(int num, InstrumentType type, const QString& name); } #endif // INSTRUMENT_COMMAND_QT_UTILS_HPP BambooTracker-0.4.6/BambooTracker/gui/command/instrument/instrument_commands_qt.hpp000066400000000000000000000030071401124043500306610ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_COMMANDS_QT_HPP #define INSTRUMENT_COMMANDS_QT_HPP /********** Instrument edit **********/ #include "add_instrument_qt_command.hpp" #include "remove_instrument_qt_command.hpp" #include "change_instrument_name_qt_command.hpp" #include "clone_instrument_qt_command.hpp" #include "deep_clone_instrument_qt_command.hpp" #include "swap_instruments_qt_command.hpp" #endif // INSTRUMENT_COMMANDS_QT_HPP BambooTracker-0.4.6/BambooTracker/gui/command/instrument/remove_instrument_qt_command.cpp000066400000000000000000000044531401124043500320540ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "remove_instrument_qt_command.hpp" #include #include "instrument.hpp" #include "command/command_id.hpp" #include "instrument_command_qt_utils.hpp" RemoveInstrumentQtCommand::RemoveInstrumentQtCommand( QListWidget *list, int num, int row, const QString& name, InstrumentType type, std::weak_ptr formMan, MainWindow* mainwin, bool updateRequested, QUndoCommand *parent) : QUndoCommand(parent), list_(list), num_(num), name_(name), row_(row), type_(type), formMan_(formMan), mainwin_(mainwin), updateRequested_(updateRequested) { } void RemoveInstrumentQtCommand::undo() { list_->insertItem(row_, gui_command_utils::createInstrumentListItem(num_, type_, name_)); if (updateRequested_ && (type_ == InstrumentType::ADPCM || type_ == InstrumentType::Drumkit)) { mainwin_->assignADPCMSamples(); } } void RemoveInstrumentQtCommand::redo() { auto&& item = list_->takeItem(row_); delete item; formMan_.lock()->remove(num_); if (updateRequested_ && (type_ == InstrumentType::ADPCM || type_ == InstrumentType::Drumkit)) { mainwin_->assignADPCMSamples(); } } int RemoveInstrumentQtCommand::id() const { return CommandId::RemoveInstrument; } BambooTracker-0.4.6/BambooTracker/gui/command/instrument/remove_instrument_qt_command.hpp000066400000000000000000000040441401124043500320550ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef REMOVE_INSTRUMENT_QT_COMMAND_HPP #define REMOVE_INSTRUMENT_QT_COMMAND_HPP #include #include #include #include #include #include "gui/mainwindow.hpp" #include "gui/instrument_editor/instrument_form_manager.hpp" enum class InstrumentType; class RemoveInstrumentQtCommand final : public QUndoCommand { public: RemoveInstrumentQtCommand(QListWidget *list, int num, int row, const QString& name, InstrumentType type, std::weak_ptr formMan, MainWindow* mainwin, bool updateRequested, QUndoCommand *parent = nullptr); void undo() override; void redo() override; int id() const override; private: QListWidget *list_; const int num_; const QString name_; const int row_; const InstrumentType type_; std::weak_ptr formMan_; MainWindow* mainwin_; const bool updateRequested_; }; #endif // REMOVE_INSTRUMENT_QT_COMMAND_HPP BambooTracker-0.4.6/BambooTracker/gui/command/instrument/swap_instruments_qt_command.cpp000066400000000000000000000062151401124043500317120ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "swap_instruments_qt_command.hpp" #include #include "instrument.hpp" #include "command/command_id.hpp" #include "gui/pattern_editor/pattern_editor.hpp" #include "instrument_command_qt_utils.hpp" SwapInstrumentsQtCommand::SwapInstrumentsQtCommand( QListWidget* list, int inst1Row, int inst2Row, const QString& inst1Name, const QString& inst2Name, std::weak_ptr formMan, PatternEditor* pattern, QUndoCommand* parent) : QUndoCommand(parent), list_(list), ptn_(pattern), formMan_(formMan) { if (inst1Row < inst2Row) { inst1Row_ = inst1Row; inst2Row_ = inst2Row; inst1Name_ = inst1Name; inst2Name_ = inst2Name; } else { inst1Row_ = inst2Row; inst2Row_ = inst1Row; inst1Name_ = inst2Name; inst2Name_ = inst1Name; } } void SwapInstrumentsQtCommand::undo() { swap(inst1Row_, inst2Row_, inst2Name_, inst1Name_); } void SwapInstrumentsQtCommand::redo() { swap(inst1Row_, inst2Row_, inst1Name_, inst2Name_); } int SwapInstrumentsQtCommand::id() const { return CommandId::SwapInstruments; } void SwapInstrumentsQtCommand::swap(int above, int below, QString aboveName, QString belowName) { QListWidgetItem* belowItem = list_->takeItem(below); int belowId = belowItem->data(Qt::UserRole).toInt(); QListWidgetItem* aboveItem = list_->takeItem(above); int aboveId = aboveItem->data(Qt::UserRole).toInt(); QString newBelowName = gui_command_utils::makeInstrumentListText(belowId, aboveName); aboveItem->setText(newBelowName); aboveItem->setData(Qt::UserRole, belowId); QString newAboveName = gui_command_utils::makeInstrumentListText(aboveId, belowName); belowItem->setText(newAboveName); belowItem->setData(Qt::UserRole, aboveId); list_->insertItem(above, belowItem); list_->insertItem(below, aboveItem); formMan_.lock()->swap(aboveId, belowId); if (auto aboveForm = formMan_.lock()->getForm(aboveId).get()) aboveForm->setWindowTitle(newAboveName); if (auto belowForm = formMan_.lock()->getForm(belowId).get()) belowForm->setWindowTitle(newBelowName); ptn_->onPatternDataGlobalChanged(); } BambooTracker-0.4.6/BambooTracker/gui/command/instrument/swap_instruments_qt_command.hpp000066400000000000000000000037731401124043500317250ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SWAP_INSTRUMENTS_QT_COMMAND_HPP #define SWAP_INSTRUMENTS_QT_COMMAND_HPP #include #include #include #include #include "gui/instrument_editor/instrument_form_manager.hpp" class PatternEditor; class SwapInstrumentsQtCommand final : public QUndoCommand { public: SwapInstrumentsQtCommand(QListWidget *list, int inst1Row, int inst2Row, const QString& inst1Name, const QString& inst2Name, std::weak_ptr formMan, PatternEditor* pattern, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; private: QListWidget* list_; PatternEditor* ptn_; int inst1Row_, inst2Row_; std::weak_ptr formMan_; QString inst1Name_, inst2Name_; void swap(int above, int below, QString aboveName, QString belowName); }; #endif // SWAP_INSTRUMENTS_QT_COMMAND_HPP BambooTracker-0.4.6/BambooTracker/gui/command/order/000077500000000000000000000000001401124043500222565ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/command/order/order_commands_qt.hpp000066400000000000000000000041641401124043500264740ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ORDER_COMMANDS_QT_HPP #define ORDER_COMMANDS_QT_HPP #include "order_list_common_qt_command.hpp" using CloneOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; using ClonePatternsQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawText; using DeleteOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; using DuplicateOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; using InsertOrderBelowQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawAll; using MoveOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawText; using PasteCopiedDataToOrderQtCommand = gui_command_impl::OrderListCommonQtCommandRedrawText; using SetPatternToOrderQtCommand = gui_command_impl::OrderListEntryQtCommandRedrawText; #endif // ORDER_COMMANDS_QT_HPP BambooTracker-0.4.6/BambooTracker/gui/command/order/order_list_common_qt_command.cpp000066400000000000000000000047041401124043500307070ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "order_list_common_qt_command.hpp" #include "gui/order_list_editor/order_list_panel.hpp" namespace gui_command_impl { OrderListCommonQtCommand::OrderListCommonQtCommand( CommandId id, OrderListPanel* panel, bool orderLengthChanged, QUndoCommand* parent) : QUndoCommand(parent), panel_(panel), id_(id), orderLenChanged_(orderLengthChanged) { } void OrderListCommonQtCommand::redo() { panel_->onOrderEdited(); panel_->redrawByPatternChanged(orderLenChanged_); } void OrderListCommonQtCommand::undo() { panel_->onOrderEdited(); panel_->redrawByPatternChanged(orderLenChanged_); } int OrderListCommonQtCommand::id() const { return id_; } OrderListEntryQtCommand::OrderListEntryQtCommand( CommandId id, OrderListPanel* panel, bool redrawAll, const OrderPosition& pos, bool secondEntry, QUndoCommand* parent) : OrderListCommonQtCommand(id, panel, redrawAll, parent), pos_(pos), isSecondEntry_(secondEntry) { } void OrderListEntryQtCommand::undo() { OrderListCommonQtCommand::undo(); panel_->resetEntryCount(); } bool OrderListEntryQtCommand::mergeWith(const QUndoCommand* other) { if (other->id() == id() && !isSecondEntry_) { auto com = dynamic_cast(other); if (com->pos_ == pos_ && com->isSecondEntry_) { isSecondEntry_ = true; redo(); return true; } } isSecondEntry_ = true; return false; } } BambooTracker-0.4.6/BambooTracker/gui/command/order/order_list_common_qt_command.hpp000066400000000000000000000062221401124043500307110ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ORDER_LIST_COMMON_QT_COMMAND_HPP #define ORDER_LIST_COMMON_QT_COMMAND_HPP #include #include "command/command_id.hpp" #include "gui/order_list_editor/order_position.hpp" class OrderListPanel; namespace gui_command_impl { class OrderListCommonQtCommand : public QUndoCommand { public: virtual void redo() override; virtual void undo() override; int id() const override final; protected: OrderListPanel* panel_; OrderListCommonQtCommand(CommandId id, OrderListPanel* panel, bool redrawAll, QUndoCommand* parent); private: CommandId id_; bool orderLenChanged_; }; template class OrderListCommonQtCommandRedraw final : public OrderListCommonQtCommand { public: OrderListCommonQtCommandRedraw(OrderListPanel* panel, QUndoCommand* parent = nullptr) : OrderListCommonQtCommand(comId, panel, redrawAll, parent) {} }; template using OrderListCommonQtCommandRedrawAll = OrderListCommonQtCommandRedraw; template using OrderListCommonQtCommandRedrawText = OrderListCommonQtCommandRedraw; class OrderListEntryQtCommand : public OrderListCommonQtCommand { public: void undo() override; bool mergeWith(const QUndoCommand* other) override; protected: OrderListEntryQtCommand(CommandId id, OrderListPanel* panel, bool redrawAll, const OrderPosition& pos, bool secondEntry, QUndoCommand* parent); private: const OrderPosition pos_; bool isSecondEntry_; }; template class OrderListEntryQtCommandRedraw final : public OrderListEntryQtCommand { public: OrderListEntryQtCommandRedraw(OrderListPanel* panel, const OrderPosition& pos, bool secondEntry, QUndoCommand* parent = nullptr) : OrderListEntryQtCommand(comId, panel, redrawAll, pos, secondEntry, parent) {} }; template using OrderListEntryQtCommandRedrawAll = OrderListEntryQtCommandRedraw; template using OrderListEntryQtCommandRedrawText = OrderListEntryQtCommandRedraw; } #endif // ORDER_LIST_COMMON_QT_COMMAND_HPP BambooTracker-0.4.6/BambooTracker/gui/command/pattern/000077500000000000000000000000001401124043500226205ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/command/pattern/pattern_commands_qt.hpp000066400000000000000000000107141401124043500273760ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef PATTERN_COMMANDS_QT_HPP #define PATTERN_COMMANDS_QT_HPP #include "pattern_editor_common_qt_command.hpp" using ChangeValuesInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using DeletePreviousStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using EraseCellsInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using EraseEffectInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using EraseEffectValueInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using EraseInstrumentInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; using EraseStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using EraseVolumeInStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; using ExpandPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using InsertStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using InterpolatePatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; using PasteCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using PasteInsertCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using PasteMixCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using PasteOverwriteCopiedDataToPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using ReplaceInstrumentInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; using ReversePatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using SetEchoBufferAccessQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; using SetEffectIDToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawAll; using SetEffectValueToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawText; using SetInstrumentToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawText; using SetKeyOffToStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; using SetKeyOnToStepQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; using SetVolumeToStepQtCommand = gui_command_impl::PatternEditorEntryQtCommandRedrawText; using ShrinkPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawAll; using TransposeNoteInPatternQtCommand = gui_command_impl::PatternEditorCommonQtCommandRedrawText; #endif // PATTERN_COMMANDS_QT_HPP BambooTracker-0.4.6/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.cpp000066400000000000000000000046531401124043500321310ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pattern_editor_common_qt_command.hpp" #include "gui/pattern_editor/pattern_editor_panel.hpp" namespace gui_command_impl { PatternEditorCommonQtCommand::PatternEditorCommonQtCommand( CommandId id, PatternEditorPanel* panel, bool redrawAll, QUndoCommand* parent) : QUndoCommand(parent), panel_(panel), id_(id), redrawAll_(redrawAll) { } void PatternEditorCommonQtCommand::redo() { panel_->redrawByPatternChanged(redrawAll_); } void PatternEditorCommonQtCommand::undo() { panel_->redrawByPatternChanged(redrawAll_); } int PatternEditorCommonQtCommand::id() const { return id_; } PatternEditorEntryQtCommand::PatternEditorEntryQtCommand( CommandId id, PatternEditorPanel* panel, bool redrawAll, const PatternPosition& pos, bool secondEntry, QUndoCommand* parent) : PatternEditorCommonQtCommand(id, panel, redrawAll, parent), pos_(pos), isSecondEntry_(secondEntry) { } void PatternEditorEntryQtCommand::undo() { PatternEditorCommonQtCommand::undo(); panel_->resetEntryCount(); } bool PatternEditorEntryQtCommand::mergeWith(const QUndoCommand* other) { if (other->id() == id() && !isSecondEntry_) { auto com = dynamic_cast(other); if (com->pos_ == pos_ && com->isSecondEntry_) { isSecondEntry_ = true; redo(); return true; } } isSecondEntry_ = true; return false; } } BambooTracker-0.4.6/BambooTracker/gui/command/pattern/pattern_editor_common_qt_command.hpp000066400000000000000000000064131401124043500321320ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef PATTERN_EDITOR_COMMON_QT_COMMAND_HPP #define PATTERN_EDITOR_COMMON_QT_COMMAND_HPP #include #include "command/command_id.hpp" #include "gui/pattern_editor/pattern_position.hpp" class PatternEditorPanel; namespace gui_command_impl { class PatternEditorCommonQtCommand : public QUndoCommand { public: virtual void redo() override; virtual void undo() override; int id() const override final; protected: PatternEditorPanel* panel_; PatternEditorCommonQtCommand(CommandId id, PatternEditorPanel* panel, bool redrawAll, QUndoCommand* parent); private: CommandId id_; bool redrawAll_; }; template class PatternEditorCommonQtCommandRedraw final : public PatternEditorCommonQtCommand { public: PatternEditorCommonQtCommandRedraw(PatternEditorPanel* panel, QUndoCommand* parent = nullptr) : PatternEditorCommonQtCommand(comId, panel, redrawAll, parent) {} }; template using PatternEditorCommonQtCommandRedrawAll = PatternEditorCommonQtCommandRedraw; template using PatternEditorCommonQtCommandRedrawText = PatternEditorCommonQtCommandRedraw; class PatternEditorEntryQtCommand : public PatternEditorCommonQtCommand { public: void undo() override; bool mergeWith(const QUndoCommand* other) override; protected: PatternEditorEntryQtCommand(CommandId id, PatternEditorPanel* panel, bool redrawAll, const PatternPosition& pos, bool secondEntry, QUndoCommand* parent); private: const PatternPosition pos_; bool isSecondEntry_; }; template class PatternEditorEntryQtCommandRedraw final : public PatternEditorEntryQtCommand { public: PatternEditorEntryQtCommandRedraw(PatternEditorPanel* panel, const PatternPosition& pos, bool secondEntry, QUndoCommand* parent = nullptr) : PatternEditorEntryQtCommand(comId, panel, redrawAll, pos, secondEntry, parent) {} }; template using PatternEditorEntryQtCommandRedrawAll = PatternEditorEntryQtCommandRedraw; template using PatternEditorEntryQtCommandRedrawText = PatternEditorEntryQtCommandRedraw; } #endif // PATTERN_EDITOR_COMMON_QT_COMMAND_HPP BambooTracker-0.4.6/BambooTracker/gui/comment_edit_dialog.cpp000066400000000000000000000032701401124043500242210ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "comment_edit_dialog.hpp" #include "ui_comment_edit_dialog.h" CommentEditDialog::CommentEditDialog(QString comment, QWidget *parent) : QDialog(parent), ui(new Ui::CommentEditDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui->plainTextEdit->setPlainText(comment); } CommentEditDialog::~CommentEditDialog() { delete ui; } void CommentEditDialog::setComment(QString text) { ui->plainTextEdit->setPlainText(text); } void CommentEditDialog::on_plainTextEdit_textChanged() { emit commentChanged(ui->plainTextEdit->toPlainText()); } BambooTracker-0.4.6/BambooTracker/gui/comment_edit_dialog.hpp000066400000000000000000000031751401124043500242320ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef COMMENT_EDIT_DIALOG_HPP #define COMMENT_EDIT_DIALOG_HPP #include #include namespace Ui { class CommentEditDialog; } class CommentEditDialog : public QDialog { Q_OBJECT public: explicit CommentEditDialog(QString comment = "", QWidget *parent = nullptr); ~CommentEditDialog() override; void setComment(QString text); signals: void commentChanged(const QString& text); private: Ui::CommentEditDialog *ui; private slots: void on_plainTextEdit_textChanged(); }; #endif // COMMENT_EDIT_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/comment_edit_dialog.ui000066400000000000000000000027441401124043500240610ustar00rootroot00000000000000 CommentEditDialog 0 0 400 300 Module Comment Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() CommentEditDialog accept() 248 254 157 274 buttonBox rejected() CommentEditDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/configuration_dialog.cpp000066400000000000000000001222671401124043500244310ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "configuration_dialog.hpp" #include "ui_configuration_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chip/opna.hpp" #include "audio/audio_stream.hpp" #include "midi/midi.hpp" #include "jamming.hpp" #include "slider_style.hpp" #include "fm_envelope_set_edit_dialog.hpp" #include "color_palette_handler.hpp" #include "gui_utils.hpp" namespace { inline Qt::CheckState toCheckState(bool enabled) { return enabled ? Qt::Checked : Qt::Unchecked; } inline bool fromCheckState(Qt::CheckState state) { return (state == Qt::Checked) ? true : false; } } ConfigurationDialog::ConfigurationDialog(std::weak_ptr config, std::weak_ptr palette, std::weak_ptr stream, QWidget *parent) : QDialog(parent), ui(new Ui::ConfigurationDialog), config_(config), refPalette_(palette), stream_(stream) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); QObject::connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, [&] { on_ConfigurationDialog_accepted(); emit applyPressed(); }); std::shared_ptr configLocked = config.lock(); // General // // General settings auto glfunc = [&](int i, bool enabled, QString desc) { QListWidgetItem* item = ui->generalSettingsListWidget->item(i); item->setCheckState(toCheckState(enabled)); item->setData(Qt::UserRole, desc); }; glfunc(0, configLocked->getWarpCursor(), tr("Warp the cursor around the edges of the pattern editor.")); glfunc(1, configLocked->getWarpAcrossOrders(), tr("Move to previous or next order when reaching top or bottom in the pattern editor.")); glfunc(2, configLocked->getShowRowNumberInHex(), tr("Display row numbers and the playback position on the status bar in hexadecimal.")); glfunc(3, configLocked->getShowPreviousNextOrders(), tr("Preview previous and next orders in the pattern editor.")); glfunc(4, configLocked->getBackupModules(), tr("Create a backup copy of the existing file when saving a module.")); glfunc(5, configLocked->getDontSelectOnDoubleClick(), tr("Don't select the whole track when double-clicking in the pattern editor.")); glfunc(6, configLocked->getReverseFMVolumeOrder(), tr("Reverse the order of FM volume so that 00 is the quietest in the pattern editor.")); glfunc(7, configLocked->getMoveCursorToRight(), tr("Move the cursor to right after entering effects in the pattern editor.")); glfunc(8, configLocked->getRetrieveChannelState(), tr("Reconstruct the current channel's state from previous orders upon playing.")); glfunc(9, configLocked->getEnableTranslation(), tr("Translate to your language from the next launch. See readme to check supported languages.")); glfunc(10, configLocked->getShowFMDetuneAsSigned(), tr("Display FM detune values as signed numbers in the FM envelope editor.")); glfunc(11, configLocked->getFill00ToEffectValue(), tr("Fill 00 to effect value column upon entering effect id.")); glfunc(12, configLocked->getMoveCursorByHorizontalScroll(), tr("Move the cursor position by cell with horizontal scroll bar in the order list and the pattern editor.")); glfunc(13, configLocked->getOverwriteUnusedUneditedPropety(), tr("Overwrite unused and unedited instrument properties on creating new properties. " "When disabled, override unused properties regardless of editing.")); glfunc(14, configLocked->getWriteOnlyUsedSamples(), tr("Send only ADPCM samples used by instruments to the ADPCM memory. " "Recommend to turn off if you change ADPCM samples frequently due to take the high rewriting cost.")); glfunc(15, configLocked->getReflectInstrumentNumberChange(), tr("Correspond the instrument number in patterns when the instrument changes its number.")); glfunc(16, configLocked->getFixJammingVolume(), tr("Set maximum volume during jam mode. When unchecked, the volume is changed by the volume spinbox.")); glfunc(17, configLocked->getMuteHiddenTracks(), tr("Mute hidden tracks when visibility of tracks is changed.")); glfunc(18, configLocked->getRestoreTrackVisibility(), tr("Restore the previous track visibility on startup.")); // Edit settings ui->pageJumpLengthSpinBox->setValue(static_cast(configLocked->getPageJumpLength())); // Wave view ui->waveViewRateSpinBox->setValue(configLocked->getWaveViewFrameRate()); // Keys ui->shortcutsTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->shortcutsTreeWidget->header()->setSectionResizeMode(1, QHeaderView::Fixed); std::map shortcutsActions = { { Configuration::ShortcutAction::KeyOff, tr("Key off") }, { Configuration::ShortcutAction::OctaveUp, tr("Octave up") }, { Configuration::ShortcutAction::OctaveDown, tr("Octave down") }, { Configuration::ShortcutAction::EchoBuffer, tr("Echo buffer") }, { Configuration::ShortcutAction::PlayAndStop, tr("Play and stop") }, { Configuration::ShortcutAction::Play, tr("Play") }, { Configuration::ShortcutAction::PlayFromStart, tr("Play from start") }, { Configuration::ShortcutAction::PlayPattern, tr("Play pattern") }, { Configuration::ShortcutAction::PlayFromCursor, tr("Play from cursor") }, { Configuration::ShortcutAction::PlayFromMarker, tr("Play from marker") }, { Configuration::ShortcutAction::PlayStep, tr("Play step") }, { Configuration::ShortcutAction::Stop, tr("Stop") }, { Configuration::ShortcutAction::FocusOnPattern, tr("Focus on pattern editor") }, { Configuration::ShortcutAction::FocusOnOrder, tr("Focus on order list") }, { Configuration::ShortcutAction::FocusOnInstrument, tr("Focus on instrument list") }, { Configuration::ShortcutAction::ToggleEditJam, tr("Toggle edit/jam mode") }, { Configuration::ShortcutAction::SetMarker, tr("Set marker") }, { Configuration::ShortcutAction::PasteMix, tr("Paste and mix") }, { Configuration::ShortcutAction::PasteOverwrite, tr("Paste and overwrite") }, { Configuration::ShortcutAction::PasteInsert, tr("Paste and insert") }, { Configuration::ShortcutAction::SelectAll, tr("Select all") }, { Configuration::ShortcutAction::Deselect, tr("Deselect") }, { Configuration::ShortcutAction::SelectRow, tr("Select row") }, { Configuration::ShortcutAction::SelectColumn, tr("Select column") }, { Configuration::ShortcutAction::SelectPattern, tr("Select pattern") }, { Configuration::ShortcutAction::SelectOrder, tr("Select order") }, { Configuration::ShortcutAction::GoToStep, tr("Go to step") }, { Configuration::ShortcutAction::ToggleTrack, tr("Toggle track") }, { Configuration::ShortcutAction::SoloTrack, tr("Solo track") }, { Configuration::ShortcutAction::Interpolate, tr("Interpolate") }, { Configuration::ShortcutAction::Reverse, tr("Reverse") }, { Configuration::ShortcutAction::GoToPrevOrder, tr("Go to previous order") }, { Configuration::ShortcutAction::GoToNextOrder, tr("Go to next order") }, { Configuration::ShortcutAction::ToggleBookmark, tr("Toggle bookmark") }, { Configuration::ShortcutAction::PrevBookmark, tr("Previous bookmark") }, { Configuration::ShortcutAction::NextBookmark, tr("Next bookmark") }, { Configuration::ShortcutAction::DecreaseNote, tr("Transpose, decrease note") }, { Configuration::ShortcutAction::IncreaseNote, tr("Transpose, increase note") }, { Configuration::ShortcutAction::DecreaseOctave, tr("Transpose, decrease octave") }, { Configuration::ShortcutAction::IncreaseOctave, tr("Transpose, increase octave") }, { Configuration::ShortcutAction::PrevInstrument, tr("Previous instrument") }, { Configuration::ShortcutAction::NextInstrument, tr("Next instrument") }, { Configuration::ShortcutAction::MaskInstrument, tr("Mask instrument") }, { Configuration::ShortcutAction::MaskVolume, tr("Mask volume") }, { Configuration::ShortcutAction::EditInstrument, tr("Edit instrument") }, { Configuration::ShortcutAction::FollowMode, tr("Follow mode") }, { Configuration::ShortcutAction::DuplicateOrder, tr("Duplicate order") }, { Configuration::ShortcutAction::ClonePatterns, tr("Clone patterns") }, { Configuration::ShortcutAction::CloneOrder, tr("Clone order") }, { Configuration::ShortcutAction::ReplaceInstrument, tr("Replace instrument") }, { Configuration::ShortcutAction::ExpandPattern, tr("Expand pattern") }, { Configuration::ShortcutAction::ShrinkPattern, tr("Shrink pattern") }, { Configuration::ShortcutAction::FineDecreaseValues, tr("Fine decrease values") }, { Configuration::ShortcutAction::FineIncreaseValues, tr("Fine increase values") }, { Configuration::ShortcutAction::CoarseDecreaseValues, tr("Coarse decrease values") }, { Configuration::ShortcutAction::CoarseIncreaseValuse, tr("Coarse increase valuse") }, { Configuration::ShortcutAction::ExpandEffect, tr("Expand effect column") }, { Configuration::ShortcutAction::ShrinkEffect, tr("Shrink effect column") }, { Configuration::ShortcutAction::PrevHighlighted, tr("Previous highlighted step") }, { Configuration::ShortcutAction::NextHighlighted, tr("Next highlighted step") }, { Configuration::ShortcutAction::IncreasePatternSize, tr("Increase pattern size") }, { Configuration::ShortcutAction::DecreasePatternSize, tr("Decrease pattern size") }, { Configuration::ShortcutAction::IncreaseEditStep, tr("Increase edit step") }, { Configuration::ShortcutAction::DecreaseEditStep, tr("Decrease edit step") }, { Configuration::ShortcutAction::DisplayEffectList, tr("Display effect list") }, { Configuration::ShortcutAction::PreviousSong, tr("Previous song") }, { Configuration::ShortcutAction::NextSong, tr("Next song") }, { Configuration::ShortcutAction::JamVolumeUp, tr("Jam volume up") }, { Configuration::ShortcutAction::JamVolumeDown, tr("Jam volume down") } }; std::unordered_map shortcuts = configLocked->getShortcuts(); for (const auto& pair : shortcutsActions) { int row = ui->shortcutsTreeWidget->topLevelItemCount(); auto item = new QTreeWidgetItem(); item->setText(0, pair.second); ui->shortcutsTreeWidget->insertTopLevelItem(row, item); auto widget = new QWidget(); widget->setLayout(new QHBoxLayout()); auto seq = new QKeySequenceEdit(gui_utils::utf8ToQString(shortcuts.at(pair.first))); shortcutsMap_[pair.first] = seq; auto button = new QToolButton(); button->setIcon(QIcon(":/icon/remove_inst")); QObject::connect(button, &QToolButton::clicked, seq, &QKeySequenceEdit::clear); auto layout = widget->layout(); layout->setSpacing(0); layout->setMargin(0); layout->addWidget(seq); layout->addWidget(button); ui->shortcutsTreeWidget->setItemWidget(item, 1, widget); } ui->keyboardTypeComboBox->setCurrentIndex(static_cast(configLocked->getNoteEntryLayout())); customLayoutKeysMap_ = { { JamKey::LowC, ui->lowCEdit }, { JamKey::LowCS, ui->lowCSEdit }, { JamKey::LowD, ui->lowDEdit }, { JamKey::LowDS, ui->lowDSEdit }, { JamKey::LowE, ui->lowEEdit }, { JamKey::LowF, ui->lowFEdit }, { JamKey::LowFS, ui->lowFSEdit }, { JamKey::LowG, ui->lowGEdit }, { JamKey::LowGS, ui->lowGSEdit }, { JamKey::LowA, ui->lowAEdit }, { JamKey::LowAS, ui->lowASEdit }, { JamKey::LowB, ui->lowBEdit }, { JamKey::LowC2, ui->lowHighCEdit }, { JamKey::LowCS2, ui->lowHighCSEdit }, { JamKey::LowD2, ui->lowHighDEdit }, { JamKey::HighC, ui->highCEdit }, { JamKey::HighCS, ui->highCSEdit }, { JamKey::HighD, ui->highDEdit }, { JamKey::HighDS, ui->highDSEdit }, { JamKey::HighE, ui->highEEdit }, { JamKey::HighF, ui->highFEdit }, { JamKey::HighFS, ui->highFSEdit }, { JamKey::HighG, ui->highGEdit }, { JamKey::HighGS, ui->highGSEdit }, { JamKey::HighA, ui->highAEdit }, { JamKey::HighAS, ui->highASEdit }, { JamKey::HighB, ui->highBEdit }, { JamKey::HighC2, ui->highHighCEdit }, { JamKey::HighCS2, ui->highHighCSEdit }, { JamKey::HighD2, ui->highHighDEdit } }; for (const auto& pair : configLocked->getCustomLayoutKeys()) { customLayoutKeysMap_.at(pair.second)->setKeySequence(QKeySequence(gui_utils::utf8ToQString(pair.first))); } // Emulation // ui->emulatorComboBox->addItem("MAME YM2608", static_cast(chip::OpnaEmulator::Mame)); ui->emulatorComboBox->addItem("Nuked OPN-Mod", static_cast(chip::OpnaEmulator::Nuked)); ui->emulatorComboBox->setCurrentIndex(ui->emulatorComboBox->findData(configLocked->getEmulator())); // Sound // { QSignalBlocker blocker(ui->audioApiComboBox); int sndApiRow = -1; int defSndApiRow = 0; for (auto& name : stream.lock()->getAvailableBackends()) { ui->audioApiComboBox->addItem(name); if (name == QString::fromStdString(configLocked->getSoundAPI())) sndApiRow = ui->audioApiComboBox->count() - 1; if (name == stream.lock()->getCurrentBackend()) defSndApiRow = sndApiRow = ui->audioApiComboBox->count() - 1; } ui->audioApiComboBox->setCurrentIndex((sndApiRow == -1) ? defSndApiRow : sndApiRow); } on_audioApiComboBox_currentIndexChanged(ui->audioApiComboBox->currentText()); ui->realChipComboBox->addItem(tr("None"), static_cast(RealChipInterface::NONE)); ui->realChipComboBox->addItem("SCCI", static_cast(RealChipInterface::SCCI)); ui->realChipComboBox->addItem("C86CTL", static_cast(RealChipInterface::C86CTL)); switch (configLocked->getRealChipInterface()) { default: // Fall through case RealChipInterface::NONE: ui->realChipComboBox->setCurrentIndex(0); break; case RealChipInterface::SCCI: ui->realChipComboBox->setCurrentIndex(1); break; case RealChipInterface::C86CTL: ui->realChipComboBox->setCurrentIndex(2); break; } { QSignalBlocker blocker1(ui->midiInputDeviceComboBox), blocker2(ui->midiApiComboBox); MidiInterface& midiIntf = MidiInterface::getInstance(); int midiApiRow = -1; int defMidiApiRow = 0; for (auto& name : midiIntf.getAvailableApis()) { ui->midiApiComboBox->addItem(gui_utils::utf8ToQString(name)); if (name == configLocked->getMidiAPI()) midiApiRow = ui->midiApiComboBox->count() - 1; if (name == midiIntf.currentApiName()) defMidiApiRow = midiApiRow = ui->midiApiComboBox->count() - 1; } ui->midiApiComboBox->setCurrentIndex((midiApiRow == -1) ? defMidiApiRow : midiApiRow); } onMidiApiChanged(ui->midiApiComboBox->currentText(), false); ui->midiInputGroupBox->setChecked(configLocked->getMidiEnabled()); ui->sampleRateComboBox->addItem("44100Hz", 44100); ui->sampleRateComboBox->addItem("48000Hz", 48000); ui->sampleRateComboBox->addItem("55466Hz", 55466); switch (configLocked->getSampleRate()) { default: // Fall through case 44100: ui->sampleRateComboBox->setCurrentIndex(0); break; case 48000: ui->sampleRateComboBox->setCurrentIndex(1); break; case 55466: ui->sampleRateComboBox->setCurrentIndex(2); break; } ui->bufferLengthHorizontalSlider->setStyle(new SliderStyle()); QObject::connect(ui->bufferLengthHorizontalSlider, &QSlider::valueChanged, this, [&](int value) { ui->bufferLengthLabel->setText(QString::number(value) + "ms"); }); ui->bufferLengthHorizontalSlider->setValue(static_cast(configLocked->getBufferLength())); // Mixer // ui->masterMixerSlider->setText(tr("Master")); ui->masterMixerSlider->setSuffix("%"); ui->masterMixerSlider->setMaximum(200); ui->masterMixerSlider->setMinimum(0); ui->masterMixerSlider->setTickPosition(QSlider::TicksBothSides); ui->masterMixerSlider->setTickInterval(20); ui->masterMixerSlider->setValue(configLocked->getMixerVolumeMaster()); ui->fmMixerSlider->setText("FM"); ui->fmMixerSlider->setSuffix("dB"); ui->fmMixerSlider->setMaximum(120); ui->fmMixerSlider->setMinimum(-120); ui->fmMixerSlider->setValueRate(0.1); ui->fmMixerSlider->setSign(true); ui->fmMixerSlider->setTickPosition(QSlider::TicksBothSides); ui->fmMixerSlider->setTickInterval(20); ui->fmMixerSlider->setValue(static_cast(configLocked->getMixerVolumeFM() * 10)); ui->ssgMixerSlider->setText("SSG"); ui->ssgMixerSlider->setSuffix("dB"); ui->ssgMixerSlider->setMaximum(120); ui->ssgMixerSlider->setMinimum(-120); ui->ssgMixerSlider->setValueRate(0.1); ui->ssgMixerSlider->setSign(true); ui->ssgMixerSlider->setTickPosition(QSlider::TicksBothSides); ui->ssgMixerSlider->setTickInterval(20); ui->ssgMixerSlider->setValue(static_cast(configLocked->getMixerVolumeSSG() * 10)); // Formats // fmEnvelopeTexts_ = configLocked->getFMEnvelopeTexts(); updateEnvelopeSetUi(); // Appearance // ui->colorsTreeWidget->setColumnWidth(0, 250); updateColorTreeFrom(palette.lock().get()); ui->ptnHdFontComboBox->setCurrentFont(QFont(gui_utils::utf8ToQString(configLocked->getPatternEditorHeaderFont()))); ui->ptnHdFontSizeComboBox->setCurrentText(QString::number(configLocked->getPatternEditorHeaderFontSize())); ui->ptnRowFontComboBox->setCurrentFont(QFont(gui_utils::utf8ToQString(configLocked->getPatternEditorRowsFont()))); ui->ptnRowFontSizeComboBox->setCurrentText(QString::number(configLocked->getPatternEditorRowsFontSize())); ui->odrHdFontComboBox->setCurrentFont(QFont(gui_utils::utf8ToQString(configLocked->getOrderListHeaderFont()))); ui->odrHdFontSizeComboBox->setCurrentText(QString::number(configLocked->getOrderListHeaderFontSize())); ui->odrRowFontComboBox->setCurrentFont(QFont(gui_utils::utf8ToQString(configLocked->getOrderListRowsFont()))); ui->odrRowFontSizeComboBox->setCurrentText(QString::number(configLocked->getOrderListRowsFontSize())); } ConfigurationDialog::~ConfigurationDialog() { delete ui; } void ConfigurationDialog::on_ConfigurationDialog_accepted() { std::shared_ptr configLocked = config_.lock(); // General // // General settings configLocked->setWarpCursor(fromCheckState(ui->generalSettingsListWidget->item(0)->checkState())); configLocked->setWarpAcrossOrders(fromCheckState(ui->generalSettingsListWidget->item(1)->checkState())); configLocked->setShowRowNumberInHex(fromCheckState(ui->generalSettingsListWidget->item(2)->checkState())); configLocked->setShowPreviousNextOrders(fromCheckState(ui->generalSettingsListWidget->item(3)->checkState())); configLocked->setBackupModules(fromCheckState(ui->generalSettingsListWidget->item(4)->checkState())); configLocked->setDontSelectOnDoubleClick(fromCheckState(ui->generalSettingsListWidget->item(5)->checkState())); configLocked->setReverseFMVolumeOrder(fromCheckState(ui->generalSettingsListWidget->item(6)->checkState())); configLocked->setMoveCursorToRight(fromCheckState(ui->generalSettingsListWidget->item(7)->checkState())); configLocked->setRetrieveChannelState(fromCheckState(ui->generalSettingsListWidget->item(8)->checkState())); configLocked->setEnableTranslation(fromCheckState(ui->generalSettingsListWidget->item(9)->checkState())); configLocked->setShowFMDetuneAsSigned(fromCheckState(ui->generalSettingsListWidget->item(10)->checkState())); configLocked->setFill00ToEffectValue(fromCheckState(ui->generalSettingsListWidget->item(11)->checkState())); configLocked->setMoveCursorByHorizontalScroll(fromCheckState(ui->generalSettingsListWidget->item(12)->checkState())); configLocked->setOverwriteUnusedUneditedPropety(fromCheckState(ui->generalSettingsListWidget->item(13)->checkState())); configLocked->setWriteOnlyUsedSamples(fromCheckState(ui->generalSettingsListWidget->item(14)->checkState())); configLocked->setReflectInstrumentNumberChange(fromCheckState(ui->generalSettingsListWidget->item(15)->checkState())); configLocked->setFixJammingVolume(fromCheckState(ui->generalSettingsListWidget->item(16)->checkState())); configLocked->setMuteHiddenTracks(fromCheckState(ui->generalSettingsListWidget->item(17)->checkState())); configLocked->setRestoreTrackVisibility(fromCheckState(ui->generalSettingsListWidget->item(18)->checkState())); // Edit settings configLocked->setPageJumpLength(static_cast(ui->pageJumpLengthSpinBox->value())); // Wave view configLocked->setWaveViewFrameRate(ui->waveViewRateSpinBox->value()); // Keys std::unordered_map shortcuts; for (const auto& pair: shortcutsMap_) { shortcuts[pair.first] = pair.second->keySequence().toString().toStdString(); } configLocked->setShortcuts(shortcuts); configLocked->setNoteEntryLayout(static_cast(ui->keyboardTypeComboBox->currentIndex())); std::unordered_map customLayoutNewKeys; for (const auto& pair : customLayoutKeysMap_) { customLayoutNewKeys[pair.second->keySequence().toString().toStdString()] = pair.first; } configLocked->setCustomLayoutKeys(customLayoutNewKeys); // Emulation // int emu = ui->emulatorComboBox->currentData().toInt(); bool changedEmu = false; if (emu != configLocked->getEmulator()) { configLocked->setEmulator(emu); changedEmu = true; } // Sound // configLocked->setSoundDevice(ui->audioDeviceComboBox->currentText().toUtf8().toStdString()); configLocked->setSoundAPI(ui->audioApiComboBox->currentText().toUtf8().toStdString()); configLocked->setRealChipInterface(static_cast( ui->realChipComboBox->currentData(Qt::UserRole).toInt())); configLocked->setMidiEnabled(ui->midiInputGroupBox->isChecked()); configLocked->setMidiAPI(ui->midiApiComboBox->currentText().toUtf8().toStdString()); configLocked->setMidiInputPort(ui->midiInputDeviceComboBox->currentData().toString().toUtf8().toStdString()); configLocked->setSampleRate(ui->sampleRateComboBox->currentData(Qt::UserRole).toUInt()); configLocked->setBufferLength(static_cast(ui->bufferLengthHorizontalSlider->value())); // Mixer // configLocked->setMixerVolumeMaster(ui->masterMixerSlider->value()); configLocked->setMixerVolumeFM(ui->fmMixerSlider->value() * 0.1); configLocked->setMixerVolumeSSG(ui->ssgMixerSlider->value() * 0.1); // Formats // std::sort(fmEnvelopeTexts_.begin(), fmEnvelopeTexts_.end(), [](const FMEnvelopeText& a, const FMEnvelopeText& b) -> bool { return (a.name < b.name); }); configLocked->setFMEnvelopeTexts(fmEnvelopeTexts_); // Appearance // setPaletteFromColorTree(refPalette_.lock().get()); configLocked->setPatternEditorHeaderFont(ui->ptnHdFontComboBox->currentFont().family().toStdString()); configLocked->setPatternEditorHeaderFontSize(ui->ptnHdFontSizeComboBox->currentText().toInt()); configLocked->setPatternEditorRowsFont(ui->ptnRowFontComboBox->currentFont().family().toStdString()); configLocked->setPatternEditorRowsFontSize(ui->ptnRowFontSizeComboBox->currentText().toInt()); configLocked->setOrderListHeaderFont(ui->odrHdFontComboBox->currentFont().family().toStdString()); configLocked->setOrderListHeaderFontSize(ui->odrHdFontSizeComboBox->currentText().toInt()); configLocked->setOrderListRowsFont(ui->odrRowFontComboBox->currentFont().family().toStdString()); configLocked->setOrderListRowsFontSize(ui->odrRowFontSizeComboBox->currentText().toInt()); if (changedEmu) { QMessageBox::information(this, tr("Configuration"), tr("The change of emulator will be effective after restarting the program.")); } } /***** General *****/ void ConfigurationDialog::on_generalSettingsListWidget_itemSelectionChanged() { QString text(""); if (QListWidgetItem* item = ui->generalSettingsListWidget->currentItem()) { text = item->data(Qt::UserRole).toString(); } ui->descPlainTextEdit->setPlainText(tr("Description: %1").arg(text)); } /***** Mixer *****/ void ConfigurationDialog::on_mixerResetPushButton_clicked() { ui->fmMixerSlider->setValue(0); ui->ssgMixerSlider->setValue(0); } /***** Sound *****/ void ConfigurationDialog::on_audioApiComboBox_currentIndexChanged(const QString &arg1) { ui->audioDeviceComboBox->clear(); std::vector devices = stream_.lock()->getAvailableDevices(arg1); if (devices.empty()) { ui->audioDeviceComboBox->setEnabled(false); return; } else { ui->audioDeviceComboBox->setEnabled(true); } int devRow = -1; int defDevRow = 0; for (auto& name : devices) { ui->audioDeviceComboBox->addItem(name); if (name == gui_utils::utf8ToQString(config_.lock()->getSoundDevice())) devRow = ui->audioDeviceComboBox->count() - 1; if (name == stream_.lock()->getDefaultOutputDevice(arg1)) defDevRow = ui->audioDeviceComboBox->count() - 1; } ui->audioDeviceComboBox->setCurrentIndex((devRow == -1) ? defDevRow : devRow); } void ConfigurationDialog::on_midiApiComboBox_currentIndexChanged(const QString &arg1) { onMidiApiChanged(arg1); } void ConfigurationDialog::onMidiApiChanged(const QString &arg1, bool hasInitialized) { ui->midiInputDeviceComboBox->clear(); MidiInterface &intf = MidiInterface::getInstance(); std::vector ports; bool vport; std::string apiName = arg1.toStdString(); if (intf.currentApiName() == apiName) { ports = intf.getRealInputPorts(); vport = intf.supportsVirtualPort(); } else { ports = intf.getRealInputPorts(apiName); vport = intf.supportsVirtualPort(apiName); } int devRow = -1; if (vport) { ui->midiInputDeviceComboBox->addItem(tr("Virtual port"), QString()); } else if (ports.empty()) { ui->midiInputDeviceComboBox->setEnabled(false); return; } if (hasInitialized) // To reflect unchecked groupbox when displaying the dialog for the first time ui->midiInputDeviceComboBox->setEnabled(true); for (auto& portName : ports) { auto name = QString::fromStdString(portName); ui->midiInputDeviceComboBox->addItem(name, name); if (portName == config_.lock()->getMidiInputPort()) devRow = ui->midiInputDeviceComboBox->count() - 1; } ui->midiInputDeviceComboBox->setCurrentIndex((devRow == -1) ? 0 : devRow); } /*****Formats *****/ void ConfigurationDialog::on_addEnvelopeSetPushButton_clicked() { auto name = tr("Set %1").arg(fmEnvelopeTexts_.size() + 1); fmEnvelopeTexts_.push_back({ name.toUtf8().toStdString(), std::vector() }); updateEnvelopeSetUi(); for (int i = ui->envelopeTypeListWidget->count() - 1; i >= 0; --i) { if (ui->envelopeTypeListWidget->item(i)->text() == name) { ui->envelopeTypeListWidget->setCurrentRow(i); break; } } } void ConfigurationDialog::on_removeEnvelopeSetpushButton_clicked() { fmEnvelopeTexts_.erase(fmEnvelopeTexts_.begin() + ui->envelopeTypeListWidget->currentRow()); updateEnvelopeSetUi(); } void ConfigurationDialog::on_editEnvelopeSetPushButton_clicked() { size_t row = static_cast(ui->envelopeTypeListWidget->currentRow()); FMEnvelopeSetEditDialog diag(fmEnvelopeTexts_.at(row).texts); diag.setWindowTitle(diag.windowTitle() + ": " + ui->envelopeSetNameLineEdit->text()); if (diag.exec() == QDialog::Accepted) { fmEnvelopeTexts_.at(row).texts = diag.getSet(); } } void ConfigurationDialog::on_envelopeSetNameLineEdit_textChanged(const QString &arg1) { fmEnvelopeTexts_.at(static_cast(ui->envelopeTypeListWidget->currentRow())).name = arg1.toStdString(); ui->envelopeTypeListWidget->currentItem()->setText(arg1); } void ConfigurationDialog::on_envelopeTypeListWidget_currentRowChanged(int currentRow) { if (currentRow == -1) { ui->editEnvelopeSetPushButton->setEnabled(false); ui->removeEnvelopeSetpushButton->setEnabled(false); ui->envelopeSetNameLineEdit->setEnabled(false); } else { ui->editEnvelopeSetPushButton->setEnabled(true); ui->removeEnvelopeSetpushButton->setEnabled(true); ui->envelopeSetNameLineEdit->setEnabled(true); ui->envelopeSetNameLineEdit->setText(ui->envelopeTypeListWidget->item(currentRow)->text()); } } void ConfigurationDialog::updateEnvelopeSetUi() { std::sort(fmEnvelopeTexts_.begin(), fmEnvelopeTexts_.end(), [](const FMEnvelopeText& a, const FMEnvelopeText& b) -> bool { return (a.name < b.name); }); ui->envelopeTypeListWidget->clear(); for (auto& texts : fmEnvelopeTexts_) ui->envelopeTypeListWidget->addItem(gui_utils::utf8ToQString(texts.name)); } /***** Keys *****/ void ConfigurationDialog::on_keyboardTypeComboBox_currentIndexChanged(int index) { Q_UNUSED(index) bool enableCustomLayoutInterface = ui->keyboardTypeComboBox->currentIndex() == 0; ui->lowHighKeysTabWidget->setEnabled(enableCustomLayoutInterface); ui->customLayoutResetButton->setEnabled(enableCustomLayoutInterface); } void ConfigurationDialog::on_customLayoutResetButton_clicked() { std::unordered_map QWERTYLayoutMapping = config_.lock()->mappingLayouts.at (Configuration::KeyboardLayout::QWERTY); std::unordered_map::const_iterator QWERTYLayoutMappingIterator = QWERTYLayoutMapping.begin(); while (QWERTYLayoutMappingIterator != QWERTYLayoutMapping.end()) { customLayoutKeysMap_.at(QWERTYLayoutMappingIterator->second)->setKeySequence(QKeySequence(QString::fromStdString(QWERTYLayoutMappingIterator->first))); QWERTYLayoutMappingIterator++; } } void ConfigurationDialog::addShortcutItem(QString action, std::string shortcut) { int row = ui->shortcutsTreeWidget->topLevelItemCount(); auto titem = new QTreeWidgetItem(); titem->setText(0, action); ui->shortcutsTreeWidget->insertTopLevelItem(row, titem); ui->shortcutsTreeWidget->setItemWidget(titem, 1, new QKeySequenceEdit(gui_utils::utf8ToQString(shortcut))); } std::string ConfigurationDialog::getShortcutString(int row) const { return qobject_cast( ui->shortcutsTreeWidget->itemWidget(ui->shortcutsTreeWidget->topLevelItem(row), 1) )->keySequence().toString().toStdString(); } /***** Appearance *****/ void ConfigurationDialog::on_colorEditPushButton_clicked() { QTreeWidgetItem* item = ui->colorsTreeWidget->currentItem(); if (item == nullptr || item->parent() == nullptr) return; QColorDialog diag(item->data(1, Qt::BackgroundRole).value()); diag.setOption(QColorDialog::ShowAlphaChannel); if (diag.exec() == QDialog::Accepted) item->setData(1, Qt::BackgroundRole, diag.currentColor()); } void ConfigurationDialog::on_colorLoadPushButton_clicked() { QString file = QFileDialog::getOpenFileName(this, tr("Open color scheme"), QApplication::applicationDirPath() + "/skins", tr("ini file (*.ini)")); if (file.isNull()) return; ColorPalette palette; if (ColorPaletteHandler::loadPalette(file, &palette)) { updateColorTreeFrom(&palette); } else { QMessageBox::critical(this, tr("Error"), tr("An unknown error occurred while loading the color scheme.")); } } void ConfigurationDialog::on_colorSavePushButton_clicked() { QString file = QFileDialog::getSaveFileName(this, tr("Save color scheme"), QApplication::applicationDirPath() + "/skins", tr("ini file (*.ini)")); if (file.isNull()) return; if (!file.endsWith(".ini")) file += ".ini"; // For linux ColorPalette palette; setPaletteFromColorTree(&palette); if (!ColorPaletteHandler::savePalette(file, &palette)) QMessageBox::critical(this, tr("Error"), tr("Failed to save the color scheme.")); } void ConfigurationDialog::updateColorTreeFrom(const ColorPalette* const palette) { QTreeWidgetItem* ptnColors = ui->colorsTreeWidget->topLevelItem(0); ptnColors->child(0)->setData(1, Qt::BackgroundRole, palette->ptnDefTextColor); ptnColors->child(1)->setData(1, Qt::BackgroundRole, palette->ptnDefStepColor); ptnColors->child(2)->setData(1, Qt::BackgroundRole, palette->ptnHl1StepColor); ptnColors->child(3)->setData(1, Qt::BackgroundRole, palette->ptnHl2StepColor); ptnColors->child(4)->setData(1, Qt::BackgroundRole, palette->ptnCurTextColor); ptnColors->child(5)->setData(1, Qt::BackgroundRole, palette->ptnCurStepColor); ptnColors->child(6)->setData(1, Qt::BackgroundRole, palette->ptnCurEditStepColor); ptnColors->child(7)->setData(1, Qt::BackgroundRole, palette->ptnCurCellColor); ptnColors->child(8)->setData(1, Qt::BackgroundRole, palette->ptnPlayStepColor); ptnColors->child(9)->setData(1, Qt::BackgroundRole, palette->ptnSelCellColor); ptnColors->child(10)->setData(1, Qt::BackgroundRole, palette->ptnHovCellColor); ptnColors->child(11)->setData(1, Qt::BackgroundRole, palette->ptnDefStepNumColor); ptnColors->child(12)->setData(1, Qt::BackgroundRole, palette->ptnHl1StepNumColor); ptnColors->child(13)->setData(1, Qt::BackgroundRole, palette->ptnHl2StepNumColor); ptnColors->child(14)->setData(1, Qt::BackgroundRole, palette->ptnNoteColor); ptnColors->child(15)->setData(1, Qt::BackgroundRole, palette->ptnInstColor); ptnColors->child(16)->setData(1, Qt::BackgroundRole, palette->ptnVolColor); ptnColors->child(17)->setData(1, Qt::BackgroundRole, palette->ptnEffColor); ptnColors->child(18)->setData(1, Qt::BackgroundRole, palette->ptnErrorColor); ptnColors->child(19)->setData(1, Qt::BackgroundRole, palette->ptnHeaderTextColor); ptnColors->child(20)->setData(1, Qt::BackgroundRole, palette->ptnHeaderRowColor); ptnColors->child(21)->setData(1, Qt::BackgroundRole, palette->ptnMaskColor); ptnColors->child(22)->setData(1, Qt::BackgroundRole, palette->ptnBorderColor); ptnColors->child(23)->setData(1, Qt::BackgroundRole, palette->ptnHeaderBorderColor); ptnColors->child(24)->setData(1, Qt::BackgroundRole, palette->ptnMuteColor); ptnColors->child(25)->setData(1, Qt::BackgroundRole, palette->ptnUnmuteColor); ptnColors->child(26)->setData(1, Qt::BackgroundRole, palette->ptnBackColor); ptnColors->child(27)->setData(1, Qt::BackgroundRole, palette->ptnMarkerColor); ptnColors->child(28)->setData(1, Qt::BackgroundRole, palette->ptnUnfocusedShadowColor); QTreeWidgetItem* odrColors = ui->colorsTreeWidget->topLevelItem(1); odrColors->child(0)->setData(1, Qt::BackgroundRole, palette->odrDefTextColor); odrColors->child(1)->setData(1, Qt::BackgroundRole, palette->odrDefRowColor); odrColors->child(2)->setData(1, Qt::BackgroundRole, palette->odrCurTextColor); odrColors->child(3)->setData(1, Qt::BackgroundRole, palette->odrCurRowColor); odrColors->child(4)->setData(1, Qt::BackgroundRole, palette->odrCurEditRowColor); odrColors->child(5)->setData(1, Qt::BackgroundRole, palette->odrCurCellColor); odrColors->child(6)->setData(1, Qt::BackgroundRole, palette->odrPlayRowColor); odrColors->child(7)->setData(1, Qt::BackgroundRole, palette->odrSelCellColor); odrColors->child(8)->setData(1, Qt::BackgroundRole, palette->odrHovCellColor); odrColors->child(9)->setData(1, Qt::BackgroundRole, palette->odrRowNumColor); odrColors->child(10)->setData(1, Qt::BackgroundRole, palette->odrHeaderTextColor); odrColors->child(11)->setData(1, Qt::BackgroundRole, palette->odrHeaderRowColor); odrColors->child(12)->setData(1, Qt::BackgroundRole, palette->odrBorderColor); odrColors->child(13)->setData(1, Qt::BackgroundRole, palette->odrHeaderBorderColor); odrColors->child(14)->setData(1, Qt::BackgroundRole, palette->odrBackColor); odrColors->child(15)->setData(1, Qt::BackgroundRole, palette->odrUnfocusedShadowColor); QTreeWidgetItem* ilistColors = ui->colorsTreeWidget->topLevelItem(2); ilistColors->child(0)->setData(1, Qt::BackgroundRole, palette->ilistTextColor); ilistColors->child(1)->setData(1, Qt::BackgroundRole, palette->ilistBackColor); ilistColors->child(2)->setData(1, Qt::BackgroundRole, palette->ilistSelBackColor); ilistColors->child(3)->setData(1, Qt::BackgroundRole, palette->ilistHovBackColor); ilistColors->child(4)->setData(1, Qt::BackgroundRole, palette->ilistHovSelBackColor); QTreeWidgetItem* wavColors = ui->colorsTreeWidget->topLevelItem(3); wavColors->child(0)->setData(1, Qt::BackgroundRole, palette->wavBackColor); wavColors->child(1)->setData(1, Qt::BackgroundRole, palette->wavDrawColor); } void ConfigurationDialog::setPaletteFromColorTree(ColorPalette* const palette) { QTreeWidgetItem* ptnColors = ui->colorsTreeWidget->topLevelItem(0); palette->ptnDefTextColor = ptnColors->child(0)->data(1, Qt::BackgroundRole).value(); palette->ptnDefStepColor = ptnColors->child(1)->data(1, Qt::BackgroundRole).value(); palette->ptnHl1StepColor = ptnColors->child(2)->data(1, Qt::BackgroundRole).value(); palette->ptnHl2StepColor = ptnColors->child(3)->data(1, Qt::BackgroundRole).value(); palette->ptnCurTextColor = ptnColors->child(4)->data(1, Qt::BackgroundRole).value(); palette->ptnCurStepColor = ptnColors->child(5)->data(1, Qt::BackgroundRole).value(); palette->ptnCurEditStepColor = ptnColors->child(6)->data(1, Qt::BackgroundRole).value(); palette->ptnCurCellColor = ptnColors->child(7)->data(1, Qt::BackgroundRole).value(); palette->ptnPlayStepColor = ptnColors->child(8)->data(1, Qt::BackgroundRole).value(); palette->ptnSelCellColor = ptnColors->child(9)->data(1, Qt::BackgroundRole).value(); palette->ptnHovCellColor = ptnColors->child(10)->data(1, Qt::BackgroundRole).value(); palette->ptnDefStepNumColor = ptnColors->child(11)->data(1, Qt::BackgroundRole).value(); palette->ptnHl1StepNumColor = ptnColors->child(12)->data(1, Qt::BackgroundRole).value(); palette->ptnHl2StepNumColor = ptnColors->child(13)->data(1, Qt::BackgroundRole).value(); palette->ptnNoteColor = ptnColors->child(14)->data(1, Qt::BackgroundRole).value(); palette->ptnInstColor = ptnColors->child(15)->data(1, Qt::BackgroundRole).value(); palette->ptnVolColor = ptnColors->child(16)->data(1, Qt::BackgroundRole).value(); palette->ptnEffColor = ptnColors->child(17)->data(1, Qt::BackgroundRole).value(); palette->ptnErrorColor = ptnColors->child(18)->data(1, Qt::BackgroundRole).value(); palette->ptnHeaderTextColor = ptnColors->child(19)->data(1, Qt::BackgroundRole).value(); palette->ptnHeaderRowColor = ptnColors->child(20)->data(1, Qt::BackgroundRole).value(); palette->ptnMaskColor = ptnColors->child(21)->data(1, Qt::BackgroundRole).value(); palette->ptnBorderColor = ptnColors->child(22)->data(1, Qt::BackgroundRole).value(); palette->ptnHeaderBorderColor = ptnColors->child(23)->data(1, Qt::BackgroundRole).value(); palette->ptnMuteColor = ptnColors->child(24)->data(1, Qt::BackgroundRole).value(); palette->ptnUnmuteColor = ptnColors->child(25)->data(1, Qt::BackgroundRole).value(); palette->ptnBackColor = ptnColors->child(26)->data(1, Qt::BackgroundRole).value(); palette->ptnMarkerColor = ptnColors->child(27)->data(1, Qt::BackgroundRole).value(); palette->ptnUnfocusedShadowColor = ptnColors->child(28)->data(1, Qt::BackgroundRole).value(); QTreeWidgetItem* odrColors = ui->colorsTreeWidget->topLevelItem(1); palette->odrDefTextColor = odrColors->child(0)->data(1, Qt::BackgroundRole).value(); palette->odrDefRowColor = odrColors->child(1)->data(1, Qt::BackgroundRole).value(); palette->odrCurTextColor = odrColors->child(2)->data(1, Qt::BackgroundRole).value(); palette->odrCurRowColor = odrColors->child(3)->data(1, Qt::BackgroundRole).value(); palette->odrCurEditRowColor = odrColors->child(4)->data(1, Qt::BackgroundRole).value(); palette->odrCurCellColor = odrColors->child(5)->data(1, Qt::BackgroundRole).value(); palette->odrPlayRowColor = odrColors->child(6)->data(1, Qt::BackgroundRole).value(); palette->odrSelCellColor = odrColors->child(7)->data(1, Qt::BackgroundRole).value(); palette->odrHovCellColor = odrColors->child(8)->data(1, Qt::BackgroundRole).value(); palette->odrRowNumColor = odrColors->child(9)->data(1, Qt::BackgroundRole).value(); palette->odrHeaderTextColor = odrColors->child(10)->data(1, Qt::BackgroundRole).value(); palette->odrHeaderRowColor = odrColors->child(11)->data(1, Qt::BackgroundRole).value(); palette->odrBorderColor = odrColors->child(12)->data(1, Qt::BackgroundRole).value(); palette->odrHeaderBorderColor = odrColors->child(13)->data(1, Qt::BackgroundRole).value(); palette->odrBackColor = odrColors->child(14)->data(1, Qt::BackgroundRole).value(); palette->odrUnfocusedShadowColor = odrColors->child(15)->data(1, Qt::BackgroundRole).value(); QTreeWidgetItem* ilistColors = ui->colorsTreeWidget->topLevelItem(2); palette->ilistTextColor = ilistColors->child(0)->data(1, Qt::BackgroundRole).value(); palette->ilistBackColor = ilistColors->child(1)->data(1, Qt::BackgroundRole).value(); palette->ilistSelBackColor = ilistColors->child(2)->data(1, Qt::BackgroundRole).value(); palette->ilistHovBackColor = ilistColors->child(3)->data(1, Qt::BackgroundRole).value(); palette->ilistHovSelBackColor = ilistColors->child(4)->data(1, Qt::BackgroundRole).value(); QTreeWidgetItem* wavColors = ui->colorsTreeWidget->topLevelItem(3); palette->wavBackColor = wavColors->child(0)->data(1, Qt::BackgroundRole).value(); palette->wavDrawColor = wavColors->child(1)->data(1, Qt::BackgroundRole).value(); } BambooTracker-0.4.6/BambooTracker/gui/configuration_dialog.hpp000066400000000000000000000067711401124043500244370ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CONFIGURATION_DIALOG_HPP #define CONFIGURATION_DIALOG_HPP #include #include #include #include #include #include #include #include "configuration.hpp" #include "color_palette.hpp" #include "enum_hash.hpp" namespace Ui { class ConfigurationDialog; } class AudioStream; class ConfigurationDialog : public QDialog { Q_OBJECT public: ConfigurationDialog(std::weak_ptr config, std::weak_ptr palette, std::weak_ptr stream, QWidget *parent = nullptr); ~ConfigurationDialog() override; signals: void applyPressed(); private slots: void on_ConfigurationDialog_accepted(); private: Ui::ConfigurationDialog *ui; std::weak_ptr config_; std::weak_ptr refPalette_; std::weak_ptr stream_; std::unordered_map customLayoutKeysMap_; /***** General *****/ private slots: void on_generalSettingsListWidget_itemSelectionChanged(); /***** Mixer *****/ private slots: void on_mixerResetPushButton_clicked(); /***** Sound *****/ private slots: void on_audioApiComboBox_currentIndexChanged(const QString &arg1); void on_midiApiComboBox_currentIndexChanged(const QString &arg1); void onMidiApiChanged(const QString &arg1, bool hasInitialized = true); /***** Formats *****/ private: std::vector fmEnvelopeTexts_; private slots: void on_addEnvelopeSetPushButton_clicked(); void on_removeEnvelopeSetpushButton_clicked(); void on_editEnvelopeSetPushButton_clicked(); void on_envelopeSetNameLineEdit_textChanged(const QString &arg1); void on_envelopeTypeListWidget_currentRowChanged(int currentRow); void updateEnvelopeSetUi(); /***** Keys *****/ private slots: void on_keyboardTypeComboBox_currentIndexChanged(int index); void on_customLayoutResetButton_clicked(); private: std::unordered_map shortcutsMap_; void addShortcutItem(QString action, std::string shortcut); std::string getShortcutString(int row) const; /***** Appearance *****/ private slots: void on_colorEditPushButton_clicked(); void on_colorLoadPushButton_clicked(); void on_colorSavePushButton_clicked(); private: void updateColorTreeFrom(const ColorPalette* const palette); void setPaletteFromColorTree(ColorPalette* const palette); }; #endif // CONFIGURATION_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/configuration_dialog.ui000066400000000000000000002700231401124043500242560ustar00rootroot00000000000000 ConfigurationDialog 0 0 523 432 Configuration 0 General Edit settings Page jump length 1 128 4 Qt::Vertical 20 40 General settings Warp cursor Checked Warp across orders Checked Show row numbers in hex Checked Preview previous/next orders Checked Backup modules Checked Don't select on double click Unchecked Reverse FM volume order Checked Move cursor to right Unchecked Retrieve channel state Unchecked Enable translation Checked Show FM detune as signed Unchecked Fill 00 to effect value Checked Move cursor by horizontal scroll Checked Overwrite unused&unedited property Unchecked Write only used samples Unchecked Reflect instrument number change Unchecked Fix jamming volume Checked Mute hidden tracks Checked Restore track visibility Unchecked Qt::NoFocus true Description: Wave view Frame rate fps 20 100 30 Sound Sample rate Buffer length 1 500 Qt::Horizontal 1ms Qt::AlignCenter Qt::Vertical 20 40 MIDI input true false Device API Audio output Real chip interface Device API Emulation core Mixer Part 0 0 QFrame::StyledPanel QFrame::Raised 0 0 QFrame::StyledPanel QFrame::Raised The level setting for each part is valid when the mixer in the module properties is not checked. true Reset QFrame::StyledPanel QFrame::Raised Appearance Font and size Order list rows Pattern editor rows Pattern editor header Order list header 0 0 10 11 12 14 16 18 20 22 0 0 10 11 12 14 16 18 20 22 0 0 10 11 12 14 16 18 20 22 0 0 10 11 12 14 16 18 20 22 Colors Qt::Vertical 20 40 Edit QAbstractItemView::NoEditTriggers QAbstractItemView::SelectItems Item Color Pattern editor Default step text Default step background Highlighted step 1 background Highlighted step 2 background Current step text Current step background Current editing step background Current cell background Current playing step background Selection background Hovered cell background Default step number Highlighted step 1 number Highlighted step 2 number Note text Instrument text Volume text Effect text Error text Header text Header background Mask Border Header border Mute Unmute Background Marker Unfocused shadow Order list Default row text Default row background Current row text Current row background Current editing row background Current cell background Current playing row background Selection background Hovered cell background Row number Header text Header background Border Header border Background Unfocused shadow Instrument list Default text Background Selected background Hovered background Selected hovered background Oscilloscope Background Foreground Save Load Formats FM envelope text Add false Edit false Remove Qt::Vertical 20 40 false Keys Shortcuts false true 2 Action Keys 0 0 16777215 216 Note entry layout Custom 0 Custom QWERTY QWERTZ AZERTY 0 0 90 16777215 Reset 0 0 16777215 120 QLabel { border: 1px solid rgb(0, 0, 0); } QTabWidget::North QTabWidget::Rounded 1 false false Low 0 2 0 0 16 0 16777215 50 0 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); QFrame::NoFrame QFrame::Plain C Qt::AlignBottom|Qt::AlignHCenter false 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); QFrame::NoFrame C# false Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true false background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); QFrame::NoFrame D Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); D# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); E Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); F Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); F# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 false true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); G Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); G# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); A Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); A# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); B Qt::AlignBottom|Qt::AlignHCenter Qt::Horizontal QSizePolicy::Fixed 5 20 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); C Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); C# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); D Qt::AlignBottom|Qt::AlignHCenter 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 16 0 High 0 2 16 0 16 0 16 0 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); C# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); F Qt::AlignBottom|Qt::AlignHCenter 16 0 16 0 16 0 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); D Qt::AlignBottom|Qt::AlignHCenter 16 0 16 0 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); G# Qt::AlignBottom|Qt::AlignHCenter 16 0 16 0 16 0 16 0 16 0 75 true false background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); QFrame::NoFrame D Qt::AlignBottom|Qt::AlignHCenter 16 0 75 false true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); G Qt::AlignBottom|Qt::AlignHCenter 16 0 16 0 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); D# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); A Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); C Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); E Qt::AlignBottom|Qt::AlignHCenter 16 0 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); QFrame::NoFrame C# false Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); A# Qt::AlignBottom|Qt::AlignHCenter 16 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); B Qt::AlignBottom|Qt::AlignHCenter 0 0 16 0 16777215 50 0 0 75 true background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); QFrame::NoFrame QFrame::Plain C Qt::AlignBottom|Qt::AlignHCenter false 16 0 75 true background-color: rgb(0, 0, 0); color: rgb(255, 255, 255); F# Qt::AlignBottom|Qt::AlignHCenter Qt::Horizontal QSizePolicy::Fixed 5 20 Qt::Horizontal QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok LabeledVerticalSlider QFrame
gui/labeled_vertical_slider.hpp
1
tabWidget generalSettingsListWidget pageJumpLengthSpinBox waveViewRateSpinBox emulatorComboBox audioApiComboBox audioDeviceComboBox realChipComboBox midiInputGroupBox midiApiComboBox midiInputDeviceComboBox sampleRateComboBox bufferLengthHorizontalSlider mixerResetPushButton colorsTreeWidget colorLoadPushButton colorSavePushButton colorEditPushButton ptnHdFontComboBox ptnHdFontSizeComboBox ptnRowFontComboBox ptnRowFontSizeComboBox odrHdFontComboBox odrHdFontSizeComboBox odrRowFontComboBox odrRowFontSizeComboBox envelopeTypeListWidget envelopeSetNameLineEdit addEnvelopeSetPushButton removeEnvelopeSetpushButton editEnvelopeSetPushButton shortcutsTreeWidget keyboardTypeComboBox customLayoutResetButton lowHighKeysTabWidget lowCEdit lowCSEdit lowDEdit lowDSEdit lowEEdit lowFEdit lowFSEdit lowGEdit lowGSEdit lowAEdit lowASEdit lowBEdit lowHighCEdit lowHighCSEdit lowHighDEdit highCEdit highCSEdit highDEdit highDSEdit highEEdit highFEdit highFSEdit highGEdit highGSEdit highAEdit highASEdit highBEdit highHighCEdit highHighCSEdit highHighDEdit buttonBox accepted() ConfigurationDialog accept() 266 395 157 274 buttonBox rejected() ConfigurationDialog reject() 334 395 286 274 envelopeTypeListWidget itemDoubleClicked(QListWidgetItem*) editEnvelopeSetPushButton click() 185 189 412 262
BambooTracker-0.4.6/BambooTracker/gui/configuration_handler.cpp000066400000000000000000000716701401124043500246100ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "configuration_handler.hpp" #include #include #include #include #include "configuration.hpp" #include "jamming.hpp" #include "enum_hash.hpp" #include "gui/gui_utils.hpp" #include "utils.hpp" namespace io { namespace { // config path (*nix): ~/.config//.ini const QString ORG_NAME = "BambooTracker"; const QString APP_NAME = "BambooTracker"; const std::unordered_map SHORTCUTS_NAME_MAP = { { Configuration::ShortcutAction::KeyOff, "keyOff" }, { Configuration::ShortcutAction::OctaveUp, "octaveUp" }, { Configuration::ShortcutAction::OctaveDown, "octaveDown" }, { Configuration::ShortcutAction::EchoBuffer, "echoBuffer" }, { Configuration::ShortcutAction::PlayAndStop, "playAndStop" }, { Configuration::ShortcutAction::Play, "play" }, { Configuration::ShortcutAction::PlayFromStart, "playFromStart" }, { Configuration::ShortcutAction::PlayPattern, "playPattern" }, { Configuration::ShortcutAction::PlayFromCursor, "playFromCursor" }, { Configuration::ShortcutAction::PlayFromMarker, "playFromMarker" }, { Configuration::ShortcutAction::PlayStep, "playStep" }, { Configuration::ShortcutAction::Stop, "stop" }, { Configuration::ShortcutAction::FocusOnPattern, "ocusOnPattern" }, { Configuration::ShortcutAction::FocusOnOrder, "focusOnOrder" }, { Configuration::ShortcutAction::FocusOnInstrument, "focusOnInstrument" }, { Configuration::ShortcutAction::ToggleEditJam, "toggleEditJam" }, { Configuration::ShortcutAction::SetMarker, "setMarker" }, { Configuration::ShortcutAction::PasteMix, "pasteMix" }, { Configuration::ShortcutAction::PasteOverwrite, "pasteOverwrite" }, { Configuration::ShortcutAction::PasteInsert, "pasteInsert" }, { Configuration::ShortcutAction::SelectAll, "selectAll" }, { Configuration::ShortcutAction::Deselect, "deselect" }, { Configuration::ShortcutAction::SelectRow, "selectRow" }, { Configuration::ShortcutAction::SelectColumn, "selectColumn" }, { Configuration::ShortcutAction::SelectPattern, "selectPattern" }, { Configuration::ShortcutAction::SelectOrder, "selectOrder" }, { Configuration::ShortcutAction::GoToStep, "goToStep" }, { Configuration::ShortcutAction::ToggleTrack, "toggleTrack" }, { Configuration::ShortcutAction::SoloTrack, "soloTrack" }, { Configuration::ShortcutAction::Interpolate, "interpolate" }, { Configuration::ShortcutAction::Reverse, "reverse" }, { Configuration::ShortcutAction::GoToPrevOrder, "goToPrevOrder" }, { Configuration::ShortcutAction::GoToNextOrder, "goToNextOrder" }, { Configuration::ShortcutAction::ToggleBookmark, "toggleBookmark" }, { Configuration::ShortcutAction::PrevBookmark, "prevBookmark" }, { Configuration::ShortcutAction::NextBookmark, "nextBookmark" }, { Configuration::ShortcutAction::DecreaseNote, "decreaseNote" }, { Configuration::ShortcutAction::IncreaseNote, "increaseNote" }, { Configuration::ShortcutAction::DecreaseOctave, "decreaseOctave" }, { Configuration::ShortcutAction::IncreaseOctave, "increaseOctave" }, { Configuration::ShortcutAction::PrevInstrument, "prevInstrument" }, { Configuration::ShortcutAction::NextInstrument, "nextInstrument" }, { Configuration::ShortcutAction::MaskInstrument, "maskInstrument" }, { Configuration::ShortcutAction::MaskVolume, "maskVolume" }, { Configuration::ShortcutAction::EditInstrument, "editInstrument" }, { Configuration::ShortcutAction::FollowMode, "followMode" }, { Configuration::ShortcutAction::DuplicateOrder, "duplicateOrder" }, { Configuration::ShortcutAction::ClonePatterns, "clonePatterns" }, { Configuration::ShortcutAction::CloneOrder, "cloneOrder" }, { Configuration::ShortcutAction::ReplaceInstrument, "replaceInstrument" }, { Configuration::ShortcutAction::ExpandPattern, "expandPattern" }, { Configuration::ShortcutAction::ShrinkPattern, "shrinkPattern" }, { Configuration::ShortcutAction::FineDecreaseValues, "fineDecreaseValues" }, { Configuration::ShortcutAction::FineIncreaseValues, "fineIncreaseValues" }, { Configuration::ShortcutAction::CoarseDecreaseValues, "coarseDecreaseValues" }, { Configuration::ShortcutAction::CoarseIncreaseValuse, "coarseIncreaseValuse" }, { Configuration::ShortcutAction::ExpandEffect, "expandEffect" }, { Configuration::ShortcutAction::ShrinkEffect, "shrinkEffect" }, { Configuration::ShortcutAction::PrevHighlighted, "prevHighlightedStep" }, { Configuration::ShortcutAction::NextHighlighted, "nextHighlightedStep" }, { Configuration::ShortcutAction::IncreasePatternSize, "incPtnSize" }, { Configuration::ShortcutAction::DecreasePatternSize, "decPtnSize" }, { Configuration::ShortcutAction::IncreaseEditStep, "incEditStep" }, { Configuration::ShortcutAction::DecreaseEditStep, "decEditStep" }, { Configuration::ShortcutAction::DisplayEffectList, "dispEffectList" }, { Configuration::ShortcutAction::PreviousSong, "prevSong" }, { Configuration::ShortcutAction::NextSong, "nextSong" }, { Configuration::ShortcutAction::JamVolumeUp, "jamVolumeUp" }, { Configuration::ShortcutAction::JamVolumeDown, "jamVolumeDown" } }; const std::unordered_map JAM_KEY_NAME_MAP = { {JamKey::LowC, "lowC"}, {JamKey::LowCS, "lowCS"}, {JamKey::LowD, "lowD"}, {JamKey::LowDS, "lowDS"}, {JamKey::LowE, "lowE"}, {JamKey::LowF, "lowF"}, {JamKey::LowFS, "lowFS"}, {JamKey::LowG, "lowG"}, {JamKey::LowGS, "lowGS"}, {JamKey::LowA, "lowA"}, {JamKey::LowAS, "lowAS"}, {JamKey::LowB, "lowB"}, {JamKey::LowC2, "lowHighC"}, {JamKey::LowCS2, "lowHighCS"}, {JamKey::LowD2, "lowHighD"}, {JamKey::HighC, "highC"}, {JamKey::HighCS, "highCS"}, {JamKey::HighD, "highD"}, {JamKey::HighDS, "highDS"}, {JamKey::HighE, "highE"}, {JamKey::HighF, "highF"}, {JamKey::HighFS, "highFS"}, {JamKey::HighG, "highG"}, {JamKey::HighGS, "highGS"}, {JamKey::HighA, "highA"}, {JamKey::HighAS, "highAS"}, {JamKey::HighB, "highB"}, {JamKey::HighC2, "highHighC"}, {JamKey::HighCS2, "highHighCS"}, {JamKey::HighD2, "highHighD"} }; } bool saveConfiguration(std::weak_ptr config) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, APP_NAME); std::shared_ptr configLocked = config.lock(); // Internal // settings.beginGroup("Internal"); settings.setValue("mainWindowWidth", configLocked->getMainWindowWidth()); settings.setValue("mainWindowHeight", configLocked->getMainWindowHeight()); settings.setValue("mainWindowMaximized", configLocked->getMainWindowMaximized()); settings.setValue("mainWindowX", configLocked->getMainWindowX()); settings.setValue("mainWindowY", configLocked->getMainWindowY()); settings.setValue("mainWindowVerticalSplit", configLocked->getMainWindowVerticalSplit()); settings.setValue("instrumentFMWindowWidth", configLocked->getInstrumentFMWindowWidth()); settings.setValue("instrumentFMWindowHeight", configLocked->getInstrumentFMWindowHeight()); settings.setValue("instrumentSSGWindowWidth", configLocked->getInstrumentSSGWindowWidth()); settings.setValue("instrumentSSGWindowHeight", configLocked->getInstrumentSSGWindowHeight()); settings.setValue("instrumentADPCMWindowWidth", configLocked->getInstrumentADPCMWindowWidth()); settings.setValue("instrumentADPCMWindowHeight", configLocked->getInstrumentADPCMWindowHeight()); settings.setValue("instrumentDrumkitWindowWidth", configLocked->getInstrumentDrumkitWindowWidth()); settings.setValue("instrumentDrumkitWindowHeight", configLocked->getInstrumentDrumkitWindowHeight()); settings.setValue("followMode", configLocked->getFollowMode()); settings.setValue("workingDirectory", QString::fromStdString(configLocked->getWorkingDirectory())); settings.setValue("instrumentOpenFormat", configLocked->getInstrumentOpenFormat()); settings.setValue("bankOpenFormat", configLocked->getBankOpenFormat()); settings.setValue("instrumentMask", configLocked->getInstrumentMask()); settings.setValue("volumeMask", configLocked->getVolumeMask()); settings.setValue("visibleToolbar", configLocked->getVisibleToolbar()); settings.setValue("visibleStatusBar", configLocked->getVisibleStatusBar()); settings.setValue("visibleWaveView", configLocked->getVisibleWaveView()); settings.setValue("pasteMode", static_cast(configLocked->getPasteMode())); auto& mainTbConfig = configLocked->getMainToolbarConfiguration(); settings.setValue("mainToolbarPosition", static_cast(mainTbConfig.getPosition())); settings.setValue("mainToolbarNumber", mainTbConfig.getNumber()); settings.setValue("hasBreakBeforeMainToolbar", mainTbConfig.hasBreakBefore()); settings.setValue("mainToolbarX", mainTbConfig.getX()); settings.setValue("mainToolbarY", mainTbConfig.getY()); auto& subTbConfig = configLocked->getSubToolbarConfiguration(); settings.setValue("subToolbarPosition", static_cast(subTbConfig.getPosition())); settings.setValue("subToolbarNumber", subTbConfig.getNumber()); settings.setValue("hasBreakBeforesubToolbar", subTbConfig.hasBreakBefore()); settings.setValue("subToolbarX", subTbConfig.getX()); settings.setValue("subToolbarY", subTbConfig.getY()); settings.endGroup(); // General // // General settings settings.beginGroup("General"); settings.setValue("warpCursor", configLocked->getWarpCursor()); settings.setValue("warpAcrossOrders", configLocked->getWarpAcrossOrders()); settings.setValue("showRowNumberInHex", configLocked->getShowRowNumberInHex()); settings.setValue("showPreviousNextOrders", configLocked->getShowPreviousNextOrders()); settings.setValue("backupModule", configLocked->getBackupModules()); settings.setValue("dontSelectOnDoubleClick", configLocked->getDontSelectOnDoubleClick()); settings.setValue("reverseFMVolumeOrder", configLocked->getReverseFMVolumeOrder()); settings.setValue("moveCursorToRight", configLocked->getMoveCursorToRight()); settings.setValue("retrieveChannelState", configLocked->getRetrieveChannelState()); settings.setValue("enableTranslation", configLocked->getEnableTranslation()); settings.setValue("showFMDetuneAsSigned", configLocked->getShowFMDetuneAsSigned()); settings.setValue("fill00ToEffectValue", configLocked->getFill00ToEffectValue()); settings.setValue("moveCursorByHScroll", configLocked->getMoveCursorByHorizontalScroll()); settings.setValue("overwriteUnusedUnedited", configLocked->getOverwriteUnusedUneditedPropety()); settings.setValue("writeOnlyUsedSamples", configLocked->getWriteOnlyUsedSamples()); settings.setValue("reflectInstNumChange", configLocked->getReflectInstrumentNumberChange()); settings.setValue("fixJammingVolume", configLocked->getFixJammingVolume()); settings.setValue("muteHiddenTracks", configLocked->getMuteHiddenTracks()); settings.setValue("restoreTrackVisibility", configLocked->getRestoreTrackVisibility()); settings.endGroup(); // Edit settings settings.beginGroup("Editing"); settings.setValue("pageJumpLength", static_cast(configLocked->getPageJumpLength())); settings.setValue("editableStep", static_cast(configLocked->getEditableStep())); settings.setValue("keyRepetition", configLocked->getKeyRepetition()); settings.endGroup(); // Wave view settings.beginGroup("WaveView"); settings.setValue("frameRate", configLocked->getWaveViewFrameRate()); settings.endGroup(); // Keys settings.beginGroup("Keys"); for (const auto& pair : configLocked->getShortcuts()) { settings.setValue("shortcut_" + SHORTCUTS_NAME_MAP.at(pair.first), gui_utils::utf8ToQString(pair.second)); } settings.setValue("noteEntryLayout", static_cast(configLocked->getNoteEntryLayout())); for (const auto& pair : configLocked->getCustomLayoutKeys()) { settings.setValue("customLayout_" + JAM_KEY_NAME_MAP.at(pair.second), gui_utils::utf8ToQString(pair.first)); } settings.endGroup(); // Sound // settings.beginGroup("Sound"); settings.setValue("soundAPI", gui_utils::utf8ToQString(configLocked->getSoundAPI())); settings.setValue("soundDevice", gui_utils::utf8ToQString(configLocked->getSoundDevice())); settings.setValue("realChipInterface", static_cast(configLocked->getRealChipInterface())); settings.setValue("emulator", configLocked->getEmulator()); settings.setValue("sampleRate", static_cast(configLocked->getSampleRate())); settings.setValue("bufferLength", static_cast(configLocked->getBufferLength())); settings.endGroup(); // Midi // settings.beginGroup("Midi"); settings.setValue("midiEnabled", configLocked->getMidiEnabled()); settings.setValue("midiAPI", QString::fromStdString(configLocked->getMidiAPI())); settings.setValue("inputPort", QString::fromStdString(configLocked->getMidiInputPort())); settings.endGroup(); // Mixer // settings.beginGroup("Mixer"); settings.setValue("mixerVolumeMaster", configLocked->getMixerVolumeMaster()); settings.setValue("mixerVolumeFM", configLocked->getMixerVolumeFM()); settings.setValue("mixerVolumeSSG", configLocked->getMixerVolumeSSG()); settings.endGroup(); // Input // settings.beginGroup("Input"); settings.beginWriteArray("fmEnvelopeTextMap"); int n = 0; for (const FMEnvelopeText& texts : config.lock()->getFMEnvelopeTexts()) { settings.setArrayIndex(n++); settings.setValue("type", gui_utils::utf8ToQString(texts.name)); QStringList typeList; std::transform(texts.texts.begin(), texts.texts.end(), std::back_inserter(typeList), [](FMEnvelopeTextType type) { return QString::number(static_cast(type)); }); settings.setValue("order", typeList.join(",")); } settings.endArray(); settings.endGroup(); settings.beginGroup("Appearance"); settings.setValue("patternEditorHeaderFont", QString::fromStdString(configLocked->getPatternEditorHeaderFont())); settings.setValue("patternEditorHeaderFontSize", configLocked->getPatternEditorHeaderFontSize()); settings.setValue("patternEditorRowsFont", QString::fromStdString(configLocked->getPatternEditorRowsFont())); settings.setValue("patternEditorRowsFontSize", configLocked->getPatternEditorRowsFontSize()); settings.setValue("orderListHeaderFont", QString::fromStdString(configLocked->getOrderListHeaderFont())); settings.setValue("orderListHeaderFontSize", configLocked->getOrderListHeaderFontSize()); settings.setValue("orderListRowsFont", QString::fromStdString(configLocked->getOrderListRowsFont())); settings.setValue("orderListRowsFontSize", configLocked->getOrderListRowsFontSize()); settings.endGroup(); return true; } catch (...) { return false; } } bool loadConfiguration(std::weak_ptr config) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, APP_NAME); std::shared_ptr configLocked = config.lock(); // Internal // settings.beginGroup("Internal"); configLocked->setMainWindowWidth(settings.value("mainWindowWidth", configLocked->getMainWindowWidth()).toInt()); configLocked->setMainWindowHeight(settings.value("mainWindowHeight", configLocked->getMainWindowHeight()).toInt()); configLocked->setMainWindowMaximized(settings.value("mainWindowMaximized", configLocked->getMainWindowMaximized()).toBool()); configLocked->setMainWindowX(settings.value("mainWindowX", configLocked->getMainWindowX()).toInt()); configLocked->setMainWindowY(settings.value("mainWindowY", configLocked->getMainWindowY()).toInt()); configLocked->setMainWindowVerticalSplit(settings.value("mainWindowVerticalSplit", configLocked->getMainWindowVerticalSplit()).toInt()); configLocked->setInstrumentFMWindowWidth(settings.value("instrumentFMWindowWidth", configLocked->getInstrumentFMWindowWidth()).toInt()); configLocked->setInstrumentFMWindowHeight(settings.value("instrumentFMWindowHeight", configLocked->getInstrumentFMWindowHeight()).toInt()); configLocked->setInstrumentSSGWindowWidth(settings.value("instrumentSSGWindowWidth", configLocked->getInstrumentSSGWindowWidth()).toInt()); configLocked->setInstrumentSSGWindowHeight(settings.value("instrumentSSGWindowHeight", configLocked->getInstrumentSSGWindowHeight()).toInt()); configLocked->setInstrumentADPCMWindowWidth(settings.value("instrumentADPCMWindowWidth", configLocked->getInstrumentADPCMWindowWidth()).toInt()); configLocked->setInstrumentADPCMWindowHeight(settings.value("instrumentADPCMWindowHeight", configLocked->getInstrumentADPCMWindowHeight()).toInt()); configLocked->setInstrumentDrumkitWindowWidth(settings.value("instrumentDrumkitWindowWidth", configLocked->getInstrumentDrumkitWindowWidth()).toInt()); configLocked->setInstrumentDrumkitWindowHeight(settings.value("instrumentDrumkitWindowHeight", configLocked->getInstrumentDrumkitWindowHeight()).toInt()); configLocked->setFollowMode(settings.value("followMode", configLocked->getFollowMode()).toBool()); configLocked->setWorkingDirectory(settings.value("workingDirectory", QString::fromStdString(configLocked->getWorkingDirectory())).toString().toStdString()); configLocked->setInstrumentOpenFormat(settings.value("instrumentOpenFormat", configLocked->getInstrumentOpenFormat()).toInt()); configLocked->setBankOpenFormat(settings.value("bankOpenFormat", configLocked->getBankOpenFormat()).toInt()); configLocked->setInstrumentMask(settings.value("instrumentMask", configLocked->getInstrumentMask()).toBool()); configLocked->setVolumeMask(settings.value("volumeMask", configLocked->getVolumeMask()).toBool()); configLocked->setVisibleToolbar(settings.value("visibleToolbar", configLocked->getVisibleToolbar()).toBool()); configLocked->setVisibleStatusBar(settings.value("visibleStatusBar", configLocked->getVisibleStatusBar()).toBool()); configLocked->setVisibleWaveView(settings.value("visibleWaveView", configLocked->getVisibleWaveView()).toBool()); configLocked->setPasteMode(static_cast(settings.value("pasteMode", static_cast(configLocked->getPasteMode())).toInt())); auto& mainTbConfig = configLocked->getMainToolbarConfiguration(); mainTbConfig.setPosition(static_cast(settings.value("mainToolbarPosition", static_cast(mainTbConfig.getPosition())).toInt())); mainTbConfig.setNumber(settings.value("mainToolbarNumber", mainTbConfig.getNumber()).toInt()); mainTbConfig.setBreakBefore(settings.value("hasBreakBeforeMainToolbar", mainTbConfig.hasBreakBefore()).toBool()); mainTbConfig.setX(settings.value("mainToolbarX", mainTbConfig.getX()).toInt()); mainTbConfig.setY(settings.value("mainToolbarY", mainTbConfig.getY()).toInt()); auto& subTbConfig = configLocked->getSubToolbarConfiguration(); subTbConfig.setPosition(static_cast(settings.value("subToolbarPosition", static_cast(subTbConfig.getPosition())).toInt())); subTbConfig.setNumber(settings.value("subToolbarNumber", subTbConfig.getNumber()).toInt()); subTbConfig.setBreakBefore(settings.value("hasBreakBeforesubToolbar", subTbConfig.hasBreakBefore()).toBool()); subTbConfig.setX(settings.value("subToolbarX", subTbConfig.getX()).toInt()); subTbConfig.setY(settings.value("subToolbarY", subTbConfig.getY()).toInt()); settings.endGroup(); // General // // General settings settings.beginGroup("General"); configLocked->setWarpCursor(settings.value("warpCursor", configLocked->getWarpCursor()).toBool()); configLocked->setWarpAcrossOrders(settings.value("warpAcrossOrders", configLocked->getWarpAcrossOrders()).toBool()); configLocked->setShowRowNumberInHex(settings.value("showRowNumberInHex", configLocked->getShowRowNumberInHex()).toBool()); configLocked->setShowPreviousNextOrders(settings.value("showPreviousNextOrders", configLocked->getShowPreviousNextOrders()).toBool()); configLocked->setBackupModules(settings.value("backupModule", configLocked->getBackupModules()).toBool()); configLocked->setDontSelectOnDoubleClick(settings.value("dontSelectOnDoubleClick", configLocked->getDontSelectOnDoubleClick()).toBool()); configLocked->setReverseFMVolumeOrder(settings.value("reverseFMVolumeOrder", configLocked->getReverseFMVolumeOrder()).toBool()); configLocked->setMoveCursorToRight(settings.value("moveCursorToRight", configLocked->getMoveCursorToRight()).toBool()); configLocked->setRetrieveChannelState(settings.value("retrieveChannelState", configLocked->getRetrieveChannelState()).toBool()); configLocked->setEnableTranslation(settings.value("enableTranslation", configLocked->getEnableTranslation()).toBool()); configLocked->setShowFMDetuneAsSigned(settings.value("showFMDetuneAsSigned", configLocked->getShowFMDetuneAsSigned()).toBool()); configLocked->setFill00ToEffectValue(settings.value("fill00ToEffectValue", configLocked->getFill00ToEffectValue()).toBool()); configLocked->setMoveCursorByHorizontalScroll(settings.value("moveCursorByHScroll", configLocked->getMoveCursorByHorizontalScroll()).toBool()); configLocked->setOverwriteUnusedUneditedPropety(settings.value("overwriteUnusedUnedited", configLocked->getOverwriteUnusedUneditedPropety()).toBool()); configLocked->setWriteOnlyUsedSamples(settings.value("writeOnlyUsedSamples", configLocked->getWriteOnlyUsedSamples()).toBool()); configLocked->setReflectInstrumentNumberChange(settings.value("reflectInstNumChange", configLocked->getReflectInstrumentNumberChange()).toBool()); configLocked->setFixJammingVolume(settings.value("fixJammingVolume", configLocked->getFixJammingVolume()).toBool()); configLocked->setMuteHiddenTracks(settings.value("muteHiddenTracks", configLocked->getMuteHiddenTracks()).toBool()); configLocked->setRestoreTrackVisibility(settings.value("restoreTrackVisibility", configLocked->getRestoreTrackVisibility()).toBool()); if (settings.contains("autosetInstrument")) { // For compatibility before v0.4.0 configLocked->setInstrumentMask(!settings.value("autosetInstrument").toBool()); settings.remove("autosetInstrument"); } if (settings.contains("showWaveVisual")) { // For compatibility before v0.4.2 configLocked->setVisibleWaveView(settings.value("showWaveVisual").toBool()); settings.remove("showWaveVisual"); } settings.endGroup(); // Edit settings settings.beginGroup("Editing"); QVariant pageJumpLengthWorkaround; pageJumpLengthWorkaround.setValue(configLocked->getPageJumpLength()); configLocked->setPageJumpLength(static_cast(settings.value("pageJumpLength", pageJumpLengthWorkaround).toInt())); QVariant editableStepWorkaround; editableStepWorkaround.setValue(configLocked->getEditableStep()); configLocked->setEditableStep(static_cast(settings.value("editableStep", editableStepWorkaround).toInt())); configLocked->setKeyRepetition(settings.value("keyRepetition", configLocked->getKeyRepetition()).toBool()); settings.endGroup(); // Wave view settings.beginGroup("WaveView"); configLocked->setWaveViewFrameRate(settings.value("frameRate", configLocked->getWaveViewFrameRate()).toInt()); settings.endGroup(); // Keys settings.beginGroup("Keys"); std::unordered_map shortcuts; for (const auto& pair : SHORTCUTS_NAME_MAP) { std::string def = configLocked->getShortcuts().at(pair.first); shortcuts[pair.first] = settings.value("shortcut_" + pair.second, gui_utils::utf8ToQString(def)).toString().toUtf8().toStdString(); } configLocked->setShortcuts(shortcuts); configLocked->setNoteEntryLayout(static_cast( settings.value("noteEntryLayout", static_cast(configLocked->getNoteEntryLayout())).toInt())); std::unordered_map customLayoutNewKeys; for (const auto& pair : JAM_KEY_NAME_MAP) { JamKey currentlyWantedJamKey = pair.first; customLayoutNewKeys[ settings.value("customLayout_" + pair.second, QString::fromStdString((*utils::findIf(configLocked->mappingLayouts.at(Configuration::KeyboardLayout::QWERTY), [currentlyWantedJamKey](const std::pair& t) -> bool { return (t.second) == currentlyWantedJamKey;}) ).first)).toString().toUtf8().toStdString()] = currentlyWantedJamKey; } configLocked->setCustomLayoutKeys(customLayoutNewKeys); settings.endGroup(); // Sound // settings.beginGroup("Sound"); configLocked->setSoundAPI(settings.value("soundAPI", QString::fromStdString(configLocked->getSoundAPI())).toString().toUtf8().toStdString()); configLocked->setSoundDevice(settings.value("soundDevice", QString::fromStdString(configLocked->getSoundDevice())).toString().toUtf8().toStdString()); configLocked->setRealChipInterface(static_cast( settings.value("realChipInterface", static_cast(configLocked->getRealChipInterface())).toInt())); configLocked->setEmulator(settings.value("emulator", configLocked->getEmulator()).toInt()); QVariant sampleRateWorkaround; sampleRateWorkaround.setValue(configLocked->getSampleRate()); configLocked->setSampleRate(static_cast(settings.value("sampleRate", sampleRateWorkaround).toInt())); QVariant bufferLengthWorkaround; bufferLengthWorkaround.setValue(configLocked->getBufferLength()); configLocked->setBufferLength(static_cast(settings.value("bufferLength", bufferLengthWorkaround).toInt())); settings.endGroup(); // Midi // settings.beginGroup("Midi"); configLocked->setMidiEnabled(settings.value("midiEnabled", configLocked->getMidiEnabled()).toBool()); configLocked->setMidiAPI(settings.value("midiAPI", QString::fromStdString(configLocked->getMidiAPI())).toString().toStdString()); configLocked->setMidiInputPort(settings.value("inputPort", QString::fromStdString(configLocked->getMidiInputPort())).toString().toStdString()); settings.endGroup(); // Mixer // settings.beginGroup("Mixer"); configLocked->setMixerVolumeMaster(settings.value("mixerVolumeMaster", configLocked->getMixerVolumeMaster()).toInt()); configLocked->setMixerVolumeFM(settings.value("mixerVolumeFM", configLocked->getMixerVolumeFM()).toDouble()); configLocked->setMixerVolumeSSG(settings.value("mixerVolumeSSG", configLocked->getMixerVolumeSSG()).toDouble()); settings.endGroup(); // Input // settings.beginGroup("Input"); int size = settings.beginReadArray("fmEnvelopeTextMap"); std::vector fmEnvelopeTexts; for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); std::string type = settings.value("type").toString().toUtf8().toStdString(); std::vector data; const QStringList list = settings.value("order").toString().split(","); for (const QString& d : list) { data.push_back(static_cast(d.toInt())); } fmEnvelopeTexts.push_back({ type, data }); } if (!fmEnvelopeTexts.empty()) config.lock()->setFMEnvelopeTexts(fmEnvelopeTexts); settings.endArray(); settings.endGroup(); // Appearance settings.beginGroup("Appearance"); configLocked->setPatternEditorHeaderFont(settings.value("patternEditorHeaderFont", QString::fromStdString(configLocked->getPatternEditorHeaderFont())).toString().toStdString()); configLocked->setPatternEditorHeaderFontSize(settings.value("patternEditorHeaderFontSize", configLocked->getPatternEditorHeaderFontSize()).toInt()); configLocked->setPatternEditorRowsFont(settings.value("patternEditorRowsFont", QString::fromStdString(configLocked->getPatternEditorRowsFont())).toString().toStdString()); configLocked->setPatternEditorRowsFontSize(settings.value("patternEditorRowsFontSize", configLocked->getPatternEditorRowsFontSize()).toInt()); configLocked->setOrderListHeaderFont(settings.value("orderListHeaderFont", QString::fromStdString(configLocked->getOrderListHeaderFont())).toString().toStdString()); configLocked->setOrderListHeaderFontSize(settings.value("orderListHeaderFontSize", configLocked->getOrderListHeaderFontSize()).toInt()); configLocked->setOrderListRowsFont(settings.value("orderListRowsFont", QString::fromStdString(configLocked->getOrderListRowsFont())).toString().toStdString()); configLocked->setOrderListRowsFontSize(settings.value("orderListRowsFontSize", configLocked->getOrderListRowsFontSize()).toInt()); settings.endGroup(); return true; } catch (...) { return false; } } } BambooTracker-0.4.6/BambooTracker/gui/configuration_handler.hpp000066400000000000000000000026041401124043500246040ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CONFIGURATION_HANDLER_HPP #define CONFIGURATION_HANDLER_HPP #include class Configuration; namespace io { bool saveConfiguration(std::weak_ptr config); bool loadConfiguration(std::weak_ptr config); } #endif // CONFIGURATION_HANDLER_HPP BambooTracker-0.4.6/BambooTracker/gui/drop_detect_list_widget.cpp000066400000000000000000000034361401124043500251310ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "drop_detect_list_widget.hpp" #include #include #include #include DropDetectListWidget::DropDetectListWidget(QWidget* parent) : QListWidget(parent) {} void DropDetectListWidget::dropEvent(QDropEvent* event) { QListWidget::dropEvent(event); if (event->source() == this) { QListWidgetItem* tgt = itemAt(event->pos()); if (tgt == nullptr) return; int tgtIdx = indexAt(event->pos()).row(); int drpIdx; { // Only 1 item stored QByteArray&& ary = event->mimeData()->data("application/x-qabstractitemmodeldatalist"); QDataStream(&ary, QIODevice::ReadOnly) >> drpIdx; } emit itemDroppedAtItemIndex(drpIdx, tgtIdx); } } BambooTracker-0.4.6/BambooTracker/gui/drop_detect_list_widget.hpp000066400000000000000000000027461401124043500251410ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef DROP_DETECT_LIST_WIDGET_HPP #define DROP_DETECT_LIST_WIDGET_HPP #include class DropDetectListWidget : public QListWidget { Q_OBJECT public: explicit DropDetectListWidget(QWidget* parent = nullptr); signals: void itemDroppedAtItemIndex(int dropped, int target); protected: void dropEvent(QDropEvent* event) override; }; #endif // DROP_DETECT_LIST_WIDGET_HPP BambooTracker-0.4.6/BambooTracker/gui/effect_description.cpp000066400000000000000000000125401401124043500240720ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "effect_description.hpp" EffectDescription::EffectDescription() {} const std::unordered_map EffectDescription::details_ = { { EffectType::Arpeggio, { "00xy", QT_TR_NOOP("Arpeggio, x: 2nd note (0-F), y: 3rd note (0-F)") } }, { EffectType::PortamentoUp, { "01xx", QT_TR_NOOP("Portamento up, xx: depth (00-FF)") } }, { EffectType::PortamentoDown, { "02xx", QT_TR_NOOP("Portamento down, xx: depth (00-FF)") } }, { EffectType::TonePortamento, { "03xx", QT_TR_NOOP("Tone portamento, xx: depth (00-FF)") } }, { EffectType::Vibrato, { "04xy", QT_TR_NOOP("Vibrato, x: period (0-F), y: depth (0-F)") } }, { EffectType::Tremolo, { "07xx", QT_TR_NOOP("Tremolo, x: period (0-F), y: depth (0-F)") } }, { EffectType::Pan, { "08xx", QT_TR_NOOP("Pan, xx: 00 = no sound, 01 = right, 02 = left, 03 = center") } }, { EffectType::VolumeSlide, { "0Axy", QT_TR_NOOP("Volume slide, x: up (0-F), y: down (0-F)") } }, { EffectType::PositionJump, { "0Bxx", QT_TR_NOOP("Jump to beginning of order xx") } }, { EffectType::SongEnd, { "0Cxx", QT_TR_NOOP("End of song") } }, { EffectType::PatternBreak, { "0Dxx", QT_TR_NOOP("Jump to step xx of next order") } }, { EffectType::SpeedTempoChange, { "0Fxx", QT_TR_NOOP("Change speed (xx: 00-1F), change tempo (xx: 20-FF)") } }, { EffectType::NoteDelay, { "0Gxx", QT_TR_NOOP("Note delay, xx: count (00-FF)") } }, { EffectType::AutoEnvelope, { "0Hxy", QT_TR_NOOP("Auto envelope, x: shift amount (0-F), y: shape (0-F)") } }, { EffectType::HardEnvHighPeriod, { "0Ixx", QT_TR_NOOP("Hardware envelope period 1, xx: high byte (00-FF)") } }, { EffectType::HardEnvLowPeriod, { "0Jxx", QT_TR_NOOP("Hardware envelope period 2, xx: low byte (00-FF)") } }, { EffectType::Groove, { "0Oxx", QT_TR_NOOP("Set groove xx") } }, { EffectType::Detune, { "0Pxx", QT_TR_NOOP("Detune, xx: pitch (00-FF)") } }, { EffectType::NoteSlideUp, { "0Qxy", QT_TR_NOOP("Note slide up, x: count (0-F), y: seminote (0-F)") } }, { EffectType::NoteSlideDown, { "0Rxy", QT_TR_NOOP("Note slide down, x: count (0-F), y: seminote (0-F)") } }, { EffectType::NoteCut, { "0Sxx", QT_TR_NOOP("Note cut, xx: count (01-FF)") } }, { EffectType::TransposeDelay, { "0Txy", QT_TR_NOOP("Transpose delay, x: count (1-7: up, 9-F: down), y: seminote (0-F)") } }, { EffectType::ToneNoiseMix, { "0Vxx", QT_TR_NOOP("Tone/Noise mix, xx: 00 = no sound, 01 = tone, 02 = noise, 03 = tone & noise") } }, { EffectType::MasterVolume, { "0Vxx", QT_TR_NOOP("Master volume, xx: volume (00-3F)") } }, { EffectType::NoisePitch, { "0Wxx", QT_TR_NOOP("Noise pitch, xx: pitch (00-1F)") } }, { EffectType::RegisterAddress0, { "0Xxx", QT_TR_NOOP("Register address bank 0, xx: address (00-6B)") } }, { EffectType::RegisterAddress1, { "0Yxx", QT_TR_NOOP("Register address bank 1, xx: address (00-6B)") } }, { EffectType::RegisterValue, { "0Zxx", QT_TR_NOOP("Register value set, xx: value (00-FF)") } }, { EffectType::ARControl, { "Axyy", QT_TR_NOOP("AR control, x: operator (1-4), yy: attack rate (00-1F)") } }, { EffectType::Brightness, { "B0xx", QT_TR_NOOP("Brightness, xx: relative value (01-FF)") } }, { EffectType::DRControl, { "Dxyy", QT_TR_NOOP("DR control, x: operator (1-4), yy: decay rate (00-1F)") } }, { EffectType::FBControl, { "FBxx", QT_TR_NOOP("FB control, xx: feedback value (00-07)") } }, { EffectType::FineDetune, { "FPxx", QT_TR_NOOP("Fine detune, xx: pitch (00-FF)") } }, { EffectType::MLControl, { "MLxy", QT_TR_NOOP("ML control, x: operator (1-4), y: multiple (0-F)") } }, { EffectType::VolumeDelay, { "Mxyy", QT_TR_NOOP("Volume delay, x: count (1-F), yy: volume (00-FF)") } }, { EffectType::RRControl, { "RRxy", QT_TR_NOOP("RR control, x: operator (1-4), y: release rate (0-F)") } }, { EffectType::TLControl, { "Txyy", QT_TR_NOOP("TL control, x: operator (1-4), yy: total level (00-7F)") } }, { EffectType::NoEffect, { "", QT_TR_NOOP("Invalid effect") } } }; QString EffectDescription::getEffectFormat(const EffectType type) { return details_.at(type).format; } QString EffectDescription::getEffectDescription(const EffectType type) { return tr(details_.at(type).desc); } QString EffectDescription::getEffectFormatAndDetailString(const EffectType type) { if (type == EffectType::NoEffect) return tr(details_.at(EffectType::NoEffect).desc); else return details_.at(type).mergedString(); } BambooTracker-0.4.6/BambooTracker/gui/effect_description.hpp000066400000000000000000000034401401124043500240760ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef EFFECTDESCRIPTION_HPP #define EFFECTDESCRIPTION_HPP #include #include #include #include "effect.hpp" #include "enum_hash.hpp" class EffectDescription : public QObject { Q_OBJECT public: static QString getEffectFormat(const EffectType type); static QString getEffectDescription(const EffectType type); static QString getEffectFormatAndDetailString(const EffectType type); private: EffectDescription(); struct EffectDetail { const QString format; const char* desc; QString mergedString() const { return format + " - " + tr(desc); } }; static const std::unordered_map details_; }; #endif // EFFECTDESCRIPTION_HPP BambooTracker-0.4.6/BambooTracker/gui/effect_list_dialog.cpp000066400000000000000000000131011401124043500240330ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "effect_list_dialog.hpp" #include "ui_effect_list_dialog.h" #include #include #include "gui/effect_description.hpp" EffectListDialog::EffectListDialog(QWidget *parent) : QDialog(parent), ui(new Ui::EffectListDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui->tableWidget->setColumnWidth(0, 50); ui->tableWidget->setColumnWidth(1, 100); addRow(EffectType::Arpeggio, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::PortamentoUp, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::PortamentoDown, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::TonePortamento, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::Vibrato, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::Tremolo, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::Pan, { SoundSource::FM, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::VolumeSlide, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::PositionJump, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::SongEnd, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::PatternBreak, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::SpeedTempoChange, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::NoteDelay, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::AutoEnvelope, { SoundSource::SSG }); addRow(EffectType::HardEnvHighPeriod, { SoundSource::SSG }); addRow(EffectType::HardEnvLowPeriod, { SoundSource::SSG }); addRow(EffectType::Groove, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::Detune, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::NoteSlideUp, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::NoteSlideDown, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::NoteCut, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::TransposeDelay, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::ToneNoiseMix, { SoundSource::SSG }); addRow(EffectType::MasterVolume, { SoundSource::RHYTHM }); addRow(EffectType::NoisePitch, { SoundSource::SSG }); addRow(EffectType::RegisterAddress0, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::RegisterAddress1, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::RegisterValue, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::ARControl, { SoundSource::FM }); addRow(EffectType::Brightness, { SoundSource::FM }); addRow(EffectType::DRControl, { SoundSource::FM }); addRow(EffectType::FBControl, { SoundSource::FM }); addRow(EffectType::FineDetune, { SoundSource::FM, SoundSource::SSG, SoundSource::ADPCM }); addRow(EffectType::VolumeDelay, { SoundSource::FM, SoundSource::SSG, SoundSource::RHYTHM, SoundSource::ADPCM }); addRow(EffectType::MLControl, { SoundSource::FM }); addRow(EffectType::RRControl, { SoundSource::FM }); addRow(EffectType::TLControl, { SoundSource::FM }); } EffectListDialog::~EffectListDialog() { delete ui; } void EffectListDialog::addRow(EffectType effect, std::unordered_set types) { int row = ui->tableWidget->rowCount(); ui->tableWidget->insertRow(row); ui->tableWidget->setItem(row, 0, new QTableWidgetItem(EffectDescription::getEffectFormat(effect))); ui->tableWidget->setRowHeight(row, ui->tableWidget->horizontalHeader()->height()); QString typeStr(""); if (types.count(SoundSource::FM)) typeStr += "FM"; if (types.count(SoundSource::SSG)) typeStr = typeStr + (typeStr.isEmpty() ? "" : ", ") + "SSG"; if (types.count(SoundSource::RHYTHM)) typeStr = typeStr + (typeStr.isEmpty() ? "" : ", ") + tr("Rhythm"); if (types.count(SoundSource::ADPCM)) typeStr = typeStr + (typeStr.isEmpty() ? "" : ", ") + "ADPCM"; ui->tableWidget->setItem(row, 1, new QTableWidgetItem(typeStr)); ui->tableWidget->setItem(row, 2, new QTableWidgetItem(EffectDescription::getEffectDescription(effect))); } BambooTracker-0.4.6/BambooTracker/gui/effect_list_dialog.hpp000066400000000000000000000031111401124043500240400ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef EFFECT_LIST_DIALOG_HPP #define EFFECT_LIST_DIALOG_HPP #include #include #include "effect.hpp" #include "enum_hash.hpp" namespace Ui { class EffectListDialog; } class EffectListDialog : public QDialog { Q_OBJECT public: explicit EffectListDialog(QWidget *parent = nullptr); ~EffectListDialog(); private: Ui::EffectListDialog *ui; void addRow(EffectType effect, std::unordered_set types); }; #endif // EFFECT_LIST_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/effect_list_dialog.ui000066400000000000000000000043001401124043500236670ustar00rootroot00000000000000 EffectListDialog 0 0 530 400 Effect list QAbstractItemView::NoEditTriggers 27 true false Effect Track type Description Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() EffectListDialog accept() 248 254 157 274 buttonBox rejected() EffectListDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/event_guard.cpp000066400000000000000000000024701401124043500225370ustar00rootroot00000000000000/* * Copyright (C) 2018 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "event_guard.hpp" namespace Ui { EventGuard::EventGuard(bool& isIgnoreEvent) : isIgnoreEvent_(isIgnoreEvent) { isIgnoreEvent_ = true; } EventGuard::~EventGuard() { isIgnoreEvent_ = false; } } BambooTracker-0.4.6/BambooTracker/gui/event_guard.hpp000066400000000000000000000025001401124043500225360ustar00rootroot00000000000000/* * Copyright (C) 2018 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef EVENT_GUARD_HPP #define EVENT_GUARD_HPP namespace Ui { class EventGuard { public: explicit EventGuard(bool& isIgnoreEvent); ~EventGuard(); private: bool& isIgnoreEvent_; }; } #endif // EVENT_GUARD_HPP BambooTracker-0.4.6/BambooTracker/gui/file_history.cpp000066400000000000000000000032501401124043500227310ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "file_history.hpp" #include #include "utils.hpp" namespace { const int HISTORY_SIZE = 8; } FileHistory::FileHistory() {} void FileHistory::addFile(QString path) { auto it = utils::find(list_, path); if (it != list_.end()) list_.erase(it); list_.push_front(path); if (list_.size() > HISTORY_SIZE) list_.pop_back(); } void FileHistory::clearHistory() { list_.clear(); } QString FileHistory::at(size_t i) { return list_.at(i); } size_t FileHistory::size() const { return list_.size(); } bool FileHistory::empty() const { return list_.empty(); } BambooTracker-0.4.6/BambooTracker/gui/file_history.hpp000066400000000000000000000026451401124043500227450ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FILE_HISTORY_HPP #define FILE_HISTORY_HPP #include #include class FileHistory { public: FileHistory(); void addFile(QString path); void clearHistory(); QString at(size_t i); size_t size() const; bool empty() const; private: std::deque list_; }; #endif // FILE_HISTORY_HPP BambooTracker-0.4.6/BambooTracker/gui/file_history_handler.cpp000066400000000000000000000044401401124043500244300ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "file_history_handler.hpp" #include // config path (*nix): ~/.config//.ini const QString FileHistoryHandler::ORGANIZATION_ = "BambooTracker"; const QString FileHistoryHandler::FILE_ = "FileHistory"; FileHistoryHandler::FileHistoryHandler() {} bool FileHistoryHandler::saveFileHistory(std::weak_ptr history) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORGANIZATION_, FILE_); settings.beginWriteArray("fileHistory"); int n = 0; for (size_t i = 0; i < history.lock()->size(); ++i) { settings.setArrayIndex(n++); settings.setValue("path", history.lock()->at(i)); } settings.endArray(); return true; } catch (...) { return false; } } bool FileHistoryHandler::loadFileHistory(std::weak_ptr history) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORGANIZATION_, FILE_); int size = settings.beginReadArray("fileHistory"); history.lock()->clearHistory(); for (int i = size - 1; 0 <= i; --i) { settings.setArrayIndex(i); history.lock()->addFile(settings.value("path").toString()); } settings.endArray(); return true; } catch (...) { return false; } } BambooTracker-0.4.6/BambooTracker/gui/file_history_handler.hpp000066400000000000000000000030301401124043500244270ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FILE_HISTORY_HANDLER_HPP #define FILE_HISTORY_HANDLER_HPP #include #include #include "file_history.hpp" class FileHistoryHandler { public: static bool saveFileHistory(std::weak_ptr history); static bool loadFileHistory(std::weak_ptr history); private: const static QString ORGANIZATION_; const static QString FILE_; FileHistoryHandler(); }; #endif // FILE_HISTORY_HANDLER_HPP BambooTracker-0.4.6/BambooTracker/gui/file_io_error_message_box.cpp000066400000000000000000000060751401124043500254340ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "file_io_error_message_box.hpp" #include const std::unordered_map FileIOErrorMessageBox::FILE_NAMES_ = { { io::FileType::Mod, QT_TR_NOOP("module") }, { io::FileType::S98, QT_TR_NOOP("s98") }, { io::FileType::VGM, QT_TR_NOOP("vgm") }, { io::FileType::WAV, QT_TR_NOOP("wav") }, { io::FileType::Bank, QT_TR_NOOP("bank") }, { io::FileType::Inst, QT_TR_NOOP("instrument") } }; FileIOErrorMessageBox::FileIOErrorMessageBox(const QString& file, bool isInput, io::FileType ftype, const QString desc, QWidget* parent) : parent_(parent), desc_(desc) { setText(file, isInput, ftype); } FileIOErrorMessageBox::FileIOErrorMessageBox(const QString& file, bool isInput, const io::FileIOError& e, QWidget* parent) : parent_(parent) { const io::FileIOError *err = &e; QString type = FILE_NAMES_.at(err->fileType()); if (dynamic_cast(err)) { desc_ = tr("Path does not exist."); } else if (dynamic_cast(err)) { desc_ = tr("Unsupported file format."); } else if (dynamic_cast(err)) { desc_ = tr("Could not load the %1 properly. " "Please make sure that you have the latest version of BambooTracker.").arg(file); } else if (auto ce = dynamic_cast(err)) { desc_ = tr("Could not load the %1. It may be corrupted. Stopped at %2.").arg(type).arg(ce->position()); } setText(file, isInput, e.fileType()); } void FileIOErrorMessageBox::setText(const QString& file, bool isInput, io::FileType ftype) { if (isInput) { text_ = tr("Failed to load %1.").arg(file); } else { switch (ftype) { case io::FileType::S98: case io::FileType::VGM: case io::FileType::WAV: text_ = tr("Failed to export to %1."); break; default: text_ = tr("Failed to save the %1."); break; } text_ = text_.arg(file); } } void FileIOErrorMessageBox::exec() { QMessageBox::critical(parent_, tr("Error"), text_ + "\n" + desc_); } BambooTracker-0.4.6/BambooTracker/gui/file_io_error_message_box.hpp000066400000000000000000000041061401124043500254320ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FILE_IO_ERROR_MESSAGE_BOX_HPP #define FILE_IO_ERROR_MESSAGE_BOX_HPP #include #include #include #include "io/io_file_type.hpp" #include "io/file_io_error.hpp" #include "enum_hash.hpp" class FileIOErrorMessageBox : public QObject { Q_OBJECT public: FileIOErrorMessageBox(const QString& file, bool isInput, io::FileType ftype, const QString desc, QWidget* parent = nullptr); FileIOErrorMessageBox(const QString& file, bool isInput, const io::FileIOError& e, QWidget* parent = nullptr); void exec(); inline static void openError(const QString& file, bool isInput, io::FileType ftype, QWidget* parent = nullptr) { FileIOErrorMessageBox(file, isInput, ftype, tr("Could not open the file."), parent).exec(); } private: QWidget* parent_; QString text_, desc_; static const std::unordered_map FILE_NAMES_; void setText(const QString& file, bool isInput, io::FileType ftype); }; #endif // FILE_IO_ERROR_MESSAGE_BOX_HPP BambooTracker-0.4.6/BambooTracker/gui/fm_envelope_set_edit_dialog.cpp000066400000000000000000000203631401124043500257330ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "fm_envelope_set_edit_dialog.hpp" #include "ui_fm_envelope_set_edit_dialog.h" #include #include "configuration.hpp" FMEnvelopeSetEditDialog::FMEnvelopeSetEditDialog(std::vector set, QWidget *parent) : QDialog(parent), ui(new Ui::FMEnvelopeSetEditDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); for (size_t i = 0; i < set.size(); ++i) { insertRow(static_cast(i), set.at(i)); } } FMEnvelopeSetEditDialog::~FMEnvelopeSetEditDialog() { delete ui; } std::vector FMEnvelopeSetEditDialog::getSet() { std::vector set; for (int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) { set.push_back(static_cast( qobject_cast(ui->treeWidget->itemWidget( ui->treeWidget->topLevelItem(i), 1))->currentData().toInt())); } return set; } void FMEnvelopeSetEditDialog::swapset(int aboveRow, int belowRow) { auto* tree = ui->treeWidget; QComboBox* belowBox = makeCombobox(); belowBox->setCurrentIndex(qobject_cast(tree->itemWidget(tree->topLevelItem(belowRow), 1))->currentIndex()); QTreeWidgetItem* below = tree->takeTopLevelItem(belowRow); if (tree->topLevelItemCount() > 2) { QComboBox* aboveBox = makeCombobox(); aboveBox->setCurrentIndex(qobject_cast(tree->itemWidget(tree->topLevelItem(aboveRow), 1))->currentIndex()); QTreeWidgetItem* above = tree->takeTopLevelItem(aboveRow); tree->insertTopLevelItem(aboveRow, below); tree->insertTopLevelItem(belowRow, above); tree->setItemWidget(below, 1, belowBox); tree->setItemWidget(above, 1, aboveBox); } else { tree->insertTopLevelItem(aboveRow, below); tree->setItemWidget(below, 1, belowBox); } if (!aboveRow || !belowRow) alignTreeOn1stItemChanged(); // Dummy set and delete to align for (int i = aboveRow; i < ui->treeWidget->topLevelItemCount(); ++i) { ui->treeWidget->topLevelItem(i)->setText(0, QString::number(i)); } } void FMEnvelopeSetEditDialog::insertRow(int row, FMEnvelopeTextType type) { if (row == -1) row = 0; auto item = new QTreeWidgetItem(); item->setText(0, QString::number(row)); QComboBox* box = makeCombobox(); for (int i = 0; i < box->count(); ++i) { if (static_cast(box->itemData(i).toInt()) == type) { box->setCurrentIndex(i); break; } } ui->treeWidget->insertTopLevelItem(row, item); ui->treeWidget->setItemWidget(item, 1, box); if (!row) alignTreeOn1stItemChanged(); // Dummy set and delete to align for (int i = row + 1; i < ui->treeWidget->topLevelItemCount(); ++i) { ui->treeWidget->topLevelItem(i)->setText(0, QString::number(i)); } } QComboBox* FMEnvelopeSetEditDialog::makeCombobox() { auto box = new QComboBox(); box->addItem(tr("Skip"), static_cast(FMEnvelopeTextType::Skip)); box->addItem("AL", static_cast(FMEnvelopeTextType::AL)); box->addItem("FB", static_cast(FMEnvelopeTextType::FB)); box->addItem("AR1", static_cast(FMEnvelopeTextType::AR1)); box->addItem("DR1", static_cast(FMEnvelopeTextType::DR1)); box->addItem("SR1", static_cast(FMEnvelopeTextType::SR1)); box->addItem("RR1", static_cast(FMEnvelopeTextType::RR1)); box->addItem("SL1", static_cast(FMEnvelopeTextType::SL1)); box->addItem("TL1", static_cast(FMEnvelopeTextType::TL1)); box->addItem("KS1", static_cast(FMEnvelopeTextType::KS1)); box->addItem("ML1", static_cast(FMEnvelopeTextType::ML1)); box->addItem("DT1", static_cast(FMEnvelopeTextType::DT1)); box->addItem("AR2", static_cast(FMEnvelopeTextType::AR2)); box->addItem("DR2", static_cast(FMEnvelopeTextType::DR2)); box->addItem("SR2", static_cast(FMEnvelopeTextType::SR2)); box->addItem("RR2", static_cast(FMEnvelopeTextType::RR2)); box->addItem("SL2", static_cast(FMEnvelopeTextType::SL2)); box->addItem("TL2", static_cast(FMEnvelopeTextType::TL2)); box->addItem("KS2", static_cast(FMEnvelopeTextType::KS2)); box->addItem("ML2", static_cast(FMEnvelopeTextType::ML2)); box->addItem("DT2", static_cast(FMEnvelopeTextType::DT2)); box->addItem("AR3", static_cast(FMEnvelopeTextType::AR3)); box->addItem("DR3", static_cast(FMEnvelopeTextType::DR3)); box->addItem("SR3", static_cast(FMEnvelopeTextType::SR3)); box->addItem("RR3", static_cast(FMEnvelopeTextType::RR3)); box->addItem("SL3", static_cast(FMEnvelopeTextType::SL3)); box->addItem("TL3", static_cast(FMEnvelopeTextType::TL3)); box->addItem("KS3", static_cast(FMEnvelopeTextType::KS3)); box->addItem("ML3", static_cast(FMEnvelopeTextType::ML3)); box->addItem("DT3", static_cast(FMEnvelopeTextType::DT3)); box->addItem("AR4", static_cast(FMEnvelopeTextType::AR4)); box->addItem("DR4", static_cast(FMEnvelopeTextType::DR4)); box->addItem("SR4", static_cast(FMEnvelopeTextType::SR4)); box->addItem("RR4", static_cast(FMEnvelopeTextType::RR4)); box->addItem("SL4", static_cast(FMEnvelopeTextType::SL4)); box->addItem("TL4", static_cast(FMEnvelopeTextType::TL4)); box->addItem("KS4", static_cast(FMEnvelopeTextType::KS4)); box->addItem("ML4", static_cast(FMEnvelopeTextType::ML4)); box->addItem("DT4", static_cast(FMEnvelopeTextType::DT4)); return box; } /// Dummy set and delete to align void FMEnvelopeSetEditDialog::alignTreeOn1stItemChanged() { auto tmp = new QTreeWidgetItem(); ui->treeWidget->insertTopLevelItem(1, tmp); delete ui->treeWidget->takeTopLevelItem(1); } void FMEnvelopeSetEditDialog::on_upToolButton_clicked() { int curRow = ui->treeWidget->currentIndex().row(); if (!curRow) return; swapset(curRow - 1, curRow); ui->treeWidget->setCurrentItem(ui->treeWidget->topLevelItem(curRow - 1)); } void FMEnvelopeSetEditDialog::on_downToolButton_clicked() { int curRow = ui->treeWidget->currentIndex().row(); if (curRow == ui->treeWidget->topLevelItemCount() - 1) return; swapset(curRow, curRow + 1); ui->treeWidget->setCurrentItem(ui->treeWidget->topLevelItem(curRow + 1)); } void FMEnvelopeSetEditDialog::on_addPushButton_clicked() { int row = ui->treeWidget->currentIndex().row(); insertRow(row, FMEnvelopeTextType::Skip); ui->treeWidget->setCurrentItem(ui->treeWidget->topLevelItem((row == -1) ? 0 : row)); ui->upToolButton->setEnabled(true); ui->downToolButton->setEnabled(true); ui->removePushButton->setEnabled(true); } void FMEnvelopeSetEditDialog::on_removePushButton_clicked() { int row = ui->treeWidget->currentIndex().row(); delete ui->treeWidget->takeTopLevelItem(row); for (int i = row; i < ui->treeWidget->topLevelItemCount(); ++i) { ui->treeWidget->topLevelItem(i)->setText(0, QString::number(i)); } if (!ui->treeWidget->topLevelItemCount()) { ui->upToolButton->setEnabled(false); ui->downToolButton->setEnabled(false); ui->removePushButton->setEnabled(false); } } void FMEnvelopeSetEditDialog::on_treeWidget_itemSelectionChanged() { if (ui->treeWidget->currentIndex().row() == -1) { ui->upToolButton->setEnabled(false); ui->downToolButton->setEnabled(false); ui->removePushButton->setEnabled(false); } else { ui->upToolButton->setEnabled(true); ui->downToolButton->setEnabled(true); ui->removePushButton->setEnabled(true); } } BambooTracker-0.4.6/BambooTracker/gui/fm_envelope_set_edit_dialog.hpp000066400000000000000000000037621401124043500257440ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FM_ENVELOPE_SET_EDIT_DIALOG_HPP #define FM_ENVELOPE_SET_EDIT_DIALOG_HPP #include #include #include namespace Ui { class FMEnvelopeSetEditDialog; } enum class FMEnvelopeTextType; class FMEnvelopeSetEditDialog : public QDialog { Q_OBJECT public: explicit FMEnvelopeSetEditDialog(std::vector set, QWidget *parent = nullptr); ~FMEnvelopeSetEditDialog(); std::vector getSet(); private slots: void on_upToolButton_clicked(); void on_downToolButton_clicked(); void on_addPushButton_clicked(); void on_removePushButton_clicked(); void on_treeWidget_itemSelectionChanged(); private: Ui::FMEnvelopeSetEditDialog *ui; void swapset(int aboveRow, int belowRow); void insertRow(int row, FMEnvelopeTextType type); QComboBox* makeCombobox(); void alignTreeOn1stItemChanged(); }; #endif // FM_ENVELOPE_SET_EDIT_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/fm_envelope_set_edit_dialog.ui000066400000000000000000000107331401124043500255660ustar00rootroot00000000000000 FMEnvelopeSetEditDialog 0 0 400 300 Edit FM envelope set false 0 0 Qt::DownArrow Add 0 0 Set digit types in the order of appearance. false Remove false 0 0 Qt::UpArrow Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Vertical 20 40 false true 2 Number Type treeWidget upToolButton downToolButton addPushButton removePushButton buttonBox accepted() FMEnvelopeSetEditDialog accept() 248 254 157 274 buttonBox rejected() FMEnvelopeSetEditDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/go_to_dialog.cpp000066400000000000000000000050501401124043500226570ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "go_to_dialog.hpp" #include "ui_go_to_dialog.h" #include #include "gui/gui_utils.hpp" GoToDialog::GoToDialog(std::weak_ptr bt, QWidget *parent) : QDialog(parent), ui(new Ui::GoToDialog), bt_(bt), song_(bt.lock()->getCurrentSongNumber()) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui->orderSpinBox->setMaximum(bt.lock()->getOrderSize(song_) - 1); ui->orderSpinBox->setValue(bt.lock()->getCurrentOrderNumber()); ui->stepSpinBox->setMaximum(bt.lock()->getPatternSizeFromOrderNumber(song_, ui->orderSpinBox->value())); ui->stepSpinBox->setValue(bt.lock()->getCurrentStepNumber()); auto style = bt.lock()->getSongStyle(bt.lock()->getCurrentSongNumber()); for (auto& attrib : style.trackAttribs) { ui->trackComboBox->addItem( gui_utils::getTrackName(style.type, attrib.source, attrib.channelInSource), attrib.number); if (bt.lock()->getCurrentTrackAttribute().number == attrib.number) ui->trackComboBox->setCurrentIndex(ui->trackComboBox->count() - 1); } } GoToDialog::~GoToDialog() { delete ui; } int GoToDialog::getOrder() const { return ui->orderSpinBox->value(); } int GoToDialog::getStep() const { return ui->stepSpinBox->value(); } int GoToDialog::getTrack() const { return ui->trackComboBox->currentData().toInt(); } void GoToDialog::on_orderSpinBox_valueChanged(int arg1) { ui->stepSpinBox->setMaximum(bt_.lock()->getPatternSizeFromOrderNumber(song_, arg1)); } BambooTracker-0.4.6/BambooTracker/gui/go_to_dialog.hpp000066400000000000000000000032071401124043500226660ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef GO_TO_DIALOG_HPP #define GO_TO_DIALOG_HPP #include #include #include "bamboo_tracker.hpp" namespace Ui { class GoToDialog; } class GoToDialog : public QDialog { Q_OBJECT public: GoToDialog(std::weak_ptr bt, QWidget *parent = nullptr); ~GoToDialog() override; int getOrder() const; int getStep() const; int getTrack() const; private slots: void on_orderSpinBox_valueChanged(int arg1); private: Ui::GoToDialog *ui; std::weak_ptr bt_; int song_; }; #endif // GO_TO_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/go_to_dialog.ui000066400000000000000000000045471401124043500225240ustar00rootroot00000000000000 GoToDialog 0 0 300 78 Go To Order Step Track Qt::Horizontal Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() GoToDialog accept() 248 254 157 274 buttonBox rejected() GoToDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/groove_settings_dialog.cpp000066400000000000000000000174231401124043500250000ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "groove_settings_dialog.hpp" #include "ui_groove_settings_dialog.h" #include #include #include #include #include #include #include "utils.hpp" GrooveSettingsDialog::GrooveSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::GrooveSettingsDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui->grooveListWidget->setSelectionMode(QAbstractItemView::SingleSelection); } GrooveSettingsDialog::~GrooveSettingsDialog() { delete ui; } void GrooveSettingsDialog::setGrooveSquences(std::vector> seqs) { seqs_ = seqs; for (size_t i = 0; i < seqs_.size(); ++i) { auto text = QString("%1: ").arg(i, 2, 16, QChar('0')).toUpper(); for (auto& g : seqs_[i]) { text = text + QString::number(g) + " "; } ui->grooveListWidget->addItem(text); } ui->grooveListWidget->setCurrentRow(0); ui->removeButton->setEnabled(ui->grooveListWidget->count() > 1); } std::vector> GrooveSettingsDialog::getGrooveSequences() { return seqs_; } void GrooveSettingsDialog::keyPressEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_Return: case Qt::Key_Enter: // Prevent dialog from closing when it finished line edit break; default: QDialog::keyPressEvent(event); break; } } void GrooveSettingsDialog::on_addButton_clicked() { ui->grooveListWidget->addItem(QString("%1: 6 6").arg(seqs_.size(), 2, 16, QChar('0')).toUpper()); seqs_.push_back({ 6, 6}); ui->removeButton->setEnabled(true); ui->grooveListWidget->setCurrentRow(ui->grooveListWidget->count() - 1); if (ui->grooveListWidget->count() == 128) ui->addButton->setEnabled(false); } void GrooveSettingsDialog::on_removeButton_clicked() { int row = ui->grooveListWidget->currentRow(); if (row == -1) return; seqs_.erase(seqs_.begin() + row); delete ui->grooveListWidget->takeItem(row); for (int i = row; i < ui->grooveListWidget->count(); ++i) { QString text = QString::number(i) + ": "; for (auto& g : seqs_[static_cast(i)]) { text = text + QString::number(g) + " "; } ui->grooveListWidget->item(i)->setText(text); } on_grooveListWidget_currentRowChanged(ui->grooveListWidget->currentRow()); ui->addButton->setEnabled(true); if (ui->grooveListWidget->count() == 1) ui->removeButton->setEnabled(false); } void GrooveSettingsDialog::on_lineEdit_editingFinished() { int row = ui->grooveListWidget->currentRow(); if (row == -1) return; std::vector seq; QString text = ui->lineEdit->text(); while (!text.isEmpty()) { QRegularExpressionMatch m = QRegularExpression("^(\\d+)").match(text); if (m.hasMatch()) { seq.push_back(m.captured(1).toInt()); text.remove(QRegularExpression("^\\d+")); continue; } m = QRegularExpression("^ +").match(text); if (m.hasMatch()) { text.remove(QRegularExpression("^ +")); continue; } return; } if (seq.empty()) return; seqs_.at(static_cast(row)) = std::move(seq); changeSequence(row); } void GrooveSettingsDialog::on_grooveListWidget_currentRowChanged(int currentRow) { if (currentRow > -1) updateSequence(static_cast(currentRow)); } void GrooveSettingsDialog::changeSequence(int seqNum) { QString text = updateSequence(static_cast(seqNum)); ui->grooveListWidget->item(seqNum)->setText(QString("%1: ").arg(seqNum, 2, 16, QChar('0')).toUpper() + text); } QString GrooveSettingsDialog::updateSequence(size_t seqNum) { ui->seqListWidget->clear(); QString text; auto& seq = seqs_.at(seqNum); for (size_t i = 0; i < seq.size(); ++i) { ui->seqListWidget->addItem(QString("%1: %2").arg(i, 2, 16, QChar('0')).toUpper().arg(seq[i])); text = text + QString::number(seq[i]) + " "; } ui->seqListWidget->setCurrentRow(0); ui->lineEdit->setText(text); double speed = std::accumulate(seq.begin(), seq.end(), 0) / static_cast(seq.size()); ui->speedLabel->setText(tr("Speed: %1").arg(QString::number(speed, 'f', 3))); return text; } void GrooveSettingsDialog::on_upToolButton_clicked() { int curRow = ui->seqListWidget->currentRow(); if (!curRow) return; swapSequenceItem(static_cast(ui->grooveListWidget->currentRow()), curRow - 1, curRow); ui->seqListWidget->setCurrentRow(curRow - 1); } void GrooveSettingsDialog::on_downToolButton_clicked() { int curRow = ui->seqListWidget->currentRow(); if (curRow == ui->seqListWidget->count() - 1) return; swapSequenceItem(static_cast(ui->grooveListWidget->currentRow()), curRow, curRow + 1); ui->seqListWidget->setCurrentRow(curRow + 1); } void GrooveSettingsDialog::swapSequenceItem(size_t seqNum, int index1, int index2) { std::iter_swap(seqs_.at(seqNum).begin() + index1, seqs_.at(seqNum).begin() + index2); changeSequence(static_cast(seqNum)); } void GrooveSettingsDialog::on_expandPushButton_clicked() { size_t id = static_cast(ui->grooveListWidget->currentRow()); auto& ref = seqs_[id]; if (utils::find(ref, 1) != ref.end()) return; std::vector seq; for (auto v : ref) { int tmp = v / 2; seq.push_back(v - tmp); seq.push_back(tmp); } seqs_.at(id) = std::move(seq); changeSequence(static_cast(id)); } void GrooveSettingsDialog::on_shrinkPushButton_clicked() { size_t id = static_cast(ui->grooveListWidget->currentRow()); auto& ref = seqs_[id]; if (ref.size() % 2) return; std::vector seq; for (auto it = ref.begin(); it != ref.end(); it += 2) seq.push_back(*it + *(it + 1)); seqs_.at(id) = std::move(seq); changeSequence(static_cast(id)); } void GrooveSettingsDialog::on_genPushButton_clicked() { int num = ui->numeratorSpinBox->value(); int denom = ui->denominatorSpinBox->value(); if (num < denom) return; std::vector seq(static_cast(denom)); for (int i = 0; i < num * denom; i += num) seq.at(static_cast(denom - i / num - 1)) = (i + num) / denom - i / denom; seqs_.at(static_cast(ui->grooveListWidget->currentRow())) = std::move(seq); changeSequence(ui->grooveListWidget->currentRow()); } void GrooveSettingsDialog::on_padPushButton_clicked() { int pad = ui->padSpinBox->value(); size_t id = static_cast(ui->grooveListWidget->currentRow()); auto& ref = seqs_[id]; if (std::any_of(ref.begin(), ref.end(), [pad](int x) {return (x <= pad); })) return; std::vector seq; for (auto v : ref) { seq.push_back(v - pad); seq.push_back(pad); } seqs_.at(id) = std::move(seq); changeSequence(static_cast(id)); } void GrooveSettingsDialog::on_copyPushButton_clicked() { auto& seq = seqs_[static_cast(ui->grooveListWidget->currentRow())]; auto text = QString("PATTERN_COPY:3,2,%1,").arg(seq.size()); for (auto v : seq) text += QString("0F,%1,").arg(v); QApplication::clipboard()->setText(text); } BambooTracker-0.4.6/BambooTracker/gui/groove_settings_dialog.hpp000066400000000000000000000044141401124043500250010ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef GROOVE_SETTINGS_DIALOG_HPP #define GROOVE_SETTINGS_DIALOG_HPP #include #include #include namespace Ui { class GrooveSettingsDialog; } class GrooveSettingsDialog : public QDialog { Q_OBJECT public: explicit GrooveSettingsDialog(QWidget *parent = nullptr); ~GrooveSettingsDialog() override; void setGrooveSquences(std::vector> seqs); std::vector> getGrooveSequences(); protected: void keyPressEvent(QKeyEvent* event) override; private slots: void on_addButton_clicked(); void on_removeButton_clicked(); void on_lineEdit_editingFinished(); void on_grooveListWidget_currentRowChanged(int currentRow); void on_upToolButton_clicked(); void on_downToolButton_clicked(); void on_expandPushButton_clicked(); void on_shrinkPushButton_clicked(); void on_genPushButton_clicked(); void on_padPushButton_clicked(); void on_copyPushButton_clicked(); private: Ui::GrooveSettingsDialog *ui; std::vector> seqs_; void changeSequence(int seqNum); QString updateSequence(size_t seqNum); void swapSequenceItem(size_t seqNum, int index1, int index2); }; #endif // GROOVE_SETTINGS_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/groove_settings_dialog.ui000066400000000000000000000166411401124043500246340ustar00rootroot00000000000000 GrooveSettingsDialog 0 0 400 260 Groove Settings false 0 0 Remove 0 0 Add Speed: xx Qt::AlignCenter Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Horizontal Sequence Copy Fxx 0 0 Qt::UpArrow Expand 3 1 100 12 0 0 / 1 100 2 Generate 0 0 Qt::DownArrow Shrink 1 Pad grooveListWidget seqListWidget upToolButton downToolButton expandPushButton shrinkPushButton genPushButton numeratorSpinBox denominatorSpinBox padPushButton padSpinBox copyPushButton lineEdit addButton removeButton buttonBox accepted() GrooveSettingsDialog accept() 248 254 157 274 buttonBox rejected() GrooveSettingsDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/gui_utils.cpp000066400000000000000000000061271401124043500222430ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "gui_utils.hpp" #include #include "song.hpp" namespace gui_utils { QString getTrackName(SongType songType, SoundSource src, int chInSrc) { QString name; switch (src) { case SoundSource::FM: switch (songType) { case SongType::Standard: name = "FM" + QString::number(chInSrc + 1); break; case SongType::FM3chExpanded: switch (chInSrc) { case 2: name = "FM3-OP1"; break; case 6: name = "FM3-OP2"; break; case 7: name = "FM3-OP3"; break; case 8: name = "FM3-OP4"; break; default: name = "FM" + QString::number(chInSrc + 1); break; } break; } break; case SoundSource::SSG: name = "SSG" + QString::number(chInSrc + 1); break; case SoundSource::RHYTHM: switch (chInSrc) { case 0: name = "Bass drum"; break; case 1: name = "Snare drum"; break; case 2: name = "Top cymbal"; break; case 3: name = "Hi-hat"; break; case 4: name = "Tom"; break; case 5: name = "Rim shot"; break; } break; case SoundSource::ADPCM: name = "ADPCM"; break; } return name; } std::vector adaptVisibleTrackList(const std::vector list, const SongType prevType, const SongType curType) { if (prevType == curType) return list; std::vector tracks; if (prevType == SongType::Standard) { for (const int& track : list) { switch (track) { case 0: case 1: tracks.push_back(track); break; case 2: { std::vector tmp { 2, 3, 4, 5 }; std::copy(tmp.begin(), tmp.end(), std::back_inserter(tracks)); break; } default: tracks.push_back(track + 3); break; } } } else { // FM3chExpanded bool hasPushed3 = false; for (const int& track : list) { switch (track) { case 0: case 1: tracks.push_back(track); break; case 2: case 3: case 4: case 5: if (!hasPushed3) { tracks.push_back(2); hasPushed3 = true; } break; default: tracks.push_back(track - 3); break; } } } return tracks; } } BambooTracker-0.4.6/BambooTracker/gui/gui_utils.hpp000066400000000000000000000033711401124043500222460ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef GUI_UTILS_HPP #define GUI_UTILS_HPP #include #include #include #include #include "bamboo_tracker_defs.hpp" enum class SongType; namespace gui_utils { QString getTrackName(SongType songType, SoundSource src, int chInSrc); inline QString utf8ToQString(const std::string& str) { return QString::fromUtf8(str.c_str(), static_cast(str.length())); } inline QKeySequence strToKeySeq(std::string str) { return QKeySequence(utf8ToQString(str)); } std::vector adaptVisibleTrackList(const std::vector list, const SongType prevType, const SongType curType); } #endif // GUI_UTILS_HPP BambooTracker-0.4.6/BambooTracker/gui/hide_tracks_dialog.cpp000066400000000000000000000061151401124043500240330ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "hide_tracks_dialog.hpp" #include "ui_hide_tracks_dialog.h" #include #include #include "song.hpp" #include "gui/gui_utils.hpp" HideTracksDialog::HideTracksDialog(const SongStyle& style, const std::vector& tracks, QWidget *parent) : QDialog(parent), ui(new Ui::HideTracksDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); for (const auto& attrib : style.trackAttribs) { auto item = new QListWidgetItem(gui_utils::getTrackName(style.type, attrib.source, attrib.channelInSource), ui->listWidget); int n = attrib.number; item->setData(Qt::UserRole, n); // Track numbers are sorted bool exists = std::any_of(tracks.begin(), tracks.end(), [n](int t) { return n == t; }); item->setCheckState(exists ? Qt::Checked : Qt::Unchecked); } QObject::connect(ui->listWidget, &QListWidget::itemChanged, this, [&](QListWidgetItem* item) { if (item->checkState() == Qt::Unchecked) { --checkCounter_; if (!checkCounter_) ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } else { ++checkCounter_; ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } }); checkCounter_ = style.trackAttribs.size(); } HideTracksDialog::~HideTracksDialog() { delete ui; } std::vector HideTracksDialog::getVisibleTracks() const { std::vector tracks; for (int i = 0; i < ui->listWidget->count(); ++i) { auto item = ui->listWidget->item(i); if (item->checkState() == Qt::Checked) tracks.push_back(item->data(Qt::UserRole).toInt()); } return tracks; } void HideTracksDialog::on_reversePushButton_clicked() { for (int i = 0; i < ui->listWidget->count(); ++i) { auto item = ui->listWidget->item(i); item->setCheckState((item->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked); } } void HideTracksDialog::on_checkAllPushButton_clicked() { for (int i = 0; i < ui->listWidget->count(); ++i) { auto item = ui->listWidget->item(i); item->setCheckState(Qt::Checked); } } BambooTracker-0.4.6/BambooTracker/gui/hide_tracks_dialog.hpp000066400000000000000000000033041401124043500240350ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef HIDE_TRACKS_DIALOG_HPP #define HIDE_TRACKS_DIALOG_HPP #include #include struct SongStyle; namespace Ui { class HideTracksDialog; } class HideTracksDialog : public QDialog { Q_OBJECT public: explicit HideTracksDialog(const SongStyle& style, const std::vector& tracks, QWidget *parent = nullptr); ~HideTracksDialog() override; std::vector getVisibleTracks() const; private slots: void on_reversePushButton_clicked(); void on_checkAllPushButton_clicked(); private: Ui::HideTracksDialog *ui; size_t checkCounter_; }; #endif // HIDE_TRACKS_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/hide_tracks_dialog.ui000066400000000000000000000046701401124043500236720ustar00rootroot00000000000000 HideTracksDialog 0 0 174 300 Hide Tracks Check All Reverse Set the visibility of tracks: Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Horizontal listWidget reversePushButton checkAllPushButton buttonBox accepted() HideTracksDialog accept() 248 254 157 274 buttonBox rejected() HideTracksDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/000077500000000000000000000000001401124043500233035ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/adpcm_sample_editor.cpp000066400000000000000000000454631401124043500300160ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "adpcm_sample_editor.hpp" #include "ui_adpcm_sample_editor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chip/codec/ymb_codec.hpp" #include "instrument/sample_adpcm.hpp" #include "gui/event_guard.hpp" #include "gui/instrument_editor/sample_length_dialog.hpp" #include "gui/instrument_editor/grid_settings_dialog.hpp" #include "gui/file_io_error_message_box.hpp" #include "gui/instrument_editor/instrument_editor_utils.hpp" #include "utils.hpp" ADPCMSampleEditor::ADPCMSampleEditor(QWidget *parent) : QWidget(parent), ui(new Ui::ADPCMSampleEditor), isIgnoreEvent_(false), zoom_(0), viewedSampLen_(2), gridIntr_(1), cursorSamp_(0, 0), prevPressedSamp_(-1, -1), addrStart_(0), addrStop_(0), sample_(2), drawMode_(DrawMode::Disabled) { ui->setupUi(this); auto rkfunc = [&](int dummy) { Q_UNUSED(dummy) if (!isIgnoreEvent_) { int rk = ui->rootKeySpinBox->value() * 12 + ui->rootKeyComboBox->currentIndex(); bt_.lock()->setSampleADPCMRootKeyNumber(ui->sampleNumSpinBox->value(), rk); emit sampleParameterChanged(ui->sampleNumSpinBox->value()); emit modified(); } }; // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->rootKeyComboBox, static_cast(&QComboBox::currentIndexChanged), this, rkfunc); QObject::connect(ui->rootKeySpinBox, static_cast(&QSpinBox::valueChanged), this, rkfunc); ui->memoryWidget->installEventFilter(this); ui->sampleViewWidget->setAttribute(Qt::WA_Hover); ui->sampleViewWidget->installEventFilter(this); auto tb = new QToolBar(); tb->setIconSize(QSize(16, 16)); tb->setMinimumHeight(28); tb->setMaximumHeight(28); tb->addActions({ ui->action_Clear, ui->action_Import, ui->action_Resize }); tb->addSeparator(); tb->addActions({ ui->actionZoom_In, ui->actionZoom_Out }); auto gridMenu = new QMenu(); gridMenu->addActions({ ui->action_Grid_View, ui->actionG_rid_Settings }); auto gridButton = new QToolButton(); gridButton->setPopupMode(QToolButton::MenuButtonPopup); gridButton->setMenu(gridMenu); gridButton->setDefaultAction(ui->action_Grid_View); tb->addWidget(gridButton); tb->addSeparator(); auto editMenu = new QMenu(); editMenu->addActions({ ui->action_Draw_Sample, ui->actionDirec_t_Draw }); auto editButton = new QToolButton(); editButton->setPopupMode(QToolButton::MenuButtonPopup); editButton->setMenu(editMenu); editButton->setDefaultAction(ui->action_Draw_Sample); QObject::connect(editButton, &QToolButton::triggered, editButton, &QToolButton::setDefaultAction); tb->addWidget(editButton); tb->addAction(ui->actionRe_verse); ui->verticalLayout_2->insertWidget(0, tb); } ADPCMSampleEditor::~ADPCMSampleEditor() { delete ui; } void ADPCMSampleEditor::setCore(std::weak_ptr core) { bt_ = core; } void ADPCMSampleEditor::setConfiguration(std::weak_ptr config) { config_ = config; } void ADPCMSampleEditor::setColorPalette(std::shared_ptr palette) { palette_ = palette; } int ADPCMSampleEditor::getSampleNumber() const { return ui->sampleNumSpinBox->value(); } void ADPCMSampleEditor::dragEnterEvent(QDragEnterEvent* event) { const QMimeData* mime = event->mimeData(); if (mime->hasUrls() && mime->urls().length() == 1 && QFileInfo(mime->urls().at(0).toLocalFile()).suffix().toLower() == "wav") { event->acceptProposedAction(); } } bool ADPCMSampleEditor::eventFilter(QObject* obj, QEvent* ev) { if (obj == ui->memoryWidget) { switch (ev->type()) { case QEvent::Paint: { QPainter painter(ui->memoryWidget); painter.drawPixmap(ui->memoryWidget->rect(), memPixmap_); break; } case QEvent::Resize: memPixmap_ = QPixmap(ui->memoryWidget->size()); updateSampleMemoryBar(); break; default: break; } return false; } if (obj == ui->sampleViewWidget) { switch (ev->type()) { case QEvent::Paint: { QPainter painter(ui->sampleViewWidget); painter.drawPixmap(ui->sampleViewWidget->rect(), sampViewPixmap_); break; } case QEvent::Resize: sampViewPixmap_ = QPixmap(ui->sampleViewWidget->size()); updateSampleView(); break; case QEvent::Wheel: { auto we = dynamic_cast(ev); int cnt = we->angleDelta().y() / 120; bool ctrl = we->modifiers().testFlag(Qt::ControlModifier); if (cnt > 0) { for (int i = 0; i < cnt; ++i) { if (ctrl) on_actionZoom_In_triggered(); else ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->value() - 1); } } else { for (int i = 0; cnt < i; --i) { if (ctrl) on_actionZoom_Out_triggered(); else ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->value() + 1); } } break; } case QEvent::HoverMove: { auto pos = dynamic_cast(ev)->pos(); detectCursorSamplePosition(pos.x(), pos.y()); if (prevPressedSamp_.x() != -1) { // Change sample const int px = prevPressedSamp_.x(); const int py = prevPressedSamp_.y(); const int cx = cursorSamp_.x(); const int cy = cursorSamp_.y(); if (px < cx) { const int dx = cx - px; const int dy = cy - py; for (int x = px + 1; x <= cx; ++x) sample_.at(x) = (x - px) * dy / dx + py; } else if (px == cx) { sample_.at(cx) = cy; } else { const int dx = px - cx; const int dy = py - cy; for (int x = cx; x < px; ++x) sample_.at(x) = (x - cx) * dy / dx + cy; } prevPressedSamp_ = cursorSamp_; if (drawMode_ == DrawMode::Direct) { sendEditedSample(); } else { updateSampleView(); ui->sampleViewWidget->update(); } } break; } case QEvent::MouseButtonPress: { if (drawMode_ == DrawMode::Disabled) break; sample_.at(cursorSamp_.x()) = cursorSamp_.y(); prevPressedSamp_ = cursorSamp_; if (drawMode_ == DrawMode::Direct) { sendEditedSample(); } else { updateSampleView(); ui->sampleViewWidget->update(); } break; } case QEvent::MouseButtonRelease: { prevPressedSamp_.setX(-1); // Clear break; } default: break; } return false; } return false; } void ADPCMSampleEditor::dropEvent(QDropEvent* event) { importSampleFrom(reinterpret_cast(event)->mimeData()->urls().first().toLocalFile()); } void ADPCMSampleEditor::setInstrumentSampleParameters(int sampNum, bool repeatable, int rKeyNum, int rDeltaN, size_t start, size_t stop, std::vector sample) { Ui::EventGuard ev(isIgnoreEvent_); ui->sampleNumSpinBox->setValue(sampNum); updateUsersView(); ui->repeatCheckBox->setChecked(repeatable); ui->rootKeyComboBox->setCurrentIndex(rKeyNum % 12); ui->rootKeySpinBox->setValue(rKeyNum / 12); ui->rootRateSpinBox->setValue(rDeltaN); addrStart_ = start; addrStop_ = stop; updateSampleMemoryBar(); ui->memoryWidget->update(); if (!ui->action_Draw_Sample->isChecked()) { sample_.resize(sample.size() * 2); codec::ymb_decode(sample.data(), sample_.data(), static_cast(sample_.size())); // Slider settings for (int z = 0, len = sample_.size(); ; ++z) { int tmp = (len + 1) >> 1; if (tmp < 2) { zoom_ = z; viewedSampLen_ = len; ui->horizontalScrollBar->setMaximum(sample_.size() - len); break; } else if (z == zoom_) { viewedSampLen_ = len; ui->horizontalScrollBar->setMaximum(sample_.size() - len); break; } else { len = tmp; } } updateSampleView(); ui->sampleViewWidget->update(); } ui->detailLabel->setText(updateDetailView()); } void ADPCMSampleEditor::importSampleFrom(const QString file) { std::unique_ptr wav; try { io::BinaryContainer container; { QFile fp(file); if (!fp.open(QIODevice::ReadOnly)) { FileIOErrorMessageBox::openError(file, true, io::FileType::WAV, this); return; } QByteArray array = fp.readAll(); fp.close(); std::move(array.begin(), array.end(), std::back_inserter(container)); } wav = std::make_unique(container); } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, true, e, this).exec(); return; } catch (std::exception& e) { FileIOErrorMessageBox(file, true, io::FileType::WAV, QString(e.what()), this).exec(); return; } if (wav->getSampleRate() < 2000 || 55466 < wav->getSampleRate()) { FileIOErrorMessageBox(file, true, io::FileType::WAV, tr("Supported sample rate is 2kHz-55.5kHz, but the rate of selected sample is %1.") .arg(wav->getSampleRate()), this).exec(); return; } if (wav->getChannelCount() != 1) { FileIOErrorMessageBox(file, true, io::FileType::WAV, tr("The selected sample is not mono channel."), this).exec(); return; } io::BinaryContainer bc = wav->getSample(); size_t rawSize = bc.size() / 2; std::vector raw(rawSize); for (size_t i = 0; i < rawSize; ++i) { raw[i] = bc.readInt16(i * 2); } std::vector adpcm((raw.size() + 1) / 2); codec::ymb_encode(raw.data(), adpcm.data(), static_cast(raw.size())); bt_.lock()->storeSampleADPCMRawSample(ui->sampleNumSpinBox->value(), std::move(adpcm)); ui->rootKeyComboBox->setCurrentIndex(SampleADPCM::DEF_ROOT_KEY % 12); ui->rootKeySpinBox->setValue(SampleADPCM::DEF_ROOT_KEY / 12); ui->rootRateSpinBox->setValue(SampleADPCM::calculateADPCMDeltaN(wav->getSampleRate())); emit modified(); emit sampleAssignRequested(); emit sampleParameterChanged(ui->sampleNumSpinBox->value()); } void ADPCMSampleEditor::updateSampleMemoryBar() { QRect bar = memPixmap_.rect(); if (!bar.isValid()) return; QPainter painter(&memPixmap_); painter.fillRect(bar, palette_->instADPCMMemBackColor); double maxSize = bt_.lock()->getADPCMStoredSize() >> 5; double limit = bt_.lock()->getADPCMLimit() >> 5; // By 32 bytes QRectF allSamp(0, 0, std::max(1., bar.width() * (maxSize / limit)), rect().height()); painter.fillRect(allSamp, palette_->instADPCMMemAllColor); if (addrStart_ || addrStart_ != addrStop_) { QRectF curSamp(bar.width() * (addrStart_ / limit), 0, std::max(1., bar.width() * ((addrStop_ - addrStart_) / limit)), rect().height()); painter.fillRect(curSamp, palette_->instADPCMMemCurColor); } } void ADPCMSampleEditor::updateSampleView() { QRect rect = sampViewPixmap_.rect(); if (!rect.isValid()) return; QPainter painter(&sampViewPixmap_); painter.fillRect(rect, palette_->instADPCMSampViewBackColor); painter.setPen(palette_->instADPCMSampViewCenterColor); const int maxX = rect.width(); const int centerY = rect.height() >> 1; painter.drawLine(0, centerY, maxX, centerY); QColor foreColor; switch (drawMode_) { case DrawMode::Disabled: foreColor = palette_->instADPCMSampViewForeColor; break; case DrawMode::Normal: foreColor = palette_->instADPCMSampViewDrawColor; break; case DrawMode::Direct: foreColor = palette_->instADPCMSampViewDirectDrawColor; break; } painter.setPen(foreColor); const int16_t maxY = std::numeric_limits::max(); const size_t first = ui->horizontalScrollBar->value(); const bool showGrid = ui->action_Grid_View->isChecked(); if (maxX < viewedSampLen_) { int prevY = centerY; size_t g = first; for (int x = 0; x < maxX; ++x) { size_t i = (viewedSampLen_ - 1) * x / (maxX - 1); size_t p = i + first; int16_t sample = sample_.at(p); int y = centerY - (centerY * sample / maxY); if (showGrid && g <= p) { painter.setPen(palette_->instADPCMSampViewGridColor); painter.drawLine(x, 0, x, rect.height()); g = (g / gridIntr_ + 1) * gridIntr_; painter.setPen(foreColor); } if (x) painter.drawLine(x - 1, prevY, x, y); prevY = y; } } else { QPoint prev, p; for (int i = 0; i < viewedSampLen_; ++i) { p.setX((maxX - 1) * i / (viewedSampLen_ - 1)); int16_t sample = sample_[i + first]; p.setY(centerY - (centerY * sample / maxY)); if (showGrid && !(i % gridIntr_)) { painter.setPen(palette_->instADPCMSampViewGridColor); painter.drawLine(p.x(), 0, p.x(), rect.height()); painter.setPen(foreColor); } if (p.x()) painter.drawLine(prev, p); prev = p; } } } void ADPCMSampleEditor::updateUsersView() { std::multiset users = bt_.lock()->getSampleADPCMUsers(ui->sampleNumSpinBox->value()); ui->usersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void ADPCMSampleEditor::detectCursorSamplePosition(int cx, int cy) { const QRect& rect = ui->sampleViewWidget->rect(); // Detect x const int w = rect.width(); if (viewedSampLen_ < w) { const double segW = rect.width() / (viewedSampLen_ - 1.); double th = segW / 2.; for (int i = 0; i < viewedSampLen_; ++i, th += segW) { if (cx < th) { cursorSamp_.setX(ui->horizontalScrollBar->value() + i); break; } } } else { cursorSamp_.setX( ui->horizontalScrollBar->value() + (viewedSampLen_ - 1) * utils::clamp(cx, 0, w - 1) / (w - 1)); } // Detect y const double centerY = rect.height() >> 1; int y = std::numeric_limits::max() * (centerY - cy) / centerY; cursorSamp_.setY(utils::clamp(y, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max()))); // Update position view ui->detailLabel->setText(updateDetailView()); } void ADPCMSampleEditor::sendEditedSample() { std::vector adpcm(sample_.size() >> 1); codec::ymb_encode(sample_.data(), adpcm.data(), static_cast(sample_.size())); bt_.lock()->storeSampleADPCMRawSample(ui->sampleNumSpinBox->value(), std::move(adpcm)); emit modified(); emit sampleAssignRequested(); emit sampleParameterChanged(ui->sampleNumSpinBox->value()); } void ADPCMSampleEditor::onSampleNumberChanged() { updateUsersView(); } void ADPCMSampleEditor::onSampleMemoryUpdated(size_t start, size_t stop) { addrStart_ = start; addrStop_ = stop; updateSampleMemoryBar(); ui->memoryWidget->update(); } void ADPCMSampleEditor::on_sampleNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) emit sampleNumberChanged(arg1); // Direct connection onSampleNumberChanged(); updateSampleMemoryBar(); ui->memoryWidget->update(); updateSampleView(); ui->sampleViewWidget->update(); } void ADPCMSampleEditor::on_repeatCheckBox_toggled(bool checked) { if (!isIgnoreEvent_) { bt_.lock()->setSampleADPCMRepeatEnabled(ui->sampleNumSpinBox->value(), checked); emit sampleParameterChanged(ui->sampleNumSpinBox->value()); emit modified(); } } void ADPCMSampleEditor::on_rootRateSpinBox_valueChanged(int arg1) { ui->rootRateSpinBox->setSuffix( QString(" (0x") + QString("%1 | ").arg(arg1, 3, 16, QChar('0')).toUpper() + QString("%1Hz)").arg(QString::number(arg1 * 55500. * std::pow(2., -16.), 'f', 3))); if (!isIgnoreEvent_) { bt_.lock()->setSampleADPCMRootDeltaN(ui->sampleNumSpinBox->value(), arg1); emit sampleParameterChanged(ui->sampleNumSpinBox->value()); emit modified(); } } void ADPCMSampleEditor::on_action_Resize_triggered() { SampleLengthDialog diag(sample_.size()); if (diag.exec() == QDialog::Accepted) { sample_.resize(diag.getLength()); sendEditedSample(); updateSampleView(); ui->sampleViewWidget->update(); } } void ADPCMSampleEditor::on_actionRe_verse_triggered() { std::reverse(sample_.begin(), sample_.end()); sendEditedSample(); updateSampleView(); ui->sampleViewWidget->update(); } void ADPCMSampleEditor::on_actionZoom_In_triggered() { int len = (viewedSampLen_ + 1) >> 1; if (len > 1) { ++zoom_; viewedSampLen_ = len; ui->horizontalScrollBar->setMaximum(sample_.size() - len); updateSampleView(); ui->sampleViewWidget->update(); ui->detailLabel->setText(updateDetailView()); } } void ADPCMSampleEditor::on_actionZoom_Out_triggered() { if (zoom_) { --zoom_; viewedSampLen_ = sample_.size() >> zoom_; ui->horizontalScrollBar->setMaximum(sample_.size() - viewedSampLen_); updateSampleView(); ui->sampleViewWidget->update(); ui->detailLabel->setText(updateDetailView()); } } void ADPCMSampleEditor::on_horizontalScrollBar_valueChanged(int value) { Q_UNUSED(value) updateSampleView(); ui->sampleViewWidget->update(); } void ADPCMSampleEditor::on_action_Import_triggered() { QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); QString file = QFileDialog::getOpenFileName(this, tr("Import sample"), (dir.isEmpty() ? "./" : dir), tr("WAV signed 16-bit PCM (*.wav)")); if (file.isNull()) return; importSampleFrom(file); } void ADPCMSampleEditor::on_action_Clear_triggered() { bt_.lock()->clearSampleADPCMRawSample(ui->sampleNumSpinBox->value()); updateSampleMemoryBar(); ui->memoryWidget->update(); emit modified(); if (config_.lock()->getWriteOnlyUsedSamples()) { emit sampleAssignRequested(); } else { emit sampleMemoryChanged(); } emit sampleParameterChanged(ui->sampleNumSpinBox->value()); } void ADPCMSampleEditor::on_action_Grid_View_triggered() { updateSampleView(); ui->sampleViewWidget->update(); } void ADPCMSampleEditor::on_actionG_rid_Settings_triggered() { GridSettingsDialog diag(gridIntr_); if (diag.exec() == QDialog::Accepted) { gridIntr_ = diag.getInterval(); updateSampleView(); ui->sampleViewWidget->update(); } } void ADPCMSampleEditor::on_action_Draw_Sample_triggered(bool checked) { if (checked) { ui->actionDirec_t_Draw->setChecked(false); drawMode_ = DrawMode::Normal; } else { if (drawMode_ == DrawMode::Normal) drawMode_ = DrawMode::Disabled; sendEditedSample(); // Convert sample } updateSampleView(); } void ADPCMSampleEditor::on_actionDirec_t_Draw_triggered(bool checked) { if (checked) { ui->action_Draw_Sample->setChecked(false); if (drawMode_ == DrawMode::Normal) sendEditedSample(); // Convert sample drawMode_ = DrawMode::Direct; } else { if (drawMode_ == DrawMode::Direct) drawMode_ = DrawMode::Disabled; } updateSampleView(); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/adpcm_sample_editor.hpp000066400000000000000000000075411401124043500300160ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ADPCM_SAMPLE_EDITOR_HPP #define ADPCM_SAMPLE_EDITOR_HPP #include #include #include #include #include #include #include #include #include #include "bamboo_tracker.hpp" #include "configuration.hpp" #include "gui/color_palette.hpp" namespace Ui { class ADPCMSampleEditor; } class ADPCMSampleEditor : public QWidget { Q_OBJECT public: explicit ADPCMSampleEditor(QWidget *parent = nullptr); ~ADPCMSampleEditor() override; void setCore(std::weak_ptr core); void setConfiguration(std::weak_ptr config); void setColorPalette(std::shared_ptr palette); int getSampleNumber() const; void setInstrumentSampleParameters(int sampNum, bool repeatable, int rKeyNum, int rDeltaN, size_t start, size_t stop, std::vector sample); signals: void modified(); void sampleNumberChanged(int n); // NEED Direct connection void sampleParameterChanged(int wfNum); void sampleAssignRequested(); void sampleMemoryChanged(); public slots: void onSampleNumberChanged(); void onSampleMemoryUpdated(size_t start, size_t stop); protected: bool eventFilter(QObject* obj, QEvent* ev) override; void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; private: Ui::ADPCMSampleEditor *ui; bool isIgnoreEvent_; std::weak_ptr bt_; std::weak_ptr config_; std::shared_ptr palette_; QPixmap memPixmap_, sampViewPixmap_; int zoom_, viewedSampLen_, gridIntr_; QPoint cursorSamp_, prevPressedSamp_; size_t addrStart_, addrStop_; std::vector sample_; void importSampleFrom(const QString file); void updateSampleMemoryBar(); void updateSampleView(); void updateUsersView(); void detectCursorSamplePosition(int cx, int cy); void sendEditedSample(); inline QString updateDetailView() const { return QString("(%1, %2), %3, x%4") .arg(cursorSamp_.x()).arg(cursorSamp_.y()).arg(sample_.size()).arg(zoom_ + 1); } enum class DrawMode { Disabled, Normal, Direct }; DrawMode drawMode_; private slots: void on_sampleNumSpinBox_valueChanged(int arg1); void on_repeatCheckBox_toggled(bool checked); void on_rootRateSpinBox_valueChanged(int arg1); void on_action_Resize_triggered(); void on_actionRe_verse_triggered(); void on_actionZoom_In_triggered(); void on_actionZoom_Out_triggered(); void on_horizontalScrollBar_valueChanged(int value); void on_action_Import_triggered(); void on_action_Clear_triggered(); void on_action_Grid_View_triggered(); void on_actionG_rid_Settings_triggered(); void on_action_Draw_Sample_triggered(bool checked); void on_actionDirec_t_Draw_triggered(bool checked); }; #endif // ADPCM_SAMPLE_EDITOR_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/adpcm_sample_editor.ui000066400000000000000000000242741401124043500276460ustar00rootroot00000000000000 ADPCMSampleEditor 0 0 400 340 true 0 0 0 0 false # 127 Users: true Qt::Horizontal 40 20 Memory: 0 0 140 0 0 0 0 true 0 Qt::Horizontal Repeat Qt::Horizontal 40 20 150 0 QFrame::WinPanel QFrame::Sunken , x, Qt::AlignCenter Root key Key C C# D D# E F F# G G# A A# B 7 5 Rate 1 65535 :/icon/settings:/icon/settings &Resize :/icon/reverse:/icon/reverse Re&verse :/icon/zoom_in:/icon/zoom_in Zoom I&n :/icon/zoom_out:/icon/zoom_out Zoom &Out :/icon/load_inst:/icon/load_inst &Import :/icon/duplicate_order:/icon/duplicate_order &Clear true :/icon/eye:/icon/eye &Grid View Grid &Settings... true :/icon/edit_inst:/icon/edit_inst &Draw Sample true :/icon/chart_edit:/icon/chart_edit Direc&t Draw sampleNumSpinBox usersLineEdit repeatCheckBox rootKeyComboBox rootKeySpinBox rootRateSpinBox BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.cpp000066400000000000000000000075441401124043500303450ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "arpeggio_macro_editor.hpp" #include #include #include "note.hpp" namespace { const QString TONE_LABS[96] = { "C-0", "C#0", "D-0", "D#0", "E-0", "F-0", "F#0", "G-0", "G#0", "A-0", "A#0", "B-0", "C-1", "C#1", "D-1", "D#1", "E-1", "F-1", "F#1", "G-1", "G#1", "A-1", "A#1", "B-1", "C-2", "C#2", "D-2", "D#2", "E-2", "F-2", "F#2", "G-2", "G#2", "A-2", "A#2", "B-2", "C-3", "C#3", "D-3", "D#3", "E-3", "F-3", "F#3", "G-3", "G#3", "A-3", "A#3", "B-3", "C-4", "C#4", "D-4", "D#4", "E-4", "F-4", "F#4", "G-4", "G#4", "A-4", "A#4", "B-4", "C-5", "C#5", "D-5", "D#5", "E-5", "F-5", "F#5", "G-5", "G#5", "A-5", "A#5", "B-5", "C-6", "C#6", "D-6", "D#6", "E-6", "F-6", "F#6", "G-6", "G#6", "A-6", "A#6", "B-6", "C-7", "C#7", "D-7", "D#7", "E-7", "F-7", "F#7", "G-7", "G#7", "A-7", "A#7", "B-7" }; static_assert (sizeof(TONE_LABS) == sizeof(QString) * Note::NOTE_NUMBER_RANGE, "Invalid note name table size"); const QHash NOTE_NAME_NUMS = { { "C-", 0 }, { "C#", 1 }, { "D-", 2 }, { "D#", 3 }, { "E", 4 }, { "F", 5 }, { "F#", 6 }, { "G-", 7 }, { "G#", 8 }, { "A-", 9 }, { "A#", 10 }, { "B", 11 } }; } ArpeggioMacroEditor::ArpeggioMacroEditor(QWidget* parent) : VisualizedInstrumentMacroEditor(parent) { constexpr int MAX_DISP_CNT = 15; setMaximumDisplayedRowCount(MAX_DISP_CNT); setDefaultRow(Note::DEFAULT_NOTE_NUM); setLabelDiaplayMode(true); for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) { AddRow(QString::asprintf("%+d", i - Note::DEFAULT_NOTE_NUM), false); } autoFitLabelWidth(); setUpperRow(Note::DEFAULT_NOTE_NUM + MAX_DISP_CNT / 2); setMMLDisplay0As(-Note::DEFAULT_NOTE_NUM); setSequenceType(SequenceType::AbsoluteSequence); } QString ArpeggioMacroEditor::convertSequenceDataUnitToMML(Column col) { if (type_ == SequenceType::FixedSequence) return TONE_LABS[col.row]; else return VisualizedInstrumentMacroEditor::convertSequenceDataUnitToMML(col); } bool ArpeggioMacroEditor::interpretDataInMML(QString &text, int &cnt, std::vector &column) { if (type_ == SequenceType::FixedSequence) { QRegularExpressionMatch m = QRegularExpression("^([A-G][-#])([0-7])").match(text); if (m.hasMatch()) { int oct = m.captured(2).toInt(); int d = 12 * oct + NOTE_NAME_NUMS[m.captured(1)]; column.push_back({ d, -1, "" }); ++cnt; text.remove(QRegularExpression("^[A-G][-#][0-7]")); return true; } return false; } else { return VisualizedInstrumentMacroEditor::interpretDataInMML(text, cnt, column); } } void ArpeggioMacroEditor::updateLabels() { if (type_ == SequenceType::FixedSequence) { for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) setLabel(i, TONE_LABS[i]); } else { for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) setLabel(i, QString::asprintf("%+d", i - Note::DEFAULT_NOTE_NUM)); } } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/arpeggio_macro_editor.hpp000066400000000000000000000032141401124043500303400ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ARPEGGIO_MACRO_EDITOR_HPP #define ARPEGGIO_MACRO_EDITOR_HPP #include "visualized_instrument_macro_editor.hpp" #include #include "instrument.hpp" class ArpeggioMacroEditor final : public VisualizedInstrumentMacroEditor { Q_OBJECT public: ArpeggioMacroEditor(QWidget *parent = nullptr); protected: QString convertSequenceDataUnitToMML(Column col) override; bool interpretDataInMML(QString &text, int &cnt, std::vector &column) override; private: void updateLabels() override; }; #endif // ARPEGGIO_MACRO_EDITOR_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/fm_operator_table.cpp000066400000000000000000000324751401124043500275060ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "fm_operator_table.hpp" #include "ui_fm_operator_table.h" #include #include #include #include #include "gui/event_guard.hpp" namespace { constexpr int DT_SIGN_TBL_[8] = { 0, 1, 2, 3, 0, -1, -2, -3 }; constexpr int DT_UNSIGN_TBL_[7] = { 7, 6, 5, 0, 1, 2, 3 }; } FMOperatorTable::FMOperatorTable(QWidget *parent) : QFrame(parent), ui(new Ui::FMOperatorTable), isDTNegative_(false), isIgnoreEvent_(false) { ui->setupUi(this); ui->groupBox->setContextMenuPolicy(Qt::CustomContextMenu); // Init sliders ui->arSlider->setText("AR"); ui->arSlider->setMaximum(31); ui->drSlider->setText("DR"); ui->drSlider->setMaximum(31); ui->srSlider->setText("SR"); ui->srSlider->setMaximum(31); ui->rrSlider->setText("RR"); ui->rrSlider->setMaximum(15); ui->slSlider->setText("SL"); ui->slSlider->setMaximum(15); ui->tlSlider->setText("TL"); ui->tlSlider->setMaximum(127); ui->ksSlider->setText("KS"); ui->ksSlider->setMaximum(3); ui->mlSlider->setText("ML"); ui->mlSlider->setMaximum(15); ui->dtSlider->setText("DT"); ui->dtSlider->setMaximum(7); sliderMap_ = { { Ui::FMOperatorParameter::AR, ui->arSlider }, { Ui::FMOperatorParameter::DR, ui->drSlider }, { Ui::FMOperatorParameter::SR, ui->srSlider }, { Ui::FMOperatorParameter::RR, ui->rrSlider }, { Ui::FMOperatorParameter::SL, ui->slSlider }, { Ui::FMOperatorParameter::TL, ui->tlSlider }, { Ui::FMOperatorParameter::KS, ui->ksSlider }, { Ui::FMOperatorParameter::ML, ui->mlSlider }, { Ui::FMOperatorParameter::DT, ui->dtSlider } }; for (auto& pair : sliderMap_) { if (pair.second == ui->dtSlider) { QObject::connect(pair.second, &LabeledVerticalSlider::valueChanged, this, [&](int value) { repaintGraph(); if (!isIgnoreEvent_) { if (isDTNegative_) value = DT_UNSIGN_TBL_[value + 3]; emit operatorValueChanged(pair.first, value); } }); } else { QObject::connect(pair.second, &LabeledVerticalSlider::valueChanged, this, [&](int value) { repaintGraph(); if (!isIgnoreEvent_) emit operatorValueChanged(pair.first, value); }); } } ui->ssgegSlider->setEnabled(false); ui->ssgegSlider->setText(tr("Type")); ui->ssgegSlider->setMaximum(7); QObject::connect(ui->ssgegSlider, &LabeledVerticalSlider::valueChanged, this, [&](int value) { repaintGraph(); if (!isIgnoreEvent_) emit operatorValueChanged(Ui::FMOperatorParameter::SSGEG, value); }); ui->envFrame->installEventFilter(this); } FMOperatorTable::~FMOperatorTable() { delete ui; } void FMOperatorTable::setEnvelopeSetNames(std::vector list) { envelopeTypes_ = list; } void FMOperatorTable::setColorPalette(std::shared_ptr palette) { palette_ = palette; } void FMOperatorTable::setOperatorNumber(int n) { number_ = n; ui->groupBox->setTitle(tr("Operator %1").arg(n + 1)); } int FMOperatorTable::operatorNumber() const { return number_; } void FMOperatorTable::setValue(Ui::FMOperatorParameter param, int value) { if (param == Ui::FMOperatorParameter::SSGEG) { if (value == -1) { ui->ssgegCheckBox->setChecked(false); } else { ui->ssgegCheckBox->setChecked(true); ui->ssgegSlider->setValue(value); } } else { if (param == Ui::FMOperatorParameter::DT && isDTNegative_) value = DT_SIGN_TBL_[value]; sliderMap_.at(param)->setValue(value); } } void FMOperatorTable::setGroupEnabled(bool enabled) { Ui::EventGuard eg(isIgnoreEvent_); ui->groupBox->setChecked(enabled); } void FMOperatorTable::setDTDisplayType(bool useNegative) { if (isDTNegative_ == useNegative) return; Ui::EventGuard eg(isIgnoreEvent_); isDTNegative_ = useNegative; if (useNegative) { int dt = DT_SIGN_TBL_[ui->dtSlider->value()]; ui->dtSlider->setMinimum(-3); ui->dtSlider->setMaximum(3); ui->dtSlider->setSign(true); ui->dtSlider->setValue(dt); } else { int dt = DT_UNSIGN_TBL_[ui->dtSlider->value() + 3]; ui->dtSlider->setMinimum(0); ui->dtSlider->setMaximum(7); ui->dtSlider->setSign(false); ui->dtSlider->setValue(dt); } } QString FMOperatorTable::toString() const { int dt = ui->dtSlider->value(); if (isDTNegative_) dt = DT_UNSIGN_TBL_[dt + 3]; auto str = QString("%1,%2,%3,%4,%5,%6,%7,%8,%9,%10") .arg(ui->arSlider->value()) .arg(ui->drSlider->value()) .arg(ui->srSlider->value()) .arg(ui->rrSlider->value()) .arg(ui->slSlider->value()) .arg(ui->tlSlider->value()) .arg(ui->ksSlider->value()) .arg(ui->mlSlider->value()) .arg(dt) .arg(ui->ssgegCheckBox->isChecked() ? ui->ssgegSlider->value() : -1); return str; } bool FMOperatorTable::eventFilter(QObject* obj, QEvent* event) { if (obj == ui->envFrame) { if (event->type() == QEvent::Paint) { QPainter painter(ui->envFrame); painter.eraseRect(ui->envFrame->rect()); painter.drawPixmap(ui->envFrame->rect(), envmap_, envmap_.rect()); } } return false; } void FMOperatorTable::showEvent(QShowEvent* event) { Q_UNUSED(event) resizeGraph(); repaintGraph(); } void FMOperatorTable::resizeEvent(QResizeEvent* event) { Q_UNUSED(event) resizeGraph(); repaintGraph(); } void FMOperatorTable::resizeGraph() { envmap_ = QPixmap(ui->envFrame->size()); xr_ = (envmap_.width() - (ENV_LINE_W_ + 1) * 2.) / ENV_W_; yr_ = (envmap_.height() - (ENV_LINE_W_ + 1) * 2.) / ENV_H_; } void FMOperatorTable::repaintGraph() { if (!palette_) return; QPointF p0, p1, p2, p3, p4; const double marginHorizon = 100; const double marginAr = 50; const double marginDr = 150; const double marginSr = 400; if (ui->arSlider->value() && ui->tlSlider->value() < 127) { p1.setY(127 - ui->tlSlider->value()); if (ui->arSlider->value() < 31) { double ar = 127. / (marginAr * (31 - ui->arSlider->value()) / 30.); p1.setX(p1.y() / ar); } if (ui->drSlider->value()) { p2.setY(p1.y() * (1. - ui->slSlider->value() / 15.)); if (ui->drSlider->value() == 31) { p2.setX(p1.x()); if (ui->slSlider->value() == 15) { p3 = p2; } else { if (ui->srSlider->value()) { p3.setY(p2.y() / 2.); if (ui->srSlider->value() == 31) { p3.setX(p2.x()); } else { double sr = 127. / (marginSr * (31 - ui->srSlider->value()) / 30.); p3.setX(p2.x() + p3.y() / 2. / sr); } } else { p3 = { p2.x() + ((ui->slSlider->value() == 15) ? 0 : marginHorizon), p2.y() }; } } } else { double dr = 127. / (marginDr * (31 - ui->drSlider->value()) / 30.); p2.setX(p1.x() + (p1.y() - p2.y()) / dr); if (ui->srSlider->value()) { p3.setY(p2.y() / 2.); if (ui->srSlider->value() == 31) { p3.setX(p2.x()); } else { double sr = 127. / (marginSr * (31 - ui->srSlider->value()) / 30.); p3.setX(p2.x() + p3.y() / 2. / sr); } } else { p3 = { p2.x() + ((ui->slSlider->value() == 15) ? 0 : marginHorizon), p2.y() }; } } } else { if (ui->slSlider->value()) { p2 = { p1.x() + marginHorizon, p1.y() }; p3 = p2; } else { p2 = p1; if (ui->srSlider->value()) { p3.setY(p2.y() / 2.); if (ui->srSlider->value() == 31) { p3.setX(p2.x()); } else { double sr = 127. / (marginSr * (31 - ui->srSlider->value()) / 30.); p3.setX(p2.x() + p3.y() / 2. / sr); } } else { p3 = { p2.x() + marginHorizon, p2.y() }; } } } if (!ui->rrSlider->value()) { p4 = { ENV_W_, p3.y() }; } else if (ui->rrSlider->value() == 31) { p4.setX(p3.x()); } else { double rr = 127. / (marginAr * (15 - ui->rrSlider->value()) / 14.); p4.setX(p3.x() + p3.y() / rr); } } double envHeight = ui->ssgegCheckBox->isChecked() ? (ENV_H_ - SSGEG_H_) : ENV_H_; double envHrate = envHeight / 127.; p0.setY((127. - p0.y()) * envHrate); p1.setY((127. - p1.y()) * envHrate); p2.setY((127. - p2.y()) * envHrate); p3.setY((127. - p3.y()) * envHrate); p4.setY((127. - p4.y()) * envHrate); envmap_.fill(palette_->instFMEnvBackColor); QPainter painter(&envmap_); painter.setPen(QPen(palette_->instFMEnvGridColor, ENV_LINE_T_)); drawLine(painter, p1.x(), 0, p1.x(), envHeight); drawLine(painter, p2.x(), 0, p2.x(), envHeight); drawLine(painter, p3.x(), 0, p3.x(), envHeight); painter.setPen(QPen(palette_->instFMEnvLine1Color, ENV_LINE_W_)); drawLines(painter, { p0, p1, p2, p3 }); painter.setPen(QPen(palette_->instFMEnvLine2Color, ENV_LINE_W_)); drawLine(painter, p3, p4); if (ui->ssgegCheckBox->isChecked()) { const double seph = envHeight + 1; painter.setPen(QPen(palette_->instFMEnvBorderColor, ENV_LINE_T_)); drawLine(painter, 0, seph, ENV_W_, seph); const double toph = seph + 2; const double both = ENV_H_; const double horsec = ENV_W_ / 5.; const double dhorsec = horsec * 2; painter.setPen(QPen(palette_->instFMEnvLine3Color, ENV_LINE_W_)); switch (ui->ssgegSlider->value()) { case 0: { for (int i = 0; i < 5; ++i) { drawLine(painter, horsec * i, both, horsec * i, toph); drawLine(painter, horsec * i, toph, horsec * (i + 1), both); } } break; case 1: { drawLine(painter, 0, both, 0, toph); drawLine(painter, 0, toph, horsec, both); drawLine(painter, horsec, both, ENV_W_, both); } break; case 2: { drawLine(painter, 0, both, 0, toph); drawLine(painter, 0, toph, horsec, both); for (int i = 0; i < 2; ++i) { drawLine(painter, horsec + dhorsec * i, both, dhorsec + dhorsec * i, toph); drawLine(painter, dhorsec + dhorsec * i, toph, horsec + dhorsec * (i + 1), both); } } break; case 3: { drawLine(painter, 0, both, 0, toph); drawLine(painter, 0, toph, horsec, both); drawLine(painter, horsec, both, horsec, toph); drawLine(painter, horsec, toph, ENV_W_, toph); } break; case 4: { for (int i = 0; i < 5; ++i) { drawLine(painter, horsec * i, both, horsec * (i + 1), toph); drawLine(painter, horsec * (i + 1), toph, horsec * (i + 1), both); } } break; case 5: { drawLine(painter, 0, both, horsec, toph); drawLine(painter, horsec, toph, ENV_W_, toph); } break; case 6: { for (int i = 0; i < 2; ++i) { drawLine(painter, dhorsec * i, both, horsec + dhorsec * i, toph); drawLine(painter, horsec + dhorsec * i, toph, dhorsec * (i + 1), both); } drawLine(painter, dhorsec * 2, both, ENV_W_, toph); } break; case 7: { drawLine(painter, 0, both, horsec, toph); drawLine(painter, horsec, toph, horsec, both); drawLine(painter, horsec, both, ENV_W_, both); } break; } } ui->envFrame->repaint(); } void FMOperatorTable::on_ssgegCheckBox_stateChanged(int arg1) { Q_UNUSED(arg1) if (ui->ssgegCheckBox->isChecked()) { ui->ssgegSlider->setEnabled(true); if (!isIgnoreEvent_) emit operatorValueChanged(Ui::FMOperatorParameter::SSGEG, ui->ssgegSlider->value()); } else { ui->ssgegSlider->setEnabled(false); if (!isIgnoreEvent_) emit operatorValueChanged(Ui::FMOperatorParameter::SSGEG, -1); } repaintGraph(); } void FMOperatorTable::on_groupBox_toggled(bool arg1) { if (!isIgnoreEvent_) emit operatorEnableChanged(arg1); } void FMOperatorTable::on_groupBox_customContextMenuRequested(const QPoint &pos) { QPoint globalPos = ui->groupBox->mapToGlobal(pos); QMenu menu; // Leave Before Qt5.7.0 style due to windows xp QAction* copyEnv = menu.addAction(tr("Copy envelope")); QObject::connect(copyEnv, &QAction::triggered, this, [&] { emit copyEnvelopePressed(); }); QAction* pasteEnv = menu.addAction(tr("Paste envelope")); QObject::connect(pasteEnv, &QAction::triggered, this, [&] { emit pasteEnvelopePressed(); }); QMenu* pasteFrom = menu.addMenu(tr("Paste envelope From")); for (size_t i = 0; i < envelopeTypes_.size(); ++i) { QAction* act = pasteFrom->addAction(envelopeTypes_[i]); act->setData(static_cast(i)); } QObject::connect(pasteFrom, &QMenu::triggered, this, [&](QAction* action) { emit pasteEnvelopeFromPressed(action->data().toInt()); }); menu.addSeparator(); QAction* copyOp = menu.addAction(tr("Copy operator")); QObject::connect(copyOp, &QAction::triggered, this, [&] { emit copyOperatorPressed(number_); }); QAction* pasteOp = menu.addAction(tr("Paste operator")); QObject::connect(pasteOp, &QAction::triggered, this, [&] { emit pasteOperatorPressed(number_); }); QClipboard* clipboard = QApplication::clipboard(); pasteEnv->setEnabled(clipboard->text().startsWith("FM_ENVELOPE:")); pasteOp->setEnabled(clipboard->text().startsWith("FM_OPERATOR:")); menu.exec(globalPos); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/fm_operator_table.hpp000066400000000000000000000074611401124043500275100ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef FM_OPERATOR_TABLE_HPP #define FM_OPERATOR_TABLE_HPP #include #include #include #include #include #include #include #include #include #include #include "gui/labeled_vertical_slider.hpp" #include "gui/color_palette.hpp" #include "enum_hash.hpp" namespace Ui { class FMOperatorTable; enum class FMOperatorParameter; } class FMOperatorTable : public QFrame { Q_OBJECT public: explicit FMOperatorTable(QWidget *parent = nullptr); ~FMOperatorTable() override; void setEnvelopeSetNames(std::vector list); void setColorPalette(std::shared_ptr palette); void setOperatorNumber(int n); int operatorNumber() const; void setValue(Ui::FMOperatorParameter param, int value); void setGroupEnabled(bool enabled); void setDTDisplayType(bool useNegative); QString toString() const; protected: bool eventFilter(QObject* obj, QEvent* event) override; void showEvent(QShowEvent* event) override; void resizeEvent(QResizeEvent* event) override; signals: void operatorValueChanged(Ui::FMOperatorParameter param, int value); void operatorEnableChanged(bool enable); void copyEnvelopePressed(); void pasteEnvelopePressed(); void pasteEnvelopeFromPressed(int typenum); void copyOperatorPressed(int num); void pasteOperatorPressed(int num); private slots: void on_ssgegCheckBox_stateChanged(int arg1); void on_groupBox_toggled(bool arg1); void on_groupBox_customContextMenuRequested(const QPoint &pos); private: Ui::FMOperatorTable *ui; std::shared_ptr palette_; int number_; std::unordered_map sliderMap_; std::vector envelopeTypes_; bool isDTNegative_; bool isIgnoreEvent_; // Envelope graph QPixmap envmap_; static constexpr int ENV_H_ = 127; static constexpr int ENV_W_ = 200; static constexpr int SSGEG_H_ = 35; static constexpr int ENV_LINE_W_ = 2; static constexpr int ENV_LINE_T_ = 1; double xr_, yr_; void resizeGraph(); void repaintGraph(); inline void drawLine(QPainter& painter, qreal x1, qreal y1, qreal x2, qreal y2) { painter.drawLine(ENV_LINE_W_ + x1 * xr_ + 1, ENV_LINE_W_ + y1 * yr_ + 1, ENV_LINE_W_ + x2 * xr_ + 1, ENV_LINE_W_ + y2 * yr_ + 1); } inline void drawLine(QPainter& painter, const QPointF& p1, const QPointF& p2) { drawLine(painter, p1.x(), p1.y(), p2.x(), p2.y()); } inline void drawLines(QPainter& painter, const QVector& ps) { for (int i = 1; i < ps.size(); ++i) { drawLine(painter, ps[i-1], ps[i]); } } }; namespace Ui { enum class FMOperatorParameter { AR, DR, SR, RR, SL, TL, KS, ML, DT, AM, SSGEG }; } #endif // FM_OPERATOR_TABLE_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/fm_operator_table.ui000066400000000000000000000205661401124043500273370ustar00rootroot00000000000000 FMOperatorTable 0 0 663 241 Frame FMOperatorTable { border: 0; } 3 0 3 0 Operator true 3 3 3 3 3 0 0 0 70 QFrame::StyledPanel QFrame::Raised 0 12 16777215 12 SSGEG Qt::AlignCenter 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 31 0 QFrame::StyledPanel QFrame::Raised 100 0 QFrame::StyledPanel QFrame::Sunken 1 LabeledVerticalSlider QFrame
gui/labeled_vertical_slider.hpp
1
BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/grid_settings_dialog.cpp000066400000000000000000000030651401124043500301770ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "grid_settings_dialog.hpp" #include "ui_grid_settings_dialog.h" GridSettingsDialog::GridSettingsDialog(int interval, QWidget *parent) : QDialog(parent), ui(new Ui::GridSettingsDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui->intrSpinBox->setValue(interval); } GridSettingsDialog::~GridSettingsDialog() { delete ui; } int GridSettingsDialog::getInterval() const { return ui->intrSpinBox->value(); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/grid_settings_dialog.hpp000066400000000000000000000027711401124043500302070ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef GRID_SETTINGS_DIALOG_HPP #define GRID_SETTINGS_DIALOG_HPP #include namespace Ui { class GridSettingsDialog; } class GridSettingsDialog : public QDialog { Q_OBJECT public: explicit GridSettingsDialog(int interval = 1, QWidget *parent = nullptr); ~GridSettingsDialog() override; int getInterval() const; private: Ui::GridSettingsDialog *ui; }; #endif // GRID_SETTINGS_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/grid_settings_dialog.ui000066400000000000000000000037321401124043500300330ustar00rootroot00000000000000 GridSettingsDialog 0 0 174 69 Grid Settings Interval 1 2147483647 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() GridSettingsDialog accept() 248 254 157 274 buttonBox rejected() GridSettingsDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_adpcm_form.cpp000066400000000000000000000535121401124043500317620ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_editor_adpcm_form.hpp" #include "ui_instrument_editor_adpcm_form.h" #include #include "instrument.hpp" #include "gui/event_guard.hpp" #include "gui/jam_layout.hpp" #include "gui/instrument_editor/instrument_editor_utils.hpp" #include "gui/gui_utils.hpp" InstrumentEditorADPCMForm::InstrumentEditorADPCMForm(int num, QWidget *parent) : QWidget(parent), ui(new Ui::InstrumentEditorADPCMForm), instNum_(num), isIgnoreEvent_(false) { ui->setupUi(this); //========== Sample ==========// QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::modified, this, [&] { emit modified(); }); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleNumberChanged, this, [&](int n) { bt_.lock()->setInstrumentADPCMSample(instNum_, n); setInstrumentSampleParameters(); emit sampleNumberChanged(); emit modified(); if (config_.lock()->getWriteOnlyUsedSamples()) { emit sampleAssignRequested(); } }, Qt::DirectConnection); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleParameterChanged, this, [&](int sampNum) { emit sampleParameterChanged(sampNum, instNum_); }); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleAssignRequested, this, [&] { emit sampleAssignRequested(); }); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleMemoryChanged, this, [&] { emit sampleMemoryChanged(); }); //========== Envelope ==========// ui->envEditor->setMaximumDisplayedRowCount(64); ui->envEditor->setDefaultRow(255); ui->envEditor->setLabelDiaplayMode(true); for (int i = 0; i < 256; ++i) { ui->envEditor->AddRow(QString::number(i), false); } ui->envEditor->autoFitLabelWidth(); ui->envEditor->setUpperRow(255); ui->envEditor->setMultipleReleaseState(true); ui->envEditor->setPermittedReleaseTypes( VisualizedInstrumentMacroEditor::PermittedReleaseFlag::ABSOLUTE_RELEASE | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::RELATIVE_RELEASE | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::FIXED_RELEASE); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addEnvelopeADPCMSequenceData(ui->envNumSpinBox->value(), row); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removeEnvelopeADPCMSequenceData(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeADPCMSequenceData(ui->envNumSpinBox->value(), col, row); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addEnvelopeADPCMLoop(ui->envNumSpinBox->value(), loop); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removeEnvelopeADPCMLoop(ui->envNumSpinBox->value(), begin, end); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearEnvelopeADPCMLoops(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changeEnvelopeADPCMLoop(ui->envNumSpinBox->value(), prevBegin, prevEnd, loop); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeADPCMRelease(ui->envNumSpinBox->value(), release); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); //========== Arpeggio ==========// ui->arpTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); ui->arpTypeComboBox->addItem(tr("Fixed"), static_cast(SequenceType::FixedSequence)); ui->arpTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addArpeggioADPCMSequenceData(ui->arpNumSpinBox->value(), row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removeArpeggioADPCMSequenceData(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setArpeggioADPCMSequenceData(ui->arpNumSpinBox->value(), col, row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addArpeggioADPCMLoop(ui->arpNumSpinBox->value(), loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removeArpeggioADPCMLoop(ui->arpNumSpinBox->value(), begin, end); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearArpeggioADPCMLoops(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changeArpeggioADPCMLoop(ui->arpNumSpinBox->value(), prevBegin, prevEnd, loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setArpeggioADPCMRelease(ui->arpNumSpinBox->value(), release); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->arpTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorADPCMForm::onArpeggioTypeChanged); //========== Pitch ==========// ui->ptEditor->setMaximumDisplayedRowCount(15); ui->ptEditor->setDefaultRow(127); ui->ptEditor->setLabelDiaplayMode(true); for (int i = 0; i < 255; ++i) { ui->ptEditor->AddRow(QString::asprintf("%+d", i - 127), false); } ui->ptEditor->autoFitLabelWidth(); ui->ptEditor->setUpperRow(134); ui->ptEditor->setMMLDisplay0As(-127); ui->ptTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); ui->ptTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addPitchADPCMSequenceData(ui->ptNumSpinBox->value(), row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removePitchADPCMSequenceData(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setPitchADPCMSequenceData(ui->ptNumSpinBox->value(), col, row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addPitchADPCMLoop(ui->ptNumSpinBox->value(), loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removePitchADPCMLoop(ui->ptNumSpinBox->value(), begin, end); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearPitchADPCMLoops(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changePitchADPCMLoop(ui->ptNumSpinBox->value(), prevBegin, prevEnd, loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setPitchADPCMRelease(ui->ptNumSpinBox->value(), release); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->ptTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorADPCMForm::onPitchTypeChanged); } InstrumentEditorADPCMForm::~InstrumentEditorADPCMForm() { delete ui; } void InstrumentEditorADPCMForm::setInstrumentNumber(int num) { instNum_ = num; } int InstrumentEditorADPCMForm::getInstrumentNumber() const { return instNum_; } void InstrumentEditorADPCMForm::setCore(std::weak_ptr core) { bt_ = core; ui->sampleEditor->setCore(core); updateInstrumentParameters(); } void InstrumentEditorADPCMForm::setConfiguration(std::weak_ptr config) { config_ = config; ui->sampleEditor->setConfiguration(config); } void InstrumentEditorADPCMForm::setColorPalette(std::shared_ptr palette) { palette_ = palette; ui->sampleEditor->setColorPalette(palette); ui->envEditor->setColorPalette(palette); ui->arpEditor->setColorPalette(palette); ui->ptEditor->setColorPalette(palette); } void InstrumentEditorADPCMForm::updateInstrumentParameters() { Ui::EventGuard eg(isIgnoreEvent_); auto name = gui_utils::utf8ToQString(bt_.lock()->getInstrument(instNum_)->getName()); setWindowTitle(QString("%1: %2").arg(instNum_, 2, 16, QChar('0')).toUpper().arg(name)); setInstrumentSampleParameters(); setInstrumentEnvelopeParameters(); setInstrumentArpeggioParameters(); setInstrumentPitchParameters(); } /********** Events **********/ // MUST DIRECT CONNECTION void InstrumentEditorADPCMForm::keyPressEvent(QKeyEvent *event) { // General keys switch (event->key()) { case Qt::Key_Escape: close(); break; default: // For jam key on if (!event->isAutoRepeat()) { // Musical keyboard Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOnEvent(jk); } catch (std::invalid_argument&) {} } break; } } // MUST DIRECT CONNECTION void InstrumentEditorADPCMForm::keyReleaseEvent(QKeyEvent *event) { // For jam key off if (!event->isAutoRepeat()) { Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOffEvent(jk); } catch (std::invalid_argument&) {} } } //--- Sample void InstrumentEditorADPCMForm::setInstrumentSampleParameters() { std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instADPCM = dynamic_cast(inst.get()); ui->sampleEditor->setInstrumentSampleParameters( instADPCM->getSampleNumber(), instADPCM->isSampleRepeatable(), instADPCM->getSampleRootKeyNumber(), instADPCM->getSampleRootDeltaN(), instADPCM->getSampleStartAddress(), instADPCM->getSampleStopAddress(), instADPCM->getRawSample()); } /********** Slots **********/ void InstrumentEditorADPCMForm::onSampleNumberChanged() { ui->sampleEditor->onSampleNumberChanged(); } void InstrumentEditorADPCMForm::onSampleParameterChanged(int sampNum) { if (ui->sampleEditor->getSampleNumber() == sampNum) { setInstrumentSampleParameters(); } } void InstrumentEditorADPCMForm::onSampleMemoryUpdated() { std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instADPCM = dynamic_cast(inst.get()); ui->sampleEditor->onSampleMemoryUpdated(instADPCM->getSampleStartAddress(), instADPCM->getSampleStopAddress()); } //--- Envelope void InstrumentEditorADPCMForm::setInstrumentEnvelopeParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instADPCM = dynamic_cast(inst.get()); ui->envNumSpinBox->setValue(instADPCM->getEnvelopeNumber()); ui->envEditor->clearData(); for (auto& unit : instADPCM->getEnvelopeSequence()) { ui->envEditor->addSequenceData(unit.data); } for (auto& loop : instADPCM->getEnvelopeLoopRoot().getAllLoops()) { ui->envEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->envEditor->setRelease(instADPCM->getEnvelopeRelease()); if (instADPCM->getEnvelopeEnabled()) { ui->envEditGroupBox->setChecked(true); onEnvelopeNumberChanged(); } else { ui->envEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorADPCMForm::onEnvelopeNumberChanged() { // Change users view std::multiset users = bt_.lock()->getEnvelopeADPCMUsers(ui->envNumSpinBox->value()); ui->envUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorADPCMForm::onEnvelopeParameterChanged(int envNum) { if (ui->envNumSpinBox->value() == envNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentEnvelopeParameters(); } } void InstrumentEditorADPCMForm::on_envEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentADPCMEnvelopeEnabled(instNum_, arg1); setInstrumentEnvelopeParameters(); emit envelopeNumberChanged(); emit modified(); } onEnvelopeNumberChanged(); } void InstrumentEditorADPCMForm::on_envNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentADPCMEnvelope(instNum_, arg1); setInstrumentEnvelopeParameters(); emit envelopeNumberChanged(); emit modified(); } onEnvelopeNumberChanged(); } //--- Arpeggio void InstrumentEditorADPCMForm::setInstrumentArpeggioParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instADPCM = dynamic_cast(inst.get()); ui->arpNumSpinBox->setValue(instADPCM->getArpeggioNumber()); ui->arpEditor->clearData(); for (auto& unit : instADPCM->getArpeggioSequence()) { ui->arpEditor->addSequenceData(unit.data); } for (auto& loop : instADPCM->getArpeggioLoopRoot().getAllLoops()) { ui->arpEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->arpEditor->setRelease(instADPCM->getArpeggioRelease()); for (int i = 0; i < ui->arpTypeComboBox->count(); ++i) { if (instADPCM->getArpeggioType() == static_cast(ui->arpTypeComboBox->itemData(i).toInt())) { ui->arpTypeComboBox->setCurrentIndex(i); break; } } if (instADPCM->getArpeggioEnabled()) { ui->arpEditGroupBox->setChecked(true); onArpeggioNumberChanged(); } else { ui->arpEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorADPCMForm::onArpeggioNumberChanged() { // Change users view std::multiset users = bt_.lock()->getArpeggioADPCMUsers(ui->arpNumSpinBox->value()); ui->arpUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorADPCMForm::onArpeggioParameterChanged(int tnNum) { if (ui->arpNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentArpeggioParameters(); } } void InstrumentEditorADPCMForm::onArpeggioTypeChanged(int index) { Q_UNUSED(index) auto type = static_cast(ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { bt_.lock()->setArpeggioADPCMType(ui->arpNumSpinBox->value(), type); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } ui->arpEditor->setSequenceType(type); } void InstrumentEditorADPCMForm::on_arpEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentADPCMArpeggioEnabled(instNum_, arg1); setInstrumentArpeggioParameters(); emit arpeggioNumberChanged(); emit modified(); } onArpeggioNumberChanged(); } void InstrumentEditorADPCMForm::on_arpNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentADPCMArpeggio(instNum_, arg1); setInstrumentArpeggioParameters(); emit arpeggioNumberChanged(); emit modified(); } onArpeggioNumberChanged(); } //--- Pitch void InstrumentEditorADPCMForm::setInstrumentPitchParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instADPCM = dynamic_cast(inst.get()); ui->ptNumSpinBox->setValue(instADPCM->getPitchNumber()); ui->ptEditor->clearData(); for (auto& unit : instADPCM->getPitchSequence()) { ui->ptEditor->addSequenceData(unit.data); } for (auto& loop : instADPCM->getPitchLoopRoot().getAllLoops()) { ui->ptEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->ptEditor->setRelease(instADPCM->getPitchRelease()); for (int i = 0; i < ui->ptTypeComboBox->count(); ++i) { if (instADPCM->getPitchType() == static_cast(ui->ptTypeComboBox->itemData(i).toInt())) { ui->ptTypeComboBox->setCurrentIndex(i); break; } } if (instADPCM->getPitchEnabled()) { ui->ptEditGroupBox->setChecked(true); onPitchNumberChanged(); } else { ui->ptEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorADPCMForm::onPitchNumberChanged() { // Change users view std::multiset users = bt_.lock()->getPitchADPCMUsers(ui->ptNumSpinBox->value()); ui->ptUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorADPCMForm::onPitchParameterChanged(int tnNum) { if (ui->ptNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentPitchParameters(); } } void InstrumentEditorADPCMForm::onPitchTypeChanged(int index) { Q_UNUSED(index) auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { bt_.lock()->setPitchADPCMType(ui->ptNumSpinBox->value(), type); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } ui->ptEditor->setSequenceType(type); } void InstrumentEditorADPCMForm::on_ptEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentADPCMPitchEnabled(instNum_, arg1); setInstrumentPitchParameters(); emit pitchNumberChanged(); emit modified(); } onPitchNumberChanged(); } void InstrumentEditorADPCMForm::on_ptNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentADPCMPitch(instNum_, arg1); setInstrumentPitchParameters(); emit pitchNumberChanged(); emit modified(); } onPitchNumberChanged(); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_adpcm_form.hpp000066400000000000000000000076511401124043500317720ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_EDITOR_ADPCM_FORM_HPP #define INSTRUMENT_EDITOR_ADPCM_FORM_HPP #include #include #include #include "bamboo_tracker.hpp" #include "configuration.hpp" #include "jamming.hpp" #include "gui/color_palette.hpp" #include "gui/instrument_editor/visualized_instrument_macro_editor.hpp" namespace Ui { class InstrumentEditorADPCMForm; } class InstrumentEditorADPCMForm : public QWidget { Q_OBJECT public: explicit InstrumentEditorADPCMForm(int num, QWidget *parent = nullptr); ~InstrumentEditorADPCMForm() override; void setInstrumentNumber(int num); int getInstrumentNumber() const; void setCore(std::weak_ptr core); void setConfiguration(std::weak_ptr config); void setColorPalette(std::shared_ptr palette); signals: void jamKeyOnEvent(JamKey key); void jamKeyOffEvent(JamKey key); void modified(); protected: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; private: Ui::InstrumentEditorADPCMForm *ui; int instNum_; bool isIgnoreEvent_; std::weak_ptr bt_; std::weak_ptr config_; std::shared_ptr palette_; void updateInstrumentParameters(); //========== Sample ==========// signals: void sampleNumberChanged(); void sampleParameterChanged(int sampNum, int fromInstNum); void sampleAssignRequested(); void sampleMemoryChanged(); public slots: void onSampleNumberChanged(); void onSampleParameterChanged(int sampNum); void onSampleMemoryUpdated(); private: void setInstrumentSampleParameters(); //========== Envelope ==========// signals: void envelopeNumberChanged(); void envelopeParameterChanged(int envNum, int fromInstNum); public slots: void onEnvelopeNumberChanged(); void onEnvelopeParameterChanged(int envNum); private: void setInstrumentEnvelopeParameters(); private slots: void on_envEditGroupBox_toggled(bool arg1); void on_envNumSpinBox_valueChanged(int arg1); //========== Arpeggio ==========// signals: void arpeggioNumberChanged(); void arpeggioParameterChanged(int arpNum, int fromInstNum); public slots: void onArpeggioNumberChanged(); void onArpeggioParameterChanged(int arpNum); private: void setInstrumentArpeggioParameters(); private slots: void onArpeggioTypeChanged(int index); void on_arpEditGroupBox_toggled(bool arg1); void on_arpNumSpinBox_valueChanged(int arg1); //========== Pitch ==========// signals: void pitchNumberChanged(); void pitchParameterChanged(int ptNum, int fromInstNum); public slots: void onPitchNumberChanged(); void onPitchParameterChanged(int arpNum); private: void setInstrumentPitchParameters(); private slots: void onPitchTypeChanged(int index); void on_ptEditGroupBox_toggled(bool arg1); void on_ptNumSpinBox_valueChanged(int arg1); }; #endif // INSTRUMENT_EDITOR_ADPCM_FORM_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_adpcm_form.ui000066400000000000000000000226621401124043500316170ustar00rootroot00000000000000 InstrumentEditorADPCMForm 0 0 507 430 Form 0 false Sample Sample Envelope Envelope true false false # 127 Users: Qt::NoFocus true Qt::Horizontal 40 20 Arpeggio Arpeggio true false Type: false # 127 Qt::Horizontal 40 20 Users: Qt::NoFocus true Pitch Pitch true false Users: Type: Qt::Horizontal 40 20 # 127 Qt::NoFocus true VisualizedInstrumentMacroEditor QWidget
gui/instrument_editor/visualized_instrument_macro_editor.hpp
1
ArpeggioMacroEditor QWidget
gui/instrument_editor/arpeggio_macro_editor.hpp
1
ADPCMSampleEditor QWidget
gui/instrument_editor/adpcm_sample_editor.hpp
1
tabWidget envEditGroupBox envNumSpinBox arpEditGroupBox arpNumSpinBox arpTypeComboBox ptEditGroupBox ptNumSpinBox ptTypeComboBox
BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_drumkit_form.cpp000066400000000000000000000221011401124043500323430ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_editor_drumkit_form.hpp" #include "ui_instrument_editor_drumkit_form.h" #include #include "instrument.hpp" #include "note.hpp" #include "gui/event_guard.hpp" #include "gui/jam_layout.hpp" #include "gui/gui_utils.hpp" InstrumentEditorDrumkitForm::InstrumentEditorDrumkitForm(int num, QWidget *parent) : QWidget(parent), ui(new Ui::InstrumentEditorDrumkitForm), instNum_(num), isIgnoreEvent_(false), hasShown_(false) { ui->setupUi(this); ui->keyTreeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); QString tone[] = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" }; for (int i = 0; i < Note::NOTE_NUMBER_RANGE; ++i) { ui->keyTreeWidget->addTopLevelItem( new QTreeWidgetItem({ tone[i % 12] + QString::number(i / 12), "-", "-" })); } //========== Sample ==========// QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::modified, this, [&] { emit modified(); }); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleNumberChanged, this, [&](int n) { bt_.lock()->setInstrumentDrumkitSample(instNum_, ui->keyTreeWidget->currentIndex().row(), n); setInstrumentSampleParameters(ui->keyTreeWidget->currentIndex().row()); emit sampleNumberChanged(); emit modified(); if (config_.lock()->getWriteOnlyUsedSamples()) { emit sampleAssignRequested(); } }, Qt::DirectConnection); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleParameterChanged, this, [&](int sampNum) { emit sampleParameterChanged(sampNum, instNum_); }); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleAssignRequested, this, [&] { emit sampleAssignRequested(); }); QObject::connect(ui->sampleEditor, &ADPCMSampleEditor::sampleMemoryChanged, this, [&] { emit sampleMemoryChanged(); }); } InstrumentEditorDrumkitForm::~InstrumentEditorDrumkitForm() { delete ui; } void InstrumentEditorDrumkitForm::setInstrumentNumber(int num) { instNum_ = num; } int InstrumentEditorDrumkitForm::getInstrumentNumber() const { return instNum_; } void InstrumentEditorDrumkitForm::setCore(std::weak_ptr core) { bt_ = core; ui->sampleEditor->setCore(core); updateInstrumentParameters(); } void InstrumentEditorDrumkitForm::setConfiguration(std::weak_ptr config) { config_ = config; ui->sampleEditor->setConfiguration(config); } void InstrumentEditorDrumkitForm::setColorPalette(std::shared_ptr palette) { palette_ = palette; ui->sampleEditor->setColorPalette(palette); } void InstrumentEditorDrumkitForm::updateInstrumentParameters() { Ui::EventGuard eg(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instKit = dynamic_cast(inst.get()); auto name = gui_utils::utf8ToQString(instKit->getName()); setWindowTitle(QString("%1: %2").arg(instNum_, 2, 16, QChar('0')).toUpper().arg(name)); for (const auto& key : instKit->getAssignedKeys()) { setInstrumentSampleParameters(key); } } /********** Events **********/ void InstrumentEditorDrumkitForm::showEvent(QShowEvent*) { if (!hasShown_) { ui->keyTreeWidget->setCurrentItem(ui->keyTreeWidget->topLevelItem(Note::DEFAULT_NOTE_NUM)); ui->keyTreeWidget->scrollTo(ui->keyTreeWidget->model()->index(Note::DEFAULT_NOTE_NUM, 0), QAbstractItemView::PositionAtTop); } hasShown_ = true; } // MUST DIRECT CONNECTION void InstrumentEditorDrumkitForm::keyPressEvent(QKeyEvent *event) { // General keys switch (event->key()) { case Qt::Key_Escape: close(); break; default: // For jam key on if (!event->isAutoRepeat()) { // Musical keyboard Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOnEvent(jk); } catch (std::invalid_argument&) {} } break; } } // MUST DIRECT CONNECTION void InstrumentEditorDrumkitForm::keyReleaseEvent(QKeyEvent *event) { // For jam key off if (!event->isAutoRepeat()) { Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOffEvent(jk); } catch (std::invalid_argument&) {} } } /********** Slots **********/ void InstrumentEditorDrumkitForm::on_keyTreeWidget_currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*) { Ui::EventGuard eg(isIgnoreEvent_); int key = ui->keyTreeWidget->currentIndex().row(); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instKit = dynamic_cast(inst.get()); bool enabled = instKit->getSampleEnabled(key); ui->sampleGroupBox->setChecked(enabled); if (enabled) { setInstrumentSampleParameters(key); ui->pitshSpinBox->setValue(instKit->getPitch(key)); } } void InstrumentEditorDrumkitForm::on_pitshSpinBox_valueChanged(int arg1) { int key = ui->keyTreeWidget->currentIndex().row(); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instKit = dynamic_cast(inst.get()); if (instKit->getSampleEnabled(key)) { bt_.lock()->setInstrumentDrumkitPitch(instNum_, key, arg1); ui->keyTreeWidget->currentItem()->setText(2, QString::number(arg1)); emit modified(); } } //--- Sample void InstrumentEditorDrumkitForm::setInstrumentSampleParameters(int key) { std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instKit = dynamic_cast(inst.get()); QTreeWidgetItem* item = ui->keyTreeWidget->topLevelItem(key); if (instKit->getSampleEnabled(key)) { int sampNum = instKit->getSampleNumber(key); ui->sampleEditor->setInstrumentSampleParameters( sampNum, instKit->isSampleRepeatable(key), instKit->getSampleRootKeyNumber(key), instKit->getSampleRootDeltaN(key), instKit->getSampleStartAddress(key), instKit->getSampleStopAddress(key), instKit->getRawSample(key)); item->setText(1, QString::number(sampNum)); item->setText(2, QString::number(instKit->getPitch(key))); } else { ui->sampleEditor->setInstrumentSampleParameters( 0, bt_.lock()->getSampleADPCMRepeatEnabled(0), bt_.lock()->getSampleADPCMRootKeyNumber(0), bt_.lock()->getSampleADPCMRootDeltaN(0), bt_.lock()->getSampleADPCMStartAddress(0), bt_.lock()->getSampleADPCMStopAddress(0), bt_.lock()->getSampleADPCMRawSample(0)); item->setText(1, "-"); item->setText(2, "-"); } } /********** Slots **********/ void InstrumentEditorDrumkitForm::onSampleNumberChanged() { ui->sampleEditor->onSampleNumberChanged(); } void InstrumentEditorDrumkitForm::onSampleParameterChanged(int sampNum) { if (ui->sampleEditor->getSampleNumber() == sampNum) { setInstrumentSampleParameters(ui->keyTreeWidget->currentIndex().row()); } } void InstrumentEditorDrumkitForm::onSampleMemoryUpdated() { std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instKit = dynamic_cast(inst.get()); int key = ui->keyTreeWidget->currentIndex().row(); if (instKit->getSampleEnabled(key)) { ui->sampleEditor->onSampleMemoryUpdated(instKit->getSampleStartAddress(key), instKit->getSampleStopAddress(key)); } else { // Clear addresses ui->sampleEditor->onSampleMemoryUpdated(0, 0); } } void InstrumentEditorDrumkitForm::on_sampleGroupBox_clicked(bool checked) { Ui::EventGuard eg(isIgnoreEvent_); int key = ui->keyTreeWidget->currentIndex().row(); bt_.lock()->setInstrumentDrumkitSampleEnabled(instNum_, key, checked); if (checked) { ui->pitshSpinBox->setValue(0); setInstrumentSampleParameters(key); } else { // Clear parameters ui->sampleEditor->setInstrumentSampleParameters( 0, bt_.lock()->getSampleADPCMRepeatEnabled(0), bt_.lock()->getSampleADPCMRootKeyNumber(0), bt_.lock()->getSampleADPCMRootDeltaN(0), bt_.lock()->getSampleADPCMStartAddress(0), bt_.lock()->getSampleADPCMStopAddress(0), bt_.lock()->getSampleADPCMRawSample(0)); ui->keyTreeWidget->currentItem()->setText(1, "-"); ui->keyTreeWidget->currentItem()->setText(2, "-"); } emit sampleNumberChanged(); emit modified(); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_drumkit_form.hpp000066400000000000000000000057111401124043500323600ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_EDITOR_DRUMKIT_FORM_HPP #define INSTRUMENT_EDITOR_DRUMKIT_FORM_HPP #include #include #include #include #include "bamboo_tracker.hpp" #include "configuration.hpp" #include "jamming.hpp" #include "gui/color_palette.hpp" namespace Ui { class InstrumentEditorDrumkitForm; } class InstrumentEditorDrumkitForm : public QWidget { Q_OBJECT public: explicit InstrumentEditorDrumkitForm(int num, QWidget *parent = nullptr); ~InstrumentEditorDrumkitForm() override; void setInstrumentNumber(int num); int getInstrumentNumber() const; void setCore(std::weak_ptr core); void setConfiguration(std::weak_ptr config); void setColorPalette(std::shared_ptr palette); signals: void jamKeyOnEvent(JamKey key); void jamKeyOffEvent(JamKey key); void modified(); protected: void showEvent(QShowEvent*) override; void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; private: Ui::InstrumentEditorDrumkitForm *ui; int instNum_; bool isIgnoreEvent_; bool hasShown_; std::weak_ptr bt_; std::weak_ptr config_; std::shared_ptr palette_; void updateInstrumentParameters(); private slots: void on_keyTreeWidget_currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*); void on_pitshSpinBox_valueChanged(int arg1); //========== Sample ==========// signals: void sampleNumberChanged(); void sampleParameterChanged(int sampNum, int fromInstNum); void sampleAssignRequested(); void sampleMemoryChanged(); public slots: void onSampleNumberChanged(); void onSampleParameterChanged(int sampNum); void onSampleMemoryUpdated(); private slots: void on_sampleGroupBox_clicked(bool checked); private: void setInstrumentSampleParameters(int key); }; #endif // INSTRUMENT_EDITOR_DRUMKIT_FORM_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_drumkit_form.ui000066400000000000000000000073471401124043500322150ustar00rootroot00000000000000 InstrumentEditorDrumkitForm 0 0 590 430 Sample assignment 0 0 130 0 130 16777215 Qt::ScrollBarAlwaysOff false true false Key # Pitch Pitch -95 95 Sample true false 0 0 ADPCMSampleEditor QWidget
gui/instrument_editor/adpcm_sample_editor.hpp
1
keyTreeWidget pitshSpinBox sampleGroupBox
BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_fm_form.cpp000066400000000000000000002273011401124043500312770ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_editor_fm_form.hpp" #include "ui_instrument_editor_fm_form.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gui/event_guard.hpp" #include "gui/jam_layout.hpp" #include "gui/instrument_editor/instrument_editor_utils.hpp" #include "gui/gui_utils.hpp" InstrumentEditorFMForm::InstrumentEditorFMForm(int num, QWidget *parent) : QWidget(parent), ui(new Ui::InstrumentEditorFMForm), instNum_(num), isIgnoreEvent_(false) { ui->setupUi(this); /******************** Envelope editor ********************/ ui->envGroupBox->setContextMenuPolicy(Qt::CustomContextMenu); auto scene = new QGraphicsScene(ui->alGraphicsView); ui->alGraphicsView->setScene(scene); ui->alSlider->setText("AL"); ui->alSlider->setMaximum(7); QObject::connect(ui->alSlider, &LabeledHorizontalSlider::valueChanged, this, [&](int value) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeFMParameter(ui->envNumSpinBox->value(), FMEnvelopeParameter::AL, value); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } paintAlgorithmDiagram(); resizeAlgorithmDiagram(); }); ui->fbSlider->setText("FB"); ui->fbSlider->setMaximum(7); QObject::connect(ui->fbSlider, &LabeledHorizontalSlider::valueChanged, this, [&](int value) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeFMParameter(ui->envNumSpinBox->value(), FMEnvelopeParameter::FB, value); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); ui->op1Table->setOperatorNumber(0); QObject::connect(ui->op1Table, &FMOperatorTable::operatorEnableChanged, this, [&](bool enable) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeFMOperatorEnable(ui->envNumSpinBox->value(), 0, enable); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op1Table, &FMOperatorTable::operatorValueChanged, this, [&](Ui::FMOperatorParameter opParam, int value) { if (!isIgnoreEvent_) { FMEnvelopeParameter param; switch (opParam) { case Ui::FMOperatorParameter::AR: param = FMEnvelopeParameter::AR1; break; case Ui::FMOperatorParameter::DR: param = FMEnvelopeParameter::DR1; break; case Ui::FMOperatorParameter::SR: param = FMEnvelopeParameter::SR1; break; case Ui::FMOperatorParameter::RR: param = FMEnvelopeParameter::RR1; break; case Ui::FMOperatorParameter::SL: param = FMEnvelopeParameter::SL1; break; case Ui::FMOperatorParameter::TL: param = FMEnvelopeParameter::TL1; break; case Ui::FMOperatorParameter::KS: param = FMEnvelopeParameter::KS1; break; case Ui::FMOperatorParameter::ML: param = FMEnvelopeParameter::ML1; break; case Ui::FMOperatorParameter::DT: param = FMEnvelopeParameter::DT1; break; case Ui::FMOperatorParameter::SSGEG: param = FMEnvelopeParameter::SSGEG1; break; default: throw std::invalid_argument("Unexpected FMOperatorParameter."); } bt_.lock()->setEnvelopeFMParameter(ui->envNumSpinBox->value(), param, value); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op1Table, &FMOperatorTable::copyEnvelopePressed, this, &InstrumentEditorFMForm::copyEnvelope); QObject::connect(ui->op1Table, &FMOperatorTable::pasteEnvelopePressed, this, &InstrumentEditorFMForm::pasteEnvelope); QObject::connect(ui->op1Table, &FMOperatorTable::copyOperatorPressed, this, &InstrumentEditorFMForm::copyOperator); QObject::connect(ui->op1Table, &FMOperatorTable::pasteOperatorPressed, this, &InstrumentEditorFMForm::pasteOperator); QObject::connect(ui->op1Table, &FMOperatorTable::pasteEnvelopeFromPressed, this, &InstrumentEditorFMForm::pasteEnvelopeFrom); ui->op2Table->setOperatorNumber(1); QObject::connect(ui->op2Table, &FMOperatorTable::operatorEnableChanged, this, [&](bool enable) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeFMOperatorEnable(ui->envNumSpinBox->value(), 1, enable); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op2Table, &FMOperatorTable::operatorValueChanged, this, [&](Ui::FMOperatorParameter opParam, int value) { if (!isIgnoreEvent_) { FMEnvelopeParameter param; switch (opParam) { case Ui::FMOperatorParameter::AR: param = FMEnvelopeParameter::AR2; break; case Ui::FMOperatorParameter::DR: param = FMEnvelopeParameter::DR2; break; case Ui::FMOperatorParameter::SR: param = FMEnvelopeParameter::SR2; break; case Ui::FMOperatorParameter::RR: param = FMEnvelopeParameter::RR2; break; case Ui::FMOperatorParameter::SL: param = FMEnvelopeParameter::SL2; break; case Ui::FMOperatorParameter::TL: param = FMEnvelopeParameter::TL2; break; case Ui::FMOperatorParameter::KS: param = FMEnvelopeParameter::KS2; break; case Ui::FMOperatorParameter::ML: param = FMEnvelopeParameter::ML2; break; case Ui::FMOperatorParameter::DT: param = FMEnvelopeParameter::DT2; break; case Ui::FMOperatorParameter::SSGEG: param = FMEnvelopeParameter::SSGEG2; break; default: throw std::invalid_argument("Unexpected FMOperatorParameter."); } bt_.lock()->setEnvelopeFMParameter(ui->envNumSpinBox->value(), param, value); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op2Table, &FMOperatorTable::copyEnvelopePressed, this, &InstrumentEditorFMForm::copyEnvelope); QObject::connect(ui->op2Table, &FMOperatorTable::pasteEnvelopePressed, this, &InstrumentEditorFMForm::pasteEnvelope); QObject::connect(ui->op2Table, &FMOperatorTable::copyOperatorPressed, this, &InstrumentEditorFMForm::copyOperator); QObject::connect(ui->op2Table, &FMOperatorTable::pasteOperatorPressed, this, &InstrumentEditorFMForm::pasteOperator); QObject::connect(ui->op2Table, &FMOperatorTable::pasteEnvelopeFromPressed, this, &InstrumentEditorFMForm::pasteEnvelopeFrom); ui->op3Table->setOperatorNumber(2); QObject::connect(ui->op3Table, &FMOperatorTable::operatorEnableChanged, this, [&](bool enable) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeFMOperatorEnable(ui->envNumSpinBox->value(), 2, enable); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op3Table, &FMOperatorTable::operatorValueChanged, this, [&](Ui::FMOperatorParameter opParam, int value) { if (!isIgnoreEvent_) { FMEnvelopeParameter param; switch (opParam) { case Ui::FMOperatorParameter::AR: param = FMEnvelopeParameter::AR3; break; case Ui::FMOperatorParameter::DR: param = FMEnvelopeParameter::DR3; break; case Ui::FMOperatorParameter::SR: param = FMEnvelopeParameter::SR3; break; case Ui::FMOperatorParameter::RR: param = FMEnvelopeParameter::RR3; break; case Ui::FMOperatorParameter::SL: param = FMEnvelopeParameter::SL3; break; case Ui::FMOperatorParameter::TL: param = FMEnvelopeParameter::TL3; break; case Ui::FMOperatorParameter::KS: param = FMEnvelopeParameter::KS3; break; case Ui::FMOperatorParameter::ML: param = FMEnvelopeParameter::ML3; break; case Ui::FMOperatorParameter::DT: param = FMEnvelopeParameter::DT3; break; case Ui::FMOperatorParameter::SSGEG: param = FMEnvelopeParameter::SSGEG3; break; default: throw std::invalid_argument("Unexpected FMOperatorParameter."); } bt_.lock()->setEnvelopeFMParameter(ui->envNumSpinBox->value(), param, value); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op3Table, &FMOperatorTable::copyEnvelopePressed, this, &InstrumentEditorFMForm::copyEnvelope); QObject::connect(ui->op3Table, &FMOperatorTable::pasteEnvelopePressed, this, &InstrumentEditorFMForm::pasteEnvelope); QObject::connect(ui->op3Table, &FMOperatorTable::copyOperatorPressed, this, &InstrumentEditorFMForm::copyOperator); QObject::connect(ui->op3Table, &FMOperatorTable::pasteOperatorPressed, this, &InstrumentEditorFMForm::pasteOperator); QObject::connect(ui->op3Table, &FMOperatorTable::pasteEnvelopeFromPressed, this, &InstrumentEditorFMForm::pasteEnvelopeFrom); ui->op4Table->setOperatorNumber(3); QObject::connect(ui->op4Table, &FMOperatorTable::operatorEnableChanged, this, [&](bool enable) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeFMOperatorEnable(ui->envNumSpinBox->value(), 3, enable); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op4Table, &FMOperatorTable::operatorValueChanged, this, [&](Ui::FMOperatorParameter opParam, int value) { if (!isIgnoreEvent_) { FMEnvelopeParameter param; switch (opParam) { case Ui::FMOperatorParameter::AR: param = FMEnvelopeParameter::AR4; break; case Ui::FMOperatorParameter::DR: param = FMEnvelopeParameter::DR4; break; case Ui::FMOperatorParameter::SR: param = FMEnvelopeParameter::SR4; break; case Ui::FMOperatorParameter::RR: param = FMEnvelopeParameter::RR4; break; case Ui::FMOperatorParameter::SL: param = FMEnvelopeParameter::SL4; break; case Ui::FMOperatorParameter::TL: param = FMEnvelopeParameter::TL4; break; case Ui::FMOperatorParameter::KS: param = FMEnvelopeParameter::KS4; break; case Ui::FMOperatorParameter::ML: param = FMEnvelopeParameter::ML4; break; case Ui::FMOperatorParameter::DT: param = FMEnvelopeParameter::DT4; break; case Ui::FMOperatorParameter::SSGEG: param = FMEnvelopeParameter::SSGEG4; break; default: throw std::invalid_argument("Unexpected FMOperatorParameter."); } bt_.lock()->setEnvelopeFMParameter(ui->envNumSpinBox->value(), param, value); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->op4Table, &FMOperatorTable::copyEnvelopePressed, this, &InstrumentEditorFMForm::copyEnvelope); QObject::connect(ui->op4Table, &FMOperatorTable::pasteEnvelopePressed, this, &InstrumentEditorFMForm::pasteEnvelope); QObject::connect(ui->op4Table, &FMOperatorTable::copyOperatorPressed, this, &InstrumentEditorFMForm::copyOperator); QObject::connect(ui->op4Table, &FMOperatorTable::pasteOperatorPressed, this, &InstrumentEditorFMForm::pasteOperator); QObject::connect(ui->op4Table, &FMOperatorTable::pasteEnvelopeFromPressed, this, &InstrumentEditorFMForm::pasteEnvelopeFrom); /******************** LFO editor ********************/ ui->lfoGroupBox->setContextMenuPolicy(Qt::CustomContextMenu); ui->lfoFreqSlider->setText(tr("Freq")); ui->lfoFreqSlider->setMaximum(7); QObject::connect(ui->lfoFreqSlider, &LabeledVerticalSlider::valueChanged, this, [&](int v) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::FREQ, v); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); ui->pmsSlider->setText("PMS"); ui->pmsSlider->setMaximum(7); QObject::connect(ui->pmsSlider, &LabeledVerticalSlider::valueChanged, this, [&](int v) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::PMS, v); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); ui->amsSlider->setText("AMS"); ui->amsSlider->setMaximum(3); QObject::connect(ui->amsSlider, &LabeledVerticalSlider::valueChanged, this, [&](int v) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::AMS, v); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->amOp1CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::AM1, (state == Qt::Checked) ? 1 : 0); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->amOp2CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::AM2, (state == Qt::Checked) ? 1 : 0); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->amOp3CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::AM3, (state == Qt::Checked) ? 1 : 0); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->amOp4CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::AM4, (state == Qt::Checked) ? 1 : 0); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->lfoStartSpinBox, static_cast(&QSpinBox::valueChanged), this, [&](int v) { if (!isIgnoreEvent_) { bt_.lock()->setLFOFMParameter(ui->lfoNumSpinBox->value(), FMLFOParameter::Count, v); emit lfoParameterChanged(ui->lfoNumSpinBox->value(), instNum_); emit modified(); } }); //========== OperatorSequence ==========// ui->opSeqTypeComboBox->addItem("AL", static_cast(FMEnvelopeParameter::AL)); ui->opSeqTypeComboBox->addItem("FB", static_cast(FMEnvelopeParameter::FB)); ui->opSeqTypeComboBox->addItem("AR1", static_cast(FMEnvelopeParameter::AR1)); ui->opSeqTypeComboBox->addItem("DR1", static_cast(FMEnvelopeParameter::DR1)); ui->opSeqTypeComboBox->addItem("SR1", static_cast(FMEnvelopeParameter::SR1)); ui->opSeqTypeComboBox->addItem("RR1", static_cast(FMEnvelopeParameter::RR1)); ui->opSeqTypeComboBox->addItem("SL1", static_cast(FMEnvelopeParameter::SL1)); ui->opSeqTypeComboBox->addItem("TL1", static_cast(FMEnvelopeParameter::TL1)); ui->opSeqTypeComboBox->addItem("KS1", static_cast(FMEnvelopeParameter::KS1)); ui->opSeqTypeComboBox->addItem("ML1", static_cast(FMEnvelopeParameter::ML1)); ui->opSeqTypeComboBox->addItem("DT1", static_cast(FMEnvelopeParameter::DT1)); ui->opSeqTypeComboBox->addItem("AR2", static_cast(FMEnvelopeParameter::AR2)); ui->opSeqTypeComboBox->addItem("DR2", static_cast(FMEnvelopeParameter::DR2)); ui->opSeqTypeComboBox->addItem("SR2", static_cast(FMEnvelopeParameter::SR2)); ui->opSeqTypeComboBox->addItem("RR2", static_cast(FMEnvelopeParameter::RR2)); ui->opSeqTypeComboBox->addItem("SL2", static_cast(FMEnvelopeParameter::SL2)); ui->opSeqTypeComboBox->addItem("TL2", static_cast(FMEnvelopeParameter::TL2)); ui->opSeqTypeComboBox->addItem("KS2", static_cast(FMEnvelopeParameter::KS2)); ui->opSeqTypeComboBox->addItem("ML2", static_cast(FMEnvelopeParameter::ML2)); ui->opSeqTypeComboBox->addItem("DT2", static_cast(FMEnvelopeParameter::DT2)); ui->opSeqTypeComboBox->addItem("AR3", static_cast(FMEnvelopeParameter::AR3)); ui->opSeqTypeComboBox->addItem("DR3", static_cast(FMEnvelopeParameter::DR3)); ui->opSeqTypeComboBox->addItem("SR3", static_cast(FMEnvelopeParameter::SR3)); ui->opSeqTypeComboBox->addItem("RR3", static_cast(FMEnvelopeParameter::RR3)); ui->opSeqTypeComboBox->addItem("SL3", static_cast(FMEnvelopeParameter::SL3)); ui->opSeqTypeComboBox->addItem("TL3", static_cast(FMEnvelopeParameter::TL3)); ui->opSeqTypeComboBox->addItem("KS3", static_cast(FMEnvelopeParameter::KS3)); ui->opSeqTypeComboBox->addItem("ML3", static_cast(FMEnvelopeParameter::ML3)); ui->opSeqTypeComboBox->addItem("DT3", static_cast(FMEnvelopeParameter::DT3)); ui->opSeqTypeComboBox->addItem("AR4", static_cast(FMEnvelopeParameter::AR4)); ui->opSeqTypeComboBox->addItem("DR4", static_cast(FMEnvelopeParameter::DR4)); ui->opSeqTypeComboBox->addItem("SR4", static_cast(FMEnvelopeParameter::SR4)); ui->opSeqTypeComboBox->addItem("RR4", static_cast(FMEnvelopeParameter::RR4)); ui->opSeqTypeComboBox->addItem("SL4", static_cast(FMEnvelopeParameter::SL4)); ui->opSeqTypeComboBox->addItem("TL4", static_cast(FMEnvelopeParameter::TL4)); ui->opSeqTypeComboBox->addItem("KS4", static_cast(FMEnvelopeParameter::KS4)); ui->opSeqTypeComboBox->addItem("ML4", static_cast(FMEnvelopeParameter::ML4)); ui->opSeqTypeComboBox->addItem("DT4", static_cast(FMEnvelopeParameter::DT4)); ui->opSeqEditor->setDefaultRow(0); ui->opSeqEditor->setLabelDiaplayMode(true); setOperatorSequenceEditor(); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->addOperatorSequenceFMSequenceData(param, ui->opSeqNumSpinBox->value(), row); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->removeOperatorSequenceFMSequenceData(param, ui->opSeqNumSpinBox->value()); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->setOperatorSequenceFMSequenceData(param, ui->opSeqNumSpinBox->value(), col, row); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->addOperatorSequenceFMLoop(param, ui->opSeqNumSpinBox->value(), loop); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->removeOperatorSequenceFMLoop( param, ui->opSeqNumSpinBox->value(), begin, end); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->clearOperatorSequenceFMLoops(param, ui->opSeqNumSpinBox->value()); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->changeOperatorSequenceFMLoop( param, ui->opSeqNumSpinBox->value(), prevBegin, prevEnd, loop); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->opSeqEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { FMEnvelopeParameter param = getOperatorSequenceParameter(); bt_.lock()->setOperatorSequenceFMRelease(param, ui->opSeqNumSpinBox->value(), release); emit operatorSequenceParameterChanged(param, ui->opSeqNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->opSeqTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorFMForm::onOperatorSequenceTypeChanged); //========== Arpeggio ==========// ui->arpOpComboBox->addItem(tr("All"), static_cast(FMOperatorType::All)); ui->arpOpComboBox->addItem("Op1", static_cast(FMOperatorType::Op1)); ui->arpOpComboBox->addItem("Op2", static_cast(FMOperatorType::Op2)); ui->arpOpComboBox->addItem("Op3", static_cast(FMOperatorType::Op3)); ui->arpOpComboBox->addItem("Op4", static_cast(FMOperatorType::Op4)); ui->arpTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); ui->arpTypeComboBox->addItem(tr("Fixed"), static_cast(SequenceType::FixedSequence)); ui->arpTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addArpeggioFMSequenceData(ui->arpNumSpinBox->value(), row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removeArpeggioFMSequenceData(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setArpeggioFMSequenceData(ui->arpNumSpinBox->value(), col, row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addArpeggioFMLoop(ui->arpNumSpinBox->value(), loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removeArpeggioFMLoop(ui->arpNumSpinBox->value(), begin, end); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearArpeggioFMLoops(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changeArpeggioFMLoop(ui->arpNumSpinBox->value(), prevBegin, prevEnd, loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setArpeggioFMRelease(ui->arpNumSpinBox->value(), release); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->arpTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorFMForm::onArpeggioTypeChanged); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->arpOpComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorFMForm::onArpeggioOperatorChanged); //========== Pitch ==========// ui->ptOpComboBox->addItem(tr("All"), static_cast(FMOperatorType::All)); ui->ptOpComboBox->addItem("Op1", static_cast(FMOperatorType::Op1)); ui->ptOpComboBox->addItem("Op2", static_cast(FMOperatorType::Op2)); ui->ptOpComboBox->addItem("Op3", static_cast(FMOperatorType::Op3)); ui->ptOpComboBox->addItem("Op4", static_cast(FMOperatorType::Op4)); ui->ptEditor->setMaximumDisplayedRowCount(15); ui->ptEditor->setDefaultRow(127); ui->ptEditor->setLabelDiaplayMode(true); for (int i = 0; i < 255; ++i) { ui->ptEditor->AddRow(QString::asprintf("%+d", i - 127), false); } ui->ptEditor->autoFitLabelWidth(); ui->ptEditor->setUpperRow(134); ui->ptEditor->setMMLDisplay0As(-127); ui->ptTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); ui->ptTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addPitchFMSequenceData(ui->ptNumSpinBox->value(), row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&]() { if (!isIgnoreEvent_) { bt_.lock()->removePitchFMSequenceData(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setPitchFMSequenceData(ui->ptNumSpinBox->value(), col, row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addPitchFMLoop(ui->ptNumSpinBox->value(), loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removePitchFMLoop(ui->ptNumSpinBox->value(), begin, end); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearPitchFMLoops(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changePitchFMLoop(ui->ptNumSpinBox->value(), prevBegin, prevEnd, loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setPitchFMRelease(ui->ptNumSpinBox->value(), release); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->ptTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorFMForm::onPitchTypeChanged); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->ptOpComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorFMForm::onPitchOperatorChanged); //========== Others ==========// QObject::connect(ui->envResetCheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMEnvelopeResetEnabled(instNum_, FMOperatorType::All, (state == Qt::Checked)); emit modified(); } }); QObject::connect(ui->envResetOp1CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMEnvelopeResetEnabled(instNum_, FMOperatorType::Op1, (state == Qt::Checked)); emit modified(); } }); QObject::connect(ui->envResetOp2CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMEnvelopeResetEnabled(instNum_, FMOperatorType::Op2, (state == Qt::Checked)); emit modified(); } }); QObject::connect(ui->envResetOp3CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMEnvelopeResetEnabled(instNum_, FMOperatorType::Op3, (state == Qt::Checked)); emit modified(); } }); QObject::connect(ui->envResetOp4CheckBox, &QCheckBox::stateChanged, this, [&](int state) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMEnvelopeResetEnabled(instNum_, FMOperatorType::Op4, (state == Qt::Checked)); emit modified(); } }); } InstrumentEditorFMForm::~InstrumentEditorFMForm() { delete ui; } void InstrumentEditorFMForm::setInstrumentNumber(int num) { instNum_ = num; } int InstrumentEditorFMForm::getInstrumentNumber() const { return instNum_; } void InstrumentEditorFMForm::setCore(std::weak_ptr core) { bt_ = core; updateInstrumentParameters(); } void InstrumentEditorFMForm::setConfiguration(std::weak_ptr config) { config_ = config; std::vector names; for (const auto& texts : config.lock()->getFMEnvelopeTexts()) { names.push_back(gui_utils::utf8ToQString(texts.name)); } ui->op1Table->setEnvelopeSetNames(names); ui->op2Table->setEnvelopeSetNames(names); ui->op3Table->setEnvelopeSetNames(names); ui->op4Table->setEnvelopeSetNames(names); updateConfigurationForDisplay(); } void InstrumentEditorFMForm::updateConfigurationForDisplay() { ui->op1Table->setDTDisplayType(config_.lock()->getShowFMDetuneAsSigned()); ui->op2Table->setDTDisplayType(config_.lock()->getShowFMDetuneAsSigned()); ui->op3Table->setDTDisplayType(config_.lock()->getShowFMDetuneAsSigned()); ui->op4Table->setDTDisplayType(config_.lock()->getShowFMDetuneAsSigned()); } void InstrumentEditorFMForm::setColorPalette(std::shared_ptr palette) { palette_ = palette; ui->op1Table->setColorPalette(palette); ui->op2Table->setColorPalette(palette); ui->op3Table->setColorPalette(palette); ui->op4Table->setColorPalette(palette); ui->opSeqEditor->setColorPalette(palette); ui->arpEditor->setColorPalette(palette); ui->ptEditor->setColorPalette(palette); } void InstrumentEditorFMForm::updateInstrumentParameters() { Ui::EventGuard eg(isIgnoreEvent_); auto name = gui_utils::utf8ToQString(bt_.lock()->getInstrument(instNum_)->getName()); setWindowTitle(QString("%1: %2").arg(instNum_, 2, 16, QChar('0')).toUpper().arg(name)); setInstrumentEnvelopeParameters(); setInstrumentLFOParameters(); setInstrumentOperatorSequenceParameters(); setInstrumentArpeggioParameters(); setInstrumentPitchParameters(); setInstrumentEnvelopeResetParameters(); } /********** Events **********/ // MUST DIRECT CONNECTION void InstrumentEditorFMForm::keyPressEvent(QKeyEvent *event) { // General keys switch (event->key()) { case Qt::Key_Escape: close(); break; default: // For jam key on if (!event->isAutoRepeat()) { // Musical keyboard Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOnEvent(jk); } catch (std::invalid_argument&) {} } break; } } // MUST DIRECT CONNECTION void InstrumentEditorFMForm::keyReleaseEvent(QKeyEvent *event) { // For jam key off if (!event->isAutoRepeat()) { Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOffEvent(jk); } catch (std::invalid_argument&) {} } } void InstrumentEditorFMForm::showEvent(QShowEvent* event) { Q_UNUSED(event) paintAlgorithmDiagram(); resizeAlgorithmDiagram(); } void InstrumentEditorFMForm::resizeEvent(QResizeEvent* event) { Q_UNUSED(event) resizeAlgorithmDiagram(); } //========== Envelope ==========// void InstrumentEditorFMForm::setInstrumentEnvelopeParameters() { Ui::EventGuard eg(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instFM = dynamic_cast(inst.get()); ui->envNumSpinBox->setValue(instFM->getEnvelopeNumber()); onEnvelopeNumberChanged(); ui->alSlider->setValue(instFM->getEnvelopeParameter(FMEnvelopeParameter::AL)); ui->fbSlider->setValue(instFM->getEnvelopeParameter(FMEnvelopeParameter::FB)); ui->op1Table->setValue(Ui::FMOperatorParameter::AR, instFM->getEnvelopeParameter(FMEnvelopeParameter::AR1)); ui->op1Table->setValue(Ui::FMOperatorParameter::DR, instFM->getEnvelopeParameter(FMEnvelopeParameter::DR1)); ui->op1Table->setValue(Ui::FMOperatorParameter::SR, instFM->getEnvelopeParameter(FMEnvelopeParameter::SR1)); ui->op1Table->setValue(Ui::FMOperatorParameter::RR, instFM->getEnvelopeParameter(FMEnvelopeParameter::RR1)); ui->op1Table->setValue(Ui::FMOperatorParameter::SL, instFM->getEnvelopeParameter(FMEnvelopeParameter::SL1)); ui->op1Table->setValue(Ui::FMOperatorParameter::TL, instFM->getEnvelopeParameter(FMEnvelopeParameter::TL1)); ui->op1Table->setValue(Ui::FMOperatorParameter::KS, instFM->getEnvelopeParameter(FMEnvelopeParameter::KS1)); ui->op1Table->setValue(Ui::FMOperatorParameter::ML, instFM->getEnvelopeParameter(FMEnvelopeParameter::ML1)); ui->op1Table->setValue(Ui::FMOperatorParameter::DT, instFM->getEnvelopeParameter(FMEnvelopeParameter::DT1)); ui->op1Table->setValue(Ui::FMOperatorParameter::SSGEG, instFM->getEnvelopeParameter(FMEnvelopeParameter::SSGEG1)); ui->op1Table->setGroupEnabled(instFM->getOperatorEnabled(0)); ui->op2Table->setValue(Ui::FMOperatorParameter::AR, instFM->getEnvelopeParameter(FMEnvelopeParameter::AR2)); ui->op2Table->setValue(Ui::FMOperatorParameter::DR, instFM->getEnvelopeParameter(FMEnvelopeParameter::DR2)); ui->op2Table->setValue(Ui::FMOperatorParameter::SR, instFM->getEnvelopeParameter(FMEnvelopeParameter::SR2)); ui->op2Table->setValue(Ui::FMOperatorParameter::RR, instFM->getEnvelopeParameter(FMEnvelopeParameter::RR2)); ui->op2Table->setValue(Ui::FMOperatorParameter::SL, instFM->getEnvelopeParameter(FMEnvelopeParameter::SL2)); ui->op2Table->setValue(Ui::FMOperatorParameter::TL, instFM->getEnvelopeParameter(FMEnvelopeParameter::TL2)); ui->op2Table->setValue(Ui::FMOperatorParameter::KS, instFM->getEnvelopeParameter(FMEnvelopeParameter::KS2)); ui->op2Table->setValue(Ui::FMOperatorParameter::ML, instFM->getEnvelopeParameter(FMEnvelopeParameter::ML2)); ui->op2Table->setValue(Ui::FMOperatorParameter::DT, instFM->getEnvelopeParameter(FMEnvelopeParameter::DT2)); ui->op2Table->setValue(Ui::FMOperatorParameter::SSGEG, instFM->getEnvelopeParameter(FMEnvelopeParameter::SSGEG2)); ui->op2Table->setGroupEnabled(instFM->getOperatorEnabled(1)); ui->op3Table->setValue(Ui::FMOperatorParameter::AR, instFM->getEnvelopeParameter(FMEnvelopeParameter::AR3)); ui->op3Table->setValue(Ui::FMOperatorParameter::DR, instFM->getEnvelopeParameter(FMEnvelopeParameter::DR3)); ui->op3Table->setValue(Ui::FMOperatorParameter::SR, instFM->getEnvelopeParameter(FMEnvelopeParameter::SR3)); ui->op3Table->setValue(Ui::FMOperatorParameter::RR, instFM->getEnvelopeParameter(FMEnvelopeParameter::RR3)); ui->op3Table->setValue(Ui::FMOperatorParameter::SL, instFM->getEnvelopeParameter(FMEnvelopeParameter::SL3)); ui->op3Table->setValue(Ui::FMOperatorParameter::TL, instFM->getEnvelopeParameter(FMEnvelopeParameter::TL3)); ui->op3Table->setValue(Ui::FMOperatorParameter::KS, instFM->getEnvelopeParameter(FMEnvelopeParameter::KS3)); ui->op3Table->setValue(Ui::FMOperatorParameter::ML, instFM->getEnvelopeParameter(FMEnvelopeParameter::ML3)); ui->op3Table->setValue(Ui::FMOperatorParameter::DT, instFM->getEnvelopeParameter(FMEnvelopeParameter::DT3)); ui->op3Table->setValue(Ui::FMOperatorParameter::SSGEG, instFM->getEnvelopeParameter(FMEnvelopeParameter::SSGEG3)); ui->op3Table->setGroupEnabled(instFM->getOperatorEnabled(2)); ui->op4Table->setValue(Ui::FMOperatorParameter::AR, instFM->getEnvelopeParameter(FMEnvelopeParameter::AR4)); ui->op4Table->setValue(Ui::FMOperatorParameter::DR, instFM->getEnvelopeParameter(FMEnvelopeParameter::DR4)); ui->op4Table->setValue(Ui::FMOperatorParameter::SR, instFM->getEnvelopeParameter(FMEnvelopeParameter::SR4)); ui->op4Table->setValue(Ui::FMOperatorParameter::RR, instFM->getEnvelopeParameter(FMEnvelopeParameter::RR4)); ui->op4Table->setValue(Ui::FMOperatorParameter::SL, instFM->getEnvelopeParameter(FMEnvelopeParameter::SL4)); ui->op4Table->setValue(Ui::FMOperatorParameter::TL, instFM->getEnvelopeParameter(FMEnvelopeParameter::TL4)); ui->op4Table->setValue(Ui::FMOperatorParameter::KS, instFM->getEnvelopeParameter(FMEnvelopeParameter::KS4)); ui->op4Table->setValue(Ui::FMOperatorParameter::ML, instFM->getEnvelopeParameter(FMEnvelopeParameter::ML4)); ui->op4Table->setValue(Ui::FMOperatorParameter::DT, instFM->getEnvelopeParameter(FMEnvelopeParameter::DT4)); ui->op4Table->setValue(Ui::FMOperatorParameter::SSGEG, instFM->getEnvelopeParameter(FMEnvelopeParameter::SSGEG4)); ui->op4Table->setGroupEnabled(instFM->getOperatorEnabled(3)); } void InstrumentEditorFMForm::setInstrumentEnvelopeParameters(QString data) { QRegularExpression re("^(?\\d+),(?\\d+),\\s*" "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?\\d+)," "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?-?\\d+),\\s*" "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?\\d+)," "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?-?\\d+),\\s*" "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?\\d+)," "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?-?\\d+),\\s*" "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?\\d+)," "(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?-?\\d+),?\\s*"); QRegularExpressionMatch match = re.match(data); if (match.hasMatch()) { ui->fbSlider->setValue(match.captured("fb").toInt()); ui->alSlider->setValue(match.captured("al").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt1").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg1").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt2").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg2").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt3").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg3").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt4").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg4").toInt()); } } void InstrumentEditorFMForm::setInstrumentEnvelopeParameters(int envTypeNum, QString data) { QStringList digits; QRegularExpression re(R"((-?\d+))"); auto it = re.globalMatch(data); while (it.hasNext()) { auto match = it.next(); digits.append(match.captured(1)); } std::vector set = config_.lock()->getFMEnvelopeTexts().at(static_cast(envTypeNum)).texts; if (static_cast(set.size()) != digits.size()) { auto name = config_.lock()->getFMEnvelopeTexts().at(static_cast(envTypeNum)).name; QMessageBox::critical(this, tr("Error"), tr("Did not match the clipboard text format with %1.") .arg(gui_utils::utf8ToQString(name))); return; } auto rangeCheck = [](int v, int min, int max) -> int { if (v < min || max < v) throw std::out_of_range(""); else return v; }; auto rangeCheckDT = [rangeCheck](int v) -> int { switch (v) { case -3: v = 7; break; case -2: v = 6; break; case -1: v = 5; break; default: break; } return rangeCheck(v, 0, 7); }; try { for (int i = 0; i < digits.size(); ++i) { int d = digits[i].toInt(); switch (set[static_cast(i)]) { case FMEnvelopeTextType::Skip: break; case FMEnvelopeTextType::AL: ui->alSlider->setValue(rangeCheck(d, 0, 7)); break; case FMEnvelopeTextType::FB: ui->fbSlider->setValue(rangeCheck(d, 0, 7)); break; case FMEnvelopeTextType::AR1: ui->op1Table->setValue(Ui::FMOperatorParameter::AR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::DR1: ui->op1Table->setValue(Ui::FMOperatorParameter::DR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::SR1: ui->op1Table->setValue(Ui::FMOperatorParameter::SR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::RR1: ui->op1Table->setValue(Ui::FMOperatorParameter::RR, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::SL1: ui->op1Table->setValue(Ui::FMOperatorParameter::SL, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::TL1: ui->op1Table->setValue(Ui::FMOperatorParameter::TL, rangeCheck(d, 0, 127)); break; case FMEnvelopeTextType::KS1: ui->op1Table->setValue(Ui::FMOperatorParameter::KS, rangeCheck(d, 0, 3)); break; case FMEnvelopeTextType::ML1: ui->op1Table->setValue(Ui::FMOperatorParameter::ML, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::DT1: ui->op1Table->setValue(Ui::FMOperatorParameter::DT, rangeCheckDT(d)); break; case FMEnvelopeTextType::AR2: ui->op2Table->setValue(Ui::FMOperatorParameter::AR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::DR2: ui->op2Table->setValue(Ui::FMOperatorParameter::DR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::SR2: ui->op2Table->setValue(Ui::FMOperatorParameter::SR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::RR2: ui->op2Table->setValue(Ui::FMOperatorParameter::RR, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::SL2: ui->op2Table->setValue(Ui::FMOperatorParameter::SL, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::TL2: ui->op2Table->setValue(Ui::FMOperatorParameter::TL, rangeCheck(d, 0, 127)); break; case FMEnvelopeTextType::KS2: ui->op2Table->setValue(Ui::FMOperatorParameter::KS, rangeCheck(d, 0, 3)); break; case FMEnvelopeTextType::ML2: ui->op2Table->setValue(Ui::FMOperatorParameter::ML, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::DT2: ui->op2Table->setValue(Ui::FMOperatorParameter::DT, rangeCheckDT(d)); break; case FMEnvelopeTextType::AR3: ui->op4Table->setValue(Ui::FMOperatorParameter::AR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::DR3: ui->op3Table->setValue(Ui::FMOperatorParameter::DR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::SR3: ui->op3Table->setValue(Ui::FMOperatorParameter::SR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::RR3: ui->op3Table->setValue(Ui::FMOperatorParameter::RR, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::SL3: ui->op3Table->setValue(Ui::FMOperatorParameter::SL, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::TL3: ui->op3Table->setValue(Ui::FMOperatorParameter::TL, rangeCheck(d, 0, 127)); break; case FMEnvelopeTextType::KS3: ui->op3Table->setValue(Ui::FMOperatorParameter::KS, rangeCheck(d, 0, 4)); break; case FMEnvelopeTextType::ML3: ui->op3Table->setValue(Ui::FMOperatorParameter::ML, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::DT3: ui->op3Table->setValue(Ui::FMOperatorParameter::DT, rangeCheckDT(d)); break; case FMEnvelopeTextType::AR4: ui->op4Table->setValue(Ui::FMOperatorParameter::AR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::DR4: ui->op4Table->setValue(Ui::FMOperatorParameter::DR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::SR4: ui->op4Table->setValue(Ui::FMOperatorParameter::SR, rangeCheck(d, 0, 31)); break; case FMEnvelopeTextType::RR4: ui->op4Table->setValue(Ui::FMOperatorParameter::RR, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::SL4: ui->op4Table->setValue(Ui::FMOperatorParameter::SL, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::TL4: ui->op4Table->setValue(Ui::FMOperatorParameter::TL, rangeCheck(d, 0, 127)); break; case FMEnvelopeTextType::KS4: ui->op4Table->setValue(Ui::FMOperatorParameter::KS, rangeCheck(d, 0, 4)); break; case FMEnvelopeTextType::ML4: ui->op4Table->setValue(Ui::FMOperatorParameter::ML, rangeCheck(d, 0, 15)); break; case FMEnvelopeTextType::DT4: ui->op4Table->setValue(Ui::FMOperatorParameter::DT, rangeCheckDT(d)); break; } } } catch (...) { auto name = config_.lock()->getFMEnvelopeTexts().at(static_cast(envTypeNum)).name; QMessageBox::critical(this, tr("Error"), tr("Did not match the clipboard text format with %1.") .arg(gui_utils::utf8ToQString(name))); return; } } void InstrumentEditorFMForm::setInstrumentOperatorParameters(int opNum, QString data) { QRegularExpression re("^(?\\d+),(?\\d+),(?\\d+),(?\\d+),(?\\d+)," "(?\\d+),(?\\d+),(?\\d+),(?
\\d+),(?-?\\d+)"); QRegularExpressionMatch match = re.match(data); if (match.hasMatch()) { switch (opNum) { case 0: ui->op1Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt").toInt()); ui->op1Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg").toInt()); break; case 1: ui->op2Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt").toInt()); ui->op2Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg").toInt()); break; case 2: ui->op3Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt").toInt()); ui->op3Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg").toInt()); break; case 3: ui->op4Table->setValue(Ui::FMOperatorParameter::AR, match.captured("ar").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::DR, match.captured("dr").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::SR, match.captured("sr").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::RR, match.captured("rr").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::SL, match.captured("sl").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::TL, match.captured("tl").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::KS, match.captured("ks").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::ML, match.captured("ml").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::DT, match.captured("dt").toInt()); ui->op4Table->setValue(Ui::FMOperatorParameter::SSGEG, match.captured("ssgeg").toInt()); break; } } } void InstrumentEditorFMForm::paintAlgorithmDiagram() { if (!palette_) return; ui->alGraphicsView->setBackgroundBrush(QBrush(palette_->instFMAlBackColor)); QGraphicsScene* scene = ui->alGraphicsView->scene(); // 200 * 70 scene->clear(); QPen pen(palette_->instFMAlForeColor); QBrush brush(palette_->instFMAlForeColor); auto one = new QGraphicsSimpleTextItem(); one->setBrush(brush); one->setText("1"); auto two = new QGraphicsSimpleTextItem(); two->setBrush(brush); two->setText("2"); auto three = new QGraphicsSimpleTextItem(); three->setBrush(brush); three->setText("3"); auto four = new QGraphicsSimpleTextItem(); four->setBrush(brush); four->setText("4"); switch (ui->alSlider->value()) { case 0: { scene->addRect(8, -4, 16, 8, pen, brush); scene->addRect(40, -4, 16, 8, pen, brush); scene->addRect(72, -4, 16, 8, pen, brush); scene->addRect(104, -4, 16, 8, pen, brush); scene->addLine(0, 0, 128, 0, pen); scene->addLine(0, -8, 32, -8, pen); scene->addLine(0, 0, 0, -8, pen); scene->addLine(32, 0, 32, -8, pen); one->setPos(14, 8); two->setPos(46, 8); three->setPos(78, 8); four->setPos(110, 8); break; } case 1: { scene->addRect(8, -12, 16, 8, pen, brush); scene->addRect(8, 4, 16, 8, pen, brush); scene->addRect(40, -4, 16, 8, pen, brush); scene->addRect(72, -4, 16, 8, pen, brush); scene->addLine(0, -8, 35, -8, pen); scene->addLine(24, 8, 35, 8, pen); scene->addLine(0, -16, 29, -16, pen); scene->addLine(0, -8, 0, -16, pen); scene->addLine(29, -8, 29, -16, pen); scene->addLine(35, -8, 35, 8, pen); scene->addLine(35, 0, 96, 0, pen); one->setPos(-8, -12); two->setPos(-8, 4); three->setPos(46, 8); four->setPos(78, 8); break; } case 2: { scene->addRect(40, -12, 16, 8, pen, brush); scene->addRect(8, 4, 16, 8, pen, brush); scene->addRect(40, 4, 16, 8, pen, brush); scene->addRect(72, -4, 16, 8, pen, brush); scene->addLine(32, -8, 67, -8, pen); scene->addLine(32, -8, 32, -16, pen); scene->addLine(61, -8, 61, -16, pen); scene->addLine(32, -16, 61, -16, pen); scene->addLine(24, 8, 67, 8, pen); scene->addLine(67, -8, 67, 8, pen); scene->addLine(67, 0, 96, 0, pen); one->setPos(24, -12); two->setPos(14, 16); three->setPos(46, 16); four->setPos(78, 8); break; } case 3: { scene->addRect(8, -12, 16, 8, pen, brush); scene->addRect(40, -12, 16, 8, pen, brush); scene->addRect(40, 4, 16, 8, pen, brush); scene->addRect(72, -4, 16, 8, pen, brush); scene->addLine(0, -8, 64, -8, pen); scene->addLine(0, -8, 0, -16, pen); scene->addLine(32, -8, 32, -16, pen); scene->addLine(0, -16, 32, -16, pen); scene->addLine(56, 8, 64, 8, pen); scene->addLine(64, -8, 64, 8, pen); scene->addLine(64, 0, 96, 0, pen); one->setPos(-8, -12); two->setPos(46, -24); three->setPos(46, 16); four->setPos(78, 8); break; } case 4: { scene->addRect(8, -12, 16, 8, pen, brush); scene->addRect(40, -12, 16, 8, pen, brush); scene->addRect(8, 4, 16, 8, pen, brush); scene->addRect(40, 4, 16, 8, pen, brush); scene->addLine(0, -8, 64, -8, pen); scene->addLine(0, -8, 0, -16, pen); scene->addLine(32, -8, 32, -16, pen); scene->addLine(0, -16, 32, -16, pen); scene->addLine(24, 8, 64, 8, pen); scene->addLine(64, -8, 64, 8, pen); scene->addLine(64, 0, 72, 0, pen); one->setPos(-8, -12); two->setPos(46, -24); three->setPos(14, 16); four->setPos(46, 16); break; } case 5: { scene->addRect(8, -4, 16, 8, pen, brush); scene->addRect(40, -20, 16, 8, pen, brush); scene->addRect(40, -4, 16, 8, pen, brush); scene->addRect(40, 12, 16, 8, pen, brush); scene->addLine(0, 0, 72, 0, pen); scene->addLine(0, 0, 0, -8, pen); scene->addLine(29, 0, 29, -8, pen); scene->addLine(0, -8, 29, -8, pen); scene->addLine(35, -16, 35, 16, pen); scene->addLine(64, -16, 64, 16, pen); scene->addLine(35, -16, 64, -16, pen); scene->addLine(35, 16, 64, 16, pen); one->setPos(14, 8); two->setPos(76, -20); three->setPos(76, -4); four->setPos(76, 12); break; } case 6: { scene->addRect(8, -20, 16, 8, pen, brush); scene->addRect(40, -20, 16, 8, pen, brush); scene->addRect(40, -4, 16, 8, pen, brush); scene->addRect(40, 12, 16, 8, pen, brush); scene->addLine(0, -16, 64, -16, pen); scene->addLine(0, -16, 0, -24, pen); scene->addLine(32, -16, 32, -24, pen); scene->addLine(0, -24, 32, -24, pen); scene->addLine(56, 0, 72, 0, pen); scene->addLine(56, 16, 64, 16, pen); scene->addLine(64, -16, 64, 16, pen); one->setPos(14, -8); two->setPos(76, -20); three->setPos(76, -4); four->setPos(76, 12); break; } case 7: { scene->addRect(-28, 8, 8, 16, pen, brush); scene->addRect(-12, 8, 8, 16, pen, brush); scene->addRect(4, 8, 8, 16, pen, brush); scene->addRect(20, 8, 8, 16, pen, brush); scene->addLine(-24, 0, -24, 35, pen); scene->addLine(-24, 0, -32, 0, pen); scene->addLine(-24, 29, -32, 29, pen); scene->addLine(-32, 0, -32, 29, pen); scene->addLine(-8, 24, -8, 35, pen); scene->addLine(8, 24, 8, 35, pen); scene->addLine(24, 24, 24, 35, pen); scene->addLine(-24, 35, 24, 35, pen); scene->addLine(0, 35, 0, 40, pen); one->setPos(-26, -12); two->setPos(-10, -12); three->setPos(6, -12); four->setPos(22, -12); break; } } scene->addItem(one); scene->addItem(two); scene->addItem(three); scene->addItem(four); scene->setSceneRect(scene->itemsBoundingRect()); } void InstrumentEditorFMForm::resizeAlgorithmDiagram() { ui->alGraphicsView->fitInView(ui->alGraphicsView->scene()->itemsBoundingRect(), Qt::AspectRatioMode::KeepAspectRatio); } /********** Slots **********/ void InstrumentEditorFMForm::copyEnvelope() { QApplication::clipboard()->setText(QString("FM_ENVELOPE:%1,%2,\n%3,\n%4,\n%5,\n%6,") .arg(ui->fbSlider->value()).arg(ui->alSlider->value()) .arg(ui->op1Table->toString(), ui->op2Table->toString(), ui->op3Table->toString(), ui->op4Table->toString())); } void InstrumentEditorFMForm::pasteEnvelope() { QString data = QApplication::clipboard()->text().remove("FM_ENVELOPE:"); setInstrumentEnvelopeParameters(data); } void InstrumentEditorFMForm::pasteEnvelopeFrom(int typenum) { setInstrumentEnvelopeParameters(typenum, QApplication::clipboard()->text()); } void InstrumentEditorFMForm::copyOperator(int opNum) { QString text; switch (opNum) { case 0: text = ui->op1Table->toString(); break; case 1: text = ui->op2Table->toString(); break; case 2: text = ui->op3Table->toString(); break; case 3: text = ui->op4Table->toString(); break; } QApplication::clipboard()->setText(QString("FM_OPERATOR:") + text); } void InstrumentEditorFMForm::pasteOperator(int opNum) { QString data = QApplication::clipboard()->text().remove("FM_OPERATOR:"); setInstrumentOperatorParameters(opNum, data); } void InstrumentEditorFMForm::on_envNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMEnvelope(instNum_, arg1); setInstrumentEnvelopeParameters(); emit envelopeNumberChanged(); emit modified(); } onEnvelopeNumberChanged(); } void InstrumentEditorFMForm::on_envGroupBox_customContextMenuRequested(const QPoint &pos) { QPoint globalPos = ui->envGroupBox->mapToGlobal(pos); QMenu menu; // Leave Before Qt5.7.0 style due to windows xp QAction* copy = menu.addAction(tr("Copy envelope")); QObject::connect(copy, &QAction::triggered, this, &InstrumentEditorFMForm::copyEnvelope); QAction* paste = menu.addAction(tr("Paste envelope")); QObject::connect(paste, &QAction::triggered, this, &InstrumentEditorFMForm::pasteEnvelope); paste->setEnabled(QApplication::clipboard()->text().startsWith("FM_ENVELOPE:")); QMenu* pasteFrom = menu.addMenu(tr("Paste envelope From")); std::vector textsSet = config_.lock()->getFMEnvelopeTexts(); for (size_t i = 0; i < textsSet.size(); ++i) { QAction* act = pasteFrom->addAction(gui_utils::utf8ToQString(textsSet[i].name)); act->setData(static_cast(i)); } QObject::connect(pasteFrom, &QMenu::triggered, this, [&](QAction* action) { pasteEnvelopeFrom(action->data().toInt()); }); menu.exec(globalPos); } void InstrumentEditorFMForm::onEnvelopeParameterChanged(int envNum) { if (ui->envNumSpinBox->value() == envNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentEnvelopeParameters(); } } void InstrumentEditorFMForm::onEnvelopeNumberChanged() { // Change users view std::multiset users = bt_.lock()->getEnvelopeFMUsers(ui->envNumSpinBox->value()); ui->envUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } //========== LFO ==========// void InstrumentEditorFMForm::setInstrumentLFOParameters() { Ui::EventGuard eg(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instFM = dynamic_cast(inst.get()); ui->lfoNumSpinBox->setValue(instFM->getLFONumber()); ui->lfoFreqSlider->setValue(instFM->getLFOParameter(FMLFOParameter::FREQ)); ui->pmsSlider->setValue(instFM->getLFOParameter(FMLFOParameter::PMS)); ui->amsSlider->setValue(instFM->getLFOParameter(FMLFOParameter::AMS)); ui->amOp1CheckBox->setChecked(instFM->getLFOParameter(FMLFOParameter::AM1)); ui->amOp2CheckBox->setChecked(instFM->getLFOParameter(FMLFOParameter::AM2)); ui->amOp3CheckBox->setChecked(instFM->getLFOParameter(FMLFOParameter::AM3)); ui->amOp4CheckBox->setChecked(instFM->getLFOParameter(FMLFOParameter::AM4)); ui->lfoStartSpinBox->setValue(instFM->getLFOParameter(FMLFOParameter::Count)); if (instFM->getLFOEnabled()) { ui->lfoGroupBox->setChecked(true); onLFONumberChanged(); } else { ui->lfoGroupBox->setChecked(false); } } void InstrumentEditorFMForm::setInstrumentLFOParameters(QString data) { QRegularExpression re("^(?\\d+),(?\\d+),(?\\d+),(?\\d+)," "(?\\d+),(?\\d+),(?\\d+),(?\\d+)"); QRegularExpressionMatch match = re.match(data); if (match.hasMatch()) { ui->lfoFreqSlider->setValue(match.captured("freq").toInt()); ui->pmsSlider->setValue(match.captured("pms").toInt()); ui->amsSlider->setValue(match.captured("ams").toInt()); ui->lfoStartSpinBox->setValue(match.captured("cnt").toInt()); ui->amOp1CheckBox->setChecked(match.captured("am1").toInt() == 1); ui->amOp2CheckBox->setChecked(match.captured("am2").toInt() == 1); ui->amOp3CheckBox->setChecked(match.captured("am3").toInt() == 1); ui->amOp4CheckBox->setChecked(match.captured("am4").toInt() == 1); } } QString InstrumentEditorFMForm::toLFOString() const { auto str = QString("%1,%2,%3,%4,%5,%6,%7,%8") .arg(ui->lfoFreqSlider->value()) .arg(ui->pmsSlider->value()) .arg(ui->amsSlider->value()) .arg(ui->lfoStartSpinBox->value()) .arg(ui->amOp1CheckBox->isChecked() ? 1 : 0) .arg(ui->amOp2CheckBox->isChecked() ? 1 : 0) .arg(ui->amOp3CheckBox->isChecked() ? 1 : 0) .arg(ui->amOp4CheckBox->isChecked() ? 1 : 0); return str; } /********** Slots **********/ void InstrumentEditorFMForm::onLFOParameterChanged(int lfoNum) { if (ui->lfoNumSpinBox->value() == lfoNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentLFOParameters(); } } void InstrumentEditorFMForm::onLFONumberChanged() { // Change users view std::multiset users = bt_.lock()->getLFOFMUsers(ui->lfoNumSpinBox->value()); ui->lfoUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::on_lfoGroupBox_customContextMenuRequested(const QPoint &pos) { QClipboard* clipboard = QApplication::clipboard(); QPoint globalPos = ui->lfoGroupBox->mapToGlobal(pos); QMenu menu; // Leave Before Qt5.7.0 style due to windows xp QAction* copy = menu.addAction(tr("Copy LFO parameters")); QObject::connect(copy, &QAction::triggered, this, [&, clipboard]() { clipboard->setText("FM_LFO:" + toLFOString()); }); QAction* paste = menu.addAction(tr("Paste LFO parameters")); QObject::connect(paste, &QAction::triggered, this, [&, clipboard]() { QString data = clipboard->text().remove("FM_LFO:"); setInstrumentLFOParameters(data); }); if (!ui->lfoGroupBox->isChecked()) { copy->setEnabled(false); paste->setEnabled(false); } else if (!clipboard->text().startsWith("FM_LFO:")) { paste->setEnabled(false); } menu.exec(globalPos); } void InstrumentEditorFMForm::on_lfoNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMLFO(instNum_, arg1); setInstrumentLFOParameters(); emit lfoNumberChanged(); emit modified(); } onLFONumberChanged(); } void InstrumentEditorFMForm::on_lfoGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMLFOEnabled(instNum_, arg1); setInstrumentLFOParameters(); emit lfoNumberChanged(); emit modified(); } onLFONumberChanged(); } //--- OperatorSequence FMEnvelopeParameter InstrumentEditorFMForm::getOperatorSequenceParameter() const { return static_cast(ui->opSeqTypeComboBox->currentData(Qt::UserRole).toInt()); } void InstrumentEditorFMForm::setInstrumentOperatorSequenceParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instFM = dynamic_cast(inst.get()); FMEnvelopeParameter param = getOperatorSequenceParameter(); ui->opSeqNumSpinBox->setValue(instFM->getOperatorSequenceNumber(param)); ui->opSeqEditor->clearData(); setOperatorSequenceEditor(); for (auto& unit : instFM->getOperatorSequenceSequence(param)) { ui->opSeqEditor->addSequenceData(unit.data); } for (auto& loop : instFM->getOperatorSequenceLoopRoot(param).getAllLoops()) { ui->opSeqEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->opSeqEditor->setRelease(instFM->getOperatorSequenceRelease(param)); if (instFM->getOperatorSequenceEnabled(param)) { ui->opSeqEditGroupBox->setChecked(true); onOperatorSequenceNumberChanged(); } else { ui->opSeqEditGroupBox->setChecked(false); } } void InstrumentEditorFMForm::setOperatorSequenceEditor() { ui->opSeqEditor->clearRow(); FMEnvelopeParameter param = getOperatorSequenceParameter(); switch (param) { case FMEnvelopeParameter::AL: case FMEnvelopeParameter::FB: case FMEnvelopeParameter::DT1: case FMEnvelopeParameter::DT2: case FMEnvelopeParameter::DT3: case FMEnvelopeParameter::DT4: ui->opSeqEditor->setMaximumDisplayedRowCount(8); for (int i = 0; i < 8; ++i) { ui->opSeqEditor->AddRow(QString::number(i), false); } ui->opSeqEditor->autoFitLabelWidth(); ui->opSeqEditor->setUpperRow(7); break; case FMEnvelopeParameter::AR1: case FMEnvelopeParameter::AR2: case FMEnvelopeParameter::AR3: case FMEnvelopeParameter::AR4: case FMEnvelopeParameter::DR1: case FMEnvelopeParameter::DR2: case FMEnvelopeParameter::DR3: case FMEnvelopeParameter::DR4: case FMEnvelopeParameter::SR1: case FMEnvelopeParameter::SR2: case FMEnvelopeParameter::SR3: case FMEnvelopeParameter::SR4: ui->opSeqEditor->setMaximumDisplayedRowCount(16); for (int i = 0; i < 32; ++i) { ui->opSeqEditor->AddRow(QString::number(i)); } ui->opSeqEditor->setUpperRow(15); break; case FMEnvelopeParameter::RR1: case FMEnvelopeParameter::RR2: case FMEnvelopeParameter::RR3: case FMEnvelopeParameter::RR4: case FMEnvelopeParameter::SL1: case FMEnvelopeParameter::SL2: case FMEnvelopeParameter::SL3: case FMEnvelopeParameter::SL4: case FMEnvelopeParameter::ML1: case FMEnvelopeParameter::ML2: case FMEnvelopeParameter::ML3: case FMEnvelopeParameter::ML4: ui->opSeqEditor->setMaximumDisplayedRowCount(16); for (int i = 0; i < 16; ++i) { ui->opSeqEditor->AddRow(QString::number(i)); } ui->opSeqEditor->setUpperRow(15); break; case FMEnvelopeParameter::KS1: case FMEnvelopeParameter::KS2: case FMEnvelopeParameter::KS3: case FMEnvelopeParameter::KS4: ui->opSeqEditor->setMaximumDisplayedRowCount(4); for (int i = 0; i < 4; ++i) { ui->opSeqEditor->AddRow(QString::number(i)); } ui->opSeqEditor->setUpperRow(3); break; case FMEnvelopeParameter::TL1: case FMEnvelopeParameter::TL2: case FMEnvelopeParameter::TL3: case FMEnvelopeParameter::TL4: ui->opSeqEditor->setMaximumDisplayedRowCount(16); for (int i = 0; i < 128; ++i) { ui->opSeqEditor->AddRow(QString::number(i)); } ui->opSeqEditor->setUpperRow(15); break; default: break; } } /********** Slots **********/ void InstrumentEditorFMForm::onOperatorSequenceNumberChanged() { // Change users view std::multiset users = bt_.lock()->getOperatorSequenceFMUsers(getOperatorSequenceParameter(), ui->opSeqNumSpinBox->value()); ui->opSeqUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::onOperatorSequenceParameterChanged(FMEnvelopeParameter param, int tnNum) { if (param == getOperatorSequenceParameter() && ui->opSeqNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentOperatorSequenceParameters(); } } void InstrumentEditorFMForm::onOperatorSequenceTypeChanged(int type) { Q_UNUSED(type) if (!isIgnoreEvent_) setInstrumentOperatorSequenceParameters(); } void InstrumentEditorFMForm::on_opSeqEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMOperatorSequenceEnabled(instNum_, getOperatorSequenceParameter(), arg1); setInstrumentOperatorSequenceParameters(); emit operatorSequenceNumberChanged(); emit modified(); } onOperatorSequenceNumberChanged(); } void InstrumentEditorFMForm::on_opSeqNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMOperatorSequence(instNum_, getOperatorSequenceParameter(), arg1); setInstrumentOperatorSequenceParameters(); emit operatorSequenceNumberChanged(); emit modified(); } onOperatorSequenceNumberChanged(); } //--- Arpeggio FMOperatorType InstrumentEditorFMForm::getArpeggioOperator() const { return static_cast(ui->arpOpComboBox->currentData(Qt::UserRole).toInt()); } void InstrumentEditorFMForm::setInstrumentArpeggioParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instFM = dynamic_cast(inst.get()); FMOperatorType param = getArpeggioOperator(); ui->arpNumSpinBox->setValue(instFM->getArpeggioNumber(param)); ui->arpEditor->clearData(); for (auto& unit : instFM->getArpeggioSequence(param)) { ui->arpEditor->addSequenceData(unit.data); } for (auto& loop : instFM->getArpeggioLoopRoot(param).getAllLoops()) { ui->arpEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->arpEditor->setRelease(instFM->getArpeggioRelease(param)); for (int i = 0; i < ui->arpTypeComboBox->count(); ++i) { if (instFM->getArpeggioType(param) == static_cast(ui->arpTypeComboBox->itemData(i).toInt())) { ui->arpTypeComboBox->setCurrentIndex(i); break; } } if (instFM->getArpeggioEnabled(param)) { ui->arpEditGroupBox->setChecked(true); onArpeggioNumberChanged(); } else { ui->arpEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorFMForm::onArpeggioNumberChanged() { // Change users view std::multiset users = bt_.lock()->getArpeggioFMUsers(ui->arpNumSpinBox->value()); ui->arpUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::onArpeggioParameterChanged(int tnNum) { if (ui->arpNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentArpeggioParameters(); } } void InstrumentEditorFMForm::onArpeggioOperatorChanged(int op) { Q_UNUSED(op) if (!isIgnoreEvent_) setInstrumentArpeggioParameters(); } void InstrumentEditorFMForm::onArpeggioTypeChanged(int index) { Q_UNUSED(index) auto type = static_cast(ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { bt_.lock()->setArpeggioFMType(ui->arpNumSpinBox->value(), type); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } ui->arpEditor->setSequenceType(type); } void InstrumentEditorFMForm::on_arpEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMArpeggioEnabled(instNum_, getArpeggioOperator(), arg1); setInstrumentArpeggioParameters(); emit arpeggioNumberChanged(); emit modified(); } onArpeggioNumberChanged(); } void InstrumentEditorFMForm::on_arpNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMArpeggio(instNum_, getArpeggioOperator(), arg1); setInstrumentArpeggioParameters(); emit arpeggioNumberChanged(); emit modified(); } onArpeggioNumberChanged(); } //--- Pitch FMOperatorType InstrumentEditorFMForm::getPitchOperator() const { return static_cast(ui->ptOpComboBox->currentData(Qt::UserRole).toInt()); } void InstrumentEditorFMForm::setInstrumentPitchParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instFM = dynamic_cast(inst.get()); FMOperatorType param = getPitchOperator(); ui->ptNumSpinBox->setValue(instFM->getPitchNumber(param)); ui->ptEditor->clearData(); for (auto& unit : instFM->getPitchSequence(param)) { ui->ptEditor->addSequenceData(unit.data); } for (auto& loop : instFM->getPitchLoopRoot(param).getAllLoops()) { ui->ptEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->ptEditor->setRelease(instFM->getPitchRelease(param)); for (int i = 0; i < ui->ptTypeComboBox->count(); ++i) { if (instFM->getPitchType(param) == static_cast(ui->ptTypeComboBox->itemData(i).toInt())) { ui->ptTypeComboBox->setCurrentIndex(i); break; } } if (instFM->getPitchEnabled(param)) { ui->ptEditGroupBox->setChecked(true); onPitchNumberChanged(); } else { ui->ptEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorFMForm::onPitchNumberChanged() { // Change users view std::multiset users = bt_.lock()->getPitchFMUsers(ui->ptNumSpinBox->value()); ui->ptUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorFMForm::onPitchParameterChanged(int tnNum) { if (ui->ptNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentPitchParameters(); } } void InstrumentEditorFMForm::onPitchOperatorChanged(int op) { Q_UNUSED(op) if (!isIgnoreEvent_) setInstrumentPitchParameters(); } void InstrumentEditorFMForm::onPitchTypeChanged(int index) { Q_UNUSED(index) auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { bt_.lock()->setPitchFMType(ui->ptNumSpinBox->value(), type); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } ui->ptEditor->setSequenceType(type); } void InstrumentEditorFMForm::on_ptEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMPitchEnabled(instNum_, getPitchOperator(), arg1); setInstrumentPitchParameters(); emit pitchNumberChanged(); emit modified(); } onPitchNumberChanged(); } void InstrumentEditorFMForm::on_ptNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentFMPitch(instNum_, getPitchOperator(), arg1); setInstrumentPitchParameters(); emit pitchNumberChanged(); emit modified(); } onPitchNumberChanged(); } //========== Others ==========// void InstrumentEditorFMForm::setInstrumentEnvelopeResetParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instFM = dynamic_cast(inst.get()); ui->envResetCheckBox->setChecked(instFM->getEnvelopeResetEnabled(FMOperatorType::All)); ui->envResetOp1CheckBox->setChecked(instFM->getEnvelopeResetEnabled(FMOperatorType::Op1)); ui->envResetOp2CheckBox->setChecked(instFM->getEnvelopeResetEnabled(FMOperatorType::Op2)); ui->envResetOp3CheckBox->setChecked(instFM->getEnvelopeResetEnabled(FMOperatorType::Op3)); ui->envResetOp4CheckBox->setChecked(instFM->getEnvelopeResetEnabled(FMOperatorType::Op4)); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_fm_form.hpp000066400000000000000000000130461401124043500313030ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_EDITOR_FM_FORM_HPP #define INSTRUMENT_EDITOR_FM_FORM_HPP #include #include #include #include #include #include "bamboo_tracker.hpp" #include "instrument.hpp" #include "configuration.hpp" #include "jamming.hpp" #include "gui/instrument_editor/visualized_instrument_macro_editor.hpp" #include "gui/color_palette.hpp" namespace Ui { class InstrumentEditorFMForm; } class InstrumentEditorFMForm : public QWidget { Q_OBJECT public: InstrumentEditorFMForm(int num, QWidget *parent = nullptr); ~InstrumentEditorFMForm() override; void setInstrumentNumber(int num); int getInstrumentNumber() const; void setCore(std::weak_ptr core); void setConfiguration(std::weak_ptr config); void updateConfigurationForDisplay(); void setColorPalette(std::shared_ptr palette); signals: void jamKeyOnEvent(JamKey key); void jamKeyOffEvent(JamKey key); void modified(); protected: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; void showEvent(QShowEvent* event) override; void resizeEvent(QResizeEvent* event) override; private: Ui::InstrumentEditorFMForm *ui; int instNum_; bool isIgnoreEvent_; std::weak_ptr bt_; std::shared_ptr palette_; std::weak_ptr config_; void updateInstrumentParameters(); //========== Envelope ==========// signals: void envelopeNumberChanged(); void envelopeParameterChanged(int envNum, int fromInstNum); public slots: void onEnvelopeParameterChanged(int envNum); void onEnvelopeNumberChanged(); private: void setInstrumentEnvelopeParameters(); void setInstrumentEnvelopeParameters(QString data); void setInstrumentEnvelopeParameters(int envTypeNum, QString data); void setInstrumentOperatorParameters(int opNum, QString data); void paintAlgorithmDiagram(); void resizeAlgorithmDiagram(); private slots: void copyEnvelope(); void pasteEnvelope(); void pasteEnvelopeFrom(int typenum); void copyOperator(int opNum); void pasteOperator(int opNum); void on_envNumSpinBox_valueChanged(int arg1); void on_envGroupBox_customContextMenuRequested(const QPoint &pos); //========== LFO ==========// signals: void lfoNumberChanged(); void lfoParameterChanged(int lfoNum, int fromInstNum); public slots: void onLFOParameterChanged(int lfoNum); void onLFONumberChanged(); private: void setInstrumentLFOParameters(); void setInstrumentLFOParameters(QString data); QString toLFOString() const; private slots: void on_lfoGroupBox_customContextMenuRequested(const QPoint &pos); void on_lfoNumSpinBox_valueChanged(int arg1); void on_lfoGroupBox_toggled(bool arg1); //========== OperatorSequence ==========// public: FMEnvelopeParameter getOperatorSequenceParameter() const; signals: void operatorSequenceNumberChanged(); void operatorSequenceParameterChanged(FMEnvelopeParameter param, int opSeqNum, int fromInstNum); public slots: void onOperatorSequenceNumberChanged(); void onOperatorSequenceParameterChanged(FMEnvelopeParameter param, int opSeqNum); private: void setInstrumentOperatorSequenceParameters(); void setOperatorSequenceEditor(); private slots: void onOperatorSequenceTypeChanged(int type); void on_opSeqEditGroupBox_toggled(bool arg1); void on_opSeqNumSpinBox_valueChanged(int arg1); //========== Arpeggio ==========// public: FMOperatorType getArpeggioOperator() const; signals: void arpeggioNumberChanged(); void arpeggioParameterChanged(int arpNum, int fromInstNum); public slots: void onArpeggioNumberChanged(); void onArpeggioParameterChanged(int arpNum); private: void setInstrumentArpeggioParameters(); private slots: void onArpeggioOperatorChanged(int op); void onArpeggioTypeChanged(int index); void on_arpEditGroupBox_toggled(bool arg1); void on_arpNumSpinBox_valueChanged(int arg1); //========== Pitch ==========// public: FMOperatorType getPitchOperator() const; signals: void pitchNumberChanged(); void pitchParameterChanged(int ptNum, int fromInstNum); public slots: void onPitchNumberChanged(); void onPitchParameterChanged(int arpNum); private: void setInstrumentPitchParameters(); private slots: void onPitchOperatorChanged(int op); void onPitchTypeChanged(int index); void on_ptEditGroupBox_toggled(bool arg1); void on_ptNumSpinBox_valueChanged(int arg1); //========== Others ==========// private: void setInstrumentEnvelopeResetParameters(); }; #endif // INSTRUMENT_EDITOR_FM_FORM_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_fm_form.ui000066400000000000000000000735001401124043500311320ustar00rootroot00000000000000 InstrumentEditorFMForm 0 0 570 750 Form 0 0 0 0 QFrame::NoFrame 0 true 0 0 570 750 0 Envelope Envelope QFrame::StyledPanel QFrame::Raised QFrame::StyledPanel QFrame::Raised false # 127 Users: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true Qt::NoFocus true QFrame::StyledPanel QFrame::Raised QFrame::StyledPanel QFrame::Raised QFrame::StyledPanel QFrame::Raised QFrame::StyledPanel QFrame::Raised LFO/Operator sequence Operator sequence Operator: Qt::Horizontal 40 20 Sequence true false false # 127 Qt::Horizontal 40 20 Users: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::NoFocus true LFO true false Qt::NoFocus true false # 127 Users: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QFrame::StyledPanel QFrame::Raised QFrame::StyledPanel QFrame::Raised QFrame::StyledPanel QFrame::Raised Start count: false AM operators Operator 1 Operator 2 Operator 3 Operator 4 Qt::Vertical 20 40 Envelope reset Reset envelope before key on false FM 3ch Operator 1 Operator 2 Operator 3 Operator 4 Qt::Vertical 20 40 Arpeggio/Pitch Pitch Operator: Qt::Horizontal 40 20 Sequence true false Qt::Horizontal 40 20 false # 127 Type: Users: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::NoFocus true Arpeggio Operator: Qt::Horizontal 40 20 Sequence true false Type: Users: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false # 127 Qt::Horizontal 40 20 Qt::NoFocus true VisualizedInstrumentMacroEditor QWidget
gui/instrument_editor/visualized_instrument_macro_editor.hpp
1
ArpeggioMacroEditor QWidget
gui/instrument_editor/arpeggio_macro_editor.hpp
1
LabeledVerticalSlider QFrame
gui/labeled_vertical_slider.hpp
1
FMOperatorTable QFrame
gui/instrument_editor/fm_operator_table.hpp
1
LabeledHorizontalSlider QFrame
gui/labeled_horizontal_slider.hpp
1
scrollArea tabWidget envNumSpinBox alGraphicsView lfoGroupBox lfoNumSpinBox lfoStartSpinBox amOp1CheckBox amOp2CheckBox amOp3CheckBox amOp4CheckBox envResetCheckBox envResetOp1CheckBox envResetOp2CheckBox envResetOp3CheckBox envResetOp4CheckBox opSeqTypeComboBox opSeqEditGroupBox opSeqNumSpinBox arpOpComboBox arpEditGroupBox arpNumSpinBox arpTypeComboBox ptOpComboBox ptEditGroupBox ptNumSpinBox ptTypeComboBox
BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.cpp000066400000000000000000001017221401124043500314670ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_editor_ssg_form.hpp" #include "ui_instrument_editor_ssg_form.h" #include #include #include #include #include "gui/event_guard.hpp" #include "note.hpp" #include "sequence_property.hpp" #include "gui/jam_layout.hpp" #include "gui/instrument_editor/instrument_editor_utils.hpp" #include "gui/gui_utils.hpp" namespace { bool isModulatedWaveformSSG(int type) { switch (type) { case SSGWaveformType::SQUARE: case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: return false; case SSGWaveformType::SQM_TRIANGLE: case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: return true; default: throw std::invalid_argument("Invalid SSGWaveformType"); } } } InstrumentEditorSSGForm::InstrumentEditorSSGForm(int num, QWidget *parent) : QWidget(parent), ui(new Ui::InstrumentEditorSSGForm), instNum_(num) { ui->setupUi(this); //========== Waveform ==========// ui->waveEditor->setMaximumDisplayedRowCount(7); ui->waveEditor->setDefaultRow(0); ui->waveEditor->AddRow(tr("Sq"), false); ui->waveEditor->AddRow(tr("Tri"), false); ui->waveEditor->AddRow(tr("Saw"), false); ui->waveEditor->AddRow(tr("InvSaw"), false); ui->waveEditor->AddRow(tr("SMTri"), false); ui->waveEditor->AddRow(tr("SMSaw"), false); ui->waveEditor->AddRow(tr("SMInvSaw"), false); ui->waveEditor->autoFitLabelWidth(); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int col) { if (!isIgnoreEvent_) { if (isModulatedWaveformSSG(row)) { SSGWaveformUnit data = setWaveformSequenceColumn(col, row); // Set square-mask frequency bt_.lock()->addWaveformSSGSequenceData(ui->waveNumSpinBox->value(), data); } else { bt_.lock()->addWaveformSSGSequenceData(ui->waveNumSpinBox->value(), SSGWaveformUnit::makeOnlyDataUnit(row)); } emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removeWaveformSSGSequenceData(ui->waveNumSpinBox->value()); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { if (isModulatedWaveformSSG(row)) { SSGWaveformUnit data = setWaveformSequenceColumn(col, row); // Set square-mask frequency bt_.lock()->setWaveformSSGSequenceData(ui->waveNumSpinBox->value(), col, data); } else { bt_.lock()->setWaveformSSGSequenceData(ui->waveNumSpinBox->value(), col, SSGWaveformUnit::makeOnlyDataUnit(row)); } emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addWaveformSSGLoop(ui->waveNumSpinBox->value(), loop); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removeWaveformSSGLoop(ui->waveNumSpinBox->value(), begin, end); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearWaveformSSGLoops(ui->waveNumSpinBox->value()); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changeWaveformSSGLoop(ui->waveNumSpinBox->value(), prevBegin, prevEnd, loop); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->waveEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setWaveformSSGRelease(ui->waveNumSpinBox->value(), release); emit waveformParameterChanged(ui->waveNumSpinBox->value(), instNum_); emit modified(); } }); //========== Tone/Noise ==========// QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addToneNoiseSSGSequenceData(ui->tnNumSpinBox->value(), row); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removeToneNoiseSSGSequenceData(ui->tnNumSpinBox->value()); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setToneNoiseSSGSequenceData(ui->tnNumSpinBox->value(), col, row); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addToneNoiseSSGLoop(ui->tnNumSpinBox->value(), loop); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removeToneNoiseSSGLoop(ui->tnNumSpinBox->value(), begin, end); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearToneNoiseSSGLoops(ui->tnNumSpinBox->value()); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changeToneNoiseSSGLoop(ui->tnNumSpinBox->value(), prevBegin, prevEnd, loop); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->tnEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setToneNoiseSSGRelease(ui->tnNumSpinBox->value(), release); emit toneNoiseParameterChanged(ui->tnNumSpinBox->value(), instNum_); emit modified(); } }); //========== Envelope ==========// ui->envEditor->setMaximumDisplayedRowCount(16); ui->envEditor->setDefaultRow(15); for (int i = 0; i < 16; ++i) { ui->envEditor->AddRow(QString::number(i), false); } for (int i = 0; i < 8; ++i) { ui->envEditor->AddRow(tr("HEnv %1").arg(i), false); } ui->envEditor->autoFitLabelWidth(); ui->envEditor->setMultipleReleaseState(true); ui->envEditor->setPermittedReleaseTypes( VisualizedInstrumentMacroEditor::PermittedReleaseFlag::ABSOLUTE_RELEASE | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::RELATIVE_RELEASE | VisualizedInstrumentMacroEditor::PermittedReleaseFlag::FIXED_RELEASE); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int col) { if (!isIgnoreEvent_) { if (row >= 16) { SSGEnvelopeUnit data = setEnvelopeSequenceColumn(col, row); // Set hard frequency bt_.lock()->addEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), data); } else { bt_.lock()->addEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), SSGEnvelopeUnit::makeOnlyDataUnit(row)); } emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removeEnvelopeSSGSequenceData(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { if (row >= 16) { SSGEnvelopeUnit data = setEnvelopeSequenceColumn(col, row); // Set hard frequency bt_.lock()->setEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), col, data); } else { bt_.lock()->setEnvelopeSSGSequenceData(ui->envNumSpinBox->value(), col, SSGEnvelopeUnit::makeOnlyDataUnit(row)); } emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addEnvelopeSSGLoop(ui->envNumSpinBox->value(), loop); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removeEnvelopeSSGLoop(ui->envNumSpinBox->value(), begin, end); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearEnvelopeSSGLoops(ui->envNumSpinBox->value()); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changeEnvelopeSSGLoop(ui->envNumSpinBox->value(), prevBegin, prevEnd, loop); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->envEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setEnvelopeSSGRelease(ui->envNumSpinBox->value(), release); emit envelopeParameterChanged(ui->envNumSpinBox->value(), instNum_); emit modified(); } }); //========== Arpeggio ==========// ui->arpTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); ui->arpTypeComboBox->addItem(tr("Fixed"), static_cast(SequenceType::FixedSequence)); ui->arpTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addArpeggioSSGSequenceData(ui->arpNumSpinBox->value(), row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removeArpeggioSSGSequenceData(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setArpeggioSSGSequenceData(ui->arpNumSpinBox->value(), col, row); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addArpeggioSSGLoop(ui->arpNumSpinBox->value(), loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removeArpeggioSSGLoop(ui->arpNumSpinBox->value(), begin, end); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearArpeggioSSGLoops(ui->arpNumSpinBox->value()); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changeArpeggioSSGLoop(ui->arpNumSpinBox->value(), prevBegin, prevEnd, loop); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->arpEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setArpeggioSSGRelease(ui->arpNumSpinBox->value(), release); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->arpTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorSSGForm::onArpeggioTypeChanged); //========== Pitch ==========// ui->ptEditor->setMaximumDisplayedRowCount(15); ui->ptEditor->setDefaultRow(127); ui->ptEditor->setLabelDiaplayMode(true); for (int i = 0; i < 255; ++i) { ui->ptEditor->AddRow(QString::asprintf("%+d", i - 127), false); } ui->ptEditor->autoFitLabelWidth(); ui->ptEditor->setUpperRow(134); ui->ptEditor->setMMLDisplay0As(-127); ui->ptTypeComboBox->addItem(tr("Absolute"), static_cast(SequenceType::AbsoluteSequence)); ui->ptTypeComboBox->addItem(tr("Relative"), static_cast(SequenceType::RelativeSequence)); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataAdded, this, [&](int row, int) { if (!isIgnoreEvent_) { bt_.lock()->addPitchSSGSequenceData(ui->ptNumSpinBox->value(), row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataRemoved, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->removePitchSSGSequenceData(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::sequenceDataChanged, this, [&](int row, int col) { if (!isIgnoreEvent_) { bt_.lock()->setPitchSSGSequenceData(ui->ptNumSpinBox->value(), col, row); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopAdded, this, [&](InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->addPitchSSGLoop(ui->ptNumSpinBox->value(), loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopRemoved, this, [&](int begin, int end) { if (!isIgnoreEvent_) { bt_.lock()->removePitchSSGLoop(ui->ptNumSpinBox->value(), begin, end); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopCleared, this, [&] { if (!isIgnoreEvent_) { bt_.lock()->clearPitchSSGLoops(ui->ptNumSpinBox->value()); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::loopChanged, this, [&](int prevBegin, int prevEnd, InstrumentSequenceLoop loop) { if (!isIgnoreEvent_) { bt_.lock()->changePitchSSGLoop(ui->ptNumSpinBox->value(), prevBegin, prevEnd, loop); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); QObject::connect(ui->ptEditor, &VisualizedInstrumentMacroEditor::releaseChanged, this, [&](InstrumentSequenceRelease release) { if (!isIgnoreEvent_) { bt_.lock()->setPitchSSGRelease(ui->ptNumSpinBox->value(), release); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->ptTypeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &InstrumentEditorSSGForm::onPitchTypeChanged); } InstrumentEditorSSGForm::~InstrumentEditorSSGForm() { delete ui; } void InstrumentEditorSSGForm::setInstrumentNumber(int num) { instNum_ = num; } int InstrumentEditorSSGForm::getInstrumentNumber() const { return instNum_; } void InstrumentEditorSSGForm::setCore(std::weak_ptr core) { bt_ = core; updateInstrumentParameters(); } void InstrumentEditorSSGForm::setConfiguration(std::weak_ptr config) { config_ = config; } void InstrumentEditorSSGForm::setColorPalette(std::shared_ptr palette) { ui->waveEditor->setColorPalette(palette); ui->tnEditor->setColorPalette(palette); ui->envEditor->setColorPalette(palette); ui->arpEditor->setColorPalette(palette); ui->ptEditor->setColorPalette(palette); } void InstrumentEditorSSGForm::updateInstrumentParameters() { Ui::EventGuard eg(isIgnoreEvent_); auto name = gui_utils::utf8ToQString(bt_.lock()->getInstrument(instNum_)->getName()); setWindowTitle(QString("%1: %2").arg(instNum_, 2, 16, QChar('0')).toUpper().arg(name)); setInstrumentWaveformParameters(); setInstrumentToneNoiseParameters(); setInstrumentEnvelopeParameters(); setInstrumentArpeggioParameters(); setInstrumentPitchParameters(); } /********** Events **********/ // MUST DIRECT CONNECTION void InstrumentEditorSSGForm::keyPressEvent(QKeyEvent *event) { // General keys switch (event->key()) { case Qt::Key_Escape: close(); break; default: // For jam key on if (!event->isAutoRepeat()) { // Musical keyboard Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOnEvent(jk); } catch (std::invalid_argument&) {} } break; } } // MUST DIRECT CONNECTION void InstrumentEditorSSGForm::keyReleaseEvent(QKeyEvent *event) { // For jam key off if (!event->isAutoRepeat()) { Qt::Key qtKey = static_cast(event->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOffEvent(jk); } catch (std::invalid_argument&) {} } } //--- Waveform void InstrumentEditorSSGForm::setInstrumentWaveformParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instSSG = dynamic_cast(inst.get()); ui->waveNumSpinBox->setValue(instSSG->getWaveformNumber()); ui->waveEditor->clearData(); for (auto& unit : instSSG->getWaveformSequence()) { QString str(""); if (isModulatedWaveformSSG(unit.data)) { if (unit.type == SSGWaveformUnit::RatioSubdata) { int r1, r2; unit.getSubdataAsRatio(r1, r2); str = QString("%1/%2").arg(r1).arg(r2); } else { str = QString::number(unit.subdata); } } ui->waveEditor->addSequenceData(unit.data, str, unit.subdata); } for (auto& loop : instSSG->getWaveformLoopRoot().getAllLoops()) { ui->waveEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->waveEditor->setRelease(instSSG->getWaveformRelease()); if (instSSG->getWaveformEnabled()) { ui->waveEditGroupBox->setChecked(true); onWaveformNumberChanged(); } else { ui->waveEditGroupBox->setChecked(false); } } SSGWaveformUnit InstrumentEditorSSGForm::setWaveformSequenceColumn(int col, int wfRow) { auto button = ui->squareMaskButtonGroup->checkedButton(); if (button == ui->squareMaskRawRadioButton) { SSGWaveformUnit unit = SSGWaveformUnit::makeRawUnit(wfRow, ui->squareMaskRawSpinBox->value()); ui->waveEditor->setText(col, QString::number(unit.subdata)); ui->waveEditor->setSubdata(col, unit.subdata); return unit; } else { SSGWaveformUnit unit = SSGWaveformUnit::makeRatioUnit(wfRow, ui->squareMaskToneSpinBox->value(), ui->squareMaskMaskSpinBox->value()); ui->waveEditor->setText(col, QString::number(ui->squareMaskToneSpinBox->value()) + "/" + QString::number(ui->squareMaskMaskSpinBox->value())); ui->waveEditor->setSubdata(col, unit.subdata); return unit; } } /********** Slots **********/ void InstrumentEditorSSGForm::onWaveformNumberChanged() { // Change users view std::multiset users = bt_.lock()->getWaveformSSGUsers(ui->waveNumSpinBox->value()); ui->waveUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onWaveformParameterChanged(int wfNum) { if (ui->waveNumSpinBox->value() == wfNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentWaveformParameters(); } } void InstrumentEditorSSGForm::on_waveEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGWaveformEnabled(instNum_, arg1); setInstrumentWaveformParameters(); emit waveformNumberChanged(); emit modified(); } onWaveformNumberChanged(); } void InstrumentEditorSSGForm::on_waveNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGWaveform(instNum_, arg1); setInstrumentWaveformParameters(); emit waveformNumberChanged(); emit modified(); } onWaveformNumberChanged(); } void InstrumentEditorSSGForm::on_squareMaskRawSpinBox_valueChanged(int arg1) { ui->squareMaskRawSpinBox->setSuffix( QString(" (0x") + QString("%1 | ").arg(arg1, 3, 16, QChar('0')).toUpper() + QString("%1Hz)").arg(arg1 ? QString::number(124800.0 / arg1, 'f', 4) : "-") ); } //--- Tone/Noise void InstrumentEditorSSGForm::setInstrumentToneNoiseParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instSSG = dynamic_cast(inst.get()); ui->tnNumSpinBox->setValue(instSSG->getToneNoiseNumber()); ui->tnEditor->clearData(); for (auto& unit : instSSG->getToneNoiseSequence()) { ui->tnEditor->addSequenceData(unit.data); } for (auto& loop : instSSG->getToneNoiseLoopRoot().getAllLoops()) { ui->tnEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->tnEditor->setRelease(instSSG->getToneNoiseRelease()); if (instSSG->getToneNoiseEnabled()) { ui->tnEditGroupBox->setChecked(true); onToneNoiseNumberChanged(); } else { ui->tnEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorSSGForm::onToneNoiseNumberChanged() { // Change users view std::multiset users = bt_.lock()->getToneNoiseSSGUsers(ui->tnNumSpinBox->value()); ui->tnUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onToneNoiseParameterChanged(int tnNum) { if (ui->tnNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentToneNoiseParameters(); } } void InstrumentEditorSSGForm::on_tnEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGToneNoiseEnabled(instNum_, arg1); setInstrumentToneNoiseParameters(); emit toneNoiseNumberChanged(); emit modified(); } onToneNoiseNumberChanged(); } void InstrumentEditorSSGForm::on_tnNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGToneNoise(instNum_, arg1); setInstrumentToneNoiseParameters(); emit toneNoiseNumberChanged(); emit modified(); } onToneNoiseNumberChanged(); } //--- Envelope void InstrumentEditorSSGForm::setInstrumentEnvelopeParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instSSG = dynamic_cast(inst.get()); ui->envNumSpinBox->setValue(instSSG->getEnvelopeNumber()); ui->envEditor->clearData(); for (auto& unit : instSSG->getEnvelopeSequence()) { QString str(""); if (unit.data >= 16) { if (unit.type == SSGEnvelopeUnit::RatioSubdata) { int r1, r2; unit.getSubdataAsRatio(r1, r2); str = QString("%1/%2").arg(r1).arg(r2); } else { str = QString::number(unit.subdata); } } ui->envEditor->addSequenceData(unit.data, str, unit.subdata); } for (auto& loop : instSSG->getEnvelopeLoopRoot().getAllLoops()) { ui->envEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->envEditor->setRelease(instSSG->getEnvelopeRelease()); if (instSSG->getEnvelopeEnabled()) { ui->envEditGroupBox->setChecked(true); onEnvelopeNumberChanged(); } else { ui->envEditGroupBox->setChecked(false); } } SSGEnvelopeUnit InstrumentEditorSSGForm::setEnvelopeSequenceColumn(int col, int envRow) { if (ui->hardFreqButtonGroup->checkedButton() == ui->hardFreqRawRadioButton) { SSGEnvelopeUnit unit = SSGEnvelopeUnit::makeRawUnit(envRow, ui->hardFreqRawSpinBox->value()); ui->envEditor->setText(col, QString::number(unit.subdata)); ui->envEditor->setSubdata(col, unit.subdata); return unit; } else { SSGEnvelopeUnit unit = SSGEnvelopeUnit::makeRatioUnit(envRow, ui->hardFreqToneSpinBox->value(), ui->hardFreqHardSpinBox->value()); ui->envEditor->setText(col, QString::number(ui->hardFreqToneSpinBox->value()) + "/" + QString::number(ui->hardFreqHardSpinBox->value())); ui->envEditor->setSubdata(col, unit.subdata); return unit; } } /********** Slots **********/ void InstrumentEditorSSGForm::onEnvelopeNumberChanged() { // Change users view std::multiset users = bt_.lock()->getEnvelopeSSGUsers(ui->envNumSpinBox->value()); ui->envUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onEnvelopeParameterChanged(int envNum) { if (ui->envNumSpinBox->value() == envNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentEnvelopeParameters(); } } void InstrumentEditorSSGForm::on_envEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGEnvelopeEnabled(instNum_, arg1); setInstrumentEnvelopeParameters(); emit envelopeNumberChanged(); emit modified(); } onEnvelopeNumberChanged(); } void InstrumentEditorSSGForm::on_envNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGEnvelope(instNum_, arg1); setInstrumentEnvelopeParameters(); emit envelopeNumberChanged(); emit modified(); } onEnvelopeNumberChanged(); } void InstrumentEditorSSGForm::on_hardFreqRawSpinBox_valueChanged(int arg1) { ui->hardFreqRawSpinBox->setSuffix( QString(" (0x") + QString("%1 | ").arg(arg1, 4, 16, QChar('0')).toUpper() + QString("%1Hz").arg(arg1 ? QString::number(7800.0 / arg1, 'f', 4) : "-")); } //--- Arpeggio void InstrumentEditorSSGForm::setInstrumentArpeggioParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instSSG = dynamic_cast(inst.get()); ui->arpNumSpinBox->setValue(instSSG->getArpeggioNumber()); ui->arpEditor->clearData(); for (auto& unit : instSSG->getArpeggioSequence()) { ui->arpEditor->addSequenceData(unit.data); } for (auto& loop : instSSG->getArpeggioLoopRoot().getAllLoops()) { ui->arpEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->arpEditor->setRelease(instSSG->getArpeggioRelease()); for (int i = 0; i < ui->arpTypeComboBox->count(); ++i) { if (instSSG->getArpeggioType() == static_cast(ui->arpTypeComboBox->itemData(i).toInt())) { ui->arpTypeComboBox->setCurrentIndex(i); break; } } if (instSSG->getArpeggioEnabled()) { ui->arpEditGroupBox->setChecked(true); onArpeggioNumberChanged(); } else { ui->arpEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorSSGForm::onArpeggioNumberChanged() { // Change users view std::multiset users = bt_.lock()->getArpeggioSSGUsers(ui->arpNumSpinBox->value()); ui->arpUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onArpeggioParameterChanged(int tnNum) { if (ui->arpNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentArpeggioParameters(); } } void InstrumentEditorSSGForm::onArpeggioTypeChanged(int index) { Q_UNUSED(index) auto type = static_cast(ui->arpTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { bt_.lock()->setArpeggioSSGType(ui->arpNumSpinBox->value(), type); emit arpeggioParameterChanged(ui->arpNumSpinBox->value(), instNum_); emit modified(); } ui->arpEditor->setSequenceType(type); } void InstrumentEditorSSGForm::on_arpEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGArpeggioEnabled(instNum_, arg1); setInstrumentArpeggioParameters(); emit arpeggioNumberChanged(); emit modified(); } onArpeggioNumberChanged(); } void InstrumentEditorSSGForm::on_arpNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGArpeggio(instNum_, arg1); setInstrumentArpeggioParameters(); emit arpeggioNumberChanged(); emit modified(); } onArpeggioNumberChanged(); } //--- Pitch void InstrumentEditorSSGForm::setInstrumentPitchParameters() { Ui::EventGuard ev(isIgnoreEvent_); std::unique_ptr inst = bt_.lock()->getInstrument(instNum_); auto instSSG = dynamic_cast(inst.get()); ui->ptNumSpinBox->setValue(instSSG->getPitchNumber()); ui->ptEditor->clearData(); for (auto& unit : instSSG->getPitchSequence()) { ui->ptEditor->addSequenceData(unit.data); } for (auto& loop : instSSG->getPitchLoopRoot().getAllLoops()) { ui->ptEditor->addLoop(loop.getBeginPos(), loop.getEndPos(), loop.getTimes()); } ui->ptEditor->setRelease(instSSG->getPitchRelease()); for (int i = 0; i < ui->ptTypeComboBox->count(); ++i) { if (instSSG->getPitchType() == static_cast(ui->ptTypeComboBox->itemData(i).toInt())) { ui->ptTypeComboBox->setCurrentIndex(i); break; } } if (instSSG->getPitchEnabled()) { ui->ptEditGroupBox->setChecked(true); onPitchNumberChanged(); } else { ui->ptEditGroupBox->setChecked(false); } } /********** Slots **********/ void InstrumentEditorSSGForm::onPitchNumberChanged() { // Change users view std::multiset users = bt_.lock()->getPitchSSGUsers(ui->ptNumSpinBox->value()); ui->ptUsersLineEdit->setText(inst_edit_utils::generateUsersString(users)); } void InstrumentEditorSSGForm::onPitchParameterChanged(int tnNum) { if (ui->ptNumSpinBox->value() == tnNum) { Ui::EventGuard eg(isIgnoreEvent_); setInstrumentPitchParameters(); } } void InstrumentEditorSSGForm::onPitchTypeChanged(int index) { Q_UNUSED(index) auto type = static_cast(ui->ptTypeComboBox->currentData(Qt::UserRole).toInt()); if (!isIgnoreEvent_) { bt_.lock()->setPitchSSGType(ui->ptNumSpinBox->value(), type); emit pitchParameterChanged(ui->ptNumSpinBox->value(), instNum_); emit modified(); } ui->ptEditor->setSequenceType(type); } void InstrumentEditorSSGForm::on_ptEditGroupBox_toggled(bool arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGPitchEnabled(instNum_, arg1); setInstrumentPitchParameters(); emit pitchNumberChanged(); emit modified(); } onPitchNumberChanged(); } void InstrumentEditorSSGForm::on_ptNumSpinBox_valueChanged(int arg1) { if (!isIgnoreEvent_) { bt_.lock()->setInstrumentSSGPitch(instNum_, arg1); setInstrumentPitchParameters(); emit pitchNumberChanged(); emit modified(); } onPitchNumberChanged(); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.hpp000066400000000000000000000111101401124043500314630ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_EDITOR_SSG_FORM_HPP #define INSTRUMENT_EDITOR_SSG_FORM_HPP #include #include #include #include "bamboo_tracker.hpp" #include "instrument.hpp" #include "configuration.hpp" #include "jamming.hpp" #include "instrument/instrument_property_defs.hpp" #include "gui/instrument_editor/visualized_instrument_macro_editor.hpp" #include "gui/color_palette.hpp" namespace Ui { class InstrumentEditorSSGForm; } class InstrumentEditorSSGForm : public QWidget { Q_OBJECT public: InstrumentEditorSSGForm(int num, QWidget *parent = nullptr); ~InstrumentEditorSSGForm() override; void setInstrumentNumber(int num); int getInstrumentNumber() const; void setCore(std::weak_ptr core); void setConfiguration(std::weak_ptr config); void setColorPalette(std::shared_ptr palette); signals: void jamKeyOnEvent(JamKey key); void jamKeyOffEvent(JamKey key); void modified(); protected: void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; private: Ui::InstrumentEditorSSGForm *ui; int instNum_; bool isIgnoreEvent_; std::weak_ptr bt_; std::weak_ptr config_; void updateInstrumentParameters(); //========== Waveform ==========// signals: void waveformNumberChanged(); void waveformParameterChanged(int wfNum, int fromInstNum); public slots: void onWaveformNumberChanged(); void onWaveformParameterChanged(int wfNum); private: void setInstrumentWaveformParameters(); SSGWaveformUnit setWaveformSequenceColumn(int col, int wfRow); private slots: void on_waveEditGroupBox_toggled(bool arg1); void on_waveNumSpinBox_valueChanged(int arg1); void on_squareMaskRawSpinBox_valueChanged(int arg1); //========== Tone/Noise ==========// signals: void toneNoiseNumberChanged(); void toneNoiseParameterChanged(int tnNum, int fromInstNum); public slots: void onToneNoiseNumberChanged(); void onToneNoiseParameterChanged(int tnNum); private: void setInstrumentToneNoiseParameters(); private slots: void on_tnEditGroupBox_toggled(bool arg1); void on_tnNumSpinBox_valueChanged(int arg1); //========== Envelope ==========// signals: void envelopeNumberChanged(); void envelopeParameterChanged(int wfNum, int fromInstNum); public slots: void onEnvelopeNumberChanged(); void onEnvelopeParameterChanged(int envNum); private: void setInstrumentEnvelopeParameters(); SSGEnvelopeUnit setEnvelopeSequenceColumn(int col, int envRow); private slots: void on_envEditGroupBox_toggled(bool arg1); void on_envNumSpinBox_valueChanged(int arg1); void on_hardFreqRawSpinBox_valueChanged(int arg1); //========== Arpeggio ==========// signals: void arpeggioNumberChanged(); void arpeggioParameterChanged(int arpNum, int fromInstNum); public slots: void onArpeggioNumberChanged(); void onArpeggioParameterChanged(int arpNum); private: void setInstrumentArpeggioParameters(); private slots: void onArpeggioTypeChanged(int index); void on_arpEditGroupBox_toggled(bool arg1); void on_arpNumSpinBox_valueChanged(int arg1); //========== Pitch ==========// signals: void pitchNumberChanged(); void pitchParameterChanged(int ptNum, int fromInstNum); public slots: void onPitchNumberChanged(); void onPitchParameterChanged(int arpNum); private: void setInstrumentPitchParameters(); private slots: void onPitchTypeChanged(int index); void on_ptEditGroupBox_toggled(bool arg1); void on_ptNumSpinBox_valueChanged(int arg1); }; #endif // INSTRUMENT_EDITOR_SSG_FORM_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_ssg_form.ui000066400000000000000000000500411401124043500313170ustar00rootroot00000000000000 InstrumentEditorSSGForm 0 0 507 390 Form 0 Waveform Waveform true false false # 127 Qt::NoFocus true Users: Qt::Horizontal 40 20 0 0 Square mask Raw true squareMaskButtonGroup (0x000 | -Hz) 4095 0 Tone/Mask ratio squareMaskButtonGroup 1 8 0 0 / 1 8 Tone/Noise Tone/Noise true false Qt::Horizontal 40 20 Users: false # 127 Qt::NoFocus true Envelope Envelope true false 0 0 Hardware envelope frequency Raw true hardFreqButtonGroup (0x0000 | -Hz) 0 65535 0 Tone/Env ratio hardFreqButtonGroup 1 8 0 0 / 1 8 Qt::Horizontal 40 20 false # 127 Users: Qt::NoFocus true Arpeggio Arpeggio true false Type: false # 127 Qt::Horizontal 40 20 Users: Qt::NoFocus true Pitch Pitch true false Users: Type: Qt::Horizontal 40 20 # 127 Qt::NoFocus true VisualizedInstrumentMacroEditor QWidget
gui/instrument_editor/visualized_instrument_macro_editor.hpp
1
ArpeggioMacroEditor QWidget
gui/instrument_editor/arpeggio_macro_editor.hpp
1
ToneNoiseMacroEditor QWidget
gui/instrument_editor/tone_noise_macro_editor.hpp
1
tabWidget waveEditGroupBox waveNumSpinBox squareMaskRawRadioButton squareMaskRawSpinBox squareMaskRatioRadioButton squareMaskToneSpinBox squareMaskMaskSpinBox tnEditGroupBox tnNumSpinBox envEditGroupBox envNumSpinBox hardFreqRawRadioButton hardFreqRawSpinBox hardFreqRatioRadioButton hardFreqToneSpinBox hardFreqHardSpinBox arpEditGroupBox arpNumSpinBox arpTypeComboBox ptEditGroupBox ptNumSpinBox ptTypeComboBox
BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_utils.cpp000066400000000000000000000027161401124043500310130ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_editor_utils.hpp" #include #include namespace inst_edit_utils { QString generateUsersString(const std::multiset& users) { QStringList l; std::transform(users.begin(), users.end(), std::back_inserter(l), [](int n) { return QString("%1").arg(n, 2, 16, QChar('0')); }); return l.join(",").toUpper(); } } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_editor_utils.hpp000066400000000000000000000025251401124043500310160ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_EDITOR_UTILS_HPP #define INSTRUMENT_EDITOR_UTILS_HPP #include #include namespace inst_edit_utils { QString generateUsersString(const std::multiset& users); } #endif // INSTRUMENT_EDITOR_UTILS_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_form_manager.cpp000066400000000000000000000366001401124043500307410ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_form_manager.hpp" #include #include #include "instrument.hpp" #include "gui/instrument_editor/instrument_editor_fm_form.hpp" #include "gui/instrument_editor/instrument_editor_ssg_form.hpp" #include "gui/instrument_editor/instrument_editor_adpcm_form.hpp" #include "gui/instrument_editor/instrument_editor_drumkit_form.hpp" #include "utils.hpp" void InstrumentFormManager::updateByConfiguration() { for (auto& pair : map_) { if (static_cast(pair.second->property("Type").toInt()) == InstrumentType::FM) { qobject_cast(pair.second.get())->updateConfigurationForDisplay(); } } } const std::shared_ptr InstrumentFormManager::getForm(int n) const { if (map_.count(n)) return map_.at(n); else return std::shared_ptr(); // nullptr } void InstrumentFormManager::add(int n, std::shared_ptr form, SoundSource src, InstrumentType type) { form->setProperty("Shown", false); form->setProperty("Source", static_cast(src)); form->setProperty("Type", static_cast(type)); map_.emplace(n, std::move(form)); // Parameter numbers update in MainWindow } void InstrumentFormManager::swap(int a, int b) { auto changeNumber = [&](int id, InstrumentType type) { QWidget* form = map_.at(id).get(); switch (type) { case InstrumentType::FM: qobject_cast(form)->setInstrumentNumber(id); break; case InstrumentType::SSG: qobject_cast(form)->setInstrumentNumber(id); break; case InstrumentType::ADPCM: qobject_cast(form)->setInstrumentNumber(id); break; case InstrumentType::Drumkit: qobject_cast(form)->setInstrumentNumber(id); break; } }; if (map_.count(a)) { if (map_.count(b)) { std::swap(map_.at(a), map_.at(b)); changeNumber(a, getFormInstrumentType(a)); changeNumber(b, getFormInstrumentType(b)); } else { map_[b] = map_.at(a); map_.erase(a); changeNumber(b, getFormInstrumentType(b)); } } else { if (map_.count(b)) { map_[a] = map_.at(b); map_.erase(b); changeNumber(a, getFormInstrumentType(a)); } } onInstrumentFMEnvelopeNumberChanged(); onInstrumentFMLFONumberChanged(); onInstrumentFMOperatorSequenceNumberChanged(); onInstrumentFMArpeggioNumberChanged(); onInstrumentFMPitchNumberChanged(); onInstrumentSSGWaveformNumberChanged(); onInstrumentSSGEnvelopeNumberChanged(); onInstrumentSSGToneNoiseNumberChanged(); onInstrumentSSGArpeggioNumberChanged(); onInstrumentSSGPitchNumberChanged(); onInstrumentADPCMSampleNumberChanged(); onInstrumentADPCMEnvelopeNumberChanged(); onInstrumentADPCMArpeggioNumberChanged(); onInstrumentADPCMPitchNumberChanged(); onInstrumentADPCMSampleNumberChanged(); } void InstrumentFormManager::remove(int n) { if (map_.count(n)) { map_.at(n)->close(); map_.erase(n); } onInstrumentFMEnvelopeNumberChanged(); onInstrumentFMLFONumberChanged(); onInstrumentFMOperatorSequenceNumberChanged(); onInstrumentFMArpeggioNumberChanged(); onInstrumentFMPitchNumberChanged(); onInstrumentSSGWaveformNumberChanged(); onInstrumentSSGEnvelopeNumberChanged(); onInstrumentSSGToneNoiseNumberChanged(); onInstrumentSSGArpeggioNumberChanged(); onInstrumentSSGPitchNumberChanged(); onInstrumentADPCMSampleNumberChanged(); onInstrumentADPCMEnvelopeNumberChanged(); onInstrumentADPCMArpeggioNumberChanged(); onInstrumentADPCMPitchNumberChanged(); onInstrumentADPCMSampleNumberChanged(); } void InstrumentFormManager::showForm(int n) { if (!map_.count(n)) return; auto& form = map_.at(n); if (form->isVisible()) { form->activateWindow(); } else { form->setProperty("Shown", true); form->show(); } } void InstrumentFormManager::closeAll() { for (auto& pair : map_) { pair.second->close(); } } void InstrumentFormManager::clearAll() { closeAll(); map_.clear(); } SoundSource InstrumentFormManager::getFormInstrumentSoundSource(int n) const { return static_cast(map_.at(n)->property("Source").toInt()); } InstrumentType InstrumentFormManager::getFormInstrumentType(int n) const { return static_cast(map_.at(n)->property("Type").toInt()); } int InstrumentFormManager::checkActivatedFormNumber() const { const QWidget* win = QApplication::activeWindow(); auto it = utils::findIf(map_, [win](const std::pair> p) { return p.second.get() == win; }); return (it == map_.end() ? -1 : it->first); } /********** Slots **********/ void InstrumentFormManager::onInstrumentFMEnvelopeParameterChanged(int envNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onEnvelopeParameterChanged(envNum); } } } void InstrumentFormManager::onInstrumentFMEnvelopeNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onEnvelopeNumberChanged(); } } } void InstrumentFormManager::onInstrumentFMLFOParameterChanged(int lfoNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onLFOParameterChanged(lfoNum); } } } void InstrumentFormManager::onInstrumentFMLFONumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onLFONumberChanged(); } } } void InstrumentFormManager::onInstrumentFMOperatorSequenceParameterChanged(FMEnvelopeParameter param, int opSeqNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onOperatorSequenceParameterChanged(param, opSeqNum); } } } void InstrumentFormManager::onInstrumentFMOperatorSequenceNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onOperatorSequenceNumberChanged(); } } } void InstrumentFormManager::onInstrumentFMArpeggioParameterChanged(int arpNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onArpeggioParameterChanged(arpNum); } } } void InstrumentFormManager::onInstrumentFMArpeggioNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onArpeggioNumberChanged(); } } } void InstrumentFormManager::onInstrumentFMPitchParameterChanged(int ptNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onPitchParameterChanged(ptNum); } } } void InstrumentFormManager::onInstrumentFMPitchNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::FM) { qobject_cast(pair.second.get())->onPitchNumberChanged(); } } } void InstrumentFormManager::onInstrumentSSGWaveformParameterChanged(int wfNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onWaveformParameterChanged(wfNum); } } } void InstrumentFormManager::onInstrumentSSGWaveformNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onWaveformNumberChanged(); } } } void InstrumentFormManager::onInstrumentSSGToneNoiseParameterChanged(int tnNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onToneNoiseParameterChanged(tnNum); } } } void InstrumentFormManager::onInstrumentSSGToneNoiseNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onToneNoiseNumberChanged(); } } } void InstrumentFormManager::onInstrumentSSGEnvelopeParameterChanged(int envNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onEnvelopeParameterChanged(envNum); } } } void InstrumentFormManager::onInstrumentSSGEnvelopeNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onEnvelopeNumberChanged(); } } } void InstrumentFormManager::onInstrumentSSGArpeggioParameterChanged(int arpNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onArpeggioParameterChanged(arpNum); } } } void InstrumentFormManager::onInstrumentSSGArpeggioNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onArpeggioNumberChanged(); } } } void InstrumentFormManager::onInstrumentSSGPitchParameterChanged(int ptNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onPitchParameterChanged(ptNum); } } } void InstrumentFormManager::onInstrumentSSGPitchNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::SSG) { qobject_cast(pair.second.get())->onPitchNumberChanged(); } } } void InstrumentFormManager::onInstrumentADPCMSampleParameterChanged(int sampNum, int fromInstNum) { Q_UNUSED(fromInstNum) // Update all adpcm editor to change sample memory viewer and sample viewer for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::ADPCM) { if (auto adpcmForm = qobject_cast(pair.second.get())) adpcmForm->onSampleParameterChanged(sampNum); else if (auto kitForm = qobject_cast(pair.second.get())) kitForm->onSampleParameterChanged(sampNum); } } } void InstrumentFormManager::onInstrumentADPCMSampleNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::ADPCM) { if (auto adpcmForm = qobject_cast(pair.second.get())) adpcmForm->onSampleNumberChanged(); else if (auto kitForm = qobject_cast(pair.second.get())) kitForm->onSampleNumberChanged(); } } } void InstrumentFormManager::onInstrumentADPCMSampleMemoryUpdated() { for (auto& pair : map_) { if (static_cast(pair.second->property("Source").toInt()) == SoundSource::ADPCM) { if (auto adpcmForm = qobject_cast(pair.second.get())) adpcmForm->onSampleMemoryUpdated(); else if (auto kitForm = qobject_cast(pair.second.get())) kitForm->onSampleMemoryUpdated(); } } } void InstrumentFormManager::onInstrumentADPCMEnvelopeParameterChanged(int envNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Type").toInt()) == InstrumentType::ADPCM) { qobject_cast(pair.second.get())->onEnvelopeParameterChanged(envNum); } } } void InstrumentFormManager::onInstrumentADPCMEnvelopeNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Type").toInt()) == InstrumentType::ADPCM) { qobject_cast(pair.second.get())->onEnvelopeNumberChanged(); } } } void InstrumentFormManager::onInstrumentADPCMArpeggioParameterChanged(int arpNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Type").toInt()) == InstrumentType::ADPCM) { qobject_cast(pair.second.get())->onArpeggioParameterChanged(arpNum); } } } void InstrumentFormManager::onInstrumentADPCMArpeggioNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Type").toInt()) == InstrumentType::ADPCM) { qobject_cast(pair.second.get())->onArpeggioNumberChanged(); } } } void InstrumentFormManager::onInstrumentADPCMPitchParameterChanged(int ptNum, int fromInstNum) { for (auto& pair : map_) { if (pair.first != fromInstNum && static_cast(pair.second->property("Type").toInt()) == InstrumentType::ADPCM) { qobject_cast(pair.second.get())->onPitchParameterChanged(ptNum); } } } void InstrumentFormManager::onInstrumentADPCMPitchNumberChanged() { for (auto& pair : map_) { if (static_cast(pair.second->property("Type").toInt()) == InstrumentType::ADPCM) { qobject_cast(pair.second.get())->onPitchNumberChanged(); } } } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/instrument_form_manager.hpp000066400000000000000000000072771401124043500307560ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INSTRUMENT_FORM_MANAGER_HPP #define INSTRUMENT_FORM_MANAGER_HPP #include #include #include #include #include "instrument/envelope_fm.hpp" #include "bamboo_tracker_defs.hpp" enum class InstrumentType; class InstrumentFormManager : public QObject { Q_OBJECT public: void updateByConfiguration(); const std::shared_ptr getForm(int n) const; void remove(int n); void add(int n, std::shared_ptr form, SoundSource src, InstrumentType type); void swap(int a, int b); void showForm(int n); void closeAll(); void clearAll(); SoundSource getFormInstrumentSoundSource(int n) const; InstrumentType getFormInstrumentType(int n) const; int checkActivatedFormNumber() const; public slots: void onInstrumentFMEnvelopeParameterChanged(int envNum, int fromInstNum); void onInstrumentFMEnvelopeNumberChanged(); void onInstrumentFMLFOParameterChanged(int lfoNum, int fromInstNum); void onInstrumentFMLFONumberChanged(); void onInstrumentFMOperatorSequenceParameterChanged(FMEnvelopeParameter param, int opSeqNum, int fromInstNum); void onInstrumentFMOperatorSequenceNumberChanged(); void onInstrumentFMArpeggioParameterChanged(int arpNum, int fromInstNum); void onInstrumentFMArpeggioNumberChanged(); void onInstrumentFMPitchParameterChanged(int ptNum, int fromInstNum); void onInstrumentFMPitchNumberChanged(); void onInstrumentSSGWaveformParameterChanged(int wfNum, int fromInstNum); void onInstrumentSSGWaveformNumberChanged(); void onInstrumentSSGToneNoiseParameterChanged(int tnNum, int fromInstNum); void onInstrumentSSGToneNoiseNumberChanged(); void onInstrumentSSGEnvelopeParameterChanged(int envNum, int fromInstNum); void onInstrumentSSGEnvelopeNumberChanged(); void onInstrumentSSGArpeggioParameterChanged(int arpNum, int fromInstNum); void onInstrumentSSGArpeggioNumberChanged(); void onInstrumentSSGPitchParameterChanged(int ptNum, int fromInstNum); void onInstrumentSSGPitchNumberChanged(); void onInstrumentADPCMSampleParameterChanged(int sampNum, int fromInstNum); void onInstrumentADPCMSampleNumberChanged(); void onInstrumentADPCMSampleMemoryUpdated(); void onInstrumentADPCMEnvelopeParameterChanged(int envNum, int fromInstNum); void onInstrumentADPCMEnvelopeNumberChanged(); void onInstrumentADPCMArpeggioParameterChanged(int arpNum, int fromInstNum); void onInstrumentADPCMArpeggioNumberChanged(); void onInstrumentADPCMPitchParameterChanged(int ptNum, int fromInstNum); void onInstrumentADPCMPitchNumberChanged(); private: std::unordered_map> map_; }; #endif // INSTRUMENT_FORM_MANAGER_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/sample_length_dialog.cpp000066400000000000000000000030411401124043500301460ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "sample_length_dialog.hpp" #include "ui_sample_length_dialog.h" SampleLengthDialog::SampleLengthDialog(int len, QWidget *parent) : QDialog(parent), ui(new Ui::SampleLengthDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui->spinBox->setValue(len); } SampleLengthDialog::~SampleLengthDialog() { delete ui; } int SampleLengthDialog::getLength() const { return ui->spinBox->value(); } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/sample_length_dialog.hpp000066400000000000000000000027561401124043500301670ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SAMPLE_LENGTH_DIALOG_HPP #define SAMPLE_LENGTH_DIALOG_HPP #include namespace Ui { class SampleLengthDialog; } class SampleLengthDialog : public QDialog { Q_OBJECT public: explicit SampleLengthDialog(int len, QWidget *parent = nullptr); ~SampleLengthDialog() override; int getLength() const; private: Ui::SampleLengthDialog *ui; }; #endif // SAMPLE_LENGTH_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/sample_length_dialog.ui000066400000000000000000000040331401124043500300030ustar00rootroot00000000000000 SampleLengthDialog 0 0 174 69 Resize Length 2 2147483646 2 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() SampleLengthDialog accept() 248 254 157 274 buttonBox rejected() SampleLengthDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/tone_noise_macro_editor.cpp000066400000000000000000000165701401124043500307110ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "tone_noise_macro_editor.hpp" #include #include ToneNoiseMacroEditor::ToneNoiseMacroEditor(QWidget *parent) : VisualizedInstrumentMacroEditor(parent) { setMaximumDisplayedRowCount(16); setDefaultRow(0); AddRow(tr("Tone"), false); AddRow(tr("Noise"), false); for (int i = 0; i < 32; ++i) { AddRow(QString::number(i), false); } autoFitLabelWidth(); } ToneNoiseMacroEditor::~ToneNoiseMacroEditor() {} void ToneNoiseMacroEditor::drawField() { QPainter painter(&pixmap_); painter.setFont(font_); int dispCnt = getDisplayedRowCount(); int textOffset = fontAscend_ - fontHeight_ + fontLeading_ / 2; // Backgtound // Tone int thi = dispCnt - 1; int ty = std::accumulate(rowHeights_.begin(), rowHeights_.begin() + thi, 0); painter.fillRect(0, ty, panelWidth(), rowHeights_[static_cast(thi)], palette_->tnToneBackColor); // Noise int nhi = dispCnt - 2; int ny = std::accumulate(rowHeights_.begin(), rowHeights_.begin() + nhi, 0); painter.fillRect(0, ny, panelWidth(), rowHeights_[static_cast(nhi)], palette_->tnNoiseBackColor); // Row label painter.setPen(palette_->instSeqTagColor); if (isLabelOmitted_ && !labels_.empty()) { painter.drawText(1, rowHeights_.front() + textOffset, labels_[static_cast(upperRow_)]); int c = dispCnt / 2; painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + c + 1, 0) + textOffset, labels_[static_cast(upperRow_ - c)]); int l = dispCnt - 3; painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + l + 1, 0) + textOffset, labels_[static_cast(upperRow_ - l)]); } else { int l = dispCnt - 2; for (int i = 0; i < l; ++i) { painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + i + 1, 0) + textOffset, labels_[static_cast(upperRow_ - i)]); } } // Noise painter.setPen(palette_->tnNoiseTextColor); int n = dispCnt - 2; painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + n + 1, 0) + textOffset, labels_[static_cast(1)]); // Tone painter.setPen(palette_->tnToneTextColor); int t = dispCnt - 1; painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + t + 1, 0) + textOffset, labels_[static_cast(0)]); for (size_t i = 1; i < cols_.size(); i += 2) { painter.fillRect(labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + static_cast(i), 0), 0, colWidths_[i], std::accumulate(rowHeights_.begin(), rowHeights_.begin() + dispCnt - 2, 0), palette_->instSeqOddColColor); } // Sequence painter.setPen(palette_->instSeqCellTextColor); for (size_t i = 0; i < cols_.size(); ++i) { int v = cols_[i].row; int x = labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + static_cast(i), 0); if (!v || (32 < v && v < 65)) { // Tone painter.fillRect(x, ty, colWidths_[i], rowHeights_[static_cast(thi)], palette_->tnToneCellColor); painter.drawText(x + 2, ty + rowHeights_[static_cast(thi)] + textOffset, cols_[i].text); } if (0 < v && v < 65) { // Noise painter.fillRect(x, ny, colWidths_[i], rowHeights_[static_cast(nhi)], palette_->tnNoiseCellColor); painter.drawText(x + 2, ny + rowHeights_[static_cast(nhi)] + textOffset, cols_[i].text); // Noise period int r = (v - 1) % 32 + 2; if (upperRow_ >= r && r > upperRow_ - dispCnt + 2) { int hi = upperRow_ - r; int y = std::accumulate(rowHeights_.begin(), rowHeights_.begin() + hi, 0); painter.fillRect(x, y, colWidths_[i], rowHeights_[static_cast(hi)], palette_->instSeqCellColor); painter.drawText(x + 2, y + rowHeights_[static_cast(hi)] + textOffset, cols_[i].text); } } } if (hovCol_ >= 0 && hovRow_ >= 0) { painter.fillRect(labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + hovCol_, 0), std::accumulate(rowHeights_.begin(), rowHeights_.begin() + hovRow_, 0), colWidths_[static_cast(hovCol_)], rowHeights_[static_cast(hovRow_)], palette_->instSeqHovColor); } } int ToneNoiseMacroEditor::detectRowNumberForMouseEvent(int col, int internalRow) const { int r = upperRow_ - internalRow; int b = upperRow_ - getDisplayedRowCount() + 1; int v = cols_.at(static_cast(col)).row; if (r == b) { // Tone if (!v) return 65; // Tone to None else if (v == 65) return 0; // None to Tone else if (v < 33) return v + 32; // Noise to Tone+Noise else return v - 32; // Tone+Noise to Noise } else if (r == b + 1) { // Noise if (!v) return 33; // Tone to Tone+Noise(0) else if (v == 65) return 1; // None to Noise(0) else if (v < 33) return 65; // Noise to None else return 0; // Tone+Noise to Tone } else { // Noise period r -= 2; // Set noise period if (0 < v && v < 33) return 1 + r; // Noise else return 33 + r; // Tone+Noise } } int ToneNoiseMacroEditor::maxInMML() const { return 66; } QString ToneNoiseMacroEditor::convertSequenceDataUnitToMML(Column col) { if (col.row == 0) return "t"; else { int p = (col.row - 1) % 32; if (col.row == 65) return "u"; else if (col.row < 33) return QString("%1n").arg(p); else return QString("%1tn").arg(p); } } bool ToneNoiseMacroEditor::interpretDataInMML(QString &text, int &cnt, std::vector &column) { // Tone+Noise QRegularExpressionMatch m = QRegularExpression("^(\\d+)(nt|tn)").match(text); if (m.hasMatch()) { int p = m.captured(1).toInt(); if (p < 0 || 31 < p) return false; column.push_back({ 33 + p, -1, "" }); ++cnt; text.remove(QRegularExpression("^\\d+(nt|tn)")); return true; } // Noise m = QRegularExpression("^(\\d+)n").match(text); if (m.hasMatch()) { int p = m.captured(1).toInt(); if (p < 0 || 31 < p) return false; column.push_back({ 1 + p, -1, "" }); ++cnt; text.remove(QRegularExpression("^\\d+n")); return true; } // Noise m = QRegularExpression(R"(^(\d+)?t)").match(text); if (m.hasMatch()) { column.push_back({ 0, -1, "" }); ++cnt; text.remove(QRegularExpression("^(\\d+)?t")); return true; } // None if (text.startsWith("u")) { column.push_back({ 65, -1, "" }); ++cnt; text.remove(0, 1); return true; } return false; } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/tone_noise_macro_editor.hpp000066400000000000000000000035561401124043500307160ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TONENOISEMACROEDITOR_HPP #define TONENOISEMACROEDITOR_HPP #include "visualized_instrument_macro_editor.hpp" class ToneNoiseMacroEditor final : public VisualizedInstrumentMacroEditor { Q_OBJECT // Only change drawing process. // The values of ``cols_`` is left as raw values. // For example, the value of "Tone+Noise 2" is 35. public: explicit ToneNoiseMacroEditor(QWidget *parent = nullptr); ~ToneNoiseMacroEditor() override; protected: void drawField() override; int detectRowNumberForMouseEvent(int col, int internalRow) const override; int maxInMML() const override; QString convertSequenceDataUnitToMML(Column col) override; bool interpretDataInMML(QString &text, int &cnt, std::vector &column) override; }; #endif // TONENOISEMACROEDITOR_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.cpp000066400000000000000000000763161401124043500332220ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "visualized_instrument_macro_editor.hpp" #include "ui_visualized_instrument_macro_editor.h" #include #include #include #include #include #include #include #include #include #include #include #include "gui/event_guard.hpp" #include "enum_hash.hpp" VisualizedInstrumentMacroEditor::VisualizedInstrumentMacroEditor(QWidget *parent) : QWidget(parent), font_(QApplication::font()), met_(font_), maxDispRowCnt_(0), upperRow_(-1), defaultRow_(0), hovRow_(-1), hovCol_(-1), type_(SequenceType::PlainSequence), permittedReleaseType_(PermittedReleaseFlag::FIXED_RELEASE), isLabelOmitted_(false), release_(InstrumentSequenceRelease::NoRelease), ui(new Ui::VisualizedInstrumentMacroEditor), pressRow_(-1), pressCol_(-1), grabLoop_(-1), isGrabLoopHead_(false), isGrabRelease_(false), isMultiReleaseState_(false), mmlBase_(0), isIgnoreEvent_(false) { ui->setupUi(this); /* Font */ font_.setPointSize(10); // Check font size #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) fontWidth_ = met_.horizontalAdvance('0'); #else fontWidth_ = met_.width('0'); #endif fontAscend_ = met_.ascent(); fontHeight_ = met_.height(); fontLeading_ = met_.leading(); /* Width & height */ autoFitLabelWidth(); ui->panel->setAttribute(Qt::WA_Hover); ui->verticalScrollBar->setVisible(false); ui->panel->installEventFilter(this); } VisualizedInstrumentMacroEditor::~VisualizedInstrumentMacroEditor() { delete ui; } void VisualizedInstrumentMacroEditor::setColorPalette(std::shared_ptr palette) { palette_ = palette; } void VisualizedInstrumentMacroEditor::AddRow(QString label, bool fitLabelWidth) { labels_.push_back(label); if (static_cast(labels_.size()) <= maxDispRowCnt_) { upperRow_ = static_cast(labels_.size()) - 1; ui->verticalScrollBar->setVisible(false); ui->verticalScrollBar->setMaximum(0); } else { ui->verticalScrollBar->setVisible(true); int max = static_cast(labels_.size()) - maxDispRowCnt_; ui->verticalScrollBar->setMaximum(max); ui->verticalScrollBar->setValue(max); } if (fitLabelWidth) autoFitLabelWidth(); updateRowHeight(); } void VisualizedInstrumentMacroEditor::setMaximumDisplayedRowCount(int count) { maxDispRowCnt_ = count; if (static_cast(labels_.size()) <= maxDispRowCnt_) { upperRow_ = static_cast(labels_.size()) - 1; ui->verticalScrollBar->setVisible(false); ui->verticalScrollBar->setMaximum(0); } else { ui->verticalScrollBar->setVisible(true); int max = static_cast(labels_.size()) - maxDispRowCnt_; ui->verticalScrollBar->setMaximum(max); ui->verticalScrollBar->setValue(max); } updateRowHeight(); } void VisualizedInstrumentMacroEditor::setDefaultRow(int row) { defaultRow_ = row; } int VisualizedInstrumentMacroEditor::getSequenceLength() const { return static_cast(cols_.size()); } void VisualizedInstrumentMacroEditor::setSequenceData(int row, int col, QString str, int subdata) { size_t idx = static_cast(col); cols_.at(idx).row = row; cols_.at(idx).text = str; cols_.at(idx).data = subdata; ui->panel->update(); printMML(); emit sequenceDataChanged(row, col); } void VisualizedInstrumentMacroEditor::setText(int col, QString text) { cols_.at(static_cast(col)).text = text; } void VisualizedInstrumentMacroEditor::setSubdata(int col, int subdata) { cols_.at(static_cast(col)).data = subdata; printMML(); } int VisualizedInstrumentMacroEditor::getSequenceAt(int col) const { return cols_.at(static_cast(col)).row; } int VisualizedInstrumentMacroEditor::getSequenceDataAt(int col) const { return cols_.at(static_cast(col)).data; } void VisualizedInstrumentMacroEditor::setMultipleReleaseState(bool enabled) { isMultiReleaseState_ = enabled; } void VisualizedInstrumentMacroEditor::addSequenceData(int row, QString str, int subdata) { cols_.push_back({ row, subdata, str }); updateColumnWidth(); ui->panel->update(); ui->colSizeLabel->setText(tr("Size: %1").arg(cols_.size())); printMML(); emit sequenceDataAdded(row, static_cast(cols_.size()) - 1); } void VisualizedInstrumentMacroEditor::removeSequenceData() { if (cols_.size() == 1) return; cols_.pop_back(); // Modify loop for (size_t i = 0; i < loops_.size();) { if (loops_[i].begin >= static_cast(cols_.size())) { loops_.erase(loops_.begin() + static_cast(i)); } else { if (loops_[i].end >= static_cast(cols_.size())) loops_[i].end = static_cast(cols_.size()) - 1; ++i; } } // Modify release if (release_.getBeginPos() >= static_cast(cols_.size())) release_.disable(); updateColumnWidth(); ui->panel->update(); ui->colSizeLabel->setText(tr("Size: %1").arg(cols_.size())); printMML(); emit sequenceDataRemoved(); } void VisualizedInstrumentMacroEditor::addLoop(int begin, int end, int times) { int inx = 0; for (size_t i = 0; i < loops_.size(); ++i) { if (loops_[i].begin > begin) { break; } ++inx; } loops_.insert(loops_.begin() + inx, { begin, end, times }); printMML(); emit loopAdded(InstrumentSequenceLoop(begin, end, times)); } void VisualizedInstrumentMacroEditor::setSequenceType(SequenceType type) { type_ = type; updateLabels(); printMML(); } void VisualizedInstrumentMacroEditor::setPermittedReleaseTypes(int types) { permittedReleaseType_ = types; } void VisualizedInstrumentMacroEditor::setRelease(const InstrumentSequenceRelease& release) { release_ = release; printMML(); } void VisualizedInstrumentMacroEditor::clearData() { cols_.clear(); loops_.clear(); release_.disable(); updateColumnWidth(); printMML(); } void VisualizedInstrumentMacroEditor::clearRow() { labels_.clear(); autoFitLabelWidth(); } void VisualizedInstrumentMacroEditor::setUpperRow(int row) { upperRow_ = row; int pos = upperRow_ + 1 - getDisplayedRowCount(); ui->panel->update(); ui->verticalScrollBar->setValue(ui->verticalScrollBar->maximum() - pos); } void VisualizedInstrumentMacroEditor::setLabel(int row, QString text) { labels_.at(static_cast(row)) = text; autoFitLabelWidth(); ui->panel->update(); } void VisualizedInstrumentMacroEditor::clearAllLabelText() { std::fill(labels_.begin(), labels_.end(), ""); autoFitLabelWidth(); ui->panel->update(); } void VisualizedInstrumentMacroEditor::setLabelDiaplayMode(bool isOmitted) { isLabelOmitted_ = isOmitted; ui->panel->update(); } void VisualizedInstrumentMacroEditor::autoFitLabelWidth() { #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) int w = met_.horizontalAdvance(VisualizedInstrumentMacroEditor::tr("Release") + " "); for (auto& lab : labels_) w = std::max(w, met_.horizontalAdvance(lab + " ")); #else int w = met_.width(VisualizedInstrumentMacroEditor::tr("Release") + " "); for (auto& lab : labels_) w = std::max(w, met_.width(lab + " ")); #endif labWidth_ = w; } /******************************/ void VisualizedInstrumentMacroEditor::initDisplay() { pixmap_ = QPixmap(ui->panel->geometry().size()); } void VisualizedInstrumentMacroEditor::drawField() { QPainter painter(&pixmap_); painter.setFont(font_); int textOffset = fontAscend_ - fontHeight_ + fontLeading_ / 2; // Row label painter.setPen(palette_->instSeqTagColor); int dispCnt = getDisplayedRowCount(); if (isLabelOmitted_ && !labels_.empty()) { painter.drawText(1, rowHeights_.front() + textOffset, labels_[static_cast(upperRow_)]); int c = dispCnt / 2; painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + c + 1, 0) + textOffset, labels_[static_cast(upperRow_ - c)]); int l = dispCnt - 1; painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + l + 1, 0) + textOffset, labels_[static_cast(upperRow_ - l)]); } else { for (int i = 0; i < dispCnt; ++i) { painter.drawText(1, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + i + 1, 0) + textOffset, labels_[static_cast(upperRow_ - i)]); } } for (size_t i = 1; i < cols_.size(); i += 2) { painter.fillRect(labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + static_cast(i), 0), 0, colWidths_[i], fieldHeight_, palette_->instSeqOddColColor); } // Sequence painter.setPen(palette_->instSeqCellTextColor); for (size_t i = 0; i < cols_.size(); ++i) { if (upperRow_ >= cols_[i].row && cols_[i].row > upperRow_ - dispCnt) { int x = labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + static_cast(i), 0); int hi = upperRow_ - cols_[i].row; int y = std::accumulate(rowHeights_.begin(), rowHeights_.begin() + hi, 0); painter.fillRect(x, y, colWidths_[i], rowHeights_[static_cast(hi)], palette_->instSeqCellColor); painter.drawText(x + 2, y + rowHeights_[static_cast(upperRow_ - cols_[i].row)] + textOffset, cols_[i].text); } } if (hovCol_ >= 0 && hovRow_ >= 0) { painter.fillRect(labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + hovCol_, 0), std::accumulate(rowHeights_.begin(), rowHeights_.begin() + hovRow_, 0), colWidths_[static_cast(hovCol_)], rowHeights_[static_cast(hovRow_)], palette_->instSeqHovColor); } } void VisualizedInstrumentMacroEditor::drawLoop() { QPainter painter(&pixmap_); painter.setFont(font_); painter.fillRect(0, loopY_, panelWidth(), fontHeight_, palette_->instSeqLoopBackColor); painter.setPen(palette_->instSeqLoopTextColor); painter.drawText(1, loopBaseY_, tr("Loop")); int w = labWidth_; int seqLen = static_cast(cols_.size()); for (int i = 0; i < seqLen; ++i) { for (size_t j = 0; j < loops_.size(); ++j) { if (loops_[j].begin <= i && i <= loops_[j].end) { int colWidth = colWidths_[static_cast(i)]; painter.fillRect(w, loopY_, colWidth, fontHeight_, palette_->instSeqLoopColor); if (loops_[j].begin == i) { painter.fillRect(w, loopY_, 2, fontHeight_, palette_->instSeqLoopEdgeColor); QString times = (loops_[j].times == 1) ? "" : QString::number(loops_[j].times); painter.drawText(QRectF(w + 2, loopY_, colWidth - 2, fontHeight_), Qt::AlignLeft + Qt::AlignVCenter, tr("Loop %1").arg(times)); } if (loops_[j].end == i) { painter.fillRect(w + colWidth - 2, loopY_, 2, fontHeight_, palette_->instSeqLoopEdgeColor); } } } if (hovRow_ == -2 && hovCol_ == i) painter.fillRect(w, loopY_, colWidths_[static_cast(i)], fontHeight_, palette_->instSeqHovColor); w += colWidths_[static_cast(i)]; } } void VisualizedInstrumentMacroEditor::drawRelease() { QPainter painter(&pixmap_); painter.setFont(font_); painter.fillRect(0, releaseY_, panelWidth(), fontHeight_, palette_->instSeqReleaseBackColor); painter.setPen(palette_->instSeqReleaseTextColor); painter.drawText(1, releaseBaseY_, tr("Release")); static const std::unordered_map DISP_TEXT_MAP = { { InstrumentSequenceRelease::NoRelease, "" }, { InstrumentSequenceRelease::FixedRelease, tr("Fixed") }, { InstrumentSequenceRelease::AbsoluteRelease, tr("Absolute") }, { InstrumentSequenceRelease::RelativeRelease, tr("Relative") } }; int w = labWidth_; int seqLen = static_cast(cols_.size()); for (int i = 0; i < seqLen; ++i) { if (release_.getBeginPos() == i) { painter.fillRect(w, releaseY_, panelWidth() - w, fontHeight_, palette_->instSeqReleaseColor); painter.fillRect(w, releaseY_, 2, fontHeight_, palette_->instSeqReleaseEdgeColor); painter.setPen(palette_->instSeqReleaseTextColor); painter.drawText(w + 2, releaseBaseY_, DISP_TEXT_MAP.at(release_.getType())); } if (hovRow_ == -3 && hovCol_ == i) painter.fillRect(w, releaseY_, colWidths_[static_cast(i)], fontHeight_, palette_->instSeqHovColor); w += colWidths_[static_cast(i)]; } } void VisualizedInstrumentMacroEditor::drawBorder() { QPainter painter(&pixmap_); painter.setPen(palette_->instSeqBorderColor); painter.drawLine(labWidth_, 0, labWidth_, ui->panel->geometry().height()); for (int i = 1; i < maxDispRowCnt_; ++i) { painter.drawLine(labWidth_, std::accumulate(rowHeights_.begin(), rowHeights_.begin() + i, 0), panelWidth(), std::accumulate(rowHeights_.begin(), rowHeights_.begin() + i, 0)); } } void VisualizedInstrumentMacroEditor::drawShadow() { QPainter painter(&pixmap_); painter.fillRect(0, 0, panelWidth(), ui->panel->geometry().height(), palette_->instSeqMaskColor); } void VisualizedInstrumentMacroEditor::updateLabels() { } void VisualizedInstrumentMacroEditor::printMML() { if (cols_.empty()) return; QString text = ""; std::vector lstack; static const std::unordered_map DISP_REL_MAP = { { InstrumentSequenceRelease::NoRelease, "" }, { InstrumentSequenceRelease::FixedRelease, "| " }, { InstrumentSequenceRelease::AbsoluteRelease, "/ " }, { InstrumentSequenceRelease::RelativeRelease, ": " } }; int seqLen = static_cast(cols_.size()); for (int cnt = 0; cnt < seqLen; ++cnt) { if (release_.getBeginPos() == cnt) { text += DISP_REL_MAP.at(release_.getType()); } for (size_t i = 0; i < loops_.size(); ++i) { if (loops_[i].begin == cnt) { lstack.push_back(loops_[i]); text += "[ "; } else if (loops_[i].begin > cnt) { break; } } text += (convertSequenceDataUnitToMML(cols_[static_cast(cnt)]) + " "); while (!lstack.empty()) { if (lstack.back().end == cnt) { text += "]"; if (lstack.back().times > 1) { text += QString::number(lstack.back().times); } text += " "; lstack.pop_back(); } else { break; } } } ui->lineEdit->setText(text); } QString VisualizedInstrumentMacroEditor::convertSequenceDataUnitToMML(Column col) { return QString::number(col.row + mmlBase_); } void VisualizedInstrumentMacroEditor::interpretMML() { if (cols_.empty()) return; QString text = ui->lineEdit->text(); std::vector column; std::vector loop; InstrumentSequenceLoopRoot loopRoot(1); std::deque lbstack; std::deque loopStack; InstrumentSequenceRelease release(InstrumentSequenceRelease::NoRelease); std::vector lstack; int cnt = 0; while (!text.isEmpty()) { QRegularExpressionMatch m = QRegularExpression("^\\[").match(text); if (m.hasMatch()) { loop.push_back({ cnt, cnt, 1 }); lstack.push_back(loop.size() - 1); lbstack.push_back(cnt); text.remove(QRegularExpression("^\\[")); continue; } m = QRegularExpression("^\\](\\d*)").match(text); if (m.hasMatch()) { if (lstack.empty() || cnt == 0) return; loop[lstack.back()].end = cnt - 1; if (!m.captured(1).isEmpty()) { int t = m.captured(1).toInt(); if (t > 1) loop[lstack.back()].times = t; else return; } lstack.pop_back(); if (lbstack.empty() || lbstack.back() == cnt) return; if (!m.captured(1).isEmpty()) { int t = m.captured(1).toInt(); if (t > 1) loopStack.push_back(InstrumentSequenceLoop(lbstack.back(), cnt - 1, t)); else return; } else { loopStack.push_back(InstrumentSequenceLoop(lbstack.back(), cnt - 1)); } lbstack.pop_back(); text.remove(QRegularExpression("^\\]\\d*")); continue; } if (permittedReleaseType_ & PermittedReleaseFlag::FIXED_RELEASE) { m = QRegularExpression("^\\|").match(text); if (m.hasMatch()) { if (release.isEnabled()) return; release.setType(InstrumentSequenceRelease::FixedRelease); release.setBeginPos(cnt); text.remove(QRegularExpression("^\\|")); continue; } } if (permittedReleaseType_ & PermittedReleaseFlag::ABSOLUTE_RELEASE) { m = QRegularExpression("^/").match(text); if (m.hasMatch()) { if (release.isEnabled()) return; release.setType(InstrumentSequenceRelease::AbsoluteRelease); release.setBeginPos(cnt); text.remove(QRegularExpression("^/")); continue; } } if (permittedReleaseType_ & PermittedReleaseFlag::RELATIVE_RELEASE) { m = QRegularExpression("^:").match(text); if (m.hasMatch()) { if (release.isEnabled()) return; release.setType(InstrumentSequenceRelease::RelativeRelease); release.setBeginPos(cnt); text.remove(QRegularExpression("^:")); continue; } } if (interpretSlopeInMML(text, cnt, column)) { // Slope loopRoot.resize(cnt); continue; } if (interpretDataInMML(text, cnt, column)) { // Data loopRoot.resize(cnt); continue; } m = QRegularExpression("^ +").match(text); if (m.hasMatch()) { text.remove(QRegularExpression("^ +")); continue; } return; } if (column.empty()) return; if (!lstack.empty()) return; if (!lbstack.empty()) return; if (release.isEnabled() && release.getBeginPos() >= static_cast(column.size())) return; while (cols_.size() > 1) removeSequenceData(); setSequenceData(column.front().row, 0); for (size_t i = 1; i < column.size(); ++i) { addSequenceData(column[i].row); } loops_ = loop; emit loopCleared(); // Reverse order because deepest is first in for (auto itr = loopStack.crbegin(); itr != loopStack.crend(); ++itr) { emit loopAdded(*itr); } release_ = release; emit releaseChanged(release); ui->panel->update(); } bool VisualizedInstrumentMacroEditor::interpretSlopeInMML(QString& text, int& cnt, std::vector& column) { QRegularExpressionMatch m = QRegularExpression("^(?-?\\d+) *_ *(?-?\\d+)( *_ *(?-?\\d+))?").match(text); if (m.hasMatch()) { int start = m.captured("start").toInt(); int end = m.captured("end").toInt(); QString diffStr = m.captured("diff"); if (start < 0 || maxInMML() <= start || end < 0 || maxInMML() <= end || start == end) return false; int diff; int d = start; if (start < end) { if (diffStr.isEmpty()) { diff = 1; } else { diff = diffStr.toInt(); if (diff <= 0) return false; } for (bool flag = true; flag ; ) { if (end <= d) { flag = false; d = end; } column.push_back({ d, -1, "" }); d += diff; ++cnt; } } else { if (diffStr.isEmpty()) { diff = -1; } else { diff = diffStr.toInt(); if (0 <= diff) return false; } for (bool flag = true; flag ; ) { if (d <= end) { flag = false; d = end; } column.push_back({ d, -1, "" }); d += diff; ++cnt; } } text.remove(QRegularExpression("^-?\\d+ *_ *-?\\d+( *_ *-?\\d+)?")); return true; } return false; } bool VisualizedInstrumentMacroEditor::interpretDataInMML(QString& text, int& cnt, std::vector& column) { QRegularExpressionMatch m = QRegularExpression("^(-?\\d+)").match(text); if (m.hasMatch()) { int d = m.captured(1).toInt() - mmlBase_; if (d < 0 || maxInMML() <= d) return false; column.push_back({ d, -1, "" }); ++cnt; text.remove(QRegularExpression("^-?\\d+")); return true; } return false; } int VisualizedInstrumentMacroEditor::checkLoopRegion(int col) { int ret = -1; for (size_t i = 0; i < loops_.size(); ++i) { if (loops_[i].begin <= col) { if (loops_[i].end >= col) { ret = static_cast(i); } } else { break; } } return ret; } void VisualizedInstrumentMacroEditor::moveLoop() { if (hovCol_ < 0) return; size_t glabIdxU = static_cast(grabLoop_); auto& tgtLoopRef = loops_[glabIdxU]; int prevBegin = tgtLoopRef.begin; int prevEnd = tgtLoopRef.end; if (isGrabLoopHead_) { if (hovCol_ < prevBegin) { if (glabIdxU && hovCol_ <= loops_[glabIdxU - 1].end) { tgtLoopRef.begin = loops_[glabIdxU - 1].end + 1; } else { tgtLoopRef.begin = hovCol_; } } else if (prevBegin < hovCol_) { if (prevEnd < hovCol_) { loops_.erase(loops_.begin() + grabLoop_); emit loopRemoved(prevBegin, prevEnd); printMML(); return; } else { tgtLoopRef.begin = hovCol_; } } else return; } else { if (hovCol_ < prevEnd) { if (hovCol_ < prevBegin) { loops_.erase(loops_.begin() + grabLoop_); emit loopRemoved(prevBegin, prevEnd); printMML(); return; } else { tgtLoopRef.end = hovCol_; } } else if (prevEnd < hovCol_) { if (glabIdxU < loops_.size() - 1 && loops_[glabIdxU + 1].begin <= hovCol_) { tgtLoopRef.end = loops_[glabIdxU + 1].begin - 1; } else { tgtLoopRef.end = hovCol_; } } else return; } emit loopChanged(prevBegin, prevEnd, InstrumentSequenceLoop(tgtLoopRef.begin, tgtLoopRef.end, tgtLoopRef.times)); printMML(); } void VisualizedInstrumentMacroEditor::setMMLDisplay0As(int n) { mmlBase_ = n; } int VisualizedInstrumentMacroEditor::panelWidth() const { return ui->panel->geometry().width(); } /********** Events **********/ bool VisualizedInstrumentMacroEditor::eventFilter(QObject*object, QEvent* event) { if (object == ui->panel) { switch (event->type()) { case QEvent::Paint: paintEventInView(dynamic_cast(event)); return false; case QEvent::Resize: resizeEventInView(dynamic_cast(event)); return false; case QEvent::MouseButtonPress: if (isEnabled()) mousePressEventInView(dynamic_cast(event)); return false; case QEvent::MouseButtonDblClick: if (isEnabled()) mousePressEventInView(dynamic_cast(event)); return false; case QEvent::MouseButtonRelease: if (isEnabled()) mouseReleaseEventInView(dynamic_cast(event)); return false; case QEvent::MouseMove: if (isEnabled()) mouseMoveEventInView(); return true; case QEvent::HoverMove: mouseHoverdEventInView(dynamic_cast(event)); return false; case QEvent::Leave: leaveEventInView(); return false; case QEvent::Wheel: wheelEventInView(dynamic_cast(event)); return false; default: return false; } } return QWidget::eventFilter(object, event); } void VisualizedInstrumentMacroEditor::paintEventInView(QPaintEvent* event) { if (!palette_) return; pixmap_.fill(Qt::black); drawField(); drawLoop(); drawRelease(); drawBorder(); if (!isEnabled()) drawShadow(); QPainter painter(ui->panel); painter.drawPixmap(event->rect(), pixmap_, event->rect()); } void VisualizedInstrumentMacroEditor::resizeEventInView(QResizeEvent* event) { Q_UNUSED(event) updateRowHeight(); updateColumnWidth(); releaseY_ = ui->panel->geometry().height() - fontHeight_; releaseBaseY_ = releaseY_ + fontAscend_ + fontLeading_ / 2; loopY_ = releaseY_ - fontHeight_; loopBaseY_ = releaseBaseY_ - fontHeight_; fieldHeight_ = loopY_; initDisplay(); } int VisualizedInstrumentMacroEditor::detectRowNumberForMouseEvent(int col, int internalRow) const { Q_UNUSED(col) return upperRow_ - internalRow; } void VisualizedInstrumentMacroEditor::mousePressEventInView(QMouseEvent* event) { if (!cols_.size()) return; pressRow_ = hovRow_; pressCol_ = hovCol_; // Check grab int x = event->pos().x(); if (hovRow_ == -2) { if (event->button() == Qt::LeftButton) { int seqLen = static_cast(cols_.size()); for (int col = 0, w = labWidth_; col < seqLen; ++col) { if (w - 4 < x && x < w + 4) { for (size_t i = 0; i < loops_.size(); ++i) { if (loops_[i].begin == col) { grabLoop_ = static_cast(i); isGrabLoopHead_ = true; } else if (loops_[i].begin > col) { break; } } } else if (w + colWidths_[static_cast(col)] - 4 < x && x < w + colWidths_[static_cast(col)] + 4) { for (size_t i = 0; i < loops_.size(); ++i) { if (loops_[i].end == col) { grabLoop_ = static_cast(i); isGrabLoopHead_ = false; } else if (loops_[i].end > col) { break; } } } w += colWidths_[static_cast(col)]; } } } else if (hovRow_ == -3 && release_.isEnabled()) { if (event->button() == Qt::LeftButton) { int w = labWidth_ + std::accumulate(colWidths_.begin(), colWidths_.begin() + release_.getBeginPos(), 0); if (w - 4 < x && x < w + 4) { isGrabRelease_ = true; } } } // Press process if (pressCol_ > -1) { if (pressRow_ == -2) { if (grabLoop_ == -1) { int i = checkLoopRegion(pressCol_); switch (event->button()) { case Qt::LeftButton: { if (i == -1) { // New loop addLoop(pressCol_, pressCol_, 1); } else { // Loop count up ++loops_[static_cast(i)].times; printMML(); auto& l = loops_[static_cast(i)]; emit loopChanged(l.begin, l.end, InstrumentSequenceLoop(l.begin, l.end, l.times)); } break; } case Qt::RightButton: { if (i > -1) { // Loop count down if (loops_[static_cast(i)].times > 1) { --loops_[static_cast(i)].times; auto& l = loops_[static_cast(i)]; emit loopChanged(l.begin, l.end, InstrumentSequenceLoop(l.begin, l.end, l.times)); } else { // Erase loop auto& l = loops_[static_cast(i)]; emit loopRemoved(l.begin, l.end); loops_.erase(loops_.begin() + i); } printMML(); } break; } default: break; } } } else if (pressRow_ == -3) { if (!isGrabRelease_) { switch (event->button()) { case Qt::LeftButton: { if (!release_.isEnabled() || pressCol_ < release_.getBeginPos()) { // New release if (!release_.isEnabled()) release_.setType(InstrumentSequenceRelease::FixedRelease); release_.setBeginPos(pressCol_); printMML(); emit releaseChanged(release_); } else if (isMultiReleaseState_) { // Change release type switch (release_.getType()) { case InstrumentSequenceRelease::FixedRelease: release_.setType(InstrumentSequenceRelease::AbsoluteRelease); break; case InstrumentSequenceRelease::AbsoluteRelease: release_.setType(InstrumentSequenceRelease::RelativeRelease); break; case InstrumentSequenceRelease::NoRelease: case InstrumentSequenceRelease::RelativeRelease: release_.setType(InstrumentSequenceRelease::FixedRelease); break; } printMML(); emit releaseChanged(release_); } break; } case Qt::RightButton: { if (pressCol_ >= release_.getBeginPos()) { // Erase release release_.disable(); printMML(); emit releaseChanged(release_); } break; } default: break; } } } else { setSequenceData(detectRowNumberForMouseEvent(hovCol_, hovRow_), hovCol_); prevPressRow_ = hovRow_; prevPressCol_ = hovCol_; } } ui->panel->update(); } void VisualizedInstrumentMacroEditor::mouseReleaseEventInView(QMouseEvent* event) { if (!cols_.size()) return; if (grabLoop_ != -1) { // Move loop if (event->button() == Qt::LeftButton) { moveLoop(); } } else if (isGrabRelease_) { // Move release if (event->button() == Qt::LeftButton) { if (hovCol_ > -1) { release_.setBeginPos(hovCol_); printMML(); emit releaseChanged(release_); } } } pressRow_ = -1; pressCol_ = -1; prevPressRow_ = -1; prevPressCol_ = -1; grabLoop_ = -1; isGrabLoopHead_ = false; isGrabRelease_ = false; ui->panel->update(); } void VisualizedInstrumentMacroEditor::mouseMoveEventInView() { if (!cols_.size()) return; if (pressRow_ >= 0 && pressCol_ >= 0 && hovRow_ >= 0 && hovCol_ >= 0) { if (prevPressRow_ != hovRow_ || prevPressCol_ != hovCol_) { prevPressRow_ = hovRow_; prevPressCol_ = hovCol_; setSequenceData(detectRowNumberForMouseEvent(hovCol_, hovRow_), hovCol_); } } } void VisualizedInstrumentMacroEditor::mouseHoverdEventInView(QHoverEvent* event) { if (!cols_.size()) return; int oldCol = hovCol_; int oldRow = hovRow_; QPoint pos = event->pos(); // Detect column if (pos.x() < labWidth_) { hovCol_ = -2; } else { int seqLen = static_cast(cols_.size()); for (int i = 0, w = labWidth_; i < seqLen; ++i) { w += colWidths_[static_cast(i)]; if (pos.x() < w) { hovCol_ = i; break; } } if (hovCol_ >= seqLen) hovCol_ = -1; // Out of range } // Detect row if (releaseY_ < pos.y()) { hovRow_ = -3; } else if (loopY_ < pos.y()) { hovRow_ = -2; } else { int cnt = getDisplayedRowCount(); for (int i = 0, w = 0; i < cnt; ++i) { w += rowHeights_[static_cast(i)]; if (pos.y() < w) { hovRow_ = i; break; } } } if (hovRow_ != oldRow || hovCol_ != oldCol) ui->panel->update(); } void VisualizedInstrumentMacroEditor::leaveEventInView() { hovRow_ = -1; hovCol_ = -1; ui->panel->update(); } void VisualizedInstrumentMacroEditor::wheelEventInView(QWheelEvent* event) { if (!cols_.size()) return; Ui::EventGuard eg(isIgnoreEvent_); int degree = - event->angleDelta().y() / 8; int pos = ui->verticalScrollBar->value() + degree / 15; int labCnt = static_cast(labels_.size()); if (0 > pos) pos = 0; else if (pos > labCnt - maxDispRowCnt_) pos = labCnt - maxDispRowCnt_; scrollUp(ui->verticalScrollBar->maximum() - pos); ui->panel->update(); ui->verticalScrollBar->setValue(pos); } void VisualizedInstrumentMacroEditor::on_colIncrToolButton_clicked() { addSequenceData(defaultRow_); } void VisualizedInstrumentMacroEditor::on_colDecrToolButton_clicked() { removeSequenceData(); } void VisualizedInstrumentMacroEditor::on_verticalScrollBar_valueChanged(int value) { if (!isIgnoreEvent_) { scrollUp(ui->verticalScrollBar->maximum() - value); ui->panel->update(); } } int VisualizedInstrumentMacroEditor::maxInMML() const { return static_cast(labels_.size()); } void VisualizedInstrumentMacroEditor::on_lineEdit_editingFinished() { interpretMML(); printMML(); } void VisualizedInstrumentMacroEditor::updateColumnWidth() { colWidths_.clear(); if (!cols_.size()) return; float ww = (panelWidth() - labWidth_) / static_cast(cols_.size()); int w = static_cast(ww); float dif = ww - w; float sum = 0; for (size_t i = 0; i < cols_.size(); ++i) { int width = w; sum += dif; if (sum >= 1.0f) { ++width; sum -= 1.0f; } colWidths_.push_back(width); } } void VisualizedInstrumentMacroEditor::updateRowHeight() { rowHeights_.clear(); if (!labels_.size()) return; int div = getDisplayedRowCount(); float hh = (ui->panel->geometry().height() - fontHeight_ * 2) / static_cast(div); int h = static_cast(hh); float dif = hh - h; float sum = 0; for (int i = 0; i < div; ++i) { int height = h; sum += dif; if (sum >= 1.0f) { ++height; sum -= 1.0f; } rowHeights_.push_back(height); } } BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.hpp000066400000000000000000000135041401124043500332150ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef VISUALIZED_INSTRUMENT_MACRO_EDITOR_HPP #define VISUALIZED_INSTRUMENT_MACRO_EDITOR_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "instrument/sequence_property.hpp" #include "gui/color_palette.hpp" namespace Ui { class VisualizedInstrumentMacroEditor; } class VisualizedInstrumentMacroEditor : public QWidget { Q_OBJECT public: explicit VisualizedInstrumentMacroEditor(QWidget *parent = nullptr); ~VisualizedInstrumentMacroEditor() override; void setColorPalette(std::shared_ptr palette); void AddRow(QString label = "", bool fitLabelWidth = true); void setMaximumDisplayedRowCount(int count); void setDefaultRow(int row); int getSequenceLength() const; void setSequenceData(int row, int col, QString str = "", int subdata = -1); void setText(int col, QString text); void setSubdata(int col, int subdata); int getSequenceAt(int col) const; int getSequenceDataAt(int col) const; void setMultipleReleaseState(bool enabled); void addSequenceData(int row, QString str = "", int subdata = -1); void removeSequenceData(); void clearSequenceData(); // NOTE: It is desirable to use loop class void addLoop(int begin, int end, int times); void setSequenceType(SequenceType type); enum PermittedReleaseFlag : int { NO_RELEASE = 0, FIXED_RELEASE = 1, ABSOLUTE_RELEASE = 2, RELATIVE_RELEASE = 4 }; void setPermittedReleaseTypes(int types); void setRelease(const InstrumentSequenceRelease& release); void clearData(); void clearRow(); void setUpperRow(int row); void setLabel(int row, QString text); void clearAllLabelText(); void setLabelDiaplayMode(bool isOmitted); void autoFitLabelWidth(); void setMMLDisplay0As(int n); signals: void sequenceDataChanged(int row, int col); void sequenceDataAdded(int row, int col); void sequenceDataRemoved(); void loopCleared(); void loopAdded(InstrumentSequenceLoop loop); void loopRemoved(int begin, int end); void loopChanged(int prevBegin, int prevEnd, InstrumentSequenceLoop loop); void releaseChanged(InstrumentSequenceRelease release); protected: bool eventFilter(QObject*object, QEvent* event) override; void paintEventInView(QPaintEvent* event); void resizeEventInView(QResizeEvent* event); void mousePressEventInView(QMouseEvent* event); void mouseReleaseEventInView(QMouseEvent* event); void mouseMoveEventInView(); void mouseHoverdEventInView(QHoverEvent* event); void leaveEventInView(); void wheelEventInView(QWheelEvent* event); QPixmap pixmap_; std::shared_ptr palette_; QFont font_; QFontMetrics met_; int fontWidth_, fontHeight_, fontAscend_, fontLeading_; int labWidth_; std::vector rowHeights_, colWidths_; int rowHeight_; int fieldHeight_; int maxDispRowCnt_; int upperRow_, defaultRow_; int hovRow_, hovCol_; virtual void drawField(); virtual void updateLabels(); SequenceType type_; struct Column { int row, data; QString text; }; std::vector labels_; std::vector cols_; int permittedReleaseType_; bool isLabelOmitted_; virtual int detectRowNumberForMouseEvent(int col, int internalRow) const; // NOTE: It is desirable to use loop class struct Loop { int begin, end, times; }; std::vector loops_; InstrumentSequenceRelease release_; void printMML(); virtual QString convertSequenceDataUnitToMML(Column col); virtual int maxInMML() const; void interpretMML(); bool interpretSlopeInMML(QString& text, int& cnt, std::vector& column); virtual bool interpretDataInMML(QString& text, int& cnt, std::vector& column); int panelWidth() const; inline int getDisplayedRowCount() const { int labCnt = static_cast(labels_.size()); return (maxDispRowCnt_ > labCnt) ? labCnt : maxDispRowCnt_; } private slots: void on_colIncrToolButton_clicked(); void on_colDecrToolButton_clicked(); void on_verticalScrollBar_valueChanged(int value); void on_lineEdit_editingFinished(); private: Ui::VisualizedInstrumentMacroEditor *ui; int pressRow_, pressCol_; int prevPressRow_, prevPressCol_; int loopY_, releaseY_; int loopBaseY_, releaseBaseY_; int grabLoop_; bool isGrabLoopHead_; bool isGrabRelease_; bool isMultiReleaseState_; int mmlBase_; bool isIgnoreEvent_; void initDisplay(); void drawLoop(); void drawRelease(); void drawBorder(); void drawShadow(); int checkLoopRegion(int col); void moveLoop(); void updateColumnWidth(); void updateRowHeight(); inline void scrollUp(int pos) { upperRow_ = pos - 1 + getDisplayedRowCount(); } }; #endif // VISUALIZED_INSTRUMENT_MACRO_EDITOR_HPP BambooTracker-0.4.6/BambooTracker/gui/instrument_editor/visualized_instrument_macro_editor.ui000066400000000000000000000040431401124043500330410ustar00rootroot00000000000000 VisualizedInstrumentMacroEditor 0 0 400 300 Form 0 0 0 0 0 Qt::Vertical false true - + Size: 1 BambooTracker-0.4.6/BambooTracker/gui/instrument_selection_dialog.cpp000066400000000000000000000105421401124043500260270ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_selection_dialog.hpp" #include "ui_instrument_selection_dialog.h" #include #include #include "bank.hpp" #include "gui/jam_layout.hpp" InstrumentSelectionDialog::InstrumentSelectionDialog(const AbstractBank &bank, const QString &text, std::weak_ptr config, QWidget *parent) : QDialog(parent), bank_(bank), config_(config), ui_(new Ui::InstrumentSelectionDialog) { ui_->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui_->label->setText(text); setupContents(); ui_->listWidget->installEventFilter(this); } InstrumentSelectionDialog::~InstrumentSelectionDialog() { } void InstrumentSelectionDialog::setupContents() { QListWidget* lw = ui_->listWidget; lw->setSelectionMode(QListWidget::MultiSelection); size_t instCount = bank_.getNumInstruments(); for (size_t i = 0; i < instCount; ++i) { QString id = QString::fromStdString(bank_.getInstrumentIdentifier(i)); QString name = QString::fromStdString(bank_.getInstrumentName(i)); QListWidgetItem* item = new QListWidgetItem(QString("%1 %2").arg(id, name)); item->setData(Qt::UserRole, static_cast(i)); lw->addItem(item); } } QVector InstrumentSelectionDialog::currentInstrumentSelection() const { QListWidget *lw = ui_->listWidget; const QList items = lw->selectedItems(); QVector selection; selection.reserve(items.size()); for (const QListWidgetItem* item : items) { size_t index = static_cast(item->data(Qt::UserRole).toULongLong()); selection.push_back(index); } return selection; } void InstrumentSelectionDialog::onJamKeyOnByMidi(int key) { auto item = ui_->listWidget->currentItem(); if (item) emit jamKeyOnMidiEvent(static_cast(item->data(Qt::UserRole).toULongLong()), key); } void InstrumentSelectionDialog::onJamKeyOffByMidi(int key) { auto item = ui_->listWidget->currentItem(); if (item) emit jamKeyOffMidiEvent(key); } bool InstrumentSelectionDialog::eventFilter(QObject* watched, QEvent* event) { Q_UNUSED(watched) // Only list widget auto item = ui_->listWidget->currentItem(); if (!item) return false; size_t n = static_cast(item->data(Qt::UserRole).toULongLong()); if (event->type() == QEvent::KeyPress) { auto ke = reinterpret_cast(event); if (!ke->isAutoRepeat()) { Qt::Key qtKey = static_cast(ke->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOnEvent(n, jk); } catch (std::invalid_argument&) {} } } else if (event->type() == QEvent::KeyRelease) { auto ke = reinterpret_cast(event); if (!ke->isAutoRepeat()) { Qt::Key qtKey = static_cast(ke->key()); try { JamKey jk = getJamKeyFromLayoutMapping(qtKey, config_); emit jamKeyOffEvent(jk); } catch (std::invalid_argument&) {} } } return false; } void InstrumentSelectionDialog::on_searchLineEdit_textChanged(const QString &search) { QListWidget *lw = ui_->listWidget; unsigned count = static_cast(lw->count()); for (unsigned row = 0; row < count; ++row) { QListWidgetItem *item = lw->item(static_cast(row)); bool accept = search.isEmpty() || item->text().contains(search, Qt::CaseInsensitive); item->setHidden(!accept); } } BambooTracker-0.4.6/BambooTracker/gui/instrument_selection_dialog.hpp000066400000000000000000000041671401124043500260420ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "configuration.hpp" class AbstractBank; namespace Ui { class InstrumentSelectionDialog; } class InstrumentSelectionDialog : public QDialog { Q_OBJECT public: InstrumentSelectionDialog(const AbstractBank &bank, const QString &text, std::weak_ptr config, QWidget *parent = nullptr); ~InstrumentSelectionDialog() override; QVector currentInstrumentSelection() const; public slots: void onJamKeyOnByMidi(int key); void onJamKeyOffByMidi(int key); signals: void jamKeyOnEvent(size_t id, JamKey key); void jamKeyOnMidiEvent(size_t id, int key); void jamKeyOffEvent(JamKey key); void jamKeyOffMidiEvent(int key); protected: bool eventFilter(QObject* watched, QEvent* event) override; private: const AbstractBank &bank_; std::weak_ptr config_; std::unique_ptr ui_; void setupContents(); private slots: void on_searchLineEdit_textChanged(const QString &search); }; BambooTracker-0.4.6/BambooTracker/gui/instrument_selection_dialog.ui000066400000000000000000000035251401124043500256650ustar00rootroot00000000000000 InstrumentSelectionDialog 0 0 400 300 Select instruments Search... Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() InstrumentSelectionDialog accept() 248 254 157 274 buttonBox rejected() InstrumentSelectionDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/jam_layout.hpp000066400000000000000000000044121401124043500224030ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef JAM_LAYOUT_HPP #define JAM_LAYOUT_HPP #include #include #include #include "configuration.hpp" #include "jamming.hpp" #include "utils.hpp" // Layout decipherer inline JamKey getJamKeyFromLayoutMapping(Qt::Key key, std::weak_ptr config) { std::shared_ptr configLocked = config.lock(); Configuration::KeyboardLayout selectedLayout = configLocked->getNoteEntryLayout(); if (configLocked->mappingLayouts.find(selectedLayout) != configLocked->mappingLayouts.end()) { std::unordered_map selectedLayoutMapping = configLocked->mappingLayouts.at(selectedLayout); auto it = utils::findIf(selectedLayoutMapping, [key](const std::pair& t) -> bool { return (QKeySequence(key).matches(QKeySequence(QString::fromStdString(t.first))) == QKeySequence::ExactMatch); }); if (it != selectedLayoutMapping.end()) { return it->second; } else { throw std::invalid_argument("Unmapped key"); } //something has gone wrong, current layout has no layout map //TODO: handle cleanly? } else throw std::out_of_range("Unmapped Layout"); } #endif // JAM_LAYOUT_HPP BambooTracker-0.4.6/BambooTracker/gui/keyboard_shortcut_list_dialog.cpp000066400000000000000000000027461401124043500263470ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "keyboard_shortcut_list_dialog.hpp" #include "ui_keyboard_shortcut_list_dialog.h" KeyboardShortcutListDialog::KeyboardShortcutListDialog(QWidget *parent) : QDialog(parent), ui(new Ui::KeyboardShortcutListDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); } KeyboardShortcutListDialog::~KeyboardShortcutListDialog() { delete ui; } BambooTracker-0.4.6/BambooTracker/gui/keyboard_shortcut_list_dialog.hpp000066400000000000000000000030061401124043500263420ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KEYBOARD_SHORTCUT_LIST_DIALOG_HPP #define KEYBOARD_SHORTCUT_LIST_DIALOG_HPP #include namespace Ui { class KeyboardShortcutListDialog; } class KeyboardShortcutListDialog : public QDialog { Q_OBJECT public: explicit KeyboardShortcutListDialog(QWidget *parent = nullptr); ~KeyboardShortcutListDialog(); private: Ui::KeyboardShortcutListDialog *ui; }; #endif // KEYBOARD_SHORTCUT_LIST_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/keyboard_shortcut_list_dialog.ui000066400000000000000000000032131401124043500261700ustar00rootroot00000000000000 KeyboardShortcutListDialog 0 0 400 300 Keyboard shortcuts qrc:/doc/shortcuts Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() KeyboardShortcutListDialog accept() 248 254 157 274 buttonBox rejected() KeyboardShortcutListDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/labeled_horizontal_slider.cpp000066400000000000000000000102461401124043500254370ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "labeled_horizontal_slider.hpp" #include "ui_labeled_horizontal_slider.h" #include #include "slider_style.hpp" LabeledHorizontalSlider::LabeledHorizontalSlider(QWidget *parent) : QFrame(parent), ui(new Ui::LabeledHorizontalSlider) { init("", "", ""); } LabeledHorizontalSlider::LabeledHorizontalSlider(QString text, QString prefix, QString suffix, QWidget *parent) : QFrame(parent), ui(new Ui::LabeledHorizontalSlider) { init(text, prefix, suffix); } void LabeledHorizontalSlider::init(QString text, QString prefix, QString suffix) { ui->setupUi(this); rate_ = 1.0; precision_ = 0; isSigned_ = false; ui->textLabel->setText(text); prefix_ = prefix; suffix_ = suffix; updateValueLabel(); ui->slider->setStyle(new SliderStyle()); ui->slider->installEventFilter(this); } LabeledHorizontalSlider::~LabeledHorizontalSlider() { delete ui; } int LabeledHorizontalSlider::value() const { return ui->slider->value(); } void LabeledHorizontalSlider::setValue(int value) { ui->slider->setValue(value); } int LabeledHorizontalSlider::maximum() const { return ui->slider->maximum(); } void LabeledHorizontalSlider::setMaximum(int value) { ui->slider->setMaximum(value); } int LabeledHorizontalSlider::minimum() const { return ui->slider->minimum(); } void LabeledHorizontalSlider::setMinimum(int value) { ui->slider->setMinimum(value); } void LabeledHorizontalSlider::setValueRate(double rate, int precision) { rate_ = rate; precision_ = precision; updateValueLabel(); } void LabeledHorizontalSlider::setSign(bool enabled) { isSigned_ = enabled; updateValueLabel(); } void LabeledHorizontalSlider::setTickPosition(QSlider::TickPosition position) { ui->slider->setTickPosition(position); } void LabeledHorizontalSlider::setTickInterval(int ti) { ui->slider->setTickInterval(ti); } QString LabeledHorizontalSlider::text() const { return ui->textLabel->text(); } void LabeledHorizontalSlider::setText(QString text) { ui->textLabel->setText(text); } QString LabeledHorizontalSlider::suffix() const { return suffix_; } void LabeledHorizontalSlider::setSuffix(QString suffix) { suffix_ = suffix; updateValueLabel(); } QString LabeledHorizontalSlider::prefix() const { return prefix_; } void LabeledHorizontalSlider::setprefix(QString prefix) { prefix_ = prefix; updateValueLabel(); } bool LabeledHorizontalSlider::eventFilter(QObject* watched, QEvent* event) { if (watched == ui->slider) { if (event->type() == QEvent::Wheel) { auto e = dynamic_cast(event); if (e->angleDelta().y() > 0) ui->slider->setValue(ui->slider->value() + 1); else if (e->angleDelta().y() < 0) ui->slider->setValue(ui->slider->value() - 1); return true; } } return QFrame::eventFilter(watched, event); } void LabeledHorizontalSlider::on_slider_valueChanged(int value) { updateValueLabel(); emit valueChanged(value); } void LabeledHorizontalSlider::updateValueLabel() { QString sign = (isSigned_ && ui->slider->value() > -1) ? "+" : ""; ui->valueLabel->setText( prefix_ + sign + QString::number(ui->slider->value() * rate_, 'f', precision_) + suffix_); } BambooTracker-0.4.6/BambooTracker/gui/labeled_horizontal_slider.hpp000066400000000000000000000047211401124043500254450ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef LABALED_HORIZONTAL_SLIDER_HPP #define LABALED_HORIZONTAL_SLIDER_HPP #include #include #include #include namespace Ui { class LabeledHorizontalSlider; } class LabeledHorizontalSlider : public QFrame { Q_OBJECT public: explicit LabeledHorizontalSlider(QWidget *parent = nullptr); LabeledHorizontalSlider(QString text, QString prefix = "", QString suffix = "", QWidget *parent = nullptr); ~LabeledHorizontalSlider() override; int value() const; void setValue(int value); int maximum() const; void setMaximum(int value); int minimum() const; void setMinimum(int value); void setValueRate(double rate, int precision = 1); void setSign(bool enabled); void setTickPosition(QSlider::TickPosition position); void setTickInterval(int ti); QString text() const; void setText(QString text); QString suffix() const; void setSuffix(QString suffix); QString prefix() const; void setprefix(QString prefix); signals: void valueChanged(int value); protected: bool eventFilter(QObject* watched, QEvent* event) override; private slots: void on_slider_valueChanged(int value); private: Ui::LabeledHorizontalSlider *ui; QString suffix_, prefix_; double rate_; int precision_; bool isSigned_; void init(QString text, QString prefix, QString suffix); void updateValueLabel(); }; #endif // LABALED_HORIZONTAL_SLIDER_HPP BambooTracker-0.4.6/BambooTracker/gui/labeled_horizontal_slider.ui000066400000000000000000000027411401124043500252730ustar00rootroot00000000000000 LabeledHorizontalSlider 0 0 136 28 Frame LabeledHorizontalSlider { border: 0; } 3 0 0 0 0 Text Qt::Horizontal 0 BambooTracker-0.4.6/BambooTracker/gui/labeled_vertical_slider.cpp000066400000000000000000000101521401124043500250530ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "labeled_vertical_slider.hpp" #include "ui_labeled_vertical_slider.h" #include #include "slider_style.hpp" LabeledVerticalSlider::LabeledVerticalSlider(QWidget *parent) : QFrame(parent), ui(new Ui::LabeledVerticalSlider) { init("", "", ""); } LabeledVerticalSlider::LabeledVerticalSlider(QString text, QString prefix, QString suffix, QWidget *parent) : QFrame(parent), ui(new Ui::LabeledVerticalSlider) { init(text, prefix, suffix); } void LabeledVerticalSlider::init(QString text, QString prefix, QString suffix) { ui->setupUi(this); rate_ = 1.0; precision_ = 0; isSigned_ = false; ui->textLabel->setText(text); prefix_ = prefix; suffix_ = suffix; updateValueLabel(); ui->slider->setStyle(new SliderStyle()); ui->slider->installEventFilter(this); } LabeledVerticalSlider::~LabeledVerticalSlider() { delete ui; } int LabeledVerticalSlider::value() const { return ui->slider->value(); } void LabeledVerticalSlider::setValue(int value) { ui->slider->setValue(value); } int LabeledVerticalSlider::maximum() const { return ui->slider->maximum(); } void LabeledVerticalSlider::setMaximum(int value) { ui->slider->setMaximum(value); } int LabeledVerticalSlider::minimum() const { return ui->slider->minimum(); } void LabeledVerticalSlider::setMinimum(int value) { ui->slider->setMinimum(value); } void LabeledVerticalSlider::setValueRate(double rate, int precision) { rate_ = rate; precision_ = precision; updateValueLabel(); } void LabeledVerticalSlider::setSign(bool enabled) { isSigned_ = enabled; updateValueLabel(); } void LabeledVerticalSlider::setTickPosition(QSlider::TickPosition position) { ui->slider->setTickPosition(position); } void LabeledVerticalSlider::setTickInterval(int ti) { ui->slider->setTickInterval(ti); } QString LabeledVerticalSlider::text() const { return ui->textLabel->text(); } void LabeledVerticalSlider::setText(QString text) { ui->textLabel->setText(text); } QString LabeledVerticalSlider::suffix() const { return suffix_; } void LabeledVerticalSlider::setSuffix(QString suffix) { suffix_ = suffix; updateValueLabel(); } QString LabeledVerticalSlider::prefix() const { return prefix_; } void LabeledVerticalSlider::setprefix(QString prefix) { prefix_ = prefix; updateValueLabel(); } bool LabeledVerticalSlider::eventFilter(QObject* watched, QEvent* event) { if (watched == ui->slider) { if (event->type() == QEvent::Wheel) { auto e = dynamic_cast(event); if (e->angleDelta().y() > 0) ui->slider->setValue(ui->slider->value() + 1); else if (e->angleDelta().y() < 0) ui->slider->setValue(ui->slider->value() - 1); return true; } } return QFrame::eventFilter(watched, event); } void LabeledVerticalSlider::on_slider_valueChanged(int value) { updateValueLabel(); emit valueChanged(value); } void LabeledVerticalSlider::updateValueLabel() { QString sign = (isSigned_ && ui->slider->value() > -1) ? "+" : ""; ui->valueLabel->setText( prefix_ + sign + QString::number(ui->slider->value() * rate_, 'f', precision_) + suffix_); } BambooTracker-0.4.6/BambooTracker/gui/labeled_vertical_slider.hpp000066400000000000000000000046771401124043500250770ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef LABELED_VERTICAL_SLIDER_HPP #define LABELED_VERTICAL_SLIDER_HPP #include #include #include #include namespace Ui { class LabeledVerticalSlider; } class LabeledVerticalSlider : public QFrame { Q_OBJECT public: explicit LabeledVerticalSlider(QWidget *parent = nullptr); LabeledVerticalSlider(QString text, QString prefix = "", QString suffix = "", QWidget *parent = nullptr); ~LabeledVerticalSlider() override; int value() const; void setValue(int value); int maximum() const; void setMaximum(int value); int minimum() const; void setMinimum(int value); void setValueRate(double rate, int precision = 1); void setSign(bool enabled); void setTickPosition(QSlider::TickPosition position); void setTickInterval(int ti); QString text() const; void setText(QString text); QString suffix() const; void setSuffix(QString suffix); QString prefix() const; void setprefix(QString prefix); signals: void valueChanged(int value); protected: bool eventFilter(QObject* watched, QEvent* event) override; private slots: void on_slider_valueChanged(int value); private: Ui::LabeledVerticalSlider *ui; QString suffix_, prefix_; double rate_; int precision_; bool isSigned_; void init(QString text, QString prefix, QString suffix); void updateValueLabel(); }; #endif // LABELED_VERTICAL_SLIDER_HPP BambooTracker-0.4.6/BambooTracker/gui/labeled_vertical_slider.ui000066400000000000000000000031401401124043500247050ustar00rootroot00000000000000 LabeledVerticalSlider 0 0 36 115 Frame LabeledVerticalSlider { border: 0; } 3 0 0 0 0 Text Qt::AlignCenter Qt::Vertical 0 Qt::AlignCenter BambooTracker-0.4.6/BambooTracker/gui/mainwindow.cpp000066400000000000000000004026521401124043500224160ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "mainwindow.hpp" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "jamming.hpp" #include "song.hpp" #include "track.hpp" #include "instrument.hpp" #include "bank.hpp" #include "io/io_file_type.hpp" #include "io/module_io.hpp" #include "io/instrument_io.hpp" #include "io/bank_io.hpp" #include "io/binary_container.hpp" #include "io/wav_container.hpp" #include "version.hpp" #include "gui/command/instrument/instrument_commands_qt.hpp" #include "gui/instrument_editor/instrument_editor_fm_form.hpp" #include "gui/instrument_editor/instrument_editor_ssg_form.hpp" #include "gui/instrument_editor/instrument_editor_adpcm_form.hpp" #include "gui/instrument_editor/instrument_editor_drumkit_form.hpp" #include "gui/module_properties_dialog.hpp" #include "gui/groove_settings_dialog.hpp" #include "gui/configuration_dialog.hpp" #include "gui/wave_export_settings_dialog.hpp" #include "gui/vgm_export_settings_dialog.hpp" #include "gui/instrument_selection_dialog.hpp" #include "gui/s98_export_settings_dialog.hpp" #include "gui/configuration_handler.hpp" #include "gui/jam_layout.hpp" #include "chip/scci/SCCIDefines.hpp" #include "chip/c86ctl/c86ctl_wrapper.hpp" #include "gui/file_history_handler.hpp" #include "midi/midi.hpp" #include "audio/audio_stream_rtaudio.hpp" #include "color_palette_handler.hpp" #include "enum_hash.hpp" #include "gui/go_to_dialog.hpp" #include "gui/transpose_song_dialog.hpp" #include "gui/swap_tracks_dialog.hpp" #include "gui/hide_tracks_dialog.hpp" #include "gui/track_visibility_memory_handler.hpp" #include "gui/file_io_error_message_box.hpp" #include "gui/gui_utils.hpp" #include "utils.hpp" namespace { const std::unordered_map TB_POS_ = { { Configuration::ToolbarPosition::TopPosition, Qt::TopToolBarArea }, { Configuration::ToolbarPosition::BottomPosition, Qt::BottomToolBarArea }, { Configuration::ToolbarPosition::LeftPosition, Qt::LeftToolBarArea }, { Configuration::ToolbarPosition::RightPosition, Qt::RightToolBarArea } }; } MainWindow::MainWindow(std::weak_ptr config, QString filePath, QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), config_(config), palette_(std::make_shared()), bt_(std::make_shared(config)), comStack_(std::make_shared(this)), fileHistory_(std::make_shared()), scciDll_(std::make_unique("scci")), c86ctlDll_(std::make_unique("c86ctl")), instForms_(std::make_shared()), renamingInstItem_(nullptr), renamingInstEdit_(nullptr), isModifiedForNotCommand_(false), hasLockedWigets_(false), isEditedPattern_(true), isEditedOrder_(false), isEditedInstList_(false), isSelectedPattern_(false), isSelectedOrder_(false), hasShownOnce_(false), firstViewUpdateRequest_(false), octUpSc_(nullptr), octDownSc_(nullptr), focusPtnSc_(this), focusOdrSc_(this), focusInstSc_(this), playAndStopSc_(nullptr), playStepSc_(nullptr), goPrevOdrSc_(nullptr), goNextOdrSc_(nullptr), prevInstSc_(nullptr), nextInstSc_(nullptr), incPtnSizeSc_(nullptr), decPtnSizeSc_(nullptr), incEditStepSc_(nullptr), decEditStepSc_(nullptr), prevSongSc_(nullptr), nextSongSc_(nullptr), jamVolUpSc_(nullptr), jamVolDownSc_(nullptr), bankJamMidiCtrl_(false) { ui->setupUi(this); if (config.lock()->getMainWindowX() == -1) { // When unset QRect rec = geometry(); rec.moveCenter(QGuiApplication::screens().at(0)->geometry().center()); setGeometry(rec); config.lock()->setMainWindowX(x()); config.lock()->setMainWindowY(y()); } else { move(config.lock()->getMainWindowX(), config.lock()->getMainWindowY()); } resize(config.lock()->getMainWindowWidth(), config.lock()->getMainWindowHeight()); if (config.lock()->getMainWindowMaximized()) showMaximized(); ui->action_Status_Bar->setChecked(config.lock()->getVisibleStatusBar()); ui->statusBar->setVisible(config.lock()->getVisibleStatusBar()); ui->actionFollow_Mode->setChecked(config.lock()->getFollowMode()); ui->action_Instrument_Mask->setChecked(config.lock()->getInstrumentMask()); ui->action_Volume_Mask->setChecked(config.lock()->getVolumeMask()); ui->action_Wave_View->setChecked(config.lock()->getVisibleWaveView()); ui->waveVisual->setVisible(config.lock()->getVisibleWaveView()); bt_->setFollowPlay(config.lock()->getFollowMode()); if (config.lock()->getPatternEditorHeaderFont().empty()) { config.lock()->setPatternEditorHeaderFont(ui->patternEditor->getHeaderFont().toStdString()); } if (config.lock()->getPatternEditorRowsFont().empty()) { config.lock()->setPatternEditorRowsFont(ui->patternEditor->getRowsFont().toStdString()); } if (config.lock()->getOrderListHeaderFont().empty()) { config.lock()->setOrderListHeaderFont(ui->orderList->getHeaderFont().toStdString()); } if (config.lock()->getOrderListRowsFont().empty()) { config.lock()->setOrderListRowsFont(ui->orderList->getRowsFont().toStdString()); } ui->patternEditor->setConfiguration(config_.lock()); ui->orderList->setConfiguration(config_.lock()); updateFonts(); ui->orderList->setHorizontalScrollMode(config.lock()->getMoveCursorByHorizontalScroll(), false); ui->patternEditor->setHorizontalScrollMode(config.lock()->getMoveCursorByHorizontalScroll(), false); ui->patternEditor->setCore(bt_); ui->orderList->setCore(bt_); ColorPaletteHandler::loadPalette(palette_.get()); ui->patternEditor->setColorPallete(palette_); ui->orderList->setColorPallete(palette_); updateInstrumentListColors(); ui->waveVisual->setColorPalette(palette_); /* Command stack */ QObject::connect(comStack_.get(), &QUndoStack::indexChanged, this, [&](int idx) { setWindowModified(idx || isModifiedForNotCommand_); ui->actionUndo->setEnabled(comStack_->canUndo()); ui->actionRedo->setEnabled(comStack_->canRedo()); }); /* File history */ FileHistoryHandler::loadFileHistory(fileHistory_); for (size_t i = 0; i < fileHistory_->size(); ++i) { // Leave Before Qt5.7.0 style due to windows xp QAction* action = ui->menu_Recent_Files->addAction(QString("&%1 %2").arg(i + 1).arg(fileHistory_->at(i))); action->setData(fileHistory_->at(i)); } QObject::connect(ui->menu_Recent_Files, &QMenu::triggered, this, [&](QAction* action) { if (action != ui->actionClear) { if (isWindowModified()) { QString modTitle = gui_utils::utf8ToQString(bt_->getModuleTitle()); if (modTitle.isEmpty()) modTitle = tr("Untitled"); QMessageBox dialog(QMessageBox::Warning, "BambooTracker", tr("Save changes to %1?").arg(modTitle), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); switch (dialog.exec()) { case QMessageBox::Yes: if (!on_actionSave_triggered()) return; break; case QMessageBox::No: break; case QMessageBox::Cancel: return; default: break; } } openModule(action->data().toString()); } }); /* Menus */ pasteModeGroup_ = std::make_unique(this); pasteModeGroup_->addAction(ui->action_Cursor); QObject::connect(ui->action_Cursor, &QAction::triggered, this, [&](bool checked) { if (checked) config_.lock()->setPasteMode(Configuration::PasteMode::Cursor); }); pasteModeGroup_->addAction(ui->action_Selection); QObject::connect(ui->action_Selection, &QAction::triggered, this, [&](bool checked) { if (checked) config_.lock()->setPasteMode(Configuration::PasteMode::Selection); }); pasteModeGroup_->addAction(ui->action_Fill); QObject::connect(ui->action_Fill, &QAction::triggered, this, [&](bool checked) { if (checked) config_.lock()->setPasteMode(Configuration::PasteMode::Fill); }); switch (config.lock()->getPasteMode()) { case Configuration::PasteMode::Cursor: ui->action_Cursor->setChecked(true); break; case Configuration::PasteMode::Selection: ui->action_Selection->setChecked(true); break; case Configuration::PasteMode::Fill: ui->action_Fill->setChecked(true); break; } /* Tool bars */ auto octLab = new QLabel(tr("Octave")); octLab->setMargin(6); ui->subToolBar->addWidget(octLab); octave_ = new QSpinBox(); octave_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); octave_->setMaximumWidth(80); octave_->setMinimum(0); octave_->setMaximum(7); octave_->setValue(bt_->getCurrentOctave()); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(octave_, static_cast(&QSpinBox::valueChanged), this, [&](int octave) { bt_->setCurrentOctave(octave); }); ui->subToolBar->addWidget(octave_); auto volLab = new QLabel(tr("Volume")); volLab->setMargin(6); ui->subToolBar->addWidget(volLab); volume_ = new QSpinBox(); volume_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); volume_->setMaximumWidth(100); volume_->setMinimum(0); volume_->setMaximum(255); volume_->setDisplayIntegerBase(16); volume_->setValue(bt_->getCurrentVolume()); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(volume_, static_cast(&QSpinBox::valueChanged), this, [&](int volume) { bt_->setCurrentVolume(volume); }); ui->subToolBar->addWidget(volume_); ui->subToolBar->addSeparator(); ui->subToolBar->addAction(ui->actionFollow_Mode); ui->subToolBar->addSeparator(); auto hlLab1 = new QLabel(tr("Step highlight 1st")); hlLab1->setMargin(6); ui->subToolBar->addWidget(hlLab1); highlight1_ = new QSpinBox(); highlight1_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); highlight1_->setMaximumWidth(80); highlight1_->setMinimum(1); highlight1_->setMaximum(256); highlight1_->setValue(8); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(highlight1_, static_cast(&QSpinBox::valueChanged), this, [&](int count) { bt_->setModuleStepHighlight1Distance(static_cast(count)); ui->patternEditor->setPatternHighlight1Count(count); }); ui->subToolBar->addWidget(highlight1_); auto hlLab2 = new QLabel(tr("2nd")); hlLab2->setMargin(6); ui->subToolBar->addWidget(hlLab2); highlight2_ = new QSpinBox(); highlight2_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); highlight2_->setMaximumWidth(80); highlight2_->setMinimum(1); highlight2_->setMaximum(256); highlight2_->setValue(8); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(highlight2_, static_cast(&QSpinBox::valueChanged), this, [&](int count) { bt_->setModuleStepHighlight2Distance(static_cast(count)); ui->patternEditor->setPatternHighlight2Count(count); }); ui->subToolBar->addWidget(highlight2_); ui->subToolBar->addSeparator(); auto& mainTbConfig = config.lock()->getMainToolbarConfiguration(); if (mainTbConfig.getPosition() == Configuration::ToolbarPosition::FloatPorition) { ui->mainToolBar->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); ui->mainToolBar->move(mainTbConfig.getX(), mainTbConfig.getY()); } else { addToolBar(TB_POS_.at(mainTbConfig.getPosition()), ui->mainToolBar); } auto& subTbConfig = config.lock()->getSubToolbarConfiguration(); if (subTbConfig.getPosition() == Configuration::ToolbarPosition::FloatPorition) { ui->subToolBar->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); ui->subToolBar->move(subTbConfig.getX(), subTbConfig.getY()); } else { auto pos = TB_POS_.at(subTbConfig.getPosition()); if (subTbConfig.getNumber()) { if (subTbConfig.hasBreakBefore()) addToolBarBreak(pos); addToolBar(pos, ui->subToolBar); } else { if (mainTbConfig.getPosition() == subTbConfig.getPosition()) { insertToolBar(ui->mainToolBar, ui->subToolBar); if (mainTbConfig.hasBreakBefore()) insertToolBarBreak(ui->mainToolBar); } else { addToolBar(pos, ui->subToolBar); } } } ui->action_Toolbar->setChecked(config.lock()->getVisibleToolbar()); ui->mainToolBar->setVisible(config.lock()->getVisibleToolbar()); ui->subToolBar->setVisible(config.lock()->getVisibleToolbar()); /* Splitter */ ui->splitter->setStretchFactor(0, 0); ui->splitter->setStretchFactor(1, 1); /* Module settings */ QObject::connect(ui->modTitleLineEdit, &QLineEdit::textEdited, this, [&](QString str) { bt_->setModuleTitle(str.toUtf8().toStdString()); setModifiedTrue(); setWindowTitle(); }); QObject::connect(ui->authorLineEdit, &QLineEdit::textEdited, this, [&](QString str) { bt_->setModuleAuthor(str.toUtf8().toStdString()); setModifiedTrue(); }); QObject::connect(ui->copyrightLineEdit, &QLineEdit::textEdited, this, [&](QString str) { bt_->setModuleCopyright(str.toUtf8().toStdString()); setModifiedTrue(); }); /* Edit settings */ // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->editableStepSpinBox, static_cast(&QSpinBox::valueChanged), this, [&](int n) { ui->patternEditor->setEditableStep(n); config_.lock()->setEditableStep(static_cast(n)); }); ui->editableStepSpinBox->setValue(static_cast(config.lock()->getEditableStep())); ui->patternEditor->setEditableStep(static_cast(config.lock()->getEditableStep())); ui->keyRepeatCheckBox->setCheckState(config.lock()->getKeyRepetition() ? Qt::Checked : Qt::Unchecked); /* Song */ // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->songComboBox, static_cast(&QComboBox::currentIndexChanged), this, [&](int num) { if (num == -1) return; freezeViews(); if (!tickTimerForRealChip_) stream_->stop(); bt_->setCurrentSongNumber(num); loadSong(); if (!tickTimerForRealChip_) stream_->start(); }); /* Song settings */ // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->tempoSpinBox, static_cast(&QSpinBox::valueChanged), this, [&](int tempo) { int curSong = bt_->getCurrentSongNumber(); if (tempo != bt_->getSongTempo(curSong)) { bt_->setSongTempo(curSong, tempo); setModifiedTrue(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->speedSpinBox, static_cast(&QSpinBox::valueChanged), this, [&](int speed) { int curSong = bt_->getCurrentSongNumber(); if (speed != bt_->getSongSpeed(curSong)) { bt_->setSongSpeed(curSong, speed); setModifiedTrue(); } }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->patternSizeSpinBox, static_cast(&QSpinBox::valueChanged), this, [&](int size) { bt_->setDefaultPatternSize(bt_->getCurrentSongNumber(), static_cast(size)); ui->patternEditor->onDefaultPatternSizeChanged(); setModifiedTrue(); }); // Leave Before Qt5.7.0 style due to windows xp QObject::connect(ui->grooveSpinBox, static_cast(&QSpinBox::valueChanged), this, [&](int n) { bt_->setSongGroove(bt_->getCurrentSongNumber(), n); setModifiedTrue(); }); /* Instrument list */ auto instToolBar = new QToolBar(); instToolBar->setIconSize(QSize(16, 16)); auto addMenu = new QMenu(); addMenu->addActions({ ui->actionNew_Instrument, ui->actionNew_Drumki_t }); auto addTB = new QToolButton(); addTB->setPopupMode(QToolButton::MenuButtonPopup); addTB->setMenu(addMenu); addTB->setDefaultAction(ui->actionNew_Instrument); instToolBar->addWidget(addTB); instToolBar->addActions({ ui->actionRemove_Instrument, ui->actionClone_Instrument }); instToolBar->addSeparator(); instToolBar->addActions({ ui->actionLoad_From_File, ui->actionSave_To_File }); instToolBar->addSeparator(); instToolBar->addAction(ui->actionEdit); instToolBar->addSeparator(); instToolBar->addAction(ui->actionRename_Instrument); ui->instrumentListGroupBox->layout()->addWidget(instToolBar); ui->instrumentList->installEventFilter(this); QObject::connect(ui->instrumentList, &DropDetectListWidget::itemDroppedAtItemIndex, this, &MainWindow::swapInstruments); /* Pattern editor */ ui->patternEditor->setCommandStack(comStack_); ui->patternEditor->installEventFilter(this); QObject::connect(ui->patternEditor, &PatternEditor::currentTrackChanged, ui->orderList, &OrderListEditor::onPatternEditorCurrentTrackChanged); QObject::connect(ui->patternEditor, &PatternEditor::currentOrderChanged, ui->orderList, &OrderListEditor::onPatternEditorCurrentOrderChanged); QObject::connect(ui->patternEditor, &PatternEditor::focusIn, this, &MainWindow::updateMenuByPattern); QObject::connect(ui->patternEditor, &PatternEditor::selected, this, &MainWindow::updateMenuByPatternSelection); QObject::connect(ui->patternEditor, &PatternEditor::instrumentEntered, this, [&](int num) { auto list = ui->instrumentList; if (num != -1) { for (int i = 0; i < list->count(); ++i) { if (list->item(i)->data(Qt::UserRole).toInt() == num) { list->setCurrentRow(i); return ; } } } }); QObject::connect(ui->patternEditor, &PatternEditor::volumeEntered, this, [&](int volume) { volume_->setValue(volume); }); QObject::connect(ui->patternEditor, &PatternEditor::effectEntered, this, [&](QString text) { statusDetail_->setText(text); }); QObject::connect(ui->patternEditor, &PatternEditor::currentTrackChanged, this, &MainWindow::onCurrentTrackChanged); /* Order List */ ui->orderList->setCommandStack(comStack_); ui->orderList->installEventFilter(this); QObject::connect(ui->orderList, &OrderListEditor::currentTrackChanged, ui->patternEditor, &PatternEditor::onOrderListCurrentTrackChanged); QObject::connect(ui->orderList, &OrderListEditor::currentOrderChanged, ui->patternEditor, &PatternEditor::onOrderListCrrentOrderChanged); QObject::connect(ui->orderList, &OrderListEditor::orderEdited, ui->patternEditor, &PatternEditor::onOrderListEdited); QObject::connect(ui->orderList, &OrderListEditor::focusIn, this, &MainWindow::updateMenuByOrder); QObject::connect(ui->orderList, &OrderListEditor::selected, this, &MainWindow::updateMenuByOrderSelection); QObject::connect(ui->orderList, &OrderListEditor::currentTrackChanged, this, &MainWindow::onCurrentTrackChanged); /* Wave view */ visualTimer_.reset(new QTimer); QObject::connect(visualTimer_.get(), &QTimer::timeout, this, &MainWindow::updateVisuals); if (config.lock()->getVisibleWaveView()) visualTimer_->start(static_cast(std::round(1000. / config.lock()->getWaveViewFrameRate()))); /* Status bar */ statusDetail_ = new QLabel(); statusDetail_->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); statusStyle_ = new QLabel(); statusStyle_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); statusInst_ = new QLabel(); statusInst_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); statusOctave_ = new QLabel(); statusOctave_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); statusIntr_ = new QLabel(); statusIntr_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); statusMixer_ = new QLabel(); statusMixer_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); statusBpm_ = new QLabel(); statusBpm_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); statusPlayPos_ = new QLabel(); statusPlayPos_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); ui->statusBar->addWidget(statusDetail_, 4); ui->statusBar->addPermanentWidget(statusStyle_, 1); ui->statusBar->addPermanentWidget(statusInst_, 1); ui->statusBar->addPermanentWidget(statusOctave_, 1); ui->statusBar->addPermanentWidget(statusIntr_, 1); ui->statusBar->addPermanentWidget(statusMixer_, 1); ui->statusBar->addPermanentWidget(statusBpm_, 1); ui->statusBar->addPermanentWidget(statusPlayPos_, 1); statusOctave_->setText(tr("Octave: %1").arg(bt_->getCurrentOctave())); statusIntr_->setText(QString::number(bt_->getModuleTickFrequency()) + QString("Hz")); /* Bookmark */ bmManForm_ = std::make_unique(bt_, config_.lock()->getShowRowNumberInHex()); QObject::connect(bmManForm_.get(), &BookmarkManagerForm::positionJumpRequested, this, [&](int order, int step) { if (bt_->isPlaySong()) return; int song = bt_->getCurrentSongNumber(); if (static_cast(bt_->getOrderSize(song)) <= order) return; if (static_cast(bt_->getPatternSizeFromOrderNumber(song, order)) <= step) return; bt_->setCurrentOrderNumber(order); bt_->setCurrentStepNumber(step); ui->orderList->updatePositionByPositionJump(); ui->patternEditor->updatepositionByPositionJump(); activateWindow(); }); QObject::connect(bmManForm_.get(), &BookmarkManagerForm::modified, this, &MainWindow::setModifiedTrue); // Shortcuts auto linkShortcut = [&](QAction* ptr) { ptr->setShortcutContext(Qt::WidgetWithChildrenShortcut); ui->orderList->addAction(ptr); ui->patternEditor->addAction(ptr); }; linkShortcut(&octUpSc_); QObject::connect(&octUpSc_, &QAction::triggered, this, [&] { changeOctave(true); }); linkShortcut(&octDownSc_); QObject::connect(&octDownSc_, &QAction::triggered, this, [&] { changeOctave(false); }); QObject::connect(&focusPtnSc_, &QShortcut::activated, this, [&] { ui->patternEditor->setFocus(); }); QObject::connect(&focusOdrSc_, &QShortcut::activated, this, [&] { ui->orderList->setFocus(); }); QObject::connect(&focusInstSc_, &QShortcut::activated, this, [&] { ui->instrumentList->setFocus(); updateMenuByInstrumentList(); }); auto playLinkShortcut = [&](QAction* ptr) { ptr->setShortcutContext(Qt::WidgetShortcut); ui->instrumentList->addAction(ptr); ui->orderList->addActionToPanel(ptr); ui->patternEditor->addActionToPanel(ptr); }; playLinkShortcut(ui->actionPlay); playLinkShortcut(&playAndStopSc_); QObject::connect(&playAndStopSc_, &QAction::triggered, this, [&] { if (bt_->isPlaySong()) stopPlaySong(); else startPlaySong(); }); playLinkShortcut(&playStepSc_); QObject::connect(&playStepSc_, &QAction::triggered, this, &MainWindow::playStep); playLinkShortcut(ui->actionPlay_From_Start); playLinkShortcut(ui->actionPlay_Pattern); playLinkShortcut(ui->actionPlay_From_Cursor); playLinkShortcut(ui->actionPlay_From_Marker); playLinkShortcut(ui->actionStop); instAddSc_ = std::make_unique(Qt::Key_Insert, ui->instrumentList, nullptr, nullptr, Qt::WidgetShortcut); QObject::connect(instAddSc_.get(), &QShortcut::activated, this, &MainWindow::addInstrument); linkShortcut(&goPrevOdrSc_); QObject::connect(&goPrevOdrSc_, &QAction::triggered, this, [&] { ui->orderList->onGoOrderRequested(false); }); linkShortcut(&goNextOdrSc_); QObject::connect(&goNextOdrSc_, &QAction::triggered, this, [&] { ui->orderList->onGoOrderRequested(true); }); linkShortcut(&prevInstSc_); QObject::connect(&prevInstSc_, &QAction::triggered, this, [&] { if (ui->instrumentList->count()) { int row = ui->instrumentList->currentRow(); if (row == -1) ui->instrumentList->setCurrentRow(0); else if (row > 0) ui->instrumentList->setCurrentRow(row - 1); } }); linkShortcut(&nextInstSc_); QObject::connect(&nextInstSc_, &QAction::triggered, this, [&] { int cnt = ui->instrumentList->count(); if (cnt) { int row = ui->instrumentList->currentRow(); if (row == -1) ui->instrumentList->setCurrentRow(cnt - 1); else if (row < cnt - 1) ui->instrumentList->setCurrentRow(row + 1); } }); linkShortcut(&incPtnSizeSc_); QObject::connect(&incPtnSizeSc_, &QAction::triggered, this, [&] { ui->patternSizeSpinBox->setValue(ui->patternSizeSpinBox->value() + 1); }); linkShortcut(&decPtnSizeSc_); QObject::connect(&decPtnSizeSc_, &QAction::triggered, this, [&] { ui->patternSizeSpinBox->setValue(ui->patternSizeSpinBox->value() - 1); }); linkShortcut(&incEditStepSc_); QObject::connect(&incEditStepSc_, &QAction::triggered, this, [&] { ui->editableStepSpinBox->setValue(ui->editableStepSpinBox->value() + 1); }); linkShortcut(&decEditStepSc_); QObject::connect(&decEditStepSc_, &QAction::triggered, this, [&] { ui->editableStepSpinBox->setValue(ui->editableStepSpinBox->value() - 1); }); linkShortcut(&prevSongSc_); QObject::connect(&prevSongSc_, &QAction::triggered, this, [&] { if (ui->songComboBox->isEnabled()) { ui->songComboBox->setCurrentIndex(std::max(ui->songComboBox->currentIndex() - 1, 0)); } }); linkShortcut(&nextSongSc_); QObject::connect(&nextSongSc_, &QAction::triggered, this, [&] { if (ui->songComboBox->isEnabled()) { ui->songComboBox->setCurrentIndex(std::min(ui->songComboBox->currentIndex() + 1, ui->songComboBox->count() - 1)); } }); linkShortcut(&jamVolUpSc_); QObject::connect(&jamVolUpSc_, &QAction::triggered, this, [&] { volume_->setValue(volume_->value() + 1); }); linkShortcut(&jamVolDownSc_); QObject::connect(&jamVolDownSc_, &QAction::triggered, this, [&] { volume_->setValue(volume_->value() - 1); }); setShortcuts(); /* Clipboard */ QObject::connect(QApplication::clipboard(), &QClipboard::dataChanged, this, [&]() { if (isEditedOrder_) updateMenuByOrder(); else if (isEditedPattern_) updateMenuByPattern(); }); /* MIDI */ setMidiConfiguration(); midiKeyEventMethod_ = metaObject()->indexOfSlot("midiKeyEvent(uchar,uchar,uchar)"); Q_ASSERT(midiKeyEventMethod_ != -1); midiProgramEventMethod_ = metaObject()->indexOfSlot("midiProgramEvent(uchar,uchar)"); Q_ASSERT(midiProgramEventMethod_ != -1); MidiInterface::getInstance().installInputHandler(&midiThreadReceivedEvent, this); /* Audio stream */ stream_ = std::make_shared(); stream_->setTickUpdateCallback(+[](void* cbPtr) -> int { auto bt = reinterpret_cast(cbPtr); return bt->streamCountUp(); }, bt_.get()); stream_->setGenerateCallback(+[](int16_t* container, size_t nSamples, void* cbPtr) { auto bt = reinterpret_cast(cbPtr); bt->getStreamSamples(container, nSamples); }, bt_.get()); QObject::connect(stream_.get(), &AudioStream::streamInterrupted, this, &MainWindow::onNewTickSignaled); QString audioApi = gui_utils::utf8ToQString(config.lock()->getSoundAPI()); if (audioApi.isEmpty()) { // On the first launch bool streamState = false; QString streamErr; for (const QString& audioApi : stream_->getAvailableBackends()) { config.lock()->setSoundAPI(audioApi.toUtf8().toStdString()); QString audioDevice = stream_->getDefaultOutputDevice(audioApi); config.lock()->setSoundDevice(audioDevice.toUtf8().toStdString()); streamState = stream_->initialize( static_cast(bt_->getStreamRate()), static_cast(bt_->getStreamDuration()), bt_->getModuleTickFrequency(), audioApi, audioDevice, &streamErr); if (streamState) break; } if (streamState) { uint32_t sr = stream_->getStreamRate(); if (config.lock()->getSampleRate() != sr) { showStreamRateWarningDialog(sr); bt_->setStreamRate(sr); } } else { showStreamFailedDialog(streamErr); } } else { // Ordinary launch bool savedApiExists = false; const std::vector audioApis = stream_->getAvailableBackends(); for (const QString& api : audioApis) { if (api.toUtf8().toStdString() == config.lock()->getSoundAPI()) { savedApiExists = true; break; } } if (!savedApiExists) { audioApi = audioApis.front(); config.lock()->setSoundAPI(audioApi.toUtf8().toStdString()); } QString audioDevice = gui_utils::utf8ToQString(config.lock()->getSoundDevice()); bool savedDeviceExists = false; for (const QString& device : stream_->getAvailableDevices(audioApi)) { if (device.toUtf8().toStdString() == config.lock()->getSoundDevice()) { savedDeviceExists = true; break; } } if (!savedDeviceExists) { audioDevice = stream_->getDefaultOutputDevice(audioApi); config.lock()->setSoundDevice(audioDevice.toUtf8().toStdString()); } QString streamErr; bool streamState = stream_->initialize( static_cast(bt_->getStreamRate()), static_cast(bt_->getStreamDuration()), bt_->getModuleTickFrequency(), audioApi, audioDevice, &streamErr); if (streamState) { uint32_t sr = stream_->getStreamRate(); if (config.lock()->getSampleRate() != sr) { showStreamRateWarningDialog(sr); bt_->setStreamRate(sr); } } else { showStreamFailedDialog(streamErr); } } RealChipInterface intf = config.lock()->getRealChipInterface(); if (intf == RealChipInterface::NONE) { bt_->useSCCI(nullptr); bt_->useC86CTL(nullptr); } else { tickTimerForRealChip_ = std::make_unique(); tickTimerForRealChip_->setInterval(1000000 / bt_->getModuleTickFrequency()); tickEventMethod_ = metaObject()->indexOfSlot("onNewTickSignaledRealChip()"); Q_ASSERT(tickEventMethod_ != -1); tickTimerForRealChip_->setFunction([&]{ QMetaMethod method = this->metaObject()->method(this->tickEventMethod_); method.invoke(this, Qt::QueuedConnection); }); setRealChipInterface(intf); tickTimerForRealChip_->start(); } /* Load module */ if (filePath.isEmpty()) { loadModule(); setInitialSelectedInstrument(); assignADPCMSamples(); if (!tickTimerForRealChip_) stream_->start(); } else { openModule(filePath); // If use emulation, stream stars } /* Track visibility */ SongType memSongType; std::vector visTracks; if (config.lock()->getRestoreTrackVisibility() && io::loadTrackVisibilityMemory(memSongType, visTracks)) { SongType songType = bt_->getSongStyle(bt_->getCurrentSongNumber()).type; visTracks = gui_utils::adaptVisibleTrackList(visTracks, songType, songType); } else { visTracks.resize(bt_->getSongStyle(0).trackAttribs.size()); std::iota(visTracks.begin(), visTracks.end(), 0); } setTrackVisibility(visTracks); } MainWindow::~MainWindow() { MidiInterface::getInstance().uninstallInputHandler(&midiThreadReceivedEvent, this); stream_->shutdown(); } bool MainWindow::eventFilter(QObject* watched, QEvent* event) { if (auto fmForm = qobject_cast(watched)) { // Change current instrument by activating FM editor if (event->type() == QEvent::WindowActivate) { int row = findRowFromInstrumentList(fmForm->getInstrumentNumber()); ui->instrumentList->setCurrentRow(row); } else if (event->type() == QEvent::Resize) { config_.lock()->setInstrumentFMWindowWidth(fmForm->width()); config_.lock()->setInstrumentFMWindowHeight(fmForm->height()); } return false; } if (auto ssgForm = qobject_cast(watched)) { // Change current instrument by activating SSG editor if (event->type() == QEvent::WindowActivate) { int row = findRowFromInstrumentList(ssgForm->getInstrumentNumber()); ui->instrumentList->setCurrentRow(row); } else if (event->type() == QEvent::Resize) { config_.lock()->setInstrumentSSGWindowWidth(ssgForm->width()); config_.lock()->setInstrumentSSGWindowHeight(ssgForm->height()); } return false; } if (auto adpcmForm = qobject_cast(watched)) { // Change current instrument by activating ADPCM editor if (event->type() == QEvent::WindowActivate) { int row = findRowFromInstrumentList(adpcmForm->getInstrumentNumber()); ui->instrumentList->setCurrentRow(row); } else if (event->type() == QEvent::Resize) { config_.lock()->setInstrumentADPCMWindowWidth(adpcmForm->width()); config_.lock()->setInstrumentADPCMWindowHeight(adpcmForm->height()); } return false; } if (auto kitForm = qobject_cast(watched)) { // Change current instrument by activating drumkit editor if (event->type() == QEvent::WindowActivate) { int row = findRowFromInstrumentList(kitForm->getInstrumentNumber()); ui->instrumentList->setCurrentRow(row); } else if (event->type() == QEvent::Resize) { config_.lock()->setInstrumentDrumkitWindowWidth(kitForm->width()); config_.lock()->setInstrumentDrumkitWindowHeight(kitForm->height()); } return false; } if (watched == ui->instrumentList) { if (event->type() == QEvent::FocusIn) updateMenuByInstrumentList(); return false; } if (watched == renamingInstEdit_) { if (event->type() == QEvent::FocusOut) finishRenamingInstrument(); return false; } return false; } void MainWindow::showEvent(QShowEvent* event) { Q_UNUSED(event) if (!hasShownOnce_) { int y = config_.lock()->getMainWindowVerticalSplit(); if (y == -1) { config_.lock()->setMainWindowVerticalSplit(ui->splitter->sizes().at(0)); } else { ui->splitter->setSizes({ y, ui->splitter->height() - ui->splitter->handleWidth() - y }); } hasShownOnce_ = true; } } void MainWindow::keyPressEvent(QKeyEvent *event) { if (!event->isAutoRepeat()) { // Musical keyboard Qt::Key qtKey = static_cast(event->key()); try { bt_->jamKeyOn(getJamKeyFromLayoutMapping(qtKey, config_), !config_.lock()->getFixJammingVolume()); } catch (std::invalid_argument&) {} } } void MainWindow::keyReleaseEvent(QKeyEvent *event) { int key = event->key(); if (!event->isAutoRepeat()) { // Musical keyboard Qt::Key qtKey = static_cast(key); try { bt_->jamKeyOff(getJamKeyFromLayoutMapping(qtKey, config_)); } catch (std::invalid_argument&) {} } } void MainWindow::dragEnterEvent(QDragEnterEvent* event) { auto mime = event->mimeData(); if (mime->hasUrls() && mime->urls().length() == 1) { const std::string ext = QFileInfo(mime->urls().at(0).toLocalFile()).suffix().toLower().toStdString(); if (io::ModuleIO::getInstance().testLoadableFormat(ext) | io::InstrumentIO::getInstance().testLoadableFormat(ext) | io::BankIO::getInstance().testLoadableFormat(ext)) event->acceptProposedAction(); } } void MainWindow::dropEvent(QDropEvent* event) { QString file = event->mimeData()->urls().first().toLocalFile(); const std::string ext = QFileInfo(file).suffix().toLower().toStdString(); if (io::ModuleIO::getInstance().testLoadableFormat(ext)) { if (isWindowModified()) { QString modTitle = gui_utils::utf8ToQString(bt_->getModuleTitle()); if (modTitle.isEmpty()) modTitle = tr("Untitled"); QMessageBox dialog(QMessageBox::Warning, "BambooTracker", tr("Save changes to %1?").arg(modTitle), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); switch (dialog.exec()) { case QMessageBox::Yes: if (!on_actionSave_triggered()) return; break; case QMessageBox::No: break; case QMessageBox::Cancel: return; default: break; } } bt_->stopPlaySong(); lockWidgets(false); openModule(file); } else if (io::InstrumentIO::getInstance().testLoadableFormat(ext)) { funcLoadInstrument(file); } if (io::BankIO::getInstance().testLoadableFormat(ext)) { funcImportInstrumentsFromBank(file); } } void MainWindow::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); if (!isMaximized()) { // Check previous size config_.lock()->setMainWindowWidth(event->oldSize().width()); config_.lock()->setMainWindowHeight(event->oldSize().height()); } } void MainWindow::moveEvent(QMoveEvent* event) { QWidget::moveEvent(event); if (!isMaximized()) { // Check previous position config_.lock()->setMainWindowX(event->oldPos().x()); config_.lock()->setMainWindowY(event->oldPos().y()); } } void MainWindow::closeEvent(QCloseEvent *event) { if (isWindowModified()) { QString modTitle = gui_utils::utf8ToQString(bt_->getModuleTitle()); if (modTitle.isEmpty()) modTitle = tr("Untitled"); QMessageBox dialog(QMessageBox::Warning, "BambooTracker", tr("Save changes to %1?").arg(modTitle), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); switch (dialog.exec()) { case QMessageBox::Yes: if (!on_actionSave_triggered()) { event->ignore(); return; } break; case QMessageBox::No: break; case QMessageBox::Cancel: event->ignore(); return; default: break; } } if (isMaximized()) { config_.lock()->setMainWindowMaximized(true); } else { config_.lock()->setMainWindowMaximized(false); config_.lock()->setMainWindowWidth(width()); config_.lock()->setMainWindowHeight(height()); config_.lock()->setMainWindowX(x()); config_.lock()->setMainWindowY(y()); } config_.lock()->setMainWindowVerticalSplit(ui->splitter->sizes().at(0)); auto& mainTbConfig = config_.lock()->getMainToolbarConfiguration(); auto& subTbConfig = config_.lock()->getSubToolbarConfiguration(); auto mainTbArea = toolBarArea(ui->mainToolBar); auto subTbArea = toolBarArea(ui->subToolBar); if (ui->mainToolBar->isFloating()) { mainTbConfig.setPosition(Configuration::ToolbarPosition::FloatPorition); mainTbConfig.setX(ui->mainToolBar->x()); mainTbConfig.setY(ui->mainToolBar->y()); } else { mainTbConfig.setPosition(utils::findMapValue(TB_POS_, mainTbArea)->first); mainTbConfig.setNumber(0); mainTbConfig.setBreakBefore(false); } if (ui->subToolBar->isFloating()) { subTbConfig.setPosition(Configuration::ToolbarPosition::FloatPorition); subTbConfig.setX(ui->subToolBar->x()); subTbConfig.setY(ui->subToolBar->y()); } else { subTbConfig.setPosition(utils::findMapValue(TB_POS_, mainTbArea)->first); subTbConfig.setNumber(0); subTbConfig.setBreakBefore(false); } if (mainTbArea == subTbArea) { auto mainPos = ui->mainToolBar->pos(); auto subPos = ui->subToolBar->pos(); switch (mainTbArea) { case Qt::TopToolBarArea: { if (mainPos.x() == subPos.x()) { bool cond = (mainPos.y() < subPos.y()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(!cond); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(cond); } else { bool cond = (mainPos.x() < subPos.x()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(false); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(false); } break; } case Qt::BottomToolBarArea: { if (mainPos.x() == subPos.x()) { bool cond = (subPos.y() < mainPos.y()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(!cond); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(cond); } else { bool cond = (mainPos.x() < subPos.x()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(false); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(false); } break; } case Qt::LeftToolBarArea: { if (mainPos.x() == subPos.x()) { bool cond = (mainPos.y() < subPos.y()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(false); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(false); } else { bool cond = (mainPos.x() < subPos.x()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(!cond); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(cond); } break; } case Qt::RightToolBarArea: { if (mainPos.x() == subPos.x()) { bool cond = (mainPos.y() < subPos.y()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(false); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(false); } else { bool cond = (subPos.x() < mainPos.x()); mainTbConfig.setNumber(cond ? 0 : 1); mainTbConfig.setBreakBefore(!cond); subTbConfig.setNumber(cond ? 1 : 0); subTbConfig.setBreakBefore(cond); } break; } default: break; } } config_.lock()->setVisibleToolbar(ui->mainToolBar->isVisible()); config_.lock()->setVisibleStatusBar(ui->statusBar->isVisible()); config_.lock()->setFollowMode(bt_->isFollowPlay()); instForms_->closeAll(); FileHistoryHandler::saveFileHistory(fileHistory_); io::saveTrackVisibilityMemory( bt_->getSongStyle(bt_->getCurrentSongNumber()).type, ui->patternEditor->getVisibleTracks()); if (effListDiag_) effListDiag_->close(); if (shortcutsDiag_) shortcutsDiag_->close(); bmManForm_->close(); if (commentDiag_) commentDiag_->close(); event->accept(); } void MainWindow::freezeViews() { ui->orderList->freeze(); ui->patternEditor->freeze(); } void MainWindow::unfreezeViews() { ui->orderList->unfreeze(); ui->patternEditor->unfreeze(); } void MainWindow::setShortcuts() { auto shortcuts = config_.lock()->getShortcuts(); octUpSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::OctaveUp))); octDownSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::OctaveDown))); focusPtnSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FocusOnPattern))); focusOdrSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FocusOnOrder))); focusInstSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FocusOnInstrument))); playAndStopSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayAndStop))); ui->actionPlay->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Play))); ui->actionPlay_From_Start->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayFromStart))); ui->actionPlay_Pattern->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayPattern))); ui->actionPlay_From_Cursor->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayFromCursor))); ui->actionPlay_From_Marker->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayFromMarker))); playStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PlayStep))); ui->actionStop->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Stop))); ui->actionEdit_Mode->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleEditJam))); ui->actionSet_Ro_w_Marker->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SetMarker))); ui->actionMix->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteMix))); ui->actionOverwrite->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteOverwrite))); ui->action_Insert->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteInsert))); ui->actionAll->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectAll))); ui->actionNone->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Deselect))); ui->actionRow->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectRow))); ui->actionColumn->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectColumn))); ui->actionPattern->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectColumn))); ui->actionOrder->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectOrder))); ui->action_Go_To->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::GoToStep))); ui->actionToggle_Track->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleTrack))); ui->actionSolo_Track->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SoloTrack))); ui->actionInterpolate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Interpolate))); goPrevOdrSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::GoToPrevOrder))); goNextOdrSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::GoToNextOrder))); ui->action_Toggle_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleBookmark))); ui->action_Previous_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PrevBookmark))); ui->action_Next_Bookmark->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextBookmark))); ui->actionDecrease_Note->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseNote))); ui->actionIncrease_Note->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseNote))); ui->actionDecrease_Octave->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseOctave))); ui->actionIncrease_Octave->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseOctave))); prevInstSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PrevInstrument))); nextInstSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextInstrument))); ui->action_Instrument_Mask->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::MaskInstrument))); ui->action_Volume_Mask->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::MaskVolume))); ui->actionEdit->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::EditInstrument))); ui->actionFollow_Mode->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FollowMode))); ui->actionDuplicate_Order->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DuplicateOrder))); ui->actionClone_Patterns->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ClonePatterns))); ui->actionClone_Order->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CloneOrder))); ui->actionReplace_Instrument->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ReplaceInstrument))); ui->actionExpand->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandPattern))); ui->actionShrink->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkPattern))); ui->actionFine_Decrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineDecreaseValues))); ui->actionFine_Increase_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineIncreaseValues))); ui->actionCoarse_D_ecrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseDecreaseValues))); ui->actionCoarse_I_ncrease_Values->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseIncreaseValuse))); incPtnSizeSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreasePatternSize))); decPtnSizeSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreasePatternSize))); incEditStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseEditStep))); decEditStepSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseEditStep))); ui->action_Effect_List->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DisplayEffectList))); prevSongSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PreviousSong))); nextSongSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextSong))); jamVolUpSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::JamVolumeUp))); jamVolDownSc_.setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::JamVolumeDown))); ui->orderList->onShortcutUpdated(); ui->patternEditor->onShortcutUpdated(); } void MainWindow::setTrackVisibility(const std::vector& visTracks) { ui->orderList->setVisibleTracks(visTracks); setOrderListGroupMaximumWidth(); ui->patternEditor->setVisibleTracks(visTracks); if (config_.lock()->getMuteHiddenTracks()) { int all = static_cast(bt_->getSongStyle(bt_->getCurrentSongNumber()).trackAttribs.size()); for (int i = 0; i < all; ++i) { if (std::none_of(visTracks.begin(), visTracks.end(), [i](const int& n) { return i == n; })) bt_->setTrackMuteState(i, true); } } } void MainWindow::updateInstrumentListColors() { ui->instrumentList->setStyleSheet( QString("QListWidget { color: %1; background: %2; }") .arg(palette_->ilistTextColor.name(QColor::HexArgb), palette_->ilistBackColor.name(QColor::HexArgb)) + QString("QListWidget::item:hover { color: %1; background: %2; }") .arg(palette_->ilistTextColor.name(QColor::HexArgb), palette_->ilistHovBackColor.name(QColor::HexArgb)) + QString("QListWidget::item:selected { color: %1; background: %2; }") .arg(palette_->ilistTextColor.name(QColor::HexArgb), palette_->ilistSelBackColor.name(QColor::HexArgb)) + QString("QListWidget::item:selected:hover { color: %1; background: %2; }") .arg(palette_->ilistTextColor.name(QColor::HexArgb), palette_->ilistHovSelBackColor.name(QColor::HexArgb))); } void MainWindow::setOrderListGroupMaximumWidth() { ui->orderListGroupBox->setMaximumWidth( ui->orderListGroupBox->contentsMargins().left() + ui->orderListGroupBox->layout()->contentsMargins().left() + ui->orderList->maximumWidth() + ui->orderListGroupBox->layout()->contentsMargins().right() + ui->orderListGroupBox->contentsMargins().right()); } /********** MIDI **********/ void MainWindow::midiThreadReceivedEvent(double delay, const uint8_t *msg, size_t len, void *userData) { MainWindow *self = reinterpret_cast(userData); Q_UNUSED(delay) // Note-On/Note-Off if (len == 3 && (msg[0] & 0xe0) == 0x80) { uint8_t status = msg[0]; uint8_t key = msg[1]; uint8_t velocity = msg[2]; QMetaMethod method = self->metaObject()->method(self->midiKeyEventMethod_); method.invoke(self, Qt::QueuedConnection, Q_ARG(uchar, status), Q_ARG(uchar, key), Q_ARG(uchar, velocity)); } // Program change else if (len == 2 && (msg[0] & 0xf0) == 0xc0) { uint8_t status = msg[0]; uint8_t program = msg[1]; QMetaMethod method = self->metaObject()->method(self->midiProgramEventMethod_); method.invoke(self, Qt::QueuedConnection, Q_ARG(uchar, status), Q_ARG(uchar, program)); } } void MainWindow::midiKeyEvent(uchar status, uchar key, uchar velocity) { bool release = ((status & 0xf0) == 0x80) || velocity == 0; int k = static_cast(key) - 12; octave_->setValue(k / 12); if (importBankDiag_) { if (bankJamMidiCtrl_.load()) return; importBankDiag_->onJamKeyOffByMidi(k); if (!release) importBankDiag_->onJamKeyOnByMidi(k); return; } int n = instForms_->checkActivatedFormNumber(); if (n == -1) { bt_->jamKeyOff(k); // possibility to recover on stuck note if (!release) bt_->jamKeyOn(k, !config_.lock()->getFixJammingVolume()); } else { SoundSource src = instForms_->getFormInstrumentSoundSource(n); bt_->jamKeyOffForced(k, src); // possibility to recover on stuck note if (!release) bt_->jamKeyOnForced(k, src, !config_.lock()->getFixJammingVolume()); } } void MainWindow::midiProgramEvent(uchar status, uchar program) { Q_UNUSED(status) int row = findRowFromInstrumentList(program); ui->instrumentList->setCurrentRow(row); } /********** Instrument list **********/ void MainWindow::addInstrument() { SoundSource src = bt_->getCurrentTrackAttribute().source; switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: { std::unordered_map map = { { SoundSource::FM, InstrumentType::FM }, { SoundSource::SSG, InstrumentType::SSG }, { SoundSource::ADPCM, InstrumentType::ADPCM }, }; auto& list = ui->instrumentList; int num = bt_->findFirstFreeInstrumentNumber(); if (num == -1) return; // Maximum count check QString name = tr("Instrument %1").arg(num); bt_->addInstrument(num, map.at(src), name.toUtf8().toStdString()); comStack_->push(new AddInstrumentQtCommand( list, num, name, map.at(src), instForms_, this, config_.lock()->getWriteOnlyUsedSamples())); ui->instrumentList->setCurrentRow(num); break; } case SoundSource::RHYTHM: break; } } void MainWindow::addDrumkit() { auto& list = ui->instrumentList; int num = bt_->findFirstFreeInstrumentNumber(); if (num == -1) return; // Maximum count check QString name = tr("Instrument %1").arg(num); bt_->addInstrument(num, InstrumentType::Drumkit, name.toUtf8().toStdString()); comStack_->push(new AddInstrumentQtCommand( list, num, name, InstrumentType::Drumkit, instForms_, this, config_.lock()->getWriteOnlyUsedSamples())); ui->instrumentList->setCurrentRow(num); } void MainWindow::removeInstrument(int row) { if (row < 0) return; auto& list = ui->instrumentList; int num = list->item(row)->data(Qt::UserRole).toInt(); auto inst = bt_->getInstrument(num); bool updateRequest = false; if (config_.lock()->getWriteOnlyUsedSamples()){ if (inst->getSoundSource() == SoundSource::ADPCM) { size_t size = bt_->getSampleADPCMUsers(dynamic_cast( inst.get())->getSampleNumber()).size(); if (size == 1) updateRequest = true; } } bt_->removeInstrument(num); comStack_->push(new RemoveInstrumentQtCommand(list, num, row, gui_utils::utf8ToQString(inst->getName()), inst->getType(), instForms_, this, updateRequest)); } void MainWindow::openInstrumentEditor() { auto item = ui->instrumentList->currentItem(); int num = item->data(Qt::UserRole).toInt(); if (!instForms_->getForm(num)) { // Create form std::shared_ptr form; auto inst = bt_->getInstrument(num); switch (inst->getType()) { case InstrumentType::FM: { auto fmForm = std::make_shared(num); form = fmForm; fmForm->setCore(bt_); fmForm->setConfiguration(config_.lock()); fmForm->setColorPalette(palette_); fmForm->resize(config_.lock()->getInstrumentFMWindowWidth(), config_.lock()->getInstrumentFMWindowHeight()); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::envelopeNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMEnvelopeNumberChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::envelopeParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMEnvelopeParameterChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::lfoNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMLFONumberChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::lfoParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMLFOParameterChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::operatorSequenceNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMOperatorSequenceNumberChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::operatorSequenceParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMOperatorSequenceParameterChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::arpeggioNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMArpeggioNumberChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::arpeggioParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMArpeggioParameterChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::pitchNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMPitchNumberChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::pitchParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentFMPitchParameterChanged); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::jamKeyOnEvent, this, [&](JamKey key) { bt_->jamKeyOnForced(key, SoundSource::FM, !config_.lock()->getFixJammingVolume()); }, Qt::DirectConnection); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::jamKeyOffEvent, this, [&](JamKey key) { bt_->jamKeyOffForced(key, SoundSource::FM); }, Qt::DirectConnection); QObject::connect(fmForm.get(), &InstrumentEditorFMForm::modified, this, &MainWindow::setModifiedTrue); fmForm->installEventFilter(this); instForms_->onInstrumentFMEnvelopeNumberChanged(); instForms_->onInstrumentFMLFONumberChanged(); instForms_->onInstrumentFMOperatorSequenceNumberChanged(); instForms_->onInstrumentFMArpeggioNumberChanged(); instForms_->onInstrumentFMPitchNumberChanged(); break; } case InstrumentType::SSG: { auto ssgForm = std::make_shared(num); form = ssgForm; ssgForm->setCore(bt_); ssgForm->setConfiguration(config_.lock()); ssgForm->setColorPalette(palette_); ssgForm->resize(config_.lock()->getInstrumentSSGWindowWidth(), config_.lock()->getInstrumentSSGWindowHeight()); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::waveformNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGWaveformNumberChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::waveformParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGWaveformParameterChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::toneNoiseNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGToneNoiseNumberChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::toneNoiseParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGToneNoiseParameterChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::envelopeNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGEnvelopeNumberChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::envelopeParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGEnvelopeParameterChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::arpeggioNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGArpeggioNumberChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::arpeggioParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGArpeggioParameterChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::pitchNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGPitchNumberChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::pitchParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentSSGPitchParameterChanged); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::jamKeyOnEvent, this, [&](JamKey key) { bt_->jamKeyOnForced(key, SoundSource::SSG, !config_.lock()->getFixJammingVolume()); }, Qt::DirectConnection); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::jamKeyOffEvent, this, [&](JamKey key) { bt_->jamKeyOffForced(key, SoundSource::SSG); } , Qt::DirectConnection); QObject::connect(ssgForm.get(), &InstrumentEditorSSGForm::modified, this, &MainWindow::setModifiedTrue); ssgForm->installEventFilter(this); instForms_->onInstrumentSSGWaveformNumberChanged(); instForms_->onInstrumentSSGToneNoiseNumberChanged(); instForms_->onInstrumentSSGEnvelopeNumberChanged(); instForms_->onInstrumentSSGArpeggioNumberChanged(); instForms_->onInstrumentSSGPitchNumberChanged(); break; } case InstrumentType::ADPCM: { auto adpcmForm = std::make_shared(num); form = adpcmForm; adpcmForm->setCore(bt_); adpcmForm->setConfiguration(config_.lock()); adpcmForm->setColorPalette(palette_); adpcmForm->resize(config_.lock()->getInstrumentADPCMWindowWidth(), config_.lock()->getInstrumentADPCMWindowHeight()); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::sampleNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMSampleNumberChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::sampleParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMSampleParameterChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::sampleAssignRequested, this, &MainWindow::assignADPCMSamples); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::sampleMemoryChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMSampleMemoryUpdated); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::envelopeNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMEnvelopeNumberChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::envelopeParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMEnvelopeParameterChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::arpeggioNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMArpeggioNumberChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::arpeggioParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMArpeggioParameterChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::pitchNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMPitchNumberChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::pitchParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMPitchParameterChanged); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::jamKeyOnEvent, this, [&](JamKey key) { bt_->jamKeyOnForced(key, SoundSource::ADPCM, !config_.lock()->getFixJammingVolume()); }, Qt::DirectConnection); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::jamKeyOffEvent, this, [&](JamKey key) { bt_->jamKeyOffForced(key, SoundSource::ADPCM); }, Qt::DirectConnection); QObject::connect(adpcmForm.get(), &InstrumentEditorADPCMForm::modified, this, &MainWindow::setModifiedTrue); adpcmForm->installEventFilter(this); instForms_->onInstrumentADPCMSampleNumberChanged(); instForms_->onInstrumentADPCMEnvelopeNumberChanged(); instForms_->onInstrumentADPCMArpeggioNumberChanged(); instForms_->onInstrumentADPCMPitchNumberChanged(); break; } case InstrumentType::Drumkit: { auto kitForm = std::make_shared(num); form = kitForm; kitForm->setCore(bt_); kitForm->setConfiguration(config_.lock()); kitForm->setColorPalette(palette_); kitForm->resize(config_.lock()->getInstrumentDrumkitWindowWidth(), config_.lock()->getInstrumentDrumkitWindowHeight()); QObject::connect(kitForm.get(), &InstrumentEditorDrumkitForm::sampleNumberChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMSampleNumberChanged); QObject::connect(kitForm.get(), &InstrumentEditorDrumkitForm::sampleParameterChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMSampleParameterChanged); QObject::connect(kitForm.get(), &InstrumentEditorDrumkitForm::sampleAssignRequested, this, &MainWindow::assignADPCMSamples); QObject::connect(kitForm.get(), &InstrumentEditorDrumkitForm::sampleMemoryChanged, instForms_.get(), &InstrumentFormManager::onInstrumentADPCMSampleMemoryUpdated); QObject::connect(kitForm.get(), &InstrumentEditorDrumkitForm::jamKeyOnEvent, this, [&](JamKey key) { bt_->jamKeyOnForced(key, SoundSource::ADPCM, !config_.lock()->getFixJammingVolume()); }, Qt::DirectConnection); QObject::connect(kitForm.get(), &InstrumentEditorDrumkitForm::jamKeyOffEvent, this, [&](JamKey key) { bt_->jamKeyOffForced(key, SoundSource::ADPCM); }, Qt::DirectConnection); QObject::connect(kitForm.get(), &InstrumentEditorDrumkitForm::modified, this, &MainWindow::setModifiedTrue); kitForm->installEventFilter(this); instForms_->onInstrumentADPCMSampleNumberChanged(); break; } default: throw std::invalid_argument("Invalid instrument type"); } form->addActions({ &octUpSc_, &octDownSc_, &jamVolUpSc_, &jamVolDownSc_ }); instForms_->add(num, std::move(form), inst->getSoundSource(), inst->getType()); } instForms_->showForm(num); } int MainWindow::findRowFromInstrumentList(int instNum) { auto& list = ui->instrumentList; int row = 0; for (; row < list->count(); ++row) { auto item = list->item(row); if (item->data(Qt::UserRole).toInt() == instNum) break; } return row; } void MainWindow::renameInstrument() { auto list = ui->instrumentList; auto item = list->currentItem(); // Finish current edit if (item == renamingInstItem_) { finishRenamingInstrument(); return; } else if (renamingInstItem_) { finishRenamingInstrument(); } renamingInstItem_ = item; int num = item->data(Qt::UserRole).toInt(); renamingInstEdit_ = new QLineEdit(gui_utils::utf8ToQString(bt_->getInstrument(num)->getName())); QObject::connect(renamingInstEdit_, &QLineEdit::editingFinished, this, &MainWindow::finishRenamingInstrument); renamingInstEdit_->installEventFilter(this); ui->instrumentList->setItemWidget(item, renamingInstEdit_); renamingInstEdit_->selectAll(); renamingInstEdit_->setFocus(); } void MainWindow::finishRenamingInstrument() { if (!renamingInstItem_ || !renamingInstEdit_) return; bool hasFocus = renamingInstEdit_->hasFocus(); auto list = ui->instrumentList; int num = renamingInstItem_->data(Qt::UserRole).toInt(); int row = findRowFromInstrumentList(num); auto oldName = gui_utils::utf8ToQString(bt_->getInstrument(num)->getName()); QString newName = renamingInstEdit_->text(); list->removeItemWidget(renamingInstItem_); if (newName != oldName) { bt_->setInstrumentName(num, newName.toUtf8().toStdString()); comStack_->push(new ChangeInstrumentNameQtCommand(list, num, row, instForms_, oldName, newName)); } renamingInstItem_ = nullptr; renamingInstEdit_ = nullptr; if (hasFocus) ui->instrumentList->setFocus(); } void MainWindow::cloneInstrument() { int num = bt_->findFirstFreeInstrumentNumber(); if (num == -1) return; int refNum = ui->instrumentList->currentItem()->data(Qt::UserRole).toInt(); // KEEP CODE ORDER // bt_->cloneInstrument(num, refNum); auto inst = bt_->getInstrument(num); comStack_->push(new CloneInstrumentQtCommand(ui->instrumentList, num, inst->getType(), gui_utils::utf8ToQString(inst->getName()), instForms_)); //----------// } void MainWindow::deepCloneInstrument() { int num = bt_->findFirstFreeInstrumentNumber(); if (num == -1) return; int refNum = ui->instrumentList->currentItem()->data(Qt::UserRole).toInt(); // KEEP CODE ORDER // bt_->deepCloneInstrument(num, refNum); auto inst = bt_->getInstrument(num); comStack_->push(new DeepCloneInstrumentQtCommand( ui->instrumentList, num, inst->getType(), gui_utils::utf8ToQString(inst->getName()), instForms_, this, config_.lock()->getWriteOnlyUsedSamples())); //----------// } void MainWindow::loadInstrument() { QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); std::vector orgFilters = io::InstrumentIO::getInstance().getLoadFilter(); QStringList filters; std::transform(orgFilters.begin(), orgFilters.end(), std::back_inserter(filters), [](const std::string& f) { return QString::fromStdString(f); }); QString defaultFilter = filters.at(config_.lock()->getInstrumentOpenFormat()); QString file = QFileDialog::getOpenFileName(this, tr("Open instrument"), (dir.isEmpty() ? "./" : dir), filters.join(";;"), &defaultFilter); if (file.isNull()) return; int index = getSelectedFileFilter(file, filters); if (index != -1) config_.lock()->setInstrumentOpenFormat(index); funcLoadInstrument(file); } void MainWindow::funcLoadInstrument(QString file) { int n = bt_->findFirstFreeInstrumentNumber(); if (n == -1) { FileIOErrorMessageBox(file, true, io::FileType::Inst, tr("The number of instruments has reached the upper limit."), this).exec(); return; } try { io::BinaryContainer contaner; { QFile fp(file); if (!fp.open(QIODevice::ReadOnly)) { FileIOErrorMessageBox::openError(file, true, io::FileType::Inst, this); return; } QByteArray&& array = fp.readAll(); fp.close(); std::move(array.begin(), array.end(), std::back_inserter(contaner)); } bt_->loadInstrument(contaner, file.toStdString(), n); auto inst = bt_->getInstrument(n); comStack_->push(new AddInstrumentQtCommand( ui->instrumentList, n, gui_utils::utf8ToQString(inst->getName()), inst->getType(), instForms_, this, config_.lock()->getWriteOnlyUsedSamples())); ui->instrumentList->setCurrentRow(n); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, true, e, this).exec(); } catch (std::exception& e) { FileIOErrorMessageBox(file, true, io::FileType::Inst, QString(e.what()), this).exec(); } } void MainWindow::saveInstrument() { int n = ui->instrumentList->currentItem()->data(Qt::UserRole).toInt(); QString name = gui_utils::utf8ToQString(bt_->getInstrument(n)->getName()); QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); std::vector orgFilters = io::InstrumentIO::getInstance().getSaveFilter(); QStringList filters; std::transform(orgFilters.begin(), orgFilters.end(), std::back_inserter(filters), [](const std::string& f) { return QString::fromStdString(f); }); QString defaultFilter = filters.front(); // bti QString file = QFileDialog::getSaveFileName( this, tr("Save instrument"), QString("%1/%2.bti").arg(dir.isEmpty() ? "." : dir, name), filters.join(";;"), &defaultFilter); if (file.isNull()) return; if (!file.endsWith(".bti")) file += ".bti"; // For linux try { QByteArray bytes; { io::BinaryContainer container; bt_->saveInstrument(container, n); bytes.reserve(container.size()); std::move(container.begin(), container.end(), std::back_inserter(bytes)); } QFile fp(file); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(file, false, io::FileType::Inst, this); return; } fp.write(bytes); fp.close(); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, false, e, this).exec(); } catch (std::exception& e) { FileIOErrorMessageBox(file, false, io::FileType::Inst, QString(e.what()), this).exec(); } } void MainWindow::importInstrumentsFromBank() { stopPlaySong(); QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); std::vector orgFilters = io::BankIO::getInstance().getLoadFilter(); QStringList filters; std::transform(orgFilters.begin(), orgFilters.end(), std::back_inserter(filters), [](const std::string& f) { return QString::fromStdString(f); }); QString defaultFilter = filters.at(config_.lock()->getBankOpenFormat()); QString file = QFileDialog::getOpenFileName(this, tr("Open bank"), (dir.isEmpty() ? "./" : dir), filters.join(";;"), &defaultFilter); if (file.isNull()) { return; } else { int index = getSelectedFileFilter(file, filters); if (index != -1) config_.lock()->setBankOpenFormat(index); } funcImportInstrumentsFromBank(file); } void MainWindow::funcImportInstrumentsFromBank(QString file) { stopPlaySong(); std::unique_ptr bank; try { io::BinaryContainer container; { QFile fp(file); if (!fp.open(QIODevice::ReadOnly)) { FileIOErrorMessageBox::openError(file, true, io::FileType::Bank, this); return; } QByteArray&& array = fp.readAll(); fp.close(); std::move(array.begin(), array.end(), std::back_inserter(container)); } bank.reset(io::BankIO::getInstance().loadBank(container, file.toStdString())); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, true, e, this).exec(); return; } catch (std::exception& e) { FileIOErrorMessageBox(file, true, io::FileType::Bank, QString(e.what()), this).exec(); return; } // Change text codec if (auto ff = dynamic_cast(bank.get())) { QTextCodec* codec = QTextCodec::codecForName("Shift-JIS"); for (size_t i = 0; i < ff->getNumInstruments(); ++i) { std::string sjis = ff->getInstrumentName(i); std::string utf8 = codec->toUnicode(sjis.c_str(), sjis.length()).toStdString(); ff->setInstrumentName(i, utf8); } } else if (auto mu88 = dynamic_cast(bank.get())) { QTextCodec* codec = QTextCodec::codecForName("Shift-JIS"); for (size_t i = 0; i < mu88->getNumInstruments(); ++i) { std::string sjis = mu88->getInstrumentName(i); std::string utf8 = codec->toUnicode(sjis.c_str(), sjis.length()).toStdString(); mu88->setInstrumentName(i, utf8); } } size_t jamId = 128; // Dummy std::shared_ptr jamInst; importBankDiag_ = std::make_unique(*bank, tr("Select instruments to load:"), config_, this); auto bankMan = std::make_shared(true); auto updateInst = [&] (size_t id) { if (id != jamId) { bankJamMidiCtrl_.store(true); jamId = id; bankMan->clearAll(); jamInst.reset(bank->loadInstrument(id, bankMan, 0)); jamInst->setNumber(128); // Special number std::unordered_map> sampNums; bt_->assignADPCMBeforeForcedJamKeyOn(jamInst, sampNums); for (const auto& pairs : sampNums) { bankMan->setSampleADPCMStartAddress(pairs.first, pairs.second[0]); bankMan->setSampleADPCMStopAddress(pairs.first, pairs.second[1]); } bankJamMidiCtrl_.store(false); } }; QObject::connect(importBankDiag_.get(), &InstrumentSelectionDialog::jamKeyOnEvent, this, [&](size_t id, JamKey key) { updateInst(id); bt_->jamKeyOnForced(key, jamInst->getSoundSource(), !config_.lock()->getFixJammingVolume(), jamInst); }, Qt::DirectConnection); QObject::connect(importBankDiag_.get(), &InstrumentSelectionDialog::jamKeyOnMidiEvent, this, [&](size_t id, int key) { updateInst(id); bt_->jamKeyOnForced(key, jamInst->getSoundSource(), !config_.lock()->getFixJammingVolume(), jamInst); }, Qt::DirectConnection); QObject::connect(importBankDiag_.get(), &InstrumentSelectionDialog::jamKeyOffEvent, this, [&](JamKey key) { bt_->jamKeyOffForced(key, jamInst->getSoundSource()); }, Qt::DirectConnection); QObject::connect(importBankDiag_.get(), &InstrumentSelectionDialog::jamKeyOffMidiEvent, this, [&](int key) { if (jamInst) bt_->jamKeyOffForced(key, jamInst->getSoundSource()); }, Qt::DirectConnection); importBankDiag_->addActions({ &octUpSc_, &octDownSc_ }); if (importBankDiag_->exec() != QDialog::Accepted) { assignADPCMSamples(); // Restore importBankDiag_.reset(); return; } const QVector selection = importBankDiag_->currentInstrumentSelection(); importBankDiag_.reset(); if (selection.empty()) return; try { bool sampleRestoreRequested = false; int lastNum = ui->instrumentList->currentRow(); for (const size_t& index : selection) { int n = bt_->findFirstFreeInstrumentNumber(); if (n == -1){ FileIOErrorMessageBox(file, true, io::FileType::Inst, tr("The number of instruments has reached the upper limit."), this).exec(); ui->instrumentList->setCurrentRow(lastNum); return; } bt_->importInstrument(*bank, index, n); auto inst = bt_->getInstrument(n); comStack_->push(new AddInstrumentQtCommand( ui->instrumentList, n, gui_utils::utf8ToQString(inst->getName()), inst->getType(), instForms_, this, config_.lock()->getWriteOnlyUsedSamples(), true)); lastNum = n; sampleRestoreRequested |= (inst->getSoundSource() == SoundSource::ADPCM); } ui->instrumentList->setCurrentRow(lastNum); if (sampleRestoreRequested) assignADPCMSamples(); // Store only once } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, true, e, this).exec(); } catch (std::exception& e) { FileIOErrorMessageBox(file, true, io::FileType::Bank, QString(e.what()), this).exec(); } } void MainWindow::exportInstrumentsToBank() { std::vector ids = bt_->getInstrumentIndices(); std::shared_ptr bank(std::make_shared(ids, bt_->getInstrumentNames())); InstrumentSelectionDialog dlg(*bank, tr("Select instruments to save:"), config_, this); if (dlg.exec() != QDialog::Accepted) return; QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); std::vector orgFilters = io::BankIO::getInstance().getSaveFilter(); QStringList filters; std::transform(orgFilters.begin(), orgFilters.end(), std::back_inserter(filters), [](const std::string& f) { return QString::fromStdString(f); }); QString defaultFilter = filters.front(); // btb QString file = QFileDialog::getSaveFileName(this, tr("Save bank"), (dir.isEmpty() ? "./" : dir), filters.join(";;"), &defaultFilter); if (file.isNull()) return; QVector selection = dlg.currentInstrumentSelection(); if (selection.empty()) return; std::vector sel; std::transform(selection.begin(), selection.end(), std::back_inserter(sel), [&ids](size_t i) { return ids.at(i); }); std::sort(sel.begin(), sel.end()); try { QByteArray bytes; { io::BinaryContainer container; bt_->exportInstruments(container, sel); bytes.reserve(container.size()); std::move(container.begin(), container.end(), std::back_inserter(bytes)); } QFile fp(file); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(file, false, io::FileType::Bank, this); return; }fp.write(bytes); fp.close(); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, false, e, this).exec(); } catch (std::exception& e) { FileIOErrorMessageBox(file, false, io::FileType::Bank, QString(e.what()), this).exec(); } } void MainWindow::swapInstruments(int row1, int row2) { if (row1 == row2) return; // KEEP CODE ORDER // int num1 = ui->instrumentList->item(row1)->data(Qt::UserRole).toInt(); int num2 = ui->instrumentList->item(row2)->data(Qt::UserRole).toInt(); QString name1 = gui_utils::utf8ToQString(bt_->getInstrument(num1)->getName()); QString name2 = gui_utils::utf8ToQString(bt_->getInstrument(num2)->getName()); bt_->swapInstruments(num1, num2, config_.lock()->getReflectInstrumentNumberChange()); comStack_->push(new SwapInstrumentsQtCommand( ui->instrumentList, row1, row2, name1, name2, instForms_, ui->patternEditor)); //----------// } /********** Undo-Redo **********/ void MainWindow::undo() { bt_->undo(); comStack_->undo(); } void MainWindow::redo() { bt_->redo(); comStack_->redo(); } /********** Load data **********/ void MainWindow::loadModule() { instForms_->clearAll(); ui->instrumentList->clear(); on_instrumentList_itemSelectionChanged(); ui->modTitleLineEdit->setText(gui_utils::utf8ToQString(bt_->getModuleTitle())); ui->modTitleLineEdit->setCursorPosition(0); ui->authorLineEdit->setText(gui_utils::utf8ToQString(bt_->getModuleAuthor())); ui->authorLineEdit->setCursorPosition(0); ui->copyrightLineEdit->setText(gui_utils::utf8ToQString(bt_->getModuleCopyright())); ui->copyrightLineEdit->setCursorPosition(0); { QSignalBlocker blocker(ui->songComboBox); // Prevent duplicated call "loadSong" ui->songComboBox->clear(); for (size_t i = 0; i < bt_->getSongCount(); ++i) { QString title = gui_utils::utf8ToQString(bt_->getSongTitle(static_cast(i))); if (title.isEmpty()) title = tr("Untitled"); ui->songComboBox->addItem(QString("#%1 %2").arg(i).arg(title)); } } highlight1_->setValue(static_cast(bt_->getModuleStepHighlight1Distance())); highlight2_->setValue(static_cast(bt_->getModuleStepHighlight2Distance())); for (auto& idx : bt_->getInstrumentIndices()) { auto inst = bt_->getInstrument(idx); comStack_->push(new AddInstrumentQtCommand( ui->instrumentList, idx, gui_utils::utf8ToQString(inst->getName()), inst->getType(), instForms_, this, config_.lock()->getWriteOnlyUsedSamples(), true)); } isSavedModBefore_ = false; loadSong(); // Set tick frequency stream_->setInterruption(bt_->getModuleTickFrequency()); if (tickTimerForRealChip_) tickTimerForRealChip_->setInterval(1000000 / bt_->getModuleTickFrequency()); statusIntr_->setText(QString::number(bt_->getModuleTickFrequency()) + QString("Hz")); // Set mixer QString text; switch (bt_->getModuleMixerType()) { case MixerType::UNSPECIFIED: bt_->setMasterVolumeFM(config_.lock()->getMixerVolumeFM()); bt_->setMasterVolumeSSG(config_.lock()->getMixerVolumeSSG()); text = tr("-"); break; case MixerType::CUSTOM: bt_->setMasterVolumeFM(bt_->getModuleCustomMixerFMLevel()); bt_->setMasterVolumeSSG(bt_->getModuleCustomMixerSSGLevel()); text = tr("Custom"); break; case MixerType::PC_9821_PC_9801_86: bt_->setMasterVolumeFM(0); bt_->setMasterVolumeSSG(-5.5); text = tr("PC-9821 with PC-9801-86"); break; case MixerType::PC_9821_SPEAK_BOARD: bt_->setMasterVolumeFM(0); bt_->setMasterVolumeSSG(-3.0); text = tr("PC-9821 with Speak Board"); break; case MixerType::PC_8801_VA2: bt_->setMasterVolumeFM(0); bt_->setMasterVolumeSSG(1.5); text = tr("PC-88VA2"); break; case MixerType::PC_8801_MKII_SR: bt_->setMasterVolumeFM(0); bt_->setMasterVolumeSSG(2.5); text = tr("NEC PC-8801mkIISR"); break; } statusMixer_->setText(text); // Set comment if (commentDiag_) commentDiag_->setComment(gui_utils::utf8ToQString(bt_->getModuleComment())); // Clear records QApplication::clipboard()->clear(); comStack_->clear(); bt_->clearCommandHistory(); } void MainWindow::openModule(QString file) { try { freezeViews(); if (tickTimerForRealChip_) tickTimerForRealChip_->stop(); else stream_->stop(); QFile fp(file); if (fp.open(QIODevice::ReadOnly)) { io::BinaryContainer container; { QByteArray&& array = fp.readAll(); fp.close(); std::move(array.begin(), array.end(), std::back_inserter(container)); } bt_->loadModule(container); bt_->setModulePath(file.toStdString()); loadModule(); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); changeFileHistory(file); goto AFTER_MOD_LOADING; // Skip error handling section } else { FileIOErrorMessageBox::openError(file, true, io::FileType::Mod, this); } } catch (std::exception& e) { if (auto ef = dynamic_cast(&e)) { FileIOErrorMessageBox(file, true, *ef, this).exec(); } else { FileIOErrorMessageBox(file, true, io::FileType::Mod, QString(e.what()), this).exec(); } } // Init module as a plain when something is wrong freezeViews(); bt_->makeNewModule(); loadModule(); AFTER_MOD_LOADING: // Post process of module loading isModifiedForNotCommand_ = false; setWindowModified(false); if (tickTimerForRealChip_) tickTimerForRealChip_->start(); else stream_->start(); setInitialSelectedInstrument(); assignADPCMSamples(); } void MainWindow::loadSong() { // Init position int songCnt = static_cast(bt_->getSongCount()); if (ui->songComboBox->currentIndex() >= songCnt) bt_->setCurrentSongNumber(songCnt - 1); else bt_->setCurrentSongNumber(bt_->getCurrentSongNumber()); bt_->setCurrentOrderNumber(0); bt_->setCurrentTrack(0); bt_->setCurrentStepNumber(0); // Init ui ui->orderList->onSongLoaded(); setOrderListGroupMaximumWidth(); ui->patternEditor->onSongLoaded(); unfreezeViews(); int curSong = bt_->getCurrentSongNumber(); ui->songComboBox->setCurrentIndex(curSong); ui->tempoSpinBox->setValue(bt_->getSongTempo(curSong)); ui->speedSpinBox->setValue(bt_->getSongSpeed(curSong)); ui->patternSizeSpinBox->setValue(static_cast(bt_->getDefaultPatternSize(curSong))); ui->grooveSpinBox->setValue(bt_->getSongGroove(curSong)); ui->grooveSpinBox->setMaximum(static_cast(bt_->getGrooveCount()) - 1); if (bt_->isUsedTempoInSong(curSong)) { ui->tempoSpinBox->setEnabled(true); ui->speedSpinBox->setEnabled(true); ui->grooveCheckBox->setChecked(false); ui->grooveSpinBox->setEnabled(false); } else { ui->tempoSpinBox->setEnabled(false); ui->speedSpinBox->setEnabled(false); ui->grooveCheckBox->setChecked(true); ui->grooveSpinBox->setEnabled(true); } onCurrentTrackChanged(); setWindowTitle(); switch (bt_->getSongStyle(bt_->getCurrentSongNumber()).type) { case SongType::Standard: statusStyle_->setText(tr("Standard")); break; case SongType::FM3chExpanded: statusStyle_->setText(tr("FM3ch expanded")); break; } statusPlayPos_->setText(config_.lock()->getShowRowNumberInHex() ? "00/00" : "000/000"); bmManForm_->onCurrentSongNumberChanged(); } void MainWindow::assignADPCMSamples() { bt_->stopPlaySong(); lockWidgets(false); if (tickTimerForRealChip_) tickTimerForRealChip_->stop(); else stream_->stop(); bool isStoredAll = bt_->assignSampleADPCMRawSamples(); // Mutex register instForms_->onInstrumentADPCMSampleMemoryUpdated(); if (!isStoredAll) { QMessageBox::warning(this, tr("Warning"), tr("Insufficient memory size to load ADPCM samples. Please delete the unused samples.")); } if (tickTimerForRealChip_) tickTimerForRealChip_->start(); else stream_->start(); } /********** Play song **********/ void MainWindow::startPlaySong() { bt_->startPlaySong(); lockWidgets(true); firstViewUpdateRequest_ = true; } void MainWindow::startPlayFromStart() { bt_->startPlayFromStart(); lockWidgets(true); firstViewUpdateRequest_ = true; } void MainWindow::startPlayPattern() { bt_->startPlayPattern(); lockWidgets(true); firstViewUpdateRequest_ = true; } void MainWindow::startPlayFromCurrentStep() { bt_->startPlayFromCurrentStep(); lockWidgets(true); firstViewUpdateRequest_ = true; } void MainWindow::startPlayFromMarker() { if (bt_->startPlayFromMarker()) { lockWidgets(true); firstViewUpdateRequest_ = true; } } void MainWindow::playStep() { if (!bt_->isPlaySong()) { bt_->playStep(); firstViewUpdateRequest_ = true; ui->patternEditor->onPlayStepPressed(); } } void MainWindow::stopPlaySong() { bt_->stopPlaySong(); lockWidgets(false); ui->patternEditor->onStoppedPlaySong(); ui->orderList->onStoppedPlaySong(); } void MainWindow::lockWidgets(bool isLock) { hasLockedWigets_ = isLock; ui->songComboBox->setEnabled(!isLock); } /********** Octave change **********/ void MainWindow::changeOctave(bool upFlag) { if (upFlag) octave_->stepUp(); else octave_->stepDown(); statusOctave_->setText(tr("Octave: %1").arg(bt_->getCurrentOctave())); } /********** Configuration change **********/ void MainWindow::changeConfiguration() { // Real chip interface bool streamState = false; RealChipInterface intf = config_.lock()->getRealChipInterface(); if (intf == RealChipInterface::NONE) { tickTimerForRealChip_.reset(); bt_->useSCCI(nullptr); bt_->useC86CTL(nullptr); QString streamErr; streamState = stream_->initialize( config_.lock()->getSampleRate(), config_.lock()->getBufferLength(), bt_->getModuleTickFrequency(), gui_utils::utf8ToQString(config_.lock()->getSoundAPI()), gui_utils::utf8ToQString(config_.lock()->getSoundDevice()), &streamErr); if (!streamState) showStreamFailedDialog(streamErr); stream_->start(); } else { stream_->stop(); if (tickTimerForRealChip_) { tickTimerForRealChip_->stop(); } else { tickTimerForRealChip_ = std::make_unique(); tickTimerForRealChip_->setInterval(1000000 / bt_->getModuleTickFrequency()); tickEventMethod_ = metaObject()->indexOfSlot("onNewTickSignaledRealChip()"); Q_ASSERT(tickEventMethod_ != -1); tickTimerForRealChip_->setFunction([&]{ QMetaMethod method = this->metaObject()->method(this->tickEventMethod_); method.invoke(this, Qt::QueuedConnection); }); } setRealChipInterface(intf); tickTimerForRealChip_->start(); } setMidiConfiguration(); updateFonts(); ui->orderList->setHorizontalScrollMode(config_.lock()->getMoveCursorByHorizontalScroll()); ui->patternEditor->setHorizontalScrollMode(config_.lock()->getMoveCursorByHorizontalScroll()); instForms_->updateByConfiguration(); bt_->changeConfiguration(config_); if (streamState) { uint32_t sr = stream_->getStreamRate(); if (config_.lock()->getSampleRate() != sr) { showStreamRateWarningDialog(sr); bt_->setStreamRate(sr); } } setShortcuts(); updateInstrumentListColors(); bmManForm_->onConfigurationChanged(config_.lock()->getShowRowNumberInHex()); visualTimer_->stop(); visualTimer_->start(static_cast(std::round(1000. / config_.lock()->getWaveViewFrameRate()))); update(); } void MainWindow::setRealChipInterface(RealChipInterface intf) { if (intf == bt_->getRealChipinterface()) return; if (isWindowModified() && QMessageBox::warning(this, tr("Warning"), tr("The module has been changed. Do you want to save it?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) { on_actionSave_As_triggered(); } switch (intf) { case RealChipInterface::SCCI: bt_->useC86CTL(nullptr); scciDll_->load(); if (scciDll_->isLoaded()) { auto getManager = reinterpret_cast( scciDll_->resolve("getSoundInterfaceManager")); bt_->useSCCI(getManager ? getManager() : nullptr); } else { bt_->useSCCI(nullptr); } break; case RealChipInterface::C86CTL: bt_->useSCCI(nullptr); c86ctlDll_->load(); if (c86ctlDll_->isLoaded()) { bt_->useC86CTL(new C86ctlBase(c86ctlDll_->resolve("CreateInstance"))); } else { bt_->useC86CTL(nullptr); } break; default: break; } bt_->assignSampleADPCMRawSamples(); // Mutex register instForms_->onInstrumentADPCMSampleMemoryUpdated(); } void MainWindow::setMidiConfiguration() { MidiInterface &midiIntf = MidiInterface::getInstance(); std::string midiApi = config_.lock()->getMidiAPI(); std::string midiInPortName = config_.lock()->getMidiInputPort(); if (config_.lock()->getMidiEnabled()) { std::string errDetail; if (midiApi.empty()) { config_.lock()->setMidiEnabled(false); midiIntf.switchApi(""); // Clear } else { if (!midiIntf.isAvailableApi(midiApi)) { showMidiFailedDialog("Invalid API name."); midiIntf.switchApi(""); // Clear return; } if (midiIntf.switchApi(midiApi, &errDetail)) { bool resPort = true; if (!midiInPortName.empty()) resPort = midiIntf.openInputPortByName(midiInPortName, &errDetail); else if (midiIntf.supportsVirtualPort()) resPort = midiIntf.openInputPort(~0u, &errDetail); else config_.lock()->setMidiEnabled(false); if (!resPort) { showMidiFailedDialog(QString::fromStdString(errDetail)); midiIntf.switchApi(""); // Clear } } else { showMidiFailedDialog(QString::fromStdString(errDetail)); midiIntf.switchApi(""); // Clear } } } } void MainWindow::updateFonts() { ui->patternEditor->setFonts( gui_utils::utf8ToQString(config_.lock()->getPatternEditorHeaderFont()), config_.lock()->getPatternEditorHeaderFontSize(), gui_utils::utf8ToQString(config_.lock()->getPatternEditorRowsFont()), config_.lock()->getPatternEditorRowsFontSize()); ui->orderList->setFonts( gui_utils::utf8ToQString(config_.lock()->getOrderListHeaderFont()), config_.lock()->getOrderListHeaderFontSize(), gui_utils::utf8ToQString(config_.lock()->getOrderListRowsFont()), config_.lock()->getOrderListRowsFontSize()); } /********** History change **********/ void MainWindow::changeFileHistory(QString file) { fileHistory_->addFile(file); for (int i = ui->menu_Recent_Files->actions().count() - 1; 1 < i; --i) ui->menu_Recent_Files->removeAction(ui->menu_Recent_Files->actions().at(i)); for (size_t i = 0; i < fileHistory_->size(); ++i) { // Leave Before Qt5.7.0 style due to windows xp QAction* action = ui->menu_Recent_Files->addAction(QString("&%1 %2").arg(i + 1).arg(fileHistory_->at(i))); action->setData(fileHistory_->at(i)); } } /********** Backup **********/ bool MainWindow::backupModule(QString srcFile) { if (!isSavedModBefore_ && config_.lock()->getBackupModules()) { bool err = false; QString backup = srcFile + ".bak"; if (QFile::exists(backup)) err = !QFile::remove(backup); if (err || !QFile::copy(srcFile, backup)) { QMessageBox::critical(this, tr("Error"), tr("Failed to backup module.")); return false; } } return true; } /******************************/ void MainWindow::setWindowTitle() { int n = bt_->getCurrentSongNumber(); QString filePath = QString::fromStdString(bt_->getModulePath()); QString fileName = filePath.isEmpty() ? tr("Untitled") : QFileInfo(filePath).fileName(); QString songTitle = gui_utils::utf8ToQString(bt_->getSongTitle(n)); if (songTitle.isEmpty()) songTitle = tr("Untitled"); QMainWindow::setWindowTitle(QString("%1[*] [#%2 %3] - BambooTracker") .arg(fileName, QString::number(n), songTitle)); } void MainWindow::setModifiedTrue() { isModifiedForNotCommand_ = true; setWindowModified(true); } void MainWindow::setInitialSelectedInstrument() { if (bt_->getInstrumentIndices().empty()) { bt_->setCurrentInstrument(-1); statusInst_->setText(tr("No instrument")); } else { ui->instrumentList->setCurrentRow(0); } } QString MainWindow::getModuleFileBaseName() const { auto filePathStd = bt_->getModulePath(); QString filePath = QString::fromStdString(filePathStd); return (filePath.isEmpty() ? tr("Untitled") : QFileInfo(filePath).completeBaseName()); } int MainWindow::getSelectedFileFilter(QString& file, QStringList& filters) const { QRegularExpression re(R"(\(\*\.(.+)\))"); QString ex = QFileInfo(file).suffix(); for (int i = 0; i < filters.size(); ++i) if (ex == re.match(filters[i]).captured(1)) return i; return -1; } /******************************/ /********** Instrument list events **********/ void MainWindow::on_instrumentList_customContextMenuRequested(const QPoint &pos) { auto& list = ui->instrumentList; QPoint globalPos = list->mapToGlobal(pos); QMenu menu; // Leave Before Qt5.7.0 style due to windows xp menu.addActions({ ui->actionNew_Instrument, ui->actionNew_Drumki_t, ui->actionRemove_Instrument }); menu.addSeparator(); menu.addAction(ui->actionRename_Instrument); menu.addSeparator(); menu.addActions({ ui->actionClone_Instrument, ui->actionDeep_Clone_Instrument }); menu.addSeparator(); menu.addActions({ ui->actionLoad_From_File, ui->actionSave_To_File }); menu.addSeparator(); menu.addActions({ ui->actionImport_From_Bank_File, ui->actionExport_To_Bank_File }); menu.addSeparator(); menu.addAction(ui->actionEdit); menu.exec(globalPos); } void MainWindow::on_instrumentList_itemDoubleClicked(QListWidgetItem *item) { Q_UNUSED(item) openInstrumentEditor(); } void MainWindow::on_instrumentList_itemSelectionChanged() { int num = (ui->instrumentList->currentRow() == -1) ? -1 : ui->instrumentList->currentItem()->data(Qt::UserRole).toInt(); bt_->setCurrentInstrument(num); if (num == -1) statusInst_->setText(tr("No instrument")); else statusInst_->setText( tr("Instrument: %1").arg(QString("%1").arg(num, 2, 16, QChar('0')).toUpper())); bool canAdd = (bt_->findFirstFreeInstrumentNumber() != -1); ui->actionLoad_From_File->setEnabled(canAdd); ui->actionImport_From_Bank_File->setEnabled(canAdd); bool isSelected = (num != -1); ui->actionRemove_Instrument->setEnabled(isSelected); ui->actionClone_Instrument->setEnabled(isSelected); ui->actionDeep_Clone_Instrument->setEnabled(isSelected); ui->actionSave_To_File->setEnabled(isSelected); ui->actionExport_To_Bank_File->setEnabled(isSelected); ui->actionRename_Instrument->setEnabled(isSelected); ui->actionEdit->setEnabled(isSelected); } void MainWindow::on_grooveCheckBox_stateChanged(int arg1) { if (arg1 == Qt::Checked) { ui->tempoSpinBox->setEnabled(false); ui->speedSpinBox->setEnabled(false); ui->grooveSpinBox->setEnabled(true); bt_->toggleTempoOrGrooveInSong(bt_->getCurrentSongNumber(), false); } else { ui->tempoSpinBox->setEnabled(true); ui->speedSpinBox->setEnabled(true); ui->grooveSpinBox->setEnabled(false); bt_->toggleTempoOrGrooveInSong(bt_->getCurrentSongNumber(), true); } setModifiedTrue(); } void MainWindow::on_actionExit_triggered() { close(); } void MainWindow::on_actionUndo_triggered() { undo(); } void MainWindow::on_actionRedo_triggered() { redo(); } void MainWindow::on_actionCut_triggered() { if (isEditedPattern_) ui->patternEditor->cutSelectedCells(); } void MainWindow::on_actionCopy_triggered() { if (isEditedPattern_) ui->patternEditor->copySelectedCells(); else if (isEditedOrder_) ui->orderList->copySelectedCells(); } void MainWindow::on_actionPaste_triggered() { if (isEditedPattern_) ui->patternEditor->onPastePressed(); else if (isEditedOrder_) ui->orderList->onPastePressed(); } void MainWindow::on_actionDelete_triggered() { if (isEditedPattern_) ui->patternEditor->onDeletePressed(); else if (isEditedOrder_) ui->orderList->deleteOrder(); else if (isEditedInstList_) on_actionRemove_Instrument_triggered(); } void MainWindow::updateMenuByPattern() { isEditedPattern_ = true; isEditedOrder_ = false; isEditedInstList_ = false; if (bt_->isJamMode()) { // Edit ui->actionPaste->setEnabled(false); ui->actionMix->setEnabled(false); ui->actionOverwrite->setEnabled(false); ui->action_Insert->setEnabled(false); ui->actionDelete->setEnabled(false); // Pattern ui->actionInterpolate->setEnabled(false); ui->actionReverse->setEnabled(false); ui->actionReplace_Instrument->setEnabled(false); ui->actionExpand->setEnabled(false); ui->actionShrink->setEnabled(false); ui->actionDecrease_Note->setEnabled(false); ui->actionIncrease_Note->setEnabled(false); ui->actionDecrease_Octave->setEnabled(false); ui->actionIncrease_Octave->setEnabled(false); ui->actionFine_Decrease_Values->setEnabled(false); ui->actionFine_Increase_Values->setEnabled(false); ui->actionCoarse_D_ecrease_Values->setEnabled(false); ui->actionCoarse_I_ncrease_Values->setEnabled(false); } else { // Edit bool enabled = QApplication::clipboard()->text().startsWith("PATTERN_"); ui->actionPaste->setEnabled(enabled); ui->actionMix->setEnabled(enabled); ui->actionOverwrite->setEnabled(enabled); ui->action_Insert->setEnabled(enabled); ui->actionDelete->setEnabled(true); // Pattern ui->actionInterpolate->setEnabled(isSelectedPattern_); ui->actionReverse->setEnabled(isSelectedPattern_); ui->actionReplace_Instrument->setEnabled( isSelectedPattern_ && ui->instrumentList->currentRow() != -1); ui->actionExpand->setEnabled(isSelectedPattern_); ui->actionShrink->setEnabled(isSelectedPattern_); ui->actionDecrease_Note->setEnabled(true); ui->actionIncrease_Note->setEnabled(true); ui->actionDecrease_Octave->setEnabled(true); ui->actionIncrease_Octave->setEnabled(true); ui->actionFine_Decrease_Values->setEnabled(true); ui->actionFine_Increase_Values->setEnabled(true); ui->actionCoarse_D_ecrease_Values->setEnabled(true); ui->actionCoarse_I_ncrease_Values->setEnabled(true); } updateMenuByPatternSelection(isSelectedPattern_); } void MainWindow::updateMenuByOrder() { isEditedPattern_ = false; isEditedOrder_ = true; isEditedInstList_ = false; // Edit bool enabled = QApplication::clipboard()->text().startsWith("ORDER_"); ui->actionCut->setEnabled(false); ui->actionPaste->setEnabled(enabled); ui->actionMix->setEnabled(false); ui->actionOverwrite->setEnabled(false); ui->action_Insert->setEnabled(false); ui->actionDelete->setEnabled(true); // Song bool canAdd = bt_->canAddNewOrder(bt_->getCurrentSongNumber()); ui->actionInsert_Order->setEnabled(canAdd); //ui->actionRemove_Order->setEnabled(true); ui->actionDuplicate_Order->setEnabled(canAdd); //ui->actionMove_Order_Up->setEnabled(true); //ui->actionMove_Order_Down->setEnabled(true); ui->actionClone_Patterns->setEnabled(canAdd); ui->actionClone_Order->setEnabled(canAdd); // Pattern ui->actionInterpolate->setEnabled(false); ui->actionReverse->setEnabled(false); ui->actionReplace_Instrument->setEnabled(false); ui->actionExpand->setEnabled(false); ui->actionShrink->setEnabled(false); ui->actionDecrease_Note->setEnabled(false); ui->actionIncrease_Note->setEnabled(false); ui->actionDecrease_Octave->setEnabled(false); ui->actionIncrease_Octave->setEnabled(false); ui->actionFine_Decrease_Values->setEnabled(false); ui->actionFine_Increase_Values->setEnabled(false); ui->actionCoarse_D_ecrease_Values->setEnabled(false); ui->actionCoarse_I_ncrease_Values->setEnabled(false); updateMenuByOrderSelection(isSelectedOrder_); } void MainWindow::onCurrentTrackChanged() { SoundSource src = bt_->getCurrentTrackAttribute().source; bool space = (bt_->findFirstFreeInstrumentNumber() != -1); ui->actionNew_Instrument->setEnabled((src != SoundSource::RHYTHM) && space); ui->actionNew_Drumki_t->setEnabled((src == SoundSource::ADPCM) && space); } void MainWindow::updateMenuByInstrumentList() { isEditedPattern_ = false; isEditedOrder_ = false; isEditedInstList_ = true; // Edit ui->actionPaste->setEnabled(false); ui->actionMix->setEnabled(false); ui->actionOverwrite->setEnabled(false); ui->action_Insert->setEnabled(false); ui->actionDelete->setEnabled(true); // Pattern ui->actionInterpolate->setEnabled(false); ui->actionReverse->setEnabled(false); ui->actionReplace_Instrument->setEnabled(false); ui->actionExpand->setEnabled(false); ui->actionShrink->setEnabled(false); ui->actionDecrease_Note->setEnabled(false); ui->actionIncrease_Note->setEnabled(false); ui->actionDecrease_Octave->setEnabled(false); ui->actionIncrease_Octave->setEnabled(false); ui->actionFine_Decrease_Values->setEnabled(false); ui->actionFine_Increase_Values->setEnabled(false); ui->actionCoarse_D_ecrease_Values->setEnabled(false); ui->actionCoarse_I_ncrease_Values->setEnabled(false); } void MainWindow::updateMenuByPatternSelection(bool isSelected) { isSelectedPattern_ = isSelected; if (bt_->isJamMode()) { // Edit ui->actionCopy->setEnabled(false); ui->actionCut->setEnabled(false); // Pattern ui->actionInterpolate->setEnabled(false); ui->actionReverse->setEnabled(false); ui->actionReplace_Instrument->setEnabled(false); ui->actionExpand->setEnabled(false); ui->actionShrink->setEnabled(false); } else { // Edit ui->actionCopy->setEnabled(isSelected); ui->actionCut->setEnabled(isEditedPattern_ ? isSelected : false); // Pattern bool enabled = (isEditedPattern_ && isEditedPattern_) ? isSelected : false; ui->actionInterpolate->setEnabled(enabled); ui->actionReverse->setEnabled(enabled); ui->actionReplace_Instrument->setEnabled( enabled && ui->instrumentList->currentRow() != -1); ui->actionExpand->setEnabled(enabled); ui->actionShrink->setEnabled(enabled); } } void MainWindow::updateMenuByOrderSelection(bool isSelected) { isSelectedOrder_ = isSelected; // Edit ui->actionCopy->setEnabled(isSelected); } void MainWindow::on_actionAll_triggered() { if (isEditedPattern_) ui->patternEditor->onSelectPressed(1); else if (isEditedOrder_) ui->orderList->onSelectPressed(1); } void MainWindow::on_actionNone_triggered() { if (isEditedPattern_) ui->patternEditor->onSelectPressed(0); else if (isEditedOrder_) ui->orderList->onSelectPressed(0); } void MainWindow::on_actionDecrease_Note_triggered() { if (isEditedPattern_) ui->patternEditor->onTransposePressed(false, false); } void MainWindow::on_actionIncrease_Note_triggered() { if (isEditedPattern_) ui->patternEditor->onTransposePressed(false, true); } void MainWindow::on_actionDecrease_Octave_triggered() { if (isEditedPattern_) ui->patternEditor->onTransposePressed(true, false); } void MainWindow::on_actionIncrease_Octave_triggered() { if (isEditedPattern_) ui->patternEditor->onTransposePressed(true, true); } void MainWindow::on_actionInsert_Order_triggered() { ui->orderList->insertOrderBelow(); } void MainWindow::on_actionRemove_Order_triggered() { ui->orderList->deleteOrder(); } void MainWindow::on_actionModule_Properties_triggered() { ModulePropertiesDialog dialog(bt_, config_.lock()->getMixerVolumeFM(), config_.lock()->getMixerVolumeSSG()); if (dialog.exec() == QDialog::Accepted && showUndoResetWarningDialog(tr("Do you want to change song properties?"))) { int instRow = ui->instrumentList->currentRow(); bt_->stopPlaySong(); lockWidgets(false); dialog.onAccepted(); freezeViews(); if (!tickTimerForRealChip_) stream_->stop(); loadModule(); setModifiedTrue(); setWindowTitle(); ui->instrumentList->setCurrentRow(instRow); if (!tickTimerForRealChip_) stream_->start(); assignADPCMSamples(); } } void MainWindow::on_actionNew_Instrument_triggered() { addInstrument(); } void MainWindow::on_actionRemove_Instrument_triggered() { removeInstrument(ui->instrumentList->currentRow()); } void MainWindow::on_actionClone_Instrument_triggered() { cloneInstrument(); } void MainWindow::on_actionDeep_Clone_Instrument_triggered() { deepCloneInstrument(); } void MainWindow::on_actionEdit_triggered() { openInstrumentEditor(); } void MainWindow::on_actionPlay_triggered() { startPlaySong(); } void MainWindow::on_actionPlay_Pattern_triggered() { startPlayPattern(); } void MainWindow::on_actionPlay_From_Start_triggered() { startPlayFromStart(); } void MainWindow::on_actionPlay_From_Cursor_triggered() { startPlayFromCurrentStep(); } void MainWindow::on_actionStop_triggered() { stopPlaySong(); } void MainWindow::on_actionEdit_Mode_triggered() { bt_->toggleJamMode(); ui->orderList->changeEditable(); ui->patternEditor->changeEditable(); if (isEditedOrder_) updateMenuByOrder(); else if (isEditedPattern_) updateMenuByPattern(); bool editable = !bt_->isJamMode(); statusDetail_->setText(editable ? tr("Change to edit mode") : tr("Change to jam mode")); ui->action_Toggle_Bookmark->setEnabled(editable); } void MainWindow::on_actionToggle_Track_triggered() { ui->patternEditor->onToggleTrackPressed(); } void MainWindow::on_actionSolo_Track_triggered() { ui->patternEditor->onSoloTrackPressed(); } void MainWindow::on_actionKill_Sound_triggered() { bt_->killSound(); } void MainWindow::on_actionAbout_triggered() { QMessageBox box(QMessageBox::NoIcon, tr("About"), QString("

BambooTracker v%1

").arg( QString::fromStdString(Version::ofApplicationInString())) + tr("YM2608 Music Tracker
" "Copyright (C) 2018-2021 Rerrah

" "
" "Libraries:
" "- C86CTL by (C) honet (BSD 3-Clause)
" "- libOPNMIDI by (C) Vitaly Novichkov (MIT License part)
" "- MAME (MAME License)
" "- Nuked OPN-MOD by (C) Alexey Khokholov (Nuke.YKT)
" "and (C) Jean Pierre Cimalando (LGPL v2.1)
" "- RtAudio by (C) Gary P. Scavone (RtAudio License)
" "- RtMidi by (C) Gary P. Scavone (RtMidi License)
" "- SCCI by (C) gasshi (SCCI License)
" "- Silk icons by (C) Mark James (CC BY 2.5 or 3.0)
" "- Qt (GPL v2+ or LGPL v3)
" "- VGMPlay by (C) Valley Bell (GPL v2)
" "
" "Also see changelog which lists contributors."), QMessageBox::Ok, this); box.setIconPixmap(QIcon(":/icon/app_icon").pixmap(QSize(44, 44))); box.exec(); } void MainWindow::on_actionFollow_Mode_triggered() { bt_->setFollowPlay(ui->actionFollow_Mode->isChecked()); config_.lock()->setFollowMode(ui->actionFollow_Mode->isChecked()); ui->orderList->onFollowModeChanged(); ui->patternEditor->onFollowModeChanged(); } void MainWindow::on_actionGroove_Settings_triggered() { std::vector> seqs(bt_->getGrooveCount()); std::generate(seqs.begin(), seqs.end(), [&, i = 0]() mutable { return bt_->getGroove(i++); }); GrooveSettingsDialog diag; diag.setGrooveSquences(seqs); if (diag.exec() == QDialog::Accepted) { bt_->stopPlaySong(); lockWidgets(false); bt_->setGrooves(diag.getGrooveSequences()); ui->grooveSpinBox->setMaximum(static_cast(bt_->getGrooveCount()) - 1); setModifiedTrue(); } } void MainWindow::on_actionConfiguration_triggered() { ConfigurationDialog diag(config_.lock(), palette_, stream_); QObject::connect(&diag, &ConfigurationDialog::applyPressed, this, &MainWindow::changeConfiguration); if (diag.exec() == QDialog::Accepted) { changeConfiguration(); io::saveConfiguration(config_.lock()); ColorPaletteHandler::savePalette(palette_.get()); } } void MainWindow::on_actionExpand_triggered() { ui->patternEditor->onExpandPressed(); } void MainWindow::on_actionShrink_triggered() { ui->patternEditor->onShrinkPressed(); } void MainWindow::on_actionDuplicate_Order_triggered() { ui->orderList->onDuplicatePressed(); } void MainWindow::on_actionMove_Order_Up_triggered() { ui->orderList->onMoveOrderPressed(true); } void MainWindow::on_actionMove_Order_Down_triggered() { ui->orderList->onMoveOrderPressed(false); } void MainWindow::on_actionClone_Patterns_triggered() { ui->orderList->onClonePatternsPressed(); } void MainWindow::on_actionClone_Order_triggered() { ui->orderList->onCloneOrderPressed(); } void MainWindow::on_actionNew_triggered() { if (isWindowModified()) { QString modTitle = gui_utils::utf8ToQString(bt_->getModuleTitle()); if (modTitle.isEmpty()) modTitle = tr("Untitled"); QMessageBox dialog(QMessageBox::Warning, "BambooTracker", tr("Save changes to %1?").arg(modTitle), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); switch (dialog.exec()) { case QMessageBox::Yes: if (!on_actionSave_triggered()) return; break; case QMessageBox::No: break; case QMessageBox::Cancel: return; default: break; } } bt_->stopPlaySong(); lockWidgets(false); freezeViews(); if (!tickTimerForRealChip_) stream_->stop(); bt_->makeNewModule(); loadModule(); setInitialSelectedInstrument(); isModifiedForNotCommand_ = false; setWindowModified(false); if (!tickTimerForRealChip_) stream_->start(); assignADPCMSamples(); } void MainWindow::on_actionComments_triggered() { if (commentDiag_) { if (commentDiag_->isVisible()) commentDiag_->activateWindow(); else commentDiag_->show(); } else { commentDiag_ = std::make_unique(gui_utils::utf8ToQString(bt_->getModuleComment())); commentDiag_->show(); QObject::connect(commentDiag_.get(), &CommentEditDialog::commentChanged, this, [&](const QString text) { bt_->setModuleComment(text.toUtf8().toStdString()); setModifiedTrue(); }); } } bool MainWindow::on_actionSave_triggered() { auto path = QString::fromStdString(bt_->getModulePath()); if (!path.isEmpty() && QFileInfo::exists(path) && QFileInfo(path).isFile()) { if (!backupModule(path)) return false; try { QByteArray bytes; { io::BinaryContainer container; bt_->saveModule(container); bytes.reserve(container.size()); std::move(container.begin(), container.end(), std::back_inserter(bytes)); } QFile fp(path); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(path, false, io::FileType::Mod, this); return false; } fp.write(bytes); fp.close(); isModifiedForNotCommand_ = false; isSavedModBefore_ = true; setWindowModified(false); setWindowTitle(); return true; } catch (io::FileIOError& e) { FileIOErrorMessageBox(path, false, e, this).exec(); return false; } catch (std::exception& e) { FileIOErrorMessageBox(path, false, io::FileType::Mod, QString(e.what()), this).exec(); return false; } } else { return on_actionSave_As_triggered(); } } bool MainWindow::on_actionSave_As_triggered() { QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); QString file = QFileDialog::getSaveFileName( this, tr("Save module"), QString("%1/%2.btm").arg(dir.isEmpty() ? "." : dir, getModuleFileBaseName()), tr("BambooTracker module (*.btm)")); if (file.isNull()) return false; if (!file.endsWith(".btm")) file += ".btm"; // For linux if (QFile::exists(file)) { // Backup if the module already exists if (!backupModule(file)) return false; } bt_->setModulePath(file.toStdString()); try { QByteArray bytes; { io::BinaryContainer container; bt_->saveModule(container); bytes.reserve(container.size()); std::move(container.begin(), container.end(), std::back_inserter(bytes)); } QFile fp(file); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(file, false, io::FileType::Mod, this); return false; } fp.write(bytes); fp.close(); isModifiedForNotCommand_ = false; isSavedModBefore_ = true; setWindowModified(false); setWindowTitle(); config_.lock()->setWorkingDirectory(QFileInfo(file).dir().path().toStdString()); changeFileHistory(file); return true; } catch (io::FileIOError& e) { FileIOErrorMessageBox(file, false, e, this).exec(); return false; } catch (std::exception& e) { FileIOErrorMessageBox(file, false, io::FileType::Mod, QString(e.what()), this).exec(); return false; } } void MainWindow::on_actionOpen_triggered() { if (isWindowModified()) { QString modTitle = gui_utils::utf8ToQString(bt_->getModuleTitle()); if (modTitle.isEmpty()) modTitle = tr("Untitled"); QMessageBox dialog(QMessageBox::Warning, "BambooTracker", tr("Save changes to %1?").arg(modTitle), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); switch (dialog.exec()) { case QMessageBox::Yes: if (!on_actionSave_triggered()) return; break; case QMessageBox::No: break; case QMessageBox::Cancel: return; default: break; } } QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); QString file = QFileDialog::getOpenFileName(this, tr("Open module"), (dir.isEmpty() ? "./" : dir), tr("BambooTracker module (*.btm)")); if (file.isNull()) return; bt_->stopPlaySong(); lockWidgets(false); openModule(file); } void MainWindow::on_actionLoad_From_File_triggered() { loadInstrument(); } void MainWindow::on_actionSave_To_File_triggered() { saveInstrument(); } void MainWindow::on_actionImport_From_Bank_File_triggered() { importInstrumentsFromBank(); } void MainWindow::on_actionInterpolate_triggered() { ui->patternEditor->onInterpolatePressed(); } void MainWindow::on_actionReverse_triggered() { ui->patternEditor->onReversePressed(); } void MainWindow::on_actionReplace_Instrument_triggered() { ui->patternEditor->onReplaceInstrumentPressed(); } void MainWindow::on_actionRow_triggered() { if (isEditedPattern_) ui->patternEditor->onSelectPressed(2); else if (isEditedOrder_) ui->orderList->onSelectPressed(2); } void MainWindow::on_actionColumn_triggered() { if (isEditedPattern_) ui->patternEditor->onSelectPressed(3); else if (isEditedOrder_) ui->orderList->onSelectPressed(3); } void MainWindow::on_actionPattern_triggered() { if (isEditedPattern_) ui->patternEditor->onSelectPressed(4); else if (isEditedOrder_) ui->orderList->onSelectPressed(4); } void MainWindow::on_actionOrder_triggered() { if (isEditedPattern_) ui->patternEditor->onSelectPressed(5); else if (isEditedOrder_) ui->orderList->onSelectPressed(5); } void MainWindow::on_actionRemove_Unused_Instruments_triggered() { if (showUndoResetWarningDialog(tr("Do you want to remove all unused instruments?"))) { bt_->stopPlaySong(); lockWidgets(false); auto list = ui->instrumentList; for (auto& n : bt_->getUnusedInstrumentIndices()) { for (int i = 0; i < list->count(); ++i) { if (list->item(i)->data(Qt::UserRole).toInt() == n) { removeInstrument(i); } } } bt_->clearUnusedInstrumentProperties(); bt_->clearCommandHistory(); comStack_->clear(); setModifiedTrue(); } } void MainWindow::on_actionRemove_Unused_Patterns_triggered() { if (showUndoResetWarningDialog(tr("Do you want to remove all unused patterns?"))) { bt_->stopPlaySong(); lockWidgets(false); bt_->clearUnusedPatterns(); bt_->clearCommandHistory(); comStack_->clear(); setModifiedTrue(); } } void MainWindow::on_actionWAV_triggered() { WaveExportSettingsDialog diag; if (diag.exec() != QDialog::Accepted) return; QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); QString path = QFileDialog::getSaveFileName( this, tr("Export to WAV"), QString("%1/%2.wav").arg(dir.isEmpty() ? "." : dir, getModuleFileBaseName()), tr("WAV signed 16-bit PCM (*.wav)")); if (path.isNull()) return; if (!path.endsWith(".wav")) path += ".wav"; // For linux int max = static_cast(bt_->getTotalStepCount( bt_->getCurrentSongNumber(), static_cast(diag.getLoopCount()))) + 3; QProgressDialog progress(tr("Export to WAV"), tr("Cancel"), 0, max); progress.setValue(0); progress.setWindowFlags(progress.windowFlags() & ~Qt::WindowContextHelpButtonHint & ~Qt::WindowCloseButtonHint); progress.show(); bt_->stopPlaySong(); lockWidgets(false); stream_->stop(); try { auto bar = [&progress]() -> bool { QApplication::processEvents(); progress.setValue(progress.value() + 1); return progress.wasCanceled(); }; QByteArray bytes; { const uint32_t rate = static_cast(diag.getSampleRate()); const uint16_t nCh = 2; const int loopCnt = diag.getLoopCount(); io::WavContainer container(rate, nCh, 16); if (!bt_->exportToWav(container, loopCnt, bar)) goto AFTER_WAV_WRITE; // Jump if cancelled bytes.reserve(container.size()); std::move(container.begin(), container.end(), std::back_inserter(bytes)); } QFile fp(path); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(path, false, io::FileType::WAV, this); goto AFTER_WAV_WRITE; // Jump to post process } fp.write(bytes); fp.close(); bar(); config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(path, false, e, this).exec(); } catch (std::exception& e) { FileIOErrorMessageBox(path, false, io::FileType::WAV, QString(e.what()), this).exec(); } AFTER_WAV_WRITE: stream_->start(); } void MainWindow::on_actionVGM_triggered() { VgmExportSettingsDialog diag; if (diag.exec() != QDialog::Accepted) return; io::GD3Tag tag = diag.getGD3Tag(); QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); QString path = QFileDialog::getSaveFileName( this, tr("Export to VGM"), QString("%1/%2.vgm").arg(dir.isEmpty() ? "." : dir, getModuleFileBaseName()), tr("VGM file (*.vgm)")); if (path.isNull()) return; if (!path.endsWith(".vgm")) path += ".vgm"; // For linux int max = static_cast(bt_->getTotalStepCount(bt_->getCurrentSongNumber(), 1)) + 3; QProgressDialog progress(tr("Export to VGM"), tr("Cancel"), 0, max); progress.setValue(0); progress.setWindowFlags(progress.windowFlags() & ~Qt::WindowContextHelpButtonHint & ~Qt::WindowCloseButtonHint); progress.show(); bt_->stopPlaySong(); lockWidgets(false); stream_->stop(); try { auto bar = [&progress]() -> bool { QApplication::processEvents(); progress.setValue(progress.value() + 1); return progress.wasCanceled(); }; QByteArray bytes; { io::BinaryContainer container; if (!bt_->exportToVgm(container, diag.getExportTarget(), diag.enabledGD3(), tag, bar)) goto AFTER_VGM_WRITE; // Jump if cancelled bytes.reserve(container.size()); std::move(container.begin(), container.end(), std::back_inserter(bytes)); } QFile fp(path); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(path, false, io::FileType::VGM, this); goto AFTER_VGM_WRITE; // Jump if cancelled } fp.write(bytes); fp.close(); bar(); config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(path, false, e, this).exec(); } catch (std::exception& e) { FileIOErrorMessageBox(path, false, io::FileType::VGM, QString(e.what()), this).exec(); } AFTER_VGM_WRITE: stream_->start(); } void MainWindow::on_actionS98_triggered() { S98ExportSettingsDialog diag; if (diag.exec() != QDialog::Accepted) return; io::S98Tag tag = diag.getS98Tag(); QString dir = QString::fromStdString(config_.lock()->getWorkingDirectory()); QString path = QFileDialog::getSaveFileName( this, tr("Export to S98"), QString("%1/%2.s98").arg(dir.isEmpty() ? "." : dir, getModuleFileBaseName()), tr("S98 file (*.s98)")); if (path.isNull()) return; if (!path.endsWith(".s98")) path += ".s98"; // For linux int max = static_cast(bt_->getTotalStepCount(bt_->getCurrentSongNumber(), 1)) + 3; QProgressDialog progress(tr("Export to S98"), tr("Cancel"), 0, max); progress.setValue(0); progress.setWindowFlags(progress.windowFlags() & ~Qt::WindowContextHelpButtonHint & ~Qt::WindowCloseButtonHint); progress.show(); bt_->stopPlaySong(); lockWidgets(false); stream_->stop(); try { auto bar = [&progress]() -> bool { QApplication::processEvents(); progress.setValue(progress.value() + 1); return progress.wasCanceled(); }; QByteArray bytes; { io::BinaryContainer container; if (!bt_->exportToS98(container, diag.getExportTarget(), diag.enabledTag(), tag, diag.getResolution(), bar)) goto AFTER_S98_WRITE; // Jump if cancelled bytes.reserve(container.size()); std::move(container.begin(), container.end(), std::back_inserter(bytes)); } QFile fp(path); if (!fp.open(QIODevice::WriteOnly)) { FileIOErrorMessageBox::openError(path, false, io::FileType::S98, this); goto AFTER_S98_WRITE; // Jump to post process } fp.write(bytes); fp.close(); bar(); config_.lock()->setWorkingDirectory(QFileInfo(path).dir().path().toStdString()); } catch (io::FileIOError& e) { FileIOErrorMessageBox(path, false, e, this).exec(); } catch (std::exception& e) { FileIOErrorMessageBox(path, false, io::FileType::S98, QString(e.what()), this).exec(); } AFTER_S98_WRITE: stream_->start(); } void MainWindow::on_actionMix_triggered() { if (isEditedPattern_) ui->patternEditor->onPasteMixPressed(); } void MainWindow::on_actionOverwrite_triggered() { if (isEditedPattern_) ui->patternEditor->onPasteOverwritePressed(); } void MainWindow::onNewTickSignaledRealChip() { onNewTickSignaled(bt_->streamCountUp()); } void MainWindow::onNewTickSignaled(int state) { if (!state) { // New step int order = bt_->getPlayingOrderNumber(); if (order > -1) { // Playing if (isVisible() && !isMinimized()) { ui->orderList->updatePositionByOrderUpdate(firstViewUpdateRequest_); ui->patternEditor->updatePositionByStepUpdate(firstViewUpdateRequest_); firstViewUpdateRequest_ = false; } int width, base; if (config_.lock()->getShowRowNumberInHex()) { width = 2; base = 16; } else { width = 3; base = 10; } statusPlayPos_->setText( QString("%1/%2") .arg(order, width, base, QChar('0')) .arg(bt_->getPlayingStepNumber(), width, base, QChar('0')).toUpper()); } } else if (state == -1) { if (hasLockedWigets_) lockWidgets(false); } // Update BPM status if (bt_->getStreamGrooveEnabled()) { statusBpm_->setText(tr("Groove")); } else { // BPM = tempo * 6 / speed * 4 / 1st highlight double bpm = 24.0 * bt_->getStreamTempo() / bt_->getStreamSpeed() / highlight1_->value(); statusBpm_->setText(QString::number(bpm, 'f', 2) + QString(" BPM")); } } void MainWindow::on_actionClear_triggered() { fileHistory_->clearHistory(); for (int i = ui->menu_Recent_Files->actions().count() - 1; 1 < i; --i) ui->menu_Recent_Files->removeAction(ui->menu_Recent_Files->actions().at(i)); } void MainWindow::on_keyRepeatCheckBox_stateChanged(int arg1) { config_.lock()->setKeyRepetition(arg1 == Qt::Checked); } void MainWindow::updateVisuals() { int16_t wave[2 * bt_defs::OUTPUT_HISTORY_SIZE]; bt_->getOutputHistory(wave); ui->waveVisual->setStereoSamples(wave, bt_defs::OUTPUT_HISTORY_SIZE); } void MainWindow::on_action_Effect_List_triggered() { if (effListDiag_) { if (effListDiag_->isVisible()) effListDiag_->activateWindow(); else effListDiag_->show(); } else { effListDiag_ = std::make_unique(); effListDiag_->show(); } } void MainWindow::on_actionShortcuts_triggered() { if (shortcutsDiag_) { if (shortcutsDiag_->isVisible()) shortcutsDiag_->activateWindow(); else shortcutsDiag_->show(); } else { shortcutsDiag_ = std::make_unique(); shortcutsDiag_->show(); } } void MainWindow::on_actionExport_To_Bank_File_triggered() { exportInstrumentsToBank(); } void MainWindow::on_actionRemove_Duplicate_Instruments_triggered() { if (showUndoResetWarningDialog(tr("Do you want to remove all duplicate instruments?"))) { bt_->stopPlaySong(); lockWidgets(false); std::unordered_map rplMap = bt_->replaceDuplicateInstrumentsInPatterns(); auto list = ui->instrumentList; for (auto& pairs : rplMap) { for (int j = 0; j < list->count(); ++j) { if (list->item(j)->data(Qt::UserRole).toInt() == pairs.first) removeInstrument(j); } } bt_->clearUnusedInstrumentProperties(); bt_->clearCommandHistory(); comStack_->clear(); ui->patternEditor->onDuplicateInstrumentsRemoved(); setModifiedTrue(); } } void MainWindow::on_actionRename_Instrument_triggered() { renameInstrument(); } void MainWindow::on_action_Bookmark_Manager_triggered() { if (bmManForm_->isVisible()) bmManForm_->activateWindow(); else bmManForm_->show(); } void MainWindow::on_actionFine_Decrease_Values_triggered() { if (isEditedPattern_) ui->patternEditor->onChangeValuesPressed(false, false); } void MainWindow::on_actionFine_Increase_Values_triggered() { if (isEditedPattern_) ui->patternEditor->onChangeValuesPressed(false, true); } void MainWindow::on_actionCoarse_D_ecrease_Values_triggered() { if (isEditedPattern_) ui->patternEditor->onChangeValuesPressed(true, false); } void MainWindow::on_actionCoarse_I_ncrease_Values_triggered() { if (isEditedPattern_) ui->patternEditor->onChangeValuesPressed(true, true); } void MainWindow::on_action_Toggle_Bookmark_triggered() { bmManForm_->onBookmarkToggleRequested(bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber()); } void MainWindow::on_action_Next_Bookmark_triggered() { bmManForm_->onBookmarkJumpRequested(true, bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber()); } void MainWindow::on_action_Previous_Bookmark_triggered() { bmManForm_->onBookmarkJumpRequested(false, bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber()); } void MainWindow::on_action_Instrument_Mask_triggered() { config_.lock()->setInstrumentMask(ui->action_Instrument_Mask->isChecked()); } void MainWindow::on_action_Volume_Mask_triggered() { config_.lock()->setVolumeMask(ui->action_Volume_Mask->isChecked()); } void MainWindow::on_actionSet_Ro_w_Marker_triggered() { bt_->setMarker(bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber()); ui->patternEditor->changeMarker(); } void MainWindow::on_actionPlay_From_Marker_triggered() { startPlayFromMarker(); } void MainWindow::on_action_Go_To_triggered() { GoToDialog diag(bt_); if (diag.exec() == QDialog::Accepted) { if (!bt_->isPlaySong() || !bt_->isFollowPlay()) { bt_->setCurrentOrderNumber(diag.getOrder()); bt_->setCurrentStepNumber(diag.getStep()); bt_->setCurrentTrack(diag.getTrack()); ui->orderList->updatePositionByPositionJump(true); ui->patternEditor->updatepositionByPositionJump(true); } } } void MainWindow::on_actionRemove_Unused_ADPCM_Samples_triggered() { if (showUndoResetWarningDialog(tr("Do you want to remove all unused ADPCM samples?"))) { bt_->stopPlaySong(); lockWidgets(false); bt_->clearUnusedADPCMSamples(); assignADPCMSamples(); bt_->clearCommandHistory(); comStack_->clear(); setModifiedTrue(); } } void MainWindow::on_action_Status_Bar_triggered() { ui->statusBar->setVisible(ui->action_Status_Bar->isChecked()); } void MainWindow::on_action_Toolbar_triggered() { bool visible = ui->action_Toolbar->isChecked(); ui->mainToolBar->setVisible(visible); ui->subToolBar->setVisible(visible); } void MainWindow::on_actionNew_Drumki_t_triggered() { addDrumkit(); } void MainWindow::on_action_Wave_View_triggered(bool checked) { config_.lock()->setVisibleWaveView(checked); ui->waveVisual->setVisible(checked); if (checked) visualTimer_->start(static_cast(std::round(1000. / config_.lock()->getWaveViewFrameRate()))); else visualTimer_->stop(); } void MainWindow::on_action_Transpose_Song_triggered() { TransposeSongDialog diag; if (diag.exec() == QDialog::Accepted) { if (showUndoResetWarningDialog(tr("Do you want to transpose a song?"))) { bt_->stopPlaySong(); lockWidgets(false); bt_->transposeSong(bt_->getCurrentSongNumber(), diag.getTransposeSeminotes(), diag.getExcludeInstruments()); ui->patternEditor->onPatternDataGlobalChanged(); bt_->clearCommandHistory(); comStack_->clear(); setModifiedTrue(); } } } void MainWindow::on_action_Swap_Tracks_triggered() { SwapTracksDialog diag(bt_->getSongStyle(bt_->getCurrentSongNumber())); if (diag.exec() == QDialog::Accepted) { int track1 = diag.getTrack1(); int track2 = diag.getTrack2(); if ((track1 != track2) && showUndoResetWarningDialog(tr("Do you want to swap tracks?"))) { bt_->stopPlaySong(); lockWidgets(false); bt_->swapTracks(bt_->getCurrentSongNumber(), track1, track2); ui->orderList->onOrderDataGlobalChanged(); ui->patternEditor->onPatternDataGlobalChanged(); bt_->clearCommandHistory(); comStack_->clear(); setModifiedTrue(); } } } void MainWindow::on_action_Insert_triggered() { if (isEditedPattern_) ui->patternEditor->onPasteInsertPressed(); } void MainWindow::on_action_Hide_Tracks_triggered() { HideTracksDialog diag(bt_->getSongStyle(bt_->getCurrentSongNumber()), ui->patternEditor->getVisibleTracks()); if (diag.exec() == QDialog::Accepted) { setTrackVisibility(diag.getVisibleTracks()); } } void MainWindow::on_action_Estimate_Song_Length_triggered() { double time = bt_->getApproximateSongLength(bt_->getCurrentSongNumber()); int seconds = static_cast(std::round(time)); QMessageBox box; box.setIcon(QMessageBox::Information); box.setText(tr("Approximate song length: %1m%2s") .arg(seconds / 60).arg(seconds % 60, 2, 10, QChar('0'))); box.exec(); } BambooTracker-0.4.6/BambooTracker/gui/mainwindow.hpp000066400000000000000000000275551401124043500224300ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "enum_hash.hpp" #include "configuration.hpp" #include "bamboo_tracker.hpp" #include "precise_timer.hpp" #include "audio/audio_stream.hpp" #include "gui/instrument_editor/instrument_form_manager.hpp" #include "gui/color_palette.hpp" #include "gui/file_history.hpp" #include "gui/effect_list_dialog.hpp" #include "gui/keyboard_shortcut_list_dialog.hpp" #include "gui/bookmark_manager_form.hpp" #include "gui/instrument_selection_dialog.hpp" #include "gui/comment_edit_dialog.hpp" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(std::weak_ptr config, QString filePath, QWidget *parent = nullptr); ~MainWindow() override; void assignADPCMSamples(); protected: bool eventFilter(QObject* watched, QEvent* event) override; void showEvent(QShowEvent* event) override; void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; void resizeEvent(QResizeEvent* event) override; void moveEvent(QMoveEvent* event) override; void closeEvent(QCloseEvent* event) override; // Midi private: static void midiThreadReceivedEvent(double delay, const uint8_t *msg, size_t len, void *userData); private slots: void midiKeyEvent(uchar status, uchar key, uchar velocity); void midiProgramEvent(uchar status, uchar program); private: std::unique_ptr ui; std::weak_ptr config_; std::shared_ptr palette_; std::shared_ptr bt_; std::shared_ptr stream_; std::unique_ptr tickTimerForRealChip_; std::unique_ptr visualTimer_; std::shared_ptr comStack_; std::shared_ptr fileHistory_; std::unique_ptr scciDll_, c86ctlDll_; // Instrument list std::shared_ptr instForms_; QListWidgetItem* renamingInstItem_; QLineEdit* renamingInstEdit_; void addInstrument(); void addDrumkit(); void removeInstrument(int row); void openInstrumentEditor(); int findRowFromInstrumentList(int instNum); void renameInstrument(); void finishRenamingInstrument(); void cloneInstrument(); void deepCloneInstrument(); void loadInstrument(); void funcLoadInstrument(QString file); void saveInstrument(); void importInstrumentsFromBank(); void funcImportInstrumentsFromBank(QString file); void exportInstrumentsToBank(); void swapInstruments(int row1, int row2); // Undo-Redo void undo(); void redo(); bool isModifiedForNotCommand_; // Load data void loadModule(); void openModule(QString file); void loadSong(); // Play song void startPlaySong(); void startPlayFromStart(); void startPlayPattern(); void startPlayFromCurrentStep(); void startPlayFromMarker(); void playStep(); void stopPlaySong(); bool hasLockedWigets_; void lockWidgets(bool isLock); // Octave change void changeOctave(bool upFlag); // Configuration change void changeConfiguration(); void setRealChipInterface(RealChipInterface intf); void setMidiConfiguration(); void updateFonts(); // History change void changeFileHistory(QString file); // Backup bool backupModule(QString srcFile); void setWindowTitle(); void setModifiedTrue(); void setInitialSelectedInstrument(); QString getModuleFileBaseName() const; int getSelectedFileFilter(QString& file, QStringList& filters) const; bool isEditedPattern_, isEditedOrder_, isEditedInstList_; bool isSelectedPattern_, isSelectedOrder_; bool hasShownOnce_; bool isSavedModBefore_; bool firstViewUpdateRequest_; // Menus std::unique_ptr pasteModeGroup_; // Toolbar QSpinBox *octave_, *highlight1_, *highlight2_, *volume_; // Status bars QLabel *statusDetail_, *statusStyle_, *statusInst_, *statusOctave_; QLabel *statusIntr_, *statusMixer_, *statusBpm_, *statusPlayPos_; // Shortcuts QAction octUpSc_, octDownSc_; QShortcut focusPtnSc_, focusOdrSc_, focusInstSc_; std::unique_ptr instAddSc_; QAction playAndStopSc_, playStepSc_, goPrevOdrSc_, goNextOdrSc_, prevInstSc_, nextInstSc_; QAction incPtnSizeSc_, decPtnSizeSc_, incEditStepSc_, decEditStepSc_, prevSongSc_, nextSongSc_; QAction jamVolUpSc_, jamVolDownSc_; void setShortcuts(); // Dialogs std::unique_ptr effListDiag_; std::unique_ptr shortcutsDiag_; std::unique_ptr bmManForm_; std::unique_ptr commentDiag_; // Bank import std::atomic_bool bankJamMidiCtrl_; std::unique_ptr importBankDiag_; // Track visibility void setTrackVisibility(const std::vector& visTracks); // Meta methods int tickEventMethod_; int midiKeyEventMethod_; int midiProgramEventMethod_; void updateInstrumentListColors(); void setOrderListGroupMaximumWidth(); void freezeViews(); void unfreezeViews(); inline bool showUndoResetWarningDialog(QString text) { return (QMessageBox::warning(this, tr("Warning"), tr("%1 If you execute this command, the command history is reset.").arg(text), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes); } inline void showStreamFailedDialog(const QString& errDetail) { QMessageBox::critical(this, tr("Error"), tr("Could not open the audio stream. Please change the sound settings in Configuration.") + "\n\n" + errDetail, QMessageBox::Ok, QMessageBox::Ok); } inline void showStreamRateWarningDialog(uint32_t curRate) { QMessageBox::warning(this, tr("Warning"), tr("Could not set the sample rate of the audio stream to %1Hz. Currently the stream runs on %2Hz instead.") .arg(config_.lock()->getSampleRate()).arg(curRate), QMessageBox::Ok, QMessageBox::Ok); } inline void showMidiFailedDialog(const QString& errDetail) { QMessageBox::critical(this, tr("Error"), tr("Could not initialize MIDI input.") + "\n\n" + errDetail, QMessageBox::Ok, QMessageBox::Ok); } private slots: void on_instrumentList_customContextMenuRequested(const QPoint& pos); void on_instrumentList_itemDoubleClicked(QListWidgetItem* item); void on_instrumentList_itemSelectionChanged(); void on_grooveCheckBox_stateChanged(int arg1); void on_actionExit_triggered(); void on_actionUndo_triggered(); void on_actionRedo_triggered(); void on_actionCut_triggered(); void on_actionCopy_triggered(); void on_actionPaste_triggered(); void on_actionDelete_triggered(); void updateMenuByPattern(); void updateMenuByOrder(); void onCurrentTrackChanged(); void updateMenuByInstrumentList(); void updateMenuByPatternSelection(bool isSelected); void updateMenuByOrderSelection(bool isSelected); void on_actionAll_triggered(); void on_actionNone_triggered(); void on_actionDecrease_Note_triggered(); void on_actionIncrease_Note_triggered(); void on_actionDecrease_Octave_triggered(); void on_actionIncrease_Octave_triggered(); void on_actionInsert_Order_triggered(); void on_actionRemove_Order_triggered(); void on_actionModule_Properties_triggered(); void on_actionNew_Instrument_triggered(); void on_actionRemove_Instrument_triggered(); void on_actionClone_Instrument_triggered(); void on_actionDeep_Clone_Instrument_triggered(); void on_actionEdit_triggered(); void on_actionPlay_triggered(); void on_actionPlay_Pattern_triggered(); void on_actionPlay_From_Start_triggered(); void on_actionPlay_From_Cursor_triggered(); void on_actionStop_triggered(); void on_actionEdit_Mode_triggered(); void on_actionToggle_Track_triggered(); void on_actionSolo_Track_triggered(); void on_actionKill_Sound_triggered(); void on_actionAbout_triggered(); void on_actionFollow_Mode_triggered(); void on_actionGroove_Settings_triggered(); void on_actionConfiguration_triggered(); void on_actionExpand_triggered(); void on_actionShrink_triggered(); void on_actionDuplicate_Order_triggered(); void on_actionMove_Order_Up_triggered(); void on_actionMove_Order_Down_triggered(); void on_actionClone_Patterns_triggered(); void on_actionClone_Order_triggered(); void on_actionNew_triggered(); void on_actionComments_triggered(); bool on_actionSave_triggered(); bool on_actionSave_As_triggered(); void on_actionOpen_triggered(); void on_actionLoad_From_File_triggered(); void on_actionSave_To_File_triggered(); void on_actionImport_From_Bank_File_triggered(); void on_actionInterpolate_triggered(); void on_actionReverse_triggered(); void on_actionReplace_Instrument_triggered(); void on_actionRow_triggered(); void on_actionColumn_triggered(); void on_actionPattern_triggered(); void on_actionOrder_triggered(); void on_actionRemove_Unused_Instruments_triggered(); void on_actionRemove_Unused_Patterns_triggered(); void on_actionWAV_triggered(); void on_actionVGM_triggered(); void on_actionS98_triggered(); void on_actionMix_triggered(); void on_actionOverwrite_triggered(); void onNewTickSignaledRealChip(); void onNewTickSignaled(int state); void on_actionClear_triggered(); void on_keyRepeatCheckBox_stateChanged(int arg1); void updateVisuals(); void on_action_Effect_List_triggered(); void on_actionShortcuts_triggered(); void on_actionExport_To_Bank_File_triggered(); void on_actionRemove_Duplicate_Instruments_triggered(); void on_actionRename_Instrument_triggered(); void on_action_Bookmark_Manager_triggered(); void on_actionFine_Decrease_Values_triggered(); void on_actionFine_Increase_Values_triggered(); void on_actionCoarse_D_ecrease_Values_triggered(); void on_actionCoarse_I_ncrease_Values_triggered(); void on_action_Toggle_Bookmark_triggered(); void on_action_Next_Bookmark_triggered(); void on_action_Previous_Bookmark_triggered(); void on_action_Instrument_Mask_triggered(); void on_action_Volume_Mask_triggered(); void on_actionSet_Ro_w_Marker_triggered(); void on_actionPlay_From_Marker_triggered(); void on_action_Go_To_triggered(); void on_actionRemove_Unused_ADPCM_Samples_triggered(); void on_action_Status_Bar_triggered(); void on_action_Toolbar_triggered(); void on_actionNew_Drumki_t_triggered(); void on_action_Wave_View_triggered(bool checked); void on_action_Transpose_Song_triggered(); void on_action_Swap_Tracks_triggered(); void on_action_Insert_triggered(); void on_action_Hide_Tracks_triggered(); void on_action_Estimate_Song_Length_triggered(); }; #endif // MAINWINDOW_HPP BambooTracker-0.4.6/BambooTracker/gui/mainwindow.ui000066400000000000000000001471261401124043500222530ustar00rootroot00000000000000 MainWindow 0 0 900 700 true BambooTracker 0 0 0 0 0 Qt::Vertical true 9 9 9 9 9 0 0 Order List 3 3 3 3 0 0 200 0 3 QLayout::SetDefaultConstraint 0 0 Song Settings 6 6 6 6 6 0 0 Tempo Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false 32 255 150 0 0 Speed Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false 1 31 6 0 0 Pattern size Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false 1 256 64 0 0 Groove false false # 127 0 0 Edit settings 6 6 6 6 6 0 0 Step Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 0 0 false 0 256 0 0 Key repetition true 3 QLayout::SetDefaultConstraint 0 0 Module Settings 6 6 6 6 0 0 Title 0 0 Author 0 0 Copyright 0 0 180 45 Songs 6 6 6 6 0 0 0 0 Instruments 0 3 3 3 3 0 0 Qt::CustomContextMenu QFrame::Panel false true QAbstractItemView::DragDrop Qt::ActionMask 16 16 true QListView::Adjust 0 0 0 0 900 21 &File &Export &Recent Files &Edit &Select Paste Specia&l &Bookmarks Patter&n &Transpose &Change Values S&ong &Module Clean&up &Instrument &Tracker &Help Vie&w Main toolbar 16 16 TopToolBarArea false Secondary toolbar TopToolBarArea false true :/icon/new:/icon/new &New... Ctrl+N true :/icon/open:/icon/open &Open... Ctrl+O true :/icon/save:/icon/save &Save Ctrl+S true Save &As... E&xit false :/icon/undo:/icon/undo &Undo Ctrl+Z false :/icon/redo:/icon/redo &Redo Ctrl+Y :/icon/cut:/icon/cut Cu&t Ctrl+X :/icon/copy:/icon/copy &Copy Ctrl+C :/icon/paste:/icon/paste &Paste Ctrl+V &Delete Del &All Ctrl+A &None Esc false E&xpand false S&hrink &Decrease Note Ctrl+F1 &Increase Note Ctrl+F2 D&ecrease Octave Ctrl+F3 I&ncrease Octave Ctrl+F4 :/icon/insert_order:/icon/insert_order &Insert Order :/icon/remove_order:/icon/remove_order &Remove Order :/icon/property:/icon/property &Module Properties... Ctrl+P :/icon/add_inst:/icon/add_inst &New Instrument false :/icon/remove_inst:/icon/remove_inst &Remove Instrument false :/icon/clone_inst:/icon/clone_inst &Clone Instrument false &Deep Clone Instrument true :/icon/load_inst:/icon/load_inst &Load From File... false :/icon/save_inst:/icon/save_inst &Save To File... false :/icon/edit_inst:/icon/edit_inst &Edit... Ctrl+I :/icon/play:/icon/play &Play :/icon/play_pattern:/icon/play_pattern Play P&attern F6 Play &From Start F5 Play From C&ursor F7 :/icon/stop:/icon/stop &Stop F8 true :/icon/record:/icon/record &Edit Mode Space To&ggle Track Alt+F9 S&olo Track Alt+F10 &Kill Sound F12 &About... true true Fo&llow Mode ScrollLock &Groove Settings... :/icon/config:/icon/config &Configuration... :/icon/duplicate_order:/icon/duplicate_order &Duplicate Order Ctrl+D :/icon/order_up:/icon/order_up Move Order &Up :/icon/order_down:/icon/order_down Move Order Do&wn &Clone Patterns Alt+D Clone &Order &Comments... false &Interpolate Ctrl+G false &Reverse Ctrl+R false R&eplace Instrument Alt+S &Row &Column &Pattern &Order Remove Unused &Instruments Remove Unused &Patterns &WAV... &VGM... &Mix Ctrl+M &Overwrite &Import From Bank File... &S98... &Clear &Effect List... Effect List F1 &Shortcuts... false E&xport To Bank File... Remove &Duplicate Instruments false :/icon/rename_inst:/icon/rename_inst Re&name Instrument &Bookmark Manager... Fine &Decrease Values Shift+F1 Fine &Increase Values Shift+F2 Coarse D&ecrease Values Shift+F3 Coarse I&ncrease Values Shift+F4 false &Toggle Bookmark Ctrl+K &Next Bookmark Ctrl+PgDown &Previous Bookmark Ctrl+PgUp true &Instrument Mask true &Volume Mask Set Ro&w Marker Ctrl+B Play From &Marker Ctrl+F7 &Go To... Alt+G Remove Unused &ADPCM Samples true true &Status Bar true true &Toolbar false New Drumki&t true true &Wave View &Transpose Song... &Swap Tracks... &Insert true &Cursor true &Selection true &Fill &Hide Tracks... &Estimate Song Length... PatternEditor QFrame
gui/pattern_editor/pattern_editor.hpp
1
OrderListEditor QFrame
gui/order_list_editor/order_list_editor.hpp
1
WaveVisual QWidget
gui/wave_visual.hpp
1
DropDetectListWidget QListWidget
gui/drop_detect_list_widget.hpp
tempoSpinBox speedSpinBox patternSizeSpinBox grooveCheckBox grooveSpinBox editableStepSpinBox keyRepeatCheckBox modTitleLineEdit authorLineEdit copyrightLineEdit songComboBox instrumentList
BambooTracker-0.4.6/BambooTracker/gui/module_properties_dialog.cpp000066400000000000000000000214021401124043500253100ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "module_properties_dialog.hpp" #include "ui_module_properties_dialog.h" #include #include #include "gui/gui_utils.hpp" const std::unordered_map ModulePropertiesDialog::SONG_TYPE_TEXT_ = { { SongType::Standard, QT_TR_NOOP("Standard") }, { SongType::FM3chExpanded, QT_TR_NOOP("FM3ch expanded") } }; ModulePropertiesDialog::ModulePropertiesDialog(std::weak_ptr core, double configFmMixer, double configSsgMixer, QWidget *parent) : QDialog(parent), ui(new Ui::ModulePropertiesDialog), bt_(core), configFmMixer_(configFmMixer), configSsgMixer_(configSsgMixer) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); int tickFreq = static_cast(core.lock()->getModuleTickFrequency()); ui->customTickFreqSpinBox->setValue(tickFreq); switch (tickFreq) { case 60: ui->ntscRadioButton->setChecked(true); break; case 50: ui->palRadioButton->setChecked(true); break; default: ui->customTickFreqRadioButton->setChecked(true); break; } MixerType mixType = core.lock()->getModuleMixerType(); if (mixType == MixerType::UNSPECIFIED) { ui->mixerGroupBox->setChecked(false); } else { ui->mixerGroupBox->setChecked(true); ui->mixerTypeComboBox->setCurrentIndex(static_cast(mixType) - 1); } setCustomMixerLevels(core.lock()->getModuleCustomMixerFMLevel(), core.lock()->getModuleCustomMixerSSGLevel()); ui->songTreeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); int songCnt = static_cast(core.lock()->getSongCount()); for (int i = 0; i < songCnt; ++i) { auto title = core.lock()->getSongTitle(i); insertSong(i, gui_utils::utf8ToQString(title), core.lock()->getSongStyle(i).type, i); } ui->sngTypeComboBox->addItem(SONG_TYPE_TEXT_.at(SongType::Standard), static_cast(SongType::Standard)); ui->sngTypeComboBox->addItem(SONG_TYPE_TEXT_.at(SongType::FM3chExpanded), static_cast(SongType::FM3chExpanded)); } ModulePropertiesDialog::~ModulePropertiesDialog() { delete ui; } void ModulePropertiesDialog::insertSong(int row, QString title, SongType type, int prevNum) { QTreeWidgetItem* item = new QTreeWidgetItem(); item->setText(0, QString::number(row)); item->setData(0, Qt::UserRole, prevNum); item->setText(1, title); item->setText(2, SONG_TYPE_TEXT_.at(type)); item->setData(2, Qt::UserRole, static_cast(type)); ui->songTreeWidget->insertTopLevelItem(row, item); for (int i = row + 1; i < ui->songTreeWidget->topLevelItemCount(); ++i) { ui->songTreeWidget->topLevelItem(i)->setText(0, QString::number(i)); } checkButtonsEnabled(); } void ModulePropertiesDialog::checkButtonsEnabled() { if (ui->songTreeWidget->currentItem() != nullptr && ui->songTreeWidget->topLevelItemCount() > 1) { ui->upToolButton->setEnabled(true); ui->downToolButton->setEnabled(true); ui->removePushButton->setEnabled(true); } else { ui->upToolButton->setEnabled(false); ui->downToolButton->setEnabled(false); ui->removePushButton->setEnabled(false); } } void ModulePropertiesDialog::swapset(int aboveRow, int belowRow) { auto* tree = ui->songTreeWidget; QTreeWidgetItem* below = tree->takeTopLevelItem(belowRow); if (tree->topLevelItemCount() > 2) { QTreeWidgetItem* above = tree->takeTopLevelItem(aboveRow); tree->insertTopLevelItem(aboveRow, below); tree->insertTopLevelItem(belowRow, above); } else { tree->insertTopLevelItem(aboveRow, below); } for (int i = aboveRow; i < ui->songTreeWidget->topLevelItemCount(); ++i) { ui->songTreeWidget->topLevelItem(i)->setText(0, QString::number(i)); } } void ModulePropertiesDialog::setCustomMixerLevels(double fm, double ssg) { fmMixer_ = fm; ssgMixer_ = ssg; ui->customMixerFMLevelLabel->setText(QString::asprintf("%+.1fdB", fmMixer_)); ui->customMixerSSGLevelLabel->setText(QString::asprintf("%+.1fdB", ssgMixer_)); } /******************************/ void ModulePropertiesDialog::on_upToolButton_clicked() { int curRow = ui->songTreeWidget->currentIndex().row(); if (!curRow) return; swapset(curRow - 1, curRow); ui->songTreeWidget->setCurrentItem(ui->songTreeWidget->topLevelItem(curRow - 1)); } void ModulePropertiesDialog::on_downToolButton_clicked() { int curRow = ui->songTreeWidget->currentIndex().row(); if (curRow == ui->songTreeWidget->topLevelItemCount() - 1) return; swapset(curRow, curRow + 1); ui->songTreeWidget->setCurrentItem(ui->songTreeWidget->topLevelItem(curRow + 1)); } void ModulePropertiesDialog::on_removePushButton_clicked() { int row = ui->songTreeWidget->currentIndex().row(); auto del = ui->songTreeWidget->takeTopLevelItem(row); delete del; for (int i = row; i < ui->songTreeWidget->topLevelItemCount(); ++ i) { ui->songTreeWidget->topLevelItem(i)->setText(0, QString::number(i)); } checkButtonsEnabled(); } void ModulePropertiesDialog::on_insertPushButton_clicked() { int row = ui->songTreeWidget->currentIndex().row(); if (row == -1) row = ui->songTreeWidget->topLevelItemCount(); insertSong(row, ui->sngTitleLineEdit->text(), static_cast(ui->sngTypeComboBox->currentData(Qt::UserRole).toInt())); ui->songTreeWidget->setCurrentItem(ui->songTreeWidget->topLevelItem(row)); } void ModulePropertiesDialog::on_songTreeWidget_itemSelectionChanged() { auto item = ui->songTreeWidget->currentItem(); ui->sngTitleLineEdit->setText(item->text(1)); int type = item->data(2, Qt::UserRole).toInt(); for (int i = 0; i < ui->sngTypeComboBox->count(); ++i) { if (ui->sngTypeComboBox->itemData(i, Qt::UserRole).toInt() == type) { ui->sngTypeComboBox->setCurrentIndex(i); break; } } checkButtonsEnabled(); } void ModulePropertiesDialog::onAccepted() { // Set tick frequency unsigned int tickFreq; if (ui->ntscRadioButton->isChecked()) tickFreq = 60; else if (ui->palRadioButton->isChecked()) tickFreq = 50; else tickFreq = static_cast(ui->customTickFreqSpinBox->value()); bt_.lock()->setModuleTickFrequency(tickFreq); // Set mixer if (ui->mixerGroupBox->isChecked()) { auto mixType = static_cast(ui->mixerTypeComboBox->currentIndex() + 1); bt_.lock()->setModuleMixerType(mixType); if (mixType == MixerType::CUSTOM) { bt_.lock()->setModuleCustomMixerFMLevel(fmMixer_); bt_.lock()->setModuleCustomMixerSSGLevel(ssgMixer_); } } else { bt_.lock()->setModuleMixerType(MixerType::UNSPECIFIED); } auto* tree = ui->songTreeWidget; std::vector newSongNums; for (int i = 0; i < tree->topLevelItemCount(); ++i) { QTreeWidgetItem* item = tree->topLevelItem(i); SongType type = static_cast(item->data(2, Qt::UserRole).toInt()); std::string title = item->text(1).toUtf8().toStdString(); if (item->data(0, Qt::UserRole).toInt() == -1) { // Add new song int n = static_cast(bt_.lock()->getSongCount()); bt_.lock()->addSong(type, title); newSongNums.push_back(n); } else { // Update song data int n = item->data(0, Qt::UserRole).toInt(); bt_.lock()->setSongTitle(n, title); if (bt_.lock()->getSongStyle(n).type != type) bt_.lock()->changeSongType(n, type); newSongNums.push_back(n); } } // Sort songs bt_.lock()->sortSongs(std::move(newSongNums)); } void ModulePropertiesDialog::on_mixerTypeComboBox_currentIndexChanged(int index) { ui->mixerCustomGroupBox->setEnabled(index == 0); } void ModulePropertiesDialog::on_customMixerSetPushButton_clicked() { setCustomMixerLevels(configFmMixer_, configSsgMixer_); } void ModulePropertiesDialog::on_updateButton_clicked() { if (auto item = ui->songTreeWidget->currentItem()) { item->setText(1, ui->sngTitleLineEdit->text()); auto typeInt = ui->sngTypeComboBox->currentData(Qt::UserRole).toInt(); item->setText(2, SONG_TYPE_TEXT_.at(static_cast(typeInt))); item->setData(2, Qt::UserRole, typeInt); } } BambooTracker-0.4.6/BambooTracker/gui/module_properties_dialog.hpp000066400000000000000000000047031401124043500253220ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MODULE_PROPERTIES_DIALOG_HPP #define MODULE_PROPERTIES_DIALOG_HPP #include #include #include #include #include #include "bamboo_tracker.hpp" #include "enum_hash.hpp" namespace Ui { class ModulePropertiesDialog; } class ModulePropertiesDialog : public QDialog { Q_OBJECT public: ModulePropertiesDialog(std::weak_ptr core, double configFmMixer, double configSsgMixer, QWidget *parent = nullptr); ~ModulePropertiesDialog() override; public slots: void onAccepted(); private slots: void on_upToolButton_clicked(); void on_downToolButton_clicked(); void on_removePushButton_clicked(); void on_insertPushButton_clicked(); void on_songTreeWidget_itemSelectionChanged(); void on_mixerTypeComboBox_currentIndexChanged(int index); void on_customMixerSetPushButton_clicked(); void on_updateButton_clicked(); private: Ui::ModulePropertiesDialog *ui; std::weak_ptr bt_; double fmMixer_, ssgMixer_; double configFmMixer_, configSsgMixer_; static const std::unordered_map SONG_TYPE_TEXT_; void insertSong(int row, QString title, SongType type, int prevNum = -1); void checkButtonsEnabled(); void swapset(int aboveRow, int belowRow); void setCustomMixerLevels(double fm, double ssg); }; #endif // MODULE_PROPERTIES_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/module_properties_dialog.ui000066400000000000000000000307511401124043500251520ustar00rootroot00000000000000 ModulePropertiesDialog 0 0 417 491 Module properties Tick frequency 60Hz (NTSC) true tickFreqButtonGroup 50Hz (PAL) tickFreqButtonGroup 0 Custom tickFreqButtonGroup Hz 1 511 60 Mixer true true 1 Custom PC-9821 with PC-9801-86 PC-9821 with Speak Board PC-88VA2 PC-8801mkIISR false Custom mixer FM Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QFrame::WinPanel QFrame::Sunken +0.0dB Qt::AlignCenter SSG Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QFrame::WinPanel QFrame::Sunken +0.0dB Qt::AlignCenter Set Song control Song Title Song type true Qt::Horizontal 40 20 0 0 Insert Update Untitled false 0 0 Qt::DownArrow false 3 Number Title Song type false 0 0 Qt::UpArrow Qt::Vertical 20 40 false Remove Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ntscRadioButton palRadioButton customTickFreqRadioButton customTickFreqSpinBox mixerGroupBox mixerTypeComboBox customMixerSetPushButton songTreeWidget upToolButton downToolButton removePushButton sngTitleLineEdit sngTypeComboBox insertPushButton updateButton buttonBox accepted() ModulePropertiesDialog accept() 248 254 157 274 buttonBox rejected() ModulePropertiesDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/order_list_editor/000077500000000000000000000000001401124043500232415ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/order_list_editor/order_list_editor.cpp000066400000000000000000000207371401124043500274720ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "order_list_editor.hpp" #include "ui_order_list_editor.h" OrderListEditor::OrderListEditor(QWidget *parent) : QFrame(parent), ui(new Ui::OrderListEditor), freezed_(false), songLoaded_(false), hScrollCellMove_(true) { ui->setupUi(this); installEventFilter(this); ui->panel->installEventFilter(this); ui->verticalScrollBar->installEventFilter(this); QObject::connect(ui->panel, &OrderListPanel::hScrollBarChangeRequested, ui->horizontalScrollBar, &QScrollBar::setValue); QObject::connect(ui->panel, &OrderListPanel::vScrollBarChangeRequested, this, [&](int num, int max) { if (ui->verticalScrollBar->maximum() < num) { ui->verticalScrollBar->setMaximum(max); ui->verticalScrollBar->setValue(num); } else { ui->verticalScrollBar->setValue(num); ui->verticalScrollBar->setMaximum(max); } }); QObject::connect(ui->panel, &OrderListPanel::currentTrackChanged, this, [&](int idx) { emit currentTrackChanged(idx); }); QObject::connect(ui->panel, &OrderListPanel::currentOrderChanged, this, [&](int num) { emit currentOrderChanged(num); }); QObject::connect(ui->panel, &OrderListPanel::orderEdited, this, [&] { emit orderEdited(); }); QObject::connect(ui->panel, &OrderListPanel::selected, this, [&](bool isSelected) { emit selected(isSelected); }); auto focusSlot = [&] { ui->panel->setFocus(); }; QObject::connect(ui->horizontalScrollBar, &QScrollBar::valueChanged, ui->panel, &OrderListPanel::onHScrollBarChanged); QObject::connect(ui->horizontalScrollBar, &QScrollBar::sliderPressed, this, focusSlot); QObject::connect(ui->verticalScrollBar, &QScrollBar::valueChanged, ui->panel, &OrderListPanel::onVScrollBarChanged); QObject::connect(ui->verticalScrollBar, &QScrollBar::sliderPressed, this, focusSlot); } OrderListEditor::~OrderListEditor() { delete ui; } void OrderListEditor::setCore(std::shared_ptr core) { bt_ = core; ui->panel->setCore(core); } void OrderListEditor::setCommandStack(std::weak_ptr stack) { ui->panel->setCommandStack(stack); } void OrderListEditor::setConfiguration(std::shared_ptr config) { ui->panel->setConfiguration(config); } void OrderListEditor::setColorPallete(std::shared_ptr palette) { ui->panel->setColorPallete(palette); } void OrderListEditor::addActionToPanel(QAction* action) { ui->panel->addAction(action); } void OrderListEditor::changeEditable() { ui->panel->changeEditable(); } void OrderListEditor::updatePositionByOrderUpdate(bool isFirstUpdate) { ui->panel->updatePositionByOrderUpdate(isFirstUpdate); } void OrderListEditor::updatePositionByPositionJump(bool trackChanged) { ui->panel->updatePositionByOrderUpdate(false, true, trackChanged); } void OrderListEditor::copySelectedCells() { ui->panel->copySelectedCells(); } void OrderListEditor::deleteOrder() { ui->panel->deleteOrder(); } void OrderListEditor::insertOrderBelow() { ui->panel->insertOrderBelow(); } void OrderListEditor::freeze() { setUpdatesEnabled(false); freezed_ = true; ui->panel->waitPaintFinish(); } void OrderListEditor::unfreeze() { freezed_ = false; setUpdatesEnabled(true); } QString OrderListEditor::getHeaderFont() const { return ui->panel->getHeaderFont(); } int OrderListEditor::getHeaderFontSize() const { return ui->panel->getHeaderFontSize(); } QString OrderListEditor::getRowsFont() const { return ui->panel->getRowsFont(); } int OrderListEditor::getRowsFontSize() const { return ui->panel->getRowsFontSize(); } void OrderListEditor::setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize) { ui->panel->setFonts(headerFont, headerSize, rowsFont, rowsSize); } void OrderListEditor::setHorizontalScrollMode(bool cellBased, bool refresh) { hScrollCellMove_ = cellBased; if (refresh) updateHorizontalSliderMaximum(); } void OrderListEditor::setVisibleTracks(std::vector tracks) { ui->horizontalScrollBar->setMaximum(20); // Dummy ui->panel->setVisibleTracks(tracks); updateMaximumWidth(); updateHorizontalSliderMaximum(); } bool OrderListEditor::eventFilter(QObject* watched, QEvent* event) { Q_UNUSED(watched) if (freezed_) return true; // Ignore every events if (watched == this) { if (event->type() == QEvent::FocusIn) { ui->panel->setFocus(); } return false; } if (watched == ui->panel) { switch (event->type()) { case QEvent::FocusIn: ui->panel->redrawByFocusChanged(); emit focusIn(); return false; case QEvent::FocusOut: ui->panel->redrawByFocusChanged(); emit focusOut(); return false; case QEvent::HoverEnter: case QEvent::HoverLeave: ui->panel->redrawByHoverChanged(); return false; default: return false; } } if (watched == ui->verticalScrollBar) { switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::DragMove: case QEvent::Wheel: return (bt_->isPlaySong() && bt_->isFollowPlay()); default: return false; } } return false; } void OrderListEditor::resizeEvent(QResizeEvent* event) { Q_UNUSED(event) // For view-based scroll updateHorizontalSliderMaximum(); } /********** Slots **********/ void OrderListEditor::onPatternEditorCurrentTrackChanged(int idx) { ui->panel->onPatternEditorCurrentTrackChanged(idx); } void OrderListEditor::onPatternEditorCurrentOrderChanged(int num, int max) { ui->panel->onPatternEditorCurrentOrderChanged(num); ui->verticalScrollBar->setMaximum(max); ui->verticalScrollBar->setValue(num); } void OrderListEditor::onSongLoaded() { ui->horizontalScrollBar->setValue(0); ui->panel->onSongLoaded(); updateMaximumWidth(); int song = bt_->getCurrentSongNumber(); songLoaded_ = true; updateHorizontalSliderMaximum(); ui->verticalScrollBar->setValue(0); // Left here to set appropriate order size before initialization of order position ui->verticalScrollBar->setMaximum(static_cast(bt_->getOrderSize(song)) - 1); } void OrderListEditor::onShortcutUpdated() { ui->panel->onShortcutUpdated(); } void OrderListEditor::onPastePressed() { ui->panel->onPastePressed(); } void OrderListEditor::onSelectPressed(int type) { ui->panel->onSelectPressed(type); } void OrderListEditor::onDuplicatePressed() { ui->panel->onDuplicatePressed(); } void OrderListEditor::onMoveOrderPressed(bool isUp) { ui->panel->onMoveOrderPressed(isUp); } void OrderListEditor::onClonePatternsPressed() { ui->panel->onClonePatternsPressed(); } void OrderListEditor::onCloneOrderPressed() { ui->panel->onCloneOrderPressed(); } void OrderListEditor::onFollowModeChanged() { ui->panel->onFollowModeChanged(); } void OrderListEditor::onStoppedPlaySong() { ui->panel->onStoppedPlaySong(); } void OrderListEditor::onGoOrderRequested(bool toNext) { ui->panel->onGoOrderRequested(toNext); } void OrderListEditor::onOrderDataGlobalChanged() { ui->panel->redrawByPatternChanged(); // Redraw only text } void OrderListEditor::updateHorizontalSliderMaximum() { if (!bt_ || !songLoaded_) return; int max = hScrollCellMove_ ? ui->panel->getFullColumnSize() : ui->panel->getScrollableCountByTrack(); ui->horizontalScrollBar->setMaximum(max); } void OrderListEditor::updateMaximumWidth() { int w; if (ui->horizontalScrollBar->sizeHint().width() < ui->panel->maximumWidth()) { w = ui->panel->maximumWidth(); } else { w = ui->horizontalScrollBar->sizeHint().width(); ui->panel->setMaximumWidth(w); } setMaximumWidth(w + ui->verticalScrollBar->width() + 2); } BambooTracker-0.4.6/BambooTracker/gui/order_list_editor/order_list_editor.hpp000066400000000000000000000066531401124043500275000ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ORDER_LIST_EDITOR_HPP #define ORDER_LIST_EDITOR_HPP #include #include #include #include #include "bamboo_tracker.hpp" #include "configuration.hpp" #include "gui/color_palette.hpp" namespace Ui { class OrderListEditor; } class OrderListEditor : public QFrame { Q_OBJECT public: explicit OrderListEditor(QWidget *parent = nullptr); ~OrderListEditor() override; void setCore(std::shared_ptr core); void setCommandStack(std::weak_ptr stack); void setConfiguration(std::shared_ptr config); void setColorPallete(std::shared_ptr palette); void addActionToPanel(QAction* action); void changeEditable(); void updatePositionByOrderUpdate(bool isFirstUpdate); void updatePositionByPositionJump(bool trackChanged = false); void copySelectedCells(); void deleteOrder(); void insertOrderBelow(); void freeze(); void unfreeze(); QString getHeaderFont() const; int getHeaderFontSize() const; QString getRowsFont() const; int getRowsFontSize() const; void setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize); void setHorizontalScrollMode(bool cellBased, bool refresh = true); void setVisibleTracks(std::vector tracks); signals: void currentTrackChanged(int idx); void currentOrderChanged(int num); void orderEdited(); void focusIn(); void focusOut(); void selected(bool isSelected); public slots: void onPatternEditorCurrentTrackChanged(int idx); void onPatternEditorCurrentOrderChanged(int num, int max); void onSongLoaded(); void onShortcutUpdated(); void onPastePressed(); /// 0: None /// 1: All /// 2: Row /// 3: Column /// 4: Pattern /// 5: Order void onSelectPressed(int type); void onDuplicatePressed(); void onMoveOrderPressed(bool isUp); void onClonePatternsPressed(); void onCloneOrderPressed(); void onFollowModeChanged(); void onStoppedPlaySong(); void onGoOrderRequested(bool toNext); void onOrderDataGlobalChanged(); protected: bool eventFilter(QObject* watched, QEvent* event) override; void resizeEvent(QResizeEvent* event) override; private: Ui::OrderListEditor *ui; std::shared_ptr bt_; bool freezed_; bool songLoaded_; bool hScrollCellMove_; void updateHorizontalSliderMaximum(); void updateMaximumWidth(); }; #endif // ORDER_LIST_EDITOR_HPP BambooTracker-0.4.6/BambooTracker/gui/order_list_editor/order_list_editor.ui000066400000000000000000000050271401124043500273200ustar00rootroot00000000000000 OrderListEditor 0 0 400 300 0 0 Frame QFrame::Panel QFrame::Sunken 1 QLayout::SetMinimumSize 0 0 0 0 0 Qt::Horizontal 0 0 17 0 17 16777215 Qt::Vertical OrderListPanel QWidget
gui/order_list_editor/order_list_panel.hpp
1
BambooTracker-0.4.6/BambooTracker/gui/order_list_editor/order_list_panel.cpp000066400000000000000000001365641401124043500273110ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "order_list_panel.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "playback.hpp" #include "track.hpp" #include "bamboo_tracker_defs.hpp" #include "gui/event_guard.hpp" #include "gui/command/order/order_commands_qt.hpp" #include "gui/gui_utils.hpp" #include "utils.hpp" OrderListPanel::OrderListPanel(QWidget *parent) : QWidget(parent), config_(std::make_shared()), // Dummy rowFontWidth_(0), rowFontHeight_(0), rowFontAscent_(0), rowFontLeading_(0), headerFontAscent_(0), widthSpace_(0), rowNumWidthCnt_(0), rowNumWidth_(0), rowNumBase_(0), trackWidth_(0), columnsWidthFromLeftToEnd_(0), headerHeight_(0), curRowBaselineY_(0), curRowY_(0), visTracks_(1), // Dummy leftTrackVisIdx_(0), curSongNum_(0), curPos_{ 0, 0 }, hovPos_{ -1, -1 }, mousePressPos_{ -1, -1 }, mouseReleasePos_{ -1, -1 }, selLeftAbovePos_{ -1, -1 }, selRightBelowPos_{ -1, -1 }, shiftPressedPos_{ -1, -1 }, isIgnoreToSlider_(false), isIgnoreToPattern_(false), entryCnt_(0), selectAllState_(-1), viewedRowCnt_(1), viewedRowsHeight_(0), viewedRowOffset_(0), viewedCenterY_(0), viewedCenterBaseY_(0), backChanged_(false), textChanged_(false), headerChanged_(false), followModeChanged_(false), hasFocussedBefore_(false), orderDownCount_(0), repaintable_(true), repaintingCnt_(0), playingRow_(-1), insSc1_(Qt::Key_Insert, this, nullptr, nullptr, Qt::WidgetShortcut), insSc2_(Qt::ALT + Qt::Key_B, this, nullptr, nullptr, Qt::WidgetShortcut), menuSc_(Qt::Key_Menu, this, nullptr, nullptr, Qt::WidgetShortcut) { setAttribute(Qt::WA_Hover); setFocusPolicy(Qt::ClickFocus); setContextMenuPolicy(Qt::CustomContextMenu); // Initialize font headerFont_ = QApplication::font(); headerFont_.setPointSize(10); rowFont_ = QFont("Monospace", 10); rowFont_.setStyleHint(QFont::TypeWriter); rowFont_.setStyleStrategy(QFont::ForceIntegerMetrics); updateSizes(); // Track visibility songStyle_.type = SongType::Standard; // Dummy songStyle_.trackAttribs.push_back({ 0, SoundSource::FM, 0 }); // Dummy std::iota(visTracks_.begin(), visTracks_.end(), 0); // Shortcuts QObject::connect(&insSc1_, &QShortcut::activated, this, &OrderListPanel::insertOrderBelow); QObject::connect(&insSc2_, &QShortcut::activated, this, &OrderListPanel::insertOrderBelow); QObject::connect(&menuSc_, &QShortcut::activated, this, [&] { showContextMenu( curPos_, QPoint(calculateColumnsWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx), curRowY_ - 8)); }); onShortcutUpdated(); } void OrderListPanel::setCore(std::shared_ptr core) { bt_ = core; } void OrderListPanel::setCommandStack(std::weak_ptr stack) { comStack_ = stack; } void OrderListPanel::setConfiguration(std::shared_ptr config) { config_ = config; } void OrderListPanel::setColorPallete(std::shared_ptr palette) { palette_ = palette; } void OrderListPanel::resetEntryCount() { entryCnt_ = 0; } void OrderListPanel::waitPaintFinish() { while (true) { if (repaintingCnt_.load()) std::this_thread::sleep_for(std::chrono::milliseconds(10)); else { curPos_.row = 0; // Init return; } } } QString OrderListPanel::getHeaderFont() const { return QFontInfo(headerFont_).family(); } int OrderListPanel::getHeaderFontSize() const { return QFontInfo(headerFont_).pointSize(); } QString OrderListPanel::getRowsFont() const { return QFontInfo(rowFont_).family(); } int OrderListPanel::getRowsFontSize() const { return QFontInfo(rowFont_).pointSize(); } void OrderListPanel::setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize) { headerFont_ = QFont(headerFont, headerSize); rowFont_ = QFont(rowsFont, rowsSize); updateSizes(); updateTracksWidthFromLeftToEnd(); setMaximumWidth(calculateColumnsWidthWithRowNum( 0, static_cast(visTracks_.size()) - 1)); redrawAll(); } void OrderListPanel::setVisibleTracks(std::vector tracks) { visTracks_ = tracks; int max = static_cast(tracks.size()); bool cond = (max <= curPos_.trackVisIdx); if (cond) curPos_.trackVisIdx = max; leftTrackVisIdx_ = std::min(leftTrackVisIdx_, curPos_.trackVisIdx); updateTracksWidthFromLeftToEnd(); setMaximumWidth(calculateColumnsWidthWithRowNum( 0, static_cast(visTracks_.size()) - 1)); initDisplay(); // Current track in core is changed in the pattern editor if (cond) { emit hScrollBarChangeRequested(config_->getMoveCursorByHorizontalScroll() ? curPos_.trackVisIdx : leftTrackVisIdx_); } redrawAll(); } void OrderListPanel::updateSizes() { QFontMetrics metrics(rowFont_); #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) rowFontWidth_ = metrics.horizontalAdvance('0'); #else rowFontWidth_ = metrics.width('0'); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) rowFontAscent_ = metrics.capHeight(); #else rowFontAscent_ = metrics.boundingRect('X').height(); #endif rowFontLeading_ = metrics.ascent() - rowFontAscent_ + metrics.descent() / 2; rowFontHeight_ = rowFontAscent_ + rowFontLeading_; hdFontMets_ = std::make_unique(headerFont_); headerHeight_ = hdFontMets_->height() + 5; headerFontAscent_ = hdFontMets_->ascent() + 2; /* Width & height */ widthSpace_ = rowFontWidth_ / 4; trackWidth_ = rowFontWidth_ * 3 + widthSpace_ * 2; if (config_->getShowRowNumberInHex()) { rowNumWidthCnt_ = 2; rowNumBase_ = 16; } else { rowNumWidthCnt_ = 3; rowNumBase_ = 10; } rowNumWidth_ = rowFontWidth_ * rowNumWidthCnt_ + widthSpace_; initDisplay(); } void OrderListPanel::initDisplay() { int width = geometry().width(); // Recalculate pixmap sizes viewedRegionHeight_ = std::max((geometry().height() - headerHeight_), rowFontHeight_); int cnt = viewedRegionHeight_ / rowFontHeight_; viewedRowCnt_ = (cnt % 2) ? (cnt + 2) : (cnt + 1); viewedRowsHeight_ = viewedRowCnt_ * rowFontHeight_; viewedRowOffset_ = (viewedRowsHeight_ - viewedRegionHeight_) >> 1; viewedCenterY_ = (viewedRowsHeight_ - rowFontHeight_) >> 1; viewedCenterBaseY_ = viewedCenterY_ + rowFontAscent_ + (rowFontLeading_ >> 1); completePixmap_ = QPixmap(geometry().size()); backPixmap_ = QPixmap(width, viewedRowsHeight_); textPixmap_ = QPixmap(width, viewedRowsHeight_); headerPixmap_ = QPixmap(width, headerHeight_); } void OrderListPanel::drawList(const QRect &rect) { if (repaintable_.load()) { repaintable_.store(false); ++repaintingCnt_; // Use module data after this line if (backChanged_ || textChanged_ || headerChanged_ || orderDownCount_ || followModeChanged_) { int maxWidth = std::min(geometry().width(), columnsWidthFromLeftToEnd_); completePixmap_.fill(palette_->odrBackColor); if (orderDownCount_ && !followModeChanged_) { quickDrawRows(maxWidth); } else { backPixmap_.fill(Qt::transparent); if (textChanged_) textPixmap_.fill(Qt::transparent); drawRows(maxWidth); } drawBorders(maxWidth); if (headerChanged_) { // headerPixmap_->fill(Qt::transparent); drawHeaders(maxWidth); } { QPainter mergePainter(&completePixmap_); QRect rowsRect(0, viewedRowOffset_, maxWidth, viewedRegionHeight_); QRect inViewRect(0, headerHeight_, maxWidth, viewedRegionHeight_); mergePainter.drawPixmap(inViewRect, backPixmap_, rowsRect); mergePainter.drawPixmap(inViewRect, textPixmap_, rowsRect); mergePainter.drawPixmap(headerPixmap_.rect(), headerPixmap_); } if (!hasFocus()) drawShadow(); backChanged_ = false; textChanged_ = false; headerChanged_ = false; followModeChanged_ = false; orderDownCount_ = 0; } --repaintingCnt_; // Used module data until this line repaintable_.store(true); } QPainter completePainter(this); completePainter.drawPixmap(rect, completePixmap_); } void OrderListPanel::drawRows(int maxWidth) { QPainter textPainter(&textPixmap_); QPainter backPainter(&backPixmap_); textPainter.setFont(rowFont_); std::vector orderRowData_; int textOffset = trackWidth_ / 2 - rowFontWidth_; /* Current row */ // Fill row backPainter.fillRect(0, viewedCenterY_, maxWidth, rowFontHeight_, hasFocus() ? palette_->odrCurEditRowColor : palette_->odrCurRowColor); if (textChanged_) { // Row number textPainter.setPen(palette_->odrRowNumColor); textPainter.drawText(1, viewedCenterBaseY_, QString("%1").arg( curPos_.row, rowNumWidthCnt_, rowNumBase_, QChar('0') ).toUpper()); } // Order data orderRowData_ = bt_->getOrderData(curSongNum_, curPos_.row); textPainter.setPen(palette_->odrCurTextColor); for (int x = rowNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { if (trackVisIdx == curPos_.trackVisIdx) // Paint current cell backPainter.fillRect(x, viewedCenterY_, trackWidth_, rowFontHeight_, palette_->odrCurCellColor); if (((hovPos_.row == curPos_.row || hovPos_.row == -2) && hovPos_.trackVisIdx == trackVisIdx) || (hovPos_.trackVisIdx == -2 && hovPos_.row == curPos_.row)) // Paint hover backPainter.fillRect(x, viewedCenterY_, trackWidth_, rowFontHeight_, palette_->odrHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.row >= 0) && isSelectedCell(trackVisIdx, curPos_.row)) // Paint selected backPainter.fillRect(x, viewedCenterY_, trackWidth_, rowFontHeight_, palette_->odrSelCellColor); if (textChanged_) { textPainter.drawText( x + textOffset, viewedCenterBaseY_, QString("%1") .arg(orderRowData_.at(static_cast(visTracks_.at(trackVisIdx))).patten, 2, 16, QChar('0')).toUpper() ); } x += trackWidth_; } viewedCenterPos_.row = curPos_.row; int rowNum; int rowY, baseY, endY; int playOdrNum = bt_->getPlayingOrderNumber(); /* Previous rows */ viewedFirstPos_.row = curPos_.row; endY = std::max(0, viewedCenterY_ - rowFontHeight_ * curPos_.row); for (rowY = viewedCenterY_ - rowFontHeight_, baseY = viewedCenterBaseY_ - rowFontHeight_, rowNum = curPos_.row - 1; rowY >= endY; rowY -= rowFontHeight_, baseY -= rowFontHeight_, --rowNum) { QColor rowColor; if (!config_->getFollowMode() && rowNum == playOdrNum) { rowColor = palette_->odrPlayRowColor; } else { rowColor = palette_->odrDefRowColor; } // Fill row backPainter.fillRect(0, rowY, maxWidth, rowFontHeight_, rowColor); if (textChanged_) { // Row number textPainter.setPen(palette_->odrRowNumColor); textPainter.drawText(1, baseY, QString("%1").arg( rowNum, rowNumWidthCnt_, rowNumBase_, QChar('0') ).toUpper()); } // Order data orderRowData_ = bt_->getOrderData(curSongNum_, rowNum); textPainter.setPen(palette_->odrDefTextColor); for (int x = rowNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { if (((hovPos_.row == rowNum || hovPos_.row == -2) && hovPos_.trackVisIdx == trackVisIdx) || (hovPos_.trackVisIdx == -2 && hovPos_.row == rowNum)) // Paint hover backPainter.fillRect(x, rowY, trackWidth_, rowFontHeight_, palette_->odrHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.row >= 0) && isSelectedCell(trackVisIdx, rowNum)) // Paint selected backPainter.fillRect(x, rowY, trackWidth_, rowFontHeight_, palette_->odrSelCellColor); if (textChanged_) { textPainter.drawText( x + textOffset, baseY, QString("%1") .arg(orderRowData_.at(static_cast(visTracks_.at(trackVisIdx))).patten, 2, 16, QChar('0')).toUpper() ); } x += trackWidth_; } viewedFirstPos_.row = rowNum; } /* Next rows */ viewedLastPos_.row = curPos_.row; endY = std::min(viewedRowsHeight_ - viewedRowOffset_, viewedCenterY_ + rowFontHeight_ * (static_cast(bt_->getOrderSize(curSongNum_)) - curPos_.row - 1)); for (rowY = viewedCenterY_ + rowFontHeight_, baseY = viewedCenterBaseY_ + rowFontHeight_, rowNum = curPos_.row + 1; rowY <= endY; rowY += rowFontHeight_, baseY += rowFontHeight_, ++rowNum) { QColor rowColor; if (!config_->getFollowMode() && rowNum == playOdrNum) rowColor = palette_->odrPlayRowColor; else rowColor = palette_->odrDefRowColor; // Fill row backPainter.fillRect(0, rowY, maxWidth, rowFontHeight_, rowColor); if (textChanged_) { // Row number textPainter.setPen(palette_->odrRowNumColor); textPainter.drawText(1, baseY, QString("%1").arg( rowNum, rowNumWidthCnt_, rowNumBase_, QChar('0') ).toUpper()); } // Order data orderRowData_ = bt_->getOrderData(curSongNum_, rowNum); textPainter.setPen(palette_->odrDefTextColor); for (int x = rowNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { if (((hovPos_.row == rowNum || hovPos_.row == -2) && hovPos_.trackVisIdx == trackVisIdx) || (hovPos_.trackVisIdx == -2 && hovPos_.row == rowNum)) // Paint hover backPainter.fillRect(x, rowY, trackWidth_, rowFontHeight_, palette_->odrHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.row >= 0) && isSelectedCell(trackVisIdx, rowNum)) // Paint selected backPainter.fillRect(x, rowY, trackWidth_, rowFontHeight_, palette_->odrSelCellColor); if (textChanged_) { textPainter.drawText( x + textOffset, baseY, QString("%1") .arg(orderRowData_.at(static_cast(visTracks_.at(trackVisIdx))).patten, 2, 16, QChar('0')).toUpper() ); } x += trackWidth_; } viewedLastPos_.row = rowNum; } } void OrderListPanel::quickDrawRows(int maxWidth) { int halfRowsCnt = viewedRowCnt_ >> 1; int shift = rowFontHeight_ * orderDownCount_; /* Move up by */ QRect srcRect(0, 0, maxWidth, viewedRowsHeight_); textPixmap_.scroll(0, -shift, srcRect); backPixmap_.scroll(0, -shift, srcRect); { int fpos = viewedCenterPos_.row + orderDownCount_ - halfRowsCnt; if (fpos >= 0) viewedFirstPos_.row = fpos; } QPainter textPainter(&textPixmap_); QPainter backPainter(&backPixmap_); textPainter.setFont(rowFont_); std::vector orderRowData_; int textOffset = trackWidth_ / 2 - rowFontWidth_; /* Clear previous cursor row, current cursor row and last rows text */ int prevY = viewedCenterY_ - shift; int lastY = viewedRowsHeight_ - shift; textPainter.setCompositionMode(QPainter::CompositionMode_Source); textPainter.fillRect(0, prevY, maxWidth, rowFontHeight_, Qt::transparent); textPainter.fillRect(0, viewedCenterY_, maxWidth, rowFontHeight_, Qt::transparent); textPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent); textPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); /* Redraw previous cursor row */ { int baseY = viewedCenterBaseY_ - shift; // Fill row backPainter.fillRect(0, prevY, maxWidth, rowFontHeight_, palette_->odrDefRowColor); // Row number textPainter.setPen(palette_->odrRowNumColor); textPainter.drawText(1, baseY, QString("%1").arg( viewedCenterPos_.row, rowNumWidthCnt_, rowNumBase_, QChar('0') ).toUpper()); // Order data orderRowData_ = bt_->getOrderData(curSongNum_, viewedCenterPos_.row); textPainter.setPen(palette_->odrDefTextColor); for (int x = rowNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { if (((hovPos_.row == viewedCenterPos_.row || hovPos_.row == -2) && hovPos_.trackVisIdx == trackVisIdx) || (hovPos_.trackVisIdx == -2 && hovPos_.row == viewedCenterPos_.row)) // Paint hover backPainter.fillRect(x, prevY, trackWidth_, rowFontHeight_, palette_->odrHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.row >= 0) && isSelectedCell(trackVisIdx, viewedCenterPos_.row)) // Paint selected backPainter.fillRect(x, prevY, trackWidth_, rowFontHeight_, palette_->odrSelCellColor); textPainter.drawText( x + textOffset, baseY, QString("%1") .arg(orderRowData_.at(static_cast(visTracks_.at(trackVisIdx))).patten, 2, 16, QChar('0')).toUpper() ); x += trackWidth_; } } /* Redraw current cursor row */ // Fill row backPainter.fillRect(0, viewedCenterY_, maxWidth, rowFontHeight_, hasFocus() ? palette_->odrCurEditRowColor : palette_->odrCurRowColor); // Row number textPainter.setPen(palette_->odrRowNumColor); textPainter.drawText(1, viewedCenterBaseY_, QString("%1").arg( curPos_.row, rowNumWidthCnt_, rowNumBase_, QChar('0') ).toUpper()); // Order data orderRowData_ = bt_->getOrderData(curSongNum_, curPos_.row); textPainter.setPen(palette_->odrCurTextColor); for (int x = rowNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { if (trackVisIdx == curPos_.trackVisIdx) // Paint current cell backPainter.fillRect(x, viewedCenterY_, trackWidth_, rowFontHeight_, palette_->odrCurCellColor); if (((hovPos_.row == curPos_.row || hovPos_.row == -2) && hovPos_.trackVisIdx == trackVisIdx) || (hovPos_.trackVisIdx == -2 && hovPos_.row == curPos_.row)) // Paint hover backPainter.fillRect(x, viewedCenterY_, trackWidth_, rowFontHeight_, palette_->odrHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.row >= 0) && isSelectedCell(trackVisIdx, curPos_.row)) // Paint selected backPainter.fillRect(x, viewedCenterY_, trackWidth_, rowFontHeight_, palette_->odrSelCellColor); textPainter.drawText( x + textOffset, viewedCenterBaseY_, QString("%1") .arg(orderRowData_.at(static_cast(visTracks_.at(trackVisIdx))).patten, 2, 16, QChar('0')).toUpper() ); x += trackWidth_; } viewedCenterPos_ = curPos_; /* Draw new rows at last if necessary */ { int bpos = viewedCenterPos_.row + halfRowsCnt; int last = static_cast(bt_->getOrderSize(curSongNum_)) - 1; bool needClear; if (bpos < last) { needClear = false; bpos = std::exchange(viewedLastPos_.row, bpos); } else { needClear = true; bpos = std::exchange(viewedLastPos_.row, last); } int baseY = lastY + (viewedCenterBaseY_ - viewedCenterY_); while (true) { if (bpos == viewedLastPos_.row) { if (needClear) { // Clear row backPainter.setCompositionMode(QPainter::CompositionMode_Source); backPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent); } break; } ++bpos; // Fill row backPainter.fillRect(0, lastY, maxWidth, rowFontHeight_, palette_->odrDefRowColor); // Row number textPainter.setPen(palette_->odrRowNumColor); textPainter.drawText(1, baseY, QString("%1").arg( viewedLastPos_.row, rowNumWidthCnt_, rowNumBase_, QChar('0') ).toUpper()); // Order data orderRowData_ = bt_->getOrderData(curSongNum_, viewedLastPos_.row); textPainter.setPen(palette_->odrDefTextColor); for (int x = rowNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { if (((hovPos_.row == viewedLastPos_.row || hovPos_.row == -2) && hovPos_.trackVisIdx == trackVisIdx) || (hovPos_.trackVisIdx == -2 && hovPos_.row == viewedLastPos_.row)) // Paint hover backPainter.fillRect(x, lastY, trackWidth_, rowFontHeight_, palette_->odrHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.row >= 0) && isSelectedCell(trackVisIdx, viewedLastPos_.row)) // Paint selected backPainter.fillRect(x, lastY, trackWidth_, rowFontHeight_, palette_->odrSelCellColor); textPainter.drawText( x + textOffset, baseY, QString("%1") .arg(orderRowData_.at(static_cast(visTracks_.at(trackVisIdx))).patten, 2, 16, QChar('0')).toUpper() ); x += trackWidth_; } baseY += rowFontHeight_; lastY += rowFontHeight_; } } } void OrderListPanel::drawHeaders(int maxWidth) { QPainter painter(&headerPixmap_); painter.setFont(headerFont_); painter.fillRect(0, 0, geometry().width(), headerHeight_, palette_->odrHeaderRowColor); painter.setPen(palette_->odrHeaderBorderColor); int bottomLineY = headerHeight_ - 1; painter.drawLine(0, bottomLineY, geometry().width(), bottomLineY); for (int x = rowNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { painter.setPen(palette_->odrHeaderBorderColor); painter.drawLine(x, 0, x, headerHeight_); QString str; auto& attrib = songStyle_.trackAttribs[static_cast(visTracks_.at(trackVisIdx))]; switch (attrib.source) { case SoundSource::FM: switch (songStyle_.type) { case SongType::Standard: str = "FM" + QString::number(attrib.channelInSource + 1); break; case SongType::FM3chExpanded: switch (attrib.channelInSource) { case 2: str = "OP1"; break; case 6: str = "OP2"; break; case 7: str = "OP3"; break; case 8: str = "OP4"; break; default: str = "FM" + QString::number(attrib.channelInSource + 1); break; } break; } break; case SoundSource::SSG: str = "SG" + QString::number(attrib.channelInSource + 1); break; case SoundSource::RHYTHM: static const QString RHYTM_NAMES[6] = { "BD", "SD", "TOP", "HH", "TOM", "RIM" }; str = RHYTM_NAMES[attrib.channelInSource]; break; case SoundSource::ADPCM: str = "AP"; break; } painter.setPen(palette_->odrHeaderTextColor); painter.drawText(QRectF(x, 0, trackWidth_, headerFontAscent_), Qt::AlignCenter, str); x += trackWidth_; } } void OrderListPanel::drawBorders(int maxWidth) { QPainter painter(&backPixmap_); painter.setPen(palette_->odrBorderColor); painter.drawLine(rowNumWidth_, 0, rowNumWidth_, backPixmap_.height()); for (int x = rowNumWidth_ + trackWidth_, trackVisIdx = leftTrackVisIdx_; x <= maxWidth; ++trackVisIdx) { painter.drawLine(x, 0, x, backPixmap_.height()); x += trackWidth_; } } void OrderListPanel::drawShadow() { QPainter painter(&completePixmap_); painter.fillRect(0, 0, geometry().width(), geometry().height(), palette_->odrUnfocusedShadowColor); } void OrderListPanel::moveCursorToRight(int n) { int oldLeftTrackIdx = leftTrackVisIdx_; int prevTrackIdx = curPos_.trackVisIdx; int tmp = curPos_.trackVisIdx + n; if (n > 0) { while (true) { int sub = tmp - static_cast(visTracks_.size()); if (sub < 0) { curPos_.trackVisIdx = tmp; break; } else { if (config_->getWarpCursor()) { tmp = sub; } else { curPos_.trackVisIdx = static_cast(visTracks_.size()) - 1; break; } } } } else { while (true) { int add = tmp + static_cast(visTracks_.size()); if (tmp < 0) { if (config_->getWarpCursor()) { tmp = add; } else { curPos_.trackVisIdx = 0; break; } } else { curPos_.trackVisIdx = tmp; break; } } } if (prevTrackIdx < curPos_.trackVisIdx) { while (calculateColumnsWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx) > geometry().width()) ++leftTrackVisIdx_; } else { if (curPos_.trackVisIdx < leftTrackVisIdx_) leftTrackVisIdx_ = curPos_.trackVisIdx; } updateTracksWidthFromLeftToEnd(); entryCnt_ = 0; if (!isIgnoreToSlider_) { // Send to slider emit hScrollBarChangeRequested(config_->getMoveCursorByHorizontalScroll() ? curPos_.trackVisIdx : leftTrackVisIdx_); } if (!isIgnoreToPattern_) emit currentTrackChanged(curPos_.trackVisIdx); // Send to pattern editor // Request fore-background repaint if leftmost track is changed else request only background repaint if (leftTrackVisIdx_ != oldLeftTrackIdx) { headerChanged_ = true; textChanged_ = true; } backChanged_ = true; repaint(); } void OrderListPanel::moveViewToRight(int n) { leftTrackVisIdx_ += n; updateTracksWidthFromLeftToEnd(); // Move cursor and repaint all headerChanged_ = true; textChanged_ = true; moveCursorToRight(n); } void OrderListPanel::moveCursorToDown(int n) { int tmp = curPos_.row + n; int endRow = static_cast(bt_->getOrderSize(curSongNum_)); if (n > 0) { while (true) { int sub = tmp - endRow; if (sub < 0) { curPos_.row = tmp; break; } else { tmp = sub; } } } else { while (true) { int add = tmp + endRow; if (tmp < 0) { tmp = add; } else { curPos_.row = tmp; break; } } } entryCnt_ = 0; if (!isIgnoreToSlider_) // Send to slider emit vScrollBarChangeRequested(curPos_.row, static_cast(bt_->getOrderSize(curSongNum_)) - 1); if (!isIgnoreToPattern_) // Send to pattern editor emit currentOrderChanged(curPos_.row); backChanged_ = true; textChanged_ = true; repaint(); } void OrderListPanel::changeEditable() { backChanged_ = true; repaint(); } int OrderListPanel::getFullColumnSize() const { return static_cast(visTracks_.size()) - 1; } void OrderListPanel::updatePositionByOrderUpdate(bool isFirstUpdate, bool forceJump, bool trackChanged) { int prev = std::exchange(playingRow_, bt_->getPlayingOrderNumber()); if (!forceJump && !config_->getFollowMode() && prev != playingRow_) { // Repaint only background backChanged_ = true; repaint(); return; } if (trackChanged) { // Update horizontal position int trackVisIdx = std::distance(visTracks_.begin(), utils::find(visTracks_, bt_->getCurrentTrackAttribute().number)); int prevTrackIdx = std::exchange(curPos_.trackVisIdx, trackVisIdx); if (prevTrackIdx < curPos_.trackVisIdx) { while (calculateColumnsWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx) > geometry().width()) { ++leftTrackVisIdx_; headerChanged_ = true; } } else { if (curPos_.trackVisIdx < leftTrackVisIdx_) { leftTrackVisIdx_ = curPos_.trackVisIdx; headerChanged_ = true; } } updateTracksWidthFromLeftToEnd(); emit hScrollBarChangeRequested(config_->getMoveCursorByHorizontalScroll() ? curPos_.trackVisIdx : leftTrackVisIdx_); } int tmp = std::exchange(curPos_.row, bt_->getCurrentOrderNumber()); int d = curPos_.row - tmp; if (d) { emit vScrollBarChangeRequested(curPos_.row, static_cast(bt_->getOrderSize(curSongNum_)) - 1); // Redraw entire area in first update and jumping order orderDownCount_ = (isFirstUpdate || d < 0 || (viewedRowCnt_ >> 1) < d) ? 0 : d; } else if (!trackChanged) return; entryCnt_ = 0; textChanged_ = true; backChanged_ = true; repaint(); } int OrderListPanel::getScrollableCountByTrack() const { int width = rowNumWidth_; size_t i = visTracks_.size(); do { --i; width += trackWidth_; if (geometry().width() < width) { return static_cast(i + 1); } } while (i); return 0; } void OrderListPanel::redrawByPatternChanged(bool ordersLengthChanged) { textChanged_ = true; // When length of orders is changed, redraw all area if (ordersLengthChanged) backChanged_ = true; repaint(); } void OrderListPanel::redrawByFocusChanged() { if (hasFocussedBefore_) { backChanged_ = true; repaint(); } else { redrawAll(); hasFocussedBefore_ = true; } } void OrderListPanel::redrawByHoverChanged() { headerChanged_ = true; backChanged_ = true; repaint(); } void OrderListPanel::redrawAll() { backChanged_ = true; textChanged_ = true; headerChanged_ = true; orderDownCount_ = 0; // Prevent quick draw repaint(); } bool OrderListPanel::enterOrder(int key) { switch (key) { case Qt::Key_0: setCellOrderNum(0x0); return true; case Qt::Key_1: setCellOrderNum(0x1); return true; case Qt::Key_2: setCellOrderNum(0x2); return true; case Qt::Key_3: setCellOrderNum(0x3); return true; case Qt::Key_4: setCellOrderNum(0x4); return true; case Qt::Key_5: setCellOrderNum(0x5); return true; case Qt::Key_6: setCellOrderNum(0x6); return true; case Qt::Key_7: setCellOrderNum(0x7); return true; case Qt::Key_8: setCellOrderNum(0x8); return true; case Qt::Key_9: setCellOrderNum(0x9); return true; case Qt::Key_A: setCellOrderNum(0xa); return true; case Qt::Key_B: setCellOrderNum(0xb); return true; case Qt::Key_C: setCellOrderNum(0xc); return true; case Qt::Key_D: setCellOrderNum(0xd); return true; case Qt::Key_E: setCellOrderNum(0xe); return true; case Qt::Key_F: setCellOrderNum(0xf); return true; default: return false; } } void OrderListPanel::setCellOrderNum(int n) { bt_->setOrderPatternDigit(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.row, n, (entryCnt_ == 1)); comStack_.lock()->push(new SetPatternToOrderQtCommand(this, curPos_, (entryCnt_ == 1))); entryCnt_ = (entryCnt_ + 1) % 2; if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !entryCnt_) moveCursorToDown(1); } void OrderListPanel::insertOrderBelow() { if (!bt_->canAddNewOrder(curSongNum_)) return; bt_->insertOrderBelow(curSongNum_, curPos_.row); comStack_.lock()->push(new InsertOrderBelowQtCommand(this)); } void OrderListPanel::deleteOrder() { if (bt_->getOrderSize(curSongNum_) > 1) { bt_->deleteOrder(curSongNum_, curPos_.row); comStack_.lock()->push(new DeleteOrderQtCommand(this)); } } void OrderListPanel::copySelectedCells() { if (selLeftAbovePos_.row == -1) return; // Real selected region width int w = visTracks_.at(selRightBelowPos_.trackVisIdx) - visTracks_.at(selLeftAbovePos_.trackVisIdx) + 1; int h = selRightBelowPos_.row - selLeftAbovePos_.row + 1; QString str = QString("ORDER_COPY:%1,%2,") .arg(QString::number(w), QString::number(h)); for (int i = 0; i < h; ++i) { std::vector odrs = bt_->getOrderData(curSongNum_, selLeftAbovePos_.row + i); for (int j = 0; j < w; ++j) { str += QString::number(odrs.at(static_cast(visTracks_.at(selLeftAbovePos_.trackVisIdx) + j)).patten); if (i < h - 1 || j < w - 1) str += ","; } } QApplication::clipboard()->setText(str); } void OrderListPanel::pasteCopiedCells(const OrderPosition& startPos) { // Analyze text QString str = QApplication::clipboard()->text().remove("ORDER_COPY:"); QStringList data = str.split(","); if (data.size() < 2) return; size_t w = data[0].toUInt(); size_t h = data[1].toUInt(); data.erase(data.begin(), data.begin() + 2); if (static_cast(w * h) != data.size()) return; std::vector> cells(h, std::vector(w)); for (size_t i = 0; i < h; ++i) { for (size_t j = 0; j < w; ++j) { cells[i][j] = data[i * w + j].toStdString(); } } // Send cells data bt_->pasteOrderCells(curSongNum_, visTracks_.at(startPos.trackVisIdx), startPos.row, std::move(cells)); comStack_.lock()->push(new PasteCopiedDataToOrderQtCommand(this)); } void OrderListPanel::setSelectedRectangle(const OrderPosition& start, const OrderPosition& end) { if (start.trackVisIdx > end.trackVisIdx) { if (start.row > end.row) { selLeftAbovePos_ = end; selRightBelowPos_ = start; } else { selLeftAbovePos_ = { end.trackVisIdx, start.row }; selRightBelowPos_ = { start.trackVisIdx, end.row }; } } else { if (start.row > end.row) { selLeftAbovePos_ = { start.trackVisIdx, end.row }; selRightBelowPos_ = { end.trackVisIdx, start.row }; } else { selLeftAbovePos_ = start; selRightBelowPos_ = end; } } emit selected(true); backChanged_ = true; repaint(); } bool OrderListPanel::isSelectedCell(int trackIdx, int row) { return (selLeftAbovePos_.trackVisIdx <= trackIdx && selRightBelowPos_.trackVisIdx >= trackIdx && selLeftAbovePos_.row <= row && selRightBelowPos_.row >= row); } void OrderListPanel::showContextMenu(const OrderPosition& pos, const QPoint& point) { QMenu menu; // Leave Before Qt5.7.0 style due to windows xp QAction* insert = menu.addAction(tr("&Insert Order")); insert->setIcon(QIcon(":/icon/insert_order")); QObject::connect(insert, &QAction::triggered, this, [&]() { insertOrderBelow(); }); QAction* remove = menu.addAction(tr("&Remove Order")); remove->setIcon(QIcon(":/icon/remove_order")); QObject::connect(remove, &QAction::triggered, this, [&]() { deleteOrder(); }); QAction* duplicate = menu.addAction(tr("&Duplicate Order")); duplicate->setIcon(QIcon(":/icon/duplicate_order")); QObject::connect(duplicate, &QAction::triggered, this, &OrderListPanel::onDuplicatePressed); QAction* clonep = menu.addAction(tr("&Clone Patterns")); QAction::connect(clonep, &QAction::triggered, this, &OrderListPanel::onClonePatternsPressed); QAction* cloneo = menu.addAction(tr("Clone &Order")); QObject::connect(cloneo, &QAction::triggered, this, &OrderListPanel::onCloneOrderPressed); menu.addSeparator(); QAction* moveUp = menu.addAction(tr("Move Order &Up")); moveUp->setIcon(QIcon(":/icon/order_up")); QObject::connect(moveUp, &QAction::triggered, this, [&]() { onMoveOrderPressed(true); }); QAction* moveDown = menu.addAction(tr("Move Order Do&wn")); moveDown->setIcon(QIcon(":/icon/order_down")); QObject::connect(moveDown, &QAction::triggered, this, [&]() { onMoveOrderPressed(false); }); menu.addSeparator(); QAction* copy = menu.addAction(tr("Cop&y")); copy->setIcon(QIcon(":/icon/copy")); QObject::connect(copy, &QAction::triggered, this, &OrderListPanel::copySelectedCells); QAction* paste = menu.addAction(tr("&Paste")); paste->setIcon(QIcon(":/icon/paste")); QObject::connect(paste, &QAction::triggered, this, [&]() { pasteCopiedCells(pos); }); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) duplicate->setShortcutVisibleInContextMenu(true); clonep->setShortcutVisibleInContextMenu(true); copy->setShortcutVisibleInContextMenu(true); paste->setShortcutVisibleInContextMenu(true); #endif auto shortcuts = config_->getShortcuts(); duplicate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DuplicateOrder))); clonep->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ClonePatterns))); cloneo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CloneOrder))); copy->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); paste->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V)); if (pos.row < 0 || pos.trackVisIdx < 0) { remove->setEnabled(false); moveUp->setEnabled(false); moveDown->setEnabled(false); copy->setEnabled(false); paste->setEnabled(false); } if (!bt_->canAddNewOrder(curSongNum_)) { insert->setEnabled(false); duplicate->setEnabled(false); moveUp->setEnabled(false); moveDown->setEnabled(false); copy->setEnabled(false); paste->setEnabled(false); } QString clipText = QApplication::clipboard()->text(); if (!clipText.startsWith("ORDER_COPY")) { paste->setEnabled(false); } if (bt_->getOrderSize(curSongNum_) == 1) { remove->setEnabled(false); } if (selRightBelowPos_.row < 0 || !isSelectedCell(pos.trackVisIdx, pos.row)) { clonep->setEnabled(false); copy->setEnabled(false); } if (pos.row == 0) { moveUp->setEnabled(false); } if (pos.row == static_cast(bt_->getOrderSize(curSongNum_)) - 1) { moveDown->setEnabled(false); } menu.exec(mapToGlobal(point)); } /********** Slots **********/ void OrderListPanel::onHScrollBarChanged(int num) { Ui::EventGuard eg(isIgnoreToSlider_); // Skip if position has already changed in panel if (config_->getMoveCursorByHorizontalScroll()) { if (int dif = num - curPos_.trackVisIdx) moveCursorToRight(dif); } else { if (int dif = num - leftTrackVisIdx_) moveViewToRight(dif); } } void OrderListPanel::onVScrollBarChanged(int num) { Ui::EventGuard eg(isIgnoreToSlider_); // Skip if position has already changed in panel if (int dif = num - curPos_.row) moveCursorToDown(dif); } void OrderListPanel::onPatternEditorCurrentTrackChanged(int idx) { Ui::EventGuard eg(isIgnoreToPattern_); // Skip if position has already changed in panel if (int dif = idx - curPos_.trackVisIdx) moveCursorToRight(dif); } void OrderListPanel::onPatternEditorCurrentOrderChanged(int num) { Ui::EventGuard eg(isIgnoreToPattern_); // Skip if position has already changed in panel if (int dif = num - curPos_.row) moveCursorToDown(dif); } void OrderListPanel::onOrderEdited() { // Move cursor int s = static_cast(bt_->getOrderSize(curSongNum_)); if (s <= curPos_.row) { curPos_.row = s - 1; bt_->setCurrentOrderNumber(curPos_.row); } emit orderEdited(); } void OrderListPanel::onSongLoaded() { curSongNum_ = bt_->getCurrentSongNumber(); SongType prevType = songStyle_.type; songStyle_ = bt_->getSongStyle(curSongNum_); visTracks_ = gui_utils::adaptVisibleTrackList(visTracks_, prevType, songStyle_.type); curPos_ = { visTracks_.front(), bt_->getCurrentOrderNumber() }; // Set cursor to the most lest-placed visible track if (visTracks_.front() != bt_->getCurrentTrackAttribute().number) { bt_->setCurrentTrack(visTracks_.front()); } leftTrackVisIdx_ = 0; updateTracksWidthFromLeftToEnd(); setMaximumWidth(columnsWidthFromLeftToEnd_); initDisplay(); // Call because resize event is not called during loading song hovPos_ = { -1, -1 }; mousePressPos_ = { -1, -1 }; mouseReleasePos_ = { -1, -1 }; selLeftAbovePos_ = { -1, -1 }; selRightBelowPos_ = { -1, -1 }; shiftPressedPos_ = { -1, -1 }; entryCnt_ = 0; selectAllState_ = -1; emit selected(false); redrawAll(); } void OrderListPanel::onShortcutUpdated() { } void OrderListPanel::onPastePressed() { pasteCopiedCells(curPos_); } void OrderListPanel::onSelectPressed(int type) { switch (type) { case 0: // None { selLeftAbovePos_ = { -1, -1 }; selRightBelowPos_ = { -1, -1 }; selectAllState_ = -1; emit selected(false); backChanged_ = true; repaint(); break; } case 1: // All { int max = static_cast(bt_->getOrderSize(curSongNum_)) - 1; selectAllState_ = (selectAllState_ + 1) % 2; OrderPosition start, end; if (selectAllState_) { start = { 0, 0 }; end = { static_cast(visTracks_.size() - 1), max }; } else { start = { curPos_.trackVisIdx, 0 }; end = { curPos_.trackVisIdx, max }; } setSelectedRectangle(start, end); break; } case 2: // Row { selectAllState_ = -1; OrderPosition start = { 0, curPos_.row }; OrderPosition end = { static_cast(visTracks_.size() - 1), curPos_.row }; setSelectedRectangle(start, end); break; } case 3: // Column { selectAllState_ = -1; OrderPosition start = { curPos_.trackVisIdx, 0 }; OrderPosition end = { curPos_.trackVisIdx, static_cast(bt_->getOrderSize(curSongNum_) - 1) }; setSelectedRectangle(start, end); break; } case 4: // Pattern { selectAllState_ = -1; setSelectedRectangle(curPos_, curPos_); break; } case 5: // Order { onSelectPressed(2); break; } } } void OrderListPanel::onDuplicatePressed() { bt_->duplicateOrder(curSongNum_, curPos_.row); comStack_.lock()->push(new DuplicateOrderQtCommand(this)); } void OrderListPanel::onMoveOrderPressed(bool isUp) { if ((isUp && curPos_.row == 0) || (!isUp && curPos_.row == static_cast(bt_->getOrderSize(curSongNum_)) - 1)) return; bt_->MoveOrder(curSongNum_, curPos_.row, isUp); comStack_.lock()->push(new MoveOrderQtCommand(this)); } void OrderListPanel::onClonePatternsPressed() { if (selLeftAbovePos_.row == -1) return; bt_->clonePatterns(curSongNum_, selLeftAbovePos_.row, visTracks_.at(selLeftAbovePos_.trackVisIdx), selRightBelowPos_.row, visTracks_.at(selRightBelowPos_.trackVisIdx)); comStack_.lock()->push(new ClonePatternsQtCommand(this)); } void OrderListPanel::onCloneOrderPressed() { bt_->cloneOrder(curSongNum_, curPos_.row); comStack_.lock()->push(new CloneOrderQtCommand(this)); } void OrderListPanel::onFollowModeChanged() { curPos_.row = bt_->getCurrentOrderNumber(); emit vScrollBarChangeRequested(curPos_.row, static_cast(bt_->getOrderSize(curSongNum_)) - 1); // Force redraw all area followModeChanged_ = true; textChanged_ = true; backChanged_ = true; repaint(); } void OrderListPanel::onStoppedPlaySong() { followModeChanged_ = true; textChanged_ = true; backChanged_ = true; repaint(); } void OrderListPanel::onGoOrderRequested(bool toNext) { moveCursorToDown(toNext ? 1 : -1); } /********** Events **********/ bool OrderListPanel::event(QEvent* event) { switch (event->type()) { case QEvent::KeyPress: return keyPressed(dynamic_cast(event)); case QEvent::KeyRelease: return keyReleased(dynamic_cast(event)); case QEvent::HoverMove: return mouseHoverd(dynamic_cast(event)); default: return QWidget::event(event); } } bool OrderListPanel::keyPressed(QKeyEvent* event) { /* General Keys */ switch (event->key()) { case Qt::Key_Shift: shiftPressedPos_ = curPos_; return true; case Qt::Key_Left: moveCursorToRight(-1); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; case Qt::Key_Right: moveCursorToRight(1); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; case Qt::Key_Up: if (bt_->isPlaySong() && bt_->isFollowPlay()) { return false; } else { moveCursorToDown(-1); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; } case Qt::Key_Down: if (bt_->isPlaySong() && bt_->isFollowPlay()) { return false; } else { moveCursorToDown(1); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; } case Qt::Key_Home: if (bt_->isPlaySong() && bt_->isFollowPlay()) { return false; } else { moveCursorToDown(-curPos_.row); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; } case Qt::Key_End: if (bt_->isPlaySong() && bt_->isFollowPlay()) { return false; } else { moveCursorToDown( static_cast(bt_->getOrderSize(curSongNum_)) - curPos_.row - 1); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; } case Qt::Key_PageUp: if (bt_->isPlaySong() && bt_->isFollowPlay()) { return false; } else { moveCursorToDown(-static_cast(config_->getPageJumpLength())); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; } case Qt::Key_PageDown: if (bt_->isPlaySong() && bt_->isFollowPlay()) { return false; } else { moveCursorToDown(static_cast(config_->getPageJumpLength())); if (event->modifiers().testFlag(Qt::ShiftModifier)) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); return true; } default: { auto modifiers = event->modifiers(); if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) { return enterOrder(event->key()); } return false; } } } bool OrderListPanel::keyReleased(QKeyEvent* event) { switch (event->key()) { case Qt::Key_Shift: shiftPressedPos_ = { -1, -1 }; return true; default: return false; } } void OrderListPanel::paintEvent(QPaintEvent* event) { if (bt_) { const QRect& area = event->rect(); if (area.x() == 0 && area.y() == 0) { drawList(area); } else { drawList(rect()); } } } void OrderListPanel::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); // Recalculate center row position curRowBaselineY_ = (geometry().height() + headerHeight_) / 2; curRowY_ = curRowBaselineY_ + rowFontLeading_ / 2 - rowFontAscent_; initDisplay(); redrawAll(); } void OrderListPanel::mousePressEvent(QMouseEvent* event) { Q_UNUSED(event) mousePressPos_ = hovPos_; mouseReleasePos_ = { -1, -1 }; if (event->button() == Qt::LeftButton) { selLeftAbovePos_ = { -1, -1 }; selRightBelowPos_ = { -1, -1 }; selectAllState_ = -1; emit selected(false); } } void OrderListPanel::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { if (mousePressPos_.trackVisIdx < 0 || mousePressPos_.row < 0) return; // Start point is out of range if (hovPos_.trackVisIdx >= 0) { setSelectedRectangle(mousePressPos_, hovPos_); } if (event->x() < rowNumWidth_ && leftTrackVisIdx_ > 0) { if (config_->getMoveCursorByHorizontalScroll()) moveCursorToRight(-1); else moveViewToRight(-1); } else if (event->x() > geometry().width() - rowNumWidth_ && hovPos_.trackVisIdx != -1) { if (config_->getMoveCursorByHorizontalScroll()) moveCursorToRight(1); else moveViewToRight(1); } if (event->pos().y() < headerHeight_ + rowFontHeight_) { if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(-1); } else if (event->pos().y() > geometry().height() - rowFontHeight_) { if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(1); } } } void OrderListPanel::mouseReleaseEvent(QMouseEvent* event) { mouseReleasePos_ = hovPos_; switch (event->button()) { case Qt::LeftButton: if (mousePressPos_ == mouseReleasePos_) { // Jump cell if (hovPos_.row >= 0 && hovPos_.trackVisIdx >= 0) { int horDif = hovPos_.trackVisIdx - curPos_.trackVisIdx; int verDif = hovPos_.row - curPos_.row; moveCursorToRight(horDif); if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(verDif); } else if (hovPos_.row == -2 && hovPos_.trackVisIdx >= 0) { // Header int horDif = hovPos_.trackVisIdx - curPos_.trackVisIdx; moveCursorToRight(horDif); } else if (hovPos_.trackVisIdx == -2 && hovPos_.row >= 0) { // Row number if (!bt_->isPlaySong() || !bt_->isFollowPlay()) { int verDif = hovPos_.row - curPos_.row; moveCursorToDown(verDif); } } } break; case Qt::RightButton: showContextMenu(mousePressPos_, event->pos()); break; case Qt::XButton1: if (!bt_->isPlaySong() || !bt_->isFollowPlay()) { moveCursorToDown(-1); } break; case Qt::XButton2: if (!bt_->isPlaySong() || !bt_->isFollowPlay()) { moveCursorToDown(1); } break; default: break; } mousePressPos_ = { -1, -1 }; mouseReleasePos_ = { -1, -1 }; } bool OrderListPanel::mouseHoverd(QHoverEvent *event) { QPoint pos = event->pos(); OrderPosition oldPos = hovPos_; // Detect row if (pos.y() <= headerHeight_) { hovPos_.row = -2; // Header } else { if (pos.y() < curRowY_) { int tmp = curPos_.row + (pos.y() - curRowY_) / rowFontHeight_ - 1; hovPos_.row = (tmp < 0) ? -1 : tmp; } else { hovPos_.row = curPos_.row + (pos.y() - curRowY_) / rowFontHeight_; if (hovPos_.row >= static_cast(bt_->getOrderSize(curSongNum_))) hovPos_.row = -1; } } // Detect track if (pos.x() <= rowNumWidth_) { hovPos_.trackVisIdx = -2; // Row number } else { int tmpWidth = rowNumWidth_; for (int i = leftTrackVisIdx_; ; ) { tmpWidth += trackWidth_; if (pos.x() <= tmpWidth) { hovPos_.trackVisIdx = i; break; } ++i; if (i == static_cast(visTracks_.size())) { hovPos_.trackVisIdx = -1; break; } } } if (hovPos_ != oldPos) redrawByHoverChanged(); return true; } void OrderListPanel::wheelEvent(QWheelEvent *event) { if (bt_->isPlaySong() && bt_->isFollowPlay()) return; int degree = event->angleDelta().y() / 8; moveCursorToDown(-degree / 15); } void OrderListPanel::leaveEvent(QEvent* event) { Q_UNUSED(event) // Clear mouse hover selection hovPos_ = { -1, -1 }; } BambooTracker-0.4.6/BambooTracker/gui/order_list_editor/order_list_panel.hpp000066400000000000000000000146741401124043500273130ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ORDER_LIST_PANEL_HPP #define ORDER_LIST_PANEL_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bamboo_tracker.hpp" #include "configuration.hpp" #include "gui/order_list_editor/order_position.hpp" #include "song.hpp" #include "gui/color_palette.hpp" class OrderListPanel : public QWidget { Q_OBJECT public: explicit OrderListPanel(QWidget *parent = nullptr); void setCore(std::shared_ptr core); void setCommandStack(std::weak_ptr stack); void setConfiguration(std::shared_ptr config); void setColorPallete(std::shared_ptr palette); void changeEditable(); int getFullColumnSize() const; void updatePositionByOrderUpdate(bool isFirstUpdate, bool forceJump = false, bool trackChanged = false); int getScrollableCountByTrack() const; void copySelectedCells(); void deleteOrder(); void insertOrderBelow(); void redrawByPatternChanged(bool ordersLengthChanged = false); void redrawByFocusChanged(); void redrawByHoverChanged(); void redrawAll(); void resetEntryCount(); void waitPaintFinish(); QString getHeaderFont() const; int getHeaderFontSize() const; QString getRowsFont() const; int getRowsFontSize() const; void setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize); void setVisibleTracks(std::vector tracks); public slots: void onHScrollBarChanged(int num); void onVScrollBarChanged(int num); void onPatternEditorCurrentTrackChanged(int idx); void onPatternEditorCurrentOrderChanged(int num); void onOrderEdited(); void onSongLoaded(); void onShortcutUpdated(); void onPastePressed(); /// 0: None /// 1: All /// 2: Row /// 3: Column /// 4: Pattern /// 5: Order void onSelectPressed(int type); void onDuplicatePressed(); void onMoveOrderPressed(bool isUp); void onClonePatternsPressed(); void onCloneOrderPressed(); void onFollowModeChanged(); void onStoppedPlaySong(); void onGoOrderRequested(bool toNext); signals: void hScrollBarChangeRequested(int num); void vScrollBarChangeRequested(int num, int max); void currentTrackChanged(int idx); void currentOrderChanged(int num); void orderEdited(); void selected(bool isSelected); protected: bool event(QEvent *event) override; bool keyPressed(QKeyEvent* event); bool keyReleased(QKeyEvent* event); void paintEvent(QPaintEvent* event) override; void resizeEvent(QResizeEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; bool mouseHoverd(QHoverEvent* event); void wheelEvent(QWheelEvent* event) override; void leaveEvent(QEvent* event) override; private: QPixmap completePixmap_, textPixmap_, backPixmap_, headerPixmap_; std::shared_ptr bt_; std::weak_ptr comStack_; std::shared_ptr config_; std::shared_ptr palette_; QFont rowFont_, headerFont_; std::unique_ptr hdFontMets_; int rowFontWidth_, rowFontHeight_, rowFontAscent_, rowFontLeading_; int headerFontAscent_; int widthSpace_; int rowNumWidthCnt_, rowNumWidth_, rowNumBase_; int trackWidth_; int columnsWidthFromLeftToEnd_; int headerHeight_; int curRowBaselineY_; int curRowY_; std::vector visTracks_; int leftTrackVisIdx_; SongStyle songStyle_; int curSongNum_; OrderPosition curPos_, hovPos_; OrderPosition mousePressPos_, mouseReleasePos_; OrderPosition selLeftAbovePos_, selRightBelowPos_; OrderPosition shiftPressedPos_; bool isIgnoreToSlider_, isIgnoreToPattern_; int entryCnt_; int selectAllState_; int viewedRowCnt_; int viewedRegionHeight_; int viewedRowsHeight_, viewedRowOffset_, viewedCenterY_, viewedCenterBaseY_; OrderPosition viewedFirstPos_, viewedCenterPos_, viewedLastPos_; bool backChanged_, textChanged_, headerChanged_, followModeChanged_; bool hasFocussedBefore_; int orderDownCount_; std::atomic_bool repaintable_; // Recurrensive repaint guard std::atomic_int repaintingCnt_; int playingRow_; QShortcut insSc1_, insSc2_, menuSc_; void updateSizes(); void initDisplay(); void drawList(const QRect& rect); void drawRows(int maxWidth); void quickDrawRows(int maxWidth); void drawHeaders(int maxWidth); void drawBorders(int maxWidth); void drawShadow(); // NOTE: Calculated by visible tracks inline int calculateColumnsWidthWithRowNum(int beginIdx, int endIdx) const { return rowNumWidth_ + trackWidth_ * (endIdx - beginIdx + 1); } inline void updateTracksWidthFromLeftToEnd() { columnsWidthFromLeftToEnd_ = calculateColumnsWidthWithRowNum( leftTrackVisIdx_, static_cast(visTracks_.size()) - 1); } void moveCursorToRight(int n); void moveViewToRight(int n); void moveCursorToDown(int n); bool enterOrder(int key); void setCellOrderNum(int n); void pasteCopiedCells(const OrderPosition& startPos); void setSelectedRectangle(const OrderPosition& start, const OrderPosition& end); bool isSelectedCell(int trackIdx, int row); void showContextMenu(const OrderPosition& pos, const QPoint& point); }; #endif // ORDER_LIST_PANEL_HPP BambooTracker-0.4.6/BambooTracker/gui/order_list_editor/order_position.hpp000066400000000000000000000031461401124043500270150ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef ORDER_POSITION_HPP #define ORDER_POSITION_HPP struct OrderPosition { int trackVisIdx, row; friend bool operator==(const OrderPosition& a, const OrderPosition& b); friend bool operator!=(const OrderPosition& a, const OrderPosition& b); }; inline bool operator==(const OrderPosition& a, const OrderPosition& b) { return (a.trackVisIdx == b.trackVisIdx && a.row == b.row); } inline bool operator!=(const OrderPosition& a, const OrderPosition& b) { return !(a == b); } #endif // ORDER_POSITION_HPP BambooTracker-0.4.6/BambooTracker/gui/pattern_editor/000077500000000000000000000000001401124043500225505ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor.cpp000066400000000000000000000243041401124043500263020ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pattern_editor.hpp" #include "ui_pattern_editor.h" PatternEditor::PatternEditor(QWidget *parent) : QFrame(parent), ui(new Ui::PatternEditor), freezed_(false), songLoaded_(false), hScrollCellMove_(true) { ui->setupUi(this); installEventFilter(this); ui->panel->installEventFilter(this); ui->verticalScrollBar->installEventFilter(this); ui->panel->setFocus(); QObject::connect(ui->panel, &PatternEditorPanel::hScrollBarChangeRequested, ui->horizontalScrollBar, &QScrollBar::setValue); QObject::connect(ui->panel, &PatternEditorPanel::vScrollBarChangeRequested, this, [&](int num, int max) { if (ui->verticalScrollBar->maximum() < num) { ui->verticalScrollBar->setMaximum(max); ui->verticalScrollBar->setValue(num); } else { ui->verticalScrollBar->setValue(num); ui->verticalScrollBar->setMaximum(max); } }); QObject::connect(ui->panel, &PatternEditorPanel::currentTrackChanged, this, [&](int idx) { emit currentTrackChanged(idx); }); QObject::connect(ui->panel, &PatternEditorPanel::currentOrderChanged, this, [&](int num, int max) { emit currentOrderChanged(num, max); }); QObject::connect(ui->panel, &PatternEditorPanel::effectColsCompanded, this, [&](int num, int max) { if (ui->horizontalScrollBar->maximum() < num) { ui->horizontalScrollBar->setMaximum(max); ui->horizontalScrollBar->setValue(num); } else { ui->horizontalScrollBar->setValue(num); ui->horizontalScrollBar->setMaximum(max); } }); QObject::connect(ui->panel, &PatternEditorPanel::selected, this, [&](bool isSelected) { emit selected(isSelected); }); QObject::connect(ui->panel, &PatternEditorPanel::instrumentEntered, this, [&](int num) { emit instrumentEntered(num); }); QObject::connect(ui->panel, &PatternEditorPanel::volumeEntered, this, [&](int volume) { emit volumeEntered(volume); }); QObject::connect(ui->panel, &PatternEditorPanel::effectEntered, this, [&](QString text) { emit effectEntered(text); }); auto focusSlot = [&] { ui->panel->setFocus(); }; QObject::connect(ui->horizontalScrollBar, &QScrollBar::valueChanged, ui->panel, &PatternEditorPanel::onHScrollBarChanged); QObject::connect(ui->horizontalScrollBar, &QScrollBar::sliderPressed, this, focusSlot); QObject::connect(ui->verticalScrollBar, &QScrollBar::valueChanged, ui->panel, &PatternEditorPanel::onVScrollBarChanged); QObject::connect(ui->verticalScrollBar, &QScrollBar::sliderPressed, this, focusSlot); } PatternEditor::~PatternEditor() { delete ui; } void PatternEditor::setCore(std::shared_ptr core) { bt_ = core; ui->panel->setCore(core); } void PatternEditor::setCommandStack(std::weak_ptr stack) { ui->panel->setCommandStack(stack); } void PatternEditor::setConfiguration(std::shared_ptr config) { ui->panel->setConfiguration(config); } void PatternEditor::setColorPallete(std::shared_ptr palette) { ui->panel->setColorPallete(palette); } void PatternEditor::addActionToPanel(QAction* action) { ui->panel->addAction(action); } void PatternEditor::changeEditable() { ui->panel->changeEditable(); } void PatternEditor::updatePositionByStepUpdate(bool isFirstUpdate) { ui->panel->updatePositionByStepUpdate(isFirstUpdate); } void PatternEditor::updatepositionByPositionJump(bool trackChanged) { ui->panel->updatePositionByStepUpdate(false, true, trackChanged); } void PatternEditor::changeMarker() { ui->panel->changeMarker(); } void PatternEditor::copySelectedCells() { ui->panel->copySelectedCells(); } void PatternEditor::cutSelectedCells() { ui->panel->cutSelectedCells(); } void PatternEditor::freeze() { setUpdatesEnabled(false); freezed_ = true; ui->panel->waitPaintFinish(); } void PatternEditor::unfreeze() { freezed_ = false; setUpdatesEnabled(true); } QString PatternEditor::getHeaderFont() const { return ui->panel->getHeaderFont(); } int PatternEditor::getHeaderFontSize() const { return ui->panel->getHeaderFontSize(); } QString PatternEditor::getRowsFont() const { return ui->panel->getRowsFont(); } int PatternEditor::getRowsFontSize() const { return ui->panel->getRowsFontSize(); } void PatternEditor::setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize) { ui->panel->setFonts(headerFont, headerSize, rowsFont, rowsSize); } void PatternEditor::setHorizontalScrollMode(bool cellBased, bool refresh) { hScrollCellMove_ = cellBased; if (refresh) updateHorizontalSliderMaximum(); } void PatternEditor::setVisibleTracks(std::vector tracks) { ui->horizontalScrollBar->setMaximum(200); // Dummy ui->panel->setVisibleTracks(tracks); updateHorizontalSliderMaximum(); } std::vector PatternEditor::getVisibleTracks() const { return ui->panel->getVisibleTracks(); } bool PatternEditor::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched) if (watched == this) { if (event->type() == QEvent::FocusIn) { ui->panel->setFocus(); } return false; } if (watched == ui->panel) { if (freezed_ && event->type() != QEvent::Paint) return true; switch (event->type()) { case QEvent::FocusIn: ui->panel->redrawByFocusChanged(); emit focusIn(); return false; case QEvent::FocusOut: ui->panel->redrawByFocusChanged(); emit focusOut(); return false; case QEvent::HoverEnter: case QEvent::HoverLeave: ui->panel->redrawByHoverChanged(); return false; default: return false; } } if (watched == ui->verticalScrollBar) { if (freezed_) return true; // Ignore every events switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::DragMove: case QEvent::Wheel: return (bt_->isPlaySong() && bt_->isFollowPlay()); default: return false; } } return false; } void PatternEditor::resizeEvent(QResizeEvent* event) { Q_UNUSED(event) // For view-based scroll updateHorizontalSliderMaximum(); } /********** Slots **********/ void PatternEditor::onOrderListCurrentTrackChanged(int idx) { ui->panel->onOrderListCurrentTrackChanged(idx); } void PatternEditor::onOrderListCrrentOrderChanged(int num) { ui->panel->onOrderListCurrentOrderChanged(num); } void PatternEditor::onOrderListEdited() { ui->panel->onOrderListEdited(); } void PatternEditor::onDefaultPatternSizeChanged() { ui->panel->onDefaultPatternSizeChanged(); } void PatternEditor::onShortcutUpdated() { ui->panel->onShortcutUpdated(); } void PatternEditor::onPatternDataGlobalChanged() { ui->panel->redrawByPatternChanged(); } void PatternEditor::setPatternHighlight1Count(int count) { ui->panel->setPatternHighlight1Count(count); } void PatternEditor::setPatternHighlight2Count(int count) { ui->panel->setPatternHighlight2Count(count); } void PatternEditor::setEditableStep(int n) { ui->panel->setEditableStep(n); } void PatternEditor::onSongLoaded() { ui->horizontalScrollBar->setValue(0); ui->panel->onSongLoaded(); songLoaded_ = true; updateHorizontalSliderMaximum(); ui->verticalScrollBar->setMaximum(static_cast(bt_->getPatternSizeFromOrderNumber( bt_->getCurrentSongNumber(), bt_->getCurrentOrderNumber())) - 1); ui->verticalScrollBar->setValue(0); } void PatternEditor::onDeletePressed() { ui->panel->onDeletePressed(); } void PatternEditor::onPastePressed() { ui->panel->onPastePressed(); } void PatternEditor::onPasteMixPressed() { ui->panel->onPasteMixPressed(); } void PatternEditor::onPasteOverwritePressed() { ui->panel->onPasteOverwritePressed(); } void PatternEditor::onPasteInsertPressed() { ui->panel->onPasteInsertPressed(); } void PatternEditor::onSelectPressed(int type) { ui->panel->onSelectPressed(type); } void PatternEditor::onTransposePressed(bool isOctave, bool isIncreased) { int seminote = isOctave ? (isIncreased ? 12 : -12) : (isIncreased ? 1 : -1); ui->panel->onNoteTransposePressed(seminote); } void PatternEditor::onChangeValuesPressed(bool isCoarse, bool isIncreased) { int val = isCoarse ? (isIncreased ? 16 : -16) : (isIncreased ? 1 : -1); ui->panel->onChangeValuesPressed(val); } void PatternEditor::onToggleTrackPressed() { ui->panel->onToggleTrackPressed(); } void PatternEditor::onSoloTrackPressed() { ui->panel->onSoloTrackPressed(); } void PatternEditor::onExpandPressed() { ui->panel->onExpandPressed(); } void PatternEditor::onShrinkPressed() { ui->panel->onShrinkPressed(); } void PatternEditor::onInterpolatePressed() { ui->panel->onInterpolatePressed(); } void PatternEditor::onReversePressed() { ui->panel->onReversePressed(); } void PatternEditor::onReplaceInstrumentPressed() { ui->panel->onReplaceInstrumentPressed(); } void PatternEditor::onFollowModeChanged() { ui->panel->onFollowModeChanged(); } void PatternEditor::onStoppedPlaySong() { ui->panel->redrawPatterns(); } void PatternEditor::onDuplicateInstrumentsRemoved() { ui->panel->redrawByPatternChanged(); } void PatternEditor::onPlayStepPressed() { ui->panel->onPlayStepPressed(); } void PatternEditor::updateHorizontalSliderMaximum() { if (!ui->panel->isReadyCore() || !songLoaded_) return; int max = hScrollCellMove_ ? ui->panel->getFullColmunSize() : ui->panel->getScrollableCountByTrack(); ui->horizontalScrollBar->setMaximum(max); } BambooTracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor.hpp000066400000000000000000000077721401124043500263210ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef PATTERN_EDITOR_HPP #define PATTERN_EDITOR_HPP #include #include #include #include #include "bamboo_tracker.hpp" #include "configuration.hpp" #include "gui/color_palette.hpp" namespace Ui { class PatternEditor; } class PatternEditor : public QFrame { Q_OBJECT public: explicit PatternEditor(QWidget *parent = nullptr); ~PatternEditor() override; void setCore(std::shared_ptr core); void setCommandStack(std::weak_ptr stack); void setConfiguration(std::shared_ptr config); void setColorPallete(std::shared_ptr palette); void addActionToPanel(QAction* action); void changeEditable(); void updatePositionByStepUpdate(bool isFirstUpdate); void updatepositionByPositionJump(bool trackChanged = false); void changeMarker(); void copySelectedCells(); void cutSelectedCells(); void freeze(); void unfreeze(); QString getHeaderFont() const; int getHeaderFontSize() const; QString getRowsFont() const; int getRowsFontSize() const; void setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize); void setHorizontalScrollMode(bool cellBased, bool refresh = true); void setVisibleTracks(std::vector tracks); std::vector getVisibleTracks() const; signals: void currentTrackChanged(int idx); void currentOrderChanged(int num, int max); void focusIn(); void focusOut(); void selected(bool isSelected); void instrumentEntered(int num); void volumeEntered(int volume); void effectEntered(QString text); protected: bool eventFilter(QObject *watched, QEvent *event) override; void resizeEvent(QResizeEvent* event) override; public slots: void onOrderListCurrentTrackChanged(int idx); void onOrderListCrrentOrderChanged(int num); void onOrderListEdited(); void onDefaultPatternSizeChanged(); void onShortcutUpdated(); void onPatternDataGlobalChanged(); void setPatternHighlight1Count(int count); void setPatternHighlight2Count(int count); void setEditableStep(int n); void onSongLoaded(); void onDeletePressed(); void onPastePressed(); void onPasteMixPressed(); void onPasteInsertPressed(); void onPasteOverwritePressed(); /// 0: None /// 1: All /// 2: Row /// 3: Column /// 4: Pattern /// 5: Order void onSelectPressed(int type); void onTransposePressed(bool isOctave, bool isIncreased); void onChangeValuesPressed(bool isCoarse, bool isIncreased); void onToggleTrackPressed(); void onSoloTrackPressed(); void onExpandPressed(); void onShrinkPressed(); void onInterpolatePressed(); void onReversePressed(); void onReplaceInstrumentPressed(); void onFollowModeChanged(); void onStoppedPlaySong(); void onDuplicateInstrumentsRemoved(); void onPlayStepPressed(); private: Ui::PatternEditor *ui; std::shared_ptr bt_; bool freezed_; bool songLoaded_; bool hScrollCellMove_; void updateHorizontalSliderMaximum(); }; #endif // PATTERN_EDITOR_HPP BambooTracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor.ui000066400000000000000000000033461401124043500261400ustar00rootroot00000000000000 PatternEditor 0 0 400 300 Frame QFrame::Panel QFrame::Sunken 0 0 0 0 0 Qt::Horizontal Qt::Vertical PatternEditorPanel QWidget
gui/pattern_editor/pattern_editor_panel.hpp
1
BambooTracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor_panel.cpp000066400000000000000000003332511401124043500274650ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pattern_editor_panel.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "midi/midi.hpp" #include "command/pattern/set_effect_value_to_step_command.hpp" #include "jamming.hpp" #include "note.hpp" #include "bamboo_tracker_defs.hpp" #include "gui/event_guard.hpp" #include "gui/command/pattern/pattern_commands_qt.hpp" #include "gui/effect_description.hpp" #include "gui/jam_layout.hpp" #include "gui/gui_utils.hpp" #include "utils.hpp" PatternEditorPanel::PatternEditorPanel(QWidget *parent) : QWidget(parent), config_(std::make_shared()), // Dummy stepFontWidth_(0), stepFontHeight_(0), stepFontAscent_(0), stepFontLeading_(0), headerFontAscent_(0), widthSpace_(0), widthSpaceDbl_(0), stepNumWidthCnt_(0), stepNumWidth_(0), stepNumBase_(0), baseTrackWidth_(0), toneNameWidth_(0), instWidth_(0), volWidth_(0), effWidth_(0), effIDWidth_(0), effValWidth_(0), tracksWidthFromLeftToEnd_(0), hdMuteToggleWidth_(0), hdEffCompandButtonWidth_(0), headerHeight_(0), hdPlusY_(0), hdMinusY_(0), curRowBaselineY_(0), curRowY_(0), visTracks_(1), // Dummy rightEffn_(16), // Dummy leftTrackVisIdx_(0), curSongNum_(0), curPos_{ 0, 0, 0, 0, }, hovPos_{ -1, -1, -1, -1 }, mousePressPos_{ -1, -1, -1, -1 }, mouseReleasePos_{ -1, -1, -1, -1 }, selLeftAbovePos_{ -1, -1, -1, -1 }, selRightBelowPos_{ -1, -1, -1, -1 }, shiftPressedPos_{ -1, -1, -1, -1 }, doubleClickPos_{ -1, -1, -1, -1 }, markerPos_{ -1, -1, -1, -1 }, isIgnoreToSlider_(false), isIgnoreToOrder_(false), isPressedPlus_(false), isPressedMinus_(false), entryCnt_(0), selectAllState_(-1), isMuteElse_(false), hl1Cnt_(4), hl2Cnt_(16), editableStepCnt_(1), viewedRowCnt_(1), viewedRowsHeight_(0), viewedRowOffset_(0), viewedCenterY_(0), viewedCenterBaseY_(0), backChanged_(false), textChanged_(false), foreChanged_(false), headerChanged_(false), focusChanged_(false), followModeChanged_(false), hasFocussedBefore_(false), stepDownCount_(0), repaintable_(true), repaintingCnt_(0), isInitedFirstMod_(false), upSc_(Qt::Key_Up, this, nullptr, nullptr, Qt::WidgetShortcut), upWSSc_(Qt::SHIFT + Qt::Key_Up, this, nullptr, nullptr, Qt::WidgetShortcut), dnSc_(Qt::Key_Down, this, nullptr, nullptr, Qt::WidgetShortcut), dnWSSc_(Qt::SHIFT + Qt::Key_Down, this, nullptr, nullptr, Qt::WidgetShortcut), pgUpSc_(Qt::Key_PageUp, this, nullptr, nullptr, Qt::WidgetShortcut), pgUpWSSc_(Qt::SHIFT + Qt::Key_PageUp, this, nullptr, nullptr, Qt::WidgetShortcut), pgDnSc_(Qt::Key_PageDown, this, nullptr, nullptr, Qt::WidgetShortcut), pgDnWSSc_(Qt::SHIFT + Qt::Key_PageDown, this, nullptr, nullptr, Qt::WidgetShortcut), homeSc_(Qt::Key_Home, this, nullptr, nullptr, Qt::WidgetShortcut), homeWSSc_(Qt::SHIFT + Qt::Key_Home, this, nullptr, nullptr, Qt::WidgetShortcut), endSc_(Qt::Key_End, this, nullptr, nullptr, Qt::WidgetShortcut), endWSSc_(Qt::SHIFT + Qt::Key_End, this, nullptr, nullptr, Qt::WidgetShortcut), hlUpSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut), hlUpWSSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut), hlDnSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut), hlDnWSSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut), ltSc_(Qt::Key_Left, this, nullptr, nullptr, Qt::WidgetShortcut), ltWSSc_(Qt::SHIFT + Qt::Key_Left, this, nullptr, nullptr, Qt::WidgetShortcut), rtSc_(Qt::Key_Right, this, nullptr, nullptr, Qt::WidgetShortcut), rtWSSc_(Qt::SHIFT + Qt::Key_Right, this, nullptr, nullptr, Qt::WidgetShortcut), keyOffSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut), echoBufSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut), stepMvUpSc_(Qt::ALT + Qt::Key_Up, this, nullptr, nullptr, Qt::WidgetShortcut), stepMvDnSc_(Qt::ALT + Qt::Key_Down, this, nullptr, nullptr, Qt::WidgetShortcut), expandColSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut), shrinkColSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut) { setAttribute(Qt::WA_Hover); setFocusPolicy(Qt::ClickFocus); setContextMenuPolicy(Qt::CustomContextMenu); // Initialize font headerFont_ = QApplication::font(); headerFont_.setPointSize(10); stepFont_ = QFont("Monospace", 10); stepFont_.setStyleHint(QFont::TypeWriter); stepFont_.setStyleStrategy(QFont::ForceIntegerMetrics); updateSizes(); // Track visibility songStyle_.type = SongType::Standard; // Dummy songStyle_.trackAttribs.push_back({ 0, SoundSource::FM, 0 }); // Dummy std::iota(visTracks_.begin(), visTracks_.end(), 0); // Shortcuts auto vMoveLam = [&] (bool isShift, auto getFunc) { // For lazy evaluation if (bt_->isPlaySong() && bt_->isFollowPlay()) return; moveCursorToDown(getFunc()); checkSelectionByCursorMove(isShift); }; auto upLam = [&] { return (editableStepCnt_ ? -editableStepCnt_ : -1); }; QObject::connect(&upSc_, &QShortcut::activated, this, [vMoveLam, upLam] { vMoveLam(false, upLam); }); QObject::connect(&upWSSc_, &QShortcut::activated, this, [vMoveLam, upLam] { vMoveLam(true, upLam); }); auto dnLam = [&] { return (editableStepCnt_ ? editableStepCnt_ : 1); }; QObject::connect(&dnSc_, &QShortcut::activated, this, [vMoveLam, dnLam] { vMoveLam(false, dnLam); }); QObject::connect(&dnWSSc_, &QShortcut::activated, this, [vMoveLam, dnLam] { vMoveLam(true, dnLam); }); auto pgUpLam = [&] { return -static_cast(config_->getPageJumpLength()); }; QObject::connect(&pgUpSc_, &QShortcut::activated, this, [vMoveLam, pgUpLam] { vMoveLam(false, pgUpLam); }); QObject::connect(&pgUpWSSc_, &QShortcut::activated, this, [vMoveLam, pgUpLam] { vMoveLam(true, pgUpLam); }); auto pgDnLam = [&] { return static_cast(config_->getPageJumpLength()); }; QObject::connect(&pgDnSc_, &QShortcut::activated, this, [vMoveLam, pgDnLam] { vMoveLam(false, pgDnLam); }); QObject::connect(&pgDnWSSc_, &QShortcut::activated, this, [vMoveLam, pgDnLam] { vMoveLam(true, pgDnLam); }); auto homeLam = [&] { return -curPos_.step; }; QObject::connect(&homeSc_, &QShortcut::activated, this, [vMoveLam, homeLam] { vMoveLam(false, homeLam); }); QObject::connect(&homeWSSc_, &QShortcut::activated, this, [vMoveLam, homeLam] { vMoveLam(true, homeLam); }); auto endLam = [&] { return (static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - curPos_.step - 1); }; QObject::connect(&endSc_, &QShortcut::activated, this, [vMoveLam, endLam] { vMoveLam(false, endLam); }); QObject::connect(&endWSSc_, &QShortcut::activated, this, [vMoveLam, endLam] { vMoveLam(true, endLam); }); auto hlUpLam = [&] { int base; if (curPos_.step) { base = curPos_.step; } else { base = static_cast(bt_->getPatternSizeFromOrderNumber( curSongNum_, (curPos_.order) ? (curPos_.order - 1) : (static_cast(bt_->getOrderSize(curSongNum_)) - 1))); } return ((base - 1) / hl1Cnt_ * hl1Cnt_ - base); }; QObject::connect(&hlUpSc_, &QShortcut::activated, this, [vMoveLam, hlUpLam] { vMoveLam(false, hlUpLam); }); QObject::connect(&hlUpWSSc_, &QShortcut::activated, this, [vMoveLam, hlUpLam] { vMoveLam(true, hlUpLam); }); auto hlDnLam = [&] { int next = std::min((curPos_.step / hl1Cnt_ + 1) * hl1Cnt_, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order))); return (next - curPos_.step); }; QObject::connect(&hlDnSc_, &QShortcut::activated, this, [vMoveLam, hlDnLam] { vMoveLam(false, hlDnLam); }); QObject::connect(&hlDnWSSc_, &QShortcut::activated, this, [vMoveLam, hlDnLam] { vMoveLam(true, hlDnLam); }); auto ltRtLam = [&] (bool isLeft, bool isShift) { moveCursorToRight(isLeft ? -1 : 1); checkSelectionByCursorMove(isShift); }; QObject::connect(<Sc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(true, false); }); QObject::connect(<WSSc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(true, true); }); QObject::connect(&rtSc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(false, false); }); QObject::connect(&rtWSSc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(false, true); }); QObject::connect(&keyOffSc_, &QShortcut::activated, this, [&] { if (!bt_->isJamMode() && curPos_.colInTrack == 0) { bt_->setStepKeyOff(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step); comStack_.lock()->push(new SetKeyOffToStepQtCommand(this)); if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_); } }); QObject::connect(&echoBufSc_, &QShortcut::activated, this, [&] { if (!bt_->isJamMode() && curPos_.colInTrack == 0) { int n = bt_->getCurrentOctave(); if (n > 3) n = 3; bt_->setEchoBufferAccess(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, n); comStack_.lock()->push(new SetEchoBufferAccessQtCommand(this)); if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_); } }); QObject::connect(&stepMvUpSc_, &QShortcut::activated, this, [&] { if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !bt_->isJamMode()) deletePreviousStep(); }); QObject::connect(&stepMvDnSc_, &QShortcut::activated, this, [&] { if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !bt_->isJamMode()) { insertStep(); moveCursorToDown(1); } }); QObject::connect(&expandColSc_, &QShortcut::activated, this, [&] { onExpandEffectColumnPressed(curPos_.trackVisIdx); }); QObject::connect(&shrinkColSc_, &QShortcut::activated, this, [&] { onShrinkEffectColumnPressed(curPos_.trackVisIdx); }); onShortcutUpdated(); // MIDI midiKeyEventMethod_ = metaObject()->indexOfSlot("midiKeyEvent(uchar,uchar,uchar)"); Q_ASSERT(midiKeyEventMethod_ != -1); MidiInterface::getInstance().installInputHandler(&midiThreadReceivedEvent, this); } PatternEditorPanel::~PatternEditorPanel() { MidiInterface::getInstance().uninstallInputHandler(&midiThreadReceivedEvent, this); } void PatternEditorPanel::funcResize() { // Recalculate center row position curRowY_ = (geometry().height() + headerHeight_ - stepFontHeight_) >> 1; curRowBaselineY_ = curRowY_ + stepFontAscent_ - (stepFontLeading_ >> 1); initDisplay(); } void PatternEditorPanel::updateSizes() { QFontMetrics metrics(stepFont_); #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) stepFontWidth_ = metrics.horizontalAdvance('0'); #else stepFontWidth_ = metrics.width('0'); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) stepFontAscent_ = metrics.capHeight(); #else stepFontAscent_ = metrics.boundingRect('X').height(); #endif stepFontLeading_ = metrics.ascent() - stepFontAscent_ + metrics.descent() / 2; stepFontHeight_ = stepFontAscent_ + stepFontLeading_; QFontMetrics m(headerFont_); headerFontAscent_ = m.ascent() + 2; /* Width & height */ widthSpace_ = stepFontWidth_ / 5 * 2; widthSpaceDbl_ = widthSpace_ * 2; stepNumWidthCnt_ = config_->getShowRowNumberInHex() ? 2 : 3; if (config_->getShowRowNumberInHex()) { stepNumWidthCnt_ = 2; stepNumBase_ = 16; } else { stepNumWidthCnt_ = 3; stepNumBase_ = 10; } stepNumWidth_ = stepFontWidth_ * stepNumWidthCnt_ + widthSpace_; toneNameWidth_ = stepFontWidth_ * 3; instWidth_ = stepFontWidth_ * 2; volWidth_ = stepFontWidth_ * 2; effIDWidth_ = stepFontWidth_ * 2; effValWidth_ = stepFontWidth_ * 2; effWidth_ = effIDWidth_ + effValWidth_ + widthSpaceDbl_; baseTrackWidth_ = toneNameWidth_ + instWidth_ + volWidth_ + effIDWidth_ + effValWidth_ + widthSpaceDbl_ * 4; #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) hdEffCompandButtonWidth_ = m.horizontalAdvance("+"); #else hdEffCompandButtonWidth_ = m.width("+"); #endif hdMuteToggleWidth_ = baseTrackWidth_ - hdEffCompandButtonWidth_ - stepFontWidth_ / 2 * 3; headerHeight_ = m.height() * 2 + 1; hdPlusY_ = headerHeight_ / 4 + m.lineSpacing() / 2 - m.leading() / 2 - m.descent(); hdMinusY_ = headerHeight_ / 2 + hdPlusY_; initDisplay(); } void PatternEditorPanel::initDisplay() { int width = geometry().width(); // Recalculate pixmap sizes viewedRegionHeight_ = std::max((geometry().height() - headerHeight_), stepFontHeight_); int cnt = viewedRegionHeight_ / stepFontHeight_; viewedRowCnt_ = (cnt % 2) ? (cnt + 2) : (cnt + 1); viewedRowsHeight_ = viewedRowCnt_ * stepFontHeight_; viewedRowOffset_ = (viewedRowsHeight_ - viewedRegionHeight_) >> 1; viewedCenterY_ = (viewedRowsHeight_ - stepFontHeight_) >> 1; viewedCenterBaseY_ = viewedCenterY_ + stepFontAscent_ + (stepFontLeading_ >> 1); completePixmap_ = QPixmap(geometry().size()); backPixmap_ = QPixmap(width, viewedRowsHeight_); textPixmap_ = QPixmap(width, viewedRowsHeight_); forePixmap_ = QPixmap(width, viewedRowsHeight_); headerPixmap_ = QPixmap(width, headerHeight_); } void PatternEditorPanel::setCore(std::shared_ptr core) { bt_ = core; } bool PatternEditorPanel::isReadyCore() const { return (bt_ != nullptr); } void PatternEditorPanel::setCommandStack(std::weak_ptr stack) { comStack_ = stack; } void PatternEditorPanel::setConfiguration(std::shared_ptr config) { config_ = config; } void PatternEditorPanel::setColorPallete(std::shared_ptr palette) { palette_ = palette; } void PatternEditorPanel::waitPaintFinish() { while (true) { if (repaintingCnt_.load()) std::this_thread::sleep_for(std::chrono::milliseconds(10)); else { curPos_ = { 0, 0, 0, 0 }; // Init return; } } } QString PatternEditorPanel::getHeaderFont() const { return QFontInfo(headerFont_).family(); } int PatternEditorPanel::getHeaderFontSize() const { return QFontInfo(headerFont_).pointSize(); } QString PatternEditorPanel::getRowsFont() const { return QFontInfo(stepFont_).family(); } int PatternEditorPanel::getRowsFontSize() const { return QFontInfo(stepFont_).pointSize(); } void PatternEditorPanel::setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize) { headerFont_ = QFont(headerFont, headerSize); stepFont_ = QFont(rowsFont, rowsSize); updateSizes(); updateTracksWidthFromLeftToEnd(); redrawAll(); } void PatternEditorPanel::setVisibleTracks(std::vector tracks) { visTracks_ = tracks; rightEffn_.resize(visTracks_.size()); std::transform(visTracks_.begin(), visTracks_.end(), rightEffn_.begin(), [&](int t) { return static_cast(bt_->getEffectDisplayWidth(curSongNum_, t)); }); int max = static_cast(tracks.size()); bool cond = (max <= curPos_.trackVisIdx); if (cond) curPos_.trackVisIdx = max; leftTrackVisIdx_ = std::min(leftTrackVisIdx_, curPos_.trackVisIdx); updateTracksWidthFromLeftToEnd(); bt_->setCurrentTrack(visTracks_.at(curPos_.trackVisIdx)); emit currentTrackChanged(curPos_.trackVisIdx); if (cond) { if (config_->getMoveCursorByHorizontalScroll()) emit hScrollBarChangeRequested(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack)); else emit hScrollBarChangeRequested(leftTrackVisIdx_); } redrawAll(); } std::vector PatternEditorPanel::getVisibleTracks() const { return visTracks_; } void PatternEditorPanel::redrawByPatternChanged(bool patternSizeChanged) { textChanged_ = true; // When pattern size is changed, redraw all area if (patternSizeChanged) { backChanged_ = true; foreChanged_ = true; } repaint(); } void PatternEditorPanel::redrawByFocusChanged() { if (hasFocussedBefore_) { focusChanged_ = true; repaint(); } else { redrawAll(); hasFocussedBefore_ = true; } } void PatternEditorPanel::redrawByHoverChanged() { headerChanged_ = true; backChanged_ = true; repaint(); } void PatternEditorPanel::redrawByMaskChanged() { foreChanged_ = true; headerChanged_ = true; repaint(); } void PatternEditorPanel::redrawPatterns() { backChanged_ = true; textChanged_ = true; foreChanged_ = true; repaint(); } void PatternEditorPanel::redrawAll() { headerChanged_ = true; redrawPatterns(); } void PatternEditorPanel::resetEntryCount() { entryCnt_ = 0; } void PatternEditorPanel::drawPattern(const QRect &rect) { if (repaintable_.load()) { repaintable_.store(false); ++repaintingCnt_; // Use module data after this line if (rect.size() != completePixmap_.size()) { // Prevent resize event was failed funcResize(); headerChanged_ = true; backChanged_ = true; textChanged_ = true; foreChanged_ = true; } if (backChanged_ || textChanged_ || foreChanged_ || headerChanged_ || focusChanged_ || stepDownCount_ || followModeChanged_) { int maxWidth = std::min(rect.width(), tracksWidthFromLeftToEnd_); completePixmap_.fill(palette_->ptnBackColor); if (!focusChanged_) { if (stepDownCount_ && !followModeChanged_) { quickDrawRows(maxWidth); } else { backPixmap_.fill(Qt::transparent); if (textChanged_) textPixmap_.fill(Qt::transparent); if (foreChanged_) forePixmap_.fill(Qt::transparent); drawRows(maxWidth); } drawBorders(maxWidth); if (headerChanged_) { // headerPixmap_->fill(Qt::transparent); drawHeaders(maxWidth); } } { QPainter mergePainter(&completePixmap_); QRect rowsRect(0, viewedRowOffset_, maxWidth, viewedRegionHeight_); QRect inViewRect(0, headerHeight_, maxWidth, viewedRegionHeight_); mergePainter.drawPixmap(inViewRect, backPixmap_, rowsRect); mergePainter.drawPixmap(inViewRect, textPixmap_, rowsRect); mergePainter.drawPixmap(inViewRect, forePixmap_, rowsRect); mergePainter.drawPixmap(headerPixmap_.rect(), headerPixmap_); } if (!hasFocus()) drawShadow(); backChanged_ = false; textChanged_ = false; foreChanged_ = false; headerChanged_ = false; focusChanged_ = false; followModeChanged_ = false; stepDownCount_ = 0; } --repaintingCnt_; // Used module data until this line repaintable_.store(true); } QPainter completePainter(this); completePainter.drawPixmap(rect, completePixmap_); } void PatternEditorPanel::drawRows(int maxWidth) { QPainter forePainter(&forePixmap_); QPainter textPainter(&textPixmap_); QPainter backPainter(&backPixmap_); textPainter.setFont(stepFont_); /* Current row */ // Fill row backPainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_, bt_->isJamMode() ? palette_->ptnCurStepColor : palette_->ptnCurEditStepColor); // Step number if (markerPos_.isEqualRows(curPos_)) backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(curPos_)) backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover if (textChanged_) { if (curPos_.step % hl2Cnt_) { textPainter.setPen(!(curPos_.step % hl2Cnt_) ? palette_->ptnHl1StepNumColor : !(curPos_.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor : palette_->ptnDefStepNumColor); } else { textPainter.setPen(palette_->ptnHl2StepNumColor); } textPainter.drawText(1, viewedCenterBaseY_, QString("%1").arg(curPos_.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper()); } // Step data for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, curPos_.order, curPos_.step, x, viewedCenterBaseY_, viewedCenterY_); } viewedCenterPos_ = curPos_; int stepNum, odrNum; int rowY, baseY; int playOdrNum = bt_->getPlayingOrderNumber(); int playStepNum = bt_->getPlayingStepNumber(); /* Previous rows */ viewedFirstPos_ = curPos_; for (rowY = viewedCenterY_ - stepFontHeight_, baseY = viewedCenterBaseY_ - stepFontHeight_, stepNum = curPos_.step - 1, odrNum = curPos_.order; rowY >= 0; rowY -= stepFontHeight_, baseY -= stepFontHeight_, --stepNum) { if (stepNum == -1) { if (odrNum == 0) { break; } else if (config_->getShowPreviousNextOrders()) { --odrNum; stepNum = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, odrNum)) - 1; } else { break; } } QColor rowColor; if (!config_->getFollowMode() && odrNum == playOdrNum && stepNum == playStepNum) { rowColor = palette_->ptnPlayStepColor; } else { rowColor = !(stepNum % hl2Cnt_) ? palette_->ptnHl2StepColor : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepColor : palette_->ptnDefStepColor; } // Fill row backPainter.fillRect(0, rowY, maxWidth, stepFontHeight_, rowColor); // Step number if (markerPos_.isEqualRows(odrNum, stepNum)) backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(odrNum, stepNum)) backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover if (textChanged_) { textPainter.setPen(!(stepNum % hl2Cnt_) ? palette_->ptnHl2StepNumColor : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepNumColor : palette_->ptnDefStepNumColor); textPainter.drawText(1, baseY, QString("%1").arg(stepNum, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper()); } // Step data for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, odrNum, stepNum, x, baseY, rowY); } if (foreChanged_) { if (odrNum != curPos_.order) // Mask forePainter.fillRect(0, rowY, maxWidth, stepFontHeight_, palette_->ptnMaskColor); } viewedFirstPos_.setRows(odrNum, stepNum); } int stepEnd = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)); /* Next rows */ viewedLastPos_ = curPos_; for (rowY = viewedCenterY_ + stepFontHeight_, baseY = viewedCenterBaseY_ + stepFontHeight_, stepNum = curPos_.step + 1, odrNum = curPos_.order; rowY < viewedRowsHeight_; rowY += stepFontHeight_, baseY += stepFontHeight_, ++stepNum) { if (stepNum == stepEnd) { if (odrNum == static_cast(bt_->getOrderSize(curSongNum_)) - 1) { break; } else if (config_->getShowPreviousNextOrders()) { ++odrNum; stepNum = 0; stepEnd = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, odrNum)); } else { break; } } QColor rowColor; if (!config_->getFollowMode() && odrNum == playOdrNum && stepNum == playStepNum) { rowColor = palette_->ptnPlayStepColor; } else { rowColor = !(stepNum % hl2Cnt_) ? palette_->ptnHl2StepColor : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepColor : palette_->ptnDefStepColor; } // Fill row backPainter.fillRect(0, rowY, maxWidth, stepFontHeight_, rowColor); // Step number if (markerPos_.isEqualRows(odrNum, stepNum)) backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(odrNum, stepNum)) backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover if (textChanged_) { textPainter.setPen(!(stepNum % hl2Cnt_) ? palette_->ptnHl2StepNumColor : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepNumColor : palette_->ptnDefStepNumColor); textPainter.drawText(1, baseY, QString("%1").arg(stepNum, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper()); } // Step data for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, odrNum, stepNum, x, baseY, rowY); } if (foreChanged_) { if (odrNum != curPos_.order) // Mask forePainter.fillRect(0, rowY, maxWidth, stepFontHeight_, palette_->ptnMaskColor); } viewedLastPos_.setRows(odrNum, stepNum); } } void PatternEditorPanel::quickDrawRows(int maxWidth) { int halfRowsCnt = viewedRowCnt_ >> 1; bool repaintForeAll = (curPos_.step - stepDownCount_ < 0); int shift = stepFontHeight_ * stepDownCount_; /* Move up */ QRect srcRect(0, 0, maxWidth, viewedRowsHeight_); if (!repaintForeAll) forePixmap_.scroll(0, -shift, srcRect); textPixmap_.scroll(0, -shift, srcRect); backPixmap_.scroll(0, -shift, srcRect); { PatternPosition fpos = calculatePositionFrom(viewedCenterPos_.order, viewedCenterPos_.step, stepDownCount_ - halfRowsCnt); if (fpos.order != -1) viewedFirstPos_ = std::move(fpos); } QPainter forePainter(&forePixmap_); QPainter textPainter(&textPixmap_); QPainter backPainter(&backPixmap_); textPainter.setFont(stepFont_); /* Clear previous cursor row, current cursor row and last rows text and foreground */ int prevY = viewedCenterY_ - shift; int lastY = viewedRowsHeight_ - shift; textPainter.setCompositionMode(QPainter::CompositionMode_Source); textPainter.fillRect(0, prevY, maxWidth, stepFontHeight_, Qt::transparent); textPainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_, Qt::transparent); textPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent); textPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); if (!repaintForeAll) { forePainter.setCompositionMode(QPainter::CompositionMode_Source); forePainter.fillRect(0, prevY, maxWidth, stepFontHeight_, Qt::transparent); forePainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_, Qt::transparent); forePainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent); forePainter.setCompositionMode(QPainter::CompositionMode_SourceOver); } /* Redraw previous cursor step */ { int baseY = viewedCenterBaseY_ - shift; QColor rowColor = !(viewedCenterPos_.step % hl2Cnt_) ? palette_->ptnHl2StepColor : !(viewedCenterPos_.step % hl1Cnt_) ? palette_->ptnHl1StepColor : palette_->ptnDefStepColor; // Fill row backPainter.fillRect(0, prevY, maxWidth, stepFontHeight_, rowColor); // Step number if (markerPos_.isEqualRows(viewedCenterPos_)) backPainter.fillRect(0, prevY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(viewedCenterPos_)) backPainter.fillRect(0, prevY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover textPainter.setPen(!(viewedCenterPos_.step % hl2Cnt_) ? palette_->ptnHl2StepNumColor : !(viewedCenterPos_.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor : palette_->ptnDefStepNumColor); textPainter.drawText(1, baseY, QString("%1").arg(viewedCenterPos_.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper()); // Step data for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, viewedCenterPos_.order, viewedCenterPos_.step, x, baseY, prevY); } } /* Redraw current cursor step */ // Fill row backPainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_, bt_->isJamMode() ? palette_->ptnCurStepColor : palette_->ptnCurEditStepColor); // Step number if (markerPos_.isEqualRows(curPos_)) backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(curPos_)) backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover if (curPos_.step % hl2Cnt_) { textPainter.setPen(!(curPos_.step % hl2Cnt_) ? palette_->ptnHl1StepNumColor : !(curPos_.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor : palette_->ptnDefStepNumColor); } else { textPainter.setPen(palette_->ptnHl2StepNumColor); } textPainter.drawText(1, viewedCenterBaseY_, QString("%1").arg(curPos_.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper()); // Step data for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, curPos_.order, curPos_.step, x, viewedCenterBaseY_, viewedCenterY_); } viewedCenterPos_ = curPos_; /* Draw new step at last if necessary */ { PatternPosition bpos = calculatePositionFrom(viewedCenterPos_.order, viewedCenterPos_.step, halfRowsCnt); if (!config_->getShowPreviousNextOrders() && viewedCenterPos_.order != bpos.order) { // Clear row backPainter.setCompositionMode(QPainter::CompositionMode_Source); backPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent); } else { int baseY = lastY + (viewedCenterBaseY_ - viewedCenterY_); bpos = std::exchange(viewedLastPos_, bpos); while (true) { if (bpos.compareRows(viewedLastPos_) == 0) break; PatternPosition tmpBpos = calculatePositionFrom(bpos.order, bpos.step, 1); if (tmpBpos.order == -1) { // when viewedLastPos_.row == -1 (viewedlastPos_.row < viewedCenterPos_.row + halRowsCnt) viewedLastPos_ = bpos; // Clear row backPainter.setCompositionMode(QPainter::CompositionMode_Source); backPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent); break; } else { bpos = tmpBpos; } QColor rowColor = !(bpos.step % hl2Cnt_) ? palette_->ptnHl2StepColor : !(bpos.step % hl1Cnt_) ? palette_->ptnHl1StepColor : palette_->ptnDefStepColor; // Fill row backPainter.fillRect(0, lastY, maxWidth, stepFontHeight_, rowColor); // Step number if (markerPos_.isEqualRows(bpos)) backPainter.fillRect(0, lastY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(bpos)) backPainter.fillRect(0, lastY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover textPainter.setPen(!(bpos.step % hl2Cnt_) ? palette_->ptnHl2StepNumColor : !(bpos.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor : palette_->ptnDefStepNumColor); textPainter.drawText(1, baseY, QString("%1").arg(bpos.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper()); // Step data for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, bpos.order, bpos.step, x, baseY, lastY); } if (bpos.order != curPos_.order) // Mask forePainter.fillRect(0, lastY, maxWidth, stepFontHeight_, palette_->ptnMaskColor); baseY += stepFontHeight_; lastY += stepFontHeight_; } } } /* Redraw foreground all area if new order */ if (repaintForeAll) { forePixmap_.fill(Qt::transparent); int y = viewedCenterY_ - viewedCenterPos_.step * stepFontHeight_; if (y > 0) forePainter.fillRect(0, 0, maxWidth, y, palette_->ptnMaskColor); if (viewedLastPos_.order != viewedCenterPos_.order) { y += static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, viewedCenterPos_.order)) * stepFontHeight_; forePainter.fillRect(0, y, maxWidth, viewedRowsHeight_ - y, palette_->ptnMaskColor); } for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { int w = baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast(trackVisIdx)); if (foreChanged_ && bt_->isMute(visTracks_.at(trackVisIdx))) // Paint mute mask forePainter.fillRect(x, 0, w, viewedRowsHeight_, palette_->ptnMaskColor); x += w; } } } int PatternEditorPanel::drawStep(QPainter &forePainter, QPainter &textPainter, QPainter& backPainter, int trackVisIdx, int orderNum, int stepNum, int x, int baseY, int rowY) { int trackNum = visTracks_.at(trackVisIdx); int offset = x + widthSpace_; PatternPosition pos{ trackVisIdx, 0, orderNum, stepNum }; QColor textColor = curPos_.isEqualRows(orderNum, stepNum) ? palette_->ptnCurTextColor : palette_->ptnDefTextColor; bool isHovTrack = (hovPos_.order == -2 && hovPos_.trackVisIdx == trackVisIdx); bool isHovStep = (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(orderNum, stepNum)); SoundSource src = songStyle_.trackAttribs[static_cast(trackNum)].source; /* Tone name */ if (pos == curPos_) // Paint current cell backPainter.fillRect(offset - widthSpace_, rowY, toneNameWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnCurCellColor); if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover backPainter.fillRect(offset - widthSpace_, rowY, toneNameWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0) && isSelectedCell(trackVisIdx, 0, orderNum, stepNum)) // Paint selected backPainter.fillRect(offset - widthSpace_, rowY, toneNameWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnSelCellColor); if (textChanged_) { int noteNum = bt_->getStepNoteNumber(curSongNum_, trackNum, orderNum, stepNum); switch (noteNum) { case -1: // None textPainter.setPen(textColor); textPainter.drawText(offset, baseY, "---"); break; case -2: // Key off textPainter.fillRect(offset, rowY + stepFontHeight_ * 2 / 5, toneNameWidth_, stepFontHeight_ / 5, palette_->ptnNoteColor); break; case -3: // Echo 0 textPainter.setPen(palette_->ptnNoteColor); textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^0"); break; case -4: // Echo 1 textPainter.setPen(palette_->ptnNoteColor); textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^1"); break; case -5: // Echo 2 textPainter.setPen(palette_->ptnNoteColor); textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^2"); break; case -6: // Echo 3 textPainter.setPen(palette_->ptnNoteColor); textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^3"); break; default: // Convert tone name { QString toneStr; switch (noteNum % 12) { case 0: toneStr = "C-"; break; case 1: toneStr = "C#"; break; case 2: toneStr = "D-"; break; case 3: toneStr = "D#"; break; case 4: toneStr = "E-"; break; case 5: toneStr = "F-"; break; case 6: toneStr = "F#"; break; case 7: toneStr = "G-"; break; case 8: toneStr = "G#"; break; case 9: toneStr = "A-"; break; case 10: toneStr = "A#"; break; case 11: toneStr = "B-"; break; } textPainter.setPen(palette_->ptnNoteColor); textPainter.drawText(offset, baseY, toneStr + QString::number(noteNum / 12)); break; } } } offset += toneNameWidth_ + widthSpaceDbl_; pos.colInTrack = 1; /* Instrument */ if (pos == curPos_) // Paint current cell backPainter.fillRect(offset - widthSpace_, rowY, instWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnCurCellColor); if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover backPainter.fillRect(offset - widthSpace_, rowY, instWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0) && isSelectedCell(trackVisIdx, 1, orderNum, stepNum)) // Paint selected backPainter.fillRect(offset - widthSpace_, rowY, instWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnSelCellColor); if (textChanged_) { int instNum = bt_->getStepInstrument(curSongNum_, trackNum, orderNum, stepNum); if (instNum == -1) { textPainter.setPen(textColor); textPainter.drawText(offset, baseY, "--"); } else { std::unique_ptr inst = bt_->getInstrument(instNum); textPainter.setPen((inst != nullptr && src == inst->getSoundSource()) ? palette_->ptnInstColor : palette_->ptnErrorColor); textPainter.drawText(offset, baseY, QString("%1").arg(instNum, 2, 16, QChar('0')).toUpper()); } } offset += instWidth_ + widthSpaceDbl_; pos.colInTrack = 2; /* Volume */ if (pos == curPos_) // Paint current cell backPainter.fillRect(offset - widthSpace_, rowY, volWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnCurCellColor); if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover backPainter.fillRect(offset - widthSpace_, rowY, volWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0) && isSelectedCell(trackVisIdx, 2, orderNum, stepNum)) // Paint selected backPainter.fillRect(offset - widthSpace_, rowY, volWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnSelCellColor); if (textChanged_) { int vol = bt_->getStepVolume(curSongNum_, trackNum, orderNum, stepNum); if (vol == -1) { textPainter.setPen(textColor); textPainter.drawText(offset, baseY, "--"); } else { int volLim = 0; // Dummy set switch (src) { case SoundSource::FM: volLim = bt_defs::NSTEP_FM_VOLUME ; break; case SoundSource::SSG: volLim = bt_defs::NSTEP_SSG_VOLUME; break; case SoundSource::RHYTHM: volLim = bt_defs::NSTEP_RHYTHM_VOLUME; break; case SoundSource::ADPCM: volLim = bt_defs::NSTEP_ADPCM_VOLUME; break; } textPainter.setPen((vol < volLim) ? palette_->ptnVolColor : palette_->ptnErrorColor); if (src == SoundSource::FM && vol < volLim && config_->getReverseFMVolumeOrder()) { vol = volLim - vol - 1; } textPainter.drawText(offset, baseY, QString("%1").arg(vol, 2, 16, QChar('0')).toUpper()); } } offset += volWidth_ + widthSpaceDbl_; pos.colInTrack = 3; /* Effect */ for (int i = 0; i <= rightEffn_.at(static_cast(trackVisIdx)); ++i) { /* Effect ID */ if (pos == curPos_) // Paint current cell backPainter.fillRect(offset - widthSpace_, rowY, effIDWidth_ + widthSpace_, stepFontHeight_, palette_->ptnCurCellColor); if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover backPainter.fillRect(offset - widthSpace_, rowY, effIDWidth_ + widthSpace_, stepFontHeight_, palette_->ptnHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0) && isSelectedCell(trackVisIdx, pos.colInTrack, orderNum, stepNum)) // Paint selected backPainter.fillRect(offset - widthSpace_, rowY, effIDWidth_ + widthSpace_, stepFontHeight_, palette_->ptnSelCellColor); std::string effId; QString effStr; if (textChanged_) { effId = bt_->getStepEffectID(curSongNum_, trackNum, orderNum, stepNum, i); effStr = QString::fromStdString(effId); if (effStr == "--") { textPainter.setPen(textColor); textPainter.drawText(offset, baseY, effStr); } else { textPainter.setPen(palette_->ptnEffColor); textPainter.drawText(offset, baseY, effStr); } } offset += effIDWidth_; ++pos.colInTrack; /* Effect Value */ if (pos == curPos_) // Paint current cell backPainter.fillRect(offset, rowY, effValWidth_ + widthSpace_, stepFontHeight_, palette_->ptnCurCellColor); if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover backPainter.fillRect(offset, rowY, effValWidth_ + widthSpace_, stepFontHeight_, palette_->ptnHovCellColor); if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0) && isSelectedCell(trackVisIdx, pos.colInTrack, orderNum, stepNum)) // Paint selected backPainter.fillRect(offset, rowY, effValWidth_ + widthSpace_, stepFontHeight_, palette_->ptnSelCellColor); if (textChanged_) { int effVal = bt_->getStepEffectValue(curSongNum_, trackNum, orderNum, stepNum, i); if (effVal == -1) { textPainter.setPen(textColor); textPainter.drawText(offset, baseY, "--"); } else { textPainter.setPen(palette_->ptnEffColor); switch (effect_utils::validateEffectId(src, effId)) { case EffectType::VolumeDelay: if (src == SoundSource::FM && config_->getReverseFMVolumeOrder()) effVal = effect_utils::reverseFmVolume(effVal); break; case EffectType::Brightness: if (config_->getReverseFMVolumeOrder()) effVal = effect_utils::reverseFmBrightness(effVal); break; default: break; } textPainter.drawText(offset, baseY, QString("%1").arg(effVal, 2, 16, QChar('0')).toUpper()); } } offset += effValWidth_ + widthSpaceDbl_; ++pos.colInTrack; } if (foreChanged_ && bt_->isMute(trackNum)) // Paint mute mask forePainter.fillRect(x, rowY, offset - x - 1, stepFontHeight_, palette_->ptnMaskColor); return baseTrackWidth_ + effWidth_ * rightEffn_[static_cast(trackVisIdx)]; } void PatternEditorPanel::drawHeaders(int maxWidth) { QPainter painter(&headerPixmap_); painter.setFont(headerFont_); painter.fillRect(0, 0, geometry().width(), headerHeight_, palette_->ptnHeaderRowColor); painter.setPen(palette_->ptnHeaderBorderColor); int bottomLineY = headerHeight_ - 1; painter.drawLine(0, bottomLineY, geometry().width(), bottomLineY); int lspace = stepFontWidth_ / 2; for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) { painter.setPen(palette_->ptnHeaderBorderColor); painter.drawLine(x, 0, x, headerHeight_); int trackNum = visTracks_.at(trackVisIdx); int tw = baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast(trackVisIdx)); if (hovPos_.order == -2 && hovPos_.trackVisIdx == trackVisIdx) painter.fillRect(x, 0, tw, headerHeight_, palette_->ptnHovCellColor); int left = x + lspace; painter.setPen(palette_->ptnHeaderTextColor); const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast(trackNum)]; painter.drawText(left, headerFontAscent_, gui_utils::getTrackName(songStyle_.type, attrib.source, attrib.channelInSource)); painter.fillRect(left, headerHeight_ - 4, hdMuteToggleWidth_, 2, bt_->isMute(trackNum) ? palette_->ptnMuteColor : palette_->ptnUnmuteColor); painter.drawText(left + hdMuteToggleWidth_ + lspace, hdPlusY_, "+"); painter.drawText(left + hdMuteToggleWidth_ + lspace, hdMinusY_, "-"); x += tw; } } void PatternEditorPanel::drawBorders(int maxWidth) { QPainter painter(&backPixmap_); painter.setPen(palette_->ptnBorderColor); painter.drawLine(stepNumWidth_, 0, stepNumWidth_, backPixmap_.height()); size_t trackVisIdx = static_cast(leftTrackVisIdx_); for (int x = stepNumWidth_; trackVisIdx < rightEffn_.size(); ) { x += (baseTrackWidth_ + effWidth_ * rightEffn_.at(trackVisIdx)); if (x > maxWidth) break; painter.drawLine(x, 0, x, backPixmap_.height()); ++trackVisIdx; } } void PatternEditorPanel::drawShadow() { QPainter painter(&completePixmap_); painter.fillRect(0, 0, geometry().width(), geometry().height(), palette_->ptnUnfocusedShadowColor); } // NOTE: end >= -1 int PatternEditorPanel::calculateTracksWidthWithRowNum(int beginIdx, int endIdx) const { int width = stepNumWidth_; for (int i = beginIdx; i <= endIdx; ++i) { width += (baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast(i))); } return width; } int PatternEditorPanel::calculateColNumInRow(int trackVisIdx, int colNumInTrack, bool isExpanded) const { if (isExpanded) { return trackVisIdx * 11 + colNumInTrack; } else { trackVisIdx = std::min(trackVisIdx, static_cast(rightEffn_.size())); return std::accumulate(rightEffn_.begin(), rightEffn_.begin() + trackVisIdx, colNumInTrack, [](int acc, int v) { return acc + 5 + 2 * v; }); } } void PatternEditorPanel::moveCursorToRight(int n) { int oldTrackIdx = curPos_.trackVisIdx; bool oldLeftTrackIdx = leftTrackVisIdx_; curPos_.colInTrack += n; if (n > 0) { while (true) { int lim = 5 + 2 * rightEffn_.at(static_cast(curPos_.trackVisIdx)); if (curPos_.colInTrack < lim) { break; } else { if (curPos_.trackVisIdx == static_cast(visTracks_.size()) - 1) { if (config_->getWarpCursor()) { curPos_.trackVisIdx = 0; } else { curPos_.colInTrack = lim - 1; break; } } else { ++curPos_.trackVisIdx; } curPos_.colInTrack -= lim; } } } else { while (true) { if (curPos_.colInTrack >= 0) { break; } else { if (!curPos_.trackVisIdx) { if (config_->getWarpCursor()) { curPos_.trackVisIdx = static_cast(visTracks_.size()) - 1; } else { curPos_.colInTrack = 0; break; } } else { --curPos_.trackVisIdx; } curPos_.colInTrack += (5 + 2 * rightEffn_.at(static_cast(curPos_.trackVisIdx))); } } } if (oldTrackIdx < curPos_.trackVisIdx) { while (calculateTracksWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx) > geometry().width()) ++leftTrackVisIdx_; } else { if (curPos_.trackVisIdx < leftTrackVisIdx_) leftTrackVisIdx_ = curPos_.trackVisIdx; } updateTracksWidthFromLeftToEnd(); entryCnt_ = 0; if (curPos_.trackVisIdx != oldTrackIdx) bt_->setCurrentTrack(visTracks_.at(curPos_.trackVisIdx)); if (!isIgnoreToSlider_) { if (config_->getMoveCursorByHorizontalScroll()) { emit hScrollBarChangeRequested(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack)); } else if (curPos_.trackVisIdx != oldTrackIdx) { emit hScrollBarChangeRequested(leftTrackVisIdx_); } } if (!isIgnoreToOrder_ && curPos_.trackVisIdx != oldTrackIdx) // Send to order list emit currentTrackChanged(curPos_.trackVisIdx); // Request fore-background repaint if leftmost track is changed else request only background repaint if (leftTrackVisIdx_ != oldLeftTrackIdx) { headerChanged_ = true; foreChanged_ = true; textChanged_ = true; } backChanged_ = true; repaint(); } void PatternEditorPanel::moveViewToRight(int n) { leftTrackVisIdx_ += n; updateTracksWidthFromLeftToEnd(); // Calculate cursor position int trackIdx = curPos_.trackVisIdx + n; int col = std::min(curPos_.colInTrack, 4 + 2 * rightEffn_.at(static_cast(trackIdx))); // Check visible int width = stepNumWidth_; for (int i = leftTrackVisIdx_; i <= trackIdx; ++i) { width += (baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast(i))); if (geometry().width() < width) { trackIdx = i - 1; col = 4 + 2 * rightEffn_.at(static_cast(trackIdx)); break; } } // Move cursor and repaint all headerChanged_ = true; foreChanged_ = true; textChanged_ = true; moveCursorToRight(calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack, trackIdx, col)); } void PatternEditorPanel::moveCursorToDown(int n) { int oldOdr = curPos_.order; int tmp = curPos_.step + n; if (n > 0) { while (true) { int dif = tmp - static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)); if (dif < 0) { curPos_.step = tmp; break; } else { if (config_->getWarpAcrossOrders()) { if (curPos_.order == static_cast(bt_->getOrderSize(curSongNum_)) - 1) { curPos_.order = 0; } else { ++curPos_.order; } } tmp = dif; } } } else { while (true) { if (tmp < 0) { if (config_->getWarpAcrossOrders()) { if (curPos_.order == 0) { curPos_.order = static_cast(bt_->getOrderSize(curSongNum_)) - 1; } else { --curPos_.order; } } tmp += bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order); } else { curPos_.step = tmp; break; } } } if (curPos_.order != oldOdr) bt_->setCurrentOrderNumber(curPos_.order); bt_->setCurrentStepNumber(curPos_.step); entryCnt_ = 0; if (!isIgnoreToSlider_) emit vScrollBarChangeRequested( curPos_.step, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1); if (!isIgnoreToOrder_ && curPos_.order != oldOdr) // Send to order list emit currentOrderChanged( curPos_.order, static_cast(bt_->getOrderSize(curSongNum_)) - 1); backChanged_ = true; textChanged_ = true; foreChanged_ = true; repaint(); } int PatternEditorPanel::calculateColumnDistance(int beginTrackIdx, int beginColumn, int endTrackIdx, int endColumn, bool isExpanded) const { return (calculateColNumInRow(endTrackIdx, endColumn, isExpanded) - calculateColNumInRow(beginTrackIdx, beginColumn, isExpanded)); } int PatternEditorPanel::calculateStepDistance(int beginOrder, int beginStep, int endOrder, int endStep) const { int d = 0; int startOrder, startStep, stopOrder, stopStep; bool flag; if (endOrder >= beginOrder) { startOrder = endOrder; startStep = endStep; stopOrder = beginOrder; stopStep = beginStep; flag = true; } else { startOrder = beginOrder; startStep = beginStep; stopOrder = endOrder; stopStep = endStep; flag = false; } while (true) { if (startOrder == stopOrder) { d += (startStep - stopStep); break; } else { d += startStep; startStep = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, --startOrder)); } } return flag ? d : -d; } PatternPosition PatternEditorPanel::calculatePositionFrom(int order, int step, int by) const { PatternPosition pos{ -1, -1, order, step + by }; if (by > 0) { while (true) { int dif = pos.step - static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, pos.order)); if (dif < 0) { break; } else { if (pos.order == static_cast(bt_->getOrderSize(curSongNum_)) - 1) { return { -1, -1, -1, -1 }; } else { ++pos.order; } pos.step = dif; } } } else { while (true) { if (pos.step < 0) { if (pos.order == 0) { return { -1, -1, -1, -1 }; } else { --pos.order; } pos.step += bt_->getPatternSizeFromOrderNumber(curSongNum_, pos.order); } else { break; } } } return pos; } QPoint PatternEditorPanel::calculateCurrentCursorPosition() const { int w = calculateTracksWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx - 1); if (curPos_.colInTrack > 0) { w = w + toneNameWidth_ + widthSpaceDbl_; if (curPos_.colInTrack > 1) { w = w + instWidth_ + widthSpaceDbl_; if (curPos_.colInTrack > 2) { w = w + volWidth_ + widthSpaceDbl_; for (int i = 3; i < 11; ++i) { if (curPos_.colInTrack == i) break; w = w + widthSpace_ + ((i % 2) ? effIDWidth_ : effValWidth_); } } } } return QPoint(w, curRowY_); } int PatternEditorPanel::getScrollableCountByTrack() const { int width = stepNumWidth_; size_t i = visTracks_.size(); do { --i; width += (baseTrackWidth_ + effWidth_ * rightEffn_.at(i)); if (geometry().width() < width) { return static_cast(i + 1); } } while (i); return 0; } void PatternEditorPanel::changeEditable() { backChanged_ = true; repaint(); } int PatternEditorPanel::getFullColmunSize() const { return calculateColNumInRow(static_cast(visTracks_.size()) - 1, 4 + 2 * rightEffn_.back()); } void PatternEditorPanel::updatePositionByStepUpdate(bool isFirstUpdate, bool forceJump, bool trackChanged) { if (!forceJump && !config_->getFollowMode()) { // Repaint only background backChanged_ = true; repaint(); return; } if (trackChanged) { int trackVisIdx = std::distance(visTracks_.begin(), utils::find(visTracks_, bt_->getCurrentTrackAttribute().number)); int oldTrackVisIdx = std::exchange(curPos_.trackVisIdx, trackVisIdx); curPos_.colInTrack = 0; if (oldTrackVisIdx < curPos_.trackVisIdx) { while (calculateTracksWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx) > geometry().width()) { ++leftTrackVisIdx_; headerChanged_ = true; } } else { if (curPos_.trackVisIdx < leftTrackVisIdx_) { leftTrackVisIdx_ = curPos_.trackVisIdx; headerChanged_ = true; } } updateTracksWidthFromLeftToEnd(); if (!isIgnoreToSlider_) { if (config_->getMoveCursorByHorizontalScroll()) emit hScrollBarChangeRequested(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack)); else emit hScrollBarChangeRequested(leftTrackVisIdx_); } } PatternPosition tmp = curPos_; curPos_.setRows(bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber()); int cmp = curPos_.compareRows(tmp); if (cmp || isFirstUpdate) { emit vScrollBarChangeRequested( curPos_.step, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1); if (isFirstUpdate || (cmp < 0) || (cmp && !config_->getShowPreviousNextOrders())) { stepDownCount_ = 0; // Redraw entire area in first update } else { int d = calculateStepDistance(tmp.order, tmp.step, curPos_.order, curPos_.step); stepDownCount_ = (d < (viewedRowCnt_ >> 1)) ? d : 0; } } else if (!trackChanged) return; // Delayed call, already updated. entryCnt_ = 0; // If stepChanged is false, repaint all pattern foreChanged_ = true; textChanged_ = true; backChanged_ = true; repaint(); } void PatternEditorPanel::changeMarker() { markerPos_.setRows(bt_->getMarkerOrder(), bt_->getMarkerStep()); backChanged_ = true; repaint(); } bool PatternEditorPanel::enterToneData(QKeyEvent* event) { int baseOct = bt_->getCurrentOctave(); auto modifiers = event->modifiers(); if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) { Qt::Key qtKey = static_cast(event->key()); try { JamKey possibleJamKey = getJamKeyFromLayoutMapping(qtKey, config_); setStepKeyOn(jam_utils::makeNote(baseOct, possibleJamKey)); } catch (std::invalid_argument &) {} } return false; } void PatternEditorPanel::setStepKeyOn(const Note& note) { bt_->setStepNote(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, note, config_->getInstrumentMask(), config_->getVolumeMask()); comStack_.lock()->push(new SetKeyOnToStepQtCommand(this)); if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_); } bool PatternEditorPanel::enterInstrumentData(int key) { switch (key) { case Qt::Key_0: setStepInstrument(0x0); return true; case Qt::Key_1: setStepInstrument(0x1); return true; case Qt::Key_2: setStepInstrument(0x2); return true; case Qt::Key_3: setStepInstrument(0x3); return true; case Qt::Key_4: setStepInstrument(0x4); return true; case Qt::Key_5: setStepInstrument(0x5); return true; case Qt::Key_6: setStepInstrument(0x6); return true; case Qt::Key_7: setStepInstrument(0x7); return true; case Qt::Key_8: setStepInstrument(0x8); return true; case Qt::Key_9: setStepInstrument(0x9); return true; case Qt::Key_A: setStepInstrument(0xa); return true; case Qt::Key_B: setStepInstrument(0xb); return true; case Qt::Key_C: setStepInstrument(0xc); return true; case Qt::Key_D: setStepInstrument(0xd); return true; case Qt::Key_E: setStepInstrument(0xe); return true; case Qt::Key_F: setStepInstrument(0xf); return true; default: return false; } } void PatternEditorPanel::setStepInstrument(int num) { int trackNum = visTracks_.at(curPos_.trackVisIdx); bt_->setStepInstrumentDigit(curSongNum_, trackNum, curPos_.order, curPos_.step, num, (entryCnt_ == 1)); comStack_.lock()->push(new SetInstrumentToStepQtCommand(this, curPos_, (entryCnt_ == 1))); emit instrumentEntered( bt_->getStepInstrument(curSongNum_, trackNum, curPos_.order, curPos_.step)); if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount()) moveCursorToDown(editableStepCnt_); } bool PatternEditorPanel::enterVolumeData(int key) { switch (key) { case Qt::Key_0: setStepVolume(0x0); return true; case Qt::Key_1: setStepVolume(0x1); return true; case Qt::Key_2: setStepVolume(0x2); return true; case Qt::Key_3: setStepVolume(0x3); return true; case Qt::Key_4: setStepVolume(0x4); return true; case Qt::Key_5: setStepVolume(0x5); return true; case Qt::Key_6: setStepVolume(0x6); return true; case Qt::Key_7: setStepVolume(0x7); return true; case Qt::Key_8: setStepVolume(0x8); return true; case Qt::Key_9: setStepVolume(0x9); return true; case Qt::Key_A: setStepVolume(0xa); return true; case Qt::Key_B: setStepVolume(0xb); return true; case Qt::Key_C: setStepVolume(0xc); return true; case Qt::Key_D: setStepVolume(0xd); return true; case Qt::Key_E: setStepVolume(0xe); return true; case Qt::Key_F: setStepVolume(0xf); return true; default: return false; } } void PatternEditorPanel::setStepVolume(int volume) { int vol = bt_->setStepVolumeDigit(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, volume, (entryCnt_ == 1)); comStack_.lock()->push(new SetVolumeToStepQtCommand(this, curPos_, (entryCnt_ == 1))); if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount()) moveCursorToDown(editableStepCnt_); emit volumeEntered(vol); } bool PatternEditorPanel::enterEffectID(int key) { switch (key) { case Qt::Key_0: setStepEffectID("0"); return true; case Qt::Key_1: setStepEffectID("1"); return true; case Qt::Key_2: setStepEffectID("2"); return true; case Qt::Key_3: setStepEffectID("3"); return true; case Qt::Key_4: setStepEffectID("4"); return true; case Qt::Key_5: setStepEffectID("5"); return true; case Qt::Key_6: setStepEffectID("6"); return true; case Qt::Key_7: setStepEffectID("7"); return true; case Qt::Key_8: setStepEffectID("8"); return true; case Qt::Key_9: setStepEffectID("9"); return true; case Qt::Key_A: setStepEffectID("A"); return true; case Qt::Key_B: setStepEffectID("B"); return true; case Qt::Key_C: setStepEffectID("C"); return true; case Qt::Key_D: setStepEffectID("D"); return true; case Qt::Key_E: setStepEffectID("E"); return true; case Qt::Key_F: setStepEffectID("F"); return true; case Qt::Key_G: setStepEffectID("G"); return true; case Qt::Key_H: setStepEffectID("H"); return true; case Qt::Key_I: setStepEffectID("I"); return true; case Qt::Key_J: setStepEffectID("J"); return true; case Qt::Key_K: setStepEffectID("K"); return true; case Qt::Key_L: setStepEffectID("L"); return true; case Qt::Key_M: setStepEffectID("M"); return true; case Qt::Key_N: setStepEffectID("N"); return true; case Qt::Key_O: setStepEffectID("O"); return true; case Qt::Key_P: setStepEffectID("P"); return true; case Qt::Key_Q: setStepEffectID("Q"); return true; case Qt::Key_R: setStepEffectID("R"); return true; case Qt::Key_S: setStepEffectID("S"); return true; case Qt::Key_T: setStepEffectID("T"); return true; case Qt::Key_U: setStepEffectID("U"); return true; case Qt::Key_V: setStepEffectID("V"); return true; case Qt::Key_W: setStepEffectID("W"); return true; case Qt::Key_X: setStepEffectID("X"); return true; case Qt::Key_Y: setStepEffectID("Y"); return true; case Qt::Key_Z: setStepEffectID("Z"); return true; default: return false; } } void PatternEditorPanel::setStepEffectID(QString str) { int curTrackNum = visTracks_.at(curPos_.trackVisIdx); bt_->setStepEffectIDCharacter(curSongNum_, curTrackNum, curPos_.order, curPos_.step, (curPos_.colInTrack - 3) / 2, str.toStdString(), config_->getFill00ToEffectValue(), (entryCnt_ == 1)); comStack_.lock()->push(new SetEffectIDToStepQtCommand(this, curPos_, (entryCnt_ == 1))); PatternPosition editPos = curPos_; if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount()) { if (config_->getMoveCursorToRight()) moveCursorToRight(1); else moveCursorToDown(editableStepCnt_); } // Send effect description std::string id = bt_->getStepEffectID(curSongNum_, visTracks_.at(editPos.trackVisIdx), editPos.order, editPos.step, (editPos.colInTrack - 3) / 2); SoundSource src = songStyle_.trackAttribs.at(static_cast(curTrackNum)).source; emit effectEntered(EffectDescription::getEffectFormatAndDetailString(effect_utils::validateEffectId(src, id))); } bool PatternEditorPanel::enterEffectValue(int key) { switch (key) { case Qt::Key_0: setStepEffectValue(0x0); return true; case Qt::Key_1: setStepEffectValue(0x1); return true; case Qt::Key_2: setStepEffectValue(0x2); return true; case Qt::Key_3: setStepEffectValue(0x3); return true; case Qt::Key_4: setStepEffectValue(0x4); return true; case Qt::Key_5: setStepEffectValue(0x5); return true; case Qt::Key_6: setStepEffectValue(0x6); return true; case Qt::Key_7: setStepEffectValue(0x7); return true; case Qt::Key_8: setStepEffectValue(0x8); return true; case Qt::Key_9: setStepEffectValue(0x9); return true; case Qt::Key_A: setStepEffectValue(0xa); return true; case Qt::Key_B: setStepEffectValue(0xb); return true; case Qt::Key_C: setStepEffectValue(0xc); return true; case Qt::Key_D: setStepEffectValue(0xd); return true; case Qt::Key_E: setStepEffectValue(0xe); return true; case Qt::Key_F: setStepEffectValue(0xf); return true; default: return false; } } void PatternEditorPanel::setStepEffectValue(int value) { int trackNum = visTracks_.at(curPos_.trackVisIdx); int n = (curPos_.colInTrack - 4) / 2; EffectDisplayControl ctrl = EffectDisplayControl::Unset; SoundSource src = songStyle_.trackAttribs[static_cast(trackNum)].source; switch (effect_utils::validateEffectId( src, bt_->getStepEffectID(curSongNum_, trackNum, curPos_.order, curPos_.step, n))) { case EffectType::VolumeDelay: if (src == SoundSource::FM && config_->getReverseFMVolumeOrder()) ctrl = EffectDisplayControl::ReverseFMVolumeDelay; break; case EffectType::Brightness: if (config_->getReverseFMVolumeOrder()) ctrl = EffectDisplayControl::ReverseFMBrightness; break; default: break; } bt_->setStepEffectValueDigit(curSongNum_, trackNum, curPos_.order, curPos_.step, n, value, ctrl, (entryCnt_ == 1)); comStack_.lock()->push(new SetEffectValueToStepQtCommand(this, curPos_, (entryCnt_ == 1))); if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount()) { if (config_->getMoveCursorToRight()) moveCursorToRight(1); else moveCursorToDown(editableStepCnt_); } } void PatternEditorPanel::insertStep() { bt_->insertStep(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step); comStack_.lock()->push(new InsertStepQtCommand(this)); } void PatternEditorPanel::deletePreviousStep() { if (curPos_.step) { bt_->deletePreviousStep(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step); comStack_.lock()->push(new DeletePreviousStepQtCommand(this)); if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(-1); } } void PatternEditorPanel::copySelectedCells() { if (selLeftAbovePos_.order == -1) return; int r = visTracks_.at(selRightBelowPos_.trackVisIdx) * 11 + selRightBelowPos_.colInTrack; int l = visTracks_.at(selLeftAbovePos_.trackVisIdx) * 11 + selLeftAbovePos_.colInTrack; int w = 1 + r - l; // Real selected region width int h = 1 + calculateStepDistance(selLeftAbovePos_.order, selLeftAbovePos_.step, selRightBelowPos_.order, selRightBelowPos_.step); QString str = QString("PATTERN_COPY:%1,%2,%3,") .arg(QString::number(selLeftAbovePos_.colInTrack), QString::number(w), QString::number(h)); // NOTE: After this line, PatternPosition.trackVisIdx indecates a real track number. PatternPosition pos = { selLeftAbovePos_.trackVisIdx, selLeftAbovePos_.colInTrack, selLeftAbovePos_.order, selLeftAbovePos_.step }; while (true) { int trackIdx = visTracks_.at(pos.trackVisIdx); switch (pos.colInTrack) { case 0: str += QString::number(bt_->getStepNoteNumber(curSongNum_, trackIdx, pos.order, pos.step)); break; case 1: str += QString::number(bt_->getStepInstrument(curSongNum_, trackIdx, pos.order, pos.step)); break; case 2: str += QString::number(bt_->getStepVolume(curSongNum_, trackIdx, pos.order, pos.step)); break; case 3: str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 0)); break; case 4: str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 0)); break; case 5: str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 1)); break; case 6: str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 1)); break; case 7: str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 2)); break; case 8: str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 2)); break; case 9: str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 3)); break; case 10: str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 3)); break; } if (pos.isEqualCols(selRightBelowPos_)) { if (pos.isEqualRows(selRightBelowPos_)) { break; } else { pos.setCols(selLeftAbovePos_.trackVisIdx, selLeftAbovePos_.colInTrack); ++pos.step; } } else { ++pos.colInTrack; pos.trackVisIdx += (pos.colInTrack / 11); pos.colInTrack %= 11; } str += ","; } QApplication::clipboard()->setText(str); } void PatternEditorPanel::eraseSelectedCells() { bt_->erasePatternCells(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack, selLeftAbovePos_.order, selLeftAbovePos_.step, visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step); comStack_.lock()->push(new EraseCellsInPatternQtCommand(this)); } void PatternEditorPanel::pasteCopiedCells(const PatternPosition& cursorPos) { int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } bt_->pastePatternCells( curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells)); comStack_.lock()->push(new PasteCopiedDataToPatternQtCommand(this)); } void PatternEditorPanel::pasteMixCopiedCells(const PatternPosition& cursorPos) { int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } bt_->pasteMixPatternCells( curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells)); comStack_.lock()->push(new PasteMixCopiedDataToPatternQtCommand(this)); } void PatternEditorPanel::pasteOverwriteCopiedCells(const PatternPosition& cursorPos) { int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } bt_->pasteOverwritePatternCells( curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells)); comStack_.lock()->push(new PasteOverwriteCopiedDataToPatternQtCommand(this)); } void PatternEditorPanel::pasteInsertCopiedCells(const PatternPosition& cursorPos) { int sCol = 0; PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol); PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size()); if (config_->getPasteMode() == Configuration::PasteMode::Fill && selLeftAbovePos_.order != -1) { cells = compandPasteCells(pos, cells); } bt_->pasteInsertPatternCells( curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells)); comStack_.lock()->push(new PasteInsertCopiedDataToPatternQtCommand(this)); } PatternEditorPanel::PatternCells PatternEditorPanel::decodeCells(QString str, int& startCol) { str.remove(QRegularExpression("PATTERN_(COPY|CUT):")); QStringList data = str.split(","); if (data.size() < 3) {}; // Error startCol = data[0].toInt(); size_t w = data[1].toUInt(); size_t h = data[2].toUInt(); data.erase(data.begin(), data.begin() + 3); if (static_cast(w * h) != data.size()) return {}; // Error PatternCells cells(h, PatternCells::value_type(w)); for (size_t i = 0; i < h; ++i) { for (size_t j = 0; j < w; ++j) { cells[i][j] = data[i * w + j].toStdString(); } } return cells; } PatternPosition PatternEditorPanel::getPasteLeftAbovePosition( int pasteCol, const PatternPosition& cursorPos, size_t cellW) const { PatternPosition pos; Configuration::PasteMode mode = config_->getPasteMode(); if ((mode == Configuration::PasteMode::Selection || mode == Configuration::PasteMode::Fill) && selLeftAbovePos_.colInTrack != -1) { pos = selLeftAbovePos_; pos.colInTrack = pasteCol; } else { pos = cursorPos; if (pasteCol < 3 || (pos.colInTrack - pasteCol) % 2 || (11 - pos.colInTrack) < static_cast(cellW)) { pos.colInTrack = pasteCol; } } return pos; } void PatternEditorPanel::cutSelectedCells() { if (selLeftAbovePos_.order == -1) return; copySelectedCells(); eraseSelectedCells(); QString str = QApplication::clipboard()->text(); str.replace("COPY", "CUT"); QApplication::clipboard()->setText(str); } PatternEditorPanel::PatternCells PatternEditorPanel::compandPasteCells(const PatternPosition& laPos, const PatternCells& cells) { PatternCells newCells; int ow = cells.front().size(); int oh = cells.size(); int l = laPos.trackVisIdx * 11 + laPos.colInTrack; int r = selRightBelowPos_.trackVisIdx * 11 + selRightBelowPos_.colInTrack; int w = r - l + 1; // Real selected region width size_t h = static_cast(calculateStepDistance(laPos.order, laPos.step, selRightBelowPos_.order, selRightBelowPos_.step) + 1); int bw = ((ow - 1) / 11 + 1) * 11; size_t padSize = bw - ow; std::vector pad(padSize); int lc = (laPos.colInTrack + ow) % 11; for (size_t i = 0; i < padSize; ++i) { switch (lc) { case 3: case 5: case 7: case 9: pad[i] = "--"; break; default: pad[i] = "-1"; break; } lc = (lc + 1) % 11; } for (size_t i = 0; i < h; ++i) { newCells.emplace_back(w); auto&& rowBeginIt = newCells.back().begin(); for (int dw = w, p = 0; dw > 0; dw -= bw, p += bw) { int ws = std::min(ow, dw); std::copy_n(cells.at(i % oh).begin(), ws, rowBeginIt + p); std::copy_n(pad.begin(), std::min(dw - ws, static_cast(padSize)), rowBeginIt + p + ws); } } return newCells; } void PatternEditorPanel::transposeNote(const PatternPosition& startPos, const PatternPosition& endPos, int seminote) { int beginTrackIdx = (startPos.colInTrack == 0) ? startPos.trackVisIdx : startPos.trackVisIdx + 1; if (beginTrackIdx <= endPos.trackVisIdx) { bt_->transposeNoteInPattern(curSongNum_, visTracks_.at(beginTrackIdx), startPos.order, startPos.step, visTracks_.at(endPos.trackVisIdx), endPos.step, seminote); comStack_.lock()->push(new TransposeNoteInPatternQtCommand(this)); } } void PatternEditorPanel::changeValuesInPattern(const PatternPosition& startPos, const PatternPosition& endPos, int value) { if (startPos.compareCols(endPos) <= 0) { bt_->changeValuesInPattern(curSongNum_, visTracks_.at(startPos.trackVisIdx), startPos.colInTrack, startPos.order, startPos.step, visTracks_.at(endPos.trackVisIdx), endPos.colInTrack, endPos.step, value); comStack_.lock()->push(new ChangeValuesInPatternQtCommand(this)); } } void PatternEditorPanel::toggleTrack(int trackIdx) { int trackNum = visTracks_.at(trackIdx); bt_->setTrackMuteState(trackNum, !bt_->isMute(trackNum)); isMuteElse_ = false; redrawByMaskChanged(); } void PatternEditorPanel::soloTrack(int trackIdx) { int soloTrackNum = visTracks_.at(trackIdx); int trackCnt = static_cast(songStyle_.trackAttribs.size()); if (isMuteElse_) { if (bt_->isMute(soloTrackNum)) { for (int t = 0; t < trackCnt; ++t) bt_->setTrackMuteState(t, t != soloTrackNum); } else { isMuteElse_ = false; for (int t = 0; t < trackCnt; ++t) bt_->setTrackMuteState(t, false); } } else { isMuteElse_ = true; for (int t = 0; t < trackCnt; ++t) bt_->setTrackMuteState(t, (t == soloTrackNum) ? false : isMuteElse_); } redrawByMaskChanged(); } void PatternEditorPanel::setSelectedRectangle(const PatternPosition& start, const PatternPosition& end) { int patMax = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, end.order)) - 1; if (start.compareCols(end) > 0) { if (start.step > end.step) { selLeftAbovePos_ = end; selRightBelowPos_ = { start.trackVisIdx, start.colInTrack, end.order, (start.step > patMax) ? patMax : start.step }; } else { selLeftAbovePos_ = { end.trackVisIdx, end.colInTrack, end.order, start.step }; selRightBelowPos_ = { start.trackVisIdx, start.colInTrack, end.order, end.step }; } } else { if (start.step > end.step) { selLeftAbovePos_ = { start.trackVisIdx, start.colInTrack, end.order, end.step }; selRightBelowPos_ = { end.trackVisIdx, end.colInTrack, end.order, (start.step > patMax) ? patMax : start.step }; } else { selLeftAbovePos_ = { start.trackVisIdx, start.colInTrack, end.order, start.step }; selRightBelowPos_ = end; } } emit selected(true); backChanged_ = true; repaint(); } bool PatternEditorPanel::isSelectedCell(int trackVisIdx, int colNum, int orderNum, int stepNum) { PatternPosition pos{ trackVisIdx, colNum, orderNum, stepNum }; return (selLeftAbovePos_.compareCols(pos) <= 0 && selRightBelowPos_.compareCols(pos) >= 0 && selLeftAbovePos_.compareRows(pos) <= 0 && selRightBelowPos_.compareRows(pos) >= 0); } void PatternEditorPanel::showPatternContextMenu(const PatternPosition& pos, const QPoint& point) { QMenu menu; // Leave Before Qt5.7.0 style due to windows xp QAction* undo = menu.addAction(tr("&Undo")); undo->setIcon(QIcon(":/icon/undo")); QObject::connect(undo, &QAction::triggered, this, [&]() { bt_->undo(); comStack_.lock()->undo(); }); QAction* redo = menu.addAction(tr("&Redo")); redo->setIcon(QIcon(":/icon/redo")); QObject::connect(redo, &QAction::triggered, this, [&]() { bt_->redo(); comStack_.lock()->redo(); }); menu.addSeparator(); QAction* copy = menu.addAction(tr("&Copy")); copy->setIcon(QIcon(":/icon/copy")); QObject::connect(copy, &QAction::triggered, this, &PatternEditorPanel::copySelectedCells); QAction* cut = menu.addAction(tr("Cu&t")); cut->setIcon(QIcon(":/icon/cut")); QObject::connect(cut, &QAction::triggered, this, &PatternEditorPanel::cutSelectedCells); QAction* paste = menu.addAction(tr("&Paste")); paste->setIcon(QIcon(":/icon/paste")); QObject::connect(paste, &QAction::triggered, this, [&]() { pasteCopiedCells(pos); }); auto pasteSp = new QMenu(tr("Paste Specia&l")); menu.addMenu(pasteSp); QAction* pasteMix = pasteSp->addAction(tr("&Mix")); QObject::connect(pasteMix, &QAction::triggered, this, [&]() { pasteMixCopiedCells(pos); }); QAction* pasteOver = pasteSp->addAction(tr("&Overwrite")); QObject::connect(pasteOver, &QAction::triggered, this, [&]() { pasteOverwriteCopiedCells(pos); }); QAction* pasteIns = pasteSp->addAction(tr("&Insert")); QObject::connect(pasteIns, &QAction::triggered, this, [&]() { pasteInsertCopiedCells(pos); }); QAction* erase = menu.addAction(tr("&Erase")); QObject::connect(erase, &QAction::triggered, this, &PatternEditorPanel::eraseSelectedCells); QAction* select = menu.addAction(tr("Select &All")); QObject::connect(select, &QAction::triggered, this, [&]() { onSelectPressed(1); }); menu.addSeparator(); auto pattern = new QMenu(tr("Patter&n")); menu.addMenu(pattern); QAction* interpolate = pattern->addAction(tr("&Interpolate")); QObject::connect(interpolate, &QAction::triggered, this, &PatternEditorPanel::onInterpolatePressed); QAction* reverse = pattern->addAction(tr("&Reverse")); QObject::connect(reverse, &QAction::triggered, this, &PatternEditorPanel::onReversePressed); QAction* replace = pattern->addAction(tr("R&eplace Instrument")); QObject::connect(replace, &QAction::triggered, this, &PatternEditorPanel::onReplaceInstrumentPressed); pattern->addSeparator(); QAction* expand = pattern->addAction(tr("E&xpand")); QObject::connect(expand, &QAction::triggered, this, &PatternEditorPanel::onExpandPressed); QAction* shrink = pattern->addAction(tr("S&hrink")); QObject::connect(shrink, &QAction::triggered, this, &PatternEditorPanel::onShrinkPressed); pattern->addSeparator(); auto transpose = new QMenu(tr("&Transpose")); pattern->addMenu(transpose); QAction* deNote = transpose->addAction(tr("&Decrease Note")); QObject::connect(deNote, &QAction::triggered, this, [&]() { onNoteTransposePressed(-1); }); QAction* inNote = transpose->addAction(tr("&Increase Note")); QObject::connect(inNote, &QAction::triggered, this, [&]() { onNoteTransposePressed(1); }); QAction* deOct = transpose->addAction(tr("D&ecrease Octave")); QObject::connect(deOct, &QAction::triggered, this, [&]() { onNoteTransposePressed(-12); }); QAction* inOct = transpose->addAction(tr("I&ncrease Octave")); QObject::connect(inOct, &QAction::triggered, this, [&]() { onNoteTransposePressed(12); }); auto changeVals = new QMenu(tr("&Change Values")); pattern->addMenu(changeVals); QAction* fdeVal = changeVals->addAction(tr("Fine &Decrease Values")); QObject::connect(fdeVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(-1); }); QAction* finVal = changeVals->addAction(tr("Fine &Increase Values")); QObject::connect(finVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(1); }); QAction* cdeVal = changeVals->addAction(tr("Coarse D&ecrease Values")); QObject::connect(cdeVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(-16); }); QAction* cinVal = changeVals->addAction(tr("Coarse I&ncrease Values")); QObject::connect(cinVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(16); }); menu.addSeparator(); QAction* toggle = menu.addAction(tr("To&ggle Track")); QObject::connect(toggle, &QAction::triggered, this, [&] { toggleTrack(pos.trackVisIdx); }); QAction* solo = menu.addAction(tr("&Solo Track")); QObject::connect(solo, &QAction::triggered, this, [&] { soloTrack(pos.trackVisIdx); }); menu.addSeparator(); QAction* exeff = menu.addAction(tr("Expand E&ffect Column")); QObject::connect(exeff, &QAction::triggered, this, [&] { onExpandEffectColumnPressed(pos.trackVisIdx); }); QAction* sheff = menu.addAction(tr("Shrin&k Effect Column")); QObject::connect(sheff, &QAction::triggered, this, [&] { onShrinkEffectColumnPressed(pos.trackVisIdx); }); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) undo->setShortcutVisibleInContextMenu(true); redo->setShortcutVisibleInContextMenu(true); copy->setShortcutVisibleInContextMenu(true); cut->setShortcutVisibleInContextMenu(true); paste->setShortcutVisibleInContextMenu(true); pasteMix->setShortcutVisibleInContextMenu(true); erase->setShortcutVisibleInContextMenu(true); select->setShortcutVisibleInContextMenu(true); interpolate->setShortcutVisibleInContextMenu(true); reverse->setShortcutVisibleInContextMenu(true); replace->setShortcutVisibleInContextMenu(true); deNote->setShortcutVisibleInContextMenu(true); inNote->setShortcutVisibleInContextMenu(true); deOct->setShortcutVisibleInContextMenu(true); inOct->setShortcutVisibleInContextMenu(true); fdeVal->setShortcutVisibleInContextMenu(true); finVal->setShortcutVisibleInContextMenu(true); cdeVal->setShortcutVisibleInContextMenu(true); cinVal->setShortcutVisibleInContextMenu(true); toggle->setShortcutVisibleInContextMenu(true); solo->setShortcutVisibleInContextMenu(true); exeff->setShortcutVisibleInContextMenu(true); sheff->setShortcutVisibleInContextMenu(true); #endif auto shortcuts = config_->getShortcuts(); undo->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z)); redo->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y)); undo->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z)); copy->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); cut->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X)); paste->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V)); pasteMix->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteMix))); pasteOver->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteOverwrite))); pasteIns->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PasteInsert))); erase->setShortcut(QKeySequence(Qt::Key_Delete)); select->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SelectAll))); interpolate->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Interpolate))); reverse->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::Reverse))); replace->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ReplaceInstrument))); expand->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandPattern))); shrink->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkPattern))); deNote->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseNote))); inNote->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseNote))); deOct->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::DecreaseOctave))); inOct->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::IncreaseOctave))); fdeVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineDecreaseValues))); finVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::FineIncreaseValues))); cdeVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseDecreaseValues))); cinVal->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::CoarseIncreaseValuse))); toggle->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleTrack))); solo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SoloTrack))); exeff->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandEffect))); sheff->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkEffect))); if (bt_->isJamMode() || pos.order < 0 || pos.trackVisIdx < 0) { copy->setEnabled(false); cut->setEnabled(false); paste->setEnabled(false); pasteMix->setEnabled(false); pasteOver->setEnabled(false); pasteIns->setEnabled(false); erase->setEnabled(false); interpolate->setEnabled(false); reverse->setEnabled(false); replace->setEnabled(false); expand->setEnabled(false); shrink->setEnabled(false); deNote->setEnabled(false); inNote->setEnabled(false); deOct->setEnabled(false); inOct->setEnabled(false); fdeVal->setEnabled(false); finVal->setEnabled(false); cdeVal->setEnabled(false); cinVal->setEnabled(false); } else { QString clipText = QApplication::clipboard()->text(); if (!clipText.startsWith("PATTERN_COPY") && !clipText.startsWith("PATTERN_CUT")) { paste->setEnabled(false); pasteMix->setEnabled(false); pasteOver->setEnabled(false); pasteIns->setEnabled(false); } if (selRightBelowPos_.order < 0 || !isSelectedCell(pos.trackVisIdx, pos.colInTrack, pos.order, pos.step)) { copy->setEnabled(false); cut->setEnabled(false); erase->setEnabled(false); interpolate->setEnabled(false); reverse->setEnabled(false); replace->setEnabled(false); expand->setEnabled(false); shrink->setEnabled(false); deNote->setEnabled(false); inNote->setEnabled(false); deOct->setEnabled(false); inOct->setEnabled(false); fdeVal->setEnabled(false); finVal->setEnabled(false); cdeVal->setEnabled(false); cinVal->setEnabled(false); } } if (!comStack_.lock()->canUndo()) { undo->setEnabled(false); } if (!comStack_.lock()->canRedo()) { redo->setEnabled(false); } if (pos.trackVisIdx < 0) { toggle->setEnabled(false); solo->setEnabled(false); exeff->setEnabled(false); sheff->setEnabled(false); } menu.exec(mapToGlobal(point)); } /********** Slots **********/ void PatternEditorPanel::onHScrollBarChanged(int num) { Ui::EventGuard eg(isIgnoreToSlider_); // Skip if position has already changed in panel if (config_->getMoveCursorByHorizontalScroll()) { if (int dif = num - calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack)) moveCursorToRight(dif); } else { if (int dif = num - leftTrackVisIdx_) moveViewToRight(dif); } } void PatternEditorPanel::onVScrollBarChanged(int num) { Ui::EventGuard eg(isIgnoreToSlider_); // Skip if position has already changed in panel if (int dif = num - curPos_.step) moveCursorToDown(dif); } void PatternEditorPanel::onOrderListCurrentTrackChanged(int idx) { Ui::EventGuard eg(isIgnoreToOrder_); int dif = calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack, idx, 0); moveCursorToRight(dif); } void PatternEditorPanel::onOrderListCurrentOrderChanged(int num) { Ui::EventGuard eg(isIgnoreToOrder_); int step = std::min(curPos_.step, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, num)) - 1); int dif = calculateStepDistance(curPos_.order, curPos_.step, num, step); moveCursorToDown(dif); } void PatternEditorPanel::onOrderListEdited() { // Reset position memory hovPos_ = { -1, -1, -1, -1 }; mousePressPos_ = { -1, -1, -1, -1 }; mouseReleasePos_ = { -1, -1, -1, -1 }; selLeftAbovePos_ = { -1, -1, -1, -1 }; selRightBelowPos_ = { -1, -1, -1, -1 }; shiftPressedPos_ = { -1, -1, -1, -1 }; selectAllState_ = -1; emit selected(false); redrawByPatternChanged(true); } void PatternEditorPanel::onDefaultPatternSizeChanged() { // Check pattern size int end = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)); if (curPos_.step >= end) curPos_.step = end - 1; redrawByPatternChanged(true); } void PatternEditorPanel::onShortcutUpdated() { auto shortcuts = config_->getShortcuts(); hlUpSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::PrevHighlighted))); hlUpWSSc_.setKey(gui_utils::strToKeySeq("Shift+" + shortcuts.at(Configuration::ShortcutAction::PrevHighlighted))); hlDnSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::NextHighlighted))); hlDnWSSc_.setKey(gui_utils::strToKeySeq("Shift+" + shortcuts.at(Configuration::ShortcutAction::NextHighlighted))); keyOffSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::KeyOff))); echoBufSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::EchoBuffer))); expandColSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ExpandEffect))); shrinkColSc_.setKey(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ShrinkEffect))); } void PatternEditorPanel::setPatternHighlight1Count(int count) { hl1Cnt_ = count; backChanged_ = true; textChanged_ = true; repaint(); } void PatternEditorPanel::setPatternHighlight2Count(int count) { hl2Cnt_ = count; backChanged_ = true; textChanged_ = true; repaint(); } void PatternEditorPanel::setEditableStep(int n) { editableStepCnt_ = n; } void PatternEditorPanel::onSongLoaded() { // NOTE: Temporary fix for https://github.com/rerrahkr/BambooTracker/issues/276 isInitedFirstMod_.store(true); // Initialize cursor position curSongNum_ = bt_->getCurrentSongNumber(); SongType prevType = songStyle_.type; songStyle_ = bt_->getSongStyle(curSongNum_); visTracks_ = gui_utils::adaptVisibleTrackList(visTracks_, prevType, songStyle_.type); rightEffn_.resize(visTracks_.size()); std::transform(visTracks_.begin(), visTracks_.end(), rightEffn_.begin(), [&](int t) { return static_cast(bt_->getEffectDisplayWidth(curSongNum_, t)); }); curPos_ = { visTracks_.front(), 0, bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber() }; // Set cursor to the most lest-placed visible track if (visTracks_.front() != bt_->getCurrentTrackAttribute().number) { bt_->setCurrentTrack(visTracks_.front()); } leftTrackVisIdx_ = 0; updateTracksWidthFromLeftToEnd(); hovPos_ = { -1, -1, -1, -1 }; mousePressPos_ = { -1, -1, -1, -1 }; mouseReleasePos_ = { -1, -1, -1, -1 }; selLeftAbovePos_ = { -1, -1, -1, -1 }; selRightBelowPos_ = { -1, -1, -1, -1 }; shiftPressedPos_ = { -1, -1, -1, -1 }; markerPos_ = { -1, -1, -1, -1 }; entryCnt_ = 0; selectAllState_ = -1; emit selected(false); redrawAll(); } void PatternEditorPanel::onDeletePressed() { if (bt_->isJamMode()) return; if (selLeftAbovePos_.order != -1) { // Delete region eraseSelectedCells(); } else { switch (curPos_.colInTrack) { case 0: bt_->eraseStepNote(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step); comStack_.lock()->push(new EraseStepQtCommand(this)); break; case 1: bt_->eraseStepInstrument(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step); comStack_.lock()->push(new EraseInstrumentInStepQtCommand(this)); break; case 2: bt_->eraseStepVolume(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step); comStack_.lock()->push(new EraseVolumeInStepQtCommand(this)); break; case 3: case 5: case 7: case 9: bt_->eraseStepEffect(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, (curPos_.colInTrack - 3) / 2); comStack_.lock()->push(new EraseEffectInStepQtCommand(this)); break; case 4: case 6: case 8: case 10: bt_->eraseStepEffectValue(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, (curPos_.colInTrack - 4) / 2); comStack_.lock()->push(new EraseEffectValueInStepQtCommand(this)); break; } if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_); } } void PatternEditorPanel::onPastePressed() { if (!bt_->isJamMode()) pasteCopiedCells(curPos_); } void PatternEditorPanel::onPasteMixPressed() { if (!bt_->isJamMode()) pasteMixCopiedCells(curPos_); } void PatternEditorPanel::onPasteOverwritePressed() { if (!bt_->isJamMode()) pasteOverwriteCopiedCells(curPos_); } void PatternEditorPanel::onPasteInsertPressed() { if (!bt_->isJamMode()) pasteInsertCopiedCells(curPos_); } void PatternEditorPanel::onSelectPressed(int type) { switch (type) { case 0: // None { selLeftAbovePos_ = { -1, -1, -1, -1 }; selRightBelowPos_ = { -1, -1, -1, -1 }; selectAllState_ = -1; emit selected(false); backChanged_ = true; repaint(); break; } case 1: // All { selectAllState_ = (selectAllState_ + 1) % 2; int max = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1; PatternPosition start, end; if (selectAllState_) { start = { 0, 0, curPos_.order, 0 }; end = { static_cast(visTracks_.size() - 1), 10, curPos_.order, max }; } else { start = { curPos_.trackVisIdx, 0, curPos_.order, 0 }; end = { curPos_.trackVisIdx, 10, curPos_.order, max }; } setSelectedRectangle(start, end); break; } case 2: // Row { selectAllState_ = -1; PatternPosition start = { 0, 0, curPos_.order, curPos_.step }; PatternPosition end = { static_cast(visTracks_.size() - 1), 10, curPos_.order, curPos_.step }; setSelectedRectangle(start, end); break; } case 3: // Column { selectAllState_ = -1; PatternPosition start = { curPos_.trackVisIdx, curPos_.colInTrack, curPos_.order, 0 }; PatternPosition end = { curPos_.trackVisIdx, curPos_.colInTrack, curPos_.order, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order) - 1) }; setSelectedRectangle(start, end); break; } case 4: // Pattern { selectAllState_ = -1; PatternPosition start = { curPos_.trackVisIdx, 0, curPos_.order, 0 }; PatternPosition end = { curPos_.trackVisIdx, 10, curPos_.order, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order) - 1) }; setSelectedRectangle(start, end); break; } case 5: // Order { selectAllState_ = -1; PatternPosition start = { 0, 0, curPos_.order, 0 }; PatternPosition end = { static_cast(visTracks_.size() - 1), 10, curPos_.order, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order) - 1) }; setSelectedRectangle(start, end); break; } } } void PatternEditorPanel::onNoteTransposePressed(int seminote) { if (bt_->isJamMode()) return; if (selLeftAbovePos_.order != -1) transposeNote(selLeftAbovePos_, selRightBelowPos_, seminote); else transposeNote(curPos_, curPos_, seminote); } void PatternEditorPanel::onToggleTrackPressed() { toggleTrack(curPos_.trackVisIdx); } void PatternEditorPanel::onSoloTrackPressed() { soloTrack(curPos_.trackVisIdx); } void PatternEditorPanel::onUnmuteAllPressed() { int trackCnt = static_cast(songStyle_.trackAttribs.size()); for (int t = 0; t < trackCnt; ++t) bt_->setTrackMuteState(t, false); isMuteElse_ = false; redrawByMaskChanged(); } void PatternEditorPanel::onExpandPressed() { if (selLeftAbovePos_.order == -1) return; bt_->expandPattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack, selLeftAbovePos_.order, selLeftAbovePos_.step, visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step); comStack_.lock()->push(new ExpandPatternQtCommand(this)); } void PatternEditorPanel::onShrinkPressed() { if (selLeftAbovePos_.order == -1) return; bt_->shrinkPattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack, selLeftAbovePos_.order, selLeftAbovePos_.step, visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step); comStack_.lock()->push(new ShrinkPatternQtCommand(this)); } void PatternEditorPanel::onInterpolatePressed() { if (selLeftAbovePos_.order == -1) return; bt_->interpolatePattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack, selLeftAbovePos_.order, selLeftAbovePos_.step, visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step); comStack_.lock()->push(new InterpolatePatternQtCommand(this)); } void PatternEditorPanel::onReversePressed() { if (selLeftAbovePos_.order == -1) return; bt_->reversePattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack, selLeftAbovePos_.order, selLeftAbovePos_.step, visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step); comStack_.lock()->push(new ReversePatternQtCommand(this)); } void PatternEditorPanel::onReplaceInstrumentPressed() { if (selLeftAbovePos_.order == -1) return; int curInst = bt_->getCurrentInstrumentNumber(); if (curInst == -1) return; int beginTrackIdx = (selLeftAbovePos_.colInTrack < 2) ? selLeftAbovePos_.trackVisIdx : (selLeftAbovePos_.trackVisIdx + 1); int endTrackIdx = (selRightBelowPos_.colInTrack == 0) ? (selRightBelowPos_.trackVisIdx - 1) : selRightBelowPos_.trackVisIdx; if (beginTrackIdx <= endTrackIdx) { bt_->replaceInstrumentInPattern(curSongNum_, visTracks_.at(beginTrackIdx), selLeftAbovePos_.order, selLeftAbovePos_.step, visTracks_.at(endTrackIdx), selRightBelowPos_.step, curInst); comStack_.lock()->push(new ReplaceInstrumentInPatternQtCommand(this)); } } void PatternEditorPanel::onExpandEffectColumnPressed(int trackVisIdx) { size_t ti = static_cast(trackVisIdx); if (rightEffn_.at(ti) == 3) return; bt_->setEffectDisplayWidth(curSongNum_, visTracks_.at(trackVisIdx), static_cast(++rightEffn_[ti])); updateTracksWidthFromLeftToEnd(); if (config_->getMoveCursorByHorizontalScroll()) { emit effectColsCompanded(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack), getFullColmunSize()); } else { emit effectColsCompanded(leftTrackVisIdx_, getScrollableCountByTrack()); } redrawAll(); } void PatternEditorPanel::onShrinkEffectColumnPressed(int trackVisIdx) { size_t ti = static_cast(trackVisIdx); if (rightEffn_.at(ti) == 0) return; bt_->setEffectDisplayWidth(curSongNum_, visTracks_.at(trackVisIdx), static_cast(--rightEffn_[ti])); updateTracksWidthFromLeftToEnd(); if (config_->getMoveCursorByHorizontalScroll()) { emit effectColsCompanded(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack), getFullColmunSize()); } else { emit effectColsCompanded(leftTrackVisIdx_, getScrollableCountByTrack()); } redrawAll(); } void PatternEditorPanel::onFollowModeChanged() { curPos_.setRows(bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber()); emit vScrollBarChangeRequested( curPos_.step, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1); // Force redraw all area followModeChanged_ = true; redrawPatterns(); } void PatternEditorPanel::onChangeValuesPressed(int value) { if (bt_->isJamMode()) return; if (selLeftAbovePos_.order != -1) changeValuesInPattern(selLeftAbovePos_, selRightBelowPos_, value); else changeValuesInPattern(curPos_, curPos_, value); } void PatternEditorPanel::onPlayStepPressed() { moveCursorToDown(1); } /********** Events **********/ bool PatternEditorPanel::event(QEvent *event) { switch (event->type()) { case QEvent::KeyPress: return keyPressed(dynamic_cast(event)); case QEvent::KeyRelease: return keyReleased(dynamic_cast(event)); case QEvent::HoverMove: return mouseHoverd(dynamic_cast(event)); default: return QWidget::event(event); } } bool PatternEditorPanel::keyPressed(QKeyEvent *event) { /* General Keys */ switch (event->key()) { case Qt::Key_Shift: shiftPressedPos_ = curPos_; return true; case Qt::Key_Tab: if (curPos_.trackVisIdx == static_cast(visTracks_.size()) - 1) { if (config_->getWarpCursor()) moveCursorToRight(-calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack)); } else { moveCursorToRight(5 + 2 * rightEffn_[static_cast(curPos_.trackVisIdx)] - curPos_.colInTrack); } return true; case Qt::Key_Backtab: if (curPos_.trackVisIdx == 0) { if (config_->getWarpCursor()) moveCursorToRight(getFullColmunSize() - 1); } else { moveCursorToRight(-5 - 2 * rightEffn_[static_cast(curPos_.trackVisIdx) - 1] - curPos_.colInTrack); } return true; case Qt::Key_Insert: if (bt_->isJamMode()) { return false; } else { insertStep(); return true; } case Qt::Key_Backspace: if (bt_->isJamMode()) { return false; } else { deletePreviousStep(); return true; } case Qt::Key_Menu: { QPoint point = calculateCurrentCursorPosition(); point.setX(point.x() + 24); point.setY(point.y() - 16); showPatternContextMenu(curPos_, point); return true; } default: if (!bt_->isJamMode()) { // Pattern edit if (!config_->getKeyRepetition() && event->isAutoRepeat()) return false; switch (curPos_.colInTrack) { case 0: return enterToneData(event); case 1: { auto modifiers = event->modifiers(); if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) return enterInstrumentData(event->key()); break; } case 2: { auto modifiers = event->modifiers(); if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) return enterVolumeData(event->key()); break; } case 3: case 5: case 7: case 9: { auto modifiers = event->modifiers(); if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) return enterEffectID(event->key()); break; } case 4: case 6: case 8: case 10: { auto modifiers = event->modifiers(); if (modifiers.testFlag(Qt::NoModifier) || modifiers.testFlag(Qt::KeypadModifier)) return enterEffectValue(event->key()); break; } } } return false; } } bool PatternEditorPanel::keyReleased(QKeyEvent* event) { switch (event->key()) { case Qt::Key_Shift: shiftPressedPos_ = { -1, -1, -1, -1 }; return true; default: return false; } } void PatternEditorPanel::paintEvent(QPaintEvent *event) { if (bt_ && isInitedFirstMod_.load()) { // Check order size int odrSize = static_cast(bt_->getOrderSize(curSongNum_)); if (curPos_.order >= odrSize) curPos_.setRows(odrSize - 1, 0); const QRect& area = event->rect(); if (area.x() == 0 && area.y() == 0) { drawPattern(area); } else { drawPattern(rect()); } } } void PatternEditorPanel::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); funcResize(); redrawAll(); } void PatternEditorPanel::mousePressEvent(QMouseEvent *event) { Q_UNUSED(event) mousePressPos_ = hovPos_; doubleClickPos_ = mousePressPos_; mouseReleasePos_ = { -1, -1, -1, -1 }; isPressedPlus_ = false; isPressedMinus_ = false; if (event->button() == Qt::LeftButton) { if (mousePressPos_.order == -2 && mousePressPos_.trackVisIdx >= 0) { int w = calculateTracksWidthWithRowNum(leftTrackVisIdx_, mousePressPos_.trackVisIdx - 1) + hdMuteToggleWidth_ + stepFontWidth_ / 2; if (w < event->pos().x() && event->pos().x() < w + hdEffCompandButtonWidth_ + stepFontWidth_) { if (event->pos().y() < headerHeight_ / 2) isPressedPlus_ = true; else isPressedMinus_ = true; } } selLeftAbovePos_ = { -1, -1, -1, -1 }; selRightBelowPos_ = { -1, -1, -1, -1 }; selectAllState_ = -1; emit selected(false); } } void PatternEditorPanel::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { if (mousePressPos_.trackVisIdx < 0 || mousePressPos_.order < 0) return; // Start point is out of range if (hovPos_.trackVisIdx >= 0 && hovPos_.order >= 0) { setSelectedRectangle(mousePressPos_, hovPos_); } if (event->x() < stepNumWidth_ && leftTrackVisIdx_ > 0) { if (config_->getMoveCursorByHorizontalScroll()) { moveCursorToRight(-(5 + 2 * rightEffn_.at(static_cast(leftTrackVisIdx_) - 1))); } else { moveViewToRight(-1); } } else if (event->x() > geometry().width() - stepNumWidth_ && hovPos_.trackVisIdx != -1) { if (config_->getMoveCursorByHorizontalScroll()) { moveCursorToRight(5 + 2 * rightEffn_.at(static_cast(leftTrackVisIdx_))); } else { moveViewToRight(1); } } if (event->pos().y() < headerHeight_ + stepFontHeight_) { if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(-1); } else if (event->pos().y() > geometry().height() - stepFontHeight_) { if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(1); } } } void PatternEditorPanel::mouseReleaseEvent(QMouseEvent* event) { mouseReleasePos_ = hovPos_; switch (event->button()) { case Qt::LeftButton: if (mousePressPos_ == mouseReleasePos_) { // Jump cell if (hovPos_.order >= 0 && hovPos_.step >= 0 && hovPos_.trackVisIdx >= 0 && hovPos_.colInTrack >= 0) { int horDif = calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack, hovPos_.trackVisIdx, hovPos_.colInTrack); int verDif = calculateStepDistance(curPos_.order, curPos_.step, hovPos_.order, hovPos_.step); moveCursorToRight(horDif); if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(verDif); } else if (hovPos_.order == -2 && hovPos_.trackVisIdx >= 0) { // Header if (isPressedPlus_) { onExpandEffectColumnPressed(hovPos_.trackVisIdx); } else if (isPressedMinus_) { onShrinkEffectColumnPressed(hovPos_.trackVisIdx); } else { toggleTrack(hovPos_.trackVisIdx); int horDif = calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack, hovPos_.trackVisIdx, 0); moveCursorToRight(horDif); } } else if (hovPos_.trackVisIdx == -2 && hovPos_.order >= 0 && hovPos_.step >= 0) { // Step number if (!bt_->isPlaySong() || !bt_->isFollowPlay()) { int verDif = calculateStepDistance(curPos_.order, curPos_.step, hovPos_.order, hovPos_.step); moveCursorToDown(verDif); } } } break; case Qt::RightButton: // Show context menu { if (mousePressPos_.order == -2) { // Header QMenu menu; // Leave Before Qt5.7.0 style due to windows xp QAction* toggle = menu.addAction(tr("To&ggle Track")); QObject::connect(toggle, &QAction::triggered, this, [&] { toggleTrack(mousePressPos_.trackVisIdx); }); QAction* solo = menu.addAction(tr("&Solo Track")); QObject::connect(solo, &QAction::triggered, this, [&] { soloTrack(mousePressPos_.trackVisIdx); }); QAction* unmute = menu.addAction(tr("&Unmute All Tracks")); QObject::connect(unmute, &QAction::triggered, this, &PatternEditorPanel::onUnmuteAllPressed); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) toggle->setShortcutVisibleInContextMenu(true); solo->setShortcutVisibleInContextMenu(true); #endif auto shortcuts = config_->getShortcuts(); toggle->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::ToggleTrack))); solo->setShortcut(gui_utils::strToKeySeq(shortcuts.at(Configuration::ShortcutAction::SoloTrack))); if (mousePressPos_.trackVisIdx < 0) { toggle->setEnabled(false); solo->setEnabled(false); unmute->setEnabled(false); } menu.exec(mapToGlobal(event->pos())); } else { // Pattern showPatternContextMenu(mousePressPos_, event->pos()); } break; } case Qt::XButton1: { if (!bt_->isPlaySong() || !bt_->isFollowPlay()) { int order = curPos_.order - 1; if (order < 0) order = static_cast(bt_->getOrderSize(curSongNum_)) - 1; int step = std::min( curPos_.step, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, order)) - 1); int d = calculateStepDistance(curPos_.order, curPos_.step, order, step); moveCursorToDown(d); } break; } case Qt::XButton2: { if (!bt_->isPlaySong() || !bt_->isFollowPlay()) { int order = curPos_.order + 1; if (static_cast(bt_->getOrderSize(curSongNum_)) - 1 < order) order = 0; int step = std::min( curPos_.step, static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, order)) - 1); int d = calculateStepDistance(curPos_.order, curPos_.step, order, step); moveCursorToDown(d); } break; } default: break; } mousePressPos_ = { -1, -1, -1, -1 }; mouseReleasePos_ = { -1, -1, -1, -1 }; } void PatternEditorPanel::mouseDoubleClickEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { if (doubleClickPos_.order >= 0) { if (!config_->getDontSelectOnDoubleClick()) { if (doubleClickPos_.trackVisIdx >= 0) { onSelectPressed(4); return; } else if (doubleClickPos_.trackVisIdx == -2) { onSelectPressed(5); return; } } } else if (doubleClickPos_.order == -2) { if (doubleClickPos_.trackVisIdx >= 0 && !isPressedPlus_ && !isPressedMinus_) { bool flag = true; int trackCnt = static_cast(songStyle_.trackAttribs.size()); int clickedNum = visTracks_.at(doubleClickPos_.trackVisIdx); for (int t = 0; t < trackCnt; ++t) { if (t != clickedNum) flag &= bt_->isMute(t); } if (flag) onUnmuteAllPressed(); else soloTrack(doubleClickPos_.trackVisIdx); return; } } } // Else mousePressEvent(event); } bool PatternEditorPanel::mouseHoverd(QHoverEvent *event) { QPoint pos = event->pos(); PatternPosition oldPos = hovPos_; // Detect Step if (pos.y() <= headerHeight_) { // Track header hovPos_.setRows(-2, -2); } else { if (pos.y() < curRowY_) { int tmpOdr = curPos_.order; int tmpStep = curPos_.step + (pos.y() - curRowY_) / stepFontHeight_ - 1; while (true) { if (tmpStep < 0) { if (tmpOdr == 0) { hovPos_.setRows(-1, -1); break; } else { tmpStep += bt_->getPatternSizeFromOrderNumber(curSongNum_, --tmpOdr); } } else { hovPos_.setRows(tmpOdr, tmpStep); break; } } } else { int tmpOdr = curPos_.order; int tmpStep = curPos_.step + (pos.y() - curRowY_) / stepFontHeight_; while (true) { int endStep = static_cast(bt_->getPatternSizeFromOrderNumber(curSongNum_, tmpOdr)); if (tmpStep < endStep) { hovPos_.setRows(tmpOdr, tmpStep); break; } else { if (tmpOdr == static_cast(bt_->getOrderSize(curSongNum_)) - 1) { hovPos_.setRows(-1, -1); break; } else { ++tmpOdr; tmpStep -= endStep; } } } } } // Detect column if (pos.x() <= stepNumWidth_) { // Row number hovPos_.setCols(-2, -2); } else { int tmpWidth = stepNumWidth_; for (int i = leftTrackVisIdx_; ; ) { tmpWidth += (toneNameWidth_ + widthSpaceDbl_); if (pos.x() <= tmpWidth) { hovPos_.setCols(i, 0); break; } tmpWidth += (instWidth_ + widthSpaceDbl_); if (pos.x() <= tmpWidth) { hovPos_.setCols(i, 1); break; } tmpWidth += (volWidth_ + widthSpaceDbl_); if (pos.x() <= tmpWidth) { hovPos_.setCols(i, 2); break; } bool flag = false; for (int j = 0; j <= rightEffn_.at(static_cast(i)); ++j) { tmpWidth += (effIDWidth_ + widthSpace_); if (pos.x() <= tmpWidth) { hovPos_.setCols(i, 3 + 2 * j); flag = true; break; } tmpWidth += (effValWidth_ + widthSpace_); if (pos.x() <= tmpWidth) { hovPos_.setCols(i, 4 + 2 * j); flag = true; break; } } if (flag) break; ++i; if (i == static_cast(visTracks_.size())) { hovPos_.setCols(-1, -1); break; } } } if (hovPos_ != oldPos) redrawByHoverChanged(); return true; } void PatternEditorPanel::wheelEvent(QWheelEvent *event) { if (bt_->isPlaySong() && bt_->isFollowPlay()) return; int cnt = event->angleDelta().y() / 120; if (event->modifiers().testFlag(Qt::ControlModifier)) { onNoteTransposePressed(cnt); } else if (event->modifiers().testFlag(Qt::ShiftModifier)) { onChangeValuesPressed(cnt); } else { moveCursorToDown(-cnt); } } void PatternEditorPanel::leaveEvent(QEvent* event) { Q_UNUSED(event) // Clear mouse hover selection hovPos_ = { -1, -1, -1, -1 }; } void PatternEditorPanel::midiThreadReceivedEvent(double delay, const uint8_t *msg, size_t len, void *userData) { PatternEditorPanel *self = reinterpret_cast(userData); Q_UNUSED(delay) // Note-On/Note-Off if (len == 3 && (msg[0] & 0xe0) == 0x80) { uint8_t status = msg[0]; uint8_t key = msg[1]; uint8_t velocity = msg[2]; QMetaMethod method = self->metaObject()->method(self->midiKeyEventMethod_); method.invoke(self, Qt::QueuedConnection, Q_ARG(uchar, status), Q_ARG(uchar, key), Q_ARG(uchar, velocity)); } } void PatternEditorPanel::midiKeyEvent(uchar status, uchar key, uchar velocity) { if (!bt_->isJamMode()) { bool release = ((status & 0xf0) == 0x80) || velocity == 0; if (!release) { setStepKeyOn(Note(static_cast(key) - 12)); } } } BambooTracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_editor_panel.hpp000066400000000000000000000242351401124043500274710ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef PATTERN_EDITOR_PANEL_HPP #define PATTERN_EDITOR_PANEL_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bamboo_tracker.hpp" #include "configuration.hpp" #include "song.hpp" #include "gui/pattern_editor/pattern_position.hpp" #include "gui/color_palette.hpp" class PatternEditorPanel : public QWidget { Q_OBJECT public: explicit PatternEditorPanel(QWidget *parent = nullptr); ~PatternEditorPanel() override; void setCore(std::shared_ptr core); bool isReadyCore() const; void setCommandStack(std::weak_ptr stack); void setConfiguration(std::shared_ptr config); void setColorPallete(std::shared_ptr palette); void changeEditable(); int getFullColmunSize() const; void updatePositionByStepUpdate(bool isFirstUpdate, bool forceJump = false, bool trackChanged = false); int getScrollableCountByTrack() const; void changeMarker(); void copySelectedCells(); void cutSelectedCells(); void redrawByPatternChanged(bool patternSizeChanged = false); void redrawByFocusChanged(); void redrawByHoverChanged(); void redrawByMaskChanged(); void redrawPatterns(); void redrawAll(); void resetEntryCount(); void waitPaintFinish(); QString getHeaderFont() const; int getHeaderFontSize() const; QString getRowsFont() const; int getRowsFontSize() const; void setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize); void setVisibleTracks(std::vector tracks); std::vector getVisibleTracks() const; public slots: void onHScrollBarChanged(int num); void onVScrollBarChanged(int num); void onOrderListCurrentTrackChanged(int idx); void onOrderListCurrentOrderChanged(int num); void onOrderListEdited(); void onDefaultPatternSizeChanged(); void onShortcutUpdated(); void setPatternHighlight1Count(int count); void setPatternHighlight2Count(int count); void setEditableStep(int n); void onSongLoaded(); void onDeletePressed(); void onPastePressed(); void onPasteMixPressed(); void onPasteOverwritePressed(); void onPasteInsertPressed(); /// 0: None /// 1: All /// 2: Row /// 3: Column /// 4: Pattern /// 5: Order void onSelectPressed(int type); void onNoteTransposePressed(int seminote); void onToggleTrackPressed(); void onSoloTrackPressed(); void onUnmuteAllPressed(); void onExpandPressed(); void onShrinkPressed(); void onInterpolatePressed(); void onReversePressed(); void onReplaceInstrumentPressed(); void onExpandEffectColumnPressed(int trackVisIdx); void onShrinkEffectColumnPressed(int trackVisIdx); void onFollowModeChanged(); void onChangeValuesPressed(int value); void onPlayStepPressed(); signals: void hScrollBarChangeRequested(int num); void vScrollBarChangeRequested(int num, int max); void currentTrackChanged(int num); void currentOrderChanged(int num, int max); void effectColsCompanded(int num, int max); void selected(bool isSelected); void instrumentEntered(int num); void volumeEntered(int volume); void effectEntered(QString text); protected: bool event(QEvent *event) override; bool keyPressed(QKeyEvent* event); bool keyReleased(QKeyEvent* event); void paintEvent(QPaintEvent* event) override; void resizeEvent(QResizeEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseDoubleClickEvent(QMouseEvent* event) override; bool mouseHoverd(QHoverEvent* event); void wheelEvent(QWheelEvent* event) override; void leaveEvent(QEvent* event) override; // Midi private: static void midiThreadReceivedEvent(double delay, const uint8_t *msg, size_t len, void *userData); private slots: void midiKeyEvent(uchar status, uchar key, uchar velocity); private: QPixmap completePixmap_, backPixmap_, textPixmap_, forePixmap_, headerPixmap_; std::shared_ptr bt_; std::weak_ptr comStack_; std::shared_ptr config_; std::shared_ptr palette_; QFont stepFont_, headerFont_; int stepFontWidth_, stepFontHeight_, stepFontAscent_, stepFontLeading_; int headerFontAscent_; int widthSpace_, widthSpaceDbl_; int stepNumWidthCnt_, stepNumWidth_, stepNumBase_; int baseTrackWidth_; int toneNameWidth_, instWidth_; int volWidth_; int effWidth_, effIDWidth_, effValWidth_; int tracksWidthFromLeftToEnd_; int hdMuteToggleWidth_, hdEffCompandButtonWidth_; int headerHeight_; int hdPlusY_, hdMinusY_; int curRowBaselineY_; int curRowY_; std::vector visTracks_, rightEffn_; int leftTrackVisIdx_; SongStyle songStyle_; int curSongNum_; PatternPosition curPos_, hovPos_; PatternPosition mousePressPos_, mouseReleasePos_; PatternPosition selLeftAbovePos_, selRightBelowPos_; PatternPosition shiftPressedPos_; PatternPosition doubleClickPos_; PatternPosition markerPos_; bool isIgnoreToSlider_, isIgnoreToOrder_; bool isPressedPlus_, isPressedMinus_; int entryCnt_; int selectAllState_; bool isMuteElse_; int hl1Cnt_, hl2Cnt_; int editableStepCnt_; int viewedRowCnt_; int viewedRegionHeight_; int viewedRowsHeight_, viewedRowOffset_, viewedCenterY_, viewedCenterBaseY_; PatternPosition viewedFirstPos_, viewedCenterPos_, viewedLastPos_; bool backChanged_, textChanged_, foreChanged_, headerChanged_, focusChanged_, followModeChanged_; bool hasFocussedBefore_; int stepDownCount_; std::atomic_bool repaintable_; // Recurrensive repaint guard std::atomic_int repaintingCnt_; std::atomic_bool isInitedFirstMod_; // Shortcuts QShortcut upSc_, upWSSc_, dnSc_, dnWSSc_, pgUpSc_, pgUpWSSc_, pgDnSc_, pgDnWSSc_; QShortcut homeSc_, homeWSSc_, endSc_, endWSSc_, hlUpSc_, hlUpWSSc_, hlDnSc_, hlDnWSSc_; QShortcut ltSc_, ltWSSc_, rtSc_, rtWSSc_; QShortcut keyOffSc_, echoBufSc_, stepMvUpSc_, stepMvDnSc_, expandColSc_, shrinkColSc_; // Meta methods int midiKeyEventMethod_; void funcResize(); void updateSizes(); void initDisplay(); void drawPattern(const QRect& rect); void drawRows(int maxWidth); void quickDrawRows(int maxWidth); /// Return: /// track width int drawStep(QPainter& forePainter, QPainter& textPainter, QPainter& backPainter, int trackVisIdx, int orderNum, int stepNum, int x, int baseY, int rowY); void drawHeaders(int maxWidth); void drawBorders(int maxWidth); void drawShadow(); // NOTE: Calculated by visible tracks int calculateTracksWidthWithRowNum(int beginIdx, int endIdx) const; int calculateColNumInRow(int trackVisIdx, int colNumInTrack, bool isExpanded = false) const; int calculateColumnDistance(int beginTrackIdx, int beginColumn, int endTrackIdx, int endColumn, bool isExpanded = false) const; int calculateStepDistance(int beginOrder, int beginStep, int endOrder, int endStep) const; PatternPosition calculatePositionFrom(int order, int step, int by) const; QPoint calculateCurrentCursorPosition() const; inline void updateTracksWidthFromLeftToEnd() { tracksWidthFromLeftToEnd_ = calculateTracksWidthWithRowNum( leftTrackVisIdx_, static_cast(visTracks_.size()) - 1); } void moveCursorToRight(int n); void moveViewToRight(int n); void moveCursorToDown(int n); inline void checkSelectionByCursorMove(bool isShift) { if (isShift) setSelectedRectangle(shiftPressedPos_, curPos_); else onSelectPressed(0); } bool enterToneData(QKeyEvent* event); void setStepKeyOn(const Note& note); bool enterInstrumentData(int key); void setStepInstrument(int num); bool enterVolumeData(int key); void setStepVolume(int volume); bool enterEffectID(int key); void setStepEffectID(QString str); bool enterEffectValue(int key); void setStepEffectValue(int value); inline int updateEntryCount() { entryCnt_ = (entryCnt_ + 1) % 2; return entryCnt_; } void insertStep(); void deletePreviousStep(); void eraseSelectedCells(); void pasteCopiedCells(const PatternPosition& cursorPos); void pasteMixCopiedCells(const PatternPosition& cursorPos); void pasteOverwriteCopiedCells(const PatternPosition& cursorPos); void pasteInsertCopiedCells(const PatternPosition& cursorPos); using PatternCells = std::vector>; PatternCells decodeCells(QString str, int& startCol); PatternPosition getPasteLeftAbovePosition( int pasteCol, const PatternPosition& cursorPos, size_t cellW) const; PatternCells compandPasteCells(const PatternPosition& laPos, const PatternCells& cells); void transposeNote(const PatternPosition& startPos, const PatternPosition& endPos, int seminote); void changeValuesInPattern(const PatternPosition& startPos, const PatternPosition& endPos, int value); void toggleTrack(int trackIdx); void soloTrack(int trackIdx); void setSelectedRectangle(const PatternPosition& start, const PatternPosition& end); bool isSelectedCell(int trackVisIdx, int colNum, int orderNum, int stepNum); void showPatternContextMenu(const PatternPosition& pos, const QPoint& point); }; #endif // PATTERN_EDITOR_PANEL_HPP BambooTracker-0.4.6/BambooTracker/gui/pattern_editor/pattern_position.hpp000066400000000000000000000107451401124043500266710ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef PATTERN_POSITION_HPP #define PATTERN_POSITION_HPP struct PatternPosition { int trackVisIdx, colInTrack, order, step; friend bool operator==(const PatternPosition& a, const PatternPosition& b); friend bool operator!=(const PatternPosition& a, const PatternPosition& b); void setCols(int trackVisIdx, int colInTrack); void setRows(int order, int step); int compareCols(const PatternPosition& b) const; int compareRows(const PatternPosition& b) const; bool isEqualCols(const PatternPosition& b) const; bool isEqualCols(const int trackVisIdx, const int colInTrack) const; bool isEqualRows(const PatternPosition& b) const; bool isEqualRows(const int order, const int step) const; static bool inRowRange(const PatternPosition& pos, const PatternPosition& begin, const PatternPosition& last) { return (begin.compareRows(pos) <= 0 && pos.compareRows(last) <= 0); } static bool inRowRange(const PatternPosition& srcBegin, const PatternPosition& srcLast, const PatternPosition& tgtBegin, const PatternPosition& tgtLast) { if (PatternPosition::inRowRange(srcBegin, tgtBegin, tgtLast)) return true; else if (PatternPosition::inRowRange(srcLast, tgtBegin, tgtLast)) return true; else return (srcBegin.compareRows(tgtBegin) < 0 && tgtLast.compareRows(srcLast)); } }; inline bool operator==(const PatternPosition& a, const PatternPosition& b) { return ((a.trackVisIdx == b.trackVisIdx) && (a.colInTrack == b.colInTrack) && (a.order == b.order) && (a.step == b.step)); } inline bool operator!=(const PatternPosition& a, const PatternPosition& b) { return !(a == b); } inline void PatternPosition::setCols(int track, int colInTrack) { this->trackVisIdx = track; this->colInTrack = colInTrack; } inline void PatternPosition::setRows(int order, int step) { this->order = order; this->step = step; } /// Return: /// -2: this->track < b.track /// -1: this->track == b.track && this->colInTrack < b.colInTrack /// 0: this->track == b.track && this->colInTrack == b.colInTrack /// 1: this->track == b.track && this->colInTrack > b.colInTrack /// 2: this->track > b.track inline int PatternPosition::compareCols(const PatternPosition& b) const { if (trackVisIdx < b.trackVisIdx) return -2; else if (trackVisIdx > b.trackVisIdx) return 2; else { if (colInTrack < b.colInTrack) return -1; else if (colInTrack > b.colInTrack) return 1; else return 0; } } /// Return: /// -2: this->order < b.order /// -1: this->order == b.order && this->step < b.step /// 0: *this == b /// 1: this->order == b.order && this->step > b.step /// 2: this->order > b.order inline int PatternPosition::compareRows(const PatternPosition& b) const { if (order < b.order) return -2; else if (order > b.order) return 2; else { if (step < b.step) return -1; else if (step > b.step) return 1; else return 0; } } inline bool PatternPosition::isEqualCols(const PatternPosition& b) const { return isEqualCols(b.trackVisIdx, b.colInTrack); } inline bool PatternPosition::isEqualCols(const int trackVisIdx, const int colInTrack) const { return (this->trackVisIdx == trackVisIdx && this->colInTrack == colInTrack); } inline bool PatternPosition::isEqualRows(const PatternPosition& b) const { return isEqualRows(b.order, b.step); } inline bool PatternPosition::isEqualRows(const int order, const int step) const { return (this->order == order && this->step == step); } #endif // PATTERN_POSITION_HPP BambooTracker-0.4.6/BambooTracker/gui/q_application_wrapper.cpp000066400000000000000000000031071401124043500246150ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "q_application_wrapper.hpp" #include #include QApplicationWrapper::QApplicationWrapper(int& argc, char** argv) : QApplication(argc, argv) {} bool QApplicationWrapper::notify(QObject* receiver, QEvent* event) { try { return QApplication::notify(receiver, event); } catch (std::exception& e) { QMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("An unknown error occurred.\n%1").arg(e.what())); return false; } } BambooTracker-0.4.6/BambooTracker/gui/q_application_wrapper.hpp000066400000000000000000000026101401124043500246200ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef Q_APPLICATION_WRAPPER_HPP #define Q_APPLICATION_WRAPPER_HPP #include class QApplicationWrapper : public QApplication { public: QApplicationWrapper(int& argc, char** argv); bool notify(QObject* receiver, QEvent* event) override; }; #endif // Q_APPLICATION_WRAPPER_HPP BambooTracker-0.4.6/BambooTracker/gui/s98_export_settings_dialog.cpp000066400000000000000000000105201401124043500255120ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "s98_export_settings_dialog.hpp" #include "ui_s98_export_settings_dialog.h" #include "io/export_io.hpp" S98ExportSettingsDialog::S98ExportSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::S98ExportSettingsDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); for (QRadioButton *button : { ui->ym2608RadioButton, ui->ym2612RadioButton, ui->ym2203RadioButton, ui->noneFmRadioButton, ui->internalSsgRadioButton, ui->ay8910PsgRadioButton }) connect(button, &QAbstractButton::toggled, this, &S98ExportSettingsDialog::updateSupportInformation); updateSupportInformation(); } S98ExportSettingsDialog::~S98ExportSettingsDialog() { delete ui; } int S98ExportSettingsDialog::getResolution() const { return ui->resSpinBox->value(); } bool S98ExportSettingsDialog::enabledTag() const { return ui->tagGroupBox->isChecked(); } io::S98Tag S98ExportSettingsDialog::getS98Tag() const { io::S98Tag tag; tag.title = ui->titleLineEdit->text().toUtf8().toStdString(); tag.artist = ui->artistLineEdit->text().toUtf8().toStdString(); tag.game = ui->gameLineEdit->text().toUtf8().toStdString(); tag.year = ui->yearLineEdit->text().toUtf8().toStdString(); tag.genre = ui->genreLineEdit->text().toUtf8().toStdString(); tag.comment = ui->commentLineEdit->text().toUtf8().toStdString(); tag.copyright = ui->copyrightLineEdit->text().toUtf8().toStdString(); tag.s98by = ui->s98byLineEdit->text().toUtf8().toStdString(); tag.system = ui->systemLineEdit->text().toUtf8().toStdString(); return tag; } int S98ExportSettingsDialog::getExportTarget() const { int target = 0; if (ui->ym2608RadioButton->isChecked()) target |= io::Export_YM2608; else if (ui->ym2612RadioButton->isChecked()) target |= io::Export_YM2612; else if (ui->ym2203RadioButton->isChecked()) target |= io::Export_YM2203; if (ui->ay8910PsgRadioButton->isChecked()) target |= io::Export_AY8910Psg; else if (ui->ym2149PsgRadioButton->isChecked()) target |= io::Export_YM2149Psg; return target; } void S98ExportSettingsDialog::updateSupportInformation() { int target = getExportTarget(); int channels; int fm = target & io::Export_FmMask; int ssg = target & io::Export_SsgMask; switch (fm) { default: channels = 6; break; case io::Export_YM2203: channels = 3; break; case io::Export_NoneFm: channels = 0; break; } bool haveSsg = fm == io::Export_YM2608 || fm == io::Export_YM2203 || ssg != io::Export_InternalSsg; bool haveRhythm = fm == io::Export_YM2608; bool haveAdpcm = fm == io::Export_YM2608; ui->supportFmChannelsLabel->setText(QString::number(channels)); ui->supportSsgLabel->setText(haveSsg ? tr("Yes") : tr("No")); ui->supportRhythmLabel->setText(haveRhythm ? tr("Yes") : tr("No")); ui->supportAdpcmLabel->setText(haveAdpcm ? tr("Yes") : tr("No")); QPalette normalPalette = palette(); QPalette warnPalette = normalPalette; warnPalette.setColor(QPalette::WindowText, QColor(0xef2929)); ui->supportFmChannelsLabel->setPalette((channels == 6) ? normalPalette : warnPalette); ui->supportSsgLabel->setPalette(haveSsg ? normalPalette : warnPalette); ui->supportRhythmLabel->setPalette(haveRhythm ? normalPalette : warnPalette); ui->supportAdpcmLabel->setPalette(haveAdpcm ? normalPalette : warnPalette); } BambooTracker-0.4.6/BambooTracker/gui/s98_export_settings_dialog.hpp000066400000000000000000000032651401124043500255270ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef S98_EXPORT_SETTINGS_DIALOG_HPP #define S98_EXPORT_SETTINGS_DIALOG_HPP #include #include "io/export_io.hpp" namespace Ui { class S98ExportSettingsDialog; } class S98ExportSettingsDialog : public QDialog { Q_OBJECT public: explicit S98ExportSettingsDialog(QWidget *parent = nullptr); ~S98ExportSettingsDialog(); int getResolution() const; bool enabledTag() const; io::S98Tag getS98Tag() const; int getExportTarget() const; private slots: void updateSupportInformation(); private: Ui::S98ExportSettingsDialog *ui; }; #endif // S98_EXPORT_SETTINGS_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/s98_export_settings_dialog.ui000066400000000000000000000302101401124043500253430ustar00rootroot00000000000000 S98ExportSettingsDialog 0 0 491 521 S98 export settings Resolution Hz 1 100000 1000 Tag true Title Artist Game Year Genre Comment Copyright S98by System NEC PC-9801 Target FM YM2608 OPNA true YM2612 OPN2 YM2203 OPN None SSG OPN internal true AY-3-8910 PSG YM2149 PSG Qt::Vertical 20 40 Support FM Channels QFrame::WinPanel QFrame::Sunken 6 Qt::PlainText Qt::AlignCenter SSG QFrame::WinPanel QFrame::Sunken Yes Qt::PlainText Qt::AlignCenter Rhythm QFrame::WinPanel QFrame::Sunken Yes Qt::PlainText Qt::AlignCenter ADPCM QFrame::WinPanel QFrame::Sunken Yes Qt::PlainText Qt::AlignCenter Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok resSpinBox tagGroupBox titleLineEdit artistLineEdit gameLineEdit yearLineEdit genreLineEdit commentLineEdit copyrightLineEdit s98byLineEdit systemLineEdit ym2608RadioButton ym2612RadioButton ym2203RadioButton noneFmRadioButton internalSsgRadioButton ay8910PsgRadioButton ym2149PsgRadioButton buttonBox accepted() S98ExportSettingsDialog accept() 248 254 157 274 buttonBox rejected() S98ExportSettingsDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/slider_style.cpp000066400000000000000000000026531401124043500227410ustar00rootroot00000000000000/* * Copyright (C) 2018 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "slider_style.hpp" int SliderStyle::styleHint (StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* returnData) const { if (hint == QStyle::SH_Slider_AbsoluteSetButtons) { return Qt::LeftButton; } else { return QProxyStyle::styleHint(hint, option, widget, returnData); } } BambooTracker-0.4.6/BambooTracker/gui/slider_style.hpp000066400000000000000000000026431401124043500227450ustar00rootroot00000000000000/* * Copyright (C) 2018 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SLIDER_STYLE_HPP #define SLIDER_STYLE_HPP #include class SliderStyle : public QProxyStyle { public: virtual int styleHint (StyleHint hint, const QStyleOption* option = nullptr, const QWidget* widget = nullptr, QStyleHintReturn* returnData = nullptr) const; }; #endif // SLIDER_STYLE_HPP BambooTracker-0.4.6/BambooTracker/gui/swap_tracks_dialog.cpp000066400000000000000000000036651401124043500241030ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "swap_tracks_dialog.hpp" #include "ui_swap_tracks_dialog.h" #include "song.hpp" #include #include "gui/gui_utils.hpp" SwapTracksDialog::SwapTracksDialog(const SongStyle& style, QWidget *parent) : QDialog(parent), ui(new Ui::SwapTracksDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); for (const auto& attrib : style.trackAttribs) { QString text = gui_utils::getTrackName(style.type, attrib.source, attrib.channelInSource); ui->track1ComboBox->addItem(text, attrib.number); ui->track2ComboBox->addItem(text, attrib.number); } } SwapTracksDialog::~SwapTracksDialog() { delete ui; } int SwapTracksDialog::getTrack1() const { return ui->track1ComboBox->currentData().toInt(); } int SwapTracksDialog::getTrack2() const { return ui->track2ComboBox->currentData().toInt(); } BambooTracker-0.4.6/BambooTracker/gui/swap_tracks_dialog.hpp000066400000000000000000000030301401124043500240720ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SWAP_TRACKS_DIALOG_HPP #define SWAP_TRACKS_DIALOG_HPP #include struct SongStyle; namespace Ui { class SwapTracksDialog; } class SwapTracksDialog : public QDialog { Q_OBJECT public: explicit SwapTracksDialog(const SongStyle& style, QWidget *parent = nullptr); ~SwapTracksDialog() override; int getTrack1() const; int getTrack2() const; private: Ui::SwapTracksDialog *ui; }; #endif // SWAP_TRACKS_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/swap_tracks_dialog.ui000066400000000000000000000041021401124043500237210ustar00rootroot00000000000000 SwapTracksDialog 0 0 174 95 Swap Tracks Track 1 Track 2 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() SwapTracksDialog accept() 248 254 157 274 buttonBox rejected() SwapTracksDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/track_visibility_memory_handler.cpp000066400000000000000000000063731401124043500267020ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "track_visibility_memory_handler.hpp" #include #include #include #include #include "song.hpp" namespace io { namespace { // config path (*nix): ~/.config//.ini const QString ORG_NAME = "BambooTracker"; const QString FILE_NAME = "TrackVisibility"; } bool saveTrackVisibilityMemory(const SongType type, const std::vector& visTracks) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, FILE_NAME); int tid; switch (type) { case SongType::Standard: tid = 0; break; case SongType::FM3chExpanded: tid = 1; break; default: return false; } settings.setValue("type", tid); QString str = std::accumulate( visTracks.begin() + 1, visTracks.end(), QString::number(visTracks.front()), [](QString str, int t) { return QString("%1,%2").arg(str).arg(t); }); settings.setValue("visTracks", str); return true; } catch (...) { return false; } } bool loadTrackVisibilityMemory(SongType& type, std::vector& visTracks) { try { QSettings settings(QSettings::IniFormat, QSettings::UserScope, ORG_NAME, FILE_NAME); if (!settings.contains("type")) return false; switch (settings.value("type", -1).toInt()) { case 0: type = SongType::Standard; break; case 1: type = SongType::FM3chExpanded; break; default: return false; } if (!settings.contains("visTracks")) return false; QString tracks = settings.value("visTracks", "-1").toString(); QStringList list = tracks.split(","); if ((type == SongType::Standard && 16 < list.size())) return false; else if ((type == SongType::FM3chExpanded && 19 < list.size())) return false; std::vector tl(list.size()); std::transform(list.begin(), list.end(), tl.begin(), [](const QString& s) { return s.toInt(); }); std::sort(tl.begin(), tl.end()); if (tl.front() < 0) return false; if ((type == SongType::Standard && 15 < tl.back())) return false; else if ((type == SongType::FM3chExpanded && 18 < tl.back())) return false; if (std::adjacent_find(tl.begin(), tl.end()) != tl.end()) return false; visTracks.swap(tl); return true; } catch (...) { return false; } } } BambooTracker-0.4.6/BambooTracker/gui/track_visibility_memory_handler.hpp000066400000000000000000000027151401124043500267030ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TRACK_VISIBILITY_MEMORY_HANDLER_HPP #define TRACK_VISIBILITY_MEMORY_HANDLER_HPP #include enum class SongType; namespace io { bool saveTrackVisibilityMemory(const SongType type, const std::vector& visTracks); bool loadTrackVisibilityMemory(SongType& type, std::vector& visTracks); } #endif // TRACK_VISIBILITY_MEMORY_HANDLER_HPP BambooTracker-0.4.6/BambooTracker/gui/transpose_song_dialog.cpp000066400000000000000000000044051401124043500246170ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "transpose_song_dialog.hpp" #include "ui_transpose_song_dialog.h" #include TransposeSongDialog::TransposeSongDialog(QWidget *parent) : QDialog(parent), ui(new Ui::TransposeSongDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); for (int i = 0; i < 128; ++i) { auto item = new QListWidgetItem(QString("%1").arg(i, 2, 16, QChar('0')).toUpper()); checks[i] = item; item->setCheckState(Qt::Unchecked); QObject::connect(ui->reversePushButton, &QPushButton::clicked, this, [item] { item->setCheckState((item->checkState() == Qt::Unchecked) ? Qt::Checked : Qt::Unchecked); }); QObject::connect(ui->clearPushButton, &QPushButton::clicked, this, [item] { item->setCheckState(Qt::Unchecked); }); ui->listWidget->addItem(item); } } TransposeSongDialog::~TransposeSongDialog() { delete ui; } int TransposeSongDialog::getTransposeSeminotes() const { return ui->spinBox->value(); } std::vector TransposeSongDialog::getExcludeInstruments() const { std::vector list; for (int i = 0; i < 128; ++i) { if (checks[i]->checkState() == Qt::Checked) list.push_back(i); } return list; } BambooTracker-0.4.6/BambooTracker/gui/transpose_song_dialog.hpp000066400000000000000000000031661401124043500246270ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TRANSPOSE_SONG_DIALOG_HPP #define TRANSPOSE_SONG_DIALOG_HPP #include #include #include namespace Ui { class TransposeSongDialog; } class TransposeSongDialog : public QDialog { Q_OBJECT public: explicit TransposeSongDialog(QWidget *parent = nullptr); ~TransposeSongDialog() override; int getTransposeSeminotes() const; std::vector getExcludeInstruments() const; private: Ui::TransposeSongDialog *ui; QListWidgetItem* checks[128]; }; #endif // TRANSPOSE_SONG_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/transpose_song_dialog.ui000066400000000000000000000114231401124043500244500ustar00rootroot00000000000000 TransposeSongDialog 0 0 300 300 Transpose Song Seminotes -96 96 Qt::Horizontal 40 20 Exclude these instruments Qt::Horizontal 40 20 Qt::Horizontal 40 20 Qt::Horizontal 40 20 Reverse Clear All Qt::ElideNone QListView::TopToBottom true QListView::Adjust 1 true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok spinBox listWidget reversePushButton clearPushButton buttonBox accepted() TransposeSongDialog accept() 248 254 157 274 buttonBox rejected() TransposeSongDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/vgm_export_settings_dialog.cpp000066400000000000000000000160251401124043500256660ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "vgm_export_settings_dialog.hpp" #include "ui_vgm_export_settings_dialog.h" #include #include "io/export_io.hpp" VgmExportSettingsDialog::VgmExportSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::VgmExportSettingsDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); for (QRadioButton *button : { ui->ym2608RadioButton, ui->ym2612RadioButton, ui->ym2203RadioButton, ui->noneFmRadioButton, ui->internalSsgRadioButton, ui->ay8910PsgRadioButton }) connect(button, &QAbstractButton::toggled, this, &VgmExportSettingsDialog::updateSupportInformation); updateSupportInformation(); } VgmExportSettingsDialog::~VgmExportSettingsDialog() { delete ui; } bool VgmExportSettingsDialog::enabledGD3() const { return ui->gd3GroupBox->isChecked(); } QString VgmExportSettingsDialog::getTrackNameEnglish() const { return ui->titleEnLineEdit->text(); } QString VgmExportSettingsDialog::getTrackNameJapanese() const { return ui->titleJpLineEdit->text(); } QString VgmExportSettingsDialog::getGameNameEnglish() const { return ui->nameEnLineEdit->text(); } QString VgmExportSettingsDialog::getGameNameJapanese() const { return ui->nameJpLineEdit->text(); } QString VgmExportSettingsDialog::getSystemNameEnglish() const { return ui->systemEnLineEdit->text(); } QString VgmExportSettingsDialog::getSystemNameJapanese() const { return ui->systemJpLineEdit->text(); } QString VgmExportSettingsDialog::getTrackAuthorEnglish() const { return ui->authorEnLineEdit->text(); } QString VgmExportSettingsDialog::getTrackAuthorJapanese() const { return ui->authorJpLineEdit->text(); } QString VgmExportSettingsDialog::getReleaseDate() const { return ui->releaseDateLineEdit->text(); } QString VgmExportSettingsDialog::getVgmCreator() const { return ui->creatorLineEdit->text(); } QString VgmExportSettingsDialog::getNotes() const { return ui->notesPlainTextEdit->toPlainText(); } io::GD3Tag VgmExportSettingsDialog::getGD3Tag() const { io::GD3Tag tag; QTextCodec* sjis = QTextCodec::codecForName("Shift-JIS"); std::string endNull = ""; endNull += '\0'; endNull += '\0'; const QByteArray trackNameEn = getTrackNameEnglish().toLatin1(); tag.trackNameEn = ""; for (const auto& c : trackNameEn) { tag.trackNameEn += c; tag.trackNameEn += '\0'; } tag.trackNameEn += endNull; const QByteArray trackNameJp = sjis->fromUnicode(getTrackNameJapanese()); tag.trackNameJp = ""; for (const auto& c : trackNameJp) { tag.trackNameJp += c; } tag.trackNameJp += endNull; const QByteArray gameNameEn = getGameNameEnglish().toLatin1(); tag.gameNameEn = ""; for (const auto& c : gameNameEn) { tag.gameNameEn += c; tag.gameNameEn += '\0'; } tag.gameNameEn += endNull; const QByteArray gameNameJp = sjis->fromUnicode(getGameNameJapanese()); tag.gameNameJp = ""; for (const auto& c : gameNameJp) { tag.gameNameJp += c; } tag.gameNameJp += endNull; const QByteArray systemNameEn = getSystemNameEnglish().toLatin1(); tag.systemNameEn = ""; for (const auto& c : systemNameEn) { tag.systemNameEn += c; tag.systemNameEn += '\0'; } tag.systemNameEn += endNull; const QByteArray systemNameJp = sjis->fromUnicode(getSystemNameJapanese()); tag.systemNameJp = ""; for (const auto& c : systemNameJp) { tag.systemNameJp += c; } tag.systemNameJp += endNull; const QByteArray authorEn = getTrackAuthorEnglish().toLatin1(); tag.authorEn = ""; for (const auto& c : authorEn) { tag.authorEn += c; tag.authorEn += '\0'; } tag.authorEn += endNull; const QByteArray authorJp = sjis->fromUnicode(getTrackAuthorJapanese()); tag.authorJp = ""; for (const auto& c : authorJp) { tag.authorJp += c; } tag.authorJp += endNull; const QByteArray releaseDate = getReleaseDate().toLatin1(); tag.releaseDate = ""; for (const auto& c : releaseDate) { tag.releaseDate += c; tag.releaseDate += '\0'; } tag.releaseDate += endNull; const QByteArray vgmCreator = getVgmCreator().toLatin1(); tag.vgmCreator = ""; for (const auto& c : vgmCreator) { tag.vgmCreator += c; tag.vgmCreator += '\0'; } tag.vgmCreator += endNull; const QByteArray notes = getNotes().toLatin1(); tag.notes = ""; for (const auto& c : notes) { tag.notes += c; tag.notes += '\0'; } tag.notes += endNull; return tag; } int VgmExportSettingsDialog::getExportTarget() const { int target = 0; if (ui->ym2608RadioButton->isChecked()) target |= io::Export_YM2608; else if (ui->ym2612RadioButton->isChecked()) target |= io::Export_YM2612; else if (ui->ym2203RadioButton->isChecked()) target |= io::Export_YM2203; if (ui->ay8910PsgRadioButton->isChecked()) target |= io::Export_AY8910Psg; else if (ui->ym2149PsgRadioButton->isChecked()) target |= io::Export_YM2149Psg; return target; } void VgmExportSettingsDialog::updateSupportInformation() { int target = getExportTarget(); int channels; int fm = target & io::Export_FmMask; int ssg = target & io::Export_SsgMask; switch (fm) { default: channels = 6; break; case io::Export_YM2203: channels = 3; break; case io::Export_NoneFm: channels = 0; break; } bool haveSsg = fm == io::Export_YM2608 || fm == io::Export_YM2203 || ssg != io::Export_InternalSsg; bool haveRhythm = fm == io::Export_YM2608; bool haveAdpcm = fm == io::Export_YM2608; ui->supportFmChannelsLabel->setText(QString::number(channels)); ui->supportSsgLabel->setText(haveSsg ? tr("Yes") : tr("No")); ui->supportRhythmLabel->setText(haveRhythm ? tr("Yes") : tr("No")); ui->supportAdpcmLabel->setText(haveAdpcm ? tr("Yes") : tr("No")); QPalette normalPalette = palette(); QPalette warnPalette = normalPalette; warnPalette.setColor(QPalette::WindowText, QColor(0xef2929)); ui->supportFmChannelsLabel->setPalette((channels == 6) ? normalPalette : warnPalette); ui->supportSsgLabel->setPalette(haveSsg ? normalPalette : warnPalette); ui->supportRhythmLabel->setPalette(haveRhythm ? normalPalette : warnPalette); ui->supportAdpcmLabel->setPalette(haveAdpcm ? normalPalette : warnPalette); } BambooTracker-0.4.6/BambooTracker/gui/vgm_export_settings_dialog.hpp000066400000000000000000000040771401124043500256770ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef VGM_EXPORT_SETTINGS_DIALOG_HPP #define VGM_EXPORT_SETTINGS_DIALOG_HPP #include #include #include "io/export_io.hpp" namespace Ui { class VgmExportSettingsDialog; } class VgmExportSettingsDialog : public QDialog { Q_OBJECT public: explicit VgmExportSettingsDialog(QWidget *parent = nullptr); ~VgmExportSettingsDialog(); bool enabledGD3() const; QString getTrackNameEnglish() const; QString getTrackNameJapanese() const; QString getGameNameEnglish() const; QString getGameNameJapanese() const; QString getSystemNameEnglish() const; QString getSystemNameJapanese() const; QString getTrackAuthorEnglish() const; QString getTrackAuthorJapanese() const; QString getReleaseDate() const; QString getVgmCreator() const; QString getNotes() const; io::GD3Tag getGD3Tag() const; int getExportTarget() const; private slots: void updateSupportInformation(); private: Ui::VgmExportSettingsDialog *ui; }; #endif // VGM_EXPORT_SETTINGS_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/vgm_export_settings_dialog.ui000066400000000000000000000451121401124043500255200ustar00rootroot00000000000000 VgmExportSettingsDialog 0 0 491 622 VGM export settings GD3 tag true 6 6 6 6 3 Game 0 0 70 0 70 16777215 Name English 0 0 70 0 70 16777215 Release date Qt::Horizontal 152 20 Japanese 0 0 70 0 70 16777215 System Japanese NEC PC-9801 English Track 0 0 70 0 70 16777215 Title English Japanese 0 0 70 0 70 16777215 Author English Japanese VGM file 0 0 70 0 70 16777215 Creator Qt::Horizontal 149 20 0 0 70 0 70 16777215 Notes Target FM YM2608 OPNA true YM2612 OPN2 YM2203 OPN None SSG OPN internal true AY-3-8910 PSG YM2149 PSG Qt::Vertical 20 40 Support FM Channels QFrame::WinPanel QFrame::Sunken 6 Qt::PlainText Qt::AlignCenter SSG QFrame::WinPanel QFrame::Sunken Yes Qt::PlainText Qt::AlignCenter Rhythm QFrame::WinPanel QFrame::Sunken Yes Qt::PlainText Qt::AlignCenter ADPCM QFrame::WinPanel QFrame::Sunken Yes Qt::PlainText Qt::AlignCenter Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok gd3GroupBox titleEnLineEdit titleJpLineEdit authorEnLineEdit authorJpLineEdit nameEnLineEdit nameJpLineEdit systemEnLineEdit systemJpLineEdit releaseDateLineEdit creatorLineEdit notesPlainTextEdit ym2608RadioButton ym2612RadioButton ym2203RadioButton noneFmRadioButton internalSsgRadioButton ay8910PsgRadioButton ym2149PsgRadioButton buttonBox accepted() VgmExportSettingsDialog accept() 248 254 157 274 buttonBox rejected() VgmExportSettingsDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/wave_export_settings_dialog.cpp000066400000000000000000000035051401124043500260360ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "wave_export_settings_dialog.hpp" #include "ui_wave_export_settings_dialog.h" WaveExportSettingsDialog::WaveExportSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::WaveExportSettingsDialog) { ui->setupUi(this); setWindowFlags(windowFlags() ^ Qt::WindowContextHelpButtonHint); ui->sampleRateComboBox->addItem("44100Hz", 44100); ui->sampleRateComboBox->addItem("48000Hz", 48000); ui->sampleRateComboBox->addItem("55466Hz", 55466); } WaveExportSettingsDialog::~WaveExportSettingsDialog() { delete ui; } int WaveExportSettingsDialog::getSampleRate() const { return ui->sampleRateComboBox->currentData().toInt(); } int WaveExportSettingsDialog::getLoopCount() const { return ui->loopSpinBox->value(); } BambooTracker-0.4.6/BambooTracker/gui/wave_export_settings_dialog.hpp000066400000000000000000000030631401124043500260420ustar00rootroot00000000000000/* * Copyright (C) 2018-2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef WAVE_EXPORT_SETTINGS_DIALOG_HPP #define WAVE_EXPORT_SETTINGS_DIALOG_HPP #include namespace Ui { class WaveExportSettingsDialog; } class WaveExportSettingsDialog : public QDialog { Q_OBJECT public: explicit WaveExportSettingsDialog(QWidget *parent = nullptr); ~WaveExportSettingsDialog(); int getSampleRate() const; int getLoopCount() const; private: Ui::WaveExportSettingsDialog *ui; }; #endif // WAVE_EXPORT_SETTINGS_DIALOG_HPP BambooTracker-0.4.6/BambooTracker/gui/wave_export_settings_dialog.ui000066400000000000000000000043031401124043500256660ustar00rootroot00000000000000 WaveExportSettingsDialog 0 0 180 100 WAV export settings Sample rate Loop 1 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok sampleRateComboBox loopSpinBox buttonBox accepted() WaveExportSettingsDialog accept() 248 254 157 274 buttonBox rejected() WaveExportSettingsDialog reject() 316 260 286 274 BambooTracker-0.4.6/BambooTracker/gui/wave_visual.cpp000066400000000000000000000054521401124043500225640ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "gui/wave_visual.hpp" #include "gui/color_palette.hpp" #include #include //Xcode 8.3: "no member names 'abs' in namespace 'std' #include WaveVisual::WaveVisual(QWidget *parent) : QWidget(parent) { setAttribute(Qt::WA_OpaquePaintEvent); } void WaveVisual::setColorPalette(std::shared_ptr palette) { palette_ = palette; } void WaveVisual::setStereoSamples(const int16_t *buffer, size_t frames) { // identify the highest of the 2 input signals int32_t sum = 0; for (size_t i = 0; i < frames; ++i) { size_t p = i << 1; sum += std::abs(buffer[p]); sum -= std::abs(buffer[p + 1]); } // use this signal as display data samples_.resize(frames); int16_t *samples = samples_.data(); for (size_t i = 0; i < frames; ++i) samples[i] = buffer[(i << 1) + (sum < 0)]; repaint(); } void WaveVisual::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QPainter painter(this); if (!palette_) return; int w = width(); int h = height(); int centerh = h >> 1; painter.fillRect(0, 0, w, h, palette_->wavBackColor); const int16_t *samples = samples_.data(); size_t frames = samples_.size(); if (frames <= 0) return; painter.setPen(palette_->wavDrawColor); int lastY = centerh; const int16_t range = std::numeric_limits::max() >> 1; for (int x = 0; x < w; ++x) { size_t index = (size_t)(x * ((double)frames / w)); int16_t sample = samples[index]; int y = centerh - (centerh * sample / range); painter.drawPoint(x, y); // draw intermediate points int y1 = lastY, y2 = y; int yM = (y1 + y2) >> 1; while (y1 != y2) { bool b = (y1 > yM) ^ (y1 > y2); painter.drawPoint(x - !b, y1); y1 += (y1 < y2) ? 1 : -1; } lastY = y; } } BambooTracker-0.4.6/BambooTracker/gui/wave_visual.hpp000066400000000000000000000032021401124043500225600ustar00rootroot00000000000000/* * Copyright (C) 2019 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #ifndef WAVE_VISUAL_HPP #define WAVE_VISUAL_HPP #include #include #include #include class ColorPalette; class WaveVisual : public QWidget { Q_OBJECT public: explicit WaveVisual(QWidget *parent = nullptr); void setColorPalette(std::shared_ptr palette); void setStereoSamples(const int16_t *buffer, size_t frames); protected: void paintEvent(QPaintEvent *event) override; private: std::shared_ptr palette_; std::vector samples_; }; #endif // WAVE_VISUAL_HPP BambooTracker-0.4.6/BambooTracker/instrument/000077500000000000000000000000001401124043500211515ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/instrument/abstract_instrument_property.cpp000066400000000000000000000032071401124043500277160ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "abstract_instrument_property.hpp" AbstractInstrumentProperty::AbstractInstrumentProperty(int num) : num_(num) { } void AbstractInstrumentProperty::registerUserInstrument(int instNum) { users_.insert(instNum); } void AbstractInstrumentProperty::deregisterUserInstrument(int instNum) { auto&& it = users_.find(instNum); if (it != users_.end()) users_.erase(it); } bool AbstractInstrumentProperty::isUserInstrument() const { return !users_.empty(); } void AbstractInstrumentProperty::clearUserInstruments() { users_.clear(); } BambooTracker-0.4.6/BambooTracker/instrument/abstract_instrument_property.hpp000066400000000000000000000033651401124043500277300ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include class AbstractInstrumentProperty { public: virtual ~AbstractInstrumentProperty() = default; inline void setNumber(int num) noexcept { num_ = num; } inline int getNumber() const noexcept { return num_; } void registerUserInstrument(int instNum); void deregisterUserInstrument(int instNum); bool isUserInstrument() const; inline std::multiset getUserInstruments() const noexcept { return users_; } void clearUserInstruments(); virtual bool isEdited() const = 0; virtual void clearParameters() = 0; protected: explicit AbstractInstrumentProperty(int num); private: int num_; std::multiset users_; }; BambooTracker-0.4.6/BambooTracker/instrument/bank.cpp000066400000000000000000000216011401124043500225700ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "bank.hpp" #include #include "io/instrument_io.hpp" #include "io/opni_io.hpp" #include "io/btb_io.hpp" #include "io/ff_io.hpp" #include "io/ppc_io.hpp" #include "io/p86_io.hpp" #include "io/pps_io.hpp" #include "io/pvi_io.hpp" #include "io/pzi_io.hpp" #include "io/dat_io.hpp" #include "format/wopn_file.h" BtBank::BtBank(const std::vector& ids, const std::vector& names) : ids_(ids), names_(names) { } BtBank::BtBank(const std::vector& ids, const std::vector& names, const std::vector& instSecs, const io::BinaryContainer& propSec, uint32_t version) : instCtrs_(instSecs), propCtr_(propSec), ids_(ids), names_(names), version_(version) { } size_t BtBank::getNumInstruments() const { return ids_.size(); } std::string BtBank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string BtBank::getInstrumentName(size_t index) const { return names_.at(index); } AbstractInstrument* BtBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::BtbIO::loadInstrument(instCtrs_.at(static_cast(index)), propCtr_, instMan, instNum, version_); } /******************************/ void WopnBank::WOPNDeleter::operator()(WOPNFile *x) { WOPN_Free(x); } struct WopnBank::InstEntry { WOPNInstrument *inst; struct ValuesType { bool percussive : 1; unsigned msb : 7; unsigned lsb : 7; unsigned nth : 7; } vals; }; WopnBank::WopnBank(WOPNFile* wopn) : wopn_(wopn) { unsigned numM = wopn->banks_count_melodic; unsigned numP = wopn->banks_count_percussion; size_t instMax = 128 * (numP + numM); entries_.reserve(instMax); for (size_t i = 0; i < instMax; ++i) { InstEntry ent; ent.vals.percussive = (i / 128) >= numM; WOPNBank& bank = ent.vals.percussive ? wopn->banks_percussive[(i / 128) - numM] : wopn->banks_melodic[i / 128]; ent.vals.msb = bank.bank_midi_msb; ent.vals.lsb = bank.bank_midi_lsb; ent.vals.nth = i % 128; ent.inst = &bank.ins[ent.vals.nth]; if ((ent.inst->inst_flags & WOPN_Ins_IsBlank) == 0) entries_.push_back(ent); } entries_.shrink_to_fit(); } size_t WopnBank::getNumInstruments() const { return entries_.size(); } std::string WopnBank::getInstrumentIdentifier(size_t index) const { const InstEntry& ent = entries_.at(index); char identifier[64]; std::sprintf(identifier, "%c%03d:%03d:%03d", "MP"[ent.vals.percussive], ent.vals.msb, ent.vals.lsb, ent.vals.nth); return identifier; } std::string WopnBank::getInstrumentName(size_t index) const { const InstEntry& ent = entries_.at(index); return ent.inst->inst_name; } AbstractInstrument* WopnBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { const InstEntry& ent = entries_.at(index); return io::OpniIO::loadWOPNInstrument(*ent.inst, instMan, instNum); } /******************************/ FfBank::FfBank(const std::vector& ids, const std::vector& names, const std::vector& ctrs) : ids_(ids), names_(names), instCtrs_(ctrs) { } size_t FfBank::getNumInstruments() const { return ids_.size(); } std::string FfBank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string FfBank::getInstrumentName(size_t index) const { return names_.at(index); } AbstractInstrument* FfBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::FfIO::loadInstrument(instCtrs_.at(index), names_.at(index), instMan, instNum); } void FfBank::setInstrumentName(size_t index, const std::string& name) { names_.at(index) = name; } /******************************/ PpcBank::PpcBank(const std::vector& ids, const std::vector>& samples) : ids_(ids), samples_(samples) { } size_t PpcBank::getNumInstruments() const { return samples_.size(); } std::string PpcBank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string PpcBank::getInstrumentName(size_t index) const { (void)index; return ""; } AbstractInstrument* PpcBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::PpcIO::loadInstrument(samples_.at(index), instMan, instNum); } /******************************/ P86Bank::P86Bank(const std::vector& ids, const std::vector>& samples) : ids_(ids), samples_(samples) { } size_t P86Bank::getNumInstruments() const { return samples_.size(); } std::string P86Bank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string P86Bank::getInstrumentName(size_t index) const { (void)index; return ""; } AbstractInstrument* P86Bank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::P86IO::loadInstrument(samples_.at(index), instMan, instNum); } /******************************/ PpsBank::PpsBank(const std::vector& ids, const std::vector>& samples) : ids_(ids), samples_(samples) { } size_t PpsBank::getNumInstruments() const { return samples_.size(); } std::string PpsBank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string PpsBank::getInstrumentName(size_t index) const { (void)index; return ""; } AbstractInstrument* PpsBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::PpsIO::loadInstrument(samples_.at(index), instMan, instNum); } /******************************/ PviBank::PviBank(const std::vector& ids, uint16_t deltaN, const std::vector>& samples) : ids_(ids), deltaN_(deltaN), samples_(samples) { } size_t PviBank::getNumInstruments() const { return samples_.size(); } std::string PviBank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string PviBank::getInstrumentName(size_t index) const { (void)index; return ""; } AbstractInstrument* PviBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::PviIO::loadInstrument(samples_.at(index), deltaN_, instMan, instNum); } /******************************/ PziBank::PziBank(const std::vector& ids, const std::vector& deltaNs, const std::vector& isRepeatedList, const std::vector>& samples) : ids_(ids), deltaNs_(deltaNs), isRepeatedList_(isRepeatedList), samples_(samples) { } size_t PziBank::getNumInstruments() const { return samples_.size(); } std::string PziBank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string PziBank::getInstrumentName(size_t index) const { (void)index; return ""; } AbstractInstrument* PziBank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::PziIO::loadInstrument(samples_.at(index), deltaNs_.at(index), isRepeatedList_.at(index), instMan, instNum); } /******************************/ Mucom88Bank::Mucom88Bank(const std::vector& ids, const std::vector& names, const std::vector& ctrs) : ids_(ids), names_(names), instCtrs_(ctrs) { } size_t Mucom88Bank::getNumInstruments() const { return ids_.size(); } std::string Mucom88Bank::getInstrumentIdentifier(size_t index) const { return std::to_string(ids_.at(index)); } std::string Mucom88Bank::getInstrumentName(size_t index) const { return names_.at(index); } AbstractInstrument* Mucom88Bank::loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const { return io::DatIO::loadInstrument(instCtrs_.at(index), names_.at(index), instMan, instNum); } void Mucom88Bank::setInstrumentName(size_t index, const std::string& name) { names_.at(index) = name; } BambooTracker-0.4.6/BambooTracker/instrument/bank.hpp000066400000000000000000000156651401124043500226120ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "io/binary_container.hpp" class AbstractInstrument; class InstrumentsManager; struct WOPNFile; class AbstractBank { public: virtual ~AbstractBank() = default; virtual size_t getNumInstruments() const = 0; virtual std::string getInstrumentIdentifier(size_t index) const = 0; virtual std::string getInstrumentName(size_t index) const = 0; virtual AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const = 0; }; class BtBank final : public AbstractBank { public: BtBank(const std::vector& ids, const std::vector& names); BtBank(const std::vector& ids, const std::vector& names, const std::vector& instSecs, const io::BinaryContainer& propSec, uint32_t version); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; private: std::vector instCtrs_; io::BinaryContainer propCtr_; std::vector ids_; std::vector names_; uint32_t version_; }; class WopnBank final : public AbstractBank { public: explicit WopnBank(WOPNFile *wopn); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; private: struct WOPNDeleter { void operator()(WOPNFile *x); }; std::unique_ptr wopn_; struct InstEntry; std::vector entries_; }; class FfBank final : public AbstractBank { public: FfBank(const std::vector& ids, const std::vector& names, const std::vector& ctrs); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; void setInstrumentName(size_t index, const std::string& name); private: std::vector ids_; std::vector names_; std::vector instCtrs_; }; class PpcBank final : public AbstractBank { public: PpcBank(const std::vector& ids, const std::vector>& samples); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; private: std::vector ids_; std::vector> samples_; }; class P86Bank final : public AbstractBank { public: P86Bank(const std::vector& ids, const std::vector>& samples); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; private: std::vector ids_; std::vector> samples_; }; class PpsBank final : public AbstractBank { public: PpsBank(const std::vector& ids, const std::vector>& samples); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; private: std::vector ids_; std::vector> samples_; }; class PviBank final : public AbstractBank { public: PviBank(const std::vector& ids, uint16_t deltaN, const std::vector>& samples); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; private: std::vector ids_; uint16_t deltaN_; std::vector> samples_; }; class PziBank final : public AbstractBank { public: PziBank(const std::vector& ids, const std::vector& deltaNs, const std::vector& isRepeatedList, const std::vector>& samples); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; private: std::vector ids_; std::vector deltaNs_; std::vector isRepeatedList_; std::vector> samples_; }; class Mucom88Bank final : public AbstractBank { public: Mucom88Bank(const std::vector& ids, const std::vector& names, const std::vector& ctrs); size_t getNumInstruments() const override; std::string getInstrumentIdentifier(size_t index) const override; std::string getInstrumentName(size_t index) const override; AbstractInstrument* loadInstrument(size_t index, std::weak_ptr instMan, int instNum) const override; void setInstrumentName(size_t index, const std::string& name); private: std::vector ids_; std::vector names_; std::vector instCtrs_; }; BambooTracker-0.4.6/BambooTracker/instrument/effect_iterator.cpp000066400000000000000000000074451401124043500250340ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "effect_iterator.hpp" #include "note.hpp" #include namespace { const InstrumentSequenceBaseUnit ARP_CENTER(Note::DEFAULT_NOTE_NUM); } ArpeggioEffectIterator::ArpeggioEffectIterator(int second, int third) : SequenceIteratorInterface(2), started_(false), second_(second + ARP_CENTER.data), third_(third + ARP_CENTER.data) { } InstrumentSequenceBaseUnit ArpeggioEffectIterator::data() const noexcept { switch (pos_) { case 0: return ARP_CENTER; case 1: return second_; case 2: return third_; default: return InstrumentSequenceBaseUnit(); } } int ArpeggioEffectIterator::next() { if (started_) { pos_ = (pos_ + 1) % 3; } else { started_ = true; } return pos_; } int ArpeggioEffectIterator::front() { pos_ = 0; started_ = true; return 0; } int ArpeggioEffectIterator::end() { pos_ = END_SEQ_POS; started_ = false; return END_SEQ_POS; } WavingEffectIterator::WavingEffectIterator(int period, int depth) : started_(false) { for (int i = 0; i <= period; ++i) { seq_.emplace_back(i * depth); } for (size_t i = static_cast(period - 1); i > 0; --i) { seq_.push_back(seq_.at(i)); } size_t p2 = static_cast(period) << 1; for (size_t i = 0; i < p2; ++i) { seq_.emplace_back(-seq_.at(i).data); } pos_ = static_cast(seq_.size()) - 1; } InstrumentSequenceBaseUnit WavingEffectIterator::data() const { return (hasEnded() ? InstrumentSequenceBaseUnit() : seq_.at(static_cast(pos_))); } int WavingEffectIterator::next() { if (started_) { pos_ = (pos_ + 1) % static_cast(seq_.size()); } else { started_ = true; } return pos_; } int WavingEffectIterator::front() { pos_ = 0; started_ = true; return 0; } int WavingEffectIterator::end() { pos_ = END_SEQ_POS; started_ = false; return END_SEQ_POS; } NoteSlideEffectIterator::NoteSlideEffectIterator(int speed, int seminote) : SequenceIteratorInterface(0), started_(false) { int d = seminote * Note::SEMINOTE_PITCH; if (speed) { int prev = 0; for (int i = 0; i <= speed; ++i) { int dif = d * i / speed - prev; seq_.emplace_back(dif); prev += dif; } } else { seq_.emplace_back(d); } } InstrumentSequenceBaseUnit NoteSlideEffectIterator::data() const { return (hasEnded() ? InstrumentSequenceBaseUnit() : seq_.at(static_cast(pos_))); } int NoteSlideEffectIterator::next() { if (started_ && !hasEnded()) { if (++pos_ >= static_cast(seq_.size())) pos_ = END_SEQ_POS; } else { started_ = true; } return pos_; } int NoteSlideEffectIterator::front() { pos_ = 0; started_ = true; return 0; } int NoteSlideEffectIterator::end() { pos_ = END_SEQ_POS; started_ = false; return END_SEQ_POS; } BambooTracker-0.4.6/BambooTracker/instrument/effect_iterator.hpp000066400000000000000000000052041401124043500250300ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "sequence_iterator_interface.hpp" #include "sequence_property.hpp" class ArpeggioEffectIterator : public SequenceIteratorInterface { public: ArpeggioEffectIterator(int second, int third); SequenceType type() const noexcept override { return SequenceType::AbsoluteSequence; } InstrumentSequenceBaseUnit data() const noexcept override; int next() override; int front() override; inline int release() override { return next(); } int end() override; private: bool started_; InstrumentSequenceBaseUnit second_, third_; }; class WavingEffectIterator : public SequenceIteratorInterface { public: WavingEffectIterator(int period, int depth); SequenceType type() const noexcept override { return SequenceType::AbsoluteSequence; } InstrumentSequenceBaseUnit data() const override; int next() override; int front() override; inline int release() override { return next(); } int end() override; private: bool started_; std::vector seq_; }; class NoteSlideEffectIterator : public SequenceIteratorInterface { public: NoteSlideEffectIterator(int speed, int seminote); SequenceType type() const noexcept override { return SequenceType::AbsoluteSequence; } InstrumentSequenceBaseUnit data() const override; int next() override; int front() override; inline int release() override { return next(); } int end() override; private: bool started_; std::vector seq_; }; BambooTracker-0.4.6/BambooTracker/instrument/envelope_fm.cpp000066400000000000000000000066771401124043500241740ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "envelope_fm.hpp" namespace { const std::unordered_map DEF_PARAMS = { { FMEnvelopeParameter::AL, 4 }, { FMEnvelopeParameter::FB, 0 }, { FMEnvelopeParameter::AR1, 31 }, { FMEnvelopeParameter::DR1, 0 }, { FMEnvelopeParameter::SR1, 0 }, { FMEnvelopeParameter::RR1, 7 }, { FMEnvelopeParameter::SL1, 0 }, { FMEnvelopeParameter::TL1, 32 }, { FMEnvelopeParameter::KS1, 0 }, { FMEnvelopeParameter::ML1, 0 }, { FMEnvelopeParameter::DT1, 0 }, { FMEnvelopeParameter::SSGEG1, -1 }, { FMEnvelopeParameter::AR2, 31 }, { FMEnvelopeParameter::DR2, 0 }, { FMEnvelopeParameter::SR2, 0 }, { FMEnvelopeParameter::RR2, 7 }, { FMEnvelopeParameter::SL2, 0 }, { FMEnvelopeParameter::TL2, 0 }, { FMEnvelopeParameter::KS2, 0 }, { FMEnvelopeParameter::ML2, 0 }, { FMEnvelopeParameter::DT2, 0 }, { FMEnvelopeParameter::SSGEG2, -1 }, { FMEnvelopeParameter::AR3, 31 }, { FMEnvelopeParameter::DR3, 0 }, { FMEnvelopeParameter::SR3, 0 }, { FMEnvelopeParameter::RR3, 7 }, { FMEnvelopeParameter::SL3, 0 }, { FMEnvelopeParameter::TL3, 32 }, { FMEnvelopeParameter::KS3, 0 }, { FMEnvelopeParameter::ML3, 0 }, { FMEnvelopeParameter::DT3, 0 }, { FMEnvelopeParameter::SSGEG3, -1 }, { FMEnvelopeParameter::AR4, 31 }, { FMEnvelopeParameter::DR4, 0 }, { FMEnvelopeParameter::SR4, 0 }, { FMEnvelopeParameter::RR4, 7 }, { FMEnvelopeParameter::SL4, 0 }, { FMEnvelopeParameter::TL4, 0 }, { FMEnvelopeParameter::KS4, 0 }, { FMEnvelopeParameter::ML4, 0 }, { FMEnvelopeParameter::DT4, 0 }, { FMEnvelopeParameter::SSGEG4, -1 } }; } EnvelopeFM::EnvelopeFM(int num) : AbstractInstrumentProperty(num) { clearParameters(); } std::unique_ptr EnvelopeFM::clone() { std::unique_ptr clone = std::make_unique(*this); clone->clearUserInstruments(); return clone; } bool EnvelopeFM::getOperatorEnabled(int num) const { return isEnabledOp_.test(num); } void EnvelopeFM::setOperatorEnabled(int num, bool enabled) { isEnabledOp_.set(num, enabled); } int EnvelopeFM::getParameterValue(FMEnvelopeParameter param) const { return params_.at(param); } void EnvelopeFM::setParameterValue(FMEnvelopeParameter param, int value) { params_.at(param) = value; } bool EnvelopeFM::isEdited() const { return (params_ != DEF_PARAMS || !isEnabledOp_.all()); } void EnvelopeFM::clearParameters() { params_ = DEF_PARAMS; isEnabledOp_.set(); } BambooTracker-0.4.6/BambooTracker/instrument/envelope_fm.hpp000066400000000000000000000043521401124043500241650ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "abstract_instrument_property.hpp" #include "enum_hash.hpp" enum class FMEnvelopeParameter { AL, FB, AR1, DR1, SR1, RR1, SL1, TL1, KS1, ML1, DT1, AR2, DR2, SR2, RR2, SL2, TL2, KS2, ML2, DT2, AR3, DR3, SR3, RR3, SL3, TL3, KS3, ML3, DT3, AR4, DR4, SR4, RR4, SL4, TL4, KS4, ML4, DT4, SSGEG1, SSGEG2, SSGEG3, SSGEG4 }; class EnvelopeFM final : public AbstractInstrumentProperty { public: explicit EnvelopeFM(int num); friend bool operator==(const EnvelopeFM& a, const EnvelopeFM& b) { return (a.params_ == b.params_ && a.isEnabledOp_ == b.isEnabledOp_); } friend bool operator!=(const EnvelopeFM& a, const EnvelopeFM& b) { return !(a == b); } std::unique_ptr clone(); bool getOperatorEnabled(int num) const; void setOperatorEnabled(int num, bool enabled); int getParameterValue(FMEnvelopeParameter param) const; void setParameterValue(FMEnvelopeParameter param, int value); bool isEdited() const override; void clearParameters() override; private: std::unordered_map params_; std::bitset<4> isEnabledOp_; }; BambooTracker-0.4.6/BambooTracker/instrument/instrument.cpp000066400000000000000000000415761401124043500241020ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument.hpp" #include "instruments_manager.hpp" #include "utils.hpp" AbstractInstrument::AbstractInstrument(int number, SoundSource src, InstrumentType type, const std::string& name, InstrumentsManager* owner) : owner_(owner), number_(number), name_(name), sndSrc_(src), instType_(type) { } bool AbstractInstrument::isRegisteredWithManager() const { return (this == owner_->getInstrumentSharedPtr(number_).get()); } /****************************************/ InstrumentFM::InstrumentFM(int number, const std::string& name, InstrumentsManager* owner) : AbstractInstrument(number, SoundSource::FM, InstrumentType::FM, name, owner), envNum_(0), lfoEnabled_(false), lfoNum_(0) { opSeqEnabled_ = { { FMEnvelopeParameter::AL, false }, { FMEnvelopeParameter::FB, false }, { FMEnvelopeParameter::AR1, false }, { FMEnvelopeParameter::DR1, false }, { FMEnvelopeParameter::SR1, false }, { FMEnvelopeParameter::RR1, false }, { FMEnvelopeParameter::SL1, false }, { FMEnvelopeParameter::TL1, false }, { FMEnvelopeParameter::KS1, false }, { FMEnvelopeParameter::ML1, false }, { FMEnvelopeParameter::DT1, false }, { FMEnvelopeParameter::AR2, false }, { FMEnvelopeParameter::DR2, false }, { FMEnvelopeParameter::SR2, false }, { FMEnvelopeParameter::RR2, false }, { FMEnvelopeParameter::SL2, false }, { FMEnvelopeParameter::TL2, false }, { FMEnvelopeParameter::KS2, false }, { FMEnvelopeParameter::ML2, false }, { FMEnvelopeParameter::DT2, false }, { FMEnvelopeParameter::AR3, false }, { FMEnvelopeParameter::DR3, false }, { FMEnvelopeParameter::SR3, false }, { FMEnvelopeParameter::RR3, false }, { FMEnvelopeParameter::SL3, false }, { FMEnvelopeParameter::TL3, false }, { FMEnvelopeParameter::KS3, false }, { FMEnvelopeParameter::ML3, false }, { FMEnvelopeParameter::DT3, false }, { FMEnvelopeParameter::AR4, false }, { FMEnvelopeParameter::DR4, false }, { FMEnvelopeParameter::SR4, false }, { FMEnvelopeParameter::RR4, false }, { FMEnvelopeParameter::SL4, false }, { FMEnvelopeParameter::TL4, false }, { FMEnvelopeParameter::KS4, false }, { FMEnvelopeParameter::ML4, false }, { FMEnvelopeParameter::DT4, false } }; opSeqNum_ = { { FMEnvelopeParameter::AL, 0 }, { FMEnvelopeParameter::FB, 0 }, { FMEnvelopeParameter::AR1, 0 }, { FMEnvelopeParameter::DR1, 0 }, { FMEnvelopeParameter::SR1, 0 }, { FMEnvelopeParameter::RR1, 0 }, { FMEnvelopeParameter::SL1, 0 }, { FMEnvelopeParameter::TL1, 0 }, { FMEnvelopeParameter::KS1, 0 }, { FMEnvelopeParameter::ML1, 0 }, { FMEnvelopeParameter::DT1, 0 }, { FMEnvelopeParameter::AR2, 0 }, { FMEnvelopeParameter::DR2, 0 }, { FMEnvelopeParameter::SR2, 0 }, { FMEnvelopeParameter::RR2, 0 }, { FMEnvelopeParameter::SL2, 0 }, { FMEnvelopeParameter::TL2, 0 }, { FMEnvelopeParameter::KS2, 0 }, { FMEnvelopeParameter::ML2, 0 }, { FMEnvelopeParameter::DT2, 0 }, { FMEnvelopeParameter::AR3, 0 }, { FMEnvelopeParameter::DR3, 0 }, { FMEnvelopeParameter::SR3, 0 }, { FMEnvelopeParameter::RR3, 0 }, { FMEnvelopeParameter::SL3, 0 }, { FMEnvelopeParameter::TL3, 0 }, { FMEnvelopeParameter::KS3, 0 }, { FMEnvelopeParameter::ML3, 0 }, { FMEnvelopeParameter::DT3, 0 }, { FMEnvelopeParameter::AR4, 0 }, { FMEnvelopeParameter::DR4, 0 }, { FMEnvelopeParameter::SR4, 0 }, { FMEnvelopeParameter::RR4, 0 }, { FMEnvelopeParameter::SL4, 0 }, { FMEnvelopeParameter::TL4, 0 }, { FMEnvelopeParameter::KS4, 0 }, { FMEnvelopeParameter::ML4, 0 }, { FMEnvelopeParameter::DT4, 0 } }; arpEnabled_ = { { FMOperatorType::All, false }, { FMOperatorType::Op1, false }, { FMOperatorType::Op2, false }, { FMOperatorType::Op3, false }, { FMOperatorType::Op4, false } }; arpNum_ = { { FMOperatorType::All, 0 }, { FMOperatorType::Op1, 0 }, { FMOperatorType::Op2, 0 }, { FMOperatorType::Op3, 0 }, { FMOperatorType::Op4, 0 } }; ptEnabled_ = { { FMOperatorType::All, false }, { FMOperatorType::Op1, false }, { FMOperatorType::Op2, false }, { FMOperatorType::Op3, false }, { FMOperatorType::Op4, false } }; ptNum_ = { { FMOperatorType::All, 0 }, { FMOperatorType::Op1, 0 }, { FMOperatorType::Op2, 0 }, { FMOperatorType::Op3, 0 }, { FMOperatorType::Op4, 0 } }; envResetEnabled_ = { { FMOperatorType::All, false }, { FMOperatorType::Op1, false }, { FMOperatorType::Op2, false }, { FMOperatorType::Op3, false }, { FMOperatorType::Op4, false } }; } AbstractInstrument* InstrumentFM::clone() { return new InstrumentFM(*this); } int InstrumentFM::getEnvelopeParameter(FMEnvelopeParameter param) const { return owner_->getEnvelopeFMParameter(envNum_, param); } bool InstrumentFM::getOperatorEnabled(int n) const { return owner_->getEnvelopeFMOperatorEnabled(envNum_, n); } int InstrumentFM::getLFOParameter(FMLFOParameter param) const { return owner_->getLFOFMparameter(lfoNum_, param); } void InstrumentFM::setOperatorSequenceEnabled(FMEnvelopeParameter param, bool enabled) { opSeqEnabled_.at(param) = enabled; } bool InstrumentFM::getOperatorSequenceEnabled(FMEnvelopeParameter param) const { return opSeqEnabled_.at(param); } void InstrumentFM::setOperatorSequenceNumber(FMEnvelopeParameter param, int n) { opSeqNum_.at(param) = n; } int InstrumentFM::getOperatorSequenceNumber(FMEnvelopeParameter param) const { return opSeqNum_.at(param); } std::vector InstrumentFM::getOperatorSequenceSequence(FMEnvelopeParameter param) const { return owner_->getOperatorSequenceFMSequence(param, opSeqNum_.at(param)); } InstrumentSequenceLoopRoot InstrumentFM::getOperatorSequenceLoopRoot(FMEnvelopeParameter param) const { return owner_->getOperatorSequenceFMLoopRoot(param, opSeqNum_.at(param)); } InstrumentSequenceRelease InstrumentFM::getOperatorSequenceRelease(FMEnvelopeParameter param) const { return owner_->getOperatorSequenceFMRelease(param, opSeqNum_.at(param)); } FMOperatorSequenceIter InstrumentFM::getOperatorSequenceSequenceIterator(FMEnvelopeParameter param) const { return owner_->getOperatorSequenceFMIterator(param, opSeqNum_.at(param)); } void InstrumentFM::setArpeggioEnabled(FMOperatorType op, bool enabled) { arpEnabled_.at(op) = enabled; } bool InstrumentFM::getArpeggioEnabled(FMOperatorType op) const { return arpEnabled_.at(op); } void InstrumentFM::setArpeggioNumber(FMOperatorType op, int n) { arpNum_.at(op) = n; } int InstrumentFM::getArpeggioNumber(FMOperatorType op) const { return arpNum_.at(op); } SequenceType InstrumentFM::getArpeggioType(FMOperatorType op) const { return owner_->getArpeggioFMType(arpNum_.at(op)); } std::vector InstrumentFM::getArpeggioSequence(FMOperatorType op) const { return owner_->getArpeggioFMSequence(arpNum_.at(op)); } InstrumentSequenceLoopRoot InstrumentFM::getArpeggioLoopRoot(FMOperatorType op) const { return owner_->getArpeggioFMLoopRoot(arpNum_.at(op)); } InstrumentSequenceRelease InstrumentFM::getArpeggioRelease(FMOperatorType op) const { return owner_->getArpeggioFMRelease(arpNum_.at(op)); } ArpeggioIter InstrumentFM::getArpeggioSequenceIterator(FMOperatorType op) const { return owner_->getArpeggioFMIterator(arpNum_.at(op)); } void InstrumentFM::setPitchEnabled(FMOperatorType op, bool enabled) { ptEnabled_.at(op) = enabled; } bool InstrumentFM::getPitchEnabled(FMOperatorType op) const { return ptEnabled_.at(op); } void InstrumentFM::setPitchNumber(FMOperatorType op, int n) { ptNum_.at(op) = n; } int InstrumentFM::getPitchNumber(FMOperatorType op) const { return ptNum_.at(op); } SequenceType InstrumentFM::getPitchType(FMOperatorType op) const { return owner_->getPitchFMType(ptNum_.at(op)); } std::vector InstrumentFM::getPitchSequence(FMOperatorType op) const { return owner_->getPitchFMSequence(ptNum_.at(op)); } InstrumentSequenceLoopRoot InstrumentFM::getPitchLoopRoot(FMOperatorType op) const { return owner_->getPitchFMLoopRoot(ptNum_.at(op)); } InstrumentSequenceRelease InstrumentFM::getPitchRelease(FMOperatorType op) const { return owner_->getPitchFMRelease(ptNum_.at(op)); } PitchIter InstrumentFM::getPitchSequenceIterator(FMOperatorType op) const { return owner_->getPitchFMIterator(ptNum_.at(op)); } void InstrumentFM::setEnvelopeResetEnabled(FMOperatorType op, bool enabled) { envResetEnabled_.at(op) = enabled; } bool InstrumentFM::getEnvelopeResetEnabled(FMOperatorType op) const { return envResetEnabled_.at(op); } /****************************************/ InstrumentSSG::InstrumentSSG(int number, const std::string& name, InstrumentsManager* owner) : AbstractInstrument(number, SoundSource::SSG, InstrumentType::SSG, name, owner), wfEnabled_(false), wfNum_(0), tnEnabled_(false), tnNum_(0), envEnabled_(false), envNum_(0), arpEnabled_(false), arpNum_(0), ptEnabled_(false), ptNum_(0) { } AbstractInstrument* InstrumentSSG::clone() { return new InstrumentSSG(*this); } std::vector InstrumentSSG::getWaveformSequence() const { return owner_->getWaveformSSGSequence(wfNum_); } InstrumentSequenceLoopRoot InstrumentSSG::getWaveformLoopRoot() const { return owner_->getWaveformSSGLoopRoot(wfNum_); } InstrumentSequenceRelease InstrumentSSG::getWaveformRelease() const { return owner_->getWaveformSSGRelease(wfNum_); } SSGWaveformIter InstrumentSSG::getWaveformSequenceIterator() const { return owner_->getWaveformSSGIterator(wfNum_); } std::vector InstrumentSSG::getToneNoiseSequence() const { return owner_->getToneNoiseSSGSequence(tnNum_); } InstrumentSequenceLoopRoot InstrumentSSG::getToneNoiseLoopRoot() const { return owner_->getToneNoiseSSGLoopRoot(tnNum_); } InstrumentSequenceRelease InstrumentSSG::getToneNoiseRelease() const { return owner_->getToneNoiseSSGRelease(tnNum_); } SSGToneNoiseIter InstrumentSSG::getToneNoiseSequenceIterator() const { return owner_->getToneNoiseSSGIterator(tnNum_); } std::vector InstrumentSSG::getEnvelopeSequence() const { return owner_->getEnvelopeSSGSequence(envNum_); } InstrumentSequenceLoopRoot InstrumentSSG::getEnvelopeLoopRoot() const { return owner_->getEnvelopeSSGLoopRoot(envNum_); } InstrumentSequenceRelease InstrumentSSG::getEnvelopeRelease() const { return owner_->getEnvelopeSSGRelease(envNum_); } SSGEnvelopeIter InstrumentSSG::getEnvelopeSequenceIterator() const { return owner_->getEnvelopeSSGIterator(envNum_); } SequenceType InstrumentSSG::getArpeggioType() const { return owner_->getArpeggioSSGType(arpNum_); } std::vector InstrumentSSG::getArpeggioSequence() const { return owner_->getArpeggioSSGSequence(arpNum_); } InstrumentSequenceLoopRoot InstrumentSSG::getArpeggioLoopRoot() const { return owner_->getArpeggioSSGLoopRoot(arpNum_); } InstrumentSequenceRelease InstrumentSSG::getArpeggioRelease() const { return owner_->getArpeggioSSGRelease(arpNum_); } ArpeggioIter InstrumentSSG::getArpeggioSequenceIterator() const { return owner_->getArpeggioSSGIterator(arpNum_); } SequenceType InstrumentSSG::getPitchType() const { return owner_->getPitchSSGType(ptNum_); } std::vector InstrumentSSG::getPitchSequence() const { return owner_->getPitchSSGSequence(ptNum_); } InstrumentSequenceLoopRoot InstrumentSSG::getPitchLoopRoot() const { return owner_->getPitchSSGLoopRoot(ptNum_); } InstrumentSequenceRelease InstrumentSSG::getPitchRelease() const { return owner_->getPitchSSGRelease(ptNum_); } PitchIter InstrumentSSG::getPitchSequenceIterator() const { return owner_->getPitchSSGIterator(ptNum_); } /****************************************/ InstrumentADPCM::InstrumentADPCM(int number, const std::string& name, InstrumentsManager* owner) : AbstractInstrument(number, SoundSource::ADPCM, InstrumentType::ADPCM, name, owner), sampNum_(0), envEnabled_(false), envNum_(0), arpEnabled_(false), arpNum_(0), ptEnabled_(false), ptNum_(0) { } AbstractInstrument* InstrumentADPCM::clone() { return new InstrumentADPCM(*this); } int InstrumentADPCM::getSampleRootKeyNumber() const { return owner_->getSampleADPCMRootKeyNumber(sampNum_); } int InstrumentADPCM::getSampleRootDeltaN() const { return owner_->getSampleADPCMRootDeltaN(sampNum_); } bool InstrumentADPCM::isSampleRepeatable() const { return owner_->isSampleADPCMRepeatable(sampNum_); } std::vector InstrumentADPCM::getRawSample() const { return owner_->getSampleADPCMRawSample(sampNum_); } size_t InstrumentADPCM::getSampleStartAddress() const { return owner_->getSampleADPCMStartAddress(sampNum_); } size_t InstrumentADPCM::getSampleStopAddress() const { return owner_->getSampleADPCMStopAddress(sampNum_); } std::vector InstrumentADPCM::getEnvelopeSequence() const { return owner_->getEnvelopeADPCMSequence(envNum_); } InstrumentSequenceLoopRoot InstrumentADPCM::getEnvelopeLoopRoot() const { return owner_->getEnvelopeADPCMLoopRoot(envNum_); } InstrumentSequenceRelease InstrumentADPCM::getEnvelopeRelease() const { return owner_->getEnvelopeADPCMRelease(envNum_); } ADPCMEnvelopeIter InstrumentADPCM::getEnvelopeSequenceIterator() const { return owner_->getEnvelopeADPCMIterator(envNum_); } SequenceType InstrumentADPCM::getArpeggioType() const { return owner_->getArpeggioADPCMType(arpNum_); } std::vector InstrumentADPCM::getArpeggioSequence() const { return owner_->getArpeggioADPCMSequence(arpNum_); } InstrumentSequenceLoopRoot InstrumentADPCM::getArpeggioLoopRoot() const { return owner_->getArpeggioADPCMLoopRoot(arpNum_); } InstrumentSequenceRelease InstrumentADPCM::getArpeggioRelease() const { return owner_->getArpeggioADPCMRelease(arpNum_); } ArpeggioIter InstrumentADPCM::getArpeggioSequenceIterator() const { return owner_->getArpeggioADPCMIterator(arpNum_); } SequenceType InstrumentADPCM::getPitchType() const { return owner_->getPitchADPCMType(ptNum_); } std::vector InstrumentADPCM::getPitchSequence() const { return owner_->getPitchADPCMSequence(ptNum_); } InstrumentSequenceLoopRoot InstrumentADPCM::getPitchLoopRoot() const { return owner_->getPitchADPCMLoopRoot(ptNum_); } InstrumentSequenceRelease InstrumentADPCM::getPitchRelease() const { return owner_->getPitchADPCMRelease(ptNum_); } PitchIter InstrumentADPCM::getPitchSequenceIterator() const { return owner_->getPitchADPCMIterator(ptNum_); } /****************************************/ InstrumentDrumkit::InstrumentDrumkit(int number, const std::string& name, InstrumentsManager* owner) : AbstractInstrument(number, SoundSource::ADPCM, InstrumentType::Drumkit, name, owner) { } AbstractInstrument* InstrumentDrumkit::clone() { return new InstrumentDrumkit(*this); } std::vector InstrumentDrumkit::getAssignedKeys() const { return utils::getMapKeys(kit_); } void InstrumentDrumkit::setSampleEnabled(int key, bool enabled) { if (enabled) kit_[key] = { 0, 0 }; else kit_.erase(key); } bool InstrumentDrumkit::getSampleEnabled(int key) const { return kit_.count(key); } void InstrumentDrumkit::setSampleNumber(int key, int n) { if (kit_.count(key)) kit_.at(key).sampNum = n; } int InstrumentDrumkit::getSampleNumber(int key) const { return kit_.at(key).sampNum; } int InstrumentDrumkit::getSampleRootKeyNumber(int key) const { return owner_->getSampleADPCMRootKeyNumber(kit_.at(key).sampNum); } int InstrumentDrumkit::getSampleRootDeltaN(int key) const { return owner_->getSampleADPCMRootDeltaN(kit_.at(key).sampNum); } bool InstrumentDrumkit::isSampleRepeatable(int key) const { return owner_->isSampleADPCMRepeatable(kit_.at(key).sampNum); } std::vector InstrumentDrumkit::getRawSample(int key) const { return owner_->getSampleADPCMRawSample(kit_.at(key).sampNum); } size_t InstrumentDrumkit::getSampleStartAddress(int key) const { return owner_->getSampleADPCMStartAddress(kit_.at(key).sampNum); } size_t InstrumentDrumkit::getSampleStopAddress(int key) const { return owner_->getSampleADPCMStopAddress(kit_.at(key).sampNum); } void InstrumentDrumkit::setPitch(int key, int pitch) { if (kit_.count(key)) kit_.at(key).pitch = pitch; } int InstrumentDrumkit::getPitch(int key) const { return kit_.at(key).pitch; } BambooTracker-0.4.6/BambooTracker/instrument/instrument.hpp000066400000000000000000000261401401124043500240750ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include "envelope_fm.hpp" #include "lfo_fm.hpp" #include "sequence_property.hpp" #include "instrument_property_defs.hpp" #include "enum_hash.hpp" #include "bamboo_tracker_defs.hpp" class InstrumentsManager; enum class InstrumentType : int { FM, SSG, ADPCM, Drumkit }; class AbstractInstrument { public: virtual ~AbstractInstrument() = default; inline int getNumber() const noexcept { return number_; } inline void setNumber(int n) noexcept { number_ = n; } inline SoundSource getSoundSource() const noexcept { return sndSrc_; } inline InstrumentType getType() const noexcept { return instType_; } inline std::string getName() const noexcept { return name_; } inline void setName(const std::string& name) { name_ = name; } bool isRegisteredWithManager() const; virtual AbstractInstrument* clone() = 0; protected: InstrumentsManager* owner_; int number_; std::string name_; // UTF-8 AbstractInstrument(int number, SoundSource src, InstrumentType type, const std::string& name, InstrumentsManager* owner); private: const SoundSource sndSrc_; const InstrumentType instType_; }; class InstrumentFM final : public AbstractInstrument { public: InstrumentFM(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; inline void setEnvelopeNumber(int n) noexcept { envNum_ = n; } inline int getEnvelopeNumber() const noexcept { return envNum_; } int getEnvelopeParameter(FMEnvelopeParameter param) const; bool getOperatorEnabled(int n) const; inline void setLFOEnabled(bool enabled) noexcept { lfoEnabled_ = enabled; } inline bool getLFOEnabled() const noexcept { return lfoEnabled_; } inline void setLFONumber(int n) noexcept { lfoNum_ = n; } inline int getLFONumber() const noexcept { return lfoNum_; } int getLFOParameter(FMLFOParameter param) const; void setOperatorSequenceEnabled(FMEnvelopeParameter param, bool enabled); bool getOperatorSequenceEnabled(FMEnvelopeParameter param) const; void setOperatorSequenceNumber(FMEnvelopeParameter param, int n); int getOperatorSequenceNumber(FMEnvelopeParameter param) const; std::vector getOperatorSequenceSequence(FMEnvelopeParameter param) const; InstrumentSequenceLoopRoot getOperatorSequenceLoopRoot(FMEnvelopeParameter param) const; InstrumentSequenceRelease getOperatorSequenceRelease(FMEnvelopeParameter param) const; FMOperatorSequenceIter getOperatorSequenceSequenceIterator(FMEnvelopeParameter param) const; void setArpeggioEnabled(FMOperatorType op, bool enabled); bool getArpeggioEnabled(FMOperatorType op) const; void setArpeggioNumber(FMOperatorType op, int n); int getArpeggioNumber(FMOperatorType op) const; SequenceType getArpeggioType(FMOperatorType op) const; std::vector getArpeggioSequence(FMOperatorType op) const; InstrumentSequenceLoopRoot getArpeggioLoopRoot(FMOperatorType op) const; InstrumentSequenceRelease getArpeggioRelease(FMOperatorType op) const; ArpeggioIter getArpeggioSequenceIterator(FMOperatorType op) const; void setPitchEnabled(FMOperatorType op, bool enabled); bool getPitchEnabled(FMOperatorType op) const; void setPitchNumber(FMOperatorType op, int n); int getPitchNumber(FMOperatorType op) const; SequenceType getPitchType(FMOperatorType op) const; std::vector getPitchSequence(FMOperatorType op) const; InstrumentSequenceLoopRoot getPitchLoopRoot(FMOperatorType op) const; InstrumentSequenceRelease getPitchRelease(FMOperatorType op) const; PitchIter getPitchSequenceIterator(FMOperatorType op) const; void setEnvelopeResetEnabled(FMOperatorType op, bool enabled); bool getEnvelopeResetEnabled(FMOperatorType op) const; private: int envNum_; bool lfoEnabled_; int lfoNum_; std::unordered_map opSeqEnabled_; std::unordered_map opSeqNum_; std::unordered_map arpEnabled_; std::unordered_map arpNum_; std::unordered_map ptEnabled_; std::unordered_map ptNum_; std::unordered_map envResetEnabled_; }; class InstrumentSSG final : public AbstractInstrument { public: InstrumentSSG(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; inline void setWaveformEnabled(bool enabled) noexcept { wfEnabled_ = enabled; } inline bool getWaveformEnabled() const noexcept { return wfEnabled_; } inline void setWaveformNumber(int n) noexcept { wfNum_ = n; } inline int getWaveformNumber() const noexcept { return wfNum_; } std::vector getWaveformSequence() const; InstrumentSequenceLoopRoot getWaveformLoopRoot() const; InstrumentSequenceRelease getWaveformRelease() const; SSGWaveformIter getWaveformSequenceIterator() const; inline void setToneNoiseEnabled(bool enabled) noexcept { tnEnabled_ = enabled; } inline bool getToneNoiseEnabled() const noexcept { return tnEnabled_; } inline void setToneNoiseNumber(int n) noexcept { tnNum_ = n; } inline int getToneNoiseNumber() const noexcept { return tnNum_; } std::vector getToneNoiseSequence() const; InstrumentSequenceLoopRoot getToneNoiseLoopRoot() const; InstrumentSequenceRelease getToneNoiseRelease() const; SSGToneNoiseIter getToneNoiseSequenceIterator() const; inline void setEnvelopeEnabled(bool enabled) noexcept { envEnabled_ = enabled; } inline bool getEnvelopeEnabled() const noexcept { return envEnabled_; } inline void setEnvelopeNumber(int n) noexcept { envNum_ = n; } inline int getEnvelopeNumber() const noexcept { return envNum_; } std::vector getEnvelopeSequence() const; InstrumentSequenceLoopRoot getEnvelopeLoopRoot() const; InstrumentSequenceRelease getEnvelopeRelease() const; SSGEnvelopeIter getEnvelopeSequenceIterator() const; inline void setArpeggioEnabled(bool enabled) noexcept { arpEnabled_ = enabled; } inline bool getArpeggioEnabled() const noexcept { return arpEnabled_; } inline void setArpeggioNumber(int n) noexcept { arpNum_ = n; } inline int getArpeggioNumber() const noexcept { return arpNum_; } SequenceType getArpeggioType() const; std::vector getArpeggioSequence() const; InstrumentSequenceLoopRoot getArpeggioLoopRoot() const; InstrumentSequenceRelease getArpeggioRelease() const; ArpeggioIter getArpeggioSequenceIterator() const; inline void setPitchEnabled(bool enabled) noexcept { ptEnabled_ = enabled; } inline bool getPitchEnabled() const noexcept { return ptEnabled_; } inline void setPitchNumber(int n) noexcept { ptNum_ = n; } inline int getPitchNumber() const noexcept { return ptNum_; } SequenceType getPitchType() const; std::vector getPitchSequence() const; InstrumentSequenceLoopRoot getPitchLoopRoot() const; InstrumentSequenceRelease getPitchRelease() const; PitchIter getPitchSequenceIterator() const; private: bool wfEnabled_; int wfNum_; bool tnEnabled_; int tnNum_; bool envEnabled_; int envNum_; bool arpEnabled_; int arpNum_; bool ptEnabled_; int ptNum_; }; class InstrumentADPCM final : public AbstractInstrument { public: InstrumentADPCM(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; inline void setSampleNumber(int n) noexcept { sampNum_ = n; } inline int getSampleNumber() const noexcept { return sampNum_; } int getSampleRootKeyNumber() const; int getSampleRootDeltaN() const; bool isSampleRepeatable() const; std::vector getRawSample() const; size_t getSampleStartAddress() const; size_t getSampleStopAddress() const; inline void setEnvelopeEnabled(bool enabled) noexcept { envEnabled_ = enabled; } inline bool getEnvelopeEnabled() const noexcept { return envEnabled_; } inline void setEnvelopeNumber(int n) noexcept { envNum_ = n; } inline int getEnvelopeNumber() const noexcept { return envNum_; } std::vector getEnvelopeSequence() const; InstrumentSequenceLoopRoot getEnvelopeLoopRoot() const; InstrumentSequenceRelease getEnvelopeRelease() const; ADPCMEnvelopeIter getEnvelopeSequenceIterator() const; inline void setArpeggioEnabled(bool enabled) noexcept { arpEnabled_ = enabled; } inline bool getArpeggioEnabled() const noexcept { return arpEnabled_; } inline void setArpeggioNumber(int n) noexcept { arpNum_ = n; } inline int getArpeggioNumber() const noexcept { return arpNum_; } SequenceType getArpeggioType() const; std::vector getArpeggioSequence() const; InstrumentSequenceLoopRoot getArpeggioLoopRoot() const; InstrumentSequenceRelease getArpeggioRelease() const; ArpeggioIter getArpeggioSequenceIterator() const; inline void setPitchEnabled(bool enabled) noexcept { ptEnabled_ = enabled; } inline bool getPitchEnabled() const noexcept { return ptEnabled_; } inline void setPitchNumber(int n) noexcept { ptNum_ = n; } inline int getPitchNumber() const noexcept { return ptNum_; } SequenceType getPitchType() const; std::vector getPitchSequence() const; InstrumentSequenceLoopRoot getPitchLoopRoot() const; InstrumentSequenceRelease getPitchRelease() const; PitchIter getPitchSequenceIterator() const; private: int sampNum_; bool envEnabled_; int envNum_; bool arpEnabled_; int arpNum_; bool ptEnabled_; int ptNum_; }; class InstrumentDrumkit final : public AbstractInstrument { public: InstrumentDrumkit(int number, const std::string& name, InstrumentsManager* owner); AbstractInstrument* clone() override; std::vector getAssignedKeys() const; void setSampleEnabled(int key, bool enabled); bool getSampleEnabled(int key) const; void setSampleNumber(int key, int n); int getSampleNumber(int key) const; int getSampleRootKeyNumber(int key) const; int getSampleRootDeltaN(int key) const; bool isSampleRepeatable(int key) const; std::vector getRawSample(int key) const; size_t getSampleStartAddress(int key) const; size_t getSampleStopAddress(int key) const; void setPitch(int key, int pitch); int getPitch(int key) const; private: struct KitProperty { int sampNum, pitch; }; std::unordered_map kit_; }; BambooTracker-0.4.6/BambooTracker/instrument/instrument_property_defs.hpp000066400000000000000000000053721401124043500270460ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "sequence_property.hpp" using FMOperatorSequenceUnit = InstrumentSequenceBaseUnit; using FMOperatorSequenceIter = std::unique_ptr::Iterator>; enum class FMOperatorType { All, Op1, Op2, Op3, Op4 }; using ArpeggioUnit = InstrumentSequenceBaseUnit; using ArpeggioIter = std::unique_ptr::Iterator>; using PitchUnit = InstrumentSequenceBaseUnit; using PitchIter = std::unique_ptr::Iterator>; constexpr int SEQ_PITCH_CENTER = 127; using SSGWaveformUnit = InstrumentSequenceExtendUnit; using SSGWaveformIter = std::unique_ptr::Iterator>; class SSGWaveformType { public: enum : int { UNSET = SSGWaveformUnit::ERR_DATA, SQUARE = 0, TRIANGLE = 1, SAW = 2, INVSAW = 3, SQM_TRIANGLE = 4, SQM_SAW = 5, SQM_INVSAW = 6 }; static bool testHardEnvelopeOccupancity(int form) { switch (form) { case SSGWaveformType::UNSET: case SSGWaveformType::SQUARE: return false; default: return true; } } SSGWaveformType() = delete; }; using SSGToneNoiseUnit = InstrumentSequenceBaseUnit; using SSGToneNoiseIter = std::unique_ptr::Iterator>; using SSGEnvelopeUnit = InstrumentSequenceExtendUnit; using SSGEnvelopeIter = std::unique_ptr::Iterator>; using ADPCMEnvelopeUnit = InstrumentSequenceBaseUnit; using ADPCMEnvelopeIter = std::unique_ptr::Iterator>; BambooTracker-0.4.6/BambooTracker/instrument/instruments_manager.cpp000066400000000000000000002460141401124043500257510ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instruments_manager.hpp" #include #include #include #include #include #include #include "instrument.hpp" #include "note.hpp" #include "utils.hpp" namespace { // Constant const FMEnvelopeParameter ENV_FM_PARAMS[] = { FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, FMEnvelopeParameter::DT1, FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, FMEnvelopeParameter::DT2, FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, FMEnvelopeParameter::DT3, FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, FMEnvelopeParameter::DT4 }; const FMOperatorType FM_OP_TYPES[] = { FMOperatorType::All, FMOperatorType::Op1, FMOperatorType::Op2, FMOperatorType::Op3, FMOperatorType::Op4 }; // Property initialization helper inline auto makeEnvelopeFMSharedPtr(int n) { return std::make_shared(n); } inline auto makeLFOFMSharedPtr(int n) { return std::make_shared(n); } inline auto makeOperatorSequenceFMSharedPtr(int n) { return std::make_shared>(n, SequenceType::PlainSequence, FMOperatorSequenceUnit(0), FMOperatorSequenceUnit()); } inline auto makeArpeggioSharedPtr(int n) { return std::make_shared>(n, SequenceType::AbsoluteSequence, ArpeggioUnit(Note::DEFAULT_NOTE_NUM), ArpeggioUnit()); } inline auto makePitchSharedPtr(int n) { return std::make_shared>(n, SequenceType::AbsoluteSequence, PitchUnit(SEQ_PITCH_CENTER), PitchUnit()); } inline auto makeWaveformSSGSharedPtr(int n) { return std::make_shared>(n, SequenceType::PlainSequence, SSGWaveformUnit::makeOnlyDataUnit(SSGWaveformType::SQUARE), SSGWaveformUnit()); } inline auto makeToneNoiseSSGSharedPtr(int n) { return std::make_shared>(n, SequenceType::PlainSequence, SSGToneNoiseUnit(0), SSGToneNoiseUnit()); } inline auto makeEnvelopeSSGSharedPtr(int n) { return std::make_shared>(n, SequenceType::PlainSequence, SSGEnvelopeUnit::makeOnlyDataUnit(15), SSGEnvelopeUnit(), 15); } inline auto makeSampleADPCMSharedPtr(int n) { return std::make_shared(n); } inline auto makeEnvelopeADPCMSharedPtr(int n) { return std::make_shared>(n, SequenceType::PlainSequence, ADPCMEnvelopeUnit(255), ADPCMEnvelopeUnit(), 127); } // Utility functor template struct IsUsed { using ArgType = typename std::add_const::type::const_reference; bool operator()(ArgType prop) const { return prop->isUserInstrument(); } }; template struct IsEdited { using ArgType = typename std::add_const::type::const_reference; bool operator()(ArgType prop) const { return prop->isEdited(); } }; template struct IsUsedOrEdited { using ArgType = typename std::add_const::type::const_reference; bool operator()(ArgType prop) const { return (prop->isUserInstrument() || prop->isEdited()); } }; // Common process for properties template int cloneProperty(propArray& aryRef, int srcNum) { // Must find an unused property since the count of used properties <= the count of instruments auto&& it = std::find_if_not(aryRef.begin(), aryRef.end(), IsUsed()); int cloneNum = std::distance(aryRef.begin(), it); *it = aryRef.at(static_cast(srcNum))->clone(); (*it)->setNumber(cloneNum); return cloneNum; } template int findFirstAssignableProperty(propArray& aryRef, bool regardingUnedited, int startOffs = 0) { std::function cond; if (regardingUnedited) cond = IsUsedOrEdited(); else cond = IsUsed(); auto&& it = std::find_if_not(aryRef.cbegin() + startOffs, aryRef.cend(), cond); if (it == aryRef.cend()) return -1; if (!regardingUnedited) (*it)->clearParameters(); return std::distance(aryRef.cbegin(), it); } } InstrumentsManager::InstrumentsManager(bool unedited) : regardingUnedited_(unedited) { clearAll(); } void InstrumentsManager::addInstrument(int instNum, InstrumentType type, const std::string& name) { if (instNum < 0 || static_cast(insts_.size()) <= instNum) return; switch (type) { case InstrumentType::FM: { auto fm = new InstrumentFM(instNum, name, this); int envNum = findFirstAssignableEnvelopeFM(); if (envNum == -1) envNum = static_cast(envFM_.size()) - 1; fm->setEnvelopeNumber(envNum); envFM_.at(static_cast(envNum))->registerUserInstrument(instNum); int lfoNum = findFirstAssignableLFOFM(); if (lfoNum == -1) lfoNum = static_cast(lfoFM_.size()) - 1; fm->setLFONumber(lfoNum); fm->setLFOEnabled(false); for (auto param : ENV_FM_PARAMS) { int opSeqNum = findFirstAssignableOperatorSequenceFM(param); if (opSeqNum == -1) opSeqNum = static_cast(opSeqFM_.at(param).size()) - 1; fm->setOperatorSequenceNumber(param, opSeqNum); fm->setOperatorSequenceEnabled(param, false); } int arpNum = findFirstAssignableArpeggioFM(); if (arpNum == -1) arpNum = static_cast(arpFM_.size()) - 1; int ptNum = findFirstAssignablePitchFM(); if (ptNum == -1) ptNum = static_cast(ptFM_.size()) - 1; for (auto type : FM_OP_TYPES) { fm->setArpeggioNumber(type, arpNum); fm->setArpeggioEnabled(type, false); fm->setPitchNumber(type, ptNum); fm->setPitchEnabled(type, false); } insts_.at(static_cast(instNum)).reset(fm); break; } case InstrumentType::SSG: { auto ssg = new InstrumentSSG(instNum, name, this); int wfNum = findFirstAssignableWaveformSSG(); if (wfNum == -1) wfNum = static_cast(wfSSG_.size()) - 1; ssg->setWaveformNumber(wfNum); ssg->setWaveformEnabled(false); int tnNum = findFirstAssignableToneNoiseSSG(); if (tnNum == -1) tnNum = static_cast(tnSSG_.size()) - 1; ssg->setToneNoiseNumber(tnNum); ssg->setToneNoiseEnabled(false); int envNum = findFirstAssignableEnvelopeSSG(); if (envNum == -1) envNum = static_cast(envSSG_.size()) - 1; ssg->setEnvelopeNumber(envNum); ssg->setEnvelopeEnabled(false); int arpNum = findFirstAssignableArpeggioSSG(); if (arpNum == -1) arpNum = static_cast(arpSSG_.size()) - 1; ssg->setArpeggioNumber(arpNum); ssg->setArpeggioEnabled(false); int ptNum = findFirstAssignablePitchSSG(); if (ptNum == -1) ptNum = static_cast(ptSSG_.size()) - 1; ssg->setPitchNumber(ptNum); ssg->setPitchEnabled(false); insts_.at(static_cast(instNum)).reset(ssg); break; } case InstrumentType::ADPCM: { auto adpcm = new InstrumentADPCM(instNum, name, this); int sampNum = findFirstAssignableSampleADPCM(); if (sampNum == -1) sampNum = static_cast(sampADPCM_.size()) - 1; adpcm->setSampleNumber(sampNum); sampADPCM_.at(static_cast(sampNum))->registerUserInstrument(instNum); int envNum = findFirstAssignableEnvelopeADPCM(); if (envNum == -1) envNum = static_cast(envADPCM_.size()) - 1; adpcm->setEnvelopeNumber(envNum); adpcm->setEnvelopeEnabled(false); int arpNum = findFirstAssignableArpeggioADPCM(); if (arpNum == -1) arpNum = static_cast(arpADPCM_.size()) - 1; adpcm->setArpeggioNumber(arpNum); adpcm->setArpeggioEnabled(false); int ptNum = findFirstAssignablePitchADPCM(); if (ptNum == -1) ptNum = static_cast(ptADPCM_.size()) - 1; adpcm->setPitchNumber(ptNum); adpcm->setPitchEnabled(false); insts_.at(static_cast(instNum)).reset(adpcm); break; } case InstrumentType::Drumkit: { insts_.at(static_cast(instNum)).reset(new InstrumentDrumkit(instNum, name, this)); break; } default: throw std::invalid_argument("invalid instrument type"); } } void InstrumentsManager::addInstrument(AbstractInstrument* newInstPtr) { int num = newInstPtr->getNumber(); std::shared_ptr& inst = insts_.at(static_cast(num)); inst.reset(newInstPtr); switch (inst->getType()) { case InstrumentType::FM: { auto fm = std::dynamic_pointer_cast(inst); envFM_.at(static_cast(fm->getEnvelopeNumber()))->registerUserInstrument(num); if (fm->getLFOEnabled()) lfoFM_.at(static_cast(fm->getLFONumber()))->registerUserInstrument(num); for (auto p : ENV_FM_PARAMS) { if (fm->getOperatorSequenceEnabled(p)) opSeqFM_.at(p).at(static_cast(fm->getOperatorSequenceNumber(p))) ->registerUserInstrument(num); } for (auto t : FM_OP_TYPES) { if (fm->getArpeggioEnabled(t)) arpFM_.at(static_cast(fm->getArpeggioNumber(t)))->registerUserInstrument(num); if (fm->getPitchEnabled(t)) ptFM_.at(static_cast(fm->getPitchNumber(t)))->registerUserInstrument(num); } break; } case InstrumentType::SSG: { auto ssg = std::dynamic_pointer_cast(inst); if (ssg->getWaveformEnabled()) wfSSG_.at(static_cast(ssg->getWaveformNumber()))->registerUserInstrument(num); if (ssg->getToneNoiseEnabled()) tnSSG_.at(static_cast(ssg->getToneNoiseNumber()))->registerUserInstrument(num); if (ssg->getEnvelopeEnabled()) envSSG_.at(static_cast(ssg->getEnvelopeNumber()))->registerUserInstrument(num); if (ssg->getArpeggioEnabled()) arpSSG_.at(static_cast(ssg->getArpeggioNumber()))->registerUserInstrument(num); if (ssg->getPitchEnabled()) ptSSG_.at(static_cast(ssg->getPitchNumber()))->registerUserInstrument(num); break; } case InstrumentType::ADPCM: { auto adpcm = std::dynamic_pointer_cast(inst); sampADPCM_.at(static_cast(adpcm->getSampleNumber()))->registerUserInstrument(num); if (adpcm->getEnvelopeEnabled()) envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->registerUserInstrument(num); if (adpcm->getArpeggioEnabled()) arpADPCM_.at(static_cast(adpcm->getArpeggioNumber()))->registerUserInstrument(num); if (adpcm->getPitchEnabled()) ptADPCM_.at(static_cast(adpcm->getPitchNumber()))->registerUserInstrument(num); break; } case InstrumentType::Drumkit: { auto kit = std::dynamic_pointer_cast(inst); for (const auto& key : kit->getAssignedKeys()) { sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->registerUserInstrument(num); } break; } default: throw std::invalid_argument("invalid instrument type"); } } std::unique_ptr InstrumentsManager::removeInstrument(int instNum) { std::shared_ptr& inst = insts_.at(static_cast(instNum)); switch (inst->getType()) { case InstrumentType::FM: { auto fm = std::dynamic_pointer_cast(inst); envFM_.at(static_cast(fm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); if (fm->getLFOEnabled()) lfoFM_.at(static_cast(fm->getLFONumber()))->deregisterUserInstrument(instNum); for (auto p : ENV_FM_PARAMS) { if (fm->getOperatorSequenceEnabled(p)) opSeqFM_.at(p).at(static_cast(fm->getOperatorSequenceNumber(p))) ->deregisterUserInstrument(instNum); } for (auto t : FM_OP_TYPES) { if (fm->getArpeggioEnabled(t)) arpFM_.at(static_cast(fm->getArpeggioNumber(t)))->deregisterUserInstrument(instNum); if (fm->getPitchEnabled(t)) ptFM_.at(static_cast(fm->getPitchNumber(t)))->deregisterUserInstrument(instNum); } break; } case InstrumentType::SSG: { auto ssg = std::dynamic_pointer_cast(inst); if (ssg->getWaveformEnabled()) wfSSG_.at(static_cast(ssg->getWaveformNumber()))->deregisterUserInstrument(instNum); if (ssg->getToneNoiseEnabled()) tnSSG_.at(static_cast(ssg->getToneNoiseNumber()))->deregisterUserInstrument(instNum); if (ssg->getEnvelopeEnabled()) envSSG_.at(static_cast(ssg->getEnvelopeNumber()))->deregisterUserInstrument(instNum); if (ssg->getArpeggioEnabled()) arpSSG_.at(static_cast(ssg->getArpeggioNumber()))->deregisterUserInstrument(instNum); if (ssg->getPitchEnabled()) ptSSG_.at(static_cast(ssg->getPitchNumber()))->deregisterUserInstrument(instNum); break; } case InstrumentType::ADPCM: { auto adpcm = std::dynamic_pointer_cast(inst); sampADPCM_.at(static_cast(adpcm->getSampleNumber()))->deregisterUserInstrument(instNum); if (adpcm->getEnvelopeEnabled()) envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); if (adpcm->getArpeggioEnabled()) arpADPCM_.at(static_cast(adpcm->getArpeggioNumber()))->deregisterUserInstrument(instNum); if (adpcm->getPitchEnabled()) ptADPCM_.at(static_cast(adpcm->getPitchNumber()))->deregisterUserInstrument(instNum); break; } case InstrumentType::Drumkit: { auto kit = std::dynamic_pointer_cast(inst); for (const int& key : kit->getAssignedKeys()) { sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->deregisterUserInstrument(instNum); } break; } default: throw std::invalid_argument("invalid instrument type"); } std::unique_ptr clone(inst->clone()); inst.reset(); return clone; } void InstrumentsManager::cloneInstrument(int cloneInstNum, int refInstNum) { std::shared_ptr& refInst = insts_.at(static_cast(refInstNum)); addInstrument(cloneInstNum, refInst->getType(), refInst->getName()); switch (refInst->getType()) { case InstrumentType::FM: { auto refFm = std::dynamic_pointer_cast(refInst); auto cloneFm = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); setInstrumentFMEnvelope(cloneInstNum, refFm->getEnvelopeNumber()); setInstrumentFMLFO(cloneInstNum, refFm->getLFONumber()); if (refFm->getLFOEnabled()) setInstrumentFMLFOEnabled(cloneInstNum, true); for (auto p : ENV_FM_PARAMS) { setInstrumentFMOperatorSequence(cloneInstNum, p, refFm->getOperatorSequenceNumber(p)); if (refFm->getOperatorSequenceEnabled(p)) setInstrumentFMOperatorSequenceEnabled(cloneInstNum, p, true); } for (auto t : FM_OP_TYPES) { setInstrumentFMArpeggio(cloneInstNum, t, refFm->getArpeggioNumber(t)); if (refFm->getArpeggioEnabled(t)) setInstrumentFMArpeggioEnabled(cloneInstNum, t, true); setInstrumentFMPitch(cloneInstNum, t, refFm->getPitchNumber(t)); if (refFm->getPitchEnabled(t)) setInstrumentFMPitchEnabled(cloneInstNum, t, true); setInstrumentFMEnvelopeResetEnabled(cloneInstNum, t, refFm->getEnvelopeResetEnabled(t)); } break; } case InstrumentType::SSG: { auto refSsg = std::dynamic_pointer_cast(refInst); auto cloneSsg = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); setInstrumentSSGWaveform(cloneInstNum, refSsg->getWaveformNumber()); if (refSsg->getWaveformEnabled()) setInstrumentSSGWaveformEnabled(cloneInstNum, true); setInstrumentSSGToneNoise(cloneInstNum, refSsg->getToneNoiseNumber()); if (refSsg->getToneNoiseEnabled()) setInstrumentSSGToneNoiseEnabled(cloneInstNum, true); setInstrumentSSGEnvelope(cloneInstNum, refSsg->getEnvelopeNumber()); if (refSsg->getEnvelopeEnabled()) setInstrumentSSGEnvelopeEnabled(cloneInstNum, true); setInstrumentSSGArpeggio(cloneInstNum, refSsg->getArpeggioNumber()); if (refSsg->getArpeggioEnabled()) setInstrumentSSGArpeggioEnabled(cloneInstNum, true); setInstrumentSSGPitch(cloneInstNum, refSsg->getPitchNumber()); if (refSsg->getPitchEnabled()) setInstrumentSSGPitchEnabled(cloneInstNum, true); break; } case InstrumentType::ADPCM: { auto refAdpcm = std::dynamic_pointer_cast(refInst); auto cloneAdpcm = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); setInstrumentADPCMSample(cloneInstNum, refAdpcm->getSampleNumber()); setInstrumentADPCMEnvelope(cloneInstNum, refAdpcm->getEnvelopeNumber()); if (refAdpcm->getEnvelopeEnabled()) setInstrumentADPCMEnvelopeEnabled(cloneInstNum, true); setInstrumentADPCMArpeggio(cloneInstNum, refAdpcm->getArpeggioNumber()); if (refAdpcm->getArpeggioEnabled()) setInstrumentADPCMArpeggioEnabled(cloneInstNum, true); setInstrumentADPCMPitch(cloneInstNum, refAdpcm->getPitchNumber()); if (refAdpcm->getPitchEnabled()) setInstrumentADPCMPitchEnabled(cloneInstNum, true); break; } case InstrumentType::Drumkit: { auto refKit = std::dynamic_pointer_cast(refInst); auto cloneKit = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); for (const int& key : refKit->getAssignedKeys()) { setInstrumentDrumkitSamples(cloneInstNum, key, refKit->getSampleNumber(key)); setInstrumentDrumkitPitch(cloneInstNum, key, refKit->getPitch(key)); } break; } default: throw std::invalid_argument("invalid instrument type"); } } void InstrumentsManager::deepCloneInstrument(int cloneInstNum, int refInstNum) { std::shared_ptr& refInst = insts_.at(static_cast(refInstNum)); addInstrument(cloneInstNum, refInst->getType(), refInst->getName()); switch (refInst->getType()) { case InstrumentType::FM: { auto refFm = std::dynamic_pointer_cast(refInst); auto cloneFm = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); envFM_[static_cast(cloneFm->getEnvelopeNumber())]->deregisterUserInstrument(cloneInstNum); // Remove temporary number int envNum = cloneProperty(envFM_, refFm->getEnvelopeNumber()); cloneFm->setEnvelopeNumber(envNum); envFM_[static_cast(envNum)]->registerUserInstrument(cloneInstNum); if (refFm->getLFOEnabled()) { cloneFm->setLFOEnabled(true); int lfoNum = cloneProperty(lfoFM_, refFm->getLFONumber()); cloneFm->setLFONumber(lfoNum); lfoFM_[static_cast(lfoNum)]->registerUserInstrument(cloneInstNum); } for (auto envParam : ENV_FM_PARAMS) { if (refFm->getOperatorSequenceEnabled(envParam)) { cloneFm->setOperatorSequenceEnabled(envParam, true); int opSeqNum = cloneProperty(opSeqFM_.at(envParam), refFm->getOperatorSequenceNumber(envParam)); cloneFm->setOperatorSequenceNumber(envParam, opSeqNum); opSeqFM_.at(envParam)[static_cast(opSeqNum)]->registerUserInstrument(cloneInstNum); } } std::unordered_map arpCloneMap; for (auto opType : FM_OP_TYPES) { if (refFm->getArpeggioEnabled(opType)) { cloneFm->setArpeggioEnabled(opType, true); int srcNum = refFm->getArpeggioNumber(opType); if (!arpCloneMap.count(srcNum)) { arpCloneMap[srcNum] = cloneProperty(arpFM_, srcNum); } cloneFm->setArpeggioNumber(opType, arpCloneMap[srcNum]); arpFM_[static_cast(arpCloneMap[srcNum])]->registerUserInstrument(cloneInstNum); } } std::unordered_map ptCloneMap; for (auto opType : FM_OP_TYPES) { if (refFm->getPitchEnabled(opType)) { cloneFm->setPitchEnabled(opType, true); int srcNum = refFm->getPitchNumber(opType); if (!ptCloneMap.count(srcNum)) { ptCloneMap[srcNum] = cloneProperty(ptFM_, srcNum); } cloneFm->setPitchNumber(opType, ptCloneMap[srcNum]); ptFM_[static_cast(ptCloneMap[srcNum])]->registerUserInstrument(cloneInstNum); } } for (auto t : FM_OP_TYPES) setInstrumentFMEnvelopeResetEnabled(cloneInstNum, t, refFm->getEnvelopeResetEnabled(t)); break; } case InstrumentType::SSG: { auto refSsg = std::dynamic_pointer_cast(refInst); auto cloneSsg = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); if (refSsg->getWaveformEnabled()) { cloneSsg->setWaveformEnabled(true); int wfNum = cloneProperty(wfSSG_, refSsg->getWaveformNumber()); cloneSsg->setWaveformNumber(wfNum); wfSSG_[static_cast(wfNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getToneNoiseEnabled()) { cloneSsg->setToneNoiseEnabled(true); int tnNum = cloneProperty(tnSSG_, refSsg->getToneNoiseNumber()); cloneSsg->setToneNoiseNumber(tnNum); tnSSG_[static_cast(tnNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getEnvelopeEnabled()) { cloneSsg->setEnvelopeEnabled(true); int envNum = cloneProperty(envSSG_, refSsg->getEnvelopeNumber()); cloneSsg->setEnvelopeNumber(envNum); envSSG_[static_cast(envNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getArpeggioEnabled()) { cloneSsg->setArpeggioEnabled(true); int arpNum = cloneProperty(arpSSG_, refSsg->getArpeggioNumber()); cloneSsg->setArpeggioNumber(arpNum); arpSSG_[static_cast(arpNum)]->registerUserInstrument(cloneInstNum); } if (refSsg->getPitchEnabled()) { cloneSsg->setPitchEnabled(true); int ptNum = cloneProperty(ptSSG_, refSsg->getPitchNumber()); cloneSsg->setPitchNumber(ptNum); ptSSG_[static_cast(ptNum)]->registerUserInstrument(cloneInstNum); } break; } case InstrumentType::ADPCM: { auto refAdpcm = std::dynamic_pointer_cast(refInst); auto cloneAdpcm = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); sampADPCM_[static_cast(cloneAdpcm->getSampleNumber())]->deregisterUserInstrument(cloneInstNum); // Remove temporary number int sampNum = cloneProperty(sampADPCM_, refAdpcm->getSampleNumber()); cloneAdpcm->setSampleNumber(sampNum); sampADPCM_[static_cast(sampNum)]->registerUserInstrument(cloneInstNum); if (refAdpcm->getEnvelopeEnabled()) { cloneAdpcm->setEnvelopeEnabled(true); int envNum = cloneProperty(envADPCM_, refAdpcm->getEnvelopeNumber()); cloneAdpcm->setEnvelopeNumber(envNum); envADPCM_[static_cast(envNum)]->registerUserInstrument(cloneInstNum); } if (refAdpcm->getArpeggioEnabled()) { cloneAdpcm->setArpeggioEnabled(true); int arpNum = cloneProperty(arpADPCM_, refAdpcm->getArpeggioNumber()); cloneAdpcm->setArpeggioNumber(arpNum); arpADPCM_[static_cast(arpNum)]->registerUserInstrument(cloneInstNum); } if (refAdpcm->getPitchEnabled()) { cloneAdpcm->setPitchEnabled(true); int ptNum = cloneProperty(ptADPCM_, refAdpcm->getPitchNumber()); cloneAdpcm->setPitchNumber(ptNum); ptADPCM_[static_cast(ptNum)]->registerUserInstrument(cloneInstNum); } break; } case InstrumentType::Drumkit: { auto refKit = std::dynamic_pointer_cast(refInst); auto cloneKit = std::dynamic_pointer_cast(insts_.at(static_cast(cloneInstNum))); std::unordered_map sampCloneMap; for (const int& key : refKit->getAssignedKeys()) { int srcNum = refKit->getSampleNumber(key); if (!sampCloneMap.count(srcNum)) sampCloneMap[srcNum] = cloneProperty(sampADPCM_, srcNum); cloneKit->setSampleNumber(key, sampCloneMap[srcNum]); sampADPCM_[static_cast(sampCloneMap[srcNum])]->registerUserInstrument(cloneInstNum); setInstrumentDrumkitPitch(cloneInstNum, key, refKit->getPitch(key)); } break; } default: throw std::invalid_argument("invalid instrument type"); } } void InstrumentsManager::swapInstruments(int inst1Num, int inst2Num) { std::unique_ptr inst1 = removeInstrument(inst1Num); std::unique_ptr inst2 = removeInstrument(inst2Num); inst1->setNumber(inst2Num); inst2->setNumber(inst1Num); addInstrument(inst1.release()); addInstrument(inst2.release()); } /// [Return] instrument shared pointer or nullptr std::shared_ptr InstrumentsManager::getInstrumentSharedPtr(int instNum) { if (0 <= instNum && instNum < static_cast(insts_.size())) return insts_[static_cast(instNum)]; else return std::shared_ptr(); } void InstrumentsManager::clearAll() { for (auto p : ENV_FM_PARAMS) { opSeqFM_.emplace(p, std::array>, 128>()); } for (size_t i = 0; i < 128; ++i) { insts_[i].reset(); envFM_[i] = makeEnvelopeFMSharedPtr(i); lfoFM_[i] = makeLFOFMSharedPtr(i); for (auto& p : opSeqFM_) p.second[i] = makeOperatorSequenceFMSharedPtr(i); arpFM_[i] = makeArpeggioSharedPtr(i); ptFM_[i] = makePitchSharedPtr(i); wfSSG_[i] = makeWaveformSSGSharedPtr(i); tnSSG_[i] = makeToneNoiseSSGSharedPtr(i); envSSG_[i] = makeEnvelopeSSGSharedPtr(i); arpSSG_[i] = makeArpeggioSharedPtr(i); ptSSG_[i] = makePitchSharedPtr(i); sampADPCM_[i] = makeSampleADPCMSharedPtr(i); envADPCM_[i] = makeEnvelopeADPCMSharedPtr(i); arpADPCM_[i] = makeArpeggioSharedPtr(i); ptADPCM_[i] = makePitchSharedPtr(i); } } std::vector InstrumentsManager::getInstrumentIndices() const { return utils::findIndicesIf(insts_, [](decltype(insts_)::const_reference inst) { return inst; }); } void InstrumentsManager::setInstrumentName(int instNum, const std::string& name) { insts_.at(static_cast(instNum))->setName(name); } std::string InstrumentsManager::getInstrumentName(int instNum) const { return insts_.at(static_cast(instNum))->getName(); } std::vector InstrumentsManager::getInstrumentNameList() const { using InstRef = decltype(insts_)::const_reference; return utils::transformIf(insts_, [](InstRef inst) { return inst; }, [](InstRef inst) { return inst->getName(); }); } void InstrumentsManager::clearUnusedInstrumentProperties() { for (size_t i = 0; i < 128; ++i) { if (!envFM_[i]->isUserInstrument()) envFM_[i] = makeEnvelopeFMSharedPtr(i); if (!lfoFM_[i]->isUserInstrument()) lfoFM_[i] = makeLFOFMSharedPtr(i); for (auto& p : opSeqFM_) { if (!p.second[i]->isUserInstrument()) p.second[i] = makeOperatorSequenceFMSharedPtr(i); } if (!arpFM_[i]->isUserInstrument()) arpFM_[i] = makeArpeggioSharedPtr(i); if (!ptFM_[i]->isUserInstrument()) ptFM_[i] = makePitchSharedPtr(i); if (!wfSSG_[i]->isUserInstrument()) wfSSG_[i] = makeWaveformSSGSharedPtr(i); if (!tnSSG_[i]->isUserInstrument()) tnSSG_[i] = makeToneNoiseSSGSharedPtr(i); if (!envSSG_[i]->isUserInstrument()) envSSG_[i] = makeEnvelopeSSGSharedPtr(i); if (!arpSSG_[i]->isUserInstrument()) arpSSG_[i] = makeArpeggioSharedPtr(i); if (!ptSSG_[i]->isUserInstrument()) ptSSG_[i] = makePitchSharedPtr(i); if (!sampADPCM_[i]->isUserInstrument()) sampADPCM_[i] = makeSampleADPCMSharedPtr(i); if (!envADPCM_[i]->isUserInstrument()) envADPCM_[i] = makeEnvelopeADPCMSharedPtr(i); if (!arpADPCM_[i]->isUserInstrument()) arpADPCM_[i] = makeArpeggioSharedPtr(i); if (!ptADPCM_[i]->isUserInstrument()) ptADPCM_[i] = makePitchSharedPtr(i); } } /// [Return] /// -1: no free instrument /// else: first free instrument number int InstrumentsManager::findFirstFreeInstrument() const { auto&& it = std::find_if_not(insts_.cbegin(), insts_.cend(), [](const std::shared_ptr& inst) { return inst; }); return (it == insts_.cend() ? -1 : std::distance(insts_.cbegin(), it)); } std::unordered_map InstrumentsManager::getDuplicateInstrumentMap() const { std::unordered_map dupMap; std::vector idcs = getInstrumentIndices(); static const std::unordered_map, std::shared_ptr) const> eqCheck = { { InstrumentType::FM, &InstrumentsManager::equalPropertiesFM }, { InstrumentType::SSG, &InstrumentsManager::equalPropertiesSSG }, { InstrumentType::ADPCM, &InstrumentsManager::equalPropertiesADPCM }, { InstrumentType::Drumkit, &InstrumentsManager::equalPropertiesDrumkit } }; for (size_t i = 0; i < idcs.size(); ++i) { int baseIdx = idcs[i]; std::shared_ptr base = insts_[baseIdx]; for (size_t j = i + 1; j < idcs.size();) { int tgtIdx = idcs[j]; std::shared_ptr tgt = insts_[tgtIdx]; if (base->getType() == tgt->getType() && (this->*eqCheck.at(base->getType()))(base, tgt)) { dupMap[tgtIdx] = baseIdx; idcs.erase(idcs.begin() + j); continue; } ++j; } } return dupMap; } //----- FM methods ----- void InstrumentsManager::setInstrumentFMEnvelope(int instNum, int envNum) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); envFM_.at(static_cast(fm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); envFM_.at(static_cast(envNum))->registerUserInstrument(instNum); fm->setEnvelopeNumber(envNum); } int InstrumentsManager::getInstrumentFMEnvelope(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeNumber(); } void InstrumentsManager::setEnvelopeFMParameter(int envNum, FMEnvelopeParameter param, int value) { envFM_.at(static_cast(envNum))->setParameterValue(param, value); } int InstrumentsManager::getEnvelopeFMParameter(int envNum, FMEnvelopeParameter param) const { return envFM_.at(static_cast(envNum))->getParameterValue(param); } void InstrumentsManager::setEnvelopeFMOperatorEnabled(int envNum, int opNum, bool enabled) { envFM_.at(static_cast(envNum))->setOperatorEnabled(opNum, enabled); } bool InstrumentsManager::getEnvelopeFMOperatorEnabled(int envNum, int opNum) const { return envFM_.at(static_cast(envNum))->getOperatorEnabled(opNum); } std::multiset InstrumentsManager::getEnvelopeFMUsers(int envNum) const { return envFM_.at(static_cast(envNum))->getUserInstruments(); } std::vector InstrumentsManager::getEnvelopeFMEntriedIndices() const { return utils::findIndicesIf(envFM_, IsEdited()); } int InstrumentsManager::findFirstAssignableEnvelopeFM() const { return findFirstAssignableProperty(envFM_, regardingUnedited_); } void InstrumentsManager::setInstrumentFMLFOEnabled(int instNum, bool enabled) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); fm->setLFOEnabled(enabled); if (enabled) lfoFM_.at(static_cast(fm->getLFONumber()))->registerUserInstrument(instNum); else lfoFM_.at(static_cast(fm->getLFONumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentFMLFOEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getLFOEnabled(); } void InstrumentsManager::setInstrumentFMLFO(int instNum, int lfoNum) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (fm->getLFOEnabled()) { lfoFM_.at(static_cast(fm->getLFONumber()))->deregisterUserInstrument(instNum); lfoFM_.at(static_cast(lfoNum))->registerUserInstrument(instNum); } fm->setLFONumber(lfoNum); } int InstrumentsManager::getInstrumentFMLFO(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getLFONumber(); } void InstrumentsManager::setLFOFMParameter(int lfoNum, FMLFOParameter param, int value) { lfoFM_.at(static_cast(lfoNum))->setParameterValue(param, value); } int InstrumentsManager::getLFOFMparameter(int lfoNum, FMLFOParameter param) const { return lfoFM_.at(static_cast(lfoNum))->getParameterValue(param); } std::multiset InstrumentsManager::getLFOFMUsers(int lfoNum) const { return lfoFM_.at(static_cast(lfoNum))->getUserInstruments(); } std::vector InstrumentsManager::getLFOFMEntriedIndices() const { return utils::findIndicesIf(lfoFM_, IsEdited()); } int InstrumentsManager::findFirstAssignableLFOFM() const { return findFirstAssignableProperty(lfoFM_, regardingUnedited_); } void InstrumentsManager::setInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param, bool enabled) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); fm->setOperatorSequenceEnabled(param, enabled); if (enabled) opSeqFM_.at(param).at(static_cast(fm->getOperatorSequenceNumber(param)))->registerUserInstrument(instNum); else opSeqFM_.at(param).at(static_cast(fm->getOperatorSequenceNumber(param)))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getOperatorSequenceEnabled(param); } void InstrumentsManager::setInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param, int opSeqNum) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (fm->getOperatorSequenceEnabled(param)) { opSeqFM_.at(param).at(static_cast(fm->getOperatorSequenceNumber(param)))->deregisterUserInstrument(instNum); opSeqFM_.at(param).at(static_cast(opSeqNum))->registerUserInstrument(instNum); } fm->setOperatorSequenceNumber(param, opSeqNum); } int InstrumentsManager::getInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getOperatorSequenceNumber(param); } void InstrumentsManager::addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data) { opSeqFM_.at(param).at(static_cast(opSeqNum))->addSequenceUnit(FMOperatorSequenceUnit(data)); } void InstrumentsManager::removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum) { opSeqFM_.at(param).at(static_cast(opSeqNum))->removeSequenceUnit(); } void InstrumentsManager::setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data) { opSeqFM_.at(param).at(static_cast(opSeqNum))->setSequenceUnit(cnt, FMOperatorSequenceUnit(data)); } std::vector InstrumentsManager::getOperatorSequenceFMSequence(FMEnvelopeParameter param, int opSeqNum) { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getSequence(); } void InstrumentsManager::addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop) { opSeqFM_.at(param).at(static_cast(opSeqNum))->addLoop(loop); } void InstrumentsManager::removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end) { opSeqFM_.at(param).at(static_cast(opSeqNum))->removeLoop(begin, end); } void InstrumentsManager::changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { opSeqFM_.at(param).at(static_cast(opSeqNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum) { opSeqFM_.at(param).at(static_cast(opSeqNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getOperatorSequenceFMLoopRoot(FMEnvelopeParameter param, int opSeqNum) const { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getLoopRoot(); } void InstrumentsManager::setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release) { opSeqFM_.at(param).at(static_cast(opSeqNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum) const { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getRelease(); } FMOperatorSequenceIter InstrumentsManager::getOperatorSequenceFMIterator(FMEnvelopeParameter param, int opSeqNum) const { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getIterator(); } std::multiset InstrumentsManager::getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const { return opSeqFM_.at(param).at(static_cast(opSeqNum))->getUserInstruments(); } std::vector InstrumentsManager::getOperatorSequenceFMEntriedIndices(FMEnvelopeParameter param) const { return utils::findIndicesIf(opSeqFM_.at(param), IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter param) const { return findFirstAssignableProperty(opSeqFM_.at(param), regardingUnedited_); } void InstrumentsManager::setInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op, bool enabled) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); fm->setArpeggioEnabled(op, enabled); if (enabled) arpFM_.at(static_cast(fm->getArpeggioNumber(op)))->registerUserInstrument(instNum); else arpFM_.at(static_cast(fm->getArpeggioNumber(op)))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioEnabled(op); } void InstrumentsManager::setInstrumentFMArpeggio(int instNum, FMOperatorType op, int arpNum) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (fm->getArpeggioEnabled(op)) { arpFM_.at(static_cast(fm->getArpeggioNumber(op)))->deregisterUserInstrument(instNum); arpFM_.at(static_cast(arpNum))->registerUserInstrument(instNum); } fm->setArpeggioNumber(op, arpNum); } int InstrumentsManager::getInstrumentFMArpeggio(int instNum, FMOperatorType op) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioNumber(op); } void InstrumentsManager::setArpeggioFMType(int arpNum, SequenceType type) { arpFM_.at(static_cast(arpNum))->setType(type); } SequenceType InstrumentsManager::getArpeggioFMType(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getType(); } void InstrumentsManager::addArpeggioFMSequenceData(int arpNum, int data) { arpFM_.at(static_cast(arpNum))->addSequenceUnit(ArpeggioUnit(data)); } void InstrumentsManager::removeArpeggioFMSequenceData(int arpNum) { arpFM_.at(static_cast(arpNum))->removeSequenceUnit(); } void InstrumentsManager::setArpeggioFMSequenceData(int arpNum, int cnt, int data) { arpFM_.at(static_cast(arpNum))->setSequenceUnit(cnt, ArpeggioUnit(data)); } std::vector InstrumentsManager::getArpeggioFMSequence(int arpNum) { return arpFM_.at(static_cast(arpNum))->getSequence(); } void InstrumentsManager::addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop) { arpFM_.at(static_cast(arpNum))->addLoop(loop); } void InstrumentsManager::removeArpeggioFMLoop(int arpNum, int begin, int end) { arpFM_.at(static_cast(arpNum))->removeLoop(begin, end); } void InstrumentsManager::changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { arpFM_.at(static_cast(arpNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearArpeggioFMLoops(int arpNum) { arpFM_.at(static_cast(arpNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getArpeggioFMLoopRoot(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getLoopRoot(); } void InstrumentsManager::setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release) { arpFM_.at(static_cast(arpNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getArpeggioFMRelease(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getRelease(); } ArpeggioIter InstrumentsManager::getArpeggioFMIterator(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getIterator(); } std::multiset InstrumentsManager::getArpeggioFMUsers(int arpNum) const { return arpFM_.at(static_cast(arpNum))->getUserInstruments(); } std::vector InstrumentsManager::getArpeggioFMEntriedIndices() const { return utils::findIndicesIf(arpFM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableArpeggioFM() const { return findFirstAssignableProperty(arpFM_, regardingUnedited_); } void InstrumentsManager::setInstrumentFMPitchEnabled(int instNum, FMOperatorType op, bool enabled) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); fm->setPitchEnabled(op, enabled); if (enabled) ptFM_.at(static_cast(fm->getPitchNumber(op)))->registerUserInstrument(instNum); else ptFM_.at(static_cast(fm->getPitchNumber(op)))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentFMPitchEnabled(int instNum, FMOperatorType op) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchEnabled(op); } void InstrumentsManager::setInstrumentFMPitch(int instNum, FMOperatorType op, int ptNum) { auto fm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (fm->getPitchEnabled(op)) { ptFM_.at(static_cast(fm->getPitchNumber(op)))->deregisterUserInstrument(instNum); ptFM_.at(static_cast(ptNum))->registerUserInstrument(instNum); } fm->setPitchNumber(op, ptNum); } int InstrumentsManager::getInstrumentFMPitch(int instNum, FMOperatorType op) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchNumber(op); } void InstrumentsManager::setPitchFMType(int ptNum, SequenceType type) { ptFM_.at(static_cast(ptNum))->setType(type); } SequenceType InstrumentsManager::getPitchFMType(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getType(); } void InstrumentsManager::addPitchFMSequenceData(int ptNum, int data) { ptFM_.at(static_cast(ptNum))->addSequenceUnit(PitchUnit(data)); } void InstrumentsManager::removePitchFMSequenceData(int ptNum) { ptFM_.at(static_cast(ptNum))->removeSequenceUnit(); } void InstrumentsManager::setPitchFMSequenceData(int ptNum, int cnt, int data) { ptFM_.at(static_cast(ptNum))->setSequenceUnit(cnt, PitchUnit(data)); } std::vector InstrumentsManager::getPitchFMSequence(int ptNum) { return ptFM_.at(static_cast(ptNum))->getSequence(); } void InstrumentsManager::addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop) { ptFM_.at(static_cast(ptNum))->addLoop(loop); } void InstrumentsManager::removePitchFMLoop(int ptNum, int begin, int end) { ptFM_.at(static_cast(ptNum))->removeLoop(begin, end); } void InstrumentsManager::changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { ptFM_.at(static_cast(ptNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearPitchFMLoops(int ptNum) { ptFM_.at(static_cast(ptNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getPitchFMLoopRoot(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getLoopRoot(); } void InstrumentsManager::setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release) { ptFM_.at(static_cast(ptNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getPitchFMRelease(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getRelease(); } PitchIter InstrumentsManager::getPitchFMIterator(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getIterator(); } std::multiset InstrumentsManager::getPitchFMUsers(int ptNum) const { return ptFM_.at(static_cast(ptNum))->getUserInstruments(); } void InstrumentsManager::setInstrumentFMEnvelopeResetEnabled(int instNum, FMOperatorType op, bool enabled) { std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->setEnvelopeResetEnabled(op, enabled); } std::vector InstrumentsManager::getPitchFMEntriedIndices() const { return utils::findIndicesIf(ptFM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignablePitchFM() const { return findFirstAssignableProperty(ptFM_, regardingUnedited_); } bool InstrumentsManager::equalPropertiesFM(std::shared_ptr a, std::shared_ptr b) const { auto aFm = std::dynamic_pointer_cast(a); auto bFm = std::dynamic_pointer_cast(b); if (*envFM_[aFm->getEnvelopeNumber()] != *envFM_[bFm->getEnvelopeNumber()]) return false; if (aFm->getLFOEnabled() != bFm->getLFOEnabled()) return false; if (aFm->getLFOEnabled() && *lfoFM_[aFm->getLFONumber()] != *lfoFM_[bFm->getLFONumber()]) return false; for (auto& pair : opSeqFM_) { if (aFm->getOperatorSequenceEnabled(pair.first) != bFm->getOperatorSequenceEnabled(pair.first)) return false; if (aFm->getOperatorSequenceEnabled(pair.first) && *pair.second[aFm->getOperatorSequenceNumber(pair.first)] != *pair.second[bFm->getOperatorSequenceNumber(pair.first)]) return false; } for (auto& type : FM_OP_TYPES) { if (aFm->getArpeggioEnabled(type) != bFm->getArpeggioEnabled(type)) return false; if (aFm->getArpeggioEnabled(type) && *arpFM_[aFm->getArpeggioNumber(type)] != *arpFM_[bFm->getArpeggioNumber(type)]) return false; if (aFm->getPitchEnabled(type) != bFm->getPitchEnabled(type)) return false; if (aFm->getPitchEnabled(type) && *ptFM_[aFm->getPitchNumber(type)] != *ptFM_[bFm->getPitchNumber(type)]) return false; if (aFm->getEnvelopeResetEnabled(type) != bFm->getEnvelopeResetEnabled(type)) return false; } return true; } //----- SSG methods ----- void InstrumentsManager::setInstrumentSSGWaveformEnabled(int instNum, bool enabled) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); ssg->setWaveformEnabled(enabled); if (enabled) wfSSG_.at(static_cast(ssg->getWaveformNumber()))->registerUserInstrument(instNum); else wfSSG_.at(static_cast(ssg->getWaveformNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentSSGWaveformEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getWaveformEnabled(); } void InstrumentsManager::setInstrumentSSGWaveform(int instNum, int wfNum) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (ssg->getWaveformEnabled()) { wfSSG_.at(static_cast(ssg->getWaveformNumber()))->deregisterUserInstrument(instNum); wfSSG_.at(static_cast(wfNum))->registerUserInstrument(instNum); } ssg->setWaveformNumber(wfNum); } int InstrumentsManager::getInstrumentSSGWaveform(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getWaveformNumber(); } void InstrumentsManager::addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data) { wfSSG_.at(static_cast(wfNum))->addSequenceUnit(data); } void InstrumentsManager::removeWaveformSSGSequenceData(int wfNum) { wfSSG_.at(static_cast(wfNum))->removeSequenceUnit(); } void InstrumentsManager::setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data) { wfSSG_.at(static_cast(wfNum))->setSequenceUnit(cnt, data); } std::vector InstrumentsManager::getWaveformSSGSequence(int wfNum) { return wfSSG_.at(static_cast(wfNum))->getSequence(); } void InstrumentsManager::addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop) { wfSSG_.at(static_cast(wfNum))->addLoop(loop); } void InstrumentsManager::removeWaveformSSGLoop(int wfNum, int begin, int end) { wfSSG_.at(static_cast(wfNum))->removeLoop(begin, end); } void InstrumentsManager::changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { wfSSG_.at(static_cast(wfNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearWaveformSSGLoops(int wfNum) { wfSSG_.at(static_cast(wfNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getWaveformSSGLoopRoot(int wfNum) const { return wfSSG_.at(static_cast(wfNum))->getLoopRoot(); } void InstrumentsManager::setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release) { wfSSG_.at(static_cast(wfNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getWaveformSSGRelease(int wfNum) const { return wfSSG_.at(static_cast(wfNum))->getRelease(); } SSGWaveformIter InstrumentsManager::getWaveformSSGIterator(int wfNum) const { return wfSSG_.at(static_cast(wfNum))->getIterator(); } std::multiset InstrumentsManager::getWaveformSSGUsers(int wfNum) const { return wfSSG_.at(static_cast(wfNum))->getUserInstruments(); } std::vector InstrumentsManager::getWaveformSSGEntriedIndices() const { return utils::findIndicesIf(wfSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableWaveformSSG() const { return findFirstAssignableProperty(wfSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGToneNoiseEnabled(int instNum, bool enabled) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); ssg->setToneNoiseEnabled(enabled); if (enabled) tnSSG_.at(static_cast(ssg->getToneNoiseNumber()))->registerUserInstrument(instNum); else tnSSG_.at(static_cast(ssg->getToneNoiseNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentSSGToneNoiseEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getToneNoiseEnabled(); } void InstrumentsManager::setInstrumentSSGToneNoise(int instNum, int tnNum) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (ssg->getToneNoiseEnabled()) { tnSSG_.at(static_cast(ssg->getToneNoiseNumber()))->deregisterUserInstrument(instNum); tnSSG_.at(static_cast(tnNum))->registerUserInstrument(instNum); } ssg->setToneNoiseNumber(tnNum); } int InstrumentsManager::getInstrumentSSGToneNoise(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getToneNoiseNumber(); } void InstrumentsManager::addToneNoiseSSGSequenceData(int tnNum, int data) { tnSSG_.at(static_cast(tnNum))->addSequenceUnit(SSGToneNoiseUnit(data)); } void InstrumentsManager::removeToneNoiseSSGSequenceData(int tnNum) { tnSSG_.at(static_cast(tnNum))->removeSequenceUnit(); } void InstrumentsManager::setToneNoiseSSGSequenceData(int tnNum, int cnt, int data) { tnSSG_.at(static_cast(tnNum))->setSequenceUnit(cnt, SSGToneNoiseUnit(data)); } std::vector InstrumentsManager::getToneNoiseSSGSequence(int tnNum) { return tnSSG_.at(static_cast(tnNum))->getSequence(); } void InstrumentsManager::addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop) { tnSSG_.at(static_cast(tnNum))->addLoop(loop); } void InstrumentsManager::removeToneNoiseSSGLoop(int tnNum, int begin, int end) { tnSSG_.at(static_cast(tnNum))->removeLoop(begin, end); } void InstrumentsManager::changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { tnSSG_.at(static_cast(tnNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearToneNoiseSSGLoops(int tnNum) { tnSSG_.at(static_cast(tnNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getToneNoiseSSGLoopRoot(int tnNum) const { return tnSSG_.at(static_cast(tnNum))->getLoopRoot(); } void InstrumentsManager::setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release) { tnSSG_.at(static_cast(tnNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getToneNoiseSSGRelease(int tnNum) const { return tnSSG_.at(static_cast(tnNum))->getRelease(); } SSGToneNoiseIter InstrumentsManager::getToneNoiseSSGIterator(int tnNum) const { return tnSSG_.at(static_cast(tnNum))->getIterator(); } std::multiset InstrumentsManager::getToneNoiseSSGUsers(int tnNum) const { return tnSSG_.at(static_cast(tnNum))->getUserInstruments(); } std::vector InstrumentsManager::getToneNoiseSSGEntriedIndices() const { return utils::findIndicesIf(tnSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableToneNoiseSSG() const { return findFirstAssignableProperty(tnSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGEnvelopeEnabled(int instNum, bool enabled) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); ssg->setEnvelopeEnabled(enabled); if (enabled) envSSG_.at(static_cast(ssg->getEnvelopeNumber()))->registerUserInstrument(instNum); else envSSG_.at(static_cast(ssg->getEnvelopeNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentSSGEnvelopeEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeEnabled(); } void InstrumentsManager::setInstrumentSSGEnvelope(int instNum, int envNum) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (ssg->getEnvelopeEnabled()) { envSSG_.at(static_cast(ssg->getEnvelopeNumber()))->deregisterUserInstrument(instNum); envSSG_.at(static_cast(envNum))->registerUserInstrument(instNum); } ssg->setEnvelopeNumber(envNum); } int InstrumentsManager::getInstrumentSSGEnvelope(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeNumber(); } void InstrumentsManager::addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data) { envSSG_.at(static_cast(envNum))->addSequenceUnit(data); } void InstrumentsManager::removeEnvelopeSSGSequenceData(int envNum) { envSSG_.at(static_cast(envNum))->removeSequenceUnit(); } void InstrumentsManager::setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data) { envSSG_.at(static_cast(envNum))->setSequenceUnit(cnt, data); } std::vector InstrumentsManager::getEnvelopeSSGSequence(int envNum) { return envSSG_.at(static_cast(envNum))->getSequence(); } void InstrumentsManager::addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop) { envSSG_.at(static_cast(envNum))->addLoop(loop); } void InstrumentsManager::removeEnvelopeSSGLoop(int envNum, int begin, int end) { envSSG_.at(static_cast(envNum))->removeLoop(begin, end); } void InstrumentsManager::changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { envSSG_.at(static_cast(envNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearEnvelopeSSGLoops(int envNum) { envSSG_.at(static_cast(envNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getEnvelopeSSGLoopRoot(int envNum) const { return envSSG_.at(static_cast(envNum))->getLoopRoot(); } void InstrumentsManager::setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release) { envSSG_.at(static_cast(envNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getEnvelopeSSGRelease(int envNum) const { return envSSG_.at(static_cast(envNum))->getRelease(); } SSGEnvelopeIter InstrumentsManager::getEnvelopeSSGIterator(int envNum) const { return envSSG_.at(static_cast(envNum))->getIterator(); } std::multiset InstrumentsManager::getEnvelopeSSGUsers(int envNum) const { return envSSG_.at(static_cast(envNum))->getUserInstruments(); } std::vector InstrumentsManager::getEnvelopeSSGEntriedIndices() const { return utils::findIndicesIf(envSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableEnvelopeSSG() const { return findFirstAssignableProperty(envSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGArpeggioEnabled(int instNum, bool enabled) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); ssg->setArpeggioEnabled(enabled); if (enabled) arpSSG_.at(static_cast(ssg->getArpeggioNumber()))->registerUserInstrument(instNum); else arpSSG_.at(static_cast(ssg->getArpeggioNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentSSGArpeggioEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioEnabled(); } void InstrumentsManager::setInstrumentSSGArpeggio(int instNum, int arpNum) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (ssg->getArpeggioEnabled()) { arpSSG_.at(static_cast(ssg->getArpeggioNumber()))->deregisterUserInstrument(instNum); arpSSG_.at(static_cast(arpNum))->registerUserInstrument(instNum); } ssg->setArpeggioNumber(arpNum); } int InstrumentsManager::getInstrumentSSGArpeggio(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioNumber(); } void InstrumentsManager::setArpeggioSSGType(int arpNum, SequenceType type) { arpSSG_.at(static_cast(arpNum))->setType(type); } SequenceType InstrumentsManager::getArpeggioSSGType(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getType(); } void InstrumentsManager::addArpeggioSSGSequenceData(int arpNum, int data) { arpSSG_.at(static_cast(arpNum))->addSequenceUnit(ArpeggioUnit(data)); } void InstrumentsManager::removeArpeggioSSGSequenceData(int arpNum) { arpSSG_.at(static_cast(arpNum))->removeSequenceUnit(); } void InstrumentsManager::setArpeggioSSGSequenceData(int arpNum, int cnt, int data) { arpSSG_.at(static_cast(arpNum))->setSequenceUnit(cnt, ArpeggioUnit(data)); } std::vector InstrumentsManager::getArpeggioSSGSequence(int arpNum) { return arpSSG_.at(static_cast(arpNum))->getSequence(); } void InstrumentsManager::addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop) { arpSSG_.at(static_cast(arpNum))->addLoop(loop); } void InstrumentsManager::removeArpeggioSSGLoop(int arpNum, int begin, int end) { arpSSG_.at(static_cast(arpNum))->removeLoop(begin, end); } void InstrumentsManager::changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { arpSSG_.at(static_cast(arpNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearArpeggioSSGLoops(int arpNum) { arpSSG_.at(static_cast(arpNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getArpeggioSSGLoopRoot(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getLoopRoot(); } void InstrumentsManager::setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release) { arpSSG_.at(static_cast(arpNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getArpeggioSSGRelease(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getRelease(); } ArpeggioIter InstrumentsManager::getArpeggioSSGIterator(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getIterator(); } std::multiset InstrumentsManager::getArpeggioSSGUsers(int arpNum) const { return arpSSG_.at(static_cast(arpNum))->getUserInstruments(); } std::vector InstrumentsManager::getArpeggioSSGEntriedIndices() const { return utils::findIndicesIf(arpSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableArpeggioSSG() const { return findFirstAssignableProperty(arpSSG_, regardingUnedited_); } void InstrumentsManager::setInstrumentSSGPitchEnabled(int instNum, bool enabled) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); ssg->setPitchEnabled(enabled); if (enabled) ptSSG_.at(static_cast(ssg->getPitchNumber()))->registerUserInstrument(instNum); else ptSSG_.at(static_cast(ssg->getPitchNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentSSGPitchEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchEnabled(); } void InstrumentsManager::setInstrumentSSGPitch(int instNum, int ptNum) { auto ssg = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (ssg->getPitchEnabled()) { ptSSG_.at(static_cast(ssg->getPitchNumber()))->deregisterUserInstrument(instNum); ptSSG_.at(static_cast(ptNum))->registerUserInstrument(instNum); } ssg->setPitchNumber(ptNum); } int InstrumentsManager::getInstrumentSSGPitch(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchNumber(); } void InstrumentsManager::setPitchSSGType(int ptNum, SequenceType type) { ptSSG_.at(static_cast(ptNum))->setType(type); } SequenceType InstrumentsManager::getPitchSSGType(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getType(); } void InstrumentsManager::addPitchSSGSequenceData(int ptNum, int data) { ptSSG_.at(static_cast(ptNum))->addSequenceUnit(PitchUnit(data)); } void InstrumentsManager::removePitchSSGSequenceData(int ptNum) { ptSSG_.at(static_cast(ptNum))->removeSequenceUnit(); } void InstrumentsManager::setPitchSSGSequenceData(int ptNum, int cnt, int data) { ptSSG_.at(static_cast(ptNum))->setSequenceUnit(cnt, PitchUnit(data)); } std::vector InstrumentsManager::getPitchSSGSequence(int ptNum) { return ptSSG_.at(static_cast(ptNum))->getSequence(); } void InstrumentsManager::addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop) { ptSSG_.at(static_cast(ptNum))->addLoop(loop); } void InstrumentsManager::removePitchSSGLoop(int ptNum, int begin, int end) { ptSSG_.at(static_cast(ptNum))->removeLoop(begin, end); } void InstrumentsManager::changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { ptSSG_.at(static_cast(ptNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearPitchSSGLoops(int ptNum) { ptSSG_.at(static_cast(ptNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getPitchSSGLoopRoot(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getLoopRoot(); } void InstrumentsManager::setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release) { ptSSG_.at(static_cast(ptNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getPitchSSGRelease(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getRelease(); } PitchIter InstrumentsManager::getPitchSSGIterator(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getIterator(); } std::multiset InstrumentsManager::getPitchSSGUsers(int ptNum) const { return ptSSG_.at(static_cast(ptNum))->getUserInstruments(); } std::vector InstrumentsManager::getPitchSSGEntriedIndices() const { return utils::findIndicesIf(ptSSG_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignablePitchSSG() const { return findFirstAssignableProperty(ptSSG_, regardingUnedited_); } bool InstrumentsManager::equalPropertiesSSG(std::shared_ptr a, std::shared_ptr b) const { auto aSsg = std::dynamic_pointer_cast(a); auto bSsg = std::dynamic_pointer_cast(b); if (aSsg->getWaveformEnabled() != bSsg->getWaveformEnabled()) return false; if (aSsg->getWaveformEnabled() && *wfSSG_[aSsg->getWaveformNumber()] != *wfSSG_[bSsg->getWaveformNumber()]) return false; if (aSsg->getToneNoiseEnabled() != bSsg->getToneNoiseEnabled()) return false; if (aSsg->getToneNoiseEnabled() && *tnSSG_[aSsg->getToneNoiseNumber()] != *tnSSG_[bSsg->getToneNoiseNumber()]) return false; if (aSsg->getEnvelopeEnabled() != bSsg->getEnvelopeEnabled()) return false; if (aSsg->getEnvelopeEnabled() && *envSSG_[aSsg->getEnvelopeNumber()] != *envSSG_[bSsg->getEnvelopeNumber()]) return false; if (aSsg->getArpeggioEnabled() != bSsg->getArpeggioEnabled()) return false; if (aSsg->getArpeggioEnabled() && *arpSSG_[aSsg->getArpeggioNumber()] != *arpSSG_[bSsg->getArpeggioNumber()]) return false; if (aSsg->getPitchEnabled() != bSsg->getPitchEnabled()) return false; if (aSsg->getPitchEnabled() && *ptSSG_[aSsg->getPitchNumber()] != *ptSSG_[bSsg->getPitchNumber()]) return false; return true; } //----- ADPCM methods ----- void InstrumentsManager::setInstrumentADPCMSample(int instNum, int sampNum) { auto adpcm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); sampADPCM_.at(static_cast(adpcm->getSampleNumber()))->deregisterUserInstrument(instNum); sampADPCM_.at(static_cast(sampNum))->registerUserInstrument(instNum); adpcm->setSampleNumber(sampNum); } int InstrumentsManager::getInstrumentADPCMSample(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getSampleNumber(); } void InstrumentsManager::setSampleADPCMRootKeyNumber(int sampNum, int n) { sampADPCM_.at(static_cast(sampNum))->setRootKeyNumber(n); } int InstrumentsManager::getSampleADPCMRootKeyNumber(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->getRootKeyNumber(); } void InstrumentsManager::setSampleADPCMRootDeltaN(int sampNum, int dn) { sampADPCM_.at(static_cast(sampNum))->setRootDeltaN(dn); } int InstrumentsManager::getSampleADPCMRootDeltaN(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->getRootDeltaN(); } void InstrumentsManager::setSampleADPCMRepeatEnabled(int sampNum, bool enabled) { sampADPCM_.at(static_cast(sampNum))->setRepeatEnabled(enabled); } bool InstrumentsManager::isSampleADPCMRepeatable(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->isRepeatable(); } void InstrumentsManager::storeSampleADPCMRawSample(int sampNum, const std::vector& sample) { sampADPCM_.at(static_cast(sampNum))->storeSample(sample); } void InstrumentsManager::storeSampleADPCMRawSample(int sampNum, std::vector&& sample) { sampADPCM_.at(static_cast(sampNum))->storeSample(sample); } void InstrumentsManager::clearSampleADPCMRawSample(int sampNum) { sampADPCM_.at(static_cast(sampNum))->clearSample(); } std::vector InstrumentsManager::getSampleADPCMRawSample(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->getSamples(); } void InstrumentsManager::setSampleADPCMStartAddress(int sampNum, size_t addr) { sampADPCM_.at(static_cast(sampNum))->setStartAddress(addr); } size_t InstrumentsManager::getSampleADPCMStartAddress(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->getStartAddress(); } void InstrumentsManager::setSampleADPCMStopAddress(int sampNum, size_t addr) { sampADPCM_.at(static_cast(sampNum))->setStopAddress(addr); } size_t InstrumentsManager::getSampleADPCMStopAddress(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->getStopAddress(); } std::multiset InstrumentsManager::getSampleADPCMUsers(int sampNum) const { return sampADPCM_.at(static_cast(sampNum))->getUserInstruments(); } std::vector InstrumentsManager::getSampleADPCMEntriedIndices() const { return utils::findIndicesIf(sampADPCM_, IsEdited()); } std::vector InstrumentsManager::getSampleADPCMValidIndices() const { return utils::findIndicesIf(sampADPCM_, IsUsedOrEdited()); } void InstrumentsManager::clearUnusedSamplesADPCM() { for (size_t i = 0; i < 128; ++i) { if (!sampADPCM_[i]->isUserInstrument()) sampADPCM_[i] = std::make_shared(i); } } int InstrumentsManager::findFirstAssignableSampleADPCM(int startIndex) const { return findFirstAssignableProperty(sampADPCM_, regardingUnedited_, startIndex); } void InstrumentsManager::setInstrumentADPCMEnvelopeEnabled(int instNum, bool enabled) { auto adpcm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); adpcm->setEnvelopeEnabled(enabled); if (enabled) envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->registerUserInstrument(instNum); else envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentADPCMEnvelopeEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeEnabled(); } void InstrumentsManager::setInstrumentADPCMEnvelope(int instNum, int envNum) { auto adpcm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (adpcm->getEnvelopeEnabled()) { envADPCM_.at(static_cast(adpcm->getEnvelopeNumber()))->deregisterUserInstrument(instNum); envADPCM_.at(static_cast(envNum))->registerUserInstrument(instNum); } adpcm->setEnvelopeNumber(envNum); } int InstrumentsManager::getInstrumentADPCMEnvelope(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getEnvelopeNumber(); } void InstrumentsManager::addEnvelopeADPCMSequenceData(int envNum, int data) { envADPCM_.at(static_cast(envNum))->addSequenceUnit(ADPCMEnvelopeUnit(data)); } void InstrumentsManager::removeEnvelopeADPCMSequenceData(int envNum) { envADPCM_.at(static_cast(envNum))->removeSequenceUnit(); } void InstrumentsManager::setEnvelopeADPCMSequenceData(int envNum, int cnt, int data) { envADPCM_.at(static_cast(envNum))->setSequenceUnit(cnt, ADPCMEnvelopeUnit(data)); } std::vector InstrumentsManager::getEnvelopeADPCMSequence(int envNum) { return envADPCM_.at(static_cast(envNum))->getSequence(); } void InstrumentsManager::addEnvelopeADPCMLoop(int envNum, const InstrumentSequenceLoop& loop) { envADPCM_.at(static_cast(envNum))->addLoop(loop); } void InstrumentsManager::removeEnvelopeADPCMLoop(int envNum, int begin, int end) { envADPCM_.at(static_cast(envNum))->removeLoop(begin, end); } void InstrumentsManager::changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { envADPCM_.at(static_cast(envNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearEnvelopeADPCMLoops(int envNum) { envADPCM_.at(static_cast(envNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getEnvelopeADPCMLoopRoot(int envNum) const { return envADPCM_.at(static_cast(envNum))->getLoopRoot(); } void InstrumentsManager::setEnvelopeADPCMRelease(int envNum, const InstrumentSequenceRelease& release) { envADPCM_.at(static_cast(envNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getEnvelopeADPCMRelease(int envNum) const { return envADPCM_.at(static_cast(envNum))->getRelease(); } ADPCMEnvelopeIter InstrumentsManager::getEnvelopeADPCMIterator(int envNum) const { return envADPCM_.at(static_cast(envNum))->getIterator(); } std::multiset InstrumentsManager::getEnvelopeADPCMUsers(int envNum) const { return envADPCM_.at(static_cast(envNum))->getUserInstruments(); } std::vector InstrumentsManager::getEnvelopeADPCMEntriedIndices() const { return utils::findIndicesIf(envADPCM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableEnvelopeADPCM() const { return findFirstAssignableProperty(envADPCM_, regardingUnedited_); } void InstrumentsManager::setInstrumentADPCMArpeggioEnabled(int instNum, bool enabled) { auto adpcm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); adpcm->setArpeggioEnabled(enabled); if (enabled) arpADPCM_.at(static_cast(adpcm->getArpeggioNumber()))->registerUserInstrument(instNum); else arpADPCM_.at(static_cast(adpcm->getArpeggioNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentADPCMArpeggioEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioEnabled(); } void InstrumentsManager::setInstrumentADPCMArpeggio(int instNum, int arpNum) { auto adpcm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (adpcm->getArpeggioEnabled()) { arpADPCM_.at(static_cast(adpcm->getArpeggioNumber()))->deregisterUserInstrument(instNum); arpADPCM_.at(static_cast(arpNum))->registerUserInstrument(instNum); } adpcm->setArpeggioNumber(arpNum); } int InstrumentsManager::getInstrumentADPCMArpeggio(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getArpeggioNumber(); } void InstrumentsManager::setArpeggioADPCMType(int arpNum, SequenceType type) { arpADPCM_.at(static_cast(arpNum))->setType(type); } SequenceType InstrumentsManager::getArpeggioADPCMType(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getType(); } void InstrumentsManager::addArpeggioADPCMSequenceData(int arpNum, int data) { arpADPCM_.at(static_cast(arpNum))->addSequenceUnit(ArpeggioUnit(data)); } void InstrumentsManager::removeArpeggioADPCMSequenceData(int arpNum) { arpADPCM_.at(static_cast(arpNum))->removeSequenceUnit(); } void InstrumentsManager::setArpeggioADPCMSequenceData(int arpNum, int cnt, int data) { arpADPCM_.at(static_cast(arpNum))->setSequenceUnit(cnt, ArpeggioUnit(data)); } std::vector InstrumentsManager::getArpeggioADPCMSequence(int arpNum) { return arpADPCM_.at(static_cast(arpNum))->getSequence(); } void InstrumentsManager::addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop) { arpADPCM_.at(static_cast(arpNum))->addLoop(loop); } void InstrumentsManager::removeArpeggioADPCMLoop(int arpNum, int begin, int end) { arpADPCM_.at(static_cast(arpNum))->removeLoop(begin, end); } void InstrumentsManager::changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { arpADPCM_.at(static_cast(arpNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearArpeggioADPCMLoops(int arpNum) { arpADPCM_.at(static_cast(arpNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getArpeggioADPCMLoopRoot(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getLoopRoot(); } void InstrumentsManager::setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release) { arpADPCM_.at(static_cast(arpNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getArpeggioADPCMRelease(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getRelease(); } ArpeggioIter InstrumentsManager::getArpeggioADPCMIterator(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getIterator(); } std::multiset InstrumentsManager::getArpeggioADPCMUsers(int arpNum) const { return arpADPCM_.at(static_cast(arpNum))->getUserInstruments(); } std::vector InstrumentsManager::getArpeggioADPCMEntriedIndices() const { return utils::findIndicesIf(arpADPCM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignableArpeggioADPCM() const { return findFirstAssignableProperty(arpADPCM_, regardingUnedited_); } void InstrumentsManager::setInstrumentADPCMPitchEnabled(int instNum, bool enabled) { auto adpcm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); adpcm->setPitchEnabled(enabled); if (enabled) ptADPCM_.at(static_cast(adpcm->getPitchNumber()))->registerUserInstrument(instNum); else ptADPCM_.at(static_cast(adpcm->getPitchNumber()))->deregisterUserInstrument(instNum); } bool InstrumentsManager::getInstrumentADPCMPitchEnabled(int instNum) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchEnabled(); } void InstrumentsManager::setInstrumentADPCMPitch(int instNum, int ptNum) { auto adpcm = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (adpcm->getPitchEnabled()) { ptADPCM_.at(static_cast(adpcm->getPitchNumber()))->deregisterUserInstrument(instNum); ptADPCM_.at(static_cast(ptNum))->registerUserInstrument(instNum); } adpcm->setPitchNumber(ptNum); } int InstrumentsManager::getInstrumentADPCMPitch(int instNum) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getPitchNumber(); } void InstrumentsManager::setPitchADPCMType(int ptNum, SequenceType type) { ptADPCM_.at(static_cast(ptNum))->setType(type); } SequenceType InstrumentsManager::getPitchADPCMType(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getType(); } void InstrumentsManager::addPitchADPCMSequenceData(int ptNum, int data) { ptADPCM_.at(static_cast(ptNum))->addSequenceUnit(PitchUnit(data)); } void InstrumentsManager::removePitchADPCMSequenceData(int ptNum) { ptADPCM_.at(static_cast(ptNum))->removeSequenceUnit(); } void InstrumentsManager::setPitchADPCMSequenceData(int ptNum, int cnt, int data) { ptADPCM_.at(static_cast(ptNum))->setSequenceUnit(cnt, PitchUnit(data)); } std::vector InstrumentsManager::getPitchADPCMSequence(int ptNum) { return ptADPCM_.at(static_cast(ptNum))->getSequence(); } void InstrumentsManager::addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop) { ptADPCM_.at(static_cast(ptNum))->addLoop(loop); } void InstrumentsManager::removePitchADPCMLoop(int ptNum, int begin, int end) { ptADPCM_.at(static_cast(ptNum))->removeLoop(begin, end); } void InstrumentsManager::changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { ptADPCM_.at(static_cast(ptNum))->changeLoop(prevBegin, prevEnd, loop); } void InstrumentsManager::clearPitchADPCMLoops(int ptNum) { ptADPCM_.at(static_cast(ptNum))->clearLoops(); } InstrumentSequenceLoopRoot InstrumentsManager::getPitchADPCMLoopRoot(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getLoopRoot(); } void InstrumentsManager::setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release) { ptADPCM_.at(static_cast(ptNum))->setRelease(release); } InstrumentSequenceRelease InstrumentsManager::getPitchADPCMRelease(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getRelease(); } PitchIter InstrumentsManager::getPitchADPCMIterator(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getIterator(); } std::multiset InstrumentsManager::getPitchADPCMUsers(int ptNum) const { return ptADPCM_.at(static_cast(ptNum))->getUserInstruments(); } std::vector InstrumentsManager::getPitchADPCMEntriedIndices() const { return utils::findIndicesIf(ptADPCM_, IsUsedOrEdited()); } int InstrumentsManager::findFirstAssignablePitchADPCM() const { return findFirstAssignableProperty(ptADPCM_, regardingUnedited_); } bool InstrumentsManager::equalPropertiesADPCM(std::shared_ptr a, std::shared_ptr b) const { auto aAdpcm = std::dynamic_pointer_cast(a); auto bAdpcm = std::dynamic_pointer_cast(b); if (*sampADPCM_[aAdpcm->getSampleNumber()] != *sampADPCM_[bAdpcm->getSampleNumber()]) return false; if (aAdpcm->getEnvelopeEnabled() != bAdpcm->getEnvelopeEnabled()) return false; if (aAdpcm->getEnvelopeEnabled() && *envADPCM_[aAdpcm->getEnvelopeNumber()] != *envADPCM_[bAdpcm->getEnvelopeNumber()]) return false; if (aAdpcm->getArpeggioEnabled() != bAdpcm->getArpeggioEnabled()) return false; if (aAdpcm->getArpeggioEnabled() && *arpADPCM_[aAdpcm->getArpeggioNumber()] != *arpADPCM_[bAdpcm->getArpeggioNumber()]) return false; if (aAdpcm->getPitchEnabled() != bAdpcm->getPitchEnabled()) return false; if (aAdpcm->getPitchEnabled() && *ptADPCM_[aAdpcm->getPitchNumber()] != *ptADPCM_[bAdpcm->getPitchNumber()]) return false; return true; } //----- Drumkit methods ----- void InstrumentsManager::setInstrumentDrumkitSamplesEnabled(int instNum, int key, bool enabled) { auto kit = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); if (enabled) { kit->setSampleEnabled(key, true); sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->registerUserInstrument(instNum); } else { sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->deregisterUserInstrument(instNum); kit->setSampleEnabled(key, false); } } bool InstrumentsManager::getInstrumentDrumkitSamplesEnabled(int instNum, int key) const { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getSampleEnabled(key); } void InstrumentsManager::setInstrumentDrumkitSamples(int instNum, int key, int sampNum) { auto kit = std::dynamic_pointer_cast(insts_.at(static_cast(instNum))); sampADPCM_.at(static_cast(kit->getSampleNumber(key)))->deregisterUserInstrument(instNum); sampADPCM_.at(static_cast(sampNum))->registerUserInstrument(instNum); kit->setSampleNumber(key, sampNum); } int InstrumentsManager::getInstrumentDrumkitSamples(int instNum, int key) { return std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->getSampleNumber(key); } void InstrumentsManager::setInstrumentDrumkitPitch(int instNum, int key, int pitch) { std::dynamic_pointer_cast(insts_.at(static_cast(instNum)))->setPitch(key, pitch); } bool InstrumentsManager::equalPropertiesDrumkit(std::shared_ptr a, std::shared_ptr b) const { auto aKit = std::dynamic_pointer_cast(a); auto bKit = std::dynamic_pointer_cast(b); std::vector aKeys = aKit->getAssignedKeys(); std::vector bKeys = bKit->getAssignedKeys(); if (aKeys.size() != bKeys.size()) return false; std::sort(aKeys.begin(), aKeys.end()); std::sort(bKeys.begin(), bKeys.end()); if (!std::includes(aKeys.cbegin(), aKeys.cend(), bKeys.cbegin(), bKeys.cend())) return false; for (const int& key : aKeys) { if (*sampADPCM_[aKit->getSampleNumber(key)] != *sampADPCM_[bKit->getSampleNumber(key)]) return false; if (aKit->getPitch(key) != bKit->getPitch(key)) return false; } return true; } BambooTracker-0.4.6/BambooTracker/instrument/instruments_manager.hpp000066400000000000000000000520431401124043500257530ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include "instrument.hpp" #include "envelope_fm.hpp" #include "lfo_fm.hpp" #include "sample_adpcm.hpp" #include "sequence_property.hpp" #include "instrument_property_defs.hpp" #include "enum_hash.hpp" enum class InstrumentType; class AbstractInstrument; class InstrumentFM; enum class FMEnvelopeParameter; class EnvelopeFM; class InstrumentSSG; class InstrumentADPCM; class InstrumentDrumkit; class InstrumentsManager { public: explicit InstrumentsManager(bool unedited); void addInstrument(int instNum, InstrumentType type, const std::string& name); void addInstrument(AbstractInstrument* newInstPtr); std::unique_ptr removeInstrument(int instNum); void cloneInstrument(int cloneInstNum, int resInstNum); void deepCloneInstrument(int cloneInstNum, int resInstNum); void swapInstruments(int inst1Num, int inst2Num); std::shared_ptr getInstrumentSharedPtr(int instNum); void clearAll(); std::vector getInstrumentIndices() const; void setInstrumentName(int instNum, const std::string& name); std::string getInstrumentName(int instNum) const; std::vector getInstrumentNameList() const; void clearUnusedInstrumentProperties(); int findFirstFreeInstrument() const; std::unordered_map getDuplicateInstrumentMap() const; inline void setPropertyFindMode(bool unedited) noexcept { regardingUnedited_ = unedited; } private: std::array, 128> insts_; bool regardingUnedited_; //----- FM methods ----- public: void setInstrumentFMEnvelope(int instNum, int envNum); int getInstrumentFMEnvelope(int instNum) const; void setEnvelopeFMParameter(int envNum, FMEnvelopeParameter param, int value); int getEnvelopeFMParameter(int envNum, FMEnvelopeParameter param) const; void setEnvelopeFMOperatorEnabled(int envNum, int opNum, bool enabled); bool getEnvelopeFMOperatorEnabled(int envNum, int opNum) const; std::multiset getEnvelopeFMUsers(int envNum) const; std::vector getEnvelopeFMEntriedIndices() const; int findFirstAssignableEnvelopeFM() const; void setInstrumentFMLFOEnabled(int instNum, bool enabled); bool getInstrumentFMLFOEnabled(int instNum) const; void setInstrumentFMLFO(int instNum, int lfoNum); int getInstrumentFMLFO(int instNum) const; void setLFOFMParameter(int lfoNum, FMLFOParameter param, int value); int getLFOFMparameter(int lfoNum, FMLFOParameter param) const; std::multiset getLFOFMUsers(int lfoNum) const; std::vector getLFOFMEntriedIndices() const; int findFirstAssignableLFOFM() const; void setInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param, bool enabled); bool getInstrumentFMOperatorSequenceEnabled(int instNum, FMEnvelopeParameter param) const; void setInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param, int opSeqNum); int getInstrumentFMOperatorSequence(int instNum, FMEnvelopeParameter param); void addOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int data); void removeOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum); void setOperatorSequenceFMSequenceData(FMEnvelopeParameter param, int opSeqNum, int cnt, int data); std::vector getOperatorSequenceFMSequence(FMEnvelopeParameter param, int opSeqNum); void addOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceLoop& loop); void removeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int begin, int end); void changeOperatorSequenceFMLoop(FMEnvelopeParameter param, int opSeqNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearOperatorSequenceFMLoops(FMEnvelopeParameter param, int opSeqNum); InstrumentSequenceLoopRoot getOperatorSequenceFMLoopRoot(FMEnvelopeParameter param, int opSeqNum) const; void setOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getOperatorSequenceFMRelease(FMEnvelopeParameter param, int opSeqNum) const; FMOperatorSequenceIter getOperatorSequenceFMIterator(FMEnvelopeParameter param, int opSeqNum) const; std::multiset getOperatorSequenceFMUsers(FMEnvelopeParameter param, int opSeqNum) const; std::vector getOperatorSequenceFMEntriedIndices(FMEnvelopeParameter param) const; int findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter param) const; void setInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op, bool enabled); bool getInstrumentFMArpeggioEnabled(int instNum, FMOperatorType op) const; void setInstrumentFMArpeggio(int instNum, FMOperatorType op, int arpNum); int getInstrumentFMArpeggio(int instNum, FMOperatorType op); void setArpeggioFMType(int arpNum, SequenceType type); SequenceType getArpeggioFMType(int arpNum) const; void addArpeggioFMSequenceData(int arpNum, int data); void removeArpeggioFMSequenceData(int arpNum); void setArpeggioFMSequenceData(int arpNum, int cnt, int data); std::vector getArpeggioFMSequence(int arpNum); void addArpeggioFMLoop(int arpNum, const InstrumentSequenceLoop& loop); void removeArpeggioFMLoop(int arpNum, int begin, int end); void changeArpeggioFMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearArpeggioFMLoops(int arpNum); InstrumentSequenceLoopRoot getArpeggioFMLoopRoot(int arpNum) const; void setArpeggioFMRelease(int arpNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getArpeggioFMRelease(int arpNum) const; ArpeggioIter getArpeggioFMIterator(int arpNum) const; std::multiset getArpeggioFMUsers(int arpNum) const; std::vector getArpeggioFMEntriedIndices() const; int findFirstAssignableArpeggioFM() const; void setInstrumentFMPitchEnabled(int instNum, FMOperatorType op, bool enabled); bool getInstrumentFMPitchEnabled(int instNum, FMOperatorType op) const; void setInstrumentFMPitch(int instNum, FMOperatorType op, int ptNum); int getInstrumentFMPitch(int instNum, FMOperatorType op); void setPitchFMType(int ptNum, SequenceType type); SequenceType getPitchFMType(int ptNum) const; void addPitchFMSequenceData(int ptNum, int data); void removePitchFMSequenceData(int ptNum); void setPitchFMSequenceData(int ptNum, int cnt, int data); std::vector getPitchFMSequence(int ptNum); void addPitchFMLoop(int ptNum, const InstrumentSequenceLoop& loop); void removePitchFMLoop(int ptNum, int begin, int end); void changePitchFMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearPitchFMLoops(int ptNum); InstrumentSequenceLoopRoot getPitchFMLoopRoot(int ptNum) const; void setPitchFMRelease(int ptNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getPitchFMRelease(int ptNum) const; PitchIter getPitchFMIterator(int ptNum) const; std::multiset getPitchFMUsers(int ptNum) const; std::vector getPitchFMEntriedIndices() const; int findFirstAssignablePitchFM() const; void setInstrumentFMEnvelopeResetEnabled(int instNum, FMOperatorType op, bool enabled); private: std::array, 128> envFM_; std::array, 128> lfoFM_; std::unordered_map>, 128>> opSeqFM_; std::array>, 128> arpFM_; std::array>, 128> ptFM_; bool equalPropertiesFM(std::shared_ptr a, std::shared_ptr b) const; //----- SSG methods ----- public: void setInstrumentSSGWaveformEnabled(int instNum, bool enabled); bool getInstrumentSSGWaveformEnabled(int instNum) const; void setInstrumentSSGWaveform(int instNum, int wfNum); int getInstrumentSSGWaveform(int instNum); void addWaveformSSGSequenceData(int wfNum, const SSGWaveformUnit& data); void removeWaveformSSGSequenceData(int wfNum); void setWaveformSSGSequenceData(int wfNum, int cnt, const SSGWaveformUnit& data); std::vector getWaveformSSGSequence(int wfNum); void addWaveformSSGLoop(int wfNum, const InstrumentSequenceLoop& loop); void removeWaveformSSGLoop(int wfNum, int begin, int end); void changeWaveformSSGLoop(int wfNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearWaveformSSGLoops(int wfNum); InstrumentSequenceLoopRoot getWaveformSSGLoopRoot(int wfNum) const; void setWaveformSSGRelease(int wfNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getWaveformSSGRelease(int wfNum) const; SSGWaveformIter getWaveformSSGIterator(int wfNum) const; std::multiset getWaveformSSGUsers(int wfNum) const; std::vector getWaveformSSGEntriedIndices() const; int findFirstAssignableWaveformSSG() const; void setInstrumentSSGToneNoiseEnabled(int instNum, bool enabled); bool getInstrumentSSGToneNoiseEnabled(int instNum) const; void setInstrumentSSGToneNoise(int instNum, int tnNum); int getInstrumentSSGToneNoise(int instNum); void addToneNoiseSSGSequenceData(int tnNum, int data); void removeToneNoiseSSGSequenceData(int tnNum); void setToneNoiseSSGSequenceData(int tnNum, int cnt, int data); std::vector getToneNoiseSSGSequence(int tnNum); void addToneNoiseSSGLoop(int tnNum, const InstrumentSequenceLoop& loop); void removeToneNoiseSSGLoop(int tnNum, int begin, int end); void changeToneNoiseSSGLoop(int tnNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearToneNoiseSSGLoops(int tnNum); InstrumentSequenceLoopRoot getToneNoiseSSGLoopRoot(int tnNum) const; void setToneNoiseSSGRelease(int tnNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getToneNoiseSSGRelease(int tnNum) const; SSGToneNoiseIter getToneNoiseSSGIterator(int tnNum) const; std::multiset getToneNoiseSSGUsers(int tnNum) const; std::vector getToneNoiseSSGEntriedIndices() const; int findFirstAssignableToneNoiseSSG() const; void setInstrumentSSGEnvelopeEnabled(int instNum, bool enabled); bool getInstrumentSSGEnvelopeEnabled(int instNum) const; void setInstrumentSSGEnvelope(int instNum, int envNum); int getInstrumentSSGEnvelope(int instNum); void addEnvelopeSSGSequenceData(int envNum, const SSGEnvelopeUnit& data); void removeEnvelopeSSGSequenceData(int envNum); void setEnvelopeSSGSequenceData(int envNum, int cnt, const SSGEnvelopeUnit& data); std::vector getEnvelopeSSGSequence(int envNum); void addEnvelopeSSGLoop(int envNum, const InstrumentSequenceLoop& loop); void removeEnvelopeSSGLoop(int envNum, int begin, int end); void changeEnvelopeSSGLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearEnvelopeSSGLoops(int envNum); InstrumentSequenceLoopRoot getEnvelopeSSGLoopRoot(int envNum) const; void setEnvelopeSSGRelease(int envNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getEnvelopeSSGRelease(int envNum) const; SSGEnvelopeIter getEnvelopeSSGIterator(int envNum) const; std::multiset getEnvelopeSSGUsers(int envNum) const; std::vector getEnvelopeSSGEntriedIndices() const; int findFirstAssignableEnvelopeSSG() const; void setInstrumentSSGArpeggioEnabled(int instNum, bool enabled); bool getInstrumentSSGArpeggioEnabled(int instNum) const; void setInstrumentSSGArpeggio(int instNum, int arpNum); int getInstrumentSSGArpeggio(int instNum); void setArpeggioSSGType(int arpNum, SequenceType type); SequenceType getArpeggioSSGType(int arpNum) const; void addArpeggioSSGSequenceData(int arpNum, int data); void removeArpeggioSSGSequenceData(int arpNum); void setArpeggioSSGSequenceData(int arpNum, int cnt, int data); std::vector getArpeggioSSGSequence(int arpNum); void addArpeggioSSGLoop(int arpNum, const InstrumentSequenceLoop& loop); void removeArpeggioSSGLoop(int arpNum, int begin, int end); void changeArpeggioSSGLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearArpeggioSSGLoops(int arpNum); InstrumentSequenceLoopRoot getArpeggioSSGLoopRoot(int arpNum) const; void setArpeggioSSGRelease(int arpNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getArpeggioSSGRelease(int arpNum) const; ArpeggioIter getArpeggioSSGIterator(int arpNum) const; std::multiset getArpeggioSSGUsers(int arpNum) const; std::vector getArpeggioSSGEntriedIndices() const; int findFirstAssignableArpeggioSSG() const; void setInstrumentSSGPitchEnabled(int instNum, bool enabled); bool getInstrumentSSGPitchEnabled(int instNum) const; void setInstrumentSSGPitch(int instNum, int ptNum); int getInstrumentSSGPitch(int instNum); void setPitchSSGType(int ptNum, SequenceType type); SequenceType getPitchSSGType(int ptNum) const; void addPitchSSGSequenceData(int ptNum, int data); void removePitchSSGSequenceData(int ptNum); void setPitchSSGSequenceData(int ptNum, int cnt, int data); std::vector getPitchSSGSequence(int ptNum); void addPitchSSGLoop(int ptNum, const InstrumentSequenceLoop& loop); void removePitchSSGLoop(int ptNum, int begin, int end); void changePitchSSGLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearPitchSSGLoops(int ptNum); InstrumentSequenceLoopRoot getPitchSSGLoopRoot(int ptNum) const; void setPitchSSGRelease(int ptNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getPitchSSGRelease(int ptNum) const; PitchIter getPitchSSGIterator(int ptNum) const; std::multiset getPitchSSGUsers(int ptNum) const; std::vector getPitchSSGEntriedIndices() const; int findFirstAssignablePitchSSG() const; private: std::array>, 128> wfSSG_; std::array>, 128> envSSG_; std::array>, 128> tnSSG_; std::array>, 128> arpSSG_; std::array>, 128> ptSSG_; bool equalPropertiesSSG(std::shared_ptr a, std::shared_ptr b) const; //----- ADPCM methods ----- public: void setInstrumentADPCMSample(int instNum, int sampNum); int getInstrumentADPCMSample(int instNum); void setSampleADPCMRootKeyNumber(int sampNum, int n); int getSampleADPCMRootKeyNumber(int sampNum) const; void setSampleADPCMRootDeltaN(int sampNum, int dn); int getSampleADPCMRootDeltaN(int sampNum) const; void setSampleADPCMRepeatEnabled(int sampNum, bool enabled); bool isSampleADPCMRepeatable(int sampNum) const; void storeSampleADPCMRawSample(int sampNum, const std::vector& sample); void storeSampleADPCMRawSample(int sampNum, std::vector&& sample); void clearSampleADPCMRawSample(int sampNum); std::vector getSampleADPCMRawSample(int sampNum) const; void setSampleADPCMStartAddress(int sampNum, size_t addr); size_t getSampleADPCMStartAddress(int sampNum) const; void setSampleADPCMStopAddress(int sampNum, size_t addr); size_t getSampleADPCMStopAddress(int sampNum) const; std::multiset getSampleADPCMUsers(int sampNum) const; std::vector getSampleADPCMEntriedIndices() const; std::vector getSampleADPCMValidIndices() const; void clearUnusedSamplesADPCM(); int findFirstAssignableSampleADPCM(int startIndex = 0) const; void setInstrumentADPCMEnvelopeEnabled(int instNum, bool enabled); bool getInstrumentADPCMEnvelopeEnabled(int instNum) const; void setInstrumentADPCMEnvelope(int instNum, int envNum); int getInstrumentADPCMEnvelope(int instNum); void addEnvelopeADPCMSequenceData(int envNum, int data); void removeEnvelopeADPCMSequenceData(int envNum); void setEnvelopeADPCMSequenceData(int envNum, int cnt, int data); std::vector getEnvelopeADPCMSequence(int envNum); void addEnvelopeADPCMLoop(int envNum, const InstrumentSequenceLoop& loop); void removeEnvelopeADPCMLoop(int envNum, int begin, int end); void changeEnvelopeADPCMLoop(int envNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearEnvelopeADPCMLoops(int envNum); InstrumentSequenceLoopRoot getEnvelopeADPCMLoopRoot(int envNum) const; void setEnvelopeADPCMRelease(int envNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getEnvelopeADPCMRelease(int envNum) const; ADPCMEnvelopeIter getEnvelopeADPCMIterator(int envNum) const; std::multiset getEnvelopeADPCMUsers(int envNum) const; std::vector getEnvelopeADPCMEntriedIndices() const; int findFirstAssignableEnvelopeADPCM() const; void setInstrumentADPCMArpeggioEnabled(int instNum, bool enabled); bool getInstrumentADPCMArpeggioEnabled(int instNum) const; void setInstrumentADPCMArpeggio(int instNum, int arpNum); int getInstrumentADPCMArpeggio(int instNum); void setArpeggioADPCMType(int arpNum, SequenceType type); SequenceType getArpeggioADPCMType(int arpNum) const; void addArpeggioADPCMSequenceData(int arpNum, int data); void removeArpeggioADPCMSequenceData(int arpNum); void setArpeggioADPCMSequenceData(int arpNum, int cnt, int data); std::vector getArpeggioADPCMSequence(int arpNum); void addArpeggioADPCMLoop(int arpNum, const InstrumentSequenceLoop& loop); void removeArpeggioADPCMLoop(int arpNum, int begin, int end); void changeArpeggioADPCMLoop(int arpNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearArpeggioADPCMLoops(int arpNum); InstrumentSequenceLoopRoot getArpeggioADPCMLoopRoot(int arpNum) const; void setArpeggioADPCMRelease(int arpNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getArpeggioADPCMRelease(int arpNum) const; ArpeggioIter getArpeggioADPCMIterator(int arpNum) const; std::multiset getArpeggioADPCMUsers(int arpNum) const; std::vector getArpeggioADPCMEntriedIndices() const; int findFirstAssignableArpeggioADPCM() const; void setInstrumentADPCMPitchEnabled(int instNum, bool enabled); bool getInstrumentADPCMPitchEnabled(int instNum) const; void setInstrumentADPCMPitch(int instNum, int ptNum); int getInstrumentADPCMPitch(int instNum); void setPitchADPCMType(int ptNum, SequenceType type); SequenceType getPitchADPCMType(int ptNum) const; void addPitchADPCMSequenceData(int ptNum, int data); void removePitchADPCMSequenceData(int ptNum); void setPitchADPCMSequenceData(int ptNum, int cnt, int data); std::vector getPitchADPCMSequence(int ptNum); void addPitchADPCMLoop(int ptNum, const InstrumentSequenceLoop& loop); void removePitchADPCMLoop(int ptNum, int begin, int end); void changePitchADPCMLoop(int ptNum, int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void clearPitchADPCMLoops(int ptNum); InstrumentSequenceLoopRoot getPitchADPCMLoopRoot(int ptNum) const; void setPitchADPCMRelease(int ptNum, const InstrumentSequenceRelease& release); InstrumentSequenceRelease getPitchADPCMRelease(int ptNum) const; PitchIter getPitchADPCMIterator(int ptNum) const; std::multiset getPitchADPCMUsers(int ptNum) const; std::vector getPitchADPCMEntriedIndices() const; int findFirstAssignablePitchADPCM() const; private: std::array, 128> sampADPCM_; std::array>, 128> envADPCM_; std::array>, 128> arpADPCM_; std::array>, 128> ptADPCM_; bool equalPropertiesADPCM(std::shared_ptr a, std::shared_ptr b) const; //----- Drumkit methods ----- public: void setInstrumentDrumkitSamplesEnabled(int instNum, int key, bool enabled); bool getInstrumentDrumkitSamplesEnabled(int instNum, int key) const; void setInstrumentDrumkitSamples(int instNum, int key, int sampNum); int getInstrumentDrumkitSamples(int instNum, int key); void setInstrumentDrumkitPitch(int instNum, int key, int pitch); private: bool equalPropertiesDrumkit(std::shared_ptr a, std::shared_ptr b) const; }; BambooTracker-0.4.6/BambooTracker/instrument/lfo_fm.cpp000066400000000000000000000037221401124043500231230ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "lfo_fm.hpp" namespace { const std::unordered_map DEF_PARAMS = { { FMLFOParameter::FREQ, 0 }, { FMLFOParameter::PMS, 0 }, { FMLFOParameter::AMS, 0 }, { FMLFOParameter::AM1, 0 }, { FMLFOParameter::AM2, 0 }, { FMLFOParameter::AM3, 0 }, { FMLFOParameter::AM4, 0 }, { FMLFOParameter::Count, 0 } }; } LFOFM::LFOFM(int n) : AbstractInstrumentProperty (n) { clearParameters(); } std::unique_ptr LFOFM::clone() { std::unique_ptr clone = std::make_unique(*this); clone->clearUserInstruments(); return clone; } void LFOFM::setParameterValue(FMLFOParameter param, int value) { params_.at(param) = value; } int LFOFM::getParameterValue(FMLFOParameter param) const { return params_.at(param); } bool LFOFM::isEdited() const { return params_ != DEF_PARAMS; } void LFOFM::clearParameters() { params_ = DEF_PARAMS; } BambooTracker-0.4.6/BambooTracker/instrument/lfo_fm.hpp000066400000000000000000000035071401124043500231310ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "abstract_instrument_property.hpp" #include "enum_hash.hpp" enum class FMLFOParameter { FREQ, AMS, PMS, Count, AM1, AM2, AM3, AM4 }; class LFOFM final : public AbstractInstrumentProperty { public: explicit LFOFM(int n); friend bool operator==(const LFOFM& a, const LFOFM& b) { return a.params_ == b.params_; } friend bool operator!=(const LFOFM& a, const LFOFM& b) { return !(a == b); } std::unique_ptr clone(); void setParameterValue(FMLFOParameter param, int value); int getParameterValue(FMLFOParameter param) const; bool isEdited() const override; void clearParameters() override; private: std::unordered_map params_; }; BambooTracker-0.4.6/BambooTracker/instrument/sample_adpcm.cpp000066400000000000000000000042441401124043500243060ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "sample_adpcm.hpp" namespace { constexpr int DEF_RT_DELTAN_ = 0x49cd; // 16000Hz constexpr bool DEF_REPET_ = false; } SampleADPCM::SampleADPCM(int num) : AbstractInstrumentProperty (num) { clearParameters(); } bool operator==(const SampleADPCM& a, const SampleADPCM& b) { return (a.rootKeyNum_ == b.rootKeyNum_ && a.rootDeltaN_ == b.rootDeltaN_ && a.isRepeated_ == b.isRepeated_ && a.sample_ == b.sample_); } std::unique_ptr SampleADPCM::clone() { std::unique_ptr clone = std::make_unique(*this); clone->clearUserInstruments(); return clone; } void SampleADPCM::clearSample() { startAddress_ = 0; stopAddress_ = 0; sample_ = std::vector(1); } bool SampleADPCM::isEdited() const { if (rootKeyNum_ != DEF_ROOT_KEY || rootDeltaN_ != DEF_RT_DELTAN_ || isRepeated_ != DEF_REPET_ || sample_.size() != 1 || sample_.front() != 0) return true; return false; } void SampleADPCM::clearParameters() { rootKeyNum_ = DEF_ROOT_KEY; rootDeltaN_ = DEF_RT_DELTAN_; isRepeated_ = DEF_REPET_; clearSample(); } BambooTracker-0.4.6/BambooTracker/instrument/sample_adpcm.hpp000066400000000000000000000055361401124043500243200ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include "abstract_instrument_property.hpp" class SampleADPCM final : public AbstractInstrumentProperty { public: explicit SampleADPCM(int num); friend bool operator==(const SampleADPCM& a, const SampleADPCM& b); friend bool operator!=(const SampleADPCM& a, const SampleADPCM& b) { return !(a == b); } std::unique_ptr clone(); inline void setRootKeyNumber(int n) noexcept { rootKeyNum_ = n; } inline int getRootKeyNumber() const noexcept {return rootKeyNum_; } inline void setRootDeltaN(int dn) noexcept { rootDeltaN_ = dn; } inline int getRootDeltaN() const noexcept { return rootDeltaN_; } inline void setRepeatEnabled(bool enabled) noexcept { isRepeated_ = enabled; } inline bool isRepeatable() const noexcept { return isRepeated_; } inline void storeSample(const std::vector& sample) { sample_ = sample; } inline void storeSample(std::vector&& sample) { sample_ = std::move(sample); } inline std::vector getSamples() const { return sample_; } void clearSample(); inline void setStartAddress(size_t addr) noexcept { startAddress_ = addr; } inline size_t getStartAddress() const noexcept { return startAddress_; } inline void setStopAddress(size_t addr) noexcept { stopAddress_ = addr; } inline size_t getStopAddress() const noexcept { return stopAddress_; } bool isEdited() const override; void clearParameters() override; static constexpr int DEF_ROOT_KEY = 60; // C5 inline static int calculateADPCMDeltaN(unsigned int rate) { return static_cast(std::round((rate << 16) / 55500.)); } private: int rootKeyNum_, rootDeltaN_; bool isRepeated_; std::vector sample_; size_t startAddress_, stopAddress_; }; BambooTracker-0.4.6/BambooTracker/instrument/sequence_iterator_interface.hpp000066400000000000000000000033661401124043500274330ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once enum class SequenceType { PlainSequence, AbsoluteSequence, FixedSequence, RelativeSequence }; template class SequenceIteratorInterface { public: virtual ~SequenceIteratorInterface() = default; static constexpr int END_SEQ_POS = -1; inline int pos() const noexcept { return pos_; } inline bool hasEnded() const noexcept { return pos_ == END_SEQ_POS; } virtual SequenceType type() const = 0; virtual T data() const = 0; virtual int next() = 0; virtual int front() = 0; virtual int release() = 0; virtual int end() = 0; protected: explicit SequenceIteratorInterface(int initPos = 0) : pos_(initPos) {} int pos_; }; BambooTracker-0.4.6/BambooTracker/instrument/sequence_property.cpp000066400000000000000000000212221401124043500254300ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "sequence_property.hpp" #include "utils.hpp" InstrumentSequenceBaseUnit::InstrumentSequenceBaseUnit() noexcept : data(ERR_DATA) { } InstrumentSequenceBaseUnit::InstrumentSequenceBaseUnit(int d) noexcept : data(d) { } InstrumentSequenceExtendUnit::InstrumentSequenceExtendUnit() noexcept : InstrumentSequenceBaseUnit(), type(InstrumentSequenceExtendUnit::UnusedSubdata), subdata(InstrumentSequenceBaseUnit::ERR_DATA) { } InstrumentSequenceExtendUnit::InstrumentSequenceExtendUnit(int d, SubdataType subType, int subData) noexcept : InstrumentSequenceBaseUnit(d), type(subType), subdata(subData) { } InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeOnlyDataUnit(int data) noexcept { return InstrumentSequenceExtendUnit(data, SubdataType::UnusedSubdata, ERR_DATA); } InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeRawUnit(int data, int sub) noexcept { return InstrumentSequenceExtendUnit(data, SubdataType::RawSubdata, sub); } InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeRatioUnit(int data, int subFirst, int subSecond) noexcept { return InstrumentSequenceExtendUnit(data, SubdataType::RatioSubdata, ((1 << 16) | (subFirst << 8) | subSecond)); } InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeShiftUnit(int data, int rshift) noexcept { int sub = (rshift > 0) ? ((2 << 16) | rshift) : ((3 << 16) | -rshift); return InstrumentSequenceExtendUnit(data, SubdataType::ShiftSubdata, sub); } InstrumentSequenceExtendUnit InstrumentSequenceExtendUnit::makeUnitWithDecode(int data, int subsrc) noexcept { SubdataType type; if (subsrc & 0x20000) type = SubdataType::ShiftSubdata; else type = (subsrc & 0x10000) ? SubdataType::RatioSubdata : SubdataType::RawSubdata; return InstrumentSequenceExtendUnit(data, type, subsrc); } void InstrumentSequenceExtendUnit::getSubdataAsRaw(int& raw) const noexcept { raw = subdata; } void InstrumentSequenceExtendUnit::getSubdataAsRatio(int& first, int& second) const noexcept { first = (subdata & 0x0ff00) >> 8; second = subdata & 0x000ff; } void InstrumentSequenceExtendUnit::getSubdataAsShift(int& rshift) const noexcept { rshift = (subdata & 0x10000) ? -(0x0ffff & data) : (0x0ffff & data); } namespace { inline InstrumentSequenceLoop::Ptr makeSequenceLoopPtr(const InstrumentSequenceLoop& loop) { return std::make_shared(loop); } } InstrumentSequenceLoop::InstrumentSequenceLoop(int begin, int end, int times) : begin_(begin), end_(end), times_(times) { } void InstrumentSequenceLoop::setBeginPos(int pos) { if (begin_ < pos && hasInnerLoop()) { auto&& it = utils::findIf(childs_, [pos](const auto& pair) { return pos <= pair.second->end_; }); if (it == childs_.end()) { removeAllInnerLoops(); } else { if (it->second->begin_ < pos) it->second->setBeginPos(pos); childs_.erase(childs_.begin(), it); } } begin_ = pos; } void InstrumentSequenceLoop::setEndPos(int pos) { if (pos < end_ && hasInnerLoop()) { auto&& it = std::find_if(childs_.rbegin(), childs_.rend(), [pos](const auto& pair) { return pair.second->begin_ <= pos; }); if (it == childs_.rend()) { removeAllInnerLoops(); } else { if (pos < it->second->end_) it->second->setEndPos(pos); childs_.erase(it.base(), childs_.end()); } } end_ = pos; } bool InstrumentSequenceLoop::addInnerLoop(const InstrumentSequenceLoop& inner) { for (auto& pair : childs_) { Ptr& loop = pair.second; if (loop->isOverlapped(inner)) { if (loop->hasSameRegion(inner)) { loop->times_ = inner.times_; return true; } else if (loop->isContainable(inner)) { return loop->addInnerLoop(inner); } else { // Illegal region return false; } } else if (loop->begin_ < inner.begin_) break; } // Add new region childs_.insert(std::make_pair(inner.begin_, makeSequenceLoopPtr(inner))); return true; } bool InstrumentSequenceLoop::changeInnerLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { if (hasInnerLoopBeginAt(prevBegin)) { InstrumentSequenceLoop::Ptr lpt = getInnerLoopBeginAt(prevBegin); if (lpt->end_ != prevEnd) return lpt->changeInnerLoop(prevBegin, prevEnd, loop); if (prevBegin != loop.begin_) lpt->setBeginPos(loop.begin_); if (prevEnd != loop.end_) lpt->setEndPos(loop.end_); if (lpt->times_ != loop.times_) lpt->setTimes(loop.times_); return true; } else { return false; } } void InstrumentSequenceLoop::removeInnerLoop(int begin, int end) { for (auto& pair : childs_) { Ptr& loop = pair.second; if (loop->isOverlapped(begin, end)) { if (loop->hasSameRegion(begin, end)) { childs_.erase(pair.first); } else if (loop->isContainable(begin, end)) { loop->removeInnerLoop(begin, end); } return; } else if (loop->begin_ < begin) return; } } void InstrumentSequenceLoop::removeAllInnerLoops() { childs_.clear(); } std::vector InstrumentSequenceLoop::getAllInnerLoops() const { std::vector list; for (const auto& pair : childs_) { list.push_back(*pair.second); auto in = pair.second->getAllInnerLoops(); list.insert(list.end(), in.begin(), in.end()); } return list; } bool InstrumentSequenceLoop::isOverlapped(int begin, int end) const { if (begin_ == begin) return true; if (begin_ < begin) return (begin <= end_); else /* begin < begin_ */ return (begin_ <= end); } bool InstrumentSequenceLoop::isContainable(int begin, int end) const { return ((begin_ < begin && end <= end_) || (begin_ == begin && end < end_)); } bool InstrumentSequenceLoop::hasSameRegion(int begin, int end) const { return (begin_ == begin && end_ == end); } InstrumentSequenceLoop InstrumentSequenceLoop::clone() const { InstrumentSequenceLoop l(begin_, end_, times_); for (const auto& pair : childs_) { l.childs_.insert(std::make_pair(pair.first, makeSequenceLoopPtr(pair.second->clone()))); } return l; } InstrumentSequenceLoopRoot InstrumentSequenceLoopRoot::clone() const { InstrumentSequenceLoopRoot r(end_); for (const auto& pair : childs_) { r.childs_.insert(std::make_pair(pair.first, makeSequenceLoopPtr(pair.second->clone()))); } return r; } namespace inst_utils { LoopStack::StackItem::StackItem(const InstrumentSequenceLoop::Ptr& ptr) : loop(ptr), count(ptr->getTimes()), isInfinite(ptr->isInfinite()) { } LoopStack::LoopStack(const std::shared_ptr& ptr) : stack_{ StackItem(std::static_pointer_cast(ptr)) } { } void LoopStack::clear() { stack_.erase(stack_.begin() + 1, stack_.end()); } void LoopStack::pushLoopsAtPos(int pos) { while (true) { InstrumentSequenceLoop::Ptr& loop = stack_.back().loop; if (!loop->hasInnerLoopBeginAt(pos)) break; stack_.emplace_back(loop->getInnerLoopBeginAt(pos)); } } int LoopStack::checkLoopEndAndNextPos(int curPos) { while (stack_.size() > 1) { StackItem& item = stack_.back(); if (item.loop->getEndPos() != curPos) { return curPos + 1; } if (item.isInfinite) { return item.loop->getBeginPos(); } if (--(item.count)) { return item.loop->getBeginPos(); } else { stack_.pop_back(); } } return curPos + 1; } } InstrumentSequenceRelease::InstrumentSequenceRelease(ReleaseType type, int beginPos) : type_(type), begin_(type_ == ReleaseType::NoRelease ? DISABLE_RELEASE : beginPos) { } void InstrumentSequenceRelease::setType(ReleaseType type) { type_ = type; if (type == ReleaseType::NoRelease) begin_ = DISABLE_RELEASE; } void InstrumentSequenceRelease::setBeginPos(int pos) { if (type_ != ReleaseType::NoRelease) begin_ = pos; } void InstrumentSequenceRelease::disable() { type_ = ReleaseType::NoRelease; begin_ = DISABLE_RELEASE; } BambooTracker-0.4.6/BambooTracker/instrument/sequence_property.hpp000066400000000000000000000340421401124043500254410ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include #include "abstract_instrument_property.hpp" #include "sequence_iterator_interface.hpp" struct InstrumentSequenceBaseUnit { int data; static constexpr int ERR_DATA = -1; InstrumentSequenceBaseUnit() noexcept; explicit InstrumentSequenceBaseUnit(int d) noexcept; virtual ~InstrumentSequenceBaseUnit() = default; friend bool operator==(const InstrumentSequenceBaseUnit& a, const InstrumentSequenceBaseUnit& b) { return a.data == b.data; } friend bool operator!=(const InstrumentSequenceBaseUnit& a, const InstrumentSequenceBaseUnit& b) { return !(a == b); } }; struct InstrumentSequenceExtendUnit : public InstrumentSequenceBaseUnit { enum SubdataType : int { UnusedSubdata, RawSubdata, RatioSubdata, ShiftSubdata } type; /// - If bit 17 is 0, /// * If bit 16 is 0, bit 0-15 is raw data /// * If bit 16 is 1, bit 0-7 is 2nd and bit 8-15 is 1st ratio value /// - If bit 17 is 1, /// * If bit 16 is 0, bit 0-15 is right shift value /// * If bit 16 is 1, bit 0-15 is left shift value int subdata; InstrumentSequenceExtendUnit() noexcept; explicit InstrumentSequenceExtendUnit(int d, SubdataType subType, int subData) noexcept; static InstrumentSequenceExtendUnit makeOnlyDataUnit(int data) noexcept; static InstrumentSequenceExtendUnit makeRawUnit(int data, int sub) noexcept; static InstrumentSequenceExtendUnit makeRatioUnit(int data, int subFirst, int subSecond) noexcept; static InstrumentSequenceExtendUnit makeShiftUnit(int data, int rshift) noexcept; static InstrumentSequenceExtendUnit makeUnitWithDecode(int data, int subsrc) noexcept; void getSubdataAsRaw(int& raw) const noexcept; void getSubdataAsRatio(int& first, int& second) const noexcept; void getSubdataAsShift(int& rshift) const noexcept; friend bool operator==(const InstrumentSequenceExtendUnit& a, const InstrumentSequenceExtendUnit& b) { return a.data == b.data && a.type == b.type && a.subdata == b.subdata; } friend bool operator!=(const InstrumentSequenceExtendUnit& a, const InstrumentSequenceExtendUnit& b) { return !(a == b); } }; class InstrumentSequenceLoop { public: using Ptr = std::shared_ptr; static constexpr int INFINITE_LOOP = 1; /// Loop in a closed interval [begin, end] InstrumentSequenceLoop(int begin, int end, int times = INFINITE_LOOP); friend bool operator==(const InstrumentSequenceLoop& a, const InstrumentSequenceLoop& b) { return (a.begin_ == b.begin_ && a.end_ == b.end_ && a.times_ == b.times_); } friend bool operator!=(const InstrumentSequenceLoop& a, const InstrumentSequenceLoop& b) { return !(a == b); } void setBeginPos(int pos); inline int getBeginPos() const noexcept { return begin_; } void setEndPos(int pos); inline int getEndPos() const noexcept { return end_; } inline void setTimes(int times) noexcept { times_ = times; } inline int getTimes() const noexcept { return times_; } inline bool isInfinite() const noexcept { return times_ == INFINITE_LOOP; } inline Ptr getInnerLoopBeginAt(int pos) const { return childs_.at(pos); } bool addInnerLoop(const InstrumentSequenceLoop& inner); bool changeInnerLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop); void removeInnerLoop(int begin, int end); void removeAllInnerLoops(); std::vector getAllInnerLoops()const; inline bool hasInnerLoop() const { return !childs_.empty(); } inline bool hasInnerLoopBeginAt(int pos) const { return childs_.count(pos); } bool isOverlapped(int begin, int end) const; inline bool isOverlapped(const InstrumentSequenceLoop& other) const { return isOverlapped(other.begin_, other.end_); } bool isContainable(int begin, int end) const; inline bool isContainable(const InstrumentSequenceLoop& other) const { return isContainable(other.begin_, other.end_); } bool hasSameRegion(int begin, int end) const; inline bool hasSameRegion(const InstrumentSequenceLoop& other) const { return hasSameRegion(other.begin_, other.end_); } InstrumentSequenceLoop clone() const; protected: int begin_, end_, times_; std::map childs_; }; class InstrumentSequenceLoopRoot : public InstrumentSequenceLoop { public: InstrumentSequenceLoopRoot(int size) : InstrumentSequenceLoop(0, size - 1) {} inline int size() const { return end_; } inline void extend() { setEndPos(end_ + 1); } inline void shrink() { if (end_) setEndPos(end_ - 1); } inline void resize(int size) { setEndPos(size - 1); } inline void clear() { removeAllInnerLoops(); setEndPos(0); } inline Ptr getLoopBeginAt(int pos) const { return getInnerLoopBeginAt(pos); } inline bool addLoop(const InstrumentSequenceLoop& loop) { return addInnerLoop(loop); } inline bool changeLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { return changeInnerLoop(prevBegin, prevEnd, loop); } inline void removeLoop(int begin, int end) { removeInnerLoop(begin, end); } inline void removeAllLoops() { removeAllInnerLoops(); } inline std::vector getAllLoops() const { return getAllInnerLoops(); } inline bool hasLoop() const { return hasInnerLoop(); } inline bool hasLoopBeginAt(int pos) const { return hasInnerLoopBeginAt(pos); } InstrumentSequenceLoopRoot clone() const; }; namespace inst_utils { class LoopStack { private: struct StackItem { InstrumentSequenceLoop::Ptr loop; int count; bool isInfinite; explicit StackItem(const InstrumentSequenceLoop::Ptr& ptr); }; std::deque stack_; public: explicit LoopStack(const std::shared_ptr& ptr); void clear(); void pushLoopsAtPos(int pos); int checkLoopEndAndNextPos(int curPos); }; } class InstrumentSequenceRelease { public: enum ReleaseType { NoRelease, FixedRelease, AbsoluteRelease, RelativeRelease }; static constexpr int DISABLE_RELEASE = -1; explicit InstrumentSequenceRelease(ReleaseType getType, int getBeginPos = DISABLE_RELEASE); friend bool operator==(const InstrumentSequenceRelease& a, const InstrumentSequenceRelease& b) { return (a.type_ == b.type_ && a.begin_ == b.begin_); } friend bool operator!=(const InstrumentSequenceRelease& a, const InstrumentSequenceRelease& b) { return !(a == b); } inline ReleaseType getType() const noexcept { return type_; } void setType(ReleaseType type); void setBeginPos(int pos); inline int getBeginPos() const noexcept { return begin_; } inline bool isEnabled() const noexcept { return (type_ != ReleaseType::NoRelease && begin_ != DISABLE_RELEASE); } void disable(); private: ReleaseType type_; int begin_; }; template class InstrumentSequenceProperty final : public AbstractInstrumentProperty { public: InstrumentSequenceProperty(int num, SequenceType defaultType, const T& defaultUnit, const T& errorUnit, int relReleaseDenom = 1) : AbstractInstrumentProperty(num), DEF_TYPE_(defaultType), DEF_UNIT_(defaultUnit), ERR_UNIT_(errorUnit), REL_RELEASE_DENOM_(relReleaseDenom), loop_(std::make_shared(1)), release_(InstrumentSequenceRelease::NoRelease) { clearParameters(); } friend bool operator==(const InstrumentSequenceProperty& a, const InstrumentSequenceProperty& b) { return (a.type_ == b.type_ && a.seq_ == b.seq_ && a.loop_ == b.loop_ && a.release_ == b.release_); } friend bool operator!=(const InstrumentSequenceProperty& a, const InstrumentSequenceProperty& b) { return !(a == b); } std::unique_ptr clone() { std::unique_ptr clone = std::make_unique(*this); clone->clearUserInstruments(); return clone; } inline void setType(SequenceType type) noexcept { type_ = type; } inline SequenceType getType() const noexcept { return type_; } bool isEdited() const override { return (seq_.size() > 1 || seq_.front() != DEF_UNIT_ || loop_->hasLoop() || release_.getType() != InstrumentSequenceRelease::NoRelease); } void clearParameters() override { type_ = DEF_TYPE_; seq_ = { DEF_UNIT_ }; loop_->clear(); release_.disable(); } //***** Sequence ***** inline size_t getSequenceSize() const { return seq_.size(); } T getSequenceUnit(int n) const { return seq_.at(static_cast(n)); } inline std::vector getSequence() const { return seq_; } void addSequenceUnit(const T& unit) { seq_.push_back(unit); loop_->extend(); } void removeSequenceUnit() { seq_.pop_back(); loop_->shrink(); if (release_.getBeginPos() == static_cast(seq_.size())) release_.disable(); } void setSequenceUnit(int n, const T& unit) { seq_.at(static_cast(n)) = unit; } //***** Loop ***** inline InstrumentSequenceLoopRoot getLoopRoot() const { return *loop_; } inline void addLoop(const InstrumentSequenceLoop& loop) const { loop_->addLoop(loop); } inline void removeLoop(int begin, int end) const { loop_->removeLoop(begin, end); } inline void clearLoops() const { loop_->removeAllLoops(); } inline void changeLoop(int prevBegin, int prevEnd, const InstrumentSequenceLoop& loop) { loop_->changeLoop(prevBegin, prevEnd, loop); } //***** Release ***** InstrumentSequenceRelease getRelease() const noexcept { return release_; } inline void setRelease(const InstrumentSequenceRelease& release) { release_ = release; } class Iterator final : public SequenceIteratorInterface { public: explicit Iterator(const InstrumentSequenceProperty* seqProp) : SequenceIteratorInterface(0), seqProp_(seqProp), started_(false), loopStack_(seqProp->loop_), isRelease_(false), relReleaseRate_(1.) { } SequenceType type() const override { return seqProp_->type_; } T data() const override { if (this->hasEnded() || static_cast(seqProp_->seq_.size()) <= this->pos_) return seqProp_->ERR_UNIT_; return (isRelease_ ? seqProp_->getSequenceUnit(this->pos_, relReleaseRate_) : seqProp_->getSequenceUnit(this->pos_)); } int next() override { if (this->hasEnded()) return this->END_SEQ_POS; if (!started_) { started_ = true; return this->pos_; } this->pos_ = loopStack_.checkLoopEndAndNextPos(this->pos_); loopStack_.pushLoopsAtPos(this->pos_); // Range check if ((!isRelease_ && seqProp_->release_.isEnabled() && this->pos_ >= seqProp_->release_.getBeginPos()) || this->pos_ >= static_cast(seqProp_->seq_.size())) { this->pos_ = this->END_SEQ_POS; } return this->pos_; } int front() override { started_ = true; loopStack_.clear(); isRelease_ = false; relReleaseRate_ = 1.; if (seqProp_->release_.getBeginPos()) { this->pos_ = 0; loopStack_.pushLoopsAtPos(0); } else { this->pos_ = this->END_SEQ_POS; } return this->pos_; } int release() override { const std::vector& seq = seqProp_->seq_; const InstrumentSequenceRelease& release = seqProp_->release_; int next = this->END_SEQ_POS; isRelease_ = true; loopStack_.clear(); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: break; case InstrumentSequenceRelease::FixedRelease: { next = release.getBeginPos(); break; } case InstrumentSequenceRelease::AbsoluteRelease: { int crtr; if (this->pos_ == this->END_SEQ_POS) { if (int relBegin = release.getBeginPos()) { crtr = seq[static_cast(relBegin - 1)].data; } else { next = relBegin; break; } } else { crtr = seq[static_cast(this->pos_)].data; } auto&& it = std::find_if(seq.begin() + release.getBeginPos(), seq.end(), [crtr](const T& unit) { return (unit.data <= crtr); }); if (it != seq.end()) next = std::distance(seq.begin(), it); break; } case InstrumentSequenceRelease::RelativeRelease: { next = release.getBeginPos(); if (this->hasEnded()) { if (next) relReleaseRate_ = seq[static_cast(next - 1)].data / seqProp_->REL_RELEASE_DENOM_; } else { relReleaseRate_ = seq[static_cast(this->pos_)].data / seqProp_->REL_RELEASE_DENOM_; } break; } } this->pos_ = next; if (next != this->END_SEQ_POS) loopStack_.pushLoopsAtPos(this->pos_); return this->pos_; } int end() override { this->pos_ = this->END_SEQ_POS; started_ = false; return this->END_SEQ_POS; } private: const InstrumentSequenceProperty* seqProp_; bool started_; inst_utils::LoopStack loopStack_; bool isRelease_; double relReleaseRate_; }; std::unique_ptr getIterator() { return std::make_unique(this); } private: const SequenceType DEF_TYPE_; const T DEF_UNIT_; const T ERR_UNIT_; const double REL_RELEASE_DENOM_; SequenceType type_; std::vector seq_; std::shared_ptr loop_; InstrumentSequenceRelease release_; inline T getSequenceUnit(int n, double relRate) const { T unit = seq_.at(static_cast(n)); unit.data = static_cast(unit.data * relRate); return unit; } }; BambooTracker-0.4.6/BambooTracker/io/000077500000000000000000000000001401124043500173505ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/io/bank_io.cpp000066400000000000000000000050311401124043500214550ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "bank_io.hpp" #include #include "file_io_error.hpp" #include "btb_io.hpp" #include "wopn_io.hpp" #include "ff_io.hpp" #include "ppc_io.hpp" #include "p86_io.hpp" #include "pps_io.hpp" #include "pvi_io.hpp" #include "pzi_io.hpp" #include "dat_io.hpp" namespace io { AbstractBank* AbstractBankIO::load(const BinaryContainer& ctr) const { (void)ctr; throw FileUnsupportedError(FileType::Bank); } void AbstractBankIO::save(BinaryContainer& ctr, const std::weak_ptr instMan, const std::vector& instNums) const { (void)ctr; (void)instMan; (void)instNums; throw FileUnsupportedError(io::FileType::Bank); } //------------------------------------------------------------ std::unique_ptr BankIO::instance_; BankIO::BankIO() { handler_.add(new BtbIO); handler_.add(new WopnIO); handler_.add(new FfIO); handler_.add(new PpcIO); handler_.add(new P86IO); handler_.add(new PpsIO); handler_.add(new PviIO); handler_.add(new PziIO); handler_.add(new DatIO); } BankIO& BankIO::getInstance() { if (!instance_) instance_.reset(new BankIO); return *instance_; } void BankIO::saveBank(BinaryContainer& ctr, const std::weak_ptr instMan, const std::vector& instNums) { handler_.at("btb")->save(ctr, instMan, instNums); } AbstractBank* BankIO::loadBank(const BinaryContainer& ctr, const std::string& path) { return handler_.at(getExtension(path))->load(ctr); } } BambooTracker-0.4.6/BambooTracker/io/bank_io.hpp000066400000000000000000000055301401124043500214660ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "instruments_manager.hpp" #include "bank.hpp" #include "binary_container.hpp" #include "io_utils.hpp" namespace io { class AbstractBankIO { public: AbstractBankIO(const std::string& ext, const std::string& desc, bool loadable, bool savable) : ext_(ext), desc_(desc), loadable_(loadable), savable_(savable) {} virtual ~AbstractBankIO() = default; virtual AbstractBank* load(const BinaryContainer& ctr) const; virtual void save(BinaryContainer& ctr, const std::weak_ptr instMan, const std::vector& instNums) const; inline std::string getExtension() const noexcept { return ext_; } inline std::string getFilterText() const { return desc_ + "(*." + ext_ + ")"; } inline bool isLoadable() const noexcept { return loadable_; } inline bool isSavable() const noexcept { return savable_; } private: const std::string ext_, desc_; bool loadable_, savable_; }; class BankIO { public: static BankIO& getInstance(); void saveBank(BinaryContainer& ctr, const std::weak_ptr instMan, const std::vector& instNums); AbstractBank* loadBank(const BinaryContainer& ctr, const std::string& path); inline bool testLoadableFormat(const std::string& ext) const { return handler_.testLoadableExtension(ext); } inline bool testSavableFormat(const std::string& ext) const { return handler_.testSavableExtension(ext); } inline std::vector getLoadFilter() const { return handler_.getLoadFilterList(); } inline std::vector getSaveFilter() const { return handler_.getSaveFilterList(); } private: BankIO(); static std::unique_ptr instance_; FileIOHandlerMap handler_; }; } BambooTracker-0.4.6/BambooTracker/io/binary_container.cpp000066400000000000000000000163111401124043500234040ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "binary_container.hpp" #include #include #include namespace io { BinaryContainer::BinaryContainer() : isLE_(true) { } BinaryContainer::BinaryContainer(const std::vector& buf) : isLE_(true) { std::copy(buf.cbegin(), buf.cend(), std::back_inserter(buf_)); } BinaryContainer::BinaryContainer(std::vector&& buf) : isLE_(true) { std::move(buf.begin(), buf.end(), std::back_inserter(buf_)); } void BinaryContainer::clear() { buf_.clear(); buf_.shrink_to_fit(); } void BinaryContainer::appendInt8(int8_t v) { buf_.push_back(static_cast(v)); } void BinaryContainer::appendUint8(uint8_t v) { buf_.push_back(v); } void BinaryContainer::appendInt16(int16_t v) { append({ static_cast(0x00ff & v), static_cast(v >> 8), }); } void BinaryContainer::appendUint16(uint16_t v) { append({ static_cast(0x00ff & v), static_cast(v >> 8), }); } void BinaryContainer::appendInt32(int32_t v) { append({ static_cast(0x000000ff & v), static_cast(0x0000ff & (v >> 8)), static_cast(0x00ff & (v >> 16)), static_cast(v >> 24), }); } void BinaryContainer::appendUint32(uint32_t v) { append({ static_cast(0x000000ff & v), static_cast(0x0000ff & (v >> 8)), static_cast(0x00ff & (v >> 16)), static_cast(v >> 24), }); } void BinaryContainer::appendChar(char c) { buf_.push_back(static_cast(c)); } void BinaryContainer::appendString(const std::string& str) { std::copy(str.cbegin(), str.cend(), std::back_inserter(buf_)); } void BinaryContainer::appendArray(const uint8_t* array, size_type size) { std::copy(array, array + size, std::back_inserter(buf_)); } void BinaryContainer::appendVector(const std::vector& vec) { std::copy(vec.cbegin(), vec.cend(), std::back_inserter(buf_)); } void BinaryContainer::appendVector(std::vector&& vec) { std::move(vec.begin(), vec.end(), std::back_inserter(buf_)); } void BinaryContainer::appendBinaryContainer(const BinaryContainer& bc) { std::copy(bc.cbegin(), bc.cend(), std::back_inserter(buf_)); } void BinaryContainer::appendBinaryContainer(BinaryContainer&& bc) { std::move(bc.begin(), bc.end(), std::back_inserter(buf_)); } void BinaryContainer::writeInt8(size_type offset, int8_t v) { buf_.at(offset) = static_cast(v); } void BinaryContainer::writeUint8(size_type offset, uint8_t v) { buf_.at(offset) = v; } void BinaryContainer::writeInt16(size_type offset, int16_t v) { write(offset, { static_cast(0x00ff & v), static_cast(v >> 8), }); } void BinaryContainer::writeUint16(size_type offset, uint16_t v) { write(offset, { static_cast(0x00ff & v), static_cast(v >> 8), }); } void BinaryContainer::writeInt32(size_type offset, int32_t v) { write(offset, { static_cast(0x000000ff & v), static_cast(0x0000ff & (v >> 8)), static_cast(0x00ff & (v >> 16)), static_cast(v >> 24), }); } void BinaryContainer::writeUint32(size_type offset, uint32_t v) { write(offset, { static_cast(0x000000ff & v), static_cast(0x0000ff & (v >> 8)), static_cast(0x00ff & (v >> 16)), static_cast(v >> 24), }); } void BinaryContainer::writeChar(size_type offset, char c) { buf_.at(offset) = static_cast(c); } void BinaryContainer::writeString(size_type offset, const std::string& str) { std::copy(str.cbegin(), str.cend(), buf_.begin() + static_cast(offset)); } int8_t BinaryContainer::readInt8(size_type offset) const { return static_cast(buf_.at(offset)); } uint8_t BinaryContainer::readUint8(size_type offset) const { return static_cast(buf_.at(offset)); } int16_t BinaryContainer::readInt16(size_type offset) const { std::vector data = read(offset, 2); return static_cast(data[0] | (data[1] << 8)); } uint16_t BinaryContainer::readUint16(size_type offset) const { std::vector data = read(offset, 2); return static_cast(data[0] | (data[1] << 8)); } int32_t BinaryContainer::readInt32(size_type offset) const { std::vector data = read(offset, 4); return static_cast(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); } uint32_t BinaryContainer::readUint32(size_type offset) const { std::vector data = read(offset, 4); return static_cast(data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); } char BinaryContainer::readChar(size_type offset) const { return buf_.at(offset); } std::string BinaryContainer::readString(size_type offset, size_type length) const { return std::string(buf_.begin() + static_cast(offset), buf_.begin() + static_cast(offset + length)); } BinaryContainer BinaryContainer::getSubcontainer(size_type offset, size_type length) const { BinaryContainer sub; std::copy_n(buf_.begin() + static_cast(offset), length, std::back_inserter(sub)); return sub; } std::vector BinaryContainer::toVector() const { std::vector vec; vec.reserve(buf_.size()); std::copy(buf_.cbegin(), buf_.cend(), std::back_inserter(vec)); return vec; } void BinaryContainer::append(const std::vector&& a) { if (isLE_) std::copy(a.cbegin(), a.cend(), std::back_inserter(buf_)); else std::reverse_copy(a.cbegin(), a.cend(), std::back_inserter(buf_)); } void BinaryContainer::write(size_t offset, const std::vector&& a) { if (isLE_) std::copy(a.cbegin(), a.cend(), buf_.begin() + static_cast(offset)); else std::reverse_copy(a.cbegin(), a.cend(), buf_.begin() + static_cast(offset)); } std::vector BinaryContainer::read(size_type offset, size_type size) const { std::vector data; if (isLE_) std::copy(buf_.cbegin() + static_cast(offset), buf_.cbegin() + static_cast(offset + size), std::back_inserter(data)); else std::reverse_copy(buf_.cbegin() + static_cast(offset), buf_.cbegin() + static_cast(offset + size), std::back_inserter(data)); return data; } } BambooTracker-0.4.6/BambooTracker/io/binary_container.hpp000066400000000000000000000107611401124043500234140ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include namespace io { class BinaryContainer { public: using container_type = std::deque; using value_type = container_type::value_type; using size_type = container_type::size_type; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using reverse_iterator = container_type::reverse_iterator; using const_reverse_iterator = container_type::const_reverse_iterator; explicit BinaryContainer(); explicit BinaryContainer(const std::vector& buf); explicit BinaryContainer(std::vector&& buf); inline iterator begin() noexcept { return buf_.begin(); } inline const_iterator begin() const noexcept { return buf_.begin(); } inline iterator end() noexcept { return buf_.end(); } inline const_iterator end() const noexcept { return buf_.end(); } inline const_iterator cbegin() const noexcept { return buf_.cbegin(); } inline const_iterator cend() const noexcept { return buf_.cend(); } inline reverse_iterator rbegin() noexcept { return buf_.rbegin(); } inline const_reverse_iterator rbegin() const noexcept { return buf_.rbegin(); } inline reverse_iterator rend() noexcept { return buf_.rend(); } inline const_reverse_iterator rend() const noexcept { return buf_.rend(); } inline const_reverse_iterator crbegin() const noexcept { return buf_.crbegin(); } inline const_reverse_iterator crend() const noexcept { return buf_.crend(); } inline void push_back(uint8_t v) { appendUint8(v); } inline size_type size() const noexcept { return buf_.size(); } void clear(); inline void resize(size_type size) { buf_.resize(size); } inline void setEndian(bool isLittleEndian) noexcept { isLE_ = isLittleEndian; } inline bool isLittleEndian() const noexcept { return isLE_; } void appendInt8(int8_t v); void appendUint8(uint8_t v); void appendInt16(int16_t v); void appendUint16(uint16_t v); void appendInt32(int32_t v); void appendUint32(uint32_t v); void appendChar(char c); void appendString(const std::string& str); void appendArray(const uint8_t* array, size_type size); void appendVector(const std::vector& vec); void appendVector(std::vector&& vec); void appendBinaryContainer(const BinaryContainer& bc); void appendBinaryContainer(BinaryContainer&& bc); void writeInt8(size_type offset, int8_t v); void writeUint8(size_type offset, uint8_t v); void writeInt16(size_type offset, int16_t v); void writeUint16(size_type offset, uint16_t v); void writeInt32(size_type offset, int32_t v); void writeUint32(size_type offset, uint32_t v); void writeChar(size_type offset, char c); void writeString(size_type offset, const std::string& str); int8_t readInt8(size_type offset) const; uint8_t readUint8(size_type offset) const; int16_t readInt16(size_type offset) const; uint16_t readUint16(size_type offset) const; int32_t readInt32(size_type offset) const; uint32_t readUint32(size_type offset) const; char readChar(size_type offset) const; std::string readString(size_type offset, size_type length) const; BinaryContainer getSubcontainer(size_type offset, size_type length) const; std::vector toVector() const; private: container_type buf_; bool isLE_; void append(const std::vector&& a); void write(size_t offset, const std::vector&& a); std::vector read(size_type offset, size_type size) const; }; } BambooTracker-0.4.6/BambooTracker/io/btb_io.cpp000066400000000000000000002230071401124043500213160ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "btb_io.hpp" #include #include #include #include #include #include #include "enum_hash.hpp" #include "version.hpp" #include "instrument.hpp" #include "file_io_error.hpp" #include "io_utils.hpp" namespace io { namespace { bool judgeRequiredProperty(const std::multiset& usedInstIdcs, const std::vector& reqInstIdcs) { std::vector intrsct; std::set_intersection(usedInstIdcs.begin(), usedInstIdcs.end(), reqInstIdcs.begin(), reqInstIdcs.end(), std::back_inserter(intrsct)); return !intrsct.empty(); } } BtbIO::BtbIO() : AbstractBankIO("btb", "BambooTracker bank", true, true) {} AbstractBank* BtbIO::load(const BinaryContainer& ctr) const { size_t globCsr = 0; if (ctr.readString(globCsr, 16) != "BambooTrackerBnk") throw FileCorruptionError(FileType::Bank, globCsr); globCsr += 16; /*size_t eofOfs = */ctr.readUint32(globCsr); globCsr += 4; size_t fileVersion = ctr.readUint32(globCsr); if (fileVersion > Version::ofBankFileInBCD()) throw FileVersionError(FileType::Bank); globCsr += 4; /***** Instrument section *****/ std::vector ids; std::vector names; std::vector instCtrs; if (ctr.readString(globCsr, 8) != "INSTRMNT") throw FileCorruptionError(FileType::Bank, globCsr); globCsr += 8; size_t instOfs = ctr.readUint32(globCsr); size_t instCsr = globCsr + 4; uint8_t instCnt = ctr.readUint8(instCsr); instCsr += 1; for (uint8_t i = 0; i < instCnt; ++i) { size_t pos = instCsr; uint8_t idx = ctr.readUint8(instCsr); ids.push_back(idx); instCsr += 1; size_t iOfs = ctr.readUint32(instCsr); size_t iCsr = instCsr + 4; size_t nameLen = ctr.readUint32(iCsr); iCsr += 4; std::string name = u8""; if (nameLen > 0) { name = ctr.readString(iCsr, nameLen); /* iCsr += nameLen; */ } names.push_back(name); instCsr += iOfs; // Jump to next instCtrs.push_back(ctr.getSubcontainer(pos, 1 + iOfs)); } globCsr += instOfs; /***** Instrument property section *****/ if (ctr.readString(globCsr, 8) != "INSTPROP") throw FileCorruptionError(FileType::Inst, globCsr); globCsr += 8; size_t instPropOfs = ctr.readUint32(globCsr); BinaryContainer propCtr = ctr.getSubcontainer(globCsr + 4, instPropOfs - 4); return new BtBank(ids, names, instCtrs, propCtr, fileVersion); } void BtbIO::save(BinaryContainer& ctr, const std::weak_ptr instMan, const std::vector& instNums) const { ctr.appendString("BambooTrackerBnk"); size_t eofOfs = ctr.size(); ctr.appendUint32(0); // Dummy EOF offset uint32_t fileVersion = Version::ofBankFileInBCD(); ctr.appendUint32(fileVersion); /***** Instrument section *****/ ctr.appendString("INSTRMNT"); size_t instOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument section offset ctr.appendUint8(static_cast(instNums.size())); for (auto& idx : instNums) { if (std::shared_ptr inst = instMan.lock()->getInstrumentSharedPtr(static_cast(idx))) { ctr.appendUint8(static_cast(idx)); size_t iOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument block offset std::string name = inst->getName(); ctr.appendUint32(name.length()); if (!name.empty()) ctr.appendString(name); switch (inst->getType()) { case InstrumentType::FM: { ctr.appendUint8(0x00); auto instFM = std::dynamic_pointer_cast(inst); ctr.appendUint8(static_cast(instFM->getEnvelopeNumber())); uint8_t tmp = static_cast(instFM->getLFONumber()); ctr.appendUint8(instFM->getLFOEnabled() ? tmp : (0x80 | tmp)); for (auto& param : FM_OPSEQ_PARAMS) { tmp = static_cast(instFM->getOperatorSequenceNumber(param)); ctr.appendUint8(instFM->getOperatorSequenceEnabled(param) ? tmp : (0x80 | tmp)); } tmp = static_cast(instFM->getArpeggioNumber(FMOperatorType::All)); ctr.appendUint8(instFM->getArpeggioEnabled(FMOperatorType::All) ? tmp : (0x80 | tmp)); tmp = static_cast(instFM->getPitchNumber(FMOperatorType::All)); ctr.appendUint8(instFM->getPitchEnabled(FMOperatorType::All) ? tmp : (0x80 | tmp)); tmp = static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::All)) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op1) << 1) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op2) << 2) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op3) << 3) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op4) << 4); ctr.appendUint8(tmp); for (auto& type : FM_OP_TYPES) { tmp = static_cast(instFM->getArpeggioNumber(type)); ctr.appendUint8(instFM->getArpeggioEnabled(type) ? tmp : (0x80 | tmp)); } for (auto& type : FM_OP_TYPES) { tmp = static_cast(instFM->getPitchNumber(type)); ctr.appendUint8(instFM->getPitchEnabled(type) ? tmp : (0x80 | tmp)); } break; } case InstrumentType::SSG: { ctr.appendUint8(0x01); auto instSSG = std::dynamic_pointer_cast(inst); uint8_t tmp = static_cast(instSSG->getWaveformNumber()); ctr.appendUint8(instSSG->getWaveformEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getToneNoiseNumber()); ctr.appendUint8(instSSG->getToneNoiseEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getEnvelopeNumber()); ctr.appendUint8(instSSG->getEnvelopeEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getArpeggioNumber()); ctr.appendUint8(instSSG->getArpeggioEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getPitchNumber()); ctr.appendUint8(instSSG->getPitchEnabled() ? tmp : (0x80 | tmp)); break; } case InstrumentType::ADPCM: { ctr.appendUint8(0x02); auto instADPCM = std::dynamic_pointer_cast(inst); ctr.appendUint8(static_cast(instADPCM->getSampleNumber())); uint8_t tmp = static_cast(instADPCM->getEnvelopeNumber()); ctr.appendUint8(instADPCM->getEnvelopeEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instADPCM->getArpeggioNumber()); ctr.appendUint8(instADPCM->getArpeggioEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instADPCM->getPitchNumber()); ctr.appendUint8(instADPCM->getPitchEnabled() ? tmp : (0x80 | tmp)); break; } case InstrumentType::Drumkit: { ctr.appendUint8(0x03); auto instKit = std::dynamic_pointer_cast(inst); std::vector keys = instKit->getAssignedKeys(); ctr.appendUint8(keys.size()); for (const int& key : keys) { ctr.appendUint8(static_cast(key)); ctr.appendUint8(static_cast(instKit->getSampleNumber(key))); ctr.appendInt8(instKit->getPitch(key)); } break; } } ctr.writeUint32(iOfs, ctr.size() - iOfs); } } ctr.writeUint32(instOfs, ctr.size() - instOfs); /***** Instrument property section *****/ ctr.appendString("INSTPROP"); size_t instPropOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument property section offset // FM envelope std::vector envFMIdcs; for (auto& idx : instMan.lock()->getEnvelopeFMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getEnvelopeFMUsers(idx), instNums)) envFMIdcs.push_back(idx); } if (!envFMIdcs.empty()) { ctr.appendUint8(0x00); ctr.appendUint8(static_cast(envFMIdcs.size())); for (auto& idx : envFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint8(0); // Dummy offset uint8_t tmp = static_cast(instMan.lock()->getEnvelopeFMParameter(idx, FMEnvelopeParameter::AL) << 4) | static_cast(instMan.lock()->getEnvelopeFMParameter(idx, FMEnvelopeParameter::FB)); ctr.appendUint8(tmp); for (int op = 0; op < 4; ++op) { // Operator auto& params = FM_OP_PARAMS[op]; tmp = instMan.lock()->getEnvelopeFMOperatorEnabled(idx, op); tmp = static_cast((tmp << 5)) | static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::AR))); ctr.appendUint8(tmp); tmp = static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::KS)) << 5) | static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DR))); ctr.appendUint8(tmp); tmp = static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DT)) << 5) | static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SR))); ctr.appendUint8(tmp); tmp = static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SL)) << 4) | static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::RR))); ctr.appendUint8(tmp); tmp = static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::TL))); ctr.appendUint8(tmp); int tmp2 = instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SSGEG)); tmp = ((tmp2 == -1) ? 0x80 : static_cast(tmp2 << 4)) | static_cast(instMan.lock()->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::ML))); ctr.appendUint8(tmp); } ctr.writeUint8(ofs, static_cast(ctr.size() - ofs)); } } // FM LFO std::vector lfoFMIdcs; for (auto& idx : instMan.lock()->getLFOFMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getLFOFMUsers(idx), instNums)) lfoFMIdcs.push_back(idx); } if (!lfoFMIdcs.empty()) { ctr.appendUint8(0x01); ctr.appendUint8(static_cast(lfoFMIdcs.size())); for (auto& idx : lfoFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint8(0); // Dummy offset uint8_t tmp = static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::FREQ) << 4) | static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::PMS)); ctr.appendUint8(tmp); tmp = static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::AM4) << 7) | static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::AM3) << 6) | static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::AM2) << 5) | static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::AM1) << 4) | static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::AMS)); ctr.appendUint8(tmp); tmp = static_cast(instMan.lock()->getLFOFMparameter(idx, FMLFOParameter::Count)); ctr.appendUint8(tmp); ctr.writeUint8(ofs, static_cast(ctr.size() - ofs)); } } // FM envelope parameter for (size_t i = 0; i < 38; ++i) { std::vector idcs; for (auto& idx : instMan.lock()->getOperatorSequenceFMEntriedIndices(FM_OPSEQ_PARAMS[i])) { if (judgeRequiredProperty(instMan.lock()->getOperatorSequenceFMUsers(FM_OPSEQ_PARAMS[i], idx), instNums)) idcs.push_back(idx); } if (!idcs.empty()) { ctr.appendUint8(0x02 + static_cast(i)); ctr.appendUint8(static_cast(idcs.size())); for (auto& idx : idcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getOperatorSequenceFMSequence(FM_OPSEQ_PARAMS[i], idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getOperatorSequenceFMLoopRoot(FM_OPSEQ_PARAMS[i], idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getOperatorSequenceFMRelease(FM_OPSEQ_PARAMS[i], idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } } // FM arpeggio std::vector arpFMIdcs; for (auto& idx : instMan.lock()->getArpeggioFMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getArpeggioFMUsers(idx), instNums)) arpFMIdcs.push_back(idx); } if (!arpFMIdcs.empty()) { ctr.appendUint8(0x28); ctr.appendUint8(static_cast(arpFMIdcs.size())); for (auto& idx : arpFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getArpeggioFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getArpeggioFMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getArpeggioFMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getArpeggioFMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // FM pitch std::vector ptFMIdcs; for (auto& idx : instMan.lock()->getPitchFMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getPitchFMUsers(idx), instNums)) ptFMIdcs.push_back(idx); } if (!ptFMIdcs.empty()) { ctr.appendUint8(0x29); ctr.appendUint8(static_cast(ptFMIdcs.size())); for (auto& idx : ptFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getPitchFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getPitchFMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getPitchFMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getPitchFMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG waveform std::vector wfSSGIdcs; for (auto& idx : instMan.lock()->getWaveformSSGEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getWaveformSSGUsers(idx), instNums)) wfSSGIdcs.push_back(idx); } if (!wfSSGIdcs.empty()) { ctr.appendUint8(0x30); ctr.appendUint8(static_cast(wfSSGIdcs.size())); for (auto& idx : wfSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getWaveformSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(static_cast(unit.subdata)); } auto loops = instMan.lock()->getWaveformSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getWaveformSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG tone/noise std::vector tnSSGIdcs; for (auto& idx : instMan.lock()->getToneNoiseSSGEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getToneNoiseSSGUsers(idx), instNums)) tnSSGIdcs.push_back(idx); } if (!tnSSGIdcs.empty()) { ctr.appendUint8(0x31); ctr.appendUint8(static_cast(tnSSGIdcs.size())); for (auto& idx : tnSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getToneNoiseSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getToneNoiseSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getToneNoiseSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG envelope std::vector envSSGIdcs; for (auto& idx : instMan.lock()->getEnvelopeSSGEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getEnvelopeSSGUsers(idx), instNums)) envSSGIdcs.push_back(idx); } if (!envSSGIdcs.empty()) { ctr.appendUint8(0x32); ctr.appendUint8(static_cast(envSSGIdcs.size())); for (auto& idx : envSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getEnvelopeSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(static_cast(unit.subdata)); } auto loops = instMan.lock()->getEnvelopeSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getEnvelopeSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG arpeggio std::vector arpSSGIdcs; for (auto& idx : instMan.lock()->getArpeggioSSGEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getArpeggioSSGUsers(idx), instNums)) arpSSGIdcs.push_back(idx); } if (!arpSSGIdcs.empty()) { ctr.appendUint8(0x33); ctr.appendUint8(static_cast(arpSSGIdcs.size())); for (auto& idx : arpSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getArpeggioSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getArpeggioSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getArpeggioSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getArpeggioSSGType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG pitch std::vector ptSSGIdcs; for (auto& idx : instMan.lock()->getPitchSSGEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getPitchSSGUsers(idx), instNums)) ptSSGIdcs.push_back(idx); } if (!ptSSGIdcs.empty()) { ctr.appendUint8(0x34); ctr.appendUint8(static_cast(ptSSGIdcs.size())); for (auto& idx : ptSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getPitchSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getPitchSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getPitchSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getPitchSSGType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // ADPCM sample std::vector sampADPCMIdcs; for (auto& idx : instMan.lock()->getSampleADPCMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getSampleADPCMUsers(idx), instNums)) sampADPCMIdcs.push_back(idx); } if (!sampADPCMIdcs.empty()) { ctr.appendUint8(0x40); ctr.appendUint8(static_cast(sampADPCMIdcs.size())); for (auto& idx : sampADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint32(0); // Dummy offset ctr.appendUint8(static_cast(instMan.lock()->getSampleADPCMRootKeyNumber(idx))); ctr.appendUint16(static_cast(instMan.lock()->getSampleADPCMRootDeltaN(idx))); ctr.appendUint8(static_cast(instMan.lock()->isSampleADPCMRepeatable(idx))); std::vector samples = instMan.lock()->getSampleADPCMRawSample(idx); ctr.appendUint32(samples.size()); ctr.appendVector(samples); ctr.writeUint32(ofs, ctr.size() - ofs); } } // ADPCM envelope std::vector envADPCMIdcs; for (auto& idx : instMan.lock()->getEnvelopeADPCMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getEnvelopeADPCMUsers(idx), instNums)) envADPCMIdcs.push_back(idx); } if (!envADPCMIdcs.empty()) { ctr.appendUint8(0x41); ctr.appendUint8(static_cast(envADPCMIdcs.size())); for (auto& idx : envADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getEnvelopeADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(0); // Dummy set for past format } auto loops = instMan.lock()->getEnvelopeADPCMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getEnvelopeADPCMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // ADPCM arpeggio std::vector arpADPCMIdcs; for (auto& idx : instMan.lock()->getArpeggioADPCMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getArpeggioADPCMUsers(idx), instNums)) arpADPCMIdcs.push_back(idx); } if (!arpADPCMIdcs.empty()) { ctr.appendUint8(0x42); ctr.appendUint8(static_cast(arpADPCMIdcs.size())); for (auto& idx : arpADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getArpeggioADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getArpeggioADPCMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getArpeggioADPCMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getArpeggioADPCMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // ADPCM pitch std::vector ptADPCMIdcs; for (auto& idx : instMan.lock()->getPitchADPCMEntriedIndices()) { if (judgeRequiredProperty(instMan.lock()->getPitchADPCMUsers(idx), instNums)) ptADPCMIdcs.push_back(idx); } if (!ptADPCMIdcs.empty()) { ctr.appendUint8(0x43); ctr.appendUint8(static_cast(ptADPCMIdcs.size())); for (auto& idx : ptADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instMan.lock()->getPitchADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instMan.lock()->getPitchADPCMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instMan.lock()->getPitchADPCMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instMan.lock()->getPitchADPCMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } ctr.writeUint32(instPropOfs, ctr.size() - instPropOfs); ctr.writeUint32(eofOfs, ctr.size() - eofOfs); } namespace { size_t getPropertyPosition(const BinaryContainer& propCtr, uint8_t subsecType, uint8_t index) { size_t csr = 0; while (csr < propCtr.size()) { uint8_t type = propCtr.readUint8(csr++); bool isSection = (type == subsecType); size_t bcnt = propCtr.readUint8(csr++); for (size_t i = 0; i < bcnt; ++i) { if (isSection) { if (propCtr.readUint8(csr++) == index) { switch (type) { case 0x00: // FM envelope case 0x01: // FM LFO csr += 1; break; case 0x40: // ADPCM sample csr += 4; break; default: // Sequence csr += 2; break; } return csr; } else { switch (type) { case 0x00: // FM envelope case 0x01: // FM LFO csr += propCtr.readUint8(csr); break; case 0x40: // ADPCM sample csr += propCtr.readUint32(csr); break; default: // Sequence csr += propCtr.readUint16(csr); break; } } } else { ++csr; // Skip index switch (type) { case 0x00: // FM envelope case 0x01: // FM LFO csr += propCtr.readUint8(csr); break; case 0x40: // ADPCM sample csr += propCtr.readUint32(csr); break; default: // Sequence csr += propCtr.readUint16(csr); break; } } } } return std::numeric_limits::max(); } } AbstractInstrument* BtbIO::loadInstrument(const BinaryContainer& instCtr, const BinaryContainer& propCtr, std::weak_ptr instMan, int instNum, uint32_t bankVersion) { std::shared_ptr instManLocked = instMan.lock(); size_t instCsr = 5; // Skip instrument id and offset size_t nameLen = instCtr.readUint32(instCsr); instCsr += 4; std::string name = u8""; if (nameLen > 0) { name = instCtr.readString(instCsr, nameLen); instCsr += nameLen; } switch (instCtr.readUint8(instCsr++)) { case 0x00: // FM { auto fm = new InstrumentFM(instNum, name, instManLocked.get()); /* Envelope */ { auto orgEnvNum = instCtr.readUint8(instCsr++); int envNum = instManLocked->findFirstAssignableEnvelopeFM(); if (envNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); fm->setEnvelopeNumber(envNum); size_t envCsr = getPropertyPosition(propCtr, 0x00, orgEnvNum); if (envCsr != std::numeric_limits::max()) { uint8_t tmp = propCtr.readUint8(envCsr++); instManLocked->setEnvelopeFMParameter(envNum, FMEnvelopeParameter::AL, tmp >> 4); instManLocked->setEnvelopeFMParameter(envNum, FMEnvelopeParameter::FB, tmp & 0x0f); for (int op = 0; op < 4; ++op) { auto& params = FM_OP_PARAMS[op]; tmp = propCtr.readUint8(envCsr++); instManLocked->setEnvelopeFMOperatorEnabled(envNum, op, (0x20 & tmp) ? true : false); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::AR), tmp & 0x1f); tmp = propCtr.readUint8(envCsr++); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::KS), tmp >> 5); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::DR), tmp & 0x1f); tmp = propCtr.readUint8(envCsr++); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::DT), tmp >> 5); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::SR), tmp & 0x1f); tmp = propCtr.readUint8(envCsr++); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::SL), tmp >> 4); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::RR), tmp & 0x0f); tmp = propCtr.readUint8(envCsr++); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::TL), tmp); tmp = propCtr.readUint8(envCsr++); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::ML), tmp & 0x0f); instManLocked->setEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::SSGEG), (tmp & 0x80) ? -1 : ((tmp >> 4) & 0x07)); } } } /* LFO */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { fm->setLFOEnabled(false); fm->setLFONumber(0x7f & tmp); } else { fm->setLFOEnabled(true); uint8_t orgLFONum = 0x7f & tmp; int lfoNum = instManLocked->findFirstAssignableLFOFM(); if (lfoNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); fm->setLFONumber(lfoNum); size_t lfoCsr = getPropertyPosition(propCtr, 0x01, orgLFONum); if (lfoCsr != std::numeric_limits::max()) { tmp = propCtr.readUint8(lfoCsr++); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::FREQ, tmp >> 4); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::PMS, tmp & 0x0f); tmp = propCtr.readUint8(lfoCsr++); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::AMS, tmp & 0x0f); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::AM1, (tmp & 0x10) ? true : false); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::AM2, (tmp & 0x20) ? true : false); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::AM3, (tmp & 0x40) ? true : false); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::AM4, (tmp & 0x80) ? true : false); tmp = propCtr.readUint8(lfoCsr++); instManLocked->setLFOFMParameter(lfoNum, FMLFOParameter::Count, tmp); } } } /* Operator sequence */ uint8_t tmpCnt = 0; for (auto& param : FM_OPSEQ_PARAMS) { ++tmpCnt; uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { fm->setOperatorSequenceEnabled(param, false); fm->setOperatorSequenceNumber(param, 0x7f & tmp); } else { fm->setOperatorSequenceEnabled(param, true); uint8_t orgOpSeqNum = 0x7f & tmp; int opSeqNum = instManLocked->findFirstAssignableOperatorSequenceFM(param); if (opSeqNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); fm->setOperatorSequenceNumber(param, opSeqNum); size_t opSeqCsr = getPropertyPosition(propCtr, 0x02 + tmpCnt, orgOpSeqNum); if (opSeqCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; if (l == 0) instManLocked->setOperatorSequenceFMSequenceData(param, opSeqNum, 0, data); else instManLocked->addOperatorSequenceFMSequenceData(param, opSeqNum, data); } uint16_t loopCnt = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; int end = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; int times = propCtr.readUint8(opSeqCsr++); instManLocked->addOperatorSequenceFMLoop(param, opSeqNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(opSeqCsr++)) { case 0x00: // No release instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(opSeqCsr); opSeqCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setOperatorSequenceFMRelease(param, opSeqNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, instCsr); } } } } /* Arpeggio */ { std::unordered_map tmpMap; std::unordered_map orgNumMap; tmpMap.emplace(FMOperatorType::All, instCtr.readUint8(instCsr)); instCsr += 3; tmpMap.emplace(FMOperatorType::Op1, instCtr.readUint8(instCsr++)); tmpMap.emplace(FMOperatorType::Op2, instCtr.readUint8(instCsr++)); tmpMap.emplace(FMOperatorType::Op3, instCtr.readUint8(instCsr++)); tmpMap.emplace(FMOperatorType::Op4, instCtr.readUint8(instCsr)); instCsr -= 5; for (auto& pair : tmpMap) { if (0x80 & pair.second) { fm->setArpeggioEnabled(pair.first, false); fm->setArpeggioNumber(pair.first, 0x7f & pair.second); } else { fm->setArpeggioEnabled(pair.first, true); uint8_t orgArpNum = 0x7f & pair.second; auto it = orgNumMap.find(orgArpNum); if (it == orgNumMap.end()) { // Make new property orgNumMap.emplace(orgArpNum, pair.first); int arpNum = instManLocked->findFirstAssignableArpeggioFM(); if (arpNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); fm->setArpeggioNumber(pair.first, arpNum); size_t arpCsr = getPropertyPosition(propCtr, 0x28, orgArpNum); if (arpCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(arpCsr); arpCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(arpCsr); arpCsr += 2; if (l == 0) instManLocked->setArpeggioFMSequenceData(arpNum, 0, data); else instManLocked->addArpeggioFMSequenceData(arpNum, data); } uint16_t loopCnt = propCtr.readUint16(arpCsr); arpCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(arpCsr); arpCsr += 2; int end = propCtr.readUint16(arpCsr); arpCsr += 2; int times = propCtr.readUint8(arpCsr++); instManLocked->addArpeggioFMLoop(arpNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // No release instManLocked->setArpeggioFMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(arpCsr); arpCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioFMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioFMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, arpCsr); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // Absolute instManLocked->setArpeggioFMType(arpNum, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioFMType(arpNum, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioFMType(arpNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setArpeggioFMType(arpNum, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Bank, arpCsr); } } } } else { // Use registered property fm->setArpeggioNumber(pair.first, fm->getArpeggioNumber(it->second)); } } } } /* Pitch */ { std::unordered_map tmpMap; std::unordered_map orgNumMap; tmpMap.emplace(FMOperatorType::All, instCtr.readUint8(instCsr)); instCsr += 6; tmpMap.emplace(FMOperatorType::Op1, instCtr.readUint8(instCsr++)); tmpMap.emplace(FMOperatorType::Op2, instCtr.readUint8(instCsr++)); tmpMap.emplace(FMOperatorType::Op3, instCtr.readUint8(instCsr++)); tmpMap.emplace(FMOperatorType::Op4, instCtr.readUint8(instCsr)); instCsr -= 8; for (auto& pair : tmpMap) { if (0x80 & pair.second) { fm->setPitchEnabled(pair.first, false); fm->setPitchNumber(pair.first, 0x7f & pair.second); } else { fm->setPitchEnabled(pair.first, true); uint8_t orgPtNum = 0x7f & pair.second; auto it = orgNumMap.find(orgPtNum); if (it == orgNumMap.end()) { // Make new property orgNumMap.emplace(orgPtNum, pair.first); int ptNum = instManLocked->findFirstAssignablePitchFM(); if (ptNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); fm->setPitchNumber(pair.first, ptNum); size_t ptCsr = getPropertyPosition(propCtr, 0x29, orgPtNum); if (ptCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(ptCsr); ptCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(ptCsr); ptCsr += 2; if (l == 0) instManLocked->setPitchFMSequenceData(ptNum, 0, data); else instManLocked->addPitchFMSequenceData(ptNum, data); } uint16_t loopCnt = propCtr.readUint16(ptCsr); ptCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(ptCsr); ptCsr += 2; int end = propCtr.readUint16(ptCsr); ptCsr += 2; int times = propCtr.readUint8(ptCsr++); instManLocked->addPitchFMLoop(ptNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // No release instManLocked->setPitchFMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(ptCsr); ptCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchFMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchFMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, ptCsr); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // Absolute instManLocked->setPitchFMType(ptNum, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchFMType(ptNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setPitchFMType(ptNum, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Bank, ptCsr); } } } } else { // Use registered property fm->setPitchNumber(pair.first, fm->getPitchNumber(it->second)); } } } } /* Envelope reset */ { uint8_t tmp = instCtr.readUint8(instCsr); fm->setEnvelopeResetEnabled(FMOperatorType::All, (tmp & 0x01)); fm->setEnvelopeResetEnabled(FMOperatorType::Op1, (tmp & 0x02)); fm->setEnvelopeResetEnabled(FMOperatorType::Op2, (tmp & 0x04)); fm->setEnvelopeResetEnabled(FMOperatorType::Op3, (tmp & 0x08)); fm->setEnvelopeResetEnabled(FMOperatorType::Op4, (tmp & 0x10)); } return fm; } case 0x01: // SSG { auto ssg = new InstrumentSSG(instNum, name, instManLocked.get()); /* Waveform */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { ssg->setWaveformEnabled(false); ssg->setWaveformNumber(0x7f & tmp); } else { ssg->setWaveformEnabled(true); uint8_t orgWfNum = 0x7f & tmp; int wfNum = instManLocked->findFirstAssignableWaveformSSG(); if (wfNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); ssg->setWaveformNumber(wfNum); size_t wfCsr = getPropertyPosition(propCtr, 0x30, orgWfNum); if (wfCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(wfCsr); wfCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(wfCsr); wfCsr += 2; int32_t subdata; subdata = propCtr.readInt32(wfCsr); wfCsr += 4; SSGWaveformUnit unit; switch (data) { case SSGWaveformType::SQM_TRIANGLE: case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: unit = SSGWaveformUnit::makeUnitWithDecode(data, subdata); break; default: unit = SSGWaveformUnit::makeOnlyDataUnit(data); break; } if (l == 0) instManLocked->setWaveformSSGSequenceData(wfNum, 0, unit); else instManLocked->addWaveformSSGSequenceData(wfNum, unit); } uint16_t loopCnt = propCtr.readUint16(wfCsr); wfCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(wfCsr); wfCsr += 2; int end = propCtr.readUint16(wfCsr); wfCsr += 2; int times = propCtr.readUint8(wfCsr++); instManLocked->addWaveformSSGLoop(wfNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(wfCsr++)) { case 0x00: // No release instManLocked->setWaveformSSGRelease(wfNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(wfCsr); wfCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setWaveformSSGRelease(wfNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setWaveformSSGRelease(wfNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, wfCsr); } } } } /* Tone/Noise */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { ssg->setToneNoiseEnabled(false); ssg->setToneNoiseNumber(0x7f & tmp); } else { ssg->setToneNoiseEnabled(true); uint8_t orgTnNum = 0x7f & tmp; int tnNum = instManLocked->findFirstAssignableToneNoiseSSG(); if (tnNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); ssg->setToneNoiseNumber(tnNum); size_t tnCsr = getPropertyPosition(propCtr, 0x31, orgTnNum); if (tnCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(tnCsr); tnCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(tnCsr); tnCsr += 2; if (bankVersion < Version::toBCD(1, 0, 1)) { if (data > 0) { uint16_t tmp = data - 1; data = tmp / 32 * 32 + (31 - tmp % 32) + 1; } } if (l == 0) instManLocked->setToneNoiseSSGSequenceData(tnNum, 0, data); else instManLocked->addToneNoiseSSGSequenceData(tnNum, data); } uint16_t loopCnt = propCtr.readUint16(tnCsr); tnCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(tnCsr); tnCsr += 2; int end = propCtr.readUint16(tnCsr); tnCsr += 2; int times = propCtr.readUint8(tnCsr++); instManLocked->addToneNoiseSSGLoop(tnNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(tnCsr++)) { case 0x00: // No release instManLocked->setToneNoiseSSGRelease(tnNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(tnCsr); tnCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(tnNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setToneNoiseSSGRelease(tnNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, tnCsr); } } } } /* Envelope */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { ssg->setEnvelopeEnabled(false); ssg->setEnvelopeNumber(0x7f & tmp); } else { ssg->setEnvelopeEnabled(true); uint8_t orgEnvNum = 0x7f & tmp; int envNum = instManLocked->findFirstAssignableEnvelopeSSG(); if (envNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); ssg->setEnvelopeNumber(envNum); size_t envCsr = getPropertyPosition(propCtr, 0x32, orgEnvNum); if (envCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(envCsr); envCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(envCsr); envCsr += 2; int32_t subdata; subdata = propCtr.readInt32(envCsr); envCsr += 4; SSGEnvelopeUnit unit = (data < 16) ? SSGEnvelopeUnit::makeOnlyDataUnit(data) : SSGEnvelopeUnit::makeUnitWithDecode(data, subdata); if (l == 0) instManLocked->setEnvelopeSSGSequenceData(envNum, 0, unit); else instManLocked->addEnvelopeSSGSequenceData(envNum, unit); } uint16_t loopCnt = propCtr.readUint16(envCsr); envCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(envCsr); envCsr += 2; int end = propCtr.readUint16(envCsr); envCsr += 2; int times = propCtr.readUint8(envCsr++); instManLocked->addEnvelopeSSGLoop(envNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(envCsr++)) { case 0x00: // No release instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); else instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); else instManLocked->setEnvelopeSSGRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, envCsr); } } } } /* Arpeggio */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { ssg->setArpeggioEnabled(false); ssg->setArpeggioNumber(0x7f & tmp); } else { ssg->setArpeggioEnabled(true); uint8_t orgArpNum = 0x7f & tmp; int arpNum = instManLocked->findFirstAssignableArpeggioSSG(); if (arpNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); ssg->setArpeggioNumber(arpNum); size_t arpCsr = getPropertyPosition(propCtr, 0x33, orgArpNum); if (arpCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(arpCsr); arpCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(arpCsr); arpCsr += 2; if (l == 0) instManLocked->setArpeggioSSGSequenceData(arpNum, 0, data); else instManLocked->addArpeggioSSGSequenceData(arpNum, data); } uint16_t loopCnt = propCtr.readUint16(arpCsr); arpCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(arpCsr); arpCsr += 2; int end = propCtr.readUint16(arpCsr); arpCsr += 2; int times = propCtr.readUint8(arpCsr++); instManLocked->addArpeggioSSGLoop(arpNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // No release instManLocked->setArpeggioSSGRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(arpCsr); arpCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioSSGRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioSSGRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, arpCsr); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // Absolute instManLocked->setArpeggioSSGType(arpNum, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioSSGType(arpNum, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioSSGType(arpNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setArpeggioSSGType(arpNum, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Bank, arpCsr); } } } } } /* Pitch */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { ssg->setPitchEnabled(false); ssg->setPitchNumber(0x7f & tmp); } else { ssg->setPitchEnabled(true); uint8_t orgPtNum = 0x7f & tmp; int ptNum = instManLocked->findFirstAssignablePitchSSG(); if (ptNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); ssg->setPitchNumber(ptNum); size_t ptCsr = getPropertyPosition(propCtr, 0x34, orgPtNum); if (ptCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(ptCsr); ptCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(ptCsr); ptCsr += 2; if (l == 0) instManLocked->setPitchSSGSequenceData(ptNum, 0, data); else instManLocked->addPitchSSGSequenceData(ptNum, data); } uint16_t loopCnt = propCtr.readUint16(ptCsr); ptCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(ptCsr); ptCsr += 2; int end = propCtr.readUint16(ptCsr); ptCsr += 2; int times = propCtr.readUint8(ptCsr++); instManLocked->addPitchSSGLoop(ptNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // No release instManLocked->setPitchSSGRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(ptCsr); ptCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchSSGRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchSSGRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, ptCsr); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // Absolute instManLocked->setPitchSSGType(ptNum, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchSSGType(ptNum, SequenceType::RelativeSequence); break; default: if (bankVersion < Version::toBCD(1, 0, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setPitchSSGType(ptNum, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Bank, ptCsr); } } } } } return ssg; } case 0x02: // ADPCM { auto adpcm = new InstrumentADPCM(instNum, name, instManLocked.get()); /* Sample */ { uint8_t orgSampNum = instCtr.readUint8(instCsr++); int sampNum = instManLocked->findFirstAssignableSampleADPCM(); if (sampNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); adpcm->setSampleNumber(sampNum); size_t sampCsr = getPropertyPosition(propCtr, 0x40, orgSampNum); if (sampCsr != std::numeric_limits::max()) { instManLocked->setSampleADPCMRootKeyNumber(sampNum, propCtr.readUint8(sampCsr++)); instManLocked->setSampleADPCMRootDeltaN(sampNum, propCtr.readUint16(sampCsr)); sampCsr += 2; instManLocked->setSampleADPCMRepeatEnabled(sampNum, (propCtr.readUint8(sampCsr++) & 0x01) != 0); uint32_t len = propCtr.readUint32(sampCsr); sampCsr += 4; std::vector samples = propCtr.getSubcontainer(sampCsr, len).toVector(); sampCsr += len; instManLocked->storeSampleADPCMRawSample(sampNum, std::move(samples)); } } /* Envelope */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { adpcm->setEnvelopeEnabled(false); adpcm->setEnvelopeNumber(0x7f & tmp); } else { adpcm->setEnvelopeEnabled(true); uint8_t orgEnvNum = 0x7f & tmp; int envNum = instManLocked->findFirstAssignableEnvelopeADPCM(); if (envNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); adpcm->setEnvelopeNumber(envNum); size_t envCsr = getPropertyPosition(propCtr, 0x41, orgEnvNum); if (envCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(envCsr); envCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(envCsr); envCsr += 6; // Skip subdata if (l == 0) instManLocked->setEnvelopeADPCMSequenceData(envNum, 0, data); else instManLocked->addEnvelopeADPCMSequenceData(envNum, data); } uint16_t loopCnt = propCtr.readUint16(envCsr); envCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(envCsr); envCsr += 2; int end = propCtr.readUint16(envCsr); envCsr += 2; int times = propCtr.readUint8(envCsr++); instManLocked->addEnvelopeADPCMLoop(envNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(envCsr++)) { case 0x00: // No release instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = propCtr.readUint16(envCsr); envCsr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(envNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, envCsr); } } } } /* Arpeggio */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { adpcm->setArpeggioEnabled(false); adpcm->setArpeggioNumber(0x7f & tmp); } else { adpcm->setArpeggioEnabled(true); uint8_t orgArpNum = 0x7f & tmp; int arpNum = instManLocked->findFirstAssignableArpeggioADPCM(); if (arpNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); adpcm->setArpeggioNumber(arpNum); size_t arpCsr = getPropertyPosition(propCtr, 0x42, orgArpNum); if (arpCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(arpCsr); arpCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(arpCsr); arpCsr += 2; if (l == 0) instManLocked->setArpeggioADPCMSequenceData(arpNum, 0, data); else instManLocked->addArpeggioADPCMSequenceData(arpNum, data); } uint16_t loopCnt = propCtr.readUint16(arpCsr); arpCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(arpCsr); arpCsr += 2; int end = propCtr.readUint16(arpCsr); arpCsr += 2; int times = propCtr.readUint8(arpCsr++); instManLocked->addArpeggioADPCMLoop(arpNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // No release instManLocked->setArpeggioADPCMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(arpCsr); arpCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioADPCMRelease(arpNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, arpCsr); } switch (propCtr.readUint8(arpCsr++)) { case 0x00: // Absolute instManLocked->setArpeggioADPCMType(arpNum, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioADPCMType(arpNum, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioADPCMType(arpNum, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Bank, arpCsr); } } } } /* Pitch */ { uint8_t tmp = instCtr.readUint8(instCsr++); if (0x80 & tmp) { adpcm->setPitchEnabled(false); adpcm->setPitchNumber(0x7f & tmp); } else { adpcm->setPitchEnabled(true); uint8_t orgPtNum = 0x7f & tmp; int ptNum = instManLocked->findFirstAssignablePitchADPCM(); if (ptNum == -1) throw FileCorruptionError(FileType::Bank, instCsr); adpcm->setPitchNumber(ptNum); size_t ptCsr = getPropertyPosition(propCtr, 0x43, orgPtNum); if (ptCsr != std::numeric_limits::max()) { uint16_t seqLen = propCtr.readUint16(ptCsr); ptCsr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = propCtr.readUint16(ptCsr); ptCsr += 2; if (l == 0) instManLocked->setPitchADPCMSequenceData(ptNum, 0, data); else instManLocked->addPitchADPCMSequenceData(ptNum, data); } uint16_t loopCnt = propCtr.readUint16(ptCsr); ptCsr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = propCtr.readUint16(ptCsr); ptCsr += 2; int end = propCtr.readUint16(ptCsr); ptCsr += 2; int times = propCtr.readUint8(ptCsr++); instManLocked->addPitchADPCMLoop(ptNum, InstrumentSequenceLoop(begin, end, times)); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // No release instManLocked->setPitchADPCMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = propCtr.readUint16(ptCsr); ptCsr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchADPCMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchADPCMRelease(ptNum, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Bank, ptCsr); } switch (propCtr.readUint8(ptCsr++)) { case 0x00: // Absolute instManLocked->setPitchADPCMType(ptNum, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchADPCMType(ptNum, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Bank, ptCsr); } } } } return adpcm; } case 0x03: // Drumkit { auto kit = new InstrumentDrumkit(instNum, name, instManLocked.get()); uint8_t keyCnt = instCtr.readUint8(instCsr++); std::unordered_map sampMap; int newSamp = 0; for (uint8_t i = 0; i < keyCnt; ++i) { int key = instCtr.readUint8(instCsr++); kit->setSampleEnabled(key, true); /* Sample */ { uint8_t orgSamp = instCtr.readUint8(instCsr++); if (sampMap.count(orgSamp)) { // Use registered property kit->setSampleNumber(key, sampMap.at(orgSamp)); } else { newSamp = instManLocked->findFirstAssignableSampleADPCM(newSamp); if (newSamp == -1) throw FileCorruptionError(FileType::Bank, instCsr); kit->setSampleNumber(key, newSamp); sampMap[orgSamp] = newSamp; size_t sampCsr = getPropertyPosition(propCtr, 0x40, orgSamp); if (sampCsr != std::numeric_limits::max()) { instManLocked->setSampleADPCMRootKeyNumber(newSamp, propCtr.readUint8(sampCsr++)); instManLocked->setSampleADPCMRootDeltaN(newSamp, propCtr.readUint16(sampCsr)); sampCsr += 2; instManLocked->setSampleADPCMRepeatEnabled(newSamp, (propCtr.readUint8(sampCsr++) & 0x01) != 0); uint32_t len = propCtr.readUint32(sampCsr); sampCsr += 4; std::vector samples = propCtr.getSubcontainer(sampCsr, len).toVector(); sampCsr += len; instManLocked->storeSampleADPCMRawSample(newSamp, std::move(samples)); ++newSamp; // Increment for search } } } /* Pitch */ kit->setPitch(key, instCtr.readInt8(instCsr++)); } return kit; } default: throw FileCorruptionError(FileType::Bank, instCsr); } } } BambooTracker-0.4.6/BambooTracker/io/btb_io.hpp000066400000000000000000000032211401124043500213150ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class BtbIO final : public AbstractBankIO { public: BtbIO(); AbstractBank* load(const BinaryContainer& ctr) const override; void save(BinaryContainer& ctr, const std::weak_ptr instMan, const std::vector& instNums) const override; static AbstractInstrument* loadInstrument(const BinaryContainer& instCtr, const BinaryContainer& propCtr, std::weak_ptr instMan, int instNum, uint32_t bankVersion); }; } BambooTracker-0.4.6/BambooTracker/io/bti_io.cpp000066400000000000000000002562731401124043500213400ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "bti_io.hpp" #include #include #include #include #include "enum_hash.hpp" #include "version.hpp" #include "file_io_error.hpp" #include "note.hpp" #include "io_utils.hpp" #include "utils.hpp" namespace io { namespace { size_t loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter param, size_t instMemCsr, std::shared_ptr& instManLocked, const BinaryContainer& ctr, InstrumentFM* inst, int idx, uint32_t version) { inst->setOperatorSequenceEnabled(param, true); inst->setOperatorSequenceNumber(param, idx); uint16_t ofs = ctr.readUint16(instMemCsr); size_t csr = instMemCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 1)) csr += 2; if (l == 0) instManLocked->setOperatorSequenceFMSequenceData(param, idx, 0, data); else instManLocked->addOperatorSequenceFMSequenceData(param, idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addOperatorSequenceFMLoop(param, idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (version >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } return ofs; } } BtiIO::BtiIO() : AbstractInstrumentIO("bti", "BambooTracker instrument", true, true) {} AbstractInstrument* BtiIO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { (void)fileName; std::shared_ptr instManLocked = instMan.lock(); size_t globCsr = 0; if (ctr.readString(globCsr, 16) != "BambooTrackerIst") throw FileCorruptionError(FileType::Inst, globCsr); globCsr += 16; /*size_t eofOfs = */ctr.readUint32(globCsr); globCsr += 4; size_t fileVersion = ctr.readUint32(globCsr); if (fileVersion > Version::ofInstrumentFileInBCD()) throw FileVersionError(FileType::Inst); globCsr += 4; /***** Instrument section *****/ if (ctr.readString(globCsr, 8) != "INSTRMNT") throw FileCorruptionError(FileType::Inst, globCsr); else { globCsr += 8; size_t instOfs = ctr.readUint32(globCsr); size_t instCsr = globCsr + 4; size_t nameLen = ctr.readUint32(instCsr); instCsr += 4; std::string name = u8""; if (nameLen > 0) { name = ctr.readString(instCsr, nameLen); instCsr += nameLen; } std::unordered_map fmArpMap, fmPtMap; std::unordered_map> kitSampFileMap, kitSampMap; AbstractInstrument* inst = nullptr; switch (ctr.readUint8(instCsr++)) { case 0x00: // FM { inst = new InstrumentFM(instNum, name, instManLocked.get()); auto fm = dynamic_cast(inst); uint8_t tmp = ctr.readUint8(instCsr++); fm->setEnvelopeResetEnabled(FMOperatorType::All, (tmp & 0x01)); fm->setEnvelopeResetEnabled(FMOperatorType::Op1, (tmp & 0x02)); fm->setEnvelopeResetEnabled(FMOperatorType::Op2, (tmp & 0x04)); fm->setEnvelopeResetEnabled(FMOperatorType::Op3, (tmp & 0x08)); fm->setEnvelopeResetEnabled(FMOperatorType::Op4, (tmp & 0x10)); if (fileVersion >= Version::toBCD(1, 1, 0)) { fmArpMap.emplace(FMOperatorType::All, ctr.readUint8(instCsr++)); for (auto& t : FM_OP_TYPES) { tmp = ctr.readUint8(instCsr++); if (!(tmp & 0x80)) fmArpMap.emplace(t, (tmp & 0x7f)); } fmPtMap.emplace(FMOperatorType::All, ctr.readUint8(instCsr++)); for (auto& t : FM_OP_TYPES) { tmp = ctr.readUint8(instCsr++); if (!(tmp & 0x80)) fmPtMap.emplace(t, (tmp & 0x7f)); } } break; } case 0x01: // SSG { inst = new InstrumentSSG(instNum, name, instManLocked.get()); break; } case 0x02: // ADPCM { inst = new InstrumentADPCM(instNum, name, instManLocked.get()); break; } case 0x03: // Drumkit { inst = new InstrumentDrumkit(instNum, name, instManLocked.get()); auto kit = dynamic_cast(inst); uint8_t cnt = ctr.readUint8(instCsr++); for (uint8_t i = 0; i < cnt; ++i) { int key = ctr.readUint8(instCsr++); int samp = ctr.readUint8(instCsr++); if (kitSampFileMap.count(samp)) kitSampFileMap[samp].push_back(key); else kitSampFileMap[samp] = { key }; kit->setSampleEnabled(key, true); kit->setPitch(key, ctr.readInt8(instCsr++)); } break; } default: throw FileCorruptionError(FileType::Inst, instCsr); } globCsr += instOfs; /***** Instrument property section *****/ if (ctr.readString(globCsr, 8) != "INSTPROP") throw FileCorruptionError(FileType::Inst, globCsr); else { globCsr += 8; size_t instPropOfs = ctr.readUint32(globCsr); size_t instPropCsr = globCsr + 4; size_t instPropCsrTmp = instPropCsr; globCsr += instPropOfs; int kitSampCnt = 0; int adpcmSampIdx = 0; std::vector nums; // Check memory range while (instPropCsr < globCsr) { switch (ctr.readUint8(instPropCsr++)) { case 0x00: // FM envelope { nums.push_back(instManLocked->findFirstAssignableEnvelopeFM()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint8(instPropCsr); break; } case 0x01: // FM LFO { nums.push_back(instManLocked->findFirstAssignableLFOFM()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint8(instPropCsr); break; } case 0x02: // FM AL { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::AL)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x03: // FM FB { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::FB)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x04: // FM AR1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::AR1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x05: // FM DR1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DR1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x06: // FM SR1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SR1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x07: // FM RR1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::RR1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x08: // FM SL1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SL1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x09: // FM TL1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::TL1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x0a: // FM KS1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::KS1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x0b: // FM ML1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::ML1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x0c: // FM DT1 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DT1)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x0d: // FM AR2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::AR2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x0e: // FM DR2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DR2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x0f: // FM SR2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SR2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x10: // FM RR2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::RR2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x11: // FM SL2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SL2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x12: // FM TL2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::TL2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x13: // FM KS2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::KS2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x14: // FM ML2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::ML2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x15: // FM DT2 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DT2)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x16: // FM AR3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::AR3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x17: // FM DR3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DR3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x18: // FM SR3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SR3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x19: // FM RR3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::RR3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x1a: // FM SL3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SL3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x1b: // FM TL3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::TL3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x1c: // FM KS3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::KS3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x1d: // FM ML3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::ML3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x1e: // FM DT3 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DT3)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x1f: // FM AR4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::AR4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x20: // FM DR4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DR4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x21: // FM SR4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SR4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x22: // FM RR4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::RR4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x23: // FM SL4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::SL4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x24: // FM TL4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::TL4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x25: // FM KS4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::KS4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x26: // FM ML4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::ML4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x27: // FM DT4 { nums.push_back(instManLocked->findFirstAssignableOperatorSequenceFM(FMEnvelopeParameter::DT4)); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x28: // FM arpeggio { nums.push_back(instManLocked->findFirstAssignableArpeggioFM()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x29: // FM pitch { nums.push_back(instManLocked->findFirstAssignablePitchFM()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x30: // SSG waveform { nums.push_back(instManLocked->findFirstAssignableWaveformSSG()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x31: // SSG tone/noise { nums.push_back(instManLocked->findFirstAssignableToneNoiseSSG()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x32: // SSG envelope { nums.push_back(instManLocked->findFirstAssignableEnvelopeSSG()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x33: // SSG arpeggio { nums.push_back(instManLocked->findFirstAssignableArpeggioSSG()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x34: // SSG pitch { nums.push_back(instManLocked->findFirstAssignablePitchSSG()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x40: // ADPCM sample { adpcmSampIdx = instManLocked->findFirstAssignableSampleADPCM(adpcmSampIdx); if (adpcmSampIdx == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); nums.push_back(adpcmSampIdx); if (inst->getType() == InstrumentType::Drumkit) { kitSampMap[adpcmSampIdx] = kitSampFileMap.at(kitSampCnt++); } instPropCsr += ctr.readUint16(instPropCsr); ++adpcmSampIdx; // Increment for search appropriate kit sample break; } case 0x41: // ADPCM envelope { nums.push_back(instManLocked->findFirstAssignableEnvelopeADPCM()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x42: // ADPCM arpeggio { nums.push_back(instManLocked->findFirstAssignableArpeggioADPCM()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } case 0x43: // ADPCM pitch { nums.push_back(instManLocked->findFirstAssignablePitchADPCM()); if (nums.back() == -1) throw FileCorruptionError(FileType::Inst, instPropCsr); instPropCsr += ctr.readUint16(instPropCsr); break; } default: throw FileCorruptionError(FileType::Inst, instPropCsr); } } // Read data instPropCsr = instPropCsrTmp; auto numIt = nums.begin(); while (instPropCsr < globCsr) { switch (ctr.readUint8(instPropCsr++)) { case 0x00: // FM envelope { int idx = *numIt++; dynamic_cast(inst)->setEnvelopeNumber(idx); uint8_t ofs = ctr.readUint8(instPropCsr); size_t csr = instPropCsr + 1; uint8_t tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, FMEnvelopeParameter::AL, tmp >> 4); instManLocked->setEnvelopeFMParameter(idx, FMEnvelopeParameter::FB, tmp & 0x0f); for (int op = 0; op < 4; ++op) { auto& params = FM_OP_PARAMS[op]; tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMOperatorEnabled(idx, op, (0x20 & tmp) ? true : false); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::AR), tmp & 0x1f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::KS), tmp >> 5); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DR), tmp & 0x1f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DT), tmp >> 5); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SR), tmp & 0x1f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SL), tmp >> 4); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::RR), tmp & 0x0f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::TL), tmp); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::ML), tmp & 0x0f); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SSGEG), (tmp & 0x80) ? -1 : ((tmp >> 4) & 0x07)); } instPropCsr += ofs; break; } case 0x01: // FM LFO { int idx = *numIt++; auto fm = dynamic_cast(inst); fm->setLFOEnabled(true); fm->setLFONumber(idx); uint8_t ofs = ctr.readUint8(instPropCsr); size_t csr = instPropCsr + 1; uint8_t tmp = ctr.readUint8(csr++); instManLocked->setLFOFMParameter(idx, FMLFOParameter::FREQ, tmp >> 4); instManLocked->setLFOFMParameter(idx, FMLFOParameter::PMS, tmp & 0x0f); tmp = ctr.readUint8(csr++); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AMS, tmp & 0x0f); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM1, (tmp & 0x10) ? true : false); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM2, (tmp & 0x20) ? true : false); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM3, (tmp & 0x40) ? true : false); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM4, (tmp & 0x80) ? true : false); tmp = ctr.readUint8(csr++); instManLocked->setLFOFMParameter(idx, FMLFOParameter::Count, tmp); instPropCsr += ofs; break; } case 0x02: // FM AL { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::AL, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x03: // FM FB { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::FB, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x04: // FM AR1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::AR1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x05: // FM DR1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DR1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x06: // FM SR1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SR1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x07: // FM RR1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::RR1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x08: // FM SL1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SL1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x09: // FM TL1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::TL1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x0a: // FM KS1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::KS1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x0b: // FM ML1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::ML1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x0c: // FM DT1 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DT1, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x0d: // FM AR2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::AR2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x0e: // FM DR2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DR2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x0f: // FM SR2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SR2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x10: // FM RR2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::RR2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x11: // FM SL2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SL2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x12: // FM TL2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::TL2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x13: // FM KS2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::KS2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x14: // FM ML2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::ML2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x15: // FM DT2 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DT2, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x16: // FM AR3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::AR3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x17: // FM DR3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DR3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x18: // FM SR3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SR3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x19: // FM RR3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::RR3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x1a: // FM SL3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SL3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x1b: // FM TL3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::TL3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x1c: // FM KS3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::KS3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x1d: // FM ML3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::ML3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x1e: // FM DT3 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DT3, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x1f: // FM AR4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::AR4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x20: // FM DR4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DR4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x21: // FM SR4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SR4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x22: // FM RR4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::RR4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x23: // FM SL4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::SL4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x24: // FM TL4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::TL4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x25: // FM KS4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::KS4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x26: // FM ML4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::ML4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x27: // FM DT4 { instPropCsr += loadInstrumentPropertyOperatorSequenceForInstrument( FMEnvelopeParameter::DT4, instPropCsr, instManLocked, ctr, dynamic_cast(inst), *numIt++, fileVersion); break; } case 0x28: // FM arpeggio { int idx = *numIt++; auto fm = dynamic_cast(inst); if (fileVersion >= Version::toBCD(1, 1, 0)) { std::vector del; for (auto& pair : fmArpMap) { if (pair.second-- == 0) { fm->setArpeggioEnabled(pair.first, true); fm->setArpeggioNumber(pair.first, idx); del.push_back(pair.first); } } for (auto t : del) fmArpMap.erase(t); } else { fm->setArpeggioEnabled(FMOperatorType::All, true); fm->setArpeggioNumber(FMOperatorType::All, idx); } uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setArpeggioFMSequenceData(idx, 0, data); else instManLocked->addArpeggioFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addArpeggioFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioFMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioFMType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Inst, csr); } } } instPropCsr += ofs; break; } case 0x29: // FM pitch { int idx = *numIt++; auto fm = dynamic_cast(inst); if (fileVersion >= Version::toBCD(1, 1, 0)) { std::vector del; for (auto& pair : fmPtMap) { if (pair.second-- == 0) { fm->setPitchEnabled(pair.first, true); fm->setPitchNumber(pair.first, idx); del.push_back(pair.first); } } for (auto t : del) fmPtMap.erase(t); } else { fm->setPitchEnabled(FMOperatorType::All, true); fm->setPitchNumber(FMOperatorType::All, idx); } uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setPitchFMSequenceData(idx, 0, data); else instManLocked->addPitchFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addPitchFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchFMType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Inst, csr); } } } instPropCsr += ofs; break; } case 0x30: // SSG waveform { int idx = *numIt++; auto ssg = dynamic_cast(inst); ssg->setWaveformEnabled(true); ssg->setWaveformNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) { if (data == 3) data = SSGWaveformType::SQM_TRIANGLE; else if (data == 4) data = SSGWaveformType::SQM_SAW; } int32_t subdata; if (fileVersion >= Version::toBCD(1, 2, 0)) { subdata = ctr.readInt32(csr); csr += 4; } else { subdata = ctr.readUint16(csr); csr += 2; if (subdata != -1) subdata = note_utils::calculateSSGSquareTP(subdata, 0); } SSGWaveformUnit unit; switch (data) { case SSGWaveformType::SQM_TRIANGLE: case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: unit = SSGWaveformUnit::makeUnitWithDecode(data, subdata); break; default: unit = SSGWaveformUnit::makeOnlyDataUnit(data); break; } if (l == 0) instManLocked->setWaveformSSGSequenceData(idx, 0, unit); else instManLocked->addWaveformSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addWaveformSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (fileVersion >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } instPropCsr += ofs; break; } case 0x31: // SSG tone/noise { int idx = *numIt++; auto ssg = dynamic_cast(inst); ssg->setToneNoiseEnabled(true); ssg->setToneNoiseNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 2)) { if (data > 0) { uint16_t tmp = data - 1; data = tmp / 32 * 32 + (31 - tmp % 32) + 1; } } if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setToneNoiseSSGSequenceData(idx, 0, data); else instManLocked->addToneNoiseSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addToneNoiseSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (fileVersion >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } instPropCsr += ofs; break; } case 0x32: // SSG envelope { int idx = *numIt++; auto ssg = dynamic_cast(inst); ssg->setEnvelopeEnabled(true); ssg->setEnvelopeNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; int32_t subdata; if (fileVersion >= Version::toBCD(1, 2, 0)) { subdata = ctr.readInt32(csr); csr += 4; } else { subdata = ctr.readUint16(csr); csr += 2; } SSGEnvelopeUnit unit = (data < 16) ? SSGEnvelopeUnit::makeOnlyDataUnit(data) : SSGEnvelopeUnit::makeUnitWithDecode(data, subdata); if (l == 0) instManLocked->setEnvelopeSSGSequenceData(idx, 0, unit); else instManLocked->addEnvelopeSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addEnvelopeSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (fileVersion >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } instPropCsr += ofs; break; } case 0x33: // SSG arpeggio { int idx = *numIt++; auto ssg = dynamic_cast(inst); ssg->setArpeggioEnabled(true); ssg->setArpeggioNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setArpeggioSSGSequenceData(idx, 0, data); else instManLocked->addArpeggioSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addArpeggioSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioSSGType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioSSGType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Inst, csr); } } } instPropCsr += ofs; break; } case 0x34: // SSG pitch { int idx = *numIt++; auto ssg = dynamic_cast(inst); ssg->setPitchEnabled(true); ssg->setPitchNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setPitchSSGSequenceData(idx, 0, data); else instManLocked->addPitchSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addPitchSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } if (fileVersion >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchSSGType(idx, SequenceType::RelativeSequence); break; default: if (fileVersion < Version::toBCD(1, 2, 3)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Inst, csr); } } } instPropCsr += ofs; break; } case 0x40: // ADPCM sample { int idx = *numIt++; if (inst->getType() == InstrumentType::ADPCM) { auto adpcm = dynamic_cast(inst); adpcm->setSampleNumber(idx); } else if (inst->getType() == InstrumentType::Drumkit) { auto kit = dynamic_cast(inst); for (const int& key : kitSampMap.at(idx)) kit->setSampleNumber(key, idx); } uint32_t ofs = ctr.readUint32(instPropCsr); size_t csr = instPropCsr + 4; instManLocked->setSampleADPCMRootKeyNumber(idx, ctr.readUint8(csr++)); instManLocked->setSampleADPCMRootDeltaN(idx, ctr.readUint16(csr)); csr += 2; instManLocked->setSampleADPCMRepeatEnabled(idx, (ctr.readUint8(csr++) & 0x01) != 0); uint32_t len = ctr.readUint32(csr); csr += 4; std::vector samples = ctr.getSubcontainer(csr, len).toVector(); /* csr += len; */ instManLocked->storeSampleADPCMRawSample(idx, std::move(samples)); instPropCsr += ofs; break; } case 0x41: // ADPCM envelope { int idx = *numIt++; auto adpcm = dynamic_cast(inst); adpcm->setEnvelopeEnabled(true); adpcm->setEnvelopeNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 6; // Skip subdata if (l == 0) instManLocked->setEnvelopeADPCMSequenceData(idx, 0, data); else instManLocked->addEnvelopeADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addEnvelopeADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } ++csr; // Skip sequence type instPropCsr += ofs; break; } case 0x42: // ADPCM arpeggio { int idx = *numIt++; auto adpcm = dynamic_cast(inst); adpcm->setArpeggioEnabled(true); adpcm->setArpeggioNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setArpeggioADPCMSequenceData(idx, 0, data); else instManLocked->addArpeggioADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addArpeggioADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setArpeggioADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioADPCMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Inst, csr); } instPropCsr += ofs; break; } case 0x43: // ADPCM pitch { int idx = *numIt++; auto adpcm = dynamic_cast(inst); adpcm->setPitchEnabled(true); adpcm->setPitchNumber(idx); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (fileVersion < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setPitchADPCMSequenceData(idx, 0, data); else instManLocked->addPitchADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addPitchADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Inst, csr); } switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setPitchADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Inst, csr); } instPropCsr += ofs; break; } default: throw FileCorruptionError(FileType::Inst, instPropCsr); } } } return inst; } } void BtiIO::save(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum) const { const std::shared_ptr instManLocked = instMan.lock(); ctr.appendString("BambooTrackerIst"); size_t eofOfs = ctr.size(); ctr.appendUint32(0); // Dummy EOF offset uint32_t fileVersion = Version::ofInstrumentFileInBCD(); ctr.appendUint32(fileVersion); /***** Instrument section *****/ ctr.appendString("INSTRMNT"); size_t instOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument section offset std::vector fmArpNums, fmPtNums; std::shared_ptr inst = instManLocked->getInstrumentSharedPtr(instNum); if (inst) { std::string name = inst->getName(); ctr.appendUint32(name.length()); if (!name.empty()) ctr.appendString(name); switch (inst->getType()) { case InstrumentType::FM: { ctr.appendUint8(0x00); auto instFM = std::dynamic_pointer_cast(inst); uint8_t tmp = static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::All)) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op1) << 1) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op2) << 2) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op3) << 3) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op4) << 4); ctr.appendUint8(tmp); if (instFM->getArpeggioEnabled(FMOperatorType::All)) fmArpNums.push_back(instFM->getArpeggioNumber(FMOperatorType::All)); for (auto& t : FM_OP_TYPES) { if (instFM->getArpeggioEnabled(t)) { int n = instFM->getArpeggioNumber(t); if (utils::find(fmArpNums, n) == fmArpNums.end()) fmArpNums.push_back(n); } } if (instFM->getArpeggioEnabled(FMOperatorType::All)) { int n = instFM->getArpeggioNumber(FMOperatorType::All); for (size_t i = 0; i < fmArpNums.size(); ++i) { if (n == fmArpNums[i]) { ctr.appendUint8(static_cast(i)); break; } } } else { ctr.appendUint8(0x80); } for (auto& t : FM_OP_TYPES) { if (instFM->getArpeggioEnabled(t)) { int n = instFM->getArpeggioNumber(t); for (size_t i = 0; i < fmArpNums.size(); ++i) { if (n == fmArpNums[i]) { ctr.appendUint8(static_cast(i)); break; } } } else { ctr.appendUint8(0x80); } } if (instFM->getPitchEnabled(FMOperatorType::All)) fmPtNums.push_back(instFM->getPitchNumber(FMOperatorType::All)); for (auto& t : FM_OP_TYPES) { if (instFM->getPitchEnabled(t)) { int n = instFM->getPitchNumber(t); if (utils::find(fmPtNums, n) == fmPtNums.end()) fmPtNums.push_back(n); } } if (instFM->getPitchEnabled(FMOperatorType::All)) { int n = instFM->getPitchNumber(FMOperatorType::All); for (size_t i = 0; i < fmPtNums.size(); ++i) { if (n == fmPtNums[i]) { ctr.appendUint8(static_cast(i)); break; } } } else { ctr.appendUint8(0x80); } for (auto& t : FM_OP_TYPES) { if (instFM->getPitchEnabled(t)) { int n = instFM->getPitchNumber(t); for (size_t i = 0; i < fmPtNums.size(); ++i) { if (n == fmPtNums[i]) { ctr.appendUint8(static_cast(i)); break; } } } else { ctr.appendUint8(0x80); } } break; } case InstrumentType::SSG: { ctr.appendUint8(0x01); break; } case InstrumentType::ADPCM: { ctr.appendUint8(0x02); break; } case InstrumentType::Drumkit: { ctr.appendUint8(0x03); auto kit = std::dynamic_pointer_cast(inst); std::vector keys = kit->getAssignedKeys(); ctr.appendUint8(keys.size()); int sampCnt = 0; std::unordered_map sampMap; for (const int& key : keys) { ctr.appendUint8(static_cast(key)); int samp = kit->getSampleNumber(key); if (!sampMap.count(samp)) sampMap[samp] = sampCnt++; ctr.appendUint8(static_cast(sampMap[samp])); ctr.appendInt8(kit->getPitch(key)); } break; } } } ctr.writeUint32(instOfs, ctr.size() - instOfs); /***** Instrument property section *****/ ctr.appendString("INSTPROP"); size_t instPropOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument property section offset switch (inst->getType()) { case InstrumentType::FM: { auto instFM = std::dynamic_pointer_cast(inst); // FM envelope int envNum = instFM->getEnvelopeNumber(); { ctr.appendUint8(0x00); size_t ofs = ctr.size(); ctr.appendUint8(0); // Dummy offset uint8_t tmp = static_cast(instManLocked->getEnvelopeFMParameter(envNum, FMEnvelopeParameter::AL) << 4) | static_cast(instManLocked->getEnvelopeFMParameter(envNum, FMEnvelopeParameter::FB)); ctr.appendUint8(tmp); for (int op = 0; op < 4; ++op) { auto& params = FM_OP_PARAMS[op]; tmp = instManLocked->getEnvelopeFMOperatorEnabled(envNum, op); tmp = static_cast(tmp << 5) | static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::AR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::KS)) << 5) | static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::DR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::DT)) << 5) | static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::SR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::SL)) << 4) | static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::RR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::TL))); ctr.appendUint8(tmp); int tmp2 = instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::SSGEG)); tmp = ((tmp2 == -1) ? 0x80 : static_cast(tmp2 << 4)) | static_cast(instManLocked->getEnvelopeFMParameter(envNum, params.at(FMOperatorParameter::ML))); ctr.appendUint8(tmp); } ctr.writeUint8(ofs, static_cast(ctr.size() - ofs)); } // FM LFO if (instFM->getLFOEnabled()) { int lfoNum = instFM->getLFONumber(); ctr.appendUint8(0x01); size_t ofs = ctr.size(); ctr.appendUint8(0); // Dummy offset uint8_t tmp = static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::FREQ) << 4) | static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::PMS)); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::AM4) << 7) | static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::AM3) << 6) | static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::AM2) << 5) | static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::AM1) << 4) | static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::AMS)); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getLFOFMparameter(lfoNum, FMLFOParameter::Count)); ctr.appendUint8(tmp); ctr.writeUint8(ofs, static_cast(ctr.size() - ofs)); } // FM envelope parameter for (size_t i = 0; i < 38; ++i) { if (instFM->getOperatorSequenceEnabled(FM_OPSEQ_PARAMS[i])) { int seqNum = instFM->getOperatorSequenceNumber(FM_OPSEQ_PARAMS[i]); ctr.appendUint8(0x02 + static_cast(i)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getOperatorSequenceFMSequence(FM_OPSEQ_PARAMS[i], seqNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getOperatorSequenceFMLoopRoot(FM_OPSEQ_PARAMS[i], seqNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getOperatorSequenceFMRelease(FM_OPSEQ_PARAMS[i], seqNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // FM arpeggio for (int& arpNum : fmArpNums) { ctr.appendUint8(0x28); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioFMSequence(arpNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getArpeggioFMLoopRoot(arpNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioFMRelease(arpNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioFMType(arpNum)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } // FM pitch for (int& ptNum : fmPtNums) { ctr.appendUint8(0x29); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchFMSequence(ptNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getPitchFMLoopRoot(ptNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchFMRelease(ptNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchFMType(ptNum)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } break; } case InstrumentType::SSG: { auto instSSG = std::dynamic_pointer_cast(inst); // SSG waveform if (instSSG->getWaveformEnabled()) { int wfNum = instSSG->getWaveformNumber(); ctr.appendUint8(0x30); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getWaveformSSGSequence(wfNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(static_cast(unit.subdata)); } auto loops = instManLocked->getWaveformSSGLoopRoot(wfNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getWaveformSSGRelease(wfNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } // SSG tone/noise if (instSSG->getToneNoiseEnabled()) { int tnNum = instSSG->getToneNoiseNumber(); ctr.appendUint8(0x31); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getToneNoiseSSGSequence(tnNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getToneNoiseSSGLoopRoot(tnNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getToneNoiseSSGRelease(tnNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } // SSG envelope if (instSSG->getEnvelopeEnabled()) { int envNum = instSSG->getEnvelopeNumber(); ctr.appendUint8(0x32); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeSSGSequence(envNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(static_cast(unit.subdata)); } auto loops = instManLocked->getEnvelopeSSGLoopRoot(envNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeSSGRelease(envNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } // SSG arpeggio if (instSSG->getArpeggioEnabled()) { int arpNum = instSSG->getArpeggioNumber(); ctr.appendUint8(0x33); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioSSGSequence(arpNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getArpeggioSSGLoopRoot(arpNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioSSGRelease(arpNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioSSGType(arpNum)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } // SSG pitch if (instSSG->getPitchEnabled()) { int ptNum = instSSG->getPitchNumber(); ctr.appendUint8(0x34); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchSSGSequence(ptNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getPitchSSGLoopRoot(ptNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchSSGRelease(ptNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchSSGType(ptNum)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } break; } case InstrumentType::ADPCM: { auto instADPCM = std::dynamic_pointer_cast(inst); // ADPCM sample int sampNum = instADPCM->getSampleNumber(); { ctr.appendUint8(0x40); size_t ofs = ctr.size(); ctr.appendUint32(0); // Dummy offset ctr.appendUint8(static_cast(instManLocked->getSampleADPCMRootKeyNumber(sampNum))); ctr.appendUint16(static_cast(instManLocked->getSampleADPCMRootDeltaN(sampNum))); ctr.appendUint8(static_cast(instManLocked->isSampleADPCMRepeatable(sampNum))); std::vector samples = instManLocked->getSampleADPCMRawSample(sampNum); ctr.appendUint32(samples.size()); ctr.appendVector(std::move(samples)); ctr.writeUint32(ofs, ctr.size() - ofs); } // ADPCM envelope if (instADPCM->getEnvelopeEnabled()) { int envNum = instADPCM->getEnvelopeNumber(); ctr.appendUint8(0x41); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeADPCMSequence(envNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(0); // Dummy set for past format } auto loops = instManLocked->getEnvelopeADPCMLoopRoot(envNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeADPCMRelease(envNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } // ADPCM arpeggio if (instADPCM->getArpeggioEnabled()) { int arpNum = instADPCM->getArpeggioNumber(); ctr.appendUint8(0x42); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioADPCMSequence(arpNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getArpeggioADPCMLoopRoot(arpNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioADPCMRelease(arpNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioADPCMType(arpNum)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } // ADPCM pitch if (instADPCM->getPitchEnabled()) { int ptNum = instADPCM->getPitchNumber(); ctr.appendUint8(0x43); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchADPCMSequence(ptNum); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getPitchADPCMLoopRoot(ptNum).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchADPCMRelease(ptNum); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchADPCMType(ptNum)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } break; } case InstrumentType::Drumkit: { auto kit = std::dynamic_pointer_cast(inst); // ADPCM sample { std::vector sampList; for (const int& key : kit->getAssignedKeys()) { int samp = kit->getSampleNumber(key); if (std::none_of(sampList.begin(), sampList.end(), [samp](const int& v) { return v == samp; })) { sampList.push_back(samp); ctr.appendUint8(0x40); size_t ofs = ctr.size(); ctr.appendUint32(0); // Dummy offset ctr.appendUint8(static_cast(instManLocked->getSampleADPCMRootKeyNumber(samp))); ctr.appendUint16(static_cast(instManLocked->getSampleADPCMRootDeltaN(samp))); ctr.appendUint8(static_cast(instManLocked->isSampleADPCMRepeatable(samp))); std::vector samples = instManLocked->getSampleADPCMRawSample(samp); ctr.appendUint32(samples.size()); ctr.appendVector(std::move(samples)); ctr.writeUint32(ofs, ctr.size() - ofs); } } } break; } } ctr.writeUint32(instPropOfs, ctr.size() - instPropOfs); ctr.writeUint32(eofOfs, ctr.size() - eofOfs); } } BambooTracker-0.4.6/BambooTracker/io/bti_io.hpp000066400000000000000000000027741401124043500213400ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "instrument_io.hpp" namespace io { class BtiIO final : public AbstractInstrumentIO { public: BtiIO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; void save(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum) const override; }; } BambooTracker-0.4.6/BambooTracker/io/btm_io.cpp000066400000000000000000002604121401124043500213320ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "btm_io.hpp" #include "file_io_error.hpp" #include "version.hpp" #include "note.hpp" #include "effect.hpp" #include "io_utils.hpp" namespace io { namespace { size_t loadModuleSection(std::weak_ptr mod, const BinaryContainer& ctr, size_t globCsr, uint32_t version) { std::shared_ptr modLocked = mod.lock(); size_t modOfs = ctr.readUint32(globCsr); size_t modCsr = globCsr + 4; size_t modTitleLen = ctr.readUint32(modCsr); modCsr += 4; if (modTitleLen > 0) { modLocked->setTitle(ctr.readString(modCsr, modTitleLen)); modCsr += modTitleLen; } size_t modAuthorLen = ctr.readUint32(modCsr); modCsr += 4; if (modAuthorLen > 0) { modLocked->setAuthor(ctr.readString(modCsr, modAuthorLen)); modCsr += modAuthorLen; } size_t modCopyrightLen = ctr.readUint32(modCsr); modCsr += 4; if (modCopyrightLen > 0) { modLocked->setCopyright(ctr.readString(modCsr, modCopyrightLen)); modCsr += modCopyrightLen; } size_t modCommentLen = ctr.readUint32(modCsr); modCsr += 4; if (modCommentLen > 0) { modLocked->setComment(ctr.readString(modCsr, modCommentLen)); modCsr += modCommentLen; } modLocked->setTickFrequency(ctr.readUint32(modCsr)); modCsr += 4; modLocked->setStepHighlight1Distance(ctr.readUint32(modCsr)); modCsr += 4; if (version >= Version::toBCD(1, 0, 3)) { modLocked->setStepHighlight2Distance(ctr.readUint32(modCsr)); modCsr += 4; } else { modLocked->setStepHighlight2Distance(modLocked->getStepHighlight1Distance() * 4); } if (version >= Version::toBCD(1, 3, 0)) { auto mixType = static_cast(ctr.readUint8(modCsr++)); modLocked->setMixerType(mixType); if (mixType == MixerType::CUSTOM) { modLocked->setCustomMixerFMLevel(ctr.readInt8(modCsr++) / 10.0); modLocked->setCustomMixerSSGLevel(ctr.readInt8(modCsr++) / 10.0); } } else { modLocked->setMixerType(MixerType::UNSPECIFIED); } return globCsr + modOfs; } size_t loadInstrumentSection(std::weak_ptr instMan, const BinaryContainer& ctr, size_t globCsr, uint32_t version) { std::shared_ptr instManLocked = instMan.lock(); size_t instOfs = ctr.readUint32(globCsr); size_t instCsr = globCsr + 4; uint8_t instCnt = ctr.readUint8(instCsr); instCsr += 1; for (uint8_t i = 0; i < instCnt; ++i) { uint8_t idx = ctr.readUint8(instCsr); instCsr += 1; size_t iOfs = ctr.readUint32(instCsr); size_t iCsr = instCsr + 4; size_t nameLen = ctr.readUint32(iCsr); iCsr += 4; std::string name = u8""; if (nameLen > 0) { name = ctr.readString(iCsr, nameLen); iCsr += nameLen; } switch (ctr.readUint8(iCsr++)) { case 0x00: // FM { auto instFM = new InstrumentFM(idx, name, instManLocked.get()); instFM->setEnvelopeNumber(ctr.readUint8(iCsr)); iCsr += 1; uint8_t tmp = ctr.readUint8(iCsr); instFM->setLFOEnabled((0x80 & tmp) ? false : true); instFM->setLFONumber(0x7f & tmp); iCsr += 1; for (auto& param : FM_OPSEQ_PARAMS) { tmp = ctr.readUint8(iCsr); instFM->setOperatorSequenceEnabled(param, (0x80 & tmp) ? false : true); instFM->setOperatorSequenceNumber(param, 0x7f & tmp); iCsr += 1; } tmp = ctr.readUint8(iCsr); instFM->setArpeggioEnabled(FMOperatorType::All, (0x80 & tmp) ? false : true); instFM->setArpeggioNumber(FMOperatorType::All, 0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instFM->setPitchEnabled(FMOperatorType::All, (0x80 & tmp) ? false : true); instFM->setPitchNumber(FMOperatorType::All, 0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instFM->setEnvelopeResetEnabled(FMOperatorType::All, (tmp & 0x01)); instFM->setEnvelopeResetEnabled(FMOperatorType::Op1, (tmp & 0x02)); instFM->setEnvelopeResetEnabled(FMOperatorType::Op2, (tmp & 0x04)); instFM->setEnvelopeResetEnabled(FMOperatorType::Op3, (tmp & 0x08)); instFM->setEnvelopeResetEnabled(FMOperatorType::Op4, (tmp & 0x10)); iCsr += 1; if (version >= Version::toBCD(1, 1, 0)) { for (auto& t : FM_OP_TYPES) { tmp = ctr.readUint8(iCsr); instFM->setArpeggioEnabled(t, (0x80 & tmp) ? false : true); instFM->setArpeggioNumber(t, 0x7f & tmp); iCsr += 1; } for (auto& t : FM_OP_TYPES) { tmp = ctr.readUint8(iCsr); instFM->setPitchEnabled(t, (0x80 & tmp) ? false : true); instFM->setPitchNumber(t, 0x7f & tmp); iCsr += 1; } } instManLocked->addInstrument(instFM); break; } case 0x01: // SSG { auto instSSG = new InstrumentSSG(idx, name, instManLocked.get()); uint8_t tmp = ctr.readUint8(iCsr); instSSG->setWaveformEnabled((0x80 & tmp) ? false : true); instSSG->setWaveformNumber(0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instSSG->setToneNoiseEnabled((0x80 & tmp) ? false : true); instSSG->setToneNoiseNumber(0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instSSG->setEnvelopeEnabled((0x80 & tmp) ? false : true); instSSG->setEnvelopeNumber(0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instSSG->setArpeggioEnabled((0x80 & tmp) ? false : true); instSSG->setArpeggioNumber(0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instSSG->setPitchEnabled((0x80 & tmp) ? false : true); instSSG->setPitchNumber(0x7f & tmp); /* iCsr += 1; */ instManLocked->addInstrument(instSSG); break; } case 0x02: // ADPCM { auto instADPCM = new InstrumentADPCM(idx, name, instManLocked.get()); instADPCM->setSampleNumber(ctr.readUint8(iCsr++)); uint8_t tmp = ctr.readUint8(iCsr); instADPCM->setEnvelopeEnabled((0x80 & tmp) ? false : true); instADPCM->setEnvelopeNumber(0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instADPCM->setArpeggioEnabled((0x80 & tmp) ? false : true); instADPCM->setArpeggioNumber(0x7f & tmp); iCsr += 1; tmp = ctr.readUint8(iCsr); instADPCM->setPitchEnabled((0x80 & tmp) ? false : true); instADPCM->setPitchNumber(0x7f & tmp); /* iCsr += 1; */ instManLocked->addInstrument(instADPCM); break; } case 0x03: // Drumkit { auto instKit = new InstrumentDrumkit(idx, name, instManLocked.get()); int cnt = ctr.readUint8(iCsr++); for (int i = 0; i < cnt; ++i) { int key = ctr.readUint8(iCsr++); instKit->setSampleEnabled(key, true); instKit->setSampleNumber(key, ctr.readUint8(iCsr++)); instKit->setPitch(key, ctr.readInt8(iCsr++)); } instManLocked->addInstrument(instKit); break; } default: throw FileCorruptionError(FileType::Mod, iCsr); } instCsr += iOfs; } return globCsr + instOfs; } size_t loadOperatorSequence(FMEnvelopeParameter param, size_t instMemCsr, std::shared_ptr& instManLocked, const BinaryContainer& ctr, uint32_t version) { uint8_t idx = ctr.readUint8(instMemCsr++); uint16_t ofs = ctr.readUint16(instMemCsr); size_t csr = instMemCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 2)) csr += 2; if (l == 0) instManLocked->setOperatorSequenceFMSequenceData(param, idx, 0, data); else instManLocked->addOperatorSequenceFMSequenceData(param, idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addOperatorSequenceFMLoop(param, idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug; see rerrahkr/BambooTracker issue #11) if (pos < seqLen) instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setOperatorSequenceFMRelease(param, idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } return ofs + 1; } size_t loadInstrumentPropertySection(std::weak_ptr instMan, const BinaryContainer& ctr, size_t globCsr, uint32_t version) { std::shared_ptr instManLocked = instMan.lock(); size_t instPropOfs = ctr.readUint32(globCsr); size_t instPropCsr = globCsr + 4; globCsr += instPropOfs; while (instPropCsr < globCsr) { switch (ctr.readUint8(instPropCsr++)) { case 0x00: // FM envelope { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint8_t ofs = ctr.readUint8(instPropCsr); size_t csr = instPropCsr + 1; uint8_t tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, FMEnvelopeParameter::AL, tmp >> 4); instManLocked->setEnvelopeFMParameter(idx, FMEnvelopeParameter::FB, tmp & 0x0f); for (int op = 0; op < 4; ++op) { auto& params = FM_OP_PARAMS[op]; tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMOperatorEnabled(idx, op, (0x20 & tmp) ? true : false); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::AR), tmp & 0x1f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::KS), tmp >> 5); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DR), tmp & 0x1f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DT), tmp >> 5); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SR), tmp & 0x1f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SL), tmp >> 4); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::RR), tmp & 0x0f); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::TL), tmp); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::ML), tmp & 0x0f); instManLocked->setEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SSGEG), (tmp & 0x80) ? -1 : ((tmp >> 4) & 0x07)); } instPropCsr += ofs; } break; } case 0x01: // FM LFO { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint8_t ofs = ctr.readUint8(instPropCsr); size_t csr = instPropCsr + 1; uint8_t tmp = ctr.readUint8(csr++); instManLocked->setLFOFMParameter(idx, FMLFOParameter::FREQ, tmp >> 4); instManLocked->setLFOFMParameter(idx, FMLFOParameter::PMS, tmp & 0x0f); tmp = ctr.readUint8(csr++); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AMS, tmp & 0x0f); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM1, (tmp & 0x10) ? true : false); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM2, (tmp & 0x20) ? true : false); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM3, (tmp & 0x40) ? true : false); instManLocked->setLFOFMParameter(idx, FMLFOParameter::AM4, (tmp & 0x80) ? true : false); tmp = ctr.readUint8(csr++); instManLocked->setLFOFMParameter(idx, FMLFOParameter::Count, tmp); instPropCsr += ofs; } break; } case 0x02: // FM AL { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AL, instPropCsr, instManLocked, ctr, version); break; } case 0x03: // FM FB { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::FB, instPropCsr, instManLocked, ctr, version); break; } case 0x04: // FM AR1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR1, instPropCsr, instManLocked, ctr, version); break; } case 0x05: // FM DR1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR1, instPropCsr, instManLocked, ctr, version); break; } case 0x06: // FM SR1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR1, instPropCsr, instManLocked, ctr, version); break; } case 0x07: // FM RR1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR1, instPropCsr, instManLocked, ctr, version); break; } case 0x08: // FM SL1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL1, instPropCsr, instManLocked, ctr, version); break; } case 0x09: // FM TL1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL1, instPropCsr, instManLocked, ctr, version); break; } case 0x0a: // FM KS1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS1, instPropCsr, instManLocked, ctr, version); break; } case 0x0b: // FM ML1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML1, instPropCsr, instManLocked, ctr, version); break; } case 0x0c: // FM DT1 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT1, instPropCsr, instManLocked, ctr, version); break; } case 0x0d: // FM AR2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR2, instPropCsr, instManLocked, ctr, version); break; } case 0x0e: // FM DR2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR2, instPropCsr, instManLocked, ctr, version); break; } case 0x0f: // FM SR2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR2, instPropCsr, instManLocked, ctr, version); break; } case 0x10: // FM RR2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR2, instPropCsr, instManLocked, ctr, version); break; } case 0x11: // FM SL2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL2, instPropCsr, instManLocked, ctr, version); break; } case 0x12: // FM TL2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL2, instPropCsr, instManLocked, ctr, version); break; } case 0x13: // FM KS2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS2, instPropCsr, instManLocked, ctr, version); break; } case 0x14: // FM ML2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML2, instPropCsr, instManLocked, ctr, version); break; } case 0x15: // FM DT2 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT2, instPropCsr, instManLocked, ctr, version); break; } case 0x16: // FM AR3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR3, instPropCsr, instManLocked, ctr, version); break; } case 0x17: // FM DR3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR3, instPropCsr, instManLocked, ctr, version); break; } case 0x18: // FM SR3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR3, instPropCsr, instManLocked, ctr, version); break; } case 0x19: // FM RR3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR3, instPropCsr, instManLocked, ctr, version); break; } case 0x1a: // FM SL3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL3, instPropCsr, instManLocked, ctr, version); break; } case 0x1b: // FM TL3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL3, instPropCsr, instManLocked, ctr, version); break; } case 0x1c: // FM KS3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS3, instPropCsr, instManLocked, ctr, version); break; } case 0x1d: // FM ML3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML3, instPropCsr, instManLocked, ctr, version); break; } case 0x1e: // FM DT3 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT3, instPropCsr, instManLocked, ctr, version); break; } case 0x1f: // FM AR4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::AR4, instPropCsr, instManLocked, ctr, version); break; } case 0x20: // FM DR4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DR4, instPropCsr, instManLocked, ctr, version); break; } case 0x21: // FM SR4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SR4, instPropCsr, instManLocked, ctr, version); break; } case 0x22: // FM RR4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::RR4, instPropCsr, instManLocked, ctr, version); break; } case 0x23: // FM SL4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::SL4, instPropCsr, instManLocked, ctr, version); break; } case 0x24: // FM TL4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::TL4, instPropCsr, instManLocked, ctr, version); break; } case 0x25: // FM KS4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::KS4, instPropCsr, instManLocked, ctr, version); break; } case 0x26: // FM ML4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::ML4, instPropCsr, instManLocked, ctr, version); break; } case 0x27: // FM DT4 { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) instPropCsr += loadOperatorSequence( FMEnvelopeParameter::DT4, instPropCsr, instManLocked, ctr, version); break; } case 0x28: // FM arpeggio { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setArpeggioFMSequenceData(idx, 0, data); else instManLocked->addArpeggioFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addArpeggioFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioFMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioFMType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setArpeggioFMType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Mod, csr); } } } instPropCsr += ofs; } break; } case 0x29: // FM pitch { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setPitchFMSequenceData(idx, 0, data); else instManLocked->addPitchFMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addPitchFMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchFMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchFMType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setPitchFMType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Mod, csr); } } } instPropCsr += ofs; } break; } case 0x30: // SSG waveform { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 0)) { if (data == 3) data = SSGWaveformType::SQM_TRIANGLE; else if (data == 4) data = SSGWaveformType::SQM_SAW; } int32_t subdata; if (version >= Version::toBCD(1, 2, 0)) { subdata = ctr.readInt32(csr); csr += 4; } else { subdata = ctr.readUint16(csr); csr += 2; if (subdata != -1) subdata = note_utils::calculateSSGSquareTP(subdata, 0); } SSGWaveformUnit unit; switch (data) { case SSGWaveformType::SQM_TRIANGLE: case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: unit = SSGWaveformUnit::makeUnitWithDecode(data, subdata); break; default: unit = SSGWaveformUnit::makeOnlyDataUnit(data); break; } if (l == 0) instManLocked->setWaveformSSGSequenceData(idx, 0, unit); else instManLocked->addWaveformSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addWaveformSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setWaveformSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } instPropCsr += ofs; } break; } case 0x31: // SSG tone/noise { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 3, 1)) { if (data > 0) { uint16_t tmp = data - 1; data = tmp / 32 * 32 + (31 - tmp % 32) + 1; } } if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setToneNoiseSSGSequenceData(idx, 0, data); else instManLocked->addToneNoiseSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addToneNoiseSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setToneNoiseSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } instPropCsr += ofs; } break; } case 0x32: // SSG envelope { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; int32_t subdata; if (version >= Version::toBCD(1, 2, 0)) { subdata = ctr.readInt32(csr); csr += 4; } else { subdata = ctr.readUint16(csr); csr += 2; } SSGEnvelopeUnit unit = (data < 16) ? SSGEnvelopeUnit::makeOnlyDataUnit(data) : SSGEnvelopeUnit::makeUnitWithDecode(data, subdata); if (l == 0) instManLocked->setEnvelopeSSGSequenceData(idx, 0, unit); else instManLocked->addEnvelopeSSGSequenceData(idx, unit); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addEnvelopeSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); else instManLocked->setEnvelopeSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { ++csr; // Skip sequence type } instPropCsr += ofs; } break; } case 0x33: // SSG arpeggio { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setArpeggioSSGSequenceData(idx, 0, data); else instManLocked->addArpeggioSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addArpeggioSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioSSGType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioSSGType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setArpeggioSSGType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Mod, csr); } } } instPropCsr += ofs; } break; } case 0x34: // SSG pitch { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (version < Version::toBCD(1, 2, 0)) csr += 2; if (l == 0) instManLocked->setPitchSSGSequenceData(idx, 0, data); else instManLocked->addPitchSSGSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addPitchSSGLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchSSGRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } if (version >= Version::toBCD(1, 0, 1)) { switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchSSGType(idx, SequenceType::RelativeSequence); break; default: if (version < Version::toBCD(1, 3, 2)) { // Recover deep clone bug // https://github.com/rerrahkr/BambooTracker/issues/170 instManLocked->setPitchSSGType(idx, SequenceType::AbsoluteSequence); break; } else { throw FileCorruptionError(FileType::Mod, csr); } } } instPropCsr += ofs; } break; } case 0x40: // ADPCM sample { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint32_t ofs = ctr.readUint32(instPropCsr); size_t csr = instPropCsr + 4; instManLocked->setSampleADPCMRootKeyNumber(idx, ctr.readUint8(csr++)); instManLocked->setSampleADPCMRootDeltaN(idx, ctr.readUint16(csr)); csr += 2; instManLocked->setSampleADPCMRepeatEnabled(idx, (ctr.readUint8(csr++) & 0x01) != 0); uint32_t len = ctr.readUint32(csr); csr += 4; std::vector samples = ctr.getSubcontainer(csr, len).toVector(); /* csr += len; */ instManLocked->storeSampleADPCMRawSample(idx, std::move(samples)); instPropCsr += ofs; } break; } case 0x41: // ADPCM envelope { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 6; // Skip subdata if (l == 0) instManLocked->setEnvelopeADPCMSequenceData(idx, 0, data); else instManLocked->addEnvelopeADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addEnvelopeADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x02: // Absolute { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::AbsoluteRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } case 0x03: // Relative { uint16_t pos = ctr.readUint16(csr); csr += 2; if (pos < seqLen) instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::RelativeRelease, pos)); else instManLocked->setEnvelopeADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } ++csr; // Skip sequence type instPropCsr += ofs; } break; } case 0x42: // ADPCM arpeggio { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (l == 0) instManLocked->setArpeggioADPCMSequenceData(idx, 0, data); else instManLocked->addArpeggioADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addArpeggioADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setArpeggioADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setArpeggioADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x01: // Fixed instManLocked->setArpeggioADPCMType(idx, SequenceType::FixedSequence); break; case 0x02: // Relative instManLocked->setArpeggioADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Mod, csr); } instPropCsr += ofs; } break; } case 0x43: // ADPCM pitch { uint8_t cnt = ctr.readUint8(instPropCsr++); for (size_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(instPropCsr++); uint16_t ofs = ctr.readUint16(instPropCsr); size_t csr = instPropCsr + 2; uint16_t seqLen = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < seqLen; ++l) { uint16_t data = ctr.readUint16(csr); csr += 2; if (l == 0) instManLocked->setPitchADPCMSequenceData(idx, 0, data); else instManLocked->addPitchADPCMSequenceData(idx, data); } uint16_t loopCnt = ctr.readUint16(csr); csr += 2; for (uint16_t l = 0; l < loopCnt; ++l) { int begin = ctr.readUint16(csr); csr += 2; int end = ctr.readUint16(csr); csr += 2; int times = ctr.readUint8(csr++); instManLocked->addPitchADPCMLoop(idx, InstrumentSequenceLoop(begin, end, times)); } switch (ctr.readUint8(csr++)) { case 0x00: // No release instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; case 0x01: // Fixed { uint16_t pos = ctr.readUint16(csr); csr += 2; // Release point check (prevents a bug) // https://github.com/rerrahkr/BambooTracker/issues/11 if (pos < seqLen) instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::FixedRelease, pos)); else instManLocked->setPitchADPCMRelease(idx, InstrumentSequenceRelease(InstrumentSequenceRelease::NoRelease)); break; } default: throw FileCorruptionError(FileType::Mod, csr); } switch (ctr.readUint8(csr++)) { case 0x00: // Absolute instManLocked->setPitchADPCMType(idx, SequenceType::AbsoluteSequence); break; case 0x02: // Relative instManLocked->setPitchADPCMType(idx, SequenceType::RelativeSequence); break; default: throw FileCorruptionError(FileType::Mod, csr); } instPropCsr += ofs; } break; } default: throw FileCorruptionError(FileType::Mod, instPropCsr); } } return globCsr; } size_t loadGrooveSection(std::weak_ptr mod, const BinaryContainer& ctr, size_t globCsr, uint32_t version) { (void)version; std::shared_ptr modLocked = mod.lock(); size_t grvOfs = ctr.readUint32(globCsr); size_t grvCsr = globCsr + 4; uint8_t cnt = ctr.readUint8(grvCsr++) + 1; for (uint8_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(grvCsr++); uint8_t seqLen = ctr.readUint8(grvCsr++); std::vector seq; for (uint8_t l = 0; l < seqLen; ++l) { seq.push_back(ctr.readUint8(grvCsr++)); } if (idx > 0) modLocked->addGroove(); modLocked->setGroove(idx, seq); } return globCsr + grvOfs; } size_t loadSongSection(std::weak_ptr mod, const BinaryContainer& ctr, size_t globCsr, uint32_t version) { std::shared_ptr modLocked = mod.lock(); size_t songOfs = ctr.readUint32(globCsr); size_t songCsr = globCsr + 4; uint8_t cnt = ctr.readUint8(songCsr++); for (uint8_t i = 0; i < cnt; ++i) { uint8_t idx = ctr.readUint8(songCsr++); size_t sOfs = ctr.readUint32(songCsr); size_t scsr = songCsr + 4; songCsr += sOfs; size_t titleLen = ctr.readUint32(scsr); scsr += 4; std::string title = u8""; if (titleLen > 0) title = ctr.readString(scsr, titleLen); scsr += titleLen; uint32_t tempo = ctr.readUint32(scsr); scsr += 4; uint8_t groove = ctr.readUint8(scsr); scsr += 1; bool isTempo = (groove & 0x80) ? true : false; groove &= 0x7f; uint32_t speed = ctr.readUint32(scsr); scsr += 4; size_t ptnSize = ctr.readUint8(scsr) + 1; scsr += 1; SongType songType; switch (ctr.readUint8(scsr++)) { case 0x00: songType = SongType::Standard; break; case 0x01: songType = SongType::FM3chExpanded; break; default: throw FileCorruptionError(FileType::Mod, scsr); } modLocked->addSong(idx, songType, title, isTempo, static_cast(tempo), groove, static_cast(speed), ptnSize); auto& song = modLocked->getSong(idx); // Bookmark if (Version::toBCD(1, 4, 1) <= version) { int bmSize = ctr.readUint8(scsr++); for (int i = 0; i < bmSize; ++i) { size_t len = ctr.readUint32(scsr); scsr += 4; std::string name = ctr.readString(scsr, len); scsr += len; int order = ctr.readUint8(scsr++); int step = ctr.readUint8(scsr++); song.addBookmark(name, order, step); } } while (scsr < songCsr) { // Track uint8_t trackIdx = ctr.readUint8(scsr++); auto& track = song.getTrack(trackIdx); size_t trackOfs = ctr.readUint32(scsr); size_t trackEnd = scsr + trackOfs; size_t tcsr = scsr + 4; uint8_t odrLen = ctr.readUint8(tcsr++) + 1; for (uint8_t oi = 0; oi < odrLen; ++oi) { if (!oi) track.registerPatternToOrder(oi, ctr.readUint8(tcsr++)); else { track.insertOrderBelow(oi - 1); track.registerPatternToOrder(oi, ctr.readUint8(tcsr++)); } } if (version >= Version::toBCD(1, 2, 1)) { track.setEffectDisplayWidth(ctr.readUint8(tcsr++)); } SoundSource sndsrc = track.getAttribute().source; // Pattern while (tcsr < trackEnd) { uint8_t ptnIdx = ctr.readUint8(tcsr++); auto& pattern = track.getPattern(ptnIdx); size_t ptnOfs = ctr.readUint32(tcsr); size_t pcsr = tcsr + 4; tcsr += ptnOfs; // Step while (pcsr < tcsr) { uint32_t stepIdx = ctr.readUint8(pcsr++); auto& step = pattern.getStep(static_cast(stepIdx)); uint16_t eventFlag = ctr.readUint16(pcsr); pcsr += 2; if (eventFlag & 0x0001) { if (version >= Version::toBCD(1, 0, 2)) { step.setNoteNumber(ctr.readInt8(pcsr++)); } else { // Change FM octave (song type is only 0x00 before v1.0.2) int8_t nn = ctr.readInt8(pcsr++); if (trackIdx < 6 && 0 <= nn && nn < 84) step.setNoteNumber(nn + 12); else step.setNoteNumber(nn); } } if (eventFlag & 0x0002) step.setInstrumentNumber(ctr.readUint8(pcsr++)); if (eventFlag & 0x0004) step.setVolume(ctr.readUint8(pcsr++)); EffectType efftype = EffectType::NoEffect; if (eventFlag & 0x0008) { std::string id = ctr.readString(pcsr, 2); step.setEffectId(0, id); efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0010) { int v = ctr.readUint8(pcsr++); if (version < Version::toBCD(1, 3, 1) && efftype == EffectType::NoisePitch && v < 32) v = 31 - v; step.setEffectValue(0, v); } efftype = EffectType::NoEffect; if (eventFlag & 0x0020) { std::string id = ctr.readString(pcsr, 2); step.setEffectId(1, id); efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0040) { int v = ctr.readUint8(pcsr++); if (version < Version::toBCD(1, 3, 1) && efftype == EffectType::NoisePitch && v < 32) v = 31 - v; step.setEffectValue(1, v); } efftype = EffectType::NoEffect; if (eventFlag & 0x0080) { std::string id = ctr.readString(pcsr, 2); step.setEffectId(2, id); efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0100) { int v = ctr.readUint8(pcsr++); if (version < Version::toBCD(1, 3, 1) && efftype == EffectType::NoisePitch && v < 32) v = 31 - v; step.setEffectValue(2, v); } efftype = EffectType::NoEffect; if (eventFlag & 0x0200) { std::string id = ctr.readString(pcsr, 2); step.setEffectId(3, id); efftype = effect_utils::validateEffectId(sndsrc, id); pcsr += 2; } if (eventFlag & 0x0400) { int v = ctr.readUint8(pcsr++); if (version < Version::toBCD(1, 3, 1) && efftype == EffectType::NoisePitch && v < 32) v = 31 - v; step.setEffectValue(3, v); } } } scsr += trackOfs; } if (version < Version::toBCD(1, 4, 0)) { // ADPCM track int odrLen = static_cast(song.getOrderSize()); int trackNum; switch (songType) { case SongType::Standard: trackNum = 15; break; case SongType::FM3chExpanded: trackNum = 18; break; } auto& track = song.getTrack(trackNum); for (int oi = 0; oi < odrLen; ++oi) { if (oi) track.insertOrderBelow(oi - 1); track.registerPatternToOrder(oi, 0); } track.setEffectDisplayWidth(0); } } return globCsr + songOfs; } } BtmIO::BtmIO() : AbstractModuleIO("btm", "BambooTracker module", true, true) {} void BtmIO::load(const BinaryContainer& ctr, std::weak_ptr mod, std::weak_ptr instMan) const { size_t globCsr = 0; if (ctr.readString(globCsr, 16) != "BambooTrackerMod") throw FileCorruptionError(FileType::Mod, globCsr); globCsr += 16; size_t eofOfs = ctr.readUint32(globCsr); size_t eof = globCsr + eofOfs; globCsr += 4; size_t fileVersion = ctr.readUint32(globCsr); if (fileVersion > Version::ofModuleFileInBCD()) throw FileVersionError(FileType::Mod); globCsr += 4; while (globCsr < eof) { if (ctr.readString(globCsr, 8) == "MODULE ") globCsr = loadModuleSection(mod, ctr, globCsr + 8, fileVersion); else if (ctr.readString(globCsr, 8) == "INSTRMNT") globCsr = loadInstrumentSection(instMan, ctr, globCsr + 8, fileVersion); else if (ctr.readString(globCsr, 8) == "INSTPROP") globCsr = loadInstrumentPropertySection(instMan, ctr, globCsr + 8, fileVersion); else if (ctr.readString(globCsr, 8) == "GROOVE ") globCsr = loadGrooveSection(mod, ctr, globCsr + 8, fileVersion); else if (ctr.readString(globCsr, 8) == "SONG ") globCsr = loadSongSection(mod, ctr, globCsr + 8, fileVersion); else throw FileCorruptionError(FileType::Mod, globCsr); } } void BtmIO::save(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan) const { std::shared_ptr instManLocked = instMan.lock(); ctr.appendString("BambooTrackerMod"); size_t eofOfs = ctr.size(); ctr.appendUint32(0); // Dummy EOF offset uint32_t fileVersion = Version::ofModuleFileInBCD(); ctr.appendUint32(fileVersion); /***** Module section *****/ ctr.appendString("MODULE "); size_t modOfs = ctr.size(); ctr.appendUint32(0); // Dummy module section offset std::string modTitle = mod.lock()->getTitle(); ctr.appendUint32(modTitle.length()); if (!modTitle.empty()) ctr.appendString(modTitle); std::string author = mod.lock()->getAuthor(); ctr.appendUint32(author.length()); if (!author.empty()) ctr.appendString(author); std::string copyright = mod.lock()->getCopyright(); ctr.appendUint32(copyright.length()); if (!copyright.empty()) ctr.appendString(copyright); std::string comment = mod.lock()->getComment(); ctr.appendUint32(comment.length()); if (!comment.empty()) ctr.appendString(comment); ctr.appendUint32(mod.lock()->getTickFrequency()); ctr.appendUint32(mod.lock()->getStepHighlight1Distance()); ctr.appendUint32(mod.lock()->getStepHighlight2Distance()); MixerType mixType = mod.lock()->getMixerType(); ctr.appendUint8(static_cast(mixType)); if (mixType == MixerType::CUSTOM) { ctr.appendInt8(static_cast(mod.lock()->getCustomMixerFMLevel() * 10)); ctr.appendInt8(static_cast(mod.lock()->getCustomMixerSSGLevel() * 10)); } ctr.writeUint32(modOfs, ctr.size() - modOfs); /***** Instrument section *****/ ctr.appendString("INSTRMNT"); size_t instOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument section offset std::vector instIdcs = instManLocked->getInstrumentIndices(); ctr.appendUint8(static_cast(instIdcs.size())); for (auto& idx : instIdcs) { if (std::shared_ptr inst = instManLocked->getInstrumentSharedPtr(idx)) { ctr.appendUint8(static_cast(inst->getNumber())); size_t iOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument block offset std::string name = inst->getName(); ctr.appendUint32(name.length()); if (!name.empty()) ctr.appendString(name); switch (inst->getType()) { case InstrumentType::FM: { ctr.appendUint8(0x00); auto instFM = std::dynamic_pointer_cast(inst); ctr.appendUint8(static_cast(instFM->getEnvelopeNumber())); uint8_t tmp = static_cast(instFM->getLFONumber()); ctr.appendUint8(instFM->getLFOEnabled() ? tmp : (0x80 | tmp)); for (auto& param : FM_OPSEQ_PARAMS) { tmp = static_cast(instFM->getOperatorSequenceNumber(param)); ctr.appendUint8(instFM->getOperatorSequenceEnabled(param) ? tmp : (0x80 | tmp)); } tmp = static_cast(instFM->getArpeggioNumber(FMOperatorType::All)); ctr.appendUint8(instFM->getArpeggioEnabled(FMOperatorType::All) ? tmp : (0x80 | tmp)); tmp = static_cast(instFM->getPitchNumber(FMOperatorType::All)); ctr.appendUint8(instFM->getPitchEnabled(FMOperatorType::All) ? tmp : (0x80 | tmp)); tmp = static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::All)) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op1) << 1) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op2) << 2) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op3) << 3) | static_cast(instFM->getEnvelopeResetEnabled(FMOperatorType::Op4) << 4); ctr.appendUint8(tmp); for (auto& type : FM_OP_TYPES) { tmp = static_cast(instFM->getArpeggioNumber(type)); ctr.appendUint8(instFM->getArpeggioEnabled(type) ? tmp : (0x80 | tmp)); } for (auto& type : FM_OP_TYPES) { tmp = static_cast(instFM->getPitchNumber(type)); ctr.appendUint8(instFM->getPitchEnabled(type) ? tmp : (0x80 | tmp)); } break; } case InstrumentType::SSG: { ctr.appendUint8(0x01); auto instSSG = std::dynamic_pointer_cast(inst); uint8_t tmp = static_cast(instSSG->getWaveformNumber()); ctr.appendUint8(instSSG->getWaveformEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getToneNoiseNumber()); ctr.appendUint8(instSSG->getToneNoiseEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getEnvelopeNumber()); ctr.appendUint8(instSSG->getEnvelopeEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getArpeggioNumber()); ctr.appendUint8(instSSG->getArpeggioEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instSSG->getPitchNumber()); ctr.appendUint8(instSSG->getPitchEnabled() ? tmp : (0x80 | tmp)); break; } case InstrumentType::ADPCM: { ctr.appendUint8(0x02); auto instADPCM = std::dynamic_pointer_cast(inst); ctr.appendUint8(static_cast(instADPCM->getSampleNumber())); uint8_t tmp = static_cast(instADPCM->getEnvelopeNumber()); ctr.appendUint8(instADPCM->getEnvelopeEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instADPCM->getArpeggioNumber()); ctr.appendUint8(instADPCM->getArpeggioEnabled() ? tmp : (0x80 | tmp)); tmp = static_cast(instADPCM->getPitchNumber()); ctr.appendUint8(instADPCM->getPitchEnabled() ? tmp : (0x80 | tmp)); break; } case InstrumentType::Drumkit: { ctr.appendUint8(0x03); auto instKit = std::dynamic_pointer_cast(inst); std::vector keys = instKit->getAssignedKeys(); ctr.appendUint8(keys.size()); for (const int& key : keys) { ctr.appendUint8(static_cast(key)); ctr.appendUint8(static_cast(instKit->getSampleNumber(key))); ctr.appendInt8(instKit->getPitch(key)); } break; } } ctr.writeUint32(iOfs, ctr.size() - iOfs); } } ctr.writeUint32(instOfs, ctr.size() - instOfs); /***** Instrument property section *****/ ctr.appendString("INSTPROP"); size_t instPropOfs = ctr.size(); ctr.appendUint32(0); // Dummy instrument property section offset // FM envelope std::vector envFMIdcs = instManLocked->getEnvelopeFMEntriedIndices(); if (!envFMIdcs.empty()) { ctr.appendUint8(0x00); ctr.appendUint8(static_cast(envFMIdcs.size())); for (auto& idx : envFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint8(0); // Dummy offset uint8_t tmp = static_cast(instManLocked->getEnvelopeFMParameter(idx, FMEnvelopeParameter::AL) << 4) | static_cast(instManLocked->getEnvelopeFMParameter(idx, FMEnvelopeParameter::FB)); ctr.appendUint8(tmp); for (int op = 0; op < 4; ++op) { auto& params = FM_OP_PARAMS[op]; tmp = instManLocked->getEnvelopeFMOperatorEnabled(idx, op); tmp = static_cast((tmp << 5)) | static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::AR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::KS)) << 5) | static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::DT)) << 5) | static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SL)) << 4) | static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::RR))); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::TL))); ctr.appendUint8(tmp); int tmp2 = instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::SSGEG)); tmp = ((tmp2 == -1) ? 0x80 : static_cast(tmp2 << 4)) | static_cast(instManLocked->getEnvelopeFMParameter(idx, params.at(FMOperatorParameter::ML))); ctr.appendUint8(tmp); } ctr.writeUint8(ofs, static_cast(ctr.size() - ofs)); } } // FM LFO std::vector lfoFMIdcs = instManLocked->getLFOFMEntriedIndices(); if (!lfoFMIdcs.empty()) { ctr.appendUint8(0x01); ctr.appendUint8(static_cast(lfoFMIdcs.size())); for (auto& idx : lfoFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint8(0); // Dummy offset uint8_t tmp = static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::FREQ) << 4) | static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::PMS)); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::AM4) << 7) | static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::AM3) << 6) | static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::AM2) << 5) | static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::AM1) << 4) | static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::AMS)); ctr.appendUint8(tmp); tmp = static_cast(instManLocked->getLFOFMparameter(idx, FMLFOParameter::Count)); ctr.appendUint8(tmp); ctr.writeUint8(ofs, static_cast(ctr.size() - ofs)); } } // FM envelope parameter for (size_t i = 0; i < 38; ++i) { std::vector idcs = instManLocked->getOperatorSequenceFMEntriedIndices(FM_OPSEQ_PARAMS[i]); if (!idcs.empty()) { ctr.appendUint8(0x02 + static_cast(i)); ctr.appendUint8(static_cast(idcs.size())); for (auto& idx : idcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getOperatorSequenceFMSequence(FM_OPSEQ_PARAMS[i], idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getOperatorSequenceFMLoopRoot(FM_OPSEQ_PARAMS[i], idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getOperatorSequenceFMRelease(FM_OPSEQ_PARAMS[i], idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } } // FM arpeggio std::vector arpFMIdcs = instManLocked->getArpeggioFMEntriedIndices(); if (!arpFMIdcs.empty()) { ctr.appendUint8(0x28); ctr.appendUint8(static_cast(arpFMIdcs.size())); for (auto& idx : arpFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getArpeggioFMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioFMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioFMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // FM pitch std::vector ptFMIdcs = instManLocked->getPitchFMEntriedIndices(); if (!ptFMIdcs.empty()) { ctr.appendUint8(0x29); ctr.appendUint8(static_cast(ptFMIdcs.size())); for (auto& idx : ptFMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchFMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getPitchFMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchFMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchFMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG waveform std::vector wfSSGIdcs = instManLocked->getWaveformSSGEntriedIndices(); if (!wfSSGIdcs.empty()) { ctr.appendUint8(0x30); ctr.appendUint8(static_cast(wfSSGIdcs.size())); for (auto& idx : wfSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getWaveformSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(static_cast(unit.subdata)); } auto loops = instManLocked->getWaveformSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getWaveformSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG tone/noise std::vector tnSSGIdcs = instManLocked->getToneNoiseSSGEntriedIndices(); if (!tnSSGIdcs.empty()) { ctr.appendUint8(0x31); ctr.appendUint8(static_cast(tnSSGIdcs.size())); for (auto& idx : tnSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getToneNoiseSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getToneNoiseSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getToneNoiseSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG envelope std::vector envSSGIdcs = instManLocked->getEnvelopeSSGEntriedIndices(); if (!envSSGIdcs.empty()) { ctr.appendUint8(0x32); ctr.appendUint8(static_cast(envSSGIdcs.size())); for (auto& idx : envSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(static_cast(unit.subdata)); } auto loops = instManLocked->getEnvelopeSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG arpeggio std::vector arpSSGIdcs = instManLocked->getArpeggioSSGEntriedIndices(); if (!arpSSGIdcs.empty()) { ctr.appendUint8(0x33); ctr.appendUint8(static_cast(arpSSGIdcs.size())); for (auto& idx : arpSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getArpeggioSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioSSGType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // SSG pitch std::vector ptSSGIdcs = instManLocked->getPitchSSGEntriedIndices(); if (!ptSSGIdcs.empty()) { ctr.appendUint8(0x34); ctr.appendUint8(static_cast(ptSSGIdcs.size())); for (auto& idx : ptSSGIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchSSGSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getPitchSSGLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchSSGRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchSSGType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // ADPCM sample std::vector sampADPCMIdcs = instManLocked->getSampleADPCMEntriedIndices(); if (!sampADPCMIdcs.empty()) { ctr.appendUint8(0x40); ctr.appendUint8(static_cast(sampADPCMIdcs.size())); for (auto& idx : sampADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint32(0); // Dummy offset ctr.appendUint8(static_cast(instManLocked->getSampleADPCMRootKeyNumber(idx))); ctr.appendUint16(static_cast(instManLocked->getSampleADPCMRootDeltaN(idx))); ctr.appendUint8(static_cast(instManLocked->isSampleADPCMRepeatable(idx))); std::vector samples = instManLocked->getSampleADPCMRawSample(idx); ctr.appendUint32(samples.size()); ctr.appendVector(std::move(samples)); ctr.writeUint32(ofs, ctr.size() - ofs); } } // ADPCM envelope std::vector envADPCMIdcs = instManLocked->getEnvelopeADPCMEntriedIndices(); if (!envADPCMIdcs.empty()) { ctr.appendUint8(0x41); ctr.appendUint8(static_cast(envADPCMIdcs.size())); for (auto& idx : envADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getEnvelopeADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); ctr.appendInt32(0); // Dummy set for past format } auto loops = instManLocked->getEnvelopeADPCMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getEnvelopeADPCMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } ctr.appendUint8(0); // Skip sequence type ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // ADPCM arpeggio std::vector arpADPCMIdcs = instManLocked->getArpeggioADPCMEntriedIndices(); if (!arpADPCMIdcs.empty()) { ctr.appendUint8(0x42); ctr.appendUint8(static_cast(arpADPCMIdcs.size())); for (auto& idx : arpADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getArpeggioADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getArpeggioADPCMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getArpeggioADPCMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getArpeggioADPCMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::FixedSequence: ctr.appendUint8(0x01); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } // ADPCM pitch std::vector ptADPCMIdcs = instManLocked->getPitchADPCMEntriedIndices(); if (!ptADPCMIdcs.empty()) { ctr.appendUint8(0x43); ctr.appendUint8(static_cast(ptADPCMIdcs.size())); for (auto& idx : ptADPCMIdcs) { ctr.appendUint8(static_cast(idx)); size_t ofs = ctr.size(); ctr.appendUint16(0); // Dummy offset auto seq = instManLocked->getPitchADPCMSequence(idx); ctr.appendUint16(static_cast(seq.size())); for (auto& unit : seq) { ctr.appendUint16(static_cast(unit.data)); } auto loops = instManLocked->getPitchADPCMLoopRoot(idx).getAllLoops(); ctr.appendUint16(static_cast(loops.size())); for (auto& loop : loops) { ctr.appendUint16(static_cast(loop.getBeginPos())); ctr.appendUint16(static_cast(loop.getEndPos())); ctr.appendUint8(static_cast(loop.getTimes())); } auto release = instManLocked->getPitchADPCMRelease(idx); switch (release.getType()) { case InstrumentSequenceRelease::NoRelease: ctr.appendUint8(0x00); // If release.type is NO_RELEASE, then release.begin == -1 so omit to save it. break; case InstrumentSequenceRelease::FixedRelease: ctr.appendUint8(0x01); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::AbsoluteRelease: ctr.appendUint8(0x02); ctr.appendUint16(static_cast(release.getBeginPos())); break; case InstrumentSequenceRelease::RelativeRelease: ctr.appendUint8(0x03); ctr.appendUint16(static_cast(release.getBeginPos())); break; } switch (instManLocked->getPitchADPCMType(idx)) { case SequenceType::AbsoluteSequence: ctr.appendUint8(0x00); break; case SequenceType::RelativeSequence: ctr.appendUint8(0x02); break; default: break; } ctr.writeUint16(ofs, static_cast(ctr.size() - ofs)); } } ctr.writeUint32(instPropOfs, ctr.size() - instPropOfs); /***** Groove section *****/ ctr.appendString("GROOVE "); size_t grooveOfs = ctr.size(); ctr.appendUint32(0); // Dummy groove section offset size_t grooveCnt = mod.lock()->getGrooveCount(); ctr.appendUint8(static_cast(grooveCnt - 1)); for (size_t i = 0; i < grooveCnt; ++i) { ctr.appendUint8(static_cast(i)); auto seq = mod.lock()->getGroove(static_cast(i)); ctr.appendUint8(static_cast(seq.size())); for (auto& g : seq) { ctr.appendUint8(static_cast(g)); } } ctr.writeUint32(grooveOfs, ctr.size() - grooveOfs); /***** Song section *****/ ctr.appendString("SONG "); size_t songSecOfs = ctr.size(); ctr.appendUint32(0); // Dummy song section offset size_t songCnt = mod.lock()->getSongCount(); ctr.appendUint8(static_cast(songCnt)); // Song for (size_t i = 0; i < songCnt; ++i) { ctr.appendUint8(static_cast(i)); size_t songOfs = ctr.size(); ctr.appendUint32(0); // Dummy song block offset auto& sng = mod.lock()->getSong(static_cast(i)); std::string title = sng.getTitle(); ctr.appendUint32(title.length()); if (!title.empty()) ctr.appendString(title); ctr.appendUint32(static_cast(sng.getTempo())); uint8_t tmp = static_cast(sng.getGroove()); ctr.appendUint8(sng.isUsedTempo() ? (0x80 | tmp) : tmp); ctr.appendUint32(static_cast(sng.getSpeed())); ctr.appendUint8(static_cast(sng.getDefaultPatternSize()) - 1); auto style = sng.getStyle(); switch (style.type) { case SongType::Standard: ctr.appendUint8(0x00); break; case SongType::FM3chExpanded: ctr.appendUint8(0x01); break; default: throw std::out_of_range(""); } // Bookmark size_t bmSize = sng.getBookmarkSize(); ctr.appendUint8(static_cast(bmSize)); for (size_t i = 0; i < bmSize; ++i) { Bookmark bm = sng.getBookmark(static_cast(i)); ctr.appendUint32(bm.name.length()); ctr.appendString(bm.name); ctr.appendUint8(static_cast(bm.order)); ctr.appendUint8(static_cast(bm.step)); } // Track for (auto& attrib : style.trackAttribs) { ctr.appendUint8(static_cast(attrib.number)); size_t trackOfs = ctr.size(); ctr.appendUint32(0); // Dummy track subblock offset auto& track = sng.getTrack(attrib.number); // Order size_t odrSize = track.getOrderSize(); ctr.appendUint8(static_cast(odrSize) - 1); for (size_t o = 0; o < odrSize; ++o) ctr.appendUint8(static_cast(track.getOrderInfo(static_cast(o)).patten)); ctr.appendUint8(static_cast(track.getEffectDisplayWidth())); // Pattern for (auto& idx : track.getEditedPatternIndices()) { ctr.appendUint8(static_cast(idx)); size_t ptnOfs = ctr.size(); ctr.appendUint32(0); // Dummy pattern subblock offset auto& pattern = track.getPattern(idx); // Step std::vector stepIdcs = pattern.getEditedStepIndices(); for (auto& sidx : stepIdcs) { ctr.appendUint8(static_cast(sidx)); size_t evFlagOfs = ctr.size(); ctr.appendUint16(0); // Dummy set event flag auto& step = pattern.getStep(sidx); uint16_t eventFlag = 0; int tmp = step.getNoteNumber(); if (!Step::testEmptyNote(tmp)) { eventFlag |= 0x0001; ctr.appendInt8(static_cast(tmp)); } tmp = step.getInstrumentNumber(); if (!Step::testEmptyInstrument(tmp)) { eventFlag |= 0x0002; ctr.appendUint8(static_cast(tmp)); } tmp = step.getVolume(); if (!Step::testEmptyVolume(tmp)) { eventFlag |= 0x0004; ctr.appendUint8(static_cast(tmp)); } for (int i = 0; i < Step::N_EFFECT; ++i) { std::string tmpstr = step.getEffectId(i); if (!Step::testEmptyEffectId(tmpstr)) { eventFlag |= (0x0008 << (i << 1)); ctr.appendString(tmpstr); } tmp = step.getEffectValue(i); if (!Step::testEmptyEffectValue(tmp)) { eventFlag |= (0x0010 << (i << 1)); ctr.appendUint8(static_cast(tmp)); } } ctr.writeUint16(evFlagOfs, eventFlag); } ctr.writeUint32(ptnOfs, ctr.size() - ptnOfs); } ctr.writeUint32(trackOfs, ctr.size() - trackOfs); } ctr.writeUint32(songOfs, ctr.size() - songOfs); } ctr.writeUint32(songSecOfs, ctr.size() - songSecOfs); ctr.writeUint32(eofOfs, ctr.size() - eofOfs); } } BambooTracker-0.4.6/BambooTracker/io/btm_io.hpp000066400000000000000000000027471401124043500213440ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "module_io.hpp" namespace io { class BtmIO final : public AbstractModuleIO { public: BtmIO(); void load(const BinaryContainer& ctr, std::weak_ptr mod, std::weak_ptr instMan) const override; void save(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan) const override; }; } BambooTracker-0.4.6/BambooTracker/io/dat_io.cpp000066400000000000000000000100531401124043500213120ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "dat_io.hpp" #include #include #include "instrument.hpp" #include "file_io_error.hpp" namespace io { DatIO::DatIO() : AbstractBankIO("dat", "MUCOM88 voice", true, false) {} AbstractBank* DatIO::load(const BinaryContainer& ctr) const { size_t csr = 0; // File size check size_t ctrSize = ctr.size(); if (!ctrSize || ctrSize != 0x2000) throw FileCorruptionError(FileType::Bank, 0); std::vector ids; std::vector names; std::vector ctrs; for (int i = 0; i < 256; ++i) { csr++; // Skip first byte (unknown byte) BinaryContainer block = ctr.getSubcontainer(csr, 25); csr += 25; std::string name = ""; for (size_t j = 0; j < 6; ++j) { if (char c = ctr.readChar(csr + j)) name += c; else break; } csr += 6; // Empty if (std::all_of(block.begin(), block.end(), [](const char c) { return c == 0; }) && name.empty()) continue; ids.push_back(i); names.push_back(name); ctrs.push_back(block); } return new Mucom88Bank(ids, names, ctrs); } AbstractInstrument* DatIO::loadInstrument(const BinaryContainer& instCtr, const std::string& name, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Bank, 0); InstrumentFM* fm = new InstrumentFM(instNum, name, instManLocked.get()); fm->setEnvelopeNumber(envIdx); uint8_t tmp; size_t initCsr[] = { 0, 2, 1, 3}; for (int op = 0; op < 4; ++op) { size_t csr = initCsr[op]; auto& params = FM_OP_PARAMS[op]; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), tmp >> 4); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), tmp & 0x0f); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), tmp); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), tmp >> 6); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), tmp & 0x1f); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), tmp & 0x1f); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), tmp); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), tmp >> 4); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), tmp & 0x0f); } tmp = instCtr.readUint8(24); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, tmp >> 3); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, tmp & 0x07); return fm; } } BambooTracker-0.4.6/BambooTracker/io/dat_io.hpp000066400000000000000000000027361401124043500213300ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class DatIO final : public AbstractBankIO { public: DatIO(); AbstractBank* load(const BinaryContainer& ctr) const override; static AbstractInstrument* loadInstrument(const BinaryContainer& instCtr, const std::string& name, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/dmp_io.cpp000066400000000000000000000151571401124043500213340ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "dmp_io.hpp" #include "file_io_error.hpp" #include "io_utils.hpp" namespace io { DmpIO::DmpIO() : AbstractInstrumentIO("dmp", "DefleMask preset", true, false) {} AbstractInstrument* DmpIO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { std::shared_ptr instManLocked = instMan.lock(); size_t csr = 0; uint8_t insType = 1; // default to FM uint8_t fileVersion = ctr.readUint8(csr++); if (fileVersion == 0) { // older, unversioned dmp if (ctr.size() != 49) throw FileCorruptionError(FileType::Inst, csr); } else { if (fileVersion < 9) throw FileCorruptionError(FileType::Inst, csr); if (fileVersion == 9 && ctr.size() != 51) { // make sure it's not for that discontinued chip throw FileCorruptionError(FileType::Inst, csr); } uint8_t system = 2; // default to genesis if (fileVersion >= 11) system = ctr.readUint8(csr++); if (system != 2 && system != 3 && system != 8) { // genesis, sms and arcade only throw FileCorruptionError(FileType::Inst, csr); } insType = ctr.readUint8(csr++); } AbstractInstrument* inst = nullptr; switch (insType) { case 0x00: // SSG { inst = new InstrumentSSG(instNum, fileName, instManLocked.get()); auto ssg = dynamic_cast(inst); uint8_t envSize = ctr.readUint8(csr++); if (envSize > 0) { int idx = instManLocked->findFirstAssignableEnvelopeSSG(); if (idx < 0) throw FileCorruptionError(FileType::Inst, csr); ssg->setEnvelopeEnabled(true); ssg->setEnvelopeNumber(idx); for (uint8_t l = 0; l < envSize; ++l) { int data = ctr.readInt32(csr); // compensate SN76489's envelope step of 2dB to SSG's 3dB if (data > 0) data = 15 - (15 - data) * 2 / 3; csr += 4; if (l == 0) instManLocked->setEnvelopeSSGSequenceData(idx, 0, SSGEnvelopeUnit::makeOnlyDataUnit(data)); else instManLocked->addEnvelopeSSGSequenceData(idx, SSGEnvelopeUnit::makeOnlyDataUnit(data)); } int8_t loop = ctr.readInt8(csr++); if (loop >= 0) instManLocked->addEnvelopeSSGLoop(idx, InstrumentSequenceLoop(loop, envSize - 1)); } uint8_t arpSize = ctr.readUint8(csr++); if (arpSize > 0) { int idx = instManLocked->findFirstAssignableArpeggioSSG(); if (idx < 0) throw FileCorruptionError(FileType::Inst, csr); ssg->setArpeggioEnabled(true); ssg->setArpeggioNumber(idx); uint8_t arpType = ctr.readUint8(csr + arpSize * 4 + 1); if (arpType == 1) instManLocked->setArpeggioSSGType(idx, SequenceType::FixedSequence); for (uint8_t l = 0; l < arpSize; ++l) { int data = ctr.readInt32(csr) + 36; csr += 4; if (arpType == 1) data -= 24; if (l == 0) instManLocked->setArpeggioSSGSequenceData(idx, 0, data); else instManLocked->addArpeggioSSGSequenceData(idx, data); } int8_t loop = ctr.readInt8(csr++); if (loop >= 0) instManLocked->addArpeggioSSGLoop(idx, InstrumentSequenceLoop(loop, arpSize - 1)); } break; } case 0x01: // FM { int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Inst, csr); inst = new InstrumentFM(instNum, fileName, instManLocked.get()); auto fm = dynamic_cast(inst); fm->setEnvelopeNumber(envIdx); if (fileVersion == 9) csr++; // skip version 9's total operators field uint8_t pms = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, ctr.readUint8(csr++)); uint8_t ams = ctr.readUint8(csr++); uint8_t am[4] = {}; for (const int op : { 0, 2, 1, 3 }) { auto& params = FM_OP_PARAMS[op]; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), ctr.readUint8(csr++)); am[op] = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), convertDtFromDmpTfiVgi(ctr.readUint8(csr++) & 15)); // mask out OPM's DT2 instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), ctr.readUint8(csr++)); int ssgeg1 = ctr.readUint8(csr++); ssgeg1 = ssgeg1 & 8 ? ssgeg1 & 7 : -1; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SSGEG), ssgeg1); } if (pms || ams) { int lfoIdx = instManLocked->findFirstAssignableLFOFM(); if (lfoIdx < 0) throw FileCorruptionError(FileType::Inst, csr); fm->setLFOEnabled(true); fm->setLFONumber(lfoIdx); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::PMS, pms); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AMS, ams); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM1, am[0]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM2, am[1]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM3, am[2]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM4, am[3]); } break; } } return inst; } } BambooTracker-0.4.6/BambooTracker/io/dmp_io.hpp000066400000000000000000000026101401124043500213270ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "instrument_io.hpp" namespace io { class DmpIO final : public AbstractInstrumentIO { public: DmpIO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; }; } BambooTracker-0.4.6/BambooTracker/io/export_io.cpp000066400000000000000000000206041401124043500220660ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "export_io.hpp" #include "binary_container.hpp" #include "io_file_type.hpp" #include "file_io_error.hpp" namespace io { void writeVgm(BinaryContainer& container, int target, const std::vector& samples, uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, uint32_t loopSamples, uint32_t totalSamples, bool gd3TagEnabled, const GD3Tag& tag) { uint32_t tagLen = 0; uint32_t tagDataLen = 0; if (gd3TagEnabled) { tagDataLen = tag.trackNameEn.length() + tag.trackNameJp.length() + tag.gameNameEn.length() + tag.gameNameJp.length() + tag.systemNameEn.length() + tag.systemNameJp.length() + tag.authorEn.length() + tag.authorJp.length() + tag.releaseDate.length() + tag.vgmCreator.length() + tag.notes.length(); tagLen = 12 + tagDataLen; } // Header // 0x00: "Vgm " ident uint8_t header[0x100] = {'V', 'g', 'm', ' '}; // 0x04: EOF offset uint32_t offset = 0x100 + samples.size() + 1 + tagLen - 4; *reinterpret_cast(header + 0x04) = offset; // 0x08: Version [v1.71] uint32_t version = 0x171; *reinterpret_cast(header + 0x08) = version; // 0x14: GD3 offset uint32_t gd3Offset = gd3TagEnabled ? (0x100 + samples.size() + 1 - 0x14) : 0; *reinterpret_cast(header + 0x14) = gd3Offset; // 0x18: Total # samples *reinterpret_cast(header + 0x18) = totalSamples; // 0x1c: Loop offset uint32_t loopOffset = loopFlag ? (loopPoint + 0x100 - 0x1c) : 0; *reinterpret_cast(header + 0x1c) = loopOffset; // 0x20: Loop # samples uint32_t loopSamps = loopFlag ? loopSamples : 0; *reinterpret_cast(header + 0x20) = loopSamps; // 0x24: Rate *reinterpret_cast(header + 0x24) = rate; // 0x34: VGM data offset uint32_t dataOffset = 0xcc; *reinterpret_cast(header + 0x34) = dataOffset; switch (target & Export_FmMask) { default: case Export_YM2608: // 0x48: YM2608 clock *reinterpret_cast(header + 0x48) = clock; break; case Export_YM2612: // 0x2c: YM2612 clock *reinterpret_cast(header + 0x2c) = clock; break; case Export_YM2203: // 0x44: YM2203 clock *reinterpret_cast(header + 0x44) = clock / 2; break; case Export_NoneFm: break; } switch (target & Export_SsgMask) { case Export_InternalSsg: break; default: // 0x74: AY8910 clock *reinterpret_cast(header + 0x74) = clock / 4; // 0x78: AY8910 chip type if ((target & Export_SsgMask) == Export_YM2149Psg) *reinterpret_cast(header + 0x78) = 0x10; // 0x79: AY8910 flags *reinterpret_cast(header + 0x79) = 0x01; break; } container.appendArray(header, 0x100); // Commands container.appendVector(samples); container.appendUint8(0x66); // End // GD3 tag if (gd3TagEnabled) { // "Gd3 " ident container.appendString("Gd3 "); // Version [v1.00] uint32_t gd3Version = 0x100; container.appendUint32(gd3Version); // Data size container.appendUint32(tagDataLen); // Track name in english container.appendString(tag.trackNameEn); // Track name in japanes container.appendString(tag.trackNameJp); // Game name in english container.appendString(tag.gameNameEn); // Game name in japanese container.appendString(tag.gameNameJp); // System name in english container.appendString(tag.systemNameEn); // System name in japanese container.appendString(tag.systemNameJp); // Track author in english container.appendString(tag.authorEn); // Track author in japanese container.appendString(tag.authorJp); // Release date container.appendString(tag.releaseDate); // VGM creator container.appendString(tag.vgmCreator); // Notes container.appendString(tag.notes); } } void writeS98(BinaryContainer& container, int target, const std::vector& samples, uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, bool tagEnabled, const S98Tag& tag) { // Header // 0x00: Magic "S98" container.appendString("S98"); // 0x03: Format version 3 uint8_t version = 0x33; container.appendUint8(version); // 0x04: Timer info (sync numerator) uint32_t timeNum = 1; container.appendUint32(timeNum); // 0x08: Timer info 2 (sync denominator) container.appendUint32(rate); // 0x0c: Deprecated uint32_t zero = 0; container.appendUint32(zero); // 0x10: Tag offset uint32_t tagOffset = tagEnabled ? (0x80 + samples.size() + 1) : 0; container.appendUint32(tagOffset); // 0x14: Dump data offset uint32_t dumpOffset = 0x80; container.appendUint32(dumpOffset); // 0x18: Loop offset uint32_t loopOffset = loopFlag ? (0x80 + loopPoint) : 0; container.appendUint32(loopOffset); // 0x1c: Device count uint32_t deviceCnt = 0; if ((target & Export_FmMask) != Export_NoneFm) ++deviceCnt; if ((target & Export_SsgMask) != Export_InternalSsg) ++deviceCnt; container.appendUint32(deviceCnt); if ((target & Export_FmMask) != Export_NoneFm) { // 0x20-0x2f: Device info (if NoneFM, skipped) // 0x20: Device type uint32_t deviceType; uint32_t deviceClock; switch (target & Export_FmMask) { default: case Export_YM2608: deviceType = 4; // OPNA deviceClock = clock; break; case Export_YM2612: deviceType = 3; // OPN2 deviceClock = clock; break; case Export_YM2203: deviceType = 2; // OPN deviceClock = clock / 2; break; } container.appendUint32(deviceType); // 0x24: Clock container.appendUint32(deviceClock); // 0x28: Pan (Unused) container.appendUint32(zero); // 0x2c: Reserved container.appendUint32(zero); } if ((target & Export_SsgMask) != Export_InternalSsg) { // 0x30-0x3f: Device info // 0x30: Device type uint32_t subdeviceType; uint32_t subdeviceClock; switch (target & Export_SsgMask) { default: case Export_AY8910Psg: subdeviceType = 15; // PSG (AY-3-8910) subdeviceClock = clock / 4; break; case Export_YM2149Psg: subdeviceType = 1; // PSG (YM 2149) subdeviceClock = clock / 2; break; } container.appendUint32(subdeviceType); // 0x34: Clock container.appendUint32(subdeviceClock); // 0x38: Pan (Unused) container.appendUint32(zero); // 0x3c: Reserved container.appendUint32(zero); } // 0x??-0x7f: Unused for (uint32_t i = 0; i < (24 - 4 * deviceCnt); ++i) container.appendUint32(zero); // Commands container.appendVector(samples); container.appendUint8(0xfd); // End // GD3 tag if (tagEnabled) { // Tag ident container.appendString("[S98]"); // BOM uint8_t bom[] = { 0xef, 0xbb, 0xbf }; container.appendArray(bom, 3); uint8_t nl = 0x0a; // Title container.appendString("title=" + tag.title); container.appendUint8(nl); // Artist container.appendString("artist=" + tag.artist); container.appendUint8(nl); // Game container.appendString("game=" + tag.game); container.appendUint8(nl); // Year container.appendString("year=" + tag.year); container.appendUint8(nl); // Genre container.appendString("genre=" + tag.genre); container.appendUint8(nl); // Comment container.appendString("comment=" + tag.comment); container.appendUint8(nl); // Copyright container.appendString("copyright=" + tag.copyright); container.appendUint8(nl); // S98by container.appendString("s98by=" + tag.s98by); container.appendUint8(nl); // System container.appendString("system=" + tag.system); container.appendUint8(nl); uint8_t end = 0; container.appendUint8(end); } } } BambooTracker-0.4.6/BambooTracker/io/export_io.hpp000066400000000000000000000047671401124043500221070ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include namespace io { class BinaryContainer; // VGM ---------- struct GD3Tag { std::string trackNameEn, trackNameJp; std::string gameNameEn, gameNameJp; std::string systemNameEn, systemNameJp; std::string authorEn, authorJp; std::string releaseDate; std::string vgmCreator; std::string notes; }; void writeVgm(BinaryContainer& container, int target, const std::vector& samples, uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, uint32_t loopSamples, uint32_t totalSamples, bool gd3TagEnabled, const GD3Tag& tag); // S98 ---------- struct S98Tag { std::string title; std::string artist; std::string game; std::string year; std::string genre; std::string comment; std::string copyright; std::string s98by; std::string system; }; void writeS98(BinaryContainer& container, int target, const std::vector& samples, uint32_t clock, uint32_t rate, bool loopFlag, uint32_t loopPoint, bool tagEnabled, const S98Tag& tag); enum ExportTargetFlag { /* target bits 0-3 : FM type */ Export_NoneFm = 0, Export_YM2608 = 1, Export_YM2612 = 2, Export_YM2203 = 4, Export_FmMask = Export_NoneFm|Export_YM2608|Export_YM2612|Export_YM2203, /* target bit 4-5 : SSG type */ Export_InternalSsg = 0, Export_AY8910Psg = 16, Export_YM2149Psg = 32, Export_SsgMask = Export_InternalSsg|Export_AY8910Psg|Export_YM2149Psg, }; } BambooTracker-0.4.6/BambooTracker/io/ff_io.cpp000066400000000000000000000104451401124043500211420ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "ff_io.hpp" #include #include #include "instrument.hpp" #include "file_io_error.hpp" namespace io { FfIO::FfIO() : AbstractBankIO("ff", "PMD FF", true, false) {} AbstractBank* FfIO::load(const BinaryContainer& ctr) const { size_t csr = 0; // File size check size_t ctrSize = ctr.size(); if (!ctrSize || ctrSize & 0x1f || ctrSize > 0x2000) throw FileCorruptionError(FileType::Bank, csr); std::vector ids; std::vector names; std::vector ctrs; int max = static_cast(ctrSize / 0x20); for (int i = 0; i < max; ++i) { BinaryContainer block = ctr.getSubcontainer(csr, 25); csr += 25; std::string name = ""; for (size_t j = 0; j < 7; ++j) { if (char c = ctr.readChar(csr + j)) name += c; else break; } csr += 7; // Empty if (std::all_of(block.begin(), block.end(), [](const char c) { return c == 0; }) && name.empty()) continue; ids.push_back(i); names.push_back(name); ctrs.push_back(block); } return new FfBank(ids, names, ctrs); } AbstractInstrument* FfIO::loadInstrument(const BinaryContainer& instCtr, const std::string& name, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Bank, 0); InstrumentFM* fm = new InstrumentFM(instNum, name, instManLocked.get()); fm->setEnvelopeNumber(envIdx); uint8_t tmp; size_t initCsr[] = { 0, 2, 1, 3 }; for (int op = 0; op < 4; ++op) { size_t csr = initCsr[op]; auto& params = FM_OP_PARAMS[op]; tmp = instCtr.readUint8(csr); uint8_t ssgeg = (tmp & 0x80) ? 8 : 0; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), (tmp & 0x70) >> 4); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), tmp & 0x0f); csr += 4; tmp = instCtr.readUint8(csr); if (tmp & 0x80) ssgeg |= 4; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), tmp & 0x7f); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), tmp >> 6); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), tmp & 0x1f); csr += 4; tmp = instCtr.readUint8(csr); ssgeg |= ((tmp & 0x60) >> 5); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SSGEG), (ssgeg & 8) ? ssgeg & 7 : -1); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), tmp & 0x1f); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), tmp); csr += 4; tmp = instCtr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), tmp >> 4); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), tmp & 0x0f); } tmp = instCtr.readUint8(24); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, tmp >> 3); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, tmp & 0x07); return fm; } } BambooTracker-0.4.6/BambooTracker/io/ff_io.hpp000066400000000000000000000027341401124043500211510ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class FfIO final : public AbstractBankIO { public: FfIO(); AbstractBank* load(const BinaryContainer& ctr) const override; static AbstractInstrument* loadInstrument(const BinaryContainer& instCtr, const std::string& name, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/file_io_error.hpp000066400000000000000000000042551401124043500227060ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "io_file_type.hpp" namespace io { class FileIOError : public std::runtime_error { protected: const FileType ftype_; FileIOError(const std::string& text, const FileType ftype) : std::runtime_error(text), ftype_(ftype) {} public: inline FileType fileType() const { return ftype_; } }; class FileNotExistError : public FileIOError { FileNotExistError(const FileType type) : FileIOError("File not exist error", type) {} }; class FileUnsupportedError : public FileIOError { public: FileUnsupportedError(const FileType type) : FileIOError("File unsupported error", type) {} }; class FileVersionError : public FileIOError { public: FileVersionError(const FileType type) : FileIOError("File version error", type) {} }; class FileCorruptionError : public FileIOError { private: size_t pos_; public: FileCorruptionError(const FileType type, size_t pos, const std::string& desc = "File corruption error") : FileIOError(desc, type), pos_(pos) {} inline size_t position() const { return pos_; } }; } BambooTracker-0.4.6/BambooTracker/io/ins_io.cpp000066400000000000000000000070141401124043500213360ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "ins_io.hpp" #include "file_io_error.hpp" namespace io { InsIO::InsIO() : AbstractInstrumentIO("ins", "MVSTracker instrument", true, false) {} AbstractInstrument* InsIO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { (void)fileName; std::shared_ptr instManLocked = instMan.lock(); size_t csr = 0; if (ctr.readString(csr, 4).compare("MVSI") != 0) throw FileCorruptionError(FileType::Inst, csr); csr += 4; /*uint8_t fileVersion = */std::stoi(ctr.readString(csr++, 1)); size_t nameCsr = 0; while (ctr.readChar(nameCsr++) != '\0') ; std::string name = ctr.readString(csr, nameCsr - csr); csr = nameCsr; if (ctr.size() - csr != 25) throw FileCorruptionError(FileType::Inst, csr); int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Inst, csr); InstrumentFM* inst = new InstrumentFM(instNum, name, instManLocked.get()); inst->setEnvelopeNumber(envIdx); uint8_t tmp; size_t initCsr[] = { 0, 1, 2, 3 }; for (int op = 0; op < 4; ++op) { size_t pcsr = csr + initCsr[op]; auto& params = FM_OP_PARAMS[op]; tmp = ctr.readUint8(pcsr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), 0x0f & tmp); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), tmp >> 4); pcsr += 4; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), ctr.readUint8(pcsr)); pcsr += 4; tmp = ctr.readUint8(pcsr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), 0x3f & tmp); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), tmp >> 6); pcsr += 4; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), ctr.readUint8(pcsr)); pcsr += 4; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), ctr.readUint8(pcsr)); pcsr += 4; tmp = ctr.readUint8(pcsr); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), 0x0f & tmp); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), tmp >> 4); } csr += 24; tmp = ctr.readUint8(csr); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, 0x07 & tmp); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, tmp >> 3); return inst; } } BambooTracker-0.4.6/BambooTracker/io/ins_io.hpp000066400000000000000000000026101401124043500213400ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "instrument_io.hpp" namespace io { class InsIO final : public AbstractInstrumentIO { public: InsIO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; }; } BambooTracker-0.4.6/BambooTracker/io/instrument_io.cpp000066400000000000000000000055351401124043500227630ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "instrument_io.hpp" #include "file_io_error.hpp" #include "bti_io.hpp" #include "dmp_io.hpp" #include "tfi_io.hpp" #include "vgi_io.hpp" #include "opni_io.hpp" #include "y12_io.hpp" #include "ins_io.hpp" namespace io { AbstractInstrument* AbstractInstrumentIO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { (void)ctr; (void)fileName; (void)instMan; (void)instNum; throw FileUnsupportedError(FileType::Inst); } void AbstractInstrumentIO::save(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum) const { (void)ctr; (void)instMan; (void)instNum; throw FileUnsupportedError(FileType::Inst); } //------------------------------------------------------------ std::unique_ptr InstrumentIO::instance_; InstrumentIO::InstrumentIO() { handler_.add(new BtiIO); handler_.add(new DmpIO); handler_.add(new TfiIO); handler_.add(new VgiIO); handler_.add(new OpniIO); handler_.add(new Y12IO); handler_.add(new InsIO); } InstrumentIO& InstrumentIO::getInstance() { if (!instance_) instance_.reset(new InstrumentIO); return *instance_; } void InstrumentIO::saveInstrument(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum) { handler_.at("bti")->save(ctr, instMan, instNum); } AbstractInstrument* InstrumentIO::loadInstrument(const BinaryContainer& ctr, const std::string& path, std::weak_ptr instMan, int instNum) { size_t fnpos = path.find_last_of("/"); std::string name = path.substr(fnpos + 1, path.find_last_of(".") - fnpos - 1); return handler_.at(getExtension(path))->load(ctr, name, instMan, instNum); } } BambooTracker-0.4.6/BambooTracker/io/instrument_io.hpp000066400000000000000000000061031401124043500227600ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "instruments_manager.hpp" #include "binary_container.hpp" #include "io_file_type.hpp" #include "io_utils.hpp" namespace io { class AbstractInstrumentIO { public: AbstractInstrumentIO(const std::string& ext, const std::string& desc, bool loadable, bool savable) : ext_(ext), desc_(desc), loadable_(loadable), savable_(savable) {} virtual ~AbstractInstrumentIO() = default; virtual AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const; virtual void save(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum) const; inline std::string getExtension() const noexcept { return ext_; } inline std::string getFilterText() const { return desc_ + "(*." + ext_ + ")"; } inline bool isLoadable() const noexcept { return loadable_; } inline bool isSavable() const noexcept { return savable_; } private: const std::string ext_, desc_; bool loadable_, savable_; }; class InstrumentIO { public: static InstrumentIO& getInstance(); void saveInstrument(BinaryContainer& ctr, const std::weak_ptr instMan, int instNum); AbstractInstrument* loadInstrument(const BinaryContainer& ctr, const std::string& path, std::weak_ptr instMan, int instNum); inline bool testLoadableFormat(const std::string& ext) const { return handler_.testLoadableExtension(ext); } inline bool testSavableFormat(const std::string& ext) const { return handler_.testSavableExtension(ext); } inline std::vector getLoadFilter() const { return handler_.getLoadFilterList(); } inline std::vector getSaveFilter() const { return handler_.getSaveFilterList(); } private: InstrumentIO(); static std::unique_ptr instance_; FileIOHandlerMap handler_; }; } BambooTracker-0.4.6/BambooTracker/io/io_file_type.hpp000066400000000000000000000023071401124043500225320ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once namespace io { enum class FileType { Mod, Inst, WAV, VGM, Bank, S98, Unknown }; } BambooTracker-0.4.6/BambooTracker/io/io_utils.cpp000066400000000000000000000120741401124043500217070ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "io_utils.hpp" #include #include "binary_container.hpp" #include "instrument/envelope_fm.hpp" #include "instrument/instrument_property_defs.hpp" namespace io { const std::unordered_map FM_OP_PARAMS[4] = { { { FMOperatorParameter::AR, FMEnvelopeParameter::AR1 }, { FMOperatorParameter::DR, FMEnvelopeParameter::DR1 }, { FMOperatorParameter::SR, FMEnvelopeParameter::SR1 }, { FMOperatorParameter::RR, FMEnvelopeParameter::RR1 }, { FMOperatorParameter::SL, FMEnvelopeParameter::SL1 }, { FMOperatorParameter::TL, FMEnvelopeParameter::TL1 }, { FMOperatorParameter::KS, FMEnvelopeParameter::KS1 }, { FMOperatorParameter::ML, FMEnvelopeParameter::ML1 }, { FMOperatorParameter::DT, FMEnvelopeParameter::DT1 }, { FMOperatorParameter::SSGEG, FMEnvelopeParameter::SSGEG1 } }, { { FMOperatorParameter::AR, FMEnvelopeParameter::AR2 }, { FMOperatorParameter::DR, FMEnvelopeParameter::DR2 }, { FMOperatorParameter::SR, FMEnvelopeParameter::SR2 }, { FMOperatorParameter::RR, FMEnvelopeParameter::RR2 }, { FMOperatorParameter::SL, FMEnvelopeParameter::SL2 }, { FMOperatorParameter::TL, FMEnvelopeParameter::TL2 }, { FMOperatorParameter::KS, FMEnvelopeParameter::KS2 }, { FMOperatorParameter::ML, FMEnvelopeParameter::ML2 }, { FMOperatorParameter::DT, FMEnvelopeParameter::DT2 }, { FMOperatorParameter::SSGEG, FMEnvelopeParameter::SSGEG2 } }, { { FMOperatorParameter::AR, FMEnvelopeParameter::AR3 }, { FMOperatorParameter::DR, FMEnvelopeParameter::DR3 }, { FMOperatorParameter::SR, FMEnvelopeParameter::SR3 }, { FMOperatorParameter::RR, FMEnvelopeParameter::RR3 }, { FMOperatorParameter::SL, FMEnvelopeParameter::SL3 }, { FMOperatorParameter::TL, FMEnvelopeParameter::TL3 }, { FMOperatorParameter::KS, FMEnvelopeParameter::KS3 }, { FMOperatorParameter::ML, FMEnvelopeParameter::ML3 }, { FMOperatorParameter::DT, FMEnvelopeParameter::DT3 }, { FMOperatorParameter::SSGEG, FMEnvelopeParameter::SSGEG3 } }, { { FMOperatorParameter::AR, FMEnvelopeParameter::AR4 }, { FMOperatorParameter::DR, FMEnvelopeParameter::DR4 }, { FMOperatorParameter::SR, FMEnvelopeParameter::SR4 }, { FMOperatorParameter::RR, FMEnvelopeParameter::RR4 }, { FMOperatorParameter::SL, FMEnvelopeParameter::SL4 }, { FMOperatorParameter::TL, FMEnvelopeParameter::TL4 }, { FMOperatorParameter::KS, FMEnvelopeParameter::KS4 }, { FMOperatorParameter::ML, FMEnvelopeParameter::ML4 }, { FMOperatorParameter::DT, FMEnvelopeParameter::DT4 }, { FMOperatorParameter::SSGEG, FMEnvelopeParameter::SSGEG4 } }, }; const FMEnvelopeParameter FM_OPSEQ_PARAMS[38] = { FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, FMEnvelopeParameter::DT1, FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, FMEnvelopeParameter::DT2, FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, FMEnvelopeParameter::DT3, FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, FMEnvelopeParameter::DT4 }; const FMOperatorType FM_OP_TYPES[4] = { FMOperatorType::Op1, FMOperatorType::Op2, FMOperatorType::Op3, FMOperatorType::Op4 }; int convertDtFromDmpTfiVgi(int dt) { switch (dt) { case 0: return 7; case 1: return 6; case 2: return 5; case 3: return 0; case 4: return 1; case 5: return 2; case 6: return 3; case 7: return 3; default: throw std::out_of_range("Out of range dt"); } } } BambooTracker-0.4.6/BambooTracker/io/io_utils.hpp000066400000000000000000000055311401124043500217140ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include "enum_hash.hpp" enum class FMEnvelopeParameter; enum class FMOperatorType; namespace io { class BinaryContainer; template class FileIOHandlerMap { public: void add(T* handler) { map_[handler->getExtension()].reset(handler); const std::string filter = handler->getFilterText(); if (handler->isLoadable()) ldFilters_.push_back(filter); if (handler->isSavable()) svFilters_.push_back(filter); } inline bool containExtension(const std::string& ext) const { return map_.count(ext); } inline bool testLoadableExtension(const std::string& ext) const { return (map_.count(ext) && map_.at(ext)->isLoadable()); } inline bool testSavableExtension(const std::string& ext) const { return (map_.count(ext) && map_.at(ext)->isSavable()); } inline const std::unique_ptr& at(const std::string& ext) const { return map_.at(ext); } inline std::vector getLoadFilterList() const noexcept { return ldFilters_; } inline std::vector getSaveFilterList() const noexcept { return svFilters_; } private: std::unordered_map> map_; std::vector ldFilters_, svFilters_; }; enum class FMOperatorParameter { AR, DR, SR, RR, SL, TL, KS, ML, DT, SSGEG }; extern const std::unordered_map FM_OP_PARAMS[4]; extern const FMEnvelopeParameter FM_OPSEQ_PARAMS[38]; extern const FMOperatorType FM_OP_TYPES[4]; inline std::string getExtension(const std::string& path) { std::string ext = path.substr(path.find_last_of(".") + 1); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); return ext; } int convertDtFromDmpTfiVgi(int dt); } BambooTracker-0.4.6/BambooTracker/io/module_io.cpp000066400000000000000000000044241401124043500220340ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "module_io.hpp" #include "file_io_error.hpp" #include "btm_io.hpp" namespace io { void AbstractModuleIO::load(const BinaryContainer& ctr, std::weak_ptr mod, std::weak_ptr instMan) const { (void)ctr; (void)mod; (void)instMan; throw FileUnsupportedError(FileType::Mod); } void AbstractModuleIO::save(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan) const { (void)ctr; (void)mod; (void)instMan; throw FileUnsupportedError(FileType::Mod); } //------------------------------------------------------------ std::unique_ptr ModuleIO::instance_; ModuleIO::ModuleIO() { handler_.add(new BtmIO); } ModuleIO& ModuleIO::getInstance() { if (!instance_) instance_.reset(new ModuleIO); return *instance_; } void ModuleIO::saveModule(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan) { handler_.at("btm")->save(ctr, mod, instMan); } void ModuleIO::loadModule(const BinaryContainer& ctr, std::weak_ptr mod, std::weak_ptr instMan) { handler_.at("btm")->load(ctr, mod, instMan); } } BambooTracker-0.4.6/BambooTracker/io/module_io.hpp000066400000000000000000000057301401124043500220420ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "module.hpp" #include "instruments_manager.hpp" #include "binary_container.hpp" #include "io_utils.hpp" namespace io { class AbstractModuleIO { public: AbstractModuleIO(const std::string& ext, const std::string& desc, bool loadable, bool savable) : ext_(ext), desc_(desc), loadable_(loadable), savable_(savable) {} virtual ~AbstractModuleIO() = default; virtual void load(const BinaryContainer& ctr, std::weak_ptr mod, std::weak_ptr instMan) const; virtual void save(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan) const; inline std::string getExtension() const noexcept { return ext_; } inline std::string getFilterText() const { return desc_ + "(*." + ext_ + ")"; } inline bool isLoadable() const noexcept { return loadable_; } inline bool isSavable() const noexcept { return savable_; } private: const std::string ext_, desc_; bool loadable_, savable_; }; class ModuleIO { public: static ModuleIO& getInstance(); void saveModule(BinaryContainer& ctr, const std::weak_ptr mod, const std::weak_ptr instMan); void loadModule(const BinaryContainer& ctr, std::weak_ptr mod, std::weak_ptr instMan); inline bool testLoadableFormat(const std::string& ext) const { return handler_.testLoadableExtension(ext); } inline bool testSavableFormat(const std::string& ext) const { return handler_.testSavableExtension(ext); } inline std::vector getLoadFilter() const { return handler_.getLoadFilterList(); } inline std::vector getSaveFilter() const { return handler_.getSaveFilterList(); } private: ModuleIO(); static std::unique_ptr instance_; FileIOHandlerMap handler_; }; } BambooTracker-0.4.6/BambooTracker/io/opni_io.cpp000066400000000000000000000115411401124043500215120ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "opni_io.hpp" #include "format/wopn_file.h" #include "file_io_error.hpp" #include "note.hpp" namespace io { OpniIO::OpniIO() : AbstractInstrumentIO("opni", "WOPN instrument", true, false) {} AbstractInstrument* OpniIO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { (void)fileName; OPNIFile opni; auto&& mem = ctr.toVector(); if (WOPN_LoadInstFromMem(&opni, mem.data(), mem.size()) != 0) throw FileCorruptionError(FileType::Inst, 0); return loadWOPNInstrument(opni.inst, instMan, instNum); } AbstractInstrument* OpniIO::loadWOPNInstrument(const WOPNInstrument &srcInst, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Bank, 0); const char *name = srcInst.inst_name; InstrumentFM* inst = new InstrumentFM(instNum, name, instManLocked.get()); inst->setEnvelopeNumber(envIdx); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, srcInst.fbalg & 7); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, (srcInst.fbalg >> 3) & 7); const WOPNOperator *op[4] = { &srcInst.operators[0], &srcInst.operators[2], &srcInst.operators[1], &srcInst.operators[3], }; int am[4]; for (int n = 0; n < 4; ++n) { auto& params = FM_OP_PARAMS[n]; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), op[n]->dtfm_30 & 15); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), (op[n]->dtfm_30 >> 4) & 7); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), op[n]->level_40); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), op[n]->rsatk_50 >> 6); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), op[n]->rsatk_50 & 31); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), op[n]->amdecay1_60 & 31); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), op[n]->decay2_70 & 31); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), op[n]->susrel_80 & 15); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), op[n]->susrel_80 >> 4); int ssgeg = op[n]->ssgeg_90; ssgeg = ssgeg & 8 ? ssgeg & 7 : -1; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SSGEG), ssgeg); am[n] = op[n]->amdecay1_60 >> 7; } if (srcInst.lfosens != 0) { int lfoIdx = instManLocked->findFirstAssignableLFOFM(); if (lfoIdx < 0) throw FileCorruptionError(FileType::Bank, 0); inst->setLFOEnabled(true); inst->setLFONumber(lfoIdx); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::PMS, srcInst.lfosens & 7); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AMS, (srcInst.lfosens >> 4) & 3); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM1, am[0]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM2, am[1]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM3, am[2]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM4, am[3]); } if (srcInst.note_offset != 0) { int arpIdx = instManLocked->findFirstAssignableArpeggioFM(); if (arpIdx < 0) throw FileCorruptionError(FileType::Bank, 0); inst->setArpeggioEnabled(FMOperatorType::All, true); inst->setArpeggioNumber(FMOperatorType::All, arpIdx); instManLocked->setArpeggioFMSequenceData(arpIdx, 0, srcInst.note_offset + Note::DEFAULT_NOTE_NUM); instManLocked->setArpeggioFMType(arpIdx, SequenceType::AbsoluteSequence); } return inst; } } BambooTracker-0.4.6/BambooTracker/io/opni_io.hpp000066400000000000000000000031061401124043500215150ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "instrument_io.hpp" struct WOPNInstrument; namespace io { class OpniIO final : public AbstractInstrumentIO { public: OpniIO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; static AbstractInstrument* loadWOPNInstrument(const WOPNInstrument &srcInst, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/p86_io.cpp000066400000000000000000000067501401124043500211700ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "p86_io.hpp" #include #include "instrument.hpp" #include "file_io_error.hpp" #include "chip/codec/ymb_codec.hpp" namespace io { namespace { inline uint32_t readUint24(const BinaryContainer& ctr, size_t offset) { return ctr.readUint8(offset) | (ctr.readUint8(offset + 1) << 8u) | (ctr.readUint8(offset + 2) << 16u); } } P86IO::P86IO() : AbstractBankIO("p86", "PMD P86", true, false) {} AbstractBank* P86IO::load(const BinaryContainer& ctr) const { using namespace std::literals::string_literals; std::string id = ctr.readString(0, 12); if (id != "PCM86 DATA\n\0"s) throw FileCorruptionError(FileType::Bank, 0); uint32_t size = readUint24(ctr, 13); if (size != ctr.size()) throw FileCorruptionError(FileType::Bank, 13); constexpr size_t SAMP_OFFS = 0x610; if (ctr.size() < SAMP_OFFS) throw FileCorruptionError(FileType::Bank, 0x10); std::vector ids; std::vector> samples; size_t globCsr = 0x10; size_t offs = 0; constexpr int MAX_CNT = 256; for (int i = 0; i < MAX_CNT; ++i) { uint32_t start = readUint24(ctr, globCsr); globCsr += 3; uint32_t len = readUint24(ctr, globCsr); globCsr += 3; if (len) { if (ids.empty()) offs = start; ids.push_back(i); std::vector&& smp = ctr.getSubcontainer(SAMP_OFFS + start - offs, len).toVector(); std::vector buf(smp.size()); std::transform(smp.begin(), smp.end(), buf.begin(), [](uint8_t v) { return static_cast(static_cast(v)) << 8; }); smp.resize((smp.size() + 1) / 2); smp.shrink_to_fit(); smp.back() = 0; // Clear last data codec::ymb_encode(buf.data(), smp.data(), buf.size()); samples.push_back(std::move(smp)); } } return new P86Bank(ids, samples); } AbstractInstrument* P86IO::loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); adpcm->setSampleNumber(sampIdx); instManLocked->storeSampleADPCMRawSample(sampIdx, sample); instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g instManLocked->setSampleADPCMRootDeltaN(sampIdx, 0x4a0d); // 16540Hz return adpcm; } } BambooTracker-0.4.6/BambooTracker/io/p86_io.hpp000066400000000000000000000026741401124043500211760ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class P86IO final : public AbstractBankIO { public: P86IO(); AbstractBank* load(const BinaryContainer& ctr) const override; static AbstractInstrument* loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/ppc_io.cpp000066400000000000000000000061031401124043500213250ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "ppc_io.hpp" #include #include "instrument.hpp" #include "file_io_error.hpp" namespace io { PpcIO::PpcIO() : AbstractBankIO("ppc", "PMD PPC", true, false) {} AbstractBank* PpcIO::load(const BinaryContainer& ctr) const { size_t globCsr = 0; if (ctr.readString(globCsr, 30) != "ADPCM DATA for PMD ver.4.4- ") throw FileCorruptionError(FileType::Bank, globCsr); globCsr += 30; uint16_t nextAddr = ctr.readUint16(globCsr); if ((nextAddr - 0x26u) * 0x20u + 0x420u != ctr.size()) // File size check throw FileCorruptionError(FileType::Bank, globCsr); globCsr += 2; size_t sampOffs = globCsr + 256 * 4; if (ctr.size() < sampOffs) throw FileCorruptionError(FileType::Bank, globCsr); std::vector ids; std::vector> samples; size_t offs = 0; constexpr int MAX_CNT = 256; for (int i = 0; i < MAX_CNT; ++i) { uint16_t start = ctr.readUint16(globCsr); globCsr += 2; uint16_t stop = ctr.readUint16(globCsr); globCsr += 2; if (start < stop) { if (ids.empty()) offs = start; ids.push_back(i); size_t st = sampOffs + static_cast((start - offs) << 5); size_t sampSize = std::min((stop + 1u - start) << 5, ctr.size() - st); samples.push_back(ctr.getSubcontainer(st, sampSize).toVector()); } } return new PpcBank(ids, samples); } AbstractInstrument* PpcIO::loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); adpcm->setSampleNumber(sampIdx); instManLocked->storeSampleADPCMRawSample(sampIdx, sample); instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g instManLocked->setSampleADPCMRootDeltaN(sampIdx, 0x49cd); // 16000Hz return adpcm; } } BambooTracker-0.4.6/BambooTracker/io/ppc_io.hpp000066400000000000000000000026741401124043500213430ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class PpcIO final : public AbstractBankIO { public: PpcIO(); AbstractBank* load(const BinaryContainer& ctr) const override; static AbstractInstrument* loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/pps_io.cpp000066400000000000000000000056561401124043500213610ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pps_io.hpp" #include #include "instrument.hpp" #include "file_io_error.hpp" #include "chip/codec/ymb_codec.hpp" namespace io { PpsIO::PpsIO() : AbstractBankIO("pps", "PMD PPS", true, false) {} AbstractBank* PpsIO::load(const BinaryContainer& ctr) const { constexpr size_t SAMP_OFFS = 0x54; if (ctr.size() < SAMP_OFFS) throw FileCorruptionError(FileType::Bank, 0); std::vector ids; std::vector> samples; size_t globCsr = 0; constexpr int MAX_CNT = 14; for (int i = 0; i < MAX_CNT; ++i) { uint16_t start = ctr.readUint16(globCsr); globCsr += 2; uint16_t len = ctr.readUint16(globCsr); globCsr += 4; // Skip 2 bytes if (len) { ids.push_back(i); std::vector&& smp = ctr.getSubcontainer(start, len).toVector(); std::vector buf(smp.size() * 2); for (size_t i = 0; i < smp.size(); ++i) { uint8_t sample = smp[i]; buf[i] = (static_cast(sample >> 4) - 8) << 12; buf[i + 1] = (static_cast(sample & 0x0f) - 8) << 12; } codec::ymb_encode(buf.data(), smp.data(), buf.size()); samples.push_back(std::move(smp)); } } return new PpsBank(ids, samples); } AbstractInstrument* PpsIO::loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); adpcm->setSampleNumber(sampIdx); instManLocked->storeSampleADPCMRawSample(sampIdx, sample); instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g instManLocked->setSampleADPCMRootDeltaN(sampIdx, 0x49cd); // 16000Hz return adpcm; } } BambooTracker-0.4.6/BambooTracker/io/pps_io.hpp000066400000000000000000000026741401124043500213630ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class PpsIO final : public AbstractBankIO { public: PpsIO(); AbstractBank* load(const BinaryContainer& ctr) const override; static AbstractInstrument* loadInstrument(const std::vector& sample, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/pvi_io.cpp000066400000000000000000000060161401124043500213440ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pvi_io.hpp" #include #include "instrument.hpp" #include "file_io_error.hpp" namespace io { PviIO::PviIO() : AbstractBankIO("pvi", "FMP PVI", true, false) {} AbstractBank* PviIO::load(const BinaryContainer& ctr) const { std::string ident = ctr.readString(0, 4); if (ident != "PVI1" && ident != "PVI2") throw FileCorruptionError(FileType::Bank, 0); uint16_t deltaN = ctr.readUint16(8); uint8_t cnt = ctr.readUint8(11); constexpr size_t sampOffs = 0x10 + 128 * 4; if (ctr.size() < sampOffs) throw FileCorruptionError(FileType::Bank, 0x10); std::vector ids; std::vector> samples; size_t offs = 0; size_t addrPos = 0x10; for (size_t i = 0; i < cnt; ++i) { uint16_t start = ctr.readUint16(addrPos); addrPos += 2; uint16_t stop = ctr.readUint16(addrPos); addrPos += 2; if (start < stop) { if (ids.empty()) offs = start; ids.push_back(static_cast(i)); size_t st = sampOffs + static_cast((start - offs) << 5); size_t sampSize = std::min((stop + 1u - start) << 5, ctr.size() - st); samples.push_back(ctr.getSubcontainer(st, sampSize).toVector()); } } /* if (ids.size() != cnt) throw FileCorruptionError(FileType::Bank, 11); */ return new PviBank(ids, deltaN, samples); } AbstractInstrument* PviIO::loadInstrument(const std::vector& sample, uint16_t deltaN, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); adpcm->setSampleNumber(sampIdx); instManLocked->storeSampleADPCMRawSample(sampIdx, sample); instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 60); // o5c instManLocked->setSampleADPCMRootDeltaN(sampIdx, deltaN); return adpcm; } } BambooTracker-0.4.6/BambooTracker/io/pvi_io.hpp000066400000000000000000000027221401124043500213510ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class PviIO final : public AbstractBankIO { public: PviIO(); AbstractBank* load(const BinaryContainer& ctr) const override; static AbstractInstrument* loadInstrument(const std::vector& sample, uint16_t deltaN, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/pzi_io.cpp000066400000000000000000000072201401124043500213460ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pzi_io.hpp" #include #include #include "instrument.hpp" #include "file_io_error.hpp" #include "chip/codec/ymb_codec.hpp" namespace io { PziIO::PziIO() : AbstractBankIO("pzi", "FMP PZI", true, false) {} AbstractBank* PziIO::load(const BinaryContainer& ctr) const { if (ctr.readString(0, 4) != "PZI1") throw FileCorruptionError(FileType::Bank, 0); constexpr size_t SAMP_OFFS = 0x920; if (ctr.size() < SAMP_OFFS) throw FileCorruptionError(FileType::Bank, 0x20); std::vector ids; std::vector> samples; std::vector isRepeatedList; std::vector deltaNs; size_t globCsr = 0x20; constexpr int MAX_CNT = 128; for (int i = 0; i < MAX_CNT; ++i) { uint32_t start = ctr.readUint32(globCsr); globCsr += 4; uint32_t len = ctr.readUint32(globCsr); globCsr += 4; uint32_t loopStart = ctr.readUint32(globCsr); globCsr += 4; uint32_t loopEnd = ctr.readUint32(globCsr); globCsr += 4; uint16_t sr = ctr.readUint16(globCsr); globCsr += 2; // only support loop within entire region bool isRepeated = (!loopStart && len == loopEnd); if (len) { ids.push_back(i); isRepeatedList.push_back(isRepeated); deltaNs.push_back(SampleADPCM::calculateADPCMDeltaN(sr)); std::vector&& smp = ctr.getSubcontainer(SAMP_OFFS + start, len).toVector(); std::vector buf(smp.size()); std::transform(smp.begin(), smp.end(), buf.begin(), [](uint8_t v) { // Centering return (static_cast(v) - std::numeric_limits::max()) << 8; }); smp.resize((smp.size() + 1) / 2); smp.shrink_to_fit(); smp.back() = 0; // Clear last data codec::ymb_encode(buf.data(), smp.data(), buf.size()); samples.push_back(std::move(smp)); } } return new PziBank(ids, deltaNs, isRepeatedList, samples); } AbstractInstrument* PziIO::loadInstrument(const std::vector& sample, int deltaN, bool isRepeated, std::weak_ptr instMan, int instNum) { std::shared_ptr instManLocked = instMan.lock(); int sampIdx = instManLocked->findFirstAssignableSampleADPCM(); if (sampIdx < 0) throw FileCorruptionError(FileType::Bank, 0); InstrumentADPCM* adpcm = new InstrumentADPCM(instNum, "", instManLocked.get()); adpcm->setSampleNumber(sampIdx); instManLocked->storeSampleADPCMRawSample(sampIdx, sample); instManLocked->setSampleADPCMRootKeyNumber(sampIdx, 67); // o5g instManLocked->setSampleADPCMRootDeltaN(sampIdx, deltaN); instManLocked->setSampleADPCMRepeatEnabled(sampIdx, isRepeated); return adpcm; } } BambooTracker-0.4.6/BambooTracker/io/pzi_io.hpp000066400000000000000000000027461401124043500213630ustar00rootroot00000000000000/* * Copyright (C) 2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class PziIO final : public AbstractBankIO { public: PziIO(); AbstractBank* load(const BinaryContainer& ctr) const override; static AbstractInstrument* loadInstrument(const std::vector& sample, int deltaN, bool isRepeated, std::weak_ptr instMan, int instNum); }; } BambooTracker-0.4.6/BambooTracker/io/tfi_io.cpp000066400000000000000000000063541401124043500213350ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "tfi_io.hpp" #include "file_io_error.hpp" #include "io_utils.hpp" namespace io { TfiIO::TfiIO() : AbstractInstrumentIO("tfi", "TFM Music Maker instrument", true, false) {} AbstractInstrument* TfiIO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { std::shared_ptr instManLocked = instMan.lock(); if (ctr.size() != 42) throw FileCorruptionError(FileType::Inst, 0); int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Inst, 0); InstrumentFM* inst = new InstrumentFM(instNum, fileName, instManLocked.get()); inst->setEnvelopeNumber(envIdx); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, ctr.readUint8(0)); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, ctr.readUint8(1)); size_t csr = 2; for (int op = 0; op < 4; ++op) { const auto& params = FM_OP_PARAMS[op]; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), convertDtFromDmpTfiVgi(ctr.readUint8(csr++))); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), ctr.readUint8(csr++)); int ssgeg = ctr.readUint8(csr++); ssgeg = ssgeg & 8 ? ssgeg & 7 : -1; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SSGEG), ssgeg); } return inst; } } BambooTracker-0.4.6/BambooTracker/io/tfi_io.hpp000066400000000000000000000026101401124043500213310ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "instrument_io.hpp" namespace io { class TfiIO final : public AbstractInstrumentIO { public: TfiIO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; }; } BambooTracker-0.4.6/BambooTracker/io/vgi_io.cpp000066400000000000000000000077261401124043500213440ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "vgi_io.hpp" #include "file_io_error.hpp" #include "io_utils.hpp" namespace io { VgiIO::VgiIO() : AbstractInstrumentIO("vgi", "VGM Music Maker instrument", true, false) {} AbstractInstrument* VgiIO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { std::shared_ptr instManLocked = instMan.lock(); if (ctr.size() != 43) throw FileCorruptionError(FileType::Inst, 0); int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Inst, 0); InstrumentFM* inst = new InstrumentFM(instNum, fileName, instManLocked.get()); inst->setEnvelopeNumber(envIdx); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, ctr.readUint8(0)); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, ctr.readUint8(1)); uint8_t pams = ctr.readUint8(2); size_t csr = 3; uint8_t am[4]; for (int op = 0; op < 4; ++op) { const auto& params = FM_OP_PARAMS[op]; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), convertDtFromDmpTfiVgi(ctr.readUint8(csr++))); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), ctr.readUint8(csr++)); uint8_t drams = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), drams & 31); am[op] = drams >> 7; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), ctr.readUint8(csr++)); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), ctr.readUint8(csr++)); int ssgeg = ctr.readUint8(csr++); ssgeg = ssgeg & 8 ? ssgeg & 7 : -1; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SSGEG), ssgeg); } if (pams != 0) { int lfoIdx = instManLocked->findFirstAssignableLFOFM(); if (lfoIdx < 0) throw FileCorruptionError(FileType::Inst, 43); inst->setLFOEnabled(true); inst->setLFONumber(lfoIdx); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::PMS, pams & 7); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AMS, pams >> 4); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM1, am[0]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM2, am[1]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM3, am[2]); instManLocked->setLFOFMParameter(lfoIdx, FMLFOParameter::AM4, am[3]); } return inst; } } BambooTracker-0.4.6/BambooTracker/io/vgi_io.hpp000066400000000000000000000026101401124043500213340ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "instrument_io.hpp" namespace io { class VgiIO final : public AbstractInstrumentIO { public: VgiIO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; }; } BambooTracker-0.4.6/BambooTracker/io/wav_container.cpp000066400000000000000000000171011401124043500227130ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "wav_container.hpp" #include #include #include "file_io_error.hpp" namespace io { namespace { inline void assertValue(bool f, size_t pos) { if (!f) throw io::FileCorruptionError(io::FileType::WAV, pos); } enum WavOffset : size_t { RIFF_OFFS = 0, FILE_SIZE_OFFS = 4, WAVE_OFFS = 8, FMT_OFS = 12, FMT_SIZE_OFFS = 16, FORMAT_OFFS = 20, NCH_OFFS = 22, RATE_OFFS = 24, BYTE_RATE_OFFS = 28, BLOCK_SIZE_OFFS = 32, BIT_SIZE_OFFS = 34, DATA_OFFS = 36, DATA_SIZE_OFFS = 40, PREPARED_SIZE = 44 }; } WavContainer::WavContainer(uint32_t rate, uint16_t nCh, uint16_t bitSize) : nCh_(nCh), bitSize_(bitSize), rate_(rate) { // RIFF header buf_.appendString("RIFF"); uint16_t byteSize = bitSize_ / 8; buf_.appendUint32(36); buf_.appendString("WAVE"); // fmt chunk buf_.appendString("fmt "); uint32_t chunkOfs = 16; buf_.appendUint32(chunkOfs); uint16_t fmtId = 1; // Raw linear PCM buf_.appendUint16(fmtId); buf_.appendUint16(nCh_); buf_.appendUint32(rate_); uint16_t blockSize = byteSize * nCh_; uint32_t byteRate = blockSize * rate_; buf_.appendUint32(byteRate); buf_.appendUint16(blockSize); buf_.appendUint16(bitSize_); // Data chunk buf_.appendString("data"); buf_.appendUint32(0); } WavContainer::WavContainer(const BinaryContainer& bc) { buf_.resize(PREPARED_SIZE); size_t p = 0; assertValue(bc.readString(p, 4) == "RIFF", p); buf_.writeString(RIFF_OFFS, "RIFF"); p += 4; uint32_t fileSize = bc.readUint32(p) + 8; assertValue(fileSize == bc.size(), p); p += 4; assertValue(bc.readString(p, 4) == "WAVE", p); buf_.writeString(WAVE_OFFS, "WAVE"); p += 4; while (p < fileSize) { std::string id = bc.readString(p, 4); p += 4; if (id == "fmt ") { buf_.writeString(FMT_OFS, "fmt "); uint32_t fmtSize = bc.readUint32(p); buf_.writeUint32(FMT_SIZE_OFFS, 16); size_t fmtp = p + 4; p = fmtp + fmtSize; assertValue(bc.readUint16(fmtp) == 1, fmtp); // Only support linear PCM buf_.writeUint16(FORMAT_OFFS, 1); fmtp += 2; nCh_ = bc.readUint16(fmtp); buf_.writeUint16(NCH_OFFS, nCh_); fmtp += 2; rate_ = bc.readUint32(fmtp); buf_.writeUint32(RATE_OFFS, rate_); fmtp += 4; byteRate_ = bc.readUint32(fmtp); buf_.writeUint32(BYTE_RATE_OFFS, byteRate_); fmtp += 4; blockSize_ = bc.readUint16(fmtp); assertValue(byteRate_ == blockSize_ * rate_, fmtp); buf_.writeUint16(BLOCK_SIZE_OFFS, blockSize_); fmtp += 2; bitSize_ = bc.readUint16(fmtp); assertValue(bitSize_ == 16, fmtp); // Only support 16-bit assertValue(blockSize_ == nCh_ * bitSize_ / 8, fmtp); buf_.writeUint16(BIT_SIZE_OFFS, bitSize_); /* fmtp += 2; */ } else if (id == "data") { buf_.writeString(DATA_OFFS, "data"); uint32_t dataSize = bc.readUint32(p); assertValue(p + dataSize <= bc.size(), p); buf_.writeUint32(DATA_SIZE_OFFS, dataSize); p += 4; buf_.appendBinaryContainer(bc.getSubcontainer(p, dataSize)); p += dataSize; } else { p += (bc.readUint32(p) + 4); // Jump to next chunk } } buf_.writeUint32(FILE_SIZE_OFFS, buf_.size() - 8); } void WavContainer::setChannelCount(uint16_t n) { nCh_ = n; buf_.writeUint16(NCH_OFFS, nCh_); updateBlockSize(); } void WavContainer::setBitSize(uint16_t size) { bitSize_ = size; buf_.writeUint16(BIT_SIZE_OFFS, bitSize_); updateBlockSize(); } void WavContainer::setSampleRate(uint32_t rate) { rate_ = rate; buf_.writeUint16(rate, rate); updateBlockSize(); updateByteRate(); } WavContainer::size_type WavContainer::getSampleCount() const { return (buf_.size() - PREPARED_SIZE) * bitSize_ / 8 / nCh_; } void WavContainer::appendSample(const int16_t* sample, size_type nSamples) { size_t dataSize = nCh_ * nSamples * sizeof(int16_t); buf_.appendArray(reinterpret_cast(sample), dataSize); updateSizeDataAfterAppendSample(); } void WavContainer::appendSample(const std::vector& sample) { size_t dataSize = sample.size() * sizeof(int16_t); buf_.appendArray(reinterpret_cast(sample.data()), dataSize); updateSizeDataAfterAppendSample(); } void WavContainer::appendSample(const BinaryContainer& sample) { buf_.appendBinaryContainer(sample); updateSizeDataAfterAppendSample(); } BinaryContainer WavContainer::getSample() const noexcept { return buf_.getSubcontainer(PREPARED_SIZE, buf_.size() - PREPARED_SIZE); } // WavContainer* WavContainer::resample(const WavContainer* src, uint32_t rate) // { // std::unique_ptr tgt // = std::make_unique(0, rate, src->getChannelCount(), src->getBitSize()); // assert(src->getBitSize() == 16); // Only support int16_t // size_t nCh = src->getChannelCount(); // size_t tsize = src->getSampleCount() * tgt->getSampleRate() / src->getSampleRate(); // BinaryContainer tbc(tsize * 2 * nCh); // BinaryContainer sbc = src->getSample(); // double r = static_cast(src->getSampleRate()) / tgt->getSampleRate(); // for (size_t n = 0; n < tsize; ++n) { // double curnf = n * r; // int curni = static_cast(curnf); // double sub = curnf - curni; // for (size_t ch = 0; ch < nCh; ++ch) { // double a = sbc.readInt16((curni * nCh + ch) * 2); // if (sub == 0.) { // double b = sbc.readInt16(((curni + 1) * nCh + ch) * 2); // tbc.appendInt16(static_cast(std::round(a + (b - a) * sub))); // } // else { // tbc.appendInt16(static_cast(std::round(a))); // } // } // } // tgt->storeSample(tbc); // return tgt.release(); // } // WavContainer* WavContainer::mono(const WavContainer* src) // { // std::unique_ptr tgt // = std::make_unique(0, src->getSampleRate(), 1, src->getBitSize()); // assert(src->getBitSize() == 16); // Only support int16_t // BinaryContainer tbc; // BinaryContainer sbc = src->getSample(); // uint16_t nCh = src->getChannelCount(); // size_t size = src->getSampleCount(); // for (size_t i = 0; i < size; ++i) { // int32_t v = 0; // for (size_t ch = 0; ch < nCh; ++ch) { // v += sbc.readInt16((i * nCh + ch) * 2); // } // tbc.writeInt16(i, static_cast(clamp(v, -32768, 32767))); // } // tgt->storeSample(tbc); // return tgt.release(); // } void WavContainer::updateBlockSize() { blockSize_ = nCh_ * bitSize_ / 8; buf_.writeUint16(BLOCK_SIZE_OFFS, blockSize_); } void WavContainer::updateByteRate() { byteRate_ = blockSize_ * rate_; buf_.writeUint32(BYTE_RATE_OFFS, byteRate_); } void WavContainer::updateSizeDataAfterAppendSample() { buf_.writeUint32(FILE_SIZE_OFFS, buf_.size() - 8); buf_.writeUint32(DATA_SIZE_OFFS, buf_.size() - PREPARED_SIZE); } } BambooTracker-0.4.6/BambooTracker/io/wav_container.hpp000066400000000000000000000067041401124043500227270ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include "binary_container.hpp" namespace io { class WavContainer { public: using value_type = BinaryContainer::value_type; using size_type = BinaryContainer::size_type; using iterator = BinaryContainer::iterator; using const_iterator = BinaryContainer::const_iterator; using reverse_iterator = BinaryContainer::reverse_iterator; using const_reverse_iterator = BinaryContainer::const_reverse_iterator; explicit WavContainer(uint32_t rate = 44100, uint16_t nCh = 2, uint16_t getBitSize = 16); explicit WavContainer(const BinaryContainer& bc); inline iterator begin() noexcept { return buf_.begin(); } inline const_iterator begin() const noexcept { return buf_.begin(); } inline iterator end() noexcept { return buf_.end(); } inline const_iterator end() const noexcept { return buf_.end(); } inline const_iterator cbegin() const noexcept { return buf_.cbegin(); } inline const_iterator cend() const noexcept { return buf_.cend(); } inline reverse_iterator rbegin() noexcept { return buf_.rbegin(); } inline const_reverse_iterator rbegin() const noexcept { return buf_.rbegin(); } inline reverse_iterator rend() noexcept { return buf_.rend(); } inline const_reverse_iterator rend() const noexcept { return buf_.rend(); } inline const_reverse_iterator crbegin() const noexcept { return buf_.crbegin(); } inline const_reverse_iterator crend() const noexcept { return buf_.crend(); } inline size_type size() const { return buf_.size(); } void setChannelCount(uint16_t n); inline uint16_t getChannelCount() const noexcept { return nCh_; } void setBitSize(uint16_t size); inline uint16_t getBitSize() const noexcept { return bitSize_; } void setSampleRate(uint32_t rate); inline uint32_t getSampleRate() const noexcept { return rate_; } size_type getSampleCount() const; void appendSample(const int16_t* sample, size_type nSamples); void appendSample(const std::vector& sample); void appendSample(const BinaryContainer& sample); BinaryContainer getSample() const noexcept; // static WavContainer* resample(const WavContainer* src, uint32_t rate); // static WavContainer* mono(const WavContainer* src); private: uint16_t nCh_, bitSize_, blockSize_; uint32_t rate_, byteRate_; BinaryContainer buf_; void updateBlockSize(); void updateByteRate(); void updateSizeDataAfterAppendSample(); }; } BambooTracker-0.4.6/BambooTracker/io/wopn_io.cpp000066400000000000000000000032671401124043500215360ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "wopn_io.hpp" #include "format/wopn_file.h" #include "file_io_error.hpp" namespace io { WopnIO::WopnIO() : AbstractBankIO("wopn", "WOPN bank", true, false) {} AbstractBank* WopnIO::load(const BinaryContainer& ctr) const { struct WOPNDeleter { void operator()(WOPNFile *x) { WOPN_Free(x); } }; std::unique_ptr wopn; auto&& mem = ctr.toVector(); wopn.reset(WOPN_LoadBankFromMem(mem.data(), mem.size(), nullptr)); if (!wopn) throw FileCorruptionError(FileType::Bank, 0); WopnBank *bank = new WopnBank(wopn.get()); wopn.release(); return bank; } } BambooTracker-0.4.6/BambooTracker/io/wopn_io.hpp000066400000000000000000000024331401124043500215350ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "bank_io.hpp" namespace io { class WopnIO final : public AbstractBankIO { public: WopnIO(); AbstractBank* load(const BinaryContainer& ctr) const override; }; } BambooTracker-0.4.6/BambooTracker/io/y12_io.cpp000066400000000000000000000064511401124043500211640ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "y12_io.hpp" #include "file_io_error.hpp" namespace io { Y12IO::Y12IO() : AbstractInstrumentIO("y12", "Gens KMod dump", true, false) {} AbstractInstrument* Y12IO::load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const { std::shared_ptr instManLocked = instMan.lock(); if (ctr.size() != 128) throw FileCorruptionError(FileType::Inst, 0); int envIdx = instManLocked->findFirstAssignableEnvelopeFM(); if (envIdx < 0) throw FileCorruptionError(FileType::Inst, 0); InstrumentFM* inst = new InstrumentFM(instNum, fileName, instManLocked.get()); inst->setEnvelopeNumber(envIdx); size_t csr = 0; for (const int op : { 0, 2, 1, 3 }) { const auto& params = FM_OP_PARAMS[op]; uint8_t tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::ML), 0x0f & tmp); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DT), 0x07 & (tmp >> 4)); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::TL), 0x7f & tmp); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::AR), 0x1f & tmp); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::KS), tmp >> 6); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::DR), 0x1f & tmp); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SR), 0x1f & tmp); tmp = ctr.readUint8(csr++); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::RR), 0x0f & tmp); instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SL), tmp >> 4); int ssgeg = ctr.readUint8(csr++); ssgeg = ssgeg & 8 ? ssgeg & 7 : -1; instManLocked->setEnvelopeFMParameter(envIdx, params.at(FMOperatorParameter::SSGEG), ssgeg); csr += 9; } instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::AL, ctr.readUint8(0x40)); instManLocked->setEnvelopeFMParameter(envIdx, FMEnvelopeParameter::FB, ctr.readUint8(0x41)); return inst; } } BambooTracker-0.4.6/BambooTracker/io/y12_io.hpp000066400000000000000000000026201401124043500211630ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include "instrument_io.hpp" namespace io { class Y12IO final : public AbstractInstrumentIO { public: Y12IO(); AbstractInstrument* load(const BinaryContainer& ctr, const std::string& fileName, std::weak_ptr instMan, int instNum) const override; }; } BambooTracker-0.4.6/BambooTracker/jamming.cpp000066400000000000000000000132001401124043500210630ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "jamming.hpp" #include #include #include #include #include "note.hpp" #include "utils.hpp" namespace jam_utils { Note makeNote(const JamKeyInfo& info, int baseOctave) { return (info.key == JamKey::MidiKey) ? Note(info.keyNum) : makeNote(baseOctave, info.key); } Note makeNote(int baseOctave, JamKey key) { switch (key) { case JamKey::LowC: return Note(baseOctave, Note::C); case JamKey::LowCS: return Note(baseOctave, Note::CS); case JamKey::LowD: return Note(baseOctave, Note::D); case JamKey::LowDS: return Note(baseOctave, Note::DS); case JamKey::LowE: return Note(baseOctave, Note::E); case JamKey::LowF: return Note(baseOctave, Note::F); case JamKey::LowFS: return Note(baseOctave, Note::FS); case JamKey::LowG: return Note(baseOctave, Note::G); case JamKey::LowGS: return Note(baseOctave, Note::GS); case JamKey::LowA: return Note(baseOctave, Note::A); case JamKey::LowAS: return Note(baseOctave, Note::AS); case JamKey::LowB: return Note(baseOctave, Note::B); case JamKey::LowC2: case JamKey::HighC: return Note(baseOctave + 1, Note::C); case JamKey::LowCS2: case JamKey::HighCS: return Note(baseOctave + 1, Note::CS); case JamKey::LowD2: case JamKey::HighD: return Note(baseOctave + 1, Note::D); case JamKey::HighDS: return Note(baseOctave + 1, Note::DS); case JamKey::HighE: return Note(baseOctave + 1, Note::E); case JamKey::HighF: return Note(baseOctave + 1, Note::F); case JamKey::HighFS: return Note(baseOctave + 1, Note::FS); case JamKey::HighG: return Note(baseOctave + 1, Note::G); case JamKey::HighGS: return Note(baseOctave + 1, Note::GS); case JamKey::HighA: return Note(baseOctave + 1, Note::A); case JamKey::HighAS: return Note(baseOctave + 1, Note::AS); case JamKey::HighB: return Note(baseOctave + 1, Note::B); case JamKey::HighC2: return Note(baseOctave + 2, Note::C); case JamKey::HighCS2: return Note(baseOctave + 2, Note::CS); case JamKey::HighD2: return Note(baseOctave + 2, Note::D); default: throw std::invalid_argument("invalid jam key"); } } } JamManager::JamManager() : isJamMode_(true), isPoly_(true) { reset(); } std::vector JamManager::keyOn(JamKey key, int channel, SoundSource source, int keyNum) { std::vector keyDataList; JamKeyInfo onData{ key, channel, source, keyNum }; std::deque& unusedCh = unusedCh_.at(source); if (!unusedCh.empty()) { if (key == JamKey::MidiKey) { if (isPoly_) onData.channelInSource = unusedCh.front(); unusedCh.pop_front(); keyOnTable_.push_back(onData); keyDataList.push_back(onData); } else { auto&& it = utils::findIf(keyOnTable_, [&](JamKeyInfo x) { return (x.source == source && x.key == key); }); if (it == keyOnTable_.end()) { if (isPoly_) onData.channelInSource = unusedCh.front(); unusedCh.pop_front(); keyOnTable_.push_back(onData); keyDataList.push_back(onData); } else { JamKeyInfo del = *it; if (isPoly_) onData.channelInSource = del.channelInSource; keyDataList.push_back(onData); keyDataList.push_back(del); keyOnTable_.erase(it); keyOnTable_.push_back(onData); } } } else { auto&& it = utils::findIf(keyOnTable_, [&](JamKeyInfo x) { return (x.source == source); }); JamKeyInfo del = *it; if (isPoly_) onData.channelInSource = del.channelInSource; keyDataList.push_back(onData); keyDataList.push_back(del); keyOnTable_.erase(it); keyOnTable_.push_back(onData); } return keyDataList; } JamKeyInfo JamManager::keyOff(JamKey key, int keyNum) { JamKeyInfo keyData; auto cond = (key == JamKey::MidiKey) ? std::function([&](JamKeyInfo x) -> bool { return (x.key == JamKey::MidiKey && x.keyNum == keyNum); }) : std::function([&](JamKeyInfo x) -> bool { return (x.key == key); }); auto&& it = utils::findIf(keyOnTable_, cond); if (it == keyOnTable_.end()) { keyData.channelInSource = -1; // Already released } else { JamKeyInfo data = *it; keyData.channelInSource = data.channelInSource; keyData.key = key; keyData.source = data.source; unusedCh_.at(keyData.source).push_front(keyData.channelInSource); keyOnTable_.erase(it); } return keyData; } void JamManager::reset() { if (isPoly_) { unusedCh_[SoundSource::FM] = std::deque(6); unusedCh_[SoundSource::SSG] = std::deque(3); unusedCh_[SoundSource::ADPCM] = std::deque(1); for (auto& pair : unusedCh_) std::iota(pair.second.begin(), pair.second.end(), 0); } else { for (auto& pair : unusedCh_) pair.second.resize(1); } } BambooTracker-0.4.6/BambooTracker/jamming.hpp000066400000000000000000000045421401124043500211010ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "enum_hash.hpp" #include "bamboo_tracker_defs.hpp" class Note; enum class JamKey { LowC, LowCS, LowD, LowDS, LowE, LowF, LowFS, LowG, LowGS, LowA, LowAS, LowB, LowC2, LowCS2, LowD2, HighC, HighCS, HighD, HighDS, HighE, HighF, HighFS, HighG, HighGS, HighA, HighAS, HighB, HighC2, HighCS2, HighD2, MidiKey }; struct JamKeyInfo { JamKey key; int channelInSource; SoundSource source; int keyNum; }; namespace jam_utils { Note makeNote(const JamKeyInfo& info, int baseOctave); Note makeNote(int baseOctave, JamKey key); } class JamManager { public: JamManager(); bool toggleJamMode(); bool isJamMode() const noexcept { return isJamMode_; } void polyphonic(bool flag); std::vector keyOn(JamKey key, int channel, SoundSource source, int keyNum); JamKeyInfo keyOff(JamKey key, int keyNum); void reset(); private: bool isJamMode_; bool isPoly_; std::vector keyOnTable_; std::unordered_map> unusedCh_; }; //=============================================== inline bool JamManager::toggleJamMode() { isJamMode_ = !isJamMode_; return isJamMode_; } inline void JamManager::polyphonic(bool flag) { isPoly_ = flag; reset(); } BambooTracker-0.4.6/BambooTracker/main.cpp000066400000000000000000000112331401124043500203710ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "./gui/mainwindow.hpp" #include #include #include #include #include #include #include #include #include #include #include "configuration.hpp" #include "gui/q_application_wrapper.hpp" #include "gui/configuration_handler.hpp" namespace { // Localization static void setupTranslations(); static QString findQtTranslationsDir(); static QString findAppTranslationsDir(); } int main(int argc, char* argv[]) { try { std::shared_ptr config = std::make_shared(); io::loadConfiguration(config); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif std::unique_ptr a(std::make_unique(argc, argv)); if (config->getEnableTranslation()) setupTranslations(); a->setWindowIcon(QIcon(":/icon/app_icon")); QString filePath = (argc > 1) ? argv[argc - 1] : ""; // Last argument file std::unique_ptr w(std::make_unique(config, filePath)); w->show(); int ret = a->exec(); io::saveConfiguration(config); if (ret) QMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("An unknown error occurred.")); return ret; } catch (std::exception& e) { QMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("An unknown error occurred.\n%1").arg(e.what())); return 1; } } namespace { // Sets up the translation according to the current language void setupTranslations() { QApplication *a = qApp; const QString lang = QLocale::system().name(); QTranslator *qtTr = new QTranslator(a); QTranslator *appTr = new QTranslator(a); QString qtDir = findQtTranslationsDir(); QString appDir = findAppTranslationsDir(); if (!qtDir.isEmpty()) { QString baseName = "qt_" + lang; qtTr->load(baseName, qtDir); qDebug() << "Translation" << baseName << "from" << qtDir; } if (!appDir.isEmpty()) { QString baseName = "bamboo_tracker_" + lang; appTr->load(baseName, appDir); qDebug() << "Translation" << baseName << "from" << appDir; } a->installTranslator(qtTr); a->installTranslator(appTr); } // Finds the location of Qt translation catalogs QString findQtTranslationsDir() { #if defined(Q_OS_DARWIN) // if this is macOS, attempt to load from inside an app bundle QString pathInAppBundle = QApplication::applicationDirPath() + "/../Resources/lang"; if (QDir(pathInAppBundle).exists()) return pathInAppBundle; #endif #if defined(Q_OS_WIN) // if this is Windows, translations should be distributed with the program return QApplication::applicationDirPath() + "/lang"; #else // the files are located in the installation of Qt return QLibraryInfo::location(QLibraryInfo::TranslationsPath); #endif } // Finds the location of our translation catalogs QString findAppTranslationsDir() { #ifndef QT_NO_DEBUG // if this is a debug build, attempt to load from the source directory QString pathInSources = QApplication::applicationDirPath() + "/.qm"; if (QDir(pathInSources).exists()) return pathInSources; #endif #if defined(Q_OS_DARWIN) // if this is macOS, attempt to load from inside an app bundle QString pathInAppBundle = QApplication::applicationDirPath() + "/../Resources/lang"; if (QDir(pathInAppBundle).exists()) return pathInAppBundle; #endif #if defined(Q_OS_WIN) // if this is Windows, translations should be distributed with the program return QApplication::applicationDirPath() + "/lang"; #else return QApplication::applicationDirPath() + "/../share/BambooTracker/lang"; #endif } } BambooTracker-0.4.6/BambooTracker/midi/000077500000000000000000000000001401124043500176635ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/midi/midi.cpp000066400000000000000000000167341401124043500213240ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "midi.hpp" #include #include "RtMidi.h" #include "utils.hpp" namespace { // Defines constexpr int MIDI_BUFFER_SIZE = 8192; const std::string MIDI_INP_CLIENT_NAME = "BambooTracker Rx"; const std::string MIDI_INP_PORT_NAME = "BambooTracker MIDI In"; constexpr bool MIDI_INP_IGNORE_SYSEX = false; constexpr bool MIDI_INP_IGNORE_TIME = false; constexpr bool MIDI_INP_IGNORE_SENSE = true; } std::unique_ptr MidiInterface::instance_; MidiInterface& MidiInterface::getInstance() { if (instance_) return *instance_; MidiInterface *out = new MidiInterface; instance_.reset(out); return *out; } MidiInterface::MidiInterface() : hasOpenInputPort_(false) {} MidiInterface::~MidiInterface() = default; std::string MidiInterface::currentApiName() const { RtMidi::Api api = inputClient_ ? inputClient_->getCurrentApi() : RtMidi::RTMIDI_DUMMY; return RtMidi::getApiDisplayName(api); } std::vector MidiInterface::getAvailableApis() const { std::vector apis; RtMidi::getCompiledApi(apis); std::vector list; for (const auto& apiAvailable : apis) list.push_back(RtMidi::getApiDisplayName(apiAvailable)); return (list.empty() ? std::vector({ "" }) : list); } bool MidiInterface::isAvailableApi(const std::string& api) const { const std::vector apis = getAvailableApis(); return (utils::find(apis, api) != apis.end()); } bool MidiInterface::switchApi(std::string api, std::string* errDetail) { std::vector apis; RtMidi::getCompiledApi(apis); for (const auto& apiAvailable : apis) { if (api == RtMidi::getApiDisplayName(apiAvailable)) { if (inputClient_ && apiAvailable == inputClient_->getCurrentApi()) return true; RtMidiIn *inputClient = nullptr; try { inputClient = new RtMidiIn(apiAvailable, MIDI_INP_CLIENT_NAME, MIDI_BUFFER_SIZE); if (errDetail) *errDetail = ""; } catch (RtMidiError &error) { error.printMessage(); if (errDetail) *errDetail = error.getMessage(); } if (inputClient) { inputClient->ignoreTypes(MIDI_INP_IGNORE_SYSEX, MIDI_INP_IGNORE_TIME, MIDI_INP_IGNORE_SENSE); inputClient->setCallback(&onMidiInput, this); } inputClient_.reset(inputClient); hasOpenInputPort_ = false; return (inputClient != nullptr); } } inputClient_ = nullptr; if (errDetail) *errDetail = "No available midi api."; return false; } bool MidiInterface::supportsVirtualPort() const { if (!inputClient_) return false; switch (inputClient_->getCurrentApi()) { case RtMidi::MACOSX_CORE: case RtMidi::LINUX_ALSA: case RtMidi::UNIX_JACK: return true; default: return false; } } bool MidiInterface::supportsVirtualPort(std::string api) const { std::vector apis; RtMidi::getCompiledApi(apis); RtMidi::Api apiType = RtMidi::RTMIDI_DUMMY; for (const auto& apiAvailable : apis) { if (api == RtMidi::getApiDisplayName(apiAvailable)) { apiType = apiAvailable; break; } } switch (apiType) { case RtMidi::MACOSX_CORE: case RtMidi::LINUX_ALSA: case RtMidi::UNIX_JACK: return true; default: return false; } } std::vector MidiInterface::getRealInputPorts() { if (!inputClient_) return { "" }; // Error RtMidiIn &client = *inputClient_; unsigned count = client.getPortCount(); std::vector ports; ports.reserve(count); for (unsigned i = 0; i < count; ++i) ports.push_back(client.getPortName(i)); return ports; } std::vector MidiInterface::getRealInputPorts(const std::string& api) { std::vector apis; RtMidi::getCompiledApi(apis); RtMidi::Api apiType = RtMidi::RTMIDI_DUMMY; for (const auto& apiAvailable : apis) { if (api == RtMidi::getApiDisplayName(apiAvailable)) { apiType = apiAvailable; break; } } if (apiType == RtMidi::RTMIDI_DUMMY) { return { "" }; // Error } std::vector ports; try { auto client = std::make_unique(apiType); unsigned count = client->getPortCount(); ports.reserve(count); for (unsigned i = 0; i < count; ++i) ports.push_back(client->getPortName(i)); } catch (RtMidiError& error) { error.printMessage(); } return ports; } void MidiInterface::closeInputPort() { if (!inputClient_) return; RtMidiIn &client = *inputClient_; if (hasOpenInputPort_) { client.closePort(); hasOpenInputPort_ = false; } } bool MidiInterface::openInputPort(unsigned port, std::string* errDetail) { if (!inputClient_) { if (errDetail) *errDetail = "Not opened input client."; hasOpenInputPort_ = false; return false; } try { RtMidiIn &client = *inputClient_; closeInputPort(); if (port == ~0u) { client.openVirtualPort(MIDI_INP_PORT_NAME); hasOpenInputPort_ = true; } else { client.openPort(port, MIDI_INP_PORT_NAME); hasOpenInputPort_ = client.isPortOpen(); } if (errDetail) *errDetail = ""; return true; } catch (RtMidiError& error) { if (errDetail) *errDetail = error.getMessage(); hasOpenInputPort_ = false; return false; } } bool MidiInterface::openInputPortByName(const std::string &portName, std::string* errDetail) { std::vector ports = getRealInputPorts(); for (unsigned i = 0, n = ports.size(); i < n; ++i) { if (ports[i] == portName) { return openInputPort(i, errDetail);; } } if (errDetail) *errDetail = "There is no port such the name."; return false; } void MidiInterface::installInputHandler(InputHandler* handler, void* user_data) { std::lock_guard lock(inputHandlersMutex_); inputHandlers_.push_back(std::make_pair(handler, user_data)); } void MidiInterface::uninstallInputHandler(InputHandler* handler, void* user_data) { std::lock_guard lock(inputHandlersMutex_); for (size_t i = 0, n = inputHandlers_.size(); i < n; ++i) { bool match = inputHandlers_[i].first == handler && inputHandlers_[i].second == user_data; if (match) { inputHandlers_.erase(inputHandlers_.begin() + i); return; } } } void MidiInterface::onMidiInput(double timestamp, std::vector *message, void *user_data) { MidiInterface *self = reinterpret_cast(user_data); std::unique_lock lock(self->inputHandlersMutex_, std::try_to_lock); if (!lock.owns_lock()) return; const uint8_t *msg = message->data(); size_t len = message->size(); for (size_t i = 0, n = self->inputHandlers_.size(); i < n; ++i) self->inputHandlers_[i].first(timestamp, msg, len, self->inputHandlers_[i].second); } BambooTracker-0.4.6/BambooTracker/midi/midi.hpp000066400000000000000000000046761401124043500213330ustar00rootroot00000000000000/* * Copyright (C) 2019-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include #include "RtMidi.h" class RtMidiIn; class MidiInterface { public: static MidiInterface& getInstance(); ~MidiInterface(); std::string currentApiName() const; std::vector getAvailableApis() const; bool isAvailableApi(const std::string& api) const; bool switchApi(std::string api, std::string* errDetail = nullptr); bool supportsVirtualPort() const; bool supportsVirtualPort(std::string api) const; std::vector getRealInputPorts(); std::vector getRealInputPorts(const std::string& api); void closeInputPort(); bool openInputPort(unsigned port, std::string* errDetail = nullptr); bool openInputPortByName(const std::string &portName, std::string* errDetail = nullptr); using InputHandler = void(double, const uint8_t*, size_t, void*); void installInputHandler(InputHandler *handler, void *userData); void uninstallInputHandler(InputHandler *handler, void *userData); private: static std::unique_ptr instance_; std::unique_ptr inputClient_; bool hasOpenInputPort_; std::mutex inputHandlersMutex_; std::vector> inputHandlers_; MidiInterface(); static void onMidiInput(double timestamp, std::vector *message, void *userData); }; BambooTracker-0.4.6/BambooTracker/module/000077500000000000000000000000001401124043500202265ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/module/effect.cpp000066400000000000000000000161771401124043500222020ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "effect.hpp" #include #include "step.hpp" namespace { const std::unordered_map CTOHEX = { { '0', 0 }, { '1', 1 }, { '2', 2 }, { '3', 3 }, { '4', 4 }, { '5', 5 }, { '6', 6 }, { '7', 7 }, { '8', 8 }, { '9', 9 }, { 'A', 10 }, { 'B', 11 }, { 'C', 12 }, { 'D', 13 }, { 'E', 14 }, { 'F', 15 } }; } namespace effect_utils { EffectType validateEffectId(SoundSource src, const std::string& id) { if (id == "00") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::Arpeggio; default: return EffectType::NoEffect; } } else if (id == "01") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::PortamentoUp; default: return EffectType::NoEffect; } } else if (id == "02") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::PortamentoDown; default: return EffectType::NoEffect; } } else if (id == "03") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::TonePortamento; default: return EffectType::NoEffect; } } else if (id == "04") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::Vibrato; default: return EffectType::NoEffect; } } else if (id == "07") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::Tremolo; default: return EffectType::NoEffect; } } else if (id == "08") { switch (src) { case SoundSource::FM: case SoundSource::RHYTHM: case SoundSource::ADPCM: return EffectType::Pan; default: return EffectType::NoEffect; } } else if (id == "0A") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::VolumeSlide; default: return EffectType::NoEffect; } } else if (id == "0B") { return EffectType::PositionJump; } else if (id == "0C") { return EffectType::SongEnd; } else if (id == "0D") { return EffectType::PatternBreak; } else if (id == "0F") { return EffectType::SpeedTempoChange; } else if (id == "0G") { return EffectType::NoteDelay; } else if (id == "0H") { switch (src) { case SoundSource::SSG: return EffectType::AutoEnvelope; default: return EffectType::NoEffect; } } else if (id == "0I") { switch (src) { case SoundSource::SSG: return EffectType::HardEnvHighPeriod; default: return EffectType::NoEffect; } } else if (id == "0J") { switch (src) { case SoundSource::SSG: return EffectType::HardEnvLowPeriod; default: return EffectType::NoEffect; } } else if (id == "0O") { return EffectType::Groove; } else if (id == "0P") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::Detune; default: return EffectType::NoEffect; } } else if (id == "0Q") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::NoteSlideUp; default: return EffectType::NoEffect; } } else if (id == "0R") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::NoteSlideDown; default: return EffectType::NoEffect; } } else if (id == "0S") { return EffectType::NoteCut; } else if (id == "0T") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::TransposeDelay; default: return EffectType::NoEffect; } } else if (id == "0V") { switch (src) { case SoundSource::SSG: return EffectType::ToneNoiseMix; case SoundSource::RHYTHM: return EffectType::MasterVolume; default: return EffectType::NoEffect; } } else if (id == "0W") { switch (src) { case SoundSource::SSG: return EffectType::NoisePitch; default: return EffectType::NoEffect; } } else if (id == "0X") { return EffectType::RegisterAddress0; } else if (id == "0Y") { return EffectType::RegisterAddress1; } else if (id == "0Z") { return EffectType::RegisterValue; } else if (id == "B0") { switch (src) { case SoundSource::FM: return EffectType::Brightness; default: return EffectType::NoEffect; } } else if (id == "FB") { switch (src) { case SoundSource::FM: return EffectType::FBControl; default: return EffectType::NoEffect; } } else if (id == "FP") { switch (src) { case SoundSource::FM: case SoundSource::SSG: case SoundSource::ADPCM: return EffectType::FineDetune; default: return EffectType::NoEffect; } } else if (id == "ML") { switch (src) { case SoundSource::FM: return EffectType::MLControl; default: return EffectType::NoEffect; } } else if (id == "RR") { switch (src) { case SoundSource::FM: return EffectType::RRControl; default: return EffectType::NoEffect; } } else { switch (id.front()) { case 'A': switch (src) { case SoundSource::FM: return EffectType::ARControl; default: return EffectType::NoEffect; } case 'D': switch (src) { case SoundSource::FM: return EffectType::DRControl; default: return EffectType::NoEffect; } case 'M': return EffectType::VolumeDelay; case 'T': switch (src) { case SoundSource::FM: return EffectType::TLControl; default: return EffectType::NoEffect; } default: return EffectType::NoEffect; } } } Effect validateEffect(SoundSource src, const std::string& id, int value) { if (value == Step::EFF_VAL_NONE) return { EffectType::NoEffect, Step::EFF_VAL_NONE }; EffectType type = effect_utils::validateEffectId(src, id); int v; switch (type) { case EffectType::NoEffect: v = Step::EFF_VAL_NONE; break; case EffectType::VolumeDelay: case EffectType::TLControl: case EffectType::ARControl: case EffectType::DRControl: v = (CTOHEX.at(id[1]) << 8) | value; break; default: v = value; } return { type, v }; } } BambooTracker-0.4.6/BambooTracker/module/effect.hpp000066400000000000000000000045171401124043500222020ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include "step.hpp" #include "bamboo_tracker_defs.hpp" enum class EffectType { NoEffect, Arpeggio, PortamentoUp, PortamentoDown, TonePortamento, Vibrato, Tremolo, Pan, VolumeSlide, PositionJump, SongEnd, PatternBreak, SpeedTempoChange, NoteDelay, Groove, Detune, NoteSlideUp, NoteSlideDown, NoteCut, TransposeDelay, MasterVolume, VolumeDelay, ToneNoiseMix, NoisePitch, HardEnvHighPeriod, HardEnvLowPeriod, AutoEnvelope, FBControl, TLControl, MLControl, ARControl, DRControl, RRControl, RegisterAddress0, RegisterAddress1, RegisterValue, Brightness, FineDetune }; struct Effect { EffectType type; int value; }; namespace effect_utils { EffectType validateEffectId(SoundSource src, const std::string& id); Effect validateEffect(SoundSource src, const std::string& id, int value); inline Effect validateEffect(SoundSource src, const Step::PlainEffect& plain) { return validateEffect(src, plain.id, plain.value); } inline int reverseFmVolume(int volume, bool over0 = false) noexcept { return (volume < bt_defs::NSTEP_FM_VOLUME) ? (bt_defs::NSTEP_FM_VOLUME - 1 - volume) : over0 ? 0 : volume; } inline int reverseFmBrightness(int value) noexcept { return (value > 0) ? (0xff - value + 1) : value; } } BambooTracker-0.4.6/BambooTracker/module/module.cpp000066400000000000000000000074101401124043500222210ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "module.hpp" #include #include #include "utils.hpp" Module::Module(const std::string& filePath, const std::string& title, const std::string& author, const std::string& copyright, const std::string& comment, unsigned int tickFreq) : filePath_(filePath), title_(title), author_(author), copyright_(copyright), comment_(comment), tickFreq_(tickFreq), stepHl1Dist_(4), stepHl2Dist_(16), mixType_(MixerType::PC_9821_PC_9801_86), customLevelFM_(0), customLevelSSG_(0) { songs_.emplace_back(0); addGroove(); } size_t Module::getSongCount() const { return songs_.size(); } size_t Module::getGrooveCount() const { return grooves_.size(); } void Module::addSong(SongType songType, const std::string& title) { songs_.emplace_back(static_cast(songs_.size()), songType, title); } void Module::addSong(int n, SongType songType, const std::string& title, bool isUsedTempo, int tempo, int groove, int speed, size_t defaultPatternSize) { if (n < static_cast(songs_.size())) songs_.at(static_cast(n)) = Song(n, songType, title, isUsedTempo, tempo, groove, speed, defaultPatternSize); else songs_.emplace_back( n, songType, title, isUsedTempo, tempo, groove, speed, defaultPatternSize); } void Module::sortSongs(const std::vector& numbers) { std::vector newSongs; newSongs.reserve(songs_.size()); for (auto& n : numbers) { auto it = std::make_move_iterator(songs_.begin() + n); it->setNumber(static_cast(newSongs.size())); newSongs.push_back(*it); } songs_ = std::move(newSongs); } Song& Module::getSong(int num) { return *utils::findIf(songs_, [num](Song& s) { return s.getNumber() == num; });; } void Module::addGroove() { // Default groove is "6 6" grooves_.push_back({ 6, 6 }); } void Module::removeGroove(int num) { grooves_.erase(grooves_.begin() + num); } void Module::setGroove(int num, const std::vector& seq) { grooves_.at(static_cast(num)) = seq; } void Module::setGrooves(const std::vector>& seqs) { grooves_ = seqs; } Groove Module::getGroove(int num) const { return grooves_.at(static_cast(num)); } std::set Module::getRegisterdInstruments() const { std::set set; for (const Song& song : songs_) { auto&& subset = song.getRegisteredInstruments(); std::copy(subset.begin(), subset.end(), std::inserter(set, set.end())); } return set; } void Module::clearUnusedPatterns() { for (Song& song : songs_) song.clearUnusedPatterns(); } void Module::replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map) { for (Song& song : songs_) song.replaceDuplicateInstrumentsInPatterns(map); } BambooTracker-0.4.6/BambooTracker/module/module.hpp000066400000000000000000000103421401124043500222240ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include "song.hpp" using Groove = std::vector; enum class MixerType : int { UNSPECIFIED = 0, CUSTOM = 1, PC_9821_PC_9801_86 = 2, PC_9821_SPEAK_BOARD = 3, PC_8801_VA2 = 4, PC_8801_MKII_SR = 5 }; class Module { public: Module(const std::string& filePath = "", const std::string& title = u8"", const std::string& author = u8"", const std::string& copyright = u8"", const std::string& comment = u8"", unsigned int tickFreq = 60); inline void setFilePath(const std::string& path) { filePath_ = path; } std::string getFilePath() const noexcept { return filePath_; }; inline void setTitle(const std::string& title) { title_ = title; } inline std::string getTitle() const noexcept { return title_; } inline void setAuthor(const std::string& author) { author_ = author; } inline std::string getAuthor() const noexcept { return author_; } inline void setCopyright(const std::string& copyright) { copyright_ = copyright; } inline std::string getCopyright() const noexcept { return copyright_; } inline void setComment(const std::string& comment) { comment_ = comment; } inline std::string getComment() const noexcept { return comment_; } inline void setTickFrequency(unsigned int freq) { tickFreq_ = freq; } inline unsigned int getTickFrequency() const noexcept { return tickFreq_; } inline void setStepHighlight1Distance(size_t dist) { stepHl1Dist_ = dist; } inline size_t getStepHighlight1Distance() const noexcept { return stepHl1Dist_; } inline void setStepHighlight2Distance(size_t dist) { stepHl2Dist_ = dist; } inline size_t getStepHighlight2Distance() const noexcept { return stepHl2Dist_; } size_t getSongCount() const; size_t getGrooveCount() const; inline void setMixerType(MixerType type) { mixType_ = type; } inline MixerType getMixerType() const noexcept { return mixType_; } inline void setCustomMixerFMLevel(double level) { customLevelFM_ = level; } inline double getCustomMixerFMLevel() const noexcept { return customLevelFM_; } inline void setCustomMixerSSGLevel(double level) { customLevelSSG_ = level; } inline double getCustomMixerSSGLevel() const noexcept { return customLevelSSG_; } void addSong(SongType songType, const std::string& title); void addSong(int n, SongType songType, const std::string& title, bool isUsedTempo, int tempo, int groove, int speed, size_t defaultPatternSize); void sortSongs(const std::vector& numbers); Song& getSong(int num); void addGroove(); void removeGroove(int num); void setGroove(int num, const std::vector& seq); void setGrooves(const std::vector>& seqs); Groove getGroove(int num) const; std::set getRegisterdInstruments() const; void clearUnusedPatterns(); void replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map); private: std::string filePath_; std::string title_; std::string author_; std::string copyright_; std::string comment_; unsigned int tickFreq_; size_t stepHl1Dist_, stepHl2Dist_; std::vector songs_; std::vector grooves_; MixerType mixType_; double customLevelFM_, customLevelSSG_; }; BambooTracker-0.4.6/BambooTracker/module/pattern.cpp000066400000000000000000000072521401124043500224150ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "pattern.hpp" #include #include "effect.hpp" #include "note.hpp" #include "utils.hpp" namespace { constexpr size_t MAX_STEP_SIZE = 256; } Pattern::Pattern(int n, size_t defSize) : num_(n), size_(defSize), steps_(defSize), usedCnt_(0) { } Pattern::Pattern(int n, size_t size, const std::vector& steps) : num_(n), size_(size), steps_(steps), usedCnt_(0) { } Step& Pattern::getStep(int n) { return steps_.at(static_cast(n)); } size_t Pattern::getSize() const { for (size_t i = 0; i < size_; ++i) { for (int j = 0; j < Step::N_EFFECT; ++j) { if (!steps_[i].hasEffectValue(j)) continue; // "SoundSource::FM" is dummy, these effects are not related with sound source switch (effect_utils::validateEffectId(SoundSource::FM, steps_[i].getEffectId(j))) { case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: return i + 1; default: break; } } } return size_; } void Pattern::changeSize(size_t size) { if (size && size <= MAX_STEP_SIZE) { size_ = size; if (steps_.size() < size) steps_.resize(size); } } void Pattern::insertStep(int n) { if (n < static_cast(size_)) steps_.emplace(steps_.begin() + n); } void Pattern::deletePreviousStep(int n) { if (!n) return; steps_.erase(steps_.begin() + n - 1); if (steps_.size() < size_) steps_.resize(size_); } bool Pattern::hasEvent() const { auto endIt = steps_.cbegin() + static_cast(size_); return std::any_of(steps_.cbegin(), endIt, [](const Step& step) { return step.hasEvent(); }); } std::vector Pattern::getEditedStepIndices() const { auto endIt = steps_.cbegin() + static_cast(size_); return utils::findIndicesIf(steps_.cbegin(), endIt, [](const Step& step) { return step.hasEvent(); }); } std::set Pattern::getRegisteredInstruments() const { std::set set; for (size_t i = 0; i < size_; ++i) { const Step& step = steps_.at(i); if (step.hasInstrument()) set.insert(step.getInstrumentNumber()); } return set; } Pattern Pattern::clone(int asNumber) { return Pattern(asNumber, size_, steps_); } void Pattern::transpose(int seminotes, const std::vector& excludeInsts) { for (size_t i = 0; i < size_; ++i) { Step& step = steps_.at(i); int note = step.getNoteNumber(); if (step.hasGeneralNote() && std::none_of(excludeInsts.begin(), excludeInsts.end(), [a = step.getInstrumentNumber()](int b) { return a == b; })) { step.setNoteNumber(utils::clamp(note + seminotes, 0, Note::NOTE_NUMBER_RANGE - 1)); } } } void Pattern::clear() { steps_ = std::vector(size_); } BambooTracker-0.4.6/BambooTracker/module/pattern.hpp000066400000000000000000000040601401124043500224140ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "step.hpp" class Pattern { public: Pattern(int n, size_t defSize); inline void setNumber(int n) noexcept { num_ = n; } inline int getNumber() const noexcept { return num_; } inline int increaseUsedCount() noexcept { return ++usedCnt_; } inline int decreaseUsedCount() noexcept { return --usedCnt_; } inline int getUsedCount() const noexcept { return usedCnt_; } Step& getStep(int n); size_t getSize() const; void changeSize(size_t size); void insertStep(int n); void deletePreviousStep(int n); bool hasEvent() const; std::vector getEditedStepIndices() const; std::set getRegisteredInstruments() const; Pattern clone(int asNumber); void transpose(int seminotes, const std::vector& excludeInsts); void clear(); private: int num_; size_t size_; std::vector steps_; int usedCnt_; Pattern(int n, size_t size, const std::vector& steps); }; BambooTracker-0.4.6/BambooTracker/module/song.cpp000066400000000000000000000213201401124043500216760ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "song.hpp" #include #include #include #include "bamboo_tracker_defs.hpp" #include "utils.hpp" Bookmark::Bookmark(const std::string& argname, int argorder, int argstep) : name(argname), order(argorder), step(argstep) { } Song::Song(int number, SongType songType, const std::string& title, bool isUsedTempo, int tempo, int groove, int speed, size_t defaultPatternSize) : num_(number), type_(songType), title_(title), isUsedTempo_(isUsedTempo), tempo_(tempo), groove_(groove), speed_(speed), defPtnSize_(defaultPatternSize) { switch (songType) { case SongType::Standard: tracks_.reserve(15); for (int i = 0; i < 6; ++i) { tracks_.emplace_back(i, SoundSource::FM, i, defaultPatternSize); } for (int i = 0; i < 3; ++i) { tracks_.emplace_back(i + 6, SoundSource::SSG, i, defaultPatternSize); } for (int i = 0; i < 6; ++i) { tracks_.emplace_back(i + 9, SoundSource::RHYTHM, i, defaultPatternSize); } tracks_.emplace_back(15, SoundSource::ADPCM, 0, defaultPatternSize); break; case SongType::FM3chExpanded: tracks_.reserve(18); for (int i = 0; i < 9; ++i) { int ch = (i < 3) ? i : (i < 6) ? (i + 3) : (i - 3); tracks_.emplace_back(i, SoundSource::FM, ch, defaultPatternSize); } for (int i = 0; i < 3; ++i) { tracks_.emplace_back(i + 9, SoundSource::SSG, i, defaultPatternSize); } for (int i = 0; i < 6; ++i) { tracks_.emplace_back(i + 12, SoundSource::RHYTHM, i, defaultPatternSize); } tracks_.emplace_back(18, SoundSource::ADPCM, 0, defaultPatternSize); break; } } void Song::setDefaultPatternSize(size_t size) { defPtnSize_ = size; for (auto& t : tracks_) { t.changeDefaultPatternSize(size); } } size_t Song::getPatternSizeFromOrderNumber(int order) { if (static_cast(getOrderSize()) <= order) return 0; // Ilegal value size_t size = 0; for (auto& t : tracks_) { size_t ptnSize = t.getPatternFromOrderNumber(order).getSize(); size = !size ? ptnSize : std::min(size, ptnSize); } return size; } SongStyle Song::getStyle() const { SongStyle style; style.type = type_; style.trackAttribs = getTrackAttributes(); return style; } std::vector Song::getTrackAttributes() const { std::vector ret; ret.reserve(tracks_.size()); std::transform(tracks_.begin(), tracks_.end(), std::back_inserter(ret), [](const Track& track) { return track.getAttribute(); }); return ret; } Track& Song::getTrack(int num) { return tracks_.at(static_cast(num)); } void Song::changeType(SongType type) { if (std::exchange(type_, type) == type_) return; switch (type_) { case SongType::Standard: // Previous type: FM3chExpanded // Remove FM3-OP2,3,4 (track 3,4,5) tracks_.erase(tracks_.begin() + 3, tracks_.begin() + 6); for (size_t i = 3; i < tracks_.size(); ++i) { const auto attrib = tracks_[i].getAttribute(); tracks_[i].setAttribute(static_cast(i), attrib.source, attrib.channelInSource); } break; case SongType::FM3chExpanded: // Previous type: Standard // Expand FM3 track for (int i = 3; i < 6; ++i) { Track src = tracks_[2]; src.setAttribute(i, SoundSource::FM, i + 3); tracks_.insert(tracks_.begin() + i, std::move(src)); } for (size_t i = 6; i < tracks_.size(); ++i) { const auto attrib = tracks_[i].getAttribute(); tracks_[i].setAttribute(static_cast(i), attrib.source, attrib.channelInSource); } break; } } std::vector Song::getOrderData(int order) const { std::vector ret; for (const Track& track : tracks_) { ret.push_back(track.getOrderInfo(order)); } return ret; } size_t Song::getOrderSize() const { return tracks_[0].getOrderSize(); } bool Song::canAddNewOrder() const { return tracks_[0].canAddNewOrder(); } void Song::insertOrderBelow(int order) { if (!canAddNewOrder()) return; for (Track& track : tracks_) { track.insertOrderBelow(order); } } void Song::deleteOrder(int order) { for (Track& track : tracks_) { track.deleteOrder(order); } } void Song::swapOrder(int a, int b) { for (Track& track : tracks_) { track.swapOrder(a, b); } } std::set Song::getRegisteredInstruments() const { std::set set; for (const Track& track : tracks_) { auto&& subset = track.getRegisteredInstruments(); std::copy(subset.begin(), subset.end(), std::inserter(set, set.end())); } return set; } void Song::clearUnusedPatterns() { for (Track& track : tracks_) track.clearUnusedPatterns(); } void Song::replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map) { for (Track& track : tracks_) track.replaceDuplicateInstrumentsInPatterns(map); } void Song::transpose(int seminotes, const std::vector& excludeInsts) { for (Track& track : tracks_) track.transpose(seminotes, excludeInsts); } void Song::swapTracks(int track1, int track2) { auto it1 = utils::findIf(tracks_, [&](const Track& t) { return t.getAttribute().number == track1; }); if (it1 == tracks_.end()) throw std::invalid_argument("Invalid track number"); auto it2 = utils::findIf(tracks_, [&](const Track& t) { return t.getAttribute().number == track2; }); if (it2 == tracks_.end()) throw std::invalid_argument("Invalid track number"); TrackAttribute attrib1 = it1->getAttribute(); TrackAttribute attrib2 = it2->getAttribute(); it1->setAttribute(attrib2.number, attrib2.source, attrib2.channelInSource); it2->setAttribute(attrib1.number, attrib1.source, attrib1.channelInSource); std::iter_swap(it1, it2); } int Song::addBookmark(const std::string& name, int order, int step) { bms_.push_back(Bookmark(name, order, step)); return static_cast(bms_.size() - 1); } void Song::changeBookmark(int i, const std::string& name, int order, int step) { Bookmark& bm = bms_.at(static_cast(i)); bm.name = name; bm.order = order; bm.step = step; } void Song::removeBookmark(int i) { bms_.erase(bms_.begin() + i); } void Song::clearBookmark() { bms_.clear(); } void Song::swapBookmarks(int a, int b) { std::swap(bms_.at(static_cast(a)), bms_.at((static_cast(b)))); } void Song::sortBookmarkByPosition() { std::stable_sort(bms_.begin(), bms_.end(), [](Bookmark a, Bookmark b) { return ((a.order == b.order) ? (a.step < b.step) : (a.order < b.order)); }); } void Song::sortBookmarkByName() { std::stable_sort(bms_.begin(), bms_.end(), [](Bookmark a, Bookmark b) { return (a.name < b.name); }); } Bookmark Song::getBookmark(int i) const { return bms_.at(static_cast(i)); } std::vector Song::findBookmarks(int order, int step) const { std::vector idcs; for (size_t i = 0; i < bms_.size(); ++i) { const Bookmark& bm = bms_[i]; if (bm.order == order && bm.step == step) idcs.push_back(static_cast(i)); } return idcs; } std::vector Song::getSortedBookmarkList() const { std::vector tmp(bms_); std::stable_sort(tmp.begin(), tmp.end(), [](Bookmark a, Bookmark b) { return ((a.order == b.order) ? (a.step < b.step) : (a.order < b.order)); }); return tmp; } Bookmark Song::getPreviousBookmark(int order, int step) const { std::vector list = getSortedBookmarkList(); size_t i = 0; for (; i < list.size(); ++i) { Bookmark& bm = list.at(i); if (order < bm.order || (order == bm.order && step <= bm.step)) { break; } } return list.at((list.size() + i - 1) % list.size()); } Bookmark Song::getNextBookmark(int order, int step) const { std::vector list = getSortedBookmarkList(); size_t i = 0; for (; i < list.size(); ++i) { Bookmark& bm = list.at(i); if (order < bm.order || (order == bm.order && step < bm.step)) { break; } } return list.at(i % list.size()); } size_t Song::getBookmarkSize() const { return bms_.size(); } BambooTracker-0.4.6/BambooTracker/module/song.hpp000066400000000000000000000104001401124043500217000ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include "track.hpp" enum class SongType { Standard, FM3chExpanded }; struct SongStyle { SongType type; std::vector trackAttribs; // Always sorted by number }; struct Bookmark { std::string name = u8""; int order, step; Bookmark(const std::string& argname, int argorder, int argstep); }; class Song { public: Song(int number, SongType songType = SongType::Standard, const std::string& title = u8"", bool isUsedTempo = true, int tempo = 150, int groove = 0, int speed = 6, size_t defaultPatternSize = 64); inline void setNumber(int n) noexcept { num_ = n; } inline int getNumber() const noexcept { return num_; } inline void setTitle(const std::string& title) { title_ = title; } inline std::string getTitle() const noexcept { return title_; } inline void setTempo(int tempo) noexcept { tempo_ = tempo; } inline int getTempo() const noexcept { return tempo_; } inline void setGroove(int groove) noexcept { groove_ = groove; } inline int getGroove() const noexcept { return groove_; } inline void toggleTempoOrGroove(bool isUsedTempo) noexcept { isUsedTempo_ = isUsedTempo; } inline bool isUsedTempo() const noexcept { return isUsedTempo_; } inline void setSpeed(int speed) noexcept { speed_ = speed; } inline int getSpeed() const noexcept { return speed_; } void setDefaultPatternSize(size_t size); inline size_t getDefaultPatternSize() const noexcept { return defPtnSize_; } size_t getPatternSizeFromOrderNumber(int order); SongStyle getStyle() const; std::vector getTrackAttributes() const; Track& getTrack(int num); void changeType(SongType type); std::vector getOrderData(int order) const; size_t getOrderSize() const; bool canAddNewOrder() const; void insertOrderBelow(int order); void deleteOrder(int order); void swapOrder(int a, int b); std::set getRegisteredInstruments() const; void clearUnusedPatterns(); void replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map); void transpose(int seminotes, const std::vector& excludeInsts); void swapTracks(int track1, int track2); // Bookmark int addBookmark(const std::string& name, int order, int step); void changeBookmark(int i, const std::string& name, int order, int step); void removeBookmark(int i); void clearBookmark(); void swapBookmarks(int a, int b); void sortBookmarkByPosition(); void sortBookmarkByName(); Bookmark getBookmark(int i) const; std::vector findBookmarks(int order, int step) const; Bookmark getPreviousBookmark(int order, int step) const; Bookmark getNextBookmark(int order, int step) const; size_t getBookmarkSize() const; inline static size_t getFMChannelCount(SongType type) { switch (type) { case SongType::Standard: return 6; case SongType::FM3chExpanded: return 9; default: throw std::invalid_argument("Invalid SongType."); } } private: int num_; SongType type_; std::string title_; bool isUsedTempo_; int tempo_; int groove_; int speed_; size_t defPtnSize_; std::vector tracks_; std::vector bms_; std::vector getSortedBookmarkList() const; }; BambooTracker-0.4.6/BambooTracker/module/step.cpp000066400000000000000000000033721401124043500217120ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "step.hpp" const std::string Step::EFF_ID_NONE = "--"; Step::Step() : note_(NOTE_NONE), inst_(INST_NONE), vol_(VOLUME_NONE) { for (size_t i = 0; i < N_EFFECT; ++i) { eff_[i].id = EFF_ID_NONE; eff_[i].value = EFF_VAL_NONE; } } void Step::clear() { clearNoteNumber(); clearInstrumentNumber(); clearVolume(); for (size_t i = 0; i < N_EFFECT; ++i) { clearEffect(i); } } bool Step::hasEvent() const { if (!isEmptyNote()) return true; if (hasInstrument()) return true; if (hasVolume()) return true; for (int i = 0; i < N_EFFECT; ++i) { if (hasEffectId(i)) return true; if (hasEffectValue(i)) return true; } return false; } BambooTracker-0.4.6/BambooTracker/module/step.hpp000066400000000000000000000110711401124043500217120ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include class Step { public: Step(); enum NoteValue : int { NOTE_NONE = -1, // Special notes NOTE_KEY_OFF = -2, NOTE_ECHO0 = -3, NOTE_ECHO1 = -4, NOTE_ECHO2 = -5, NOTE_ECHO3 = -6 }; inline int getNoteNumber() const noexcept { return note_; } inline void setNoteNumber(int num) { note_ = num; } inline void setKeyOff() { note_ = NOTE_KEY_OFF; } inline void setEchoBuffer(int n) { note_ = NOTE_ECHO0 - n; } inline void clearNoteNumber() noexcept { note_ = NOTE_NONE; } inline bool hasGeneralNote() const noexcept { return note_ > NOTE_NONE; } inline bool hasKeyOff() const noexcept { return note_ == NOTE_KEY_OFF; } inline bool hasNoteEchoBuffer(int n) const { return note_ == (NOTE_ECHO0 - n); } inline bool isEmptyNote() const noexcept { return note_ == NOTE_NONE; } static inline bool testEmptyNote(int note) { return note == NOTE_NONE; } static constexpr int INST_NONE = -1; inline int getInstrumentNumber() const noexcept { return inst_; } inline void setInstrumentNumber(int num) { inst_ = num; } inline void clearInstrumentNumber() noexcept { inst_ = INST_NONE; } inline bool hasInstrument() const noexcept { return inst_ != INST_NONE; } static inline bool testEmptyInstrument(int inst) { return inst == INST_NONE; } static constexpr int VOLUME_NONE = -1; inline int getVolume() const noexcept { return vol_; } inline void setVolume(int volume) { vol_ = volume; } inline void clearVolume() noexcept { vol_ = VOLUME_NONE; } inline bool hasVolume() const noexcept { return vol_ != VOLUME_NONE; } static inline bool testEmptyVolume(int vol) { return vol == VOLUME_NONE; } static const std::string EFF_ID_NONE; // "--" inline std::string getEffectId(int n) const { return eff_[n].id; } inline void setEffectId(int n, const std::string& str) { eff_[n].id = str; } inline void clearEffectId(int n) { eff_[n].id = EFF_ID_NONE; } inline bool hasEffectId(int n) const { return eff_[n].id != EFF_ID_NONE; } static inline bool testEmptyEffectId(const std::string& id) { return id == EFF_ID_NONE; } static constexpr int EFF_VAL_NONE = -1; inline int getEffectValue(int n) const { return eff_[n].value; } inline void setEffectValue(int n, int v) { eff_[n].value = v; } inline void clearEffectValue(int n) { eff_[n].value = EFF_VAL_NONE; } inline bool hasEffectValue(int n) const { return eff_[n].value != EFF_VAL_NONE; } static inline bool testEmptyEffectValue(int v) { return v == EFF_VAL_NONE; } struct PlainEffect { std::string id; int value; }; static constexpr int N_EFFECT = 4; inline PlainEffect getEffect(int n) const { return eff_[n]; } inline void setEffect(int n, const PlainEffect& effect) { eff_[n] = effect; } inline void setEffect(int n, const std::string& id, int value) { setEffectId(n, id); setEffectValue(n, value); } inline void clearEffect(int n) { clearEffectId(n); clearEffectValue(n); } static constexpr int N_COLUMN = 3 + N_EFFECT * 2; void clear(); bool hasEvent() const; private: /// noteNum_ /// 0<=: note number (key on) /// -1: none /// -2: key off /// -3: echo previous note /// -4: echo 2 notes before /// -5: echo 3 notes before /// -6: echo 4 notes before int note_; /// instNum_ /// 0<=: instrument number /// -1: none int inst_; /// vol_ /// 0<=: volume level /// -1: none int vol_; /// eff /// [id] /// "--": none /// other: effect id /// [value] /// 0<=: effect value /// -1: none PlainEffect eff_[N_EFFECT]; }; BambooTracker-0.4.6/BambooTracker/module/track.cpp000066400000000000000000000115701401124043500220420ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "track.hpp" #include #include #include "utils.hpp" namespace { constexpr int PATTERN_SIZE = 256; constexpr int MAX_ORDER_SIZE = 256; } Track::Track(int number, SoundSource source, int channelInSource, int defPattenSize) : effetDisplayWidth_(0) { setAttribute(number, source, channelInSource); patterns_.reserve(PATTERN_SIZE); for (int i = 0; i < PATTERN_SIZE; ++i) { patterns_.emplace_back(i, defPattenSize); } patterns_[0].increaseUsedCount(); order_.push_back(0); // Set first order } void Track::setAttribute(int number, SoundSource source, int channelInSource) noexcept { attrib_.number = number; attrib_.source = source; attrib_.channelInSource = channelInSource; } OrderInfo Track::getOrderInfo(int order) const { OrderInfo info; info.trackAttribute = attrib_; info.order = order; info.patten = order_.at(static_cast(order)); return info; } size_t Track::getOrderSize() const { return order_.size(); } bool Track::canAddNewOrder() const { return order_.size() < MAX_ORDER_SIZE; } Pattern& Track::getPattern(int num) { return patterns_.at(static_cast(num)); } Pattern& Track::getPatternFromOrderNumber(int num) { return getPattern(order_.at(static_cast(num))); } int Track::searchFirstUneditedUnusedPattern() const { auto it = utils::findIf(patterns_, [](const Pattern& pattern) { return (!pattern.hasEvent() && !pattern.getUsedCount()); }); return (it == patterns_.cend() ? -1 : std::distance(patterns_.cbegin(), it)); } int Track::clonePattern(int num) { int n = searchFirstUneditedUnusedPattern(); if (n == -1) return num; else { patterns_.at(static_cast(n)) = patterns_.at(static_cast(num)).clone(n); return n; } } std::vector Track::getEditedPatternIndices() const { return utils::findIndicesIf(patterns_, [](const Pattern& pattern) { return pattern.hasEvent(); }); } std::set Track::getRegisteredInstruments() const { std::set set; for (const Pattern& pattern : patterns_) { auto&& insts = pattern.getRegisteredInstruments(); std::copy(insts.cbegin(), insts.cend(), std::inserter(set, set.end())); } return set; } void Track::registerPatternToOrder(int order, int pattern) { patterns_.at(static_cast(pattern)).increaseUsedCount(); patterns_.at(static_cast(order_.at(static_cast(order)))).decreaseUsedCount(); order_.at(static_cast(order)) = pattern; } void Track::insertOrderBelow(int order) { int n = searchFirstUneditedUnusedPattern(); if (n == -1) n = PATTERN_SIZE - 1; if (order == static_cast(order_.size()) - 1) order_.push_back(n); else order_.insert(order_.begin() + order + 1, n); patterns_[static_cast(n)].increaseUsedCount(); } void Track::deleteOrder(int order) { patterns_.at(static_cast(order_.at(static_cast(order)))).decreaseUsedCount(); order_.erase(order_.begin() + order); } void Track::swapOrder(int a, int b) { std::swap(order_.at(static_cast(a)), order_.at((static_cast(b)))); } void Track::changeDefaultPatternSize(size_t size) { for (auto& ptn : patterns_) ptn.changeSize(size); } void Track::clearUnusedPatterns() { for (Pattern& pattern : patterns_) { if (!pattern.getUsedCount() && pattern.hasEvent()) pattern.clear(); } } void Track::replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map) { for (Pattern& pattern : patterns_) { if (pattern.hasEvent()) { for (size_t i = 0; i < pattern.getSize(); ++i) { Step& step = pattern.getStep(static_cast(i)); int inst = step.getInstrumentNumber(); if (map.count(inst)) step.setInstrumentNumber(map.at(inst)); } } } } void Track::transpose(int seminotes, const std::vector& excludeInsts) { for (Pattern& pattern : patterns_) pattern.transpose(seminotes, excludeInsts); } BambooTracker-0.4.6/BambooTracker/module/track.hpp000066400000000000000000000052271401124043500220510ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "pattern.hpp" #include "bamboo_tracker_defs.hpp" struct TrackAttribute { int number; SoundSource source; int channelInSource; }; struct OrderInfo { TrackAttribute trackAttribute; int order; int patten; }; class Track { public: Track(int number, SoundSource source, int channelInSource, int defPattenSize); void setAttribute(int number, SoundSource source, int channelInSource) noexcept; inline TrackAttribute getAttribute() const noexcept { return attrib_; } OrderInfo getOrderInfo(int order) const; size_t getOrderSize() const; bool canAddNewOrder() const; Pattern& getPattern(int num); Pattern& getPatternFromOrderNumber(int num); int searchFirstUneditedUnusedPattern() const; int clonePattern(int num); std::vector getEditedPatternIndices() const; std::set getRegisteredInstruments() const; void registerPatternToOrder(int order, int pattern); void insertOrderBelow(int order); void deleteOrder(int order); void swapOrder(int a, int b); void changeDefaultPatternSize(size_t size); inline void setEffectDisplayWidth(size_t w) noexcept { effetDisplayWidth_ = w; } inline size_t getEffectDisplayWidth() const noexcept { return effetDisplayWidth_; } void clearUnusedPatterns(); void replaceDuplicateInstrumentsInPatterns(const std::unordered_map& map); void transpose(int seminotes, const std::vector& excludeInsts); private: TrackAttribute attrib_; std::vector order_; std::vector patterns_; size_t effetDisplayWidth_; }; BambooTracker-0.4.6/BambooTracker/note.cpp000066400000000000000000002667551401124043500204370ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "note.hpp" #include #include namespace note_utils { namespace { const uint16_t centTableFM[3072] = { 0x026a, 0x026b, 0x026c, 0x026e, 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0276, 0x0277, 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028d, 0x028e, 0x028f, 0x0290, 0x0291, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029f, 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a5, 0x02a6, 0x02a7, 0x02a8, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b5, 0x02b6, 0x02b7, 0x02b8, 0x02ba, 0x02bb, 0x02bc, 0x02be, 0x02bf, 0x02c0, 0x02c1, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c8, 0x02c9, 0x02ca, 0x02cc, 0x02cd, 0x02ce, 0x02cf, 0x02d1, 0x02d2, 0x02d3, 0x02d5, 0x02d6, 0x02d7, 0x02d9, 0x02da, 0x02db, 0x02dd, 0x02de, 0x02df, 0x02e1, 0x02e2, 0x02e3, 0x02e5, 0x02e6, 0x02e7, 0x02e9, 0x02ea, 0x02eb, 0x02ed, 0x02ee, 0x02ef, 0x02f1, 0x02f2, 0x02f3, 0x02f5, 0x02f6, 0x02f7, 0x02f9, 0x02fa, 0x02fc, 0x02fd, 0x02fe, 0x0300, 0x0301, 0x0303, 0x0304, 0x0305, 0x0307, 0x0308, 0x030a, 0x030b, 0x030c, 0x030e, 0x030f, 0x0311, 0x0312, 0x0313, 0x0315, 0x0316, 0x0318, 0x0319, 0x031b, 0x031c, 0x031d, 0x031f, 0x0320, 0x0322, 0x0323, 0x0325, 0x0326, 0x0328, 0x0329, 0x032a, 0x032c, 0x032d, 0x032f, 0x0330, 0x0332, 0x0333, 0x0335, 0x0336, 0x0338, 0x0339, 0x033b, 0x033c, 0x033e, 0x033f, 0x0341, 0x0342, 0x0344, 0x0345, 0x0347, 0x0348, 0x034a, 0x034b, 0x034d, 0x034e, 0x0350, 0x0351, 0x0353, 0x0355, 0x0356, 0x0358, 0x0359, 0x035b, 0x035c, 0x035e, 0x035f, 0x0361, 0x0362, 0x0364, 0x0366, 0x0367, 0x0369, 0x036a, 0x036c, 0x036d, 0x036f, 0x0371, 0x0372, 0x0374, 0x0375, 0x0377, 0x0379, 0x037a, 0x037c, 0x037d, 0x037f, 0x0381, 0x0382, 0x0384, 0x0386, 0x0387, 0x0389, 0x038a, 0x038c, 0x038e, 0x038f, 0x0391, 0x0393, 0x0394, 0x0396, 0x0398, 0x0399, 0x039b, 0x039d, 0x039e, 0x03a0, 0x03a2, 0x03a3, 0x03a5, 0x03a7, 0x03a8, 0x03aa, 0x03ac, 0x03ad, 0x03af, 0x03b1, 0x03b3, 0x03b4, 0x03b6, 0x03b8, 0x03b9, 0x03bb, 0x03bd, 0x03bf, 0x03c0, 0x03c2, 0x03c4, 0x03c6, 0x03c7, 0x03c9, 0x03cb, 0x03cd, 0x03ce, 0x03d0, 0x03d2, 0x03d4, 0x03d5, 0x03d7, 0x03d9, 0x03db, 0x03dd, 0x03de, 0x03e0, 0x03e2, 0x03e4, 0x03e5, 0x03e7, 0x03e9, 0x03eb, 0x03ed, 0x03ef, 0x03f0, 0x03f2, 0x03f4, 0x03f6, 0x03f8, 0x03f9, 0x03fb, 0x03fd, 0x03ff, 0x0401, 0x0403, 0x0405, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e, 0x0410, 0x0412, 0x0414, 0x0415, 0x0417, 0x0419, 0x041b, 0x041d, 0x041f, 0x0421, 0x0423, 0x0425, 0x0427, 0x0428, 0x042a, 0x042c, 0x042e, 0x0430, 0x0432, 0x0434, 0x0436, 0x0438, 0x043a, 0x043c, 0x043e, 0x0440, 0x0442, 0x0444, 0x0446, 0x0448, 0x044a, 0x044c, 0x044e, 0x0450, 0x0452, 0x0454, 0x0456, 0x0458, 0x045a, 0x045c, 0x045e, 0x0460, 0x0462, 0x0464, 0x0466, 0x0468, 0x046a, 0x046c, 0x046e, 0x0470, 0x0472, 0x0474, 0x0476, 0x0478, 0x047a, 0x047c, 0x047e, 0x0480, 0x0483, 0x0485, 0x0487, 0x0489, 0x048b, 0x048d, 0x048f, 0x0491, 0x0493, 0x0495, 0x0498, 0x049a, 0x049c, 0x049e, 0x04a0, 0x04a2, 0x04a4, 0x04a6, 0x04a9, 0x04ab, 0x04ad, 0x04af, 0x04b1, 0x04b3, 0x04b6, 0x04b8, 0x04ba, 0x04bc, 0x04be, 0x04c1, 0x04c3, 0x04c5, 0x04c7, 0x04c9, 0x04cc, 0x04ce, 0x04d0, 0x04d2, 0x04d4, 0x04d7, 0x04d9, 0x04db, 0x04dd, 0x04e0, 0x04e2, 0x04e4, 0x04e6, 0x04e9, 0x04eb, 0x04ed, 0x04f0, 0x04f2, 0x04f4, 0x04f6, 0x04f9, 0x04fb, 0x04fd, 0x0500, 0x0502, 0x0504, 0x0507, 0x0509, 0x050b, 0x050e, 0x0510, 0x0512, 0x0515, 0x0517, 0x0519, 0x051c, 0x051e, 0x0520, 0x0523, 0x0525, 0x0528, 0x052a, 0x052c, 0x052f, 0x0531, 0x0533, 0x0536, 0x0538, 0x053b, 0x053d, 0x0540, 0x0542, 0x0544, 0x0547, 0x0549, 0x054c, 0x054e, 0x0551, 0x0553, 0x0556, 0x0558, 0x055a, 0x055d, 0x055f, 0x0562, 0x0564, 0x0567, 0x0569, 0x056c, 0x056e, 0x0571, 0x0573, 0x0576, 0x0578, 0x057b, 0x057e, 0x0580, 0x0583, 0x0585, 0x0588, 0x058a, 0x058d, 0x058f, 0x0592, 0x0595, 0x0597, 0x059a, 0x059c, 0x059f, 0x05a2, 0x05a4, 0x05a7, 0x05a9, 0x05ac, 0x05af, 0x05b1, 0x05b4, 0x05b6, 0x05b9, 0x05bc, 0x05be, 0x05c1, 0x05c4, 0x05c6, 0x05c9, 0x05cc, 0x05ce, 0x05d1, 0x05d4, 0x05d7, 0x05d9, 0x05dc, 0x05df, 0x05e1, 0x05e4, 0x05e7, 0x05ea, 0x05ec, 0x05ef, 0x05f2, 0x05f4, 0x05f7, 0x05fa, 0x05fd, 0x0600, 0x0602, 0x0605, 0x0608, 0x060b, 0x060d, 0x0610, 0x0613, 0x0616, 0x0619, 0x061c, 0x061e, 0x0621, 0x0624, 0x0627, 0x062a, 0x062d, 0x062f, 0x0632, 0x0635, 0x0638, 0x063b, 0x063e, 0x0641, 0x0644, 0x0646, 0x0649, 0x064c, 0x064f, 0x0652, 0x0655, 0x0658, 0x065b, 0x065e, 0x0661, 0x0664, 0x0667, 0x066a, 0x066d, 0x0670, 0x0673, 0x0675, 0x0678, 0x067b, 0x067e, 0x0681, 0x0684, 0x0687, 0x068b, 0x068e, 0x0691, 0x0694, 0x0697, 0x069a, 0x069d, 0x06a0, 0x06a3, 0x06a6, 0x06a9, 0x06ac, 0x06af, 0x06b2, 0x06b5, 0x06b8, 0x06bc, 0x06bf, 0x06c2, 0x06c5, 0x06c8, 0x06cb, 0x06ce, 0x06d1, 0x06d5, 0x06d8, 0x06db, 0x06de, 0x06e1, 0x06e5, 0x06e8, 0x06eb, 0x06ee, 0x06f1, 0x06f5, 0x06f8, 0x06fb, 0x06fe, 0x0701, 0x0705, 0x0708, 0x070b, 0x070e, 0x0712, 0x0715, 0x0718, 0x071b, 0x071f, 0x0722, 0x0725, 0x0729, 0x072c, 0x072f, 0x0733, 0x0736, 0x0739, 0x073d, 0x0740, 0x0743, 0x0747, 0x074a, 0x074d, 0x0751, 0x0754, 0x0758, 0x075b, 0x075e, 0x0762, 0x0765, 0x0769, 0x076c, 0x076f, 0x0773, 0x0776, 0x077a, 0x077d, 0x0781, 0x0784, 0x0788, 0x078b, 0x078f, 0x0792, 0x0796, 0x0799, 0x079d, 0x07a0, 0x07a4, 0x07a7, 0x07ab, 0x07ae, 0x07b2, 0x07b5, 0x07b9, 0x07bd, 0x07c0, 0x07c4, 0x07c7, 0x07cb, 0x07cf, 0x07d2, 0x07d6, 0x07d9, 0x07dd, 0x07e1, 0x07e4, 0x07e8, 0x07ec, 0x07ef, 0x07f3, 0x07f7, 0x07fa, 0x07fe, 0x0c01, 0x0c03, 0x0c05, 0x0c06, 0x0c08, 0x0c0a, 0x0c0c, 0x0c0e, 0x0c10, 0x0c12, 0x0c14, 0x0c15, 0x0c17, 0x0c19, 0x0c1b, 0x0c1d, 0x0c1f, 0x0c21, 0x0c23, 0x0c25, 0x0c27, 0x0c28, 0x0c2a, 0x0c2c, 0x0c2e, 0x0c30, 0x0c32, 0x0c34, 0x0c36, 0x0c38, 0x0c3a, 0x0c3c, 0x0c3e, 0x0c40, 0x0c42, 0x0c44, 0x0c46, 0x0c48, 0x0c4a, 0x0c4c, 0x0c4e, 0x0c50, 0x0c52, 0x0c54, 0x0c56, 0x0c58, 0x0c5a, 0x0c5c, 0x0c5e, 0x0c60, 0x0c62, 0x0c64, 0x0c66, 0x0c68, 0x0c6a, 0x0c6c, 0x0c6e, 0x0c70, 0x0c72, 0x0c74, 0x0c76, 0x0c78, 0x0c7a, 0x0c7c, 0x0c7e, 0x0c80, 0x0c83, 0x0c85, 0x0c87, 0x0c89, 0x0c8b, 0x0c8d, 0x0c8f, 0x0c91, 0x0c93, 0x0c95, 0x0c98, 0x0c9a, 0x0c9c, 0x0c9e, 0x0ca0, 0x0ca2, 0x0ca4, 0x0ca6, 0x0ca9, 0x0cab, 0x0cad, 0x0caf, 0x0cb1, 0x0cb3, 0x0cb6, 0x0cb8, 0x0cba, 0x0cbc, 0x0cbe, 0x0cc1, 0x0cc3, 0x0cc5, 0x0cc7, 0x0cc9, 0x0ccc, 0x0cce, 0x0cd0, 0x0cd2, 0x0cd4, 0x0cd7, 0x0cd9, 0x0cdb, 0x0cdd, 0x0ce0, 0x0ce2, 0x0ce4, 0x0ce6, 0x0ce9, 0x0ceb, 0x0ced, 0x0cf0, 0x0cf2, 0x0cf4, 0x0cf6, 0x0cf9, 0x0cfb, 0x0cfd, 0x0d00, 0x0d02, 0x0d04, 0x0d07, 0x0d09, 0x0d0b, 0x0d0e, 0x0d10, 0x0d12, 0x0d15, 0x0d17, 0x0d19, 0x0d1c, 0x0d1e, 0x0d20, 0x0d23, 0x0d25, 0x0d28, 0x0d2a, 0x0d2c, 0x0d2f, 0x0d31, 0x0d33, 0x0d36, 0x0d38, 0x0d3b, 0x0d3d, 0x0d40, 0x0d42, 0x0d44, 0x0d47, 0x0d49, 0x0d4c, 0x0d4e, 0x0d51, 0x0d53, 0x0d56, 0x0d58, 0x0d5a, 0x0d5d, 0x0d5f, 0x0d62, 0x0d64, 0x0d67, 0x0d69, 0x0d6c, 0x0d6e, 0x0d71, 0x0d73, 0x0d76, 0x0d78, 0x0d7b, 0x0d7e, 0x0d80, 0x0d83, 0x0d85, 0x0d88, 0x0d8a, 0x0d8d, 0x0d8f, 0x0d92, 0x0d95, 0x0d97, 0x0d9a, 0x0d9c, 0x0d9f, 0x0da2, 0x0da4, 0x0da7, 0x0da9, 0x0dac, 0x0daf, 0x0db1, 0x0db4, 0x0db6, 0x0db9, 0x0dbc, 0x0dbe, 0x0dc1, 0x0dc4, 0x0dc6, 0x0dc9, 0x0dcc, 0x0dce, 0x0dd1, 0x0dd4, 0x0dd7, 0x0dd9, 0x0ddc, 0x0ddf, 0x0de1, 0x0de4, 0x0de7, 0x0dea, 0x0dec, 0x0def, 0x0df2, 0x0df4, 0x0df7, 0x0dfa, 0x0dfd, 0x0e00, 0x0e02, 0x0e05, 0x0e08, 0x0e0b, 0x0e0d, 0x0e10, 0x0e13, 0x0e16, 0x0e19, 0x0e1c, 0x0e1e, 0x0e21, 0x0e24, 0x0e27, 0x0e2a, 0x0e2d, 0x0e2f, 0x0e32, 0x0e35, 0x0e38, 0x0e3b, 0x0e3e, 0x0e41, 0x0e44, 0x0e46, 0x0e49, 0x0e4c, 0x0e4f, 0x0e52, 0x0e55, 0x0e58, 0x0e5b, 0x0e5e, 0x0e61, 0x0e64, 0x0e67, 0x0e6a, 0x0e6d, 0x0e70, 0x0e73, 0x0e75, 0x0e78, 0x0e7b, 0x0e7e, 0x0e81, 0x0e84, 0x0e87, 0x0e8b, 0x0e8e, 0x0e91, 0x0e94, 0x0e97, 0x0e9a, 0x0e9d, 0x0ea0, 0x0ea3, 0x0ea6, 0x0ea9, 0x0eac, 0x0eaf, 0x0eb2, 0x0eb5, 0x0eb8, 0x0ebc, 0x0ebf, 0x0ec2, 0x0ec5, 0x0ec8, 0x0ecb, 0x0ece, 0x0ed1, 0x0ed5, 0x0ed8, 0x0edb, 0x0ede, 0x0ee1, 0x0ee5, 0x0ee8, 0x0eeb, 0x0eee, 0x0ef1, 0x0ef5, 0x0ef8, 0x0efb, 0x0efe, 0x0f01, 0x0f05, 0x0f08, 0x0f0b, 0x0f0e, 0x0f12, 0x0f15, 0x0f18, 0x0f1b, 0x0f1f, 0x0f22, 0x0f25, 0x0f29, 0x0f2c, 0x0f2f, 0x0f33, 0x0f36, 0x0f39, 0x0f3d, 0x0f40, 0x0f43, 0x0f47, 0x0f4a, 0x0f4d, 0x0f51, 0x0f54, 0x0f58, 0x0f5b, 0x0f5e, 0x0f62, 0x0f65, 0x0f69, 0x0f6c, 0x0f6f, 0x0f73, 0x0f76, 0x0f7a, 0x0f7d, 0x0f81, 0x0f84, 0x0f88, 0x0f8b, 0x0f8f, 0x0f92, 0x0f96, 0x0f99, 0x0f9d, 0x0fa0, 0x0fa4, 0x0fa7, 0x0fab, 0x0fae, 0x0fb2, 0x0fb5, 0x0fb9, 0x0fbd, 0x0fc0, 0x0fc4, 0x0fc7, 0x0fcb, 0x0fcf, 0x0fd2, 0x0fd6, 0x0fd9, 0x0fdd, 0x0fe1, 0x0fe4, 0x0fe8, 0x0fec, 0x0fef, 0x0ff3, 0x0ff7, 0x0ffa, 0x0ffe, 0x1401, 0x1403, 0x1405, 0x1406, 0x1408, 0x140a, 0x140c, 0x140e, 0x1410, 0x1412, 0x1414, 0x1415, 0x1417, 0x1419, 0x141b, 0x141d, 0x141f, 0x1421, 0x1423, 0x1425, 0x1427, 0x1428, 0x142a, 0x142c, 0x142e, 0x1430, 0x1432, 0x1434, 0x1436, 0x1438, 0x143a, 0x143c, 0x143e, 0x1440, 0x1442, 0x1444, 0x1446, 0x1448, 0x144a, 0x144c, 0x144e, 0x1450, 0x1452, 0x1454, 0x1456, 0x1458, 0x145a, 0x145c, 0x145e, 0x1460, 0x1462, 0x1464, 0x1466, 0x1468, 0x146a, 0x146c, 0x146e, 0x1470, 0x1472, 0x1474, 0x1476, 0x1478, 0x147a, 0x147c, 0x147e, 0x1480, 0x1483, 0x1485, 0x1487, 0x1489, 0x148b, 0x148d, 0x148f, 0x1491, 0x1493, 0x1495, 0x1498, 0x149a, 0x149c, 0x149e, 0x14a0, 0x14a2, 0x14a4, 0x14a6, 0x14a9, 0x14ab, 0x14ad, 0x14af, 0x14b1, 0x14b3, 0x14b6, 0x14b8, 0x14ba, 0x14bc, 0x14be, 0x14c1, 0x14c3, 0x14c5, 0x14c7, 0x14c9, 0x14cc, 0x14ce, 0x14d0, 0x14d2, 0x14d4, 0x14d7, 0x14d9, 0x14db, 0x14dd, 0x14e0, 0x14e2, 0x14e4, 0x14e6, 0x14e9, 0x14eb, 0x14ed, 0x14f0, 0x14f2, 0x14f4, 0x14f6, 0x14f9, 0x14fb, 0x14fd, 0x1500, 0x1502, 0x1504, 0x1507, 0x1509, 0x150b, 0x150e, 0x1510, 0x1512, 0x1515, 0x1517, 0x1519, 0x151c, 0x151e, 0x1520, 0x1523, 0x1525, 0x1528, 0x152a, 0x152c, 0x152f, 0x1531, 0x1533, 0x1536, 0x1538, 0x153b, 0x153d, 0x1540, 0x1542, 0x1544, 0x1547, 0x1549, 0x154c, 0x154e, 0x1551, 0x1553, 0x1556, 0x1558, 0x155a, 0x155d, 0x155f, 0x1562, 0x1564, 0x1567, 0x1569, 0x156c, 0x156e, 0x1571, 0x1573, 0x1576, 0x1578, 0x157b, 0x157e, 0x1580, 0x1583, 0x1585, 0x1588, 0x158a, 0x158d, 0x158f, 0x1592, 0x1595, 0x1597, 0x159a, 0x159c, 0x159f, 0x15a2, 0x15a4, 0x15a7, 0x15a9, 0x15ac, 0x15af, 0x15b1, 0x15b4, 0x15b6, 0x15b9, 0x15bc, 0x15be, 0x15c1, 0x15c4, 0x15c6, 0x15c9, 0x15cc, 0x15ce, 0x15d1, 0x15d4, 0x15d7, 0x15d9, 0x15dc, 0x15df, 0x15e1, 0x15e4, 0x15e7, 0x15ea, 0x15ec, 0x15ef, 0x15f2, 0x15f4, 0x15f7, 0x15fa, 0x15fd, 0x1600, 0x1602, 0x1605, 0x1608, 0x160b, 0x160d, 0x1610, 0x1613, 0x1616, 0x1619, 0x161c, 0x161e, 0x1621, 0x1624, 0x1627, 0x162a, 0x162d, 0x162f, 0x1632, 0x1635, 0x1638, 0x163b, 0x163e, 0x1641, 0x1644, 0x1646, 0x1649, 0x164c, 0x164f, 0x1652, 0x1655, 0x1658, 0x165b, 0x165e, 0x1661, 0x1664, 0x1667, 0x166a, 0x166d, 0x1670, 0x1673, 0x1675, 0x1678, 0x167b, 0x167e, 0x1681, 0x1684, 0x1687, 0x168b, 0x168e, 0x1691, 0x1694, 0x1697, 0x169a, 0x169d, 0x16a0, 0x16a3, 0x16a6, 0x16a9, 0x16ac, 0x16af, 0x16b2, 0x16b5, 0x16b8, 0x16bc, 0x16bf, 0x16c2, 0x16c5, 0x16c8, 0x16cb, 0x16ce, 0x16d1, 0x16d5, 0x16d8, 0x16db, 0x16de, 0x16e1, 0x16e5, 0x16e8, 0x16eb, 0x16ee, 0x16f1, 0x16f5, 0x16f8, 0x16fb, 0x16fe, 0x1701, 0x1705, 0x1708, 0x170b, 0x170e, 0x1712, 0x1715, 0x1718, 0x171b, 0x171f, 0x1722, 0x1725, 0x1729, 0x172c, 0x172f, 0x1733, 0x1736, 0x1739, 0x173d, 0x1740, 0x1743, 0x1747, 0x174a, 0x174d, 0x1751, 0x1754, 0x1758, 0x175b, 0x175e, 0x1762, 0x1765, 0x1769, 0x176c, 0x176f, 0x1773, 0x1776, 0x177a, 0x177d, 0x1781, 0x1784, 0x1788, 0x178b, 0x178f, 0x1792, 0x1796, 0x1799, 0x179d, 0x17a0, 0x17a4, 0x17a7, 0x17ab, 0x17ae, 0x17b2, 0x17b5, 0x17b9, 0x17bd, 0x17c0, 0x17c4, 0x17c7, 0x17cb, 0x17cf, 0x17d2, 0x17d6, 0x17d9, 0x17dd, 0x17e1, 0x17e4, 0x17e8, 0x17ec, 0x17ef, 0x17f3, 0x17f7, 0x17fa, 0x17fe, 0x1c01, 0x1c03, 0x1c05, 0x1c06, 0x1c08, 0x1c0a, 0x1c0c, 0x1c0e, 0x1c10, 0x1c12, 0x1c14, 0x1c15, 0x1c17, 0x1c19, 0x1c1b, 0x1c1d, 0x1c1f, 0x1c21, 0x1c23, 0x1c25, 0x1c27, 0x1c28, 0x1c2a, 0x1c2c, 0x1c2e, 0x1c30, 0x1c32, 0x1c34, 0x1c36, 0x1c38, 0x1c3a, 0x1c3c, 0x1c3e, 0x1c40, 0x1c42, 0x1c44, 0x1c46, 0x1c48, 0x1c4a, 0x1c4c, 0x1c4e, 0x1c50, 0x1c52, 0x1c54, 0x1c56, 0x1c58, 0x1c5a, 0x1c5c, 0x1c5e, 0x1c60, 0x1c62, 0x1c64, 0x1c66, 0x1c68, 0x1c6a, 0x1c6c, 0x1c6e, 0x1c70, 0x1c72, 0x1c74, 0x1c76, 0x1c78, 0x1c7a, 0x1c7c, 0x1c7e, 0x1c80, 0x1c83, 0x1c85, 0x1c87, 0x1c89, 0x1c8b, 0x1c8d, 0x1c8f, 0x1c91, 0x1c93, 0x1c95, 0x1c98, 0x1c9a, 0x1c9c, 0x1c9e, 0x1ca0, 0x1ca2, 0x1ca4, 0x1ca6, 0x1ca9, 0x1cab, 0x1cad, 0x1caf, 0x1cb1, 0x1cb3, 0x1cb6, 0x1cb8, 0x1cba, 0x1cbc, 0x1cbe, 0x1cc1, 0x1cc3, 0x1cc5, 0x1cc7, 0x1cc9, 0x1ccc, 0x1cce, 0x1cd0, 0x1cd2, 0x1cd4, 0x1cd7, 0x1cd9, 0x1cdb, 0x1cdd, 0x1ce0, 0x1ce2, 0x1ce4, 0x1ce6, 0x1ce9, 0x1ceb, 0x1ced, 0x1cf0, 0x1cf2, 0x1cf4, 0x1cf6, 0x1cf9, 0x1cfb, 0x1cfd, 0x1d00, 0x1d02, 0x1d04, 0x1d07, 0x1d09, 0x1d0b, 0x1d0e, 0x1d10, 0x1d12, 0x1d15, 0x1d17, 0x1d19, 0x1d1c, 0x1d1e, 0x1d20, 0x1d23, 0x1d25, 0x1d28, 0x1d2a, 0x1d2c, 0x1d2f, 0x1d31, 0x1d33, 0x1d36, 0x1d38, 0x1d3b, 0x1d3d, 0x1d40, 0x1d42, 0x1d44, 0x1d47, 0x1d49, 0x1d4c, 0x1d4e, 0x1d51, 0x1d53, 0x1d56, 0x1d58, 0x1d5a, 0x1d5d, 0x1d5f, 0x1d62, 0x1d64, 0x1d67, 0x1d69, 0x1d6c, 0x1d6e, 0x1d71, 0x1d73, 0x1d76, 0x1d78, 0x1d7b, 0x1d7e, 0x1d80, 0x1d83, 0x1d85, 0x1d88, 0x1d8a, 0x1d8d, 0x1d8f, 0x1d92, 0x1d95, 0x1d97, 0x1d9a, 0x1d9c, 0x1d9f, 0x1da2, 0x1da4, 0x1da7, 0x1da9, 0x1dac, 0x1daf, 0x1db1, 0x1db4, 0x1db6, 0x1db9, 0x1dbc, 0x1dbe, 0x1dc1, 0x1dc4, 0x1dc6, 0x1dc9, 0x1dcc, 0x1dce, 0x1dd1, 0x1dd4, 0x1dd7, 0x1dd9, 0x1ddc, 0x1ddf, 0x1de1, 0x1de4, 0x1de7, 0x1dea, 0x1dec, 0x1def, 0x1df2, 0x1df4, 0x1df7, 0x1dfa, 0x1dfd, 0x1e00, 0x1e02, 0x1e05, 0x1e08, 0x1e0b, 0x1e0d, 0x1e10, 0x1e13, 0x1e16, 0x1e19, 0x1e1c, 0x1e1e, 0x1e21, 0x1e24, 0x1e27, 0x1e2a, 0x1e2d, 0x1e2f, 0x1e32, 0x1e35, 0x1e38, 0x1e3b, 0x1e3e, 0x1e41, 0x1e44, 0x1e46, 0x1e49, 0x1e4c, 0x1e4f, 0x1e52, 0x1e55, 0x1e58, 0x1e5b, 0x1e5e, 0x1e61, 0x1e64, 0x1e67, 0x1e6a, 0x1e6d, 0x1e70, 0x1e73, 0x1e75, 0x1e78, 0x1e7b, 0x1e7e, 0x1e81, 0x1e84, 0x1e87, 0x1e8b, 0x1e8e, 0x1e91, 0x1e94, 0x1e97, 0x1e9a, 0x1e9d, 0x1ea0, 0x1ea3, 0x1ea6, 0x1ea9, 0x1eac, 0x1eaf, 0x1eb2, 0x1eb5, 0x1eb8, 0x1ebc, 0x1ebf, 0x1ec2, 0x1ec5, 0x1ec8, 0x1ecb, 0x1ece, 0x1ed1, 0x1ed5, 0x1ed8, 0x1edb, 0x1ede, 0x1ee1, 0x1ee5, 0x1ee8, 0x1eeb, 0x1eee, 0x1ef1, 0x1ef5, 0x1ef8, 0x1efb, 0x1efe, 0x1f01, 0x1f05, 0x1f08, 0x1f0b, 0x1f0e, 0x1f12, 0x1f15, 0x1f18, 0x1f1b, 0x1f1f, 0x1f22, 0x1f25, 0x1f29, 0x1f2c, 0x1f2f, 0x1f33, 0x1f36, 0x1f39, 0x1f3d, 0x1f40, 0x1f43, 0x1f47, 0x1f4a, 0x1f4d, 0x1f51, 0x1f54, 0x1f58, 0x1f5b, 0x1f5e, 0x1f62, 0x1f65, 0x1f69, 0x1f6c, 0x1f6f, 0x1f73, 0x1f76, 0x1f7a, 0x1f7d, 0x1f81, 0x1f84, 0x1f88, 0x1f8b, 0x1f8f, 0x1f92, 0x1f96, 0x1f99, 0x1f9d, 0x1fa0, 0x1fa4, 0x1fa7, 0x1fab, 0x1fae, 0x1fb2, 0x1fb5, 0x1fb9, 0x1fbd, 0x1fc0, 0x1fc4, 0x1fc7, 0x1fcb, 0x1fcf, 0x1fd2, 0x1fd6, 0x1fd9, 0x1fdd, 0x1fe1, 0x1fe4, 0x1fe8, 0x1fec, 0x1fef, 0x1ff3, 0x1ff7, 0x1ffa, 0x1ffe, 0x2401, 0x2403, 0x2405, 0x2406, 0x2408, 0x240a, 0x240c, 0x240e, 0x2410, 0x2412, 0x2414, 0x2415, 0x2417, 0x2419, 0x241b, 0x241d, 0x241f, 0x2421, 0x2423, 0x2425, 0x2427, 0x2428, 0x242a, 0x242c, 0x242e, 0x2430, 0x2432, 0x2434, 0x2436, 0x2438, 0x243a, 0x243c, 0x243e, 0x2440, 0x2442, 0x2444, 0x2446, 0x2448, 0x244a, 0x244c, 0x244e, 0x2450, 0x2452, 0x2454, 0x2456, 0x2458, 0x245a, 0x245c, 0x245e, 0x2460, 0x2462, 0x2464, 0x2466, 0x2468, 0x246a, 0x246c, 0x246e, 0x2470, 0x2472, 0x2474, 0x2476, 0x2478, 0x247a, 0x247c, 0x247e, 0x2480, 0x2483, 0x2485, 0x2487, 0x2489, 0x248b, 0x248d, 0x248f, 0x2491, 0x2493, 0x2495, 0x2498, 0x249a, 0x249c, 0x249e, 0x24a0, 0x24a2, 0x24a4, 0x24a6, 0x24a9, 0x24ab, 0x24ad, 0x24af, 0x24b1, 0x24b3, 0x24b6, 0x24b8, 0x24ba, 0x24bc, 0x24be, 0x24c1, 0x24c3, 0x24c5, 0x24c7, 0x24c9, 0x24cc, 0x24ce, 0x24d0, 0x24d2, 0x24d4, 0x24d7, 0x24d9, 0x24db, 0x24dd, 0x24e0, 0x24e2, 0x24e4, 0x24e6, 0x24e9, 0x24eb, 0x24ed, 0x24f0, 0x24f2, 0x24f4, 0x24f6, 0x24f9, 0x24fb, 0x24fd, 0x2500, 0x2502, 0x2504, 0x2507, 0x2509, 0x250b, 0x250e, 0x2510, 0x2512, 0x2515, 0x2517, 0x2519, 0x251c, 0x251e, 0x2520, 0x2523, 0x2525, 0x2528, 0x252a, 0x252c, 0x252f, 0x2531, 0x2533, 0x2536, 0x2538, 0x253b, 0x253d, 0x2540, 0x2542, 0x2544, 0x2547, 0x2549, 0x254c, 0x254e, 0x2551, 0x2553, 0x2556, 0x2558, 0x255a, 0x255d, 0x255f, 0x2562, 0x2564, 0x2567, 0x2569, 0x256c, 0x256e, 0x2571, 0x2573, 0x2576, 0x2578, 0x257b, 0x257e, 0x2580, 0x2583, 0x2585, 0x2588, 0x258a, 0x258d, 0x258f, 0x2592, 0x2595, 0x2597, 0x259a, 0x259c, 0x259f, 0x25a2, 0x25a4, 0x25a7, 0x25a9, 0x25ac, 0x25af, 0x25b1, 0x25b4, 0x25b6, 0x25b9, 0x25bc, 0x25be, 0x25c1, 0x25c4, 0x25c6, 0x25c9, 0x25cc, 0x25ce, 0x25d1, 0x25d4, 0x25d7, 0x25d9, 0x25dc, 0x25df, 0x25e1, 0x25e4, 0x25e7, 0x25ea, 0x25ec, 0x25ef, 0x25f2, 0x25f4, 0x25f7, 0x25fa, 0x25fd, 0x2600, 0x2602, 0x2605, 0x2608, 0x260b, 0x260d, 0x2610, 0x2613, 0x2616, 0x2619, 0x261c, 0x261e, 0x2621, 0x2624, 0x2627, 0x262a, 0x262d, 0x262f, 0x2632, 0x2635, 0x2638, 0x263b, 0x263e, 0x2641, 0x2644, 0x2646, 0x2649, 0x264c, 0x264f, 0x2652, 0x2655, 0x2658, 0x265b, 0x265e, 0x2661, 0x2664, 0x2667, 0x266a, 0x266d, 0x2670, 0x2673, 0x2675, 0x2678, 0x267b, 0x267e, 0x2681, 0x2684, 0x2687, 0x268b, 0x268e, 0x2691, 0x2694, 0x2697, 0x269a, 0x269d, 0x26a0, 0x26a3, 0x26a6, 0x26a9, 0x26ac, 0x26af, 0x26b2, 0x26b5, 0x26b8, 0x26bc, 0x26bf, 0x26c2, 0x26c5, 0x26c8, 0x26cb, 0x26ce, 0x26d1, 0x26d5, 0x26d8, 0x26db, 0x26de, 0x26e1, 0x26e5, 0x26e8, 0x26eb, 0x26ee, 0x26f1, 0x26f5, 0x26f8, 0x26fb, 0x26fe, 0x2701, 0x2705, 0x2708, 0x270b, 0x270e, 0x2712, 0x2715, 0x2718, 0x271b, 0x271f, 0x2722, 0x2725, 0x2729, 0x272c, 0x272f, 0x2733, 0x2736, 0x2739, 0x273d, 0x2740, 0x2743, 0x2747, 0x274a, 0x274d, 0x2751, 0x2754, 0x2758, 0x275b, 0x275e, 0x2762, 0x2765, 0x2769, 0x276c, 0x276f, 0x2773, 0x2776, 0x277a, 0x277d, 0x2781, 0x2784, 0x2788, 0x278b, 0x278f, 0x2792, 0x2796, 0x2799, 0x279d, 0x27a0, 0x27a4, 0x27a7, 0x27ab, 0x27ae, 0x27b2, 0x27b5, 0x27b9, 0x27bd, 0x27c0, 0x27c4, 0x27c7, 0x27cb, 0x27cf, 0x27d2, 0x27d6, 0x27d9, 0x27dd, 0x27e1, 0x27e4, 0x27e8, 0x27ec, 0x27ef, 0x27f3, 0x27f7, 0x27fa, 0x27fe, 0x2c01, 0x2c03, 0x2c05, 0x2c06, 0x2c08, 0x2c0a, 0x2c0c, 0x2c0e, 0x2c10, 0x2c12, 0x2c14, 0x2c15, 0x2c17, 0x2c19, 0x2c1b, 0x2c1d, 0x2c1f, 0x2c21, 0x2c23, 0x2c25, 0x2c27, 0x2c28, 0x2c2a, 0x2c2c, 0x2c2e, 0x2c30, 0x2c32, 0x2c34, 0x2c36, 0x2c38, 0x2c3a, 0x2c3c, 0x2c3e, 0x2c40, 0x2c42, 0x2c44, 0x2c46, 0x2c48, 0x2c4a, 0x2c4c, 0x2c4e, 0x2c50, 0x2c52, 0x2c54, 0x2c56, 0x2c58, 0x2c5a, 0x2c5c, 0x2c5e, 0x2c60, 0x2c62, 0x2c64, 0x2c66, 0x2c68, 0x2c6a, 0x2c6c, 0x2c6e, 0x2c70, 0x2c72, 0x2c74, 0x2c76, 0x2c78, 0x2c7a, 0x2c7c, 0x2c7e, 0x2c80, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2ce0, 0x2ce2, 0x2ce4, 0x2ce6, 0x2ce9, 0x2ceb, 0x2ced, 0x2cf0, 0x2cf2, 0x2cf4, 0x2cf6, 0x2cf9, 0x2cfb, 0x2cfd, 0x2d00, 0x2d02, 0x2d04, 0x2d07, 0x2d09, 0x2d0b, 0x2d0e, 0x2d10, 0x2d12, 0x2d15, 0x2d17, 0x2d19, 0x2d1c, 0x2d1e, 0x2d20, 0x2d23, 0x2d25, 0x2d28, 0x2d2a, 0x2d2c, 0x2d2f, 0x2d31, 0x2d33, 0x2d36, 0x2d38, 0x2d3b, 0x2d3d, 0x2d40, 0x2d42, 0x2d44, 0x2d47, 0x2d49, 0x2d4c, 0x2d4e, 0x2d51, 0x2d53, 0x2d56, 0x2d58, 0x2d5a, 0x2d5d, 0x2d5f, 0x2d62, 0x2d64, 0x2d67, 0x2d69, 0x2d6c, 0x2d6e, 0x2d71, 0x2d73, 0x2d76, 0x2d78, 0x2d7b, 0x2d7e, 0x2d80, 0x2d83, 0x2d85, 0x2d88, 0x2d8a, 0x2d8d, 0x2d8f, 0x2d92, 0x2d95, 0x2d97, 0x2d9a, 0x2d9c, 0x2d9f, 0x2da2, 0x2da4, 0x2da7, 0x2da9, 0x2dac, 0x2daf, 0x2db1, 0x2db4, 0x2db6, 0x2db9, 0x2dbc, 0x2dbe, 0x2dc1, 0x2dc4, 0x2dc6, 0x2dc9, 0x2dcc, 0x2dce, 0x2dd1, 0x2dd4, 0x2dd7, 0x2dd9, 0x2ddc, 0x2ddf, 0x2de1, 0x2de4, 0x2de7, 0x2dea, 0x2dec, 0x2def, 0x2df2, 0x2df4, 0x2df7, 0x2dfa, 0x2dfd, 0x2e00, 0x2e02, 0x2e05, 0x2e08, 0x2e0b, 0x2e0d, 0x2e10, 0x2e13, 0x2e16, 0x2e19, 0x2e1c, 0x2e1e, 0x2e21, 0x2e24, 0x2e27, 0x2e2a, 0x2e2d, 0x2e2f, 0x2e32, 0x2e35, 0x2e38, 0x2e3b, 0x2e3e, 0x2e41, 0x2e44, 0x2e46, 0x2e49, 0x2e4c, 0x2e4f, 0x2e52, 0x2e55, 0x2e58, 0x2e5b, 0x2e5e, 0x2e61, 0x2e64, 0x2e67, 0x2e6a, 0x2e6d, 0x2e70, 0x2e73, 0x2e75, 0x2e78, 0x2e7b, 0x2e7e, 0x2e81, 0x2e84, 0x2e87, 0x2e8b, 0x2e8e, 0x2e91, 0x2e94, 0x2e97, 0x2e9a, 0x2e9d, 0x2ea0, 0x2ea3, 0x2ea6, 0x2ea9, 0x2eac, 0x2eaf, 0x2eb2, 0x2eb5, 0x2eb8, 0x2ebc, 0x2ebf, 0x2ec2, 0x2ec5, 0x2ec8, 0x2ecb, 0x2ece, 0x2ed1, 0x2ed5, 0x2ed8, 0x2edb, 0x2ede, 0x2ee1, 0x2ee5, 0x2ee8, 0x2eeb, 0x2eee, 0x2ef1, 0x2ef5, 0x2ef8, 0x2efb, 0x2efe, 0x2f01, 0x2f05, 0x2f08, 0x2f0b, 0x2f0e, 0x2f12, 0x2f15, 0x2f18, 0x2f1b, 0x2f1f, 0x2f22, 0x2f25, 0x2f29, 0x2f2c, 0x2f2f, 0x2f33, 0x2f36, 0x2f39, 0x2f3d, 0x2f40, 0x2f43, 0x2f47, 0x2f4a, 0x2f4d, 0x2f51, 0x2f54, 0x2f58, 0x2f5b, 0x2f5e, 0x2f62, 0x2f65, 0x2f69, 0x2f6c, 0x2f6f, 0x2f73, 0x2f76, 0x2f7a, 0x2f7d, 0x2f81, 0x2f84, 0x2f88, 0x2f8b, 0x2f8f, 0x2f92, 0x2f96, 0x2f99, 0x2f9d, 0x2fa0, 0x2fa4, 0x2fa7, 0x2fab, 0x2fae, 0x2fb2, 0x2fb5, 0x2fb9, 0x2fbd, 0x2fc0, 0x2fc4, 0x2fc7, 0x2fcb, 0x2fcf, 0x2fd2, 0x2fd6, 0x2fd9, 0x2fdd, 0x2fe1, 0x2fe4, 0x2fe8, 0x2fec, 0x2fef, 0x2ff3, 0x2ff7, 0x2ffa, 0x2ffe, 0x3401, 0x3403, 0x3405, 0x3406, 0x3408, 0x340a, 0x340c, 0x340e, 0x3410, 0x3412, 0x3414, 0x3415, 0x3417, 0x3419, 0x341b, 0x341d, 0x341f, 0x3421, 0x3423, 0x3425, 0x3427, 0x3428, 0x342a, 0x342c, 0x342e, 0x3430, 0x3432, 0x3434, 0x3436, 0x3438, 0x343a, 0x343c, 0x343e, 0x3440, 0x3442, 0x3444, 0x3446, 0x3448, 0x344a, 0x344c, 0x344e, 0x3450, 0x3452, 0x3454, 0x3456, 0x3458, 0x345a, 0x345c, 0x345e, 0x3460, 0x3462, 0x3464, 0x3466, 0x3468, 0x346a, 0x346c, 0x346e, 0x3470, 0x3472, 0x3474, 0x3476, 0x3478, 0x347a, 0x347c, 0x347e, 0x3480, 0x3483, 0x3485, 0x3487, 0x3489, 0x348b, 0x348d, 0x348f, 0x3491, 0x3493, 0x3495, 0x3498, 0x349a, 0x349c, 0x349e, 0x34a0, 0x34a2, 0x34a4, 0x34a6, 0x34a9, 0x34ab, 0x34ad, 0x34af, 0x34b1, 0x34b3, 0x34b6, 0x34b8, 0x34ba, 0x34bc, 0x34be, 0x34c1, 0x34c3, 0x34c5, 0x34c7, 0x34c9, 0x34cc, 0x34ce, 0x34d0, 0x34d2, 0x34d4, 0x34d7, 0x34d9, 0x34db, 0x34dd, 0x34e0, 0x34e2, 0x34e4, 0x34e6, 0x34e9, 0x34eb, 0x34ed, 0x34f0, 0x34f2, 0x34f4, 0x34f6, 0x34f9, 0x34fb, 0x34fd, 0x3500, 0x3502, 0x3504, 0x3507, 0x3509, 0x350b, 0x350e, 0x3510, 0x3512, 0x3515, 0x3517, 0x3519, 0x351c, 0x351e, 0x3520, 0x3523, 0x3525, 0x3528, 0x352a, 0x352c, 0x352f, 0x3531, 0x3533, 0x3536, 0x3538, 0x353b, 0x353d, 0x3540, 0x3542, 0x3544, 0x3547, 0x3549, 0x354c, 0x354e, 0x3551, 0x3553, 0x3556, 0x3558, 0x355a, 0x355d, 0x355f, 0x3562, 0x3564, 0x3567, 0x3569, 0x356c, 0x356e, 0x3571, 0x3573, 0x3576, 0x3578, 0x357b, 0x357e, 0x3580, 0x3583, 0x3585, 0x3588, 0x358a, 0x358d, 0x358f, 0x3592, 0x3595, 0x3597, 0x359a, 0x359c, 0x359f, 0x35a2, 0x35a4, 0x35a7, 0x35a9, 0x35ac, 0x35af, 0x35b1, 0x35b4, 0x35b6, 0x35b9, 0x35bc, 0x35be, 0x35c1, 0x35c4, 0x35c6, 0x35c9, 0x35cc, 0x35ce, 0x35d1, 0x35d4, 0x35d7, 0x35d9, 0x35dc, 0x35df, 0x35e1, 0x35e4, 0x35e7, 0x35ea, 0x35ec, 0x35ef, 0x35f2, 0x35f4, 0x35f7, 0x35fa, 0x35fd, 0x3600, 0x3602, 0x3605, 0x3608, 0x360b, 0x360d, 0x3610, 0x3613, 0x3616, 0x3619, 0x361c, 0x361e, 0x3621, 0x3624, 0x3627, 0x362a, 0x362d, 0x362f, 0x3632, 0x3635, 0x3638, 0x363b, 0x363e, 0x3641, 0x3644, 0x3646, 0x3649, 0x364c, 0x364f, 0x3652, 0x3655, 0x3658, 0x365b, 0x365e, 0x3661, 0x3664, 0x3667, 0x366a, 0x366d, 0x3670, 0x3673, 0x3675, 0x3678, 0x367b, 0x367e, 0x3681, 0x3684, 0x3687, 0x368b, 0x368e, 0x3691, 0x3694, 0x3697, 0x369a, 0x369d, 0x36a0, 0x36a3, 0x36a6, 0x36a9, 0x36ac, 0x36af, 0x36b2, 0x36b5, 0x36b8, 0x36bc, 0x36bf, 0x36c2, 0x36c5, 0x36c8, 0x36cb, 0x36ce, 0x36d1, 0x36d5, 0x36d8, 0x36db, 0x36de, 0x36e1, 0x36e5, 0x36e8, 0x36eb, 0x36ee, 0x36f1, 0x36f5, 0x36f8, 0x36fb, 0x36fe, 0x3701, 0x3705, 0x3708, 0x370b, 0x370e, 0x3712, 0x3715, 0x3718, 0x371b, 0x371f, 0x3722, 0x3725, 0x3729, 0x372c, 0x372f, 0x3733, 0x3736, 0x3739, 0x373d, 0x3740, 0x3743, 0x3747, 0x374a, 0x374d, 0x3751, 0x3754, 0x3758, 0x375b, 0x375e, 0x3762, 0x3765, 0x3769, 0x376c, 0x376f, 0x3773, 0x3776, 0x377a, 0x377d, 0x3781, 0x3784, 0x3788, 0x378b, 0x378f, 0x3792, 0x3796, 0x3799, 0x379d, 0x37a0, 0x37a4, 0x37a7, 0x37ab, 0x37ae, 0x37b2, 0x37b5, 0x37b9, 0x37bd, 0x37c0, 0x37c4, 0x37c7, 0x37cb, 0x37cf, 0x37d2, 0x37d6, 0x37d9, 0x37dd, 0x37e1, 0x37e4, 0x37e8, 0x37ec, 0x37ef, 0x37f3, 0x37f7, 0x37fa, 0x37fe, 0x3c01, 0x3c03, 0x3c05, 0x3c06, 0x3c08, 0x3c0a, 0x3c0c, 0x3c0e, 0x3c10, 0x3c12, 0x3c14, 0x3c15, 0x3c17, 0x3c19, 0x3c1b, 0x3c1d, 0x3c1f, 0x3c21, 0x3c23, 0x3c25, 0x3c27, 0x3c28, 0x3c2a, 0x3c2c, 0x3c2e, 0x3c30, 0x3c32, 0x3c34, 0x3c36, 0x3c38, 0x3c3a, 0x3c3c, 0x3c3e, 0x3c40, 0x3c42, 0x3c44, 0x3c46, 0x3c48, 0x3c4a, 0x3c4c, 0x3c4e, 0x3c50, 0x3c52, 0x3c54, 0x3c56, 0x3c58, 0x3c5a, 0x3c5c, 0x3c5e, 0x3c60, 0x3c62, 0x3c64, 0x3c66, 0x3c68, 0x3c6a, 0x3c6c, 0x3c6e, 0x3c70, 0x3c72, 0x3c74, 0x3c76, 0x3c78, 0x3c7a, 0x3c7c, 0x3c7e, 0x3c80, 0x3c83, 0x3c85, 0x3c87, 0x3c89, 0x3c8b, 0x3c8d, 0x3c8f, 0x3c91, 0x3c93, 0x3c95, 0x3c98, 0x3c9a, 0x3c9c, 0x3c9e, 0x3ca0, 0x3ca2, 0x3ca4, 0x3ca6, 0x3ca9, 0x3cab, 0x3cad, 0x3caf, 0x3cb1, 0x3cb3, 0x3cb6, 0x3cb8, 0x3cba, 0x3cbc, 0x3cbe, 0x3cc1, 0x3cc3, 0x3cc5, 0x3cc7, 0x3cc9, 0x3ccc, 0x3cce, 0x3cd0, 0x3cd2 }; static_assert(sizeof(centTableFM) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, "Invalid FM cent table size."); const uint16_t centTableSSGSquare[3072] = { 0xee8, 0xee1, 0xeda, 0xed4, 0xecd, 0xec6, 0xebf, 0xeb8, 0xeb1, 0xeab, 0xea4, 0xe9d, 0xe96, 0xe90, 0xe89, 0xe82, 0xe7c, 0xe75, 0xe6e, 0xe67, 0xe61, 0xe5a, 0xe54, 0xe4d, 0xe46, 0xe40, 0xe39, 0xe33, 0xe2c, 0xe26, 0xe1f, 0xe18, 0xe12, 0xe0b, 0xe05, 0xdff, 0xdf8, 0xdf2, 0xdeb, 0xde5, 0xdde, 0xdd8, 0xdd2, 0xdcb, 0xdc5, 0xdbe, 0xdb8, 0xdb2, 0xdab, 0xda5, 0xd9f, 0xd99, 0xd92, 0xd8c, 0xd86, 0xd7f, 0xd79, 0xd73, 0xd6d, 0xd67, 0xd60, 0xd5a, 0xd54, 0xd4e, 0xd48, 0xd42, 0xd3c, 0xd35, 0xd2f, 0xd29, 0xd23, 0xd1d, 0xd17, 0xd11, 0xd0b, 0xd05, 0xcff, 0xcf9, 0xcf3, 0xced, 0xce7, 0xce1, 0xcdb, 0xcd5, 0xccf, 0xcc9, 0xcc3, 0xcbe, 0xcb8, 0xcb2, 0xcac, 0xca6, 0xca0, 0xc9a, 0xc95, 0xc8f, 0xc89, 0xc83, 0xc7d, 0xc78, 0xc72, 0xc6c, 0xc66, 0xc61, 0xc5b, 0xc55, 0xc50, 0xc4a, 0xc44, 0xc3f, 0xc39, 0xc33, 0xc2e, 0xc28, 0xc22, 0xc1d, 0xc17, 0xc12, 0xc0c, 0xc06, 0xc01, 0xbfb, 0xbf6, 0xbf0, 0xbeb, 0xbe5, 0xbe0, 0xbda, 0xbd5, 0xbcf, 0xbca, 0xbc5, 0xbbf, 0xbba, 0xbb4, 0xbaf, 0xba9, 0xba4, 0xb9f, 0xb99, 0xb94, 0xb8f, 0xb89, 0xb84, 0xb7f, 0xb79, 0xb74, 0xb6f, 0xb69, 0xb64, 0xb5f, 0xb5a, 0xb54, 0xb4f, 0xb4a, 0xb45, 0xb40, 0xb3a, 0xb35, 0xb30, 0xb2b, 0xb26, 0xb21, 0xb1b, 0xb16, 0xb11, 0xb0c, 0xb07, 0xb02, 0xafd, 0xaf8, 0xaf3, 0xaee, 0xae9, 0xae4, 0xadf, 0xad9, 0xad4, 0xacf, 0xaca, 0xac6, 0xac1, 0xabc, 0xab7, 0xab2, 0xaad, 0xaa8, 0xaa3, 0xa9e, 0xa99, 0xa94, 0xa8f, 0xa8a, 0xa86, 0xa81, 0xa7c, 0xa77, 0xa72, 0xa6d, 0xa69, 0xa64, 0xa5f, 0xa5a, 0xa55, 0xa51, 0xa4c, 0xa47, 0xa42, 0xa3e, 0xa39, 0xa34, 0xa2f, 0xa2b, 0xa26, 0xa21, 0xa1d, 0xa18, 0xa13, 0xa0f, 0xa0a, 0xa05, 0xa01, 0x9fc, 0x9f8, 0x9f3, 0x9ee, 0x9ea, 0x9e5, 0x9e1, 0x9dc, 0x9d8, 0x9d3, 0x9ce, 0x9ca, 0x9c5, 0x9c1, 0x9bc, 0x9b8, 0x9b3, 0x9af, 0x9aa, 0x9a6, 0x9a2, 0x99d, 0x999, 0x994, 0x990, 0x98b, 0x987, 0x983, 0x97e, 0x97a, 0x975, 0x971, 0x96d, 0x968, 0x964, 0x960, 0x95b, 0x957, 0x953, 0x94e, 0x94a, 0x946, 0x942, 0x93d, 0x939, 0x935, 0x931, 0x92c, 0x928, 0x924, 0x920, 0x91b, 0x917, 0x913, 0x90f, 0x90b, 0x906, 0x902, 0x8fe, 0x8fa, 0x8f6, 0x8f2, 0x8ee, 0x8e9, 0x8e5, 0x8e1, 0x8dd, 0x8d9, 0x8d5, 0x8d1, 0x8cd, 0x8c9, 0x8c5, 0x8c1, 0x8bd, 0x8b9, 0x8b4, 0x8b0, 0x8ac, 0x8a8, 0x8a4, 0x8a0, 0x89c, 0x899, 0x895, 0x891, 0x88d, 0x889, 0x885, 0x881, 0x87d, 0x879, 0x875, 0x871, 0x86d, 0x869, 0x865, 0x862, 0x85e, 0x85a, 0x856, 0x852, 0x84e, 0x84a, 0x847, 0x843, 0x83f, 0x83b, 0x837, 0x834, 0x830, 0x82c, 0x828, 0x825, 0x821, 0x81d, 0x819, 0x816, 0x812, 0x80e, 0x80a, 0x807, 0x803, 0x7ff, 0x7fc, 0x7f8, 0x7f4, 0x7f1, 0x7ed, 0x7e9, 0x7e6, 0x7e2, 0x7de, 0x7db, 0x7d7, 0x7d3, 0x7d0, 0x7cc, 0x7c9, 0x7c5, 0x7c1, 0x7be, 0x7ba, 0x7b7, 0x7b3, 0x7b0, 0x7ac, 0x7a8, 0x7a5, 0x7a1, 0x79e, 0x79a, 0x797, 0x793, 0x790, 0x78c, 0x789, 0x785, 0x782, 0x77e, 0x77b, 0x778, 0x774, 0x771, 0x76d, 0x76a, 0x766, 0x763, 0x760, 0x75c, 0x759, 0x755, 0x752, 0x74f, 0x74b, 0x748, 0x744, 0x741, 0x73e, 0x73a, 0x737, 0x734, 0x730, 0x72d, 0x72a, 0x726, 0x723, 0x720, 0x71d, 0x719, 0x716, 0x713, 0x70f, 0x70c, 0x709, 0x706, 0x702, 0x6ff, 0x6fc, 0x6f9, 0x6f6, 0x6f2, 0x6ef, 0x6ec, 0x6e9, 0x6e6, 0x6e2, 0x6df, 0x6dc, 0x6d9, 0x6d6, 0x6d3, 0x6cf, 0x6cc, 0x6c9, 0x6c6, 0x6c3, 0x6c0, 0x6bd, 0x6ba, 0x6b6, 0x6b3, 0x6b0, 0x6ad, 0x6aa, 0x6a7, 0x6a4, 0x6a1, 0x69e, 0x69b, 0x698, 0x695, 0x692, 0x68f, 0x68c, 0x689, 0x685, 0x682, 0x67f, 0x67c, 0x679, 0x676, 0x674, 0x671, 0x66e, 0x66b, 0x668, 0x665, 0x662, 0x65f, 0x65c, 0x659, 0x656, 0x653, 0x650, 0x64d, 0x64a, 0x647, 0x644, 0x642, 0x63f, 0x63c, 0x639, 0x636, 0x633, 0x630, 0x62d, 0x62b, 0x628, 0x625, 0x622, 0x61f, 0x61c, 0x61a, 0x617, 0x614, 0x611, 0x60e, 0x60c, 0x609, 0x606, 0x603, 0x600, 0x5fe, 0x5fb, 0x5f8, 0x5f5, 0x5f3, 0x5f0, 0x5ed, 0x5ea, 0x5e8, 0x5e5, 0x5e2, 0x5e0, 0x5dd, 0x5da, 0x5d7, 0x5d5, 0x5d2, 0x5cf, 0x5cd, 0x5ca, 0x5c7, 0x5c5, 0x5c2, 0x5bf, 0x5bd, 0x5ba, 0x5b7, 0x5b5, 0x5b2, 0x5af, 0x5ad, 0x5aa, 0x5a8, 0x5a5, 0x5a2, 0x5a0, 0x59d, 0x59b, 0x598, 0x595, 0x593, 0x590, 0x58e, 0x58b, 0x589, 0x586, 0x583, 0x581, 0x57e, 0x57c, 0x579, 0x577, 0x574, 0x572, 0x56f, 0x56d, 0x56a, 0x568, 0x565, 0x563, 0x560, 0x55e, 0x55b, 0x559, 0x556, 0x554, 0x551, 0x54f, 0x54d, 0x54a, 0x548, 0x545, 0x543, 0x540, 0x53e, 0x53c, 0x539, 0x537, 0x534, 0x532, 0x52f, 0x52d, 0x52b, 0x528, 0x526, 0x524, 0x521, 0x51f, 0x51c, 0x51a, 0x518, 0x515, 0x513, 0x511, 0x50e, 0x50c, 0x50a, 0x507, 0x505, 0x503, 0x500, 0x4fe, 0x4fc, 0x4f9, 0x4f7, 0x4f5, 0x4f3, 0x4f0, 0x4ee, 0x4ec, 0x4e9, 0x4e7, 0x4e5, 0x4e3, 0x4e0, 0x4de, 0x4dc, 0x4da, 0x4d7, 0x4d5, 0x4d3, 0x4d1, 0x4cf, 0x4cc, 0x4ca, 0x4c8, 0x4c6, 0x4c3, 0x4c1, 0x4bf, 0x4bd, 0x4bb, 0x4b9, 0x4b6, 0x4b4, 0x4b2, 0x4b0, 0x4ae, 0x4ac, 0x4a9, 0x4a7, 0x4a5, 0x4a3, 0x4a1, 0x49f, 0x49d, 0x49a, 0x498, 0x496, 0x494, 0x492, 0x490, 0x48e, 0x48c, 0x489, 0x487, 0x485, 0x483, 0x481, 0x47f, 0x47d, 0x47b, 0x479, 0x477, 0x475, 0x473, 0x471, 0x46f, 0x46c, 0x46a, 0x468, 0x466, 0x464, 0x462, 0x460, 0x45e, 0x45c, 0x45a, 0x458, 0x456, 0x454, 0x452, 0x450, 0x44e, 0x44c, 0x44a, 0x448, 0x446, 0x444, 0x442, 0x440, 0x43e, 0x43c, 0x43b, 0x439, 0x437, 0x435, 0x433, 0x431, 0x42f, 0x42d, 0x42b, 0x429, 0x427, 0x425, 0x423, 0x421, 0x420, 0x41e, 0x41c, 0x41a, 0x418, 0x416, 0x414, 0x412, 0x410, 0x40f, 0x40d, 0x40b, 0x409, 0x407, 0x405, 0x403, 0x401, 0x400, 0x3fe, 0x3fc, 0x3fa, 0x3f8, 0x3f6, 0x3f5, 0x3f3, 0x3f1, 0x3ef, 0x3ed, 0x3eb, 0x3ea, 0x3e8, 0x3e6, 0x3e4, 0x3e2, 0x3e1, 0x3df, 0x3dd, 0x3db, 0x3da, 0x3d8, 0x3d6, 0x3d4, 0x3d2, 0x3d1, 0x3cf, 0x3cd, 0x3cb, 0x3ca, 0x3c8, 0x3c6, 0x3c4, 0x3c3, 0x3c1, 0x3bf, 0x3bd, 0x3bc, 0x3ba, 0x3b8, 0x3b7, 0x3b5, 0x3b3, 0x3b1, 0x3b0, 0x3ae, 0x3ac, 0x3ab, 0x3a9, 0x3a7, 0x3a6, 0x3a4, 0x3a2, 0x3a1, 0x39f, 0x39d, 0x39c, 0x39a, 0x398, 0x397, 0x395, 0x393, 0x392, 0x390, 0x38e, 0x38d, 0x38b, 0x389, 0x388, 0x386, 0x384, 0x383, 0x381, 0x380, 0x37e, 0x37c, 0x37b, 0x379, 0x378, 0x376, 0x374, 0x373, 0x371, 0x370, 0x36e, 0x36c, 0x36b, 0x369, 0x368, 0x366, 0x365, 0x363, 0x361, 0x360, 0x35e, 0x35d, 0x35b, 0x35a, 0x358, 0x357, 0x355, 0x353, 0x352, 0x350, 0x34f, 0x34d, 0x34c, 0x34a, 0x349, 0x347, 0x346, 0x344, 0x343, 0x341, 0x340, 0x33e, 0x33d, 0x33b, 0x33a, 0x338, 0x337, 0x335, 0x334, 0x332, 0x331, 0x32f, 0x32e, 0x32c, 0x32b, 0x32a, 0x328, 0x327, 0x325, 0x324, 0x322, 0x321, 0x31f, 0x31e, 0x31c, 0x31b, 0x31a, 0x318, 0x317, 0x315, 0x314, 0x312, 0x311, 0x310, 0x30e, 0x30d, 0x30b, 0x30a, 0x309, 0x307, 0x306, 0x304, 0x303, 0x302, 0x300, 0x2ff, 0x2fd, 0x2fc, 0x2fb, 0x2f9, 0x2f8, 0x2f7, 0x2f5, 0x2f4, 0x2f2, 0x2f1, 0x2f0, 0x2ee, 0x2ed, 0x2ec, 0x2ea, 0x2e9, 0x2e8, 0x2e6, 0x2e5, 0x2e4, 0x2e2, 0x2e1, 0x2e0, 0x2de, 0x2dd, 0x2dc, 0x2da, 0x2d9, 0x2d8, 0x2d6, 0x2d5, 0x2d4, 0x2d3, 0x2d1, 0x2d0, 0x2cf, 0x2cd, 0x2cc, 0x2cb, 0x2c9, 0x2c8, 0x2c7, 0x2c6, 0x2c4, 0x2c3, 0x2c2, 0x2c0, 0x2bf, 0x2be, 0x2bd, 0x2bb, 0x2ba, 0x2b9, 0x2b8, 0x2b6, 0x2b5, 0x2b4, 0x2b3, 0x2b1, 0x2b0, 0x2af, 0x2ae, 0x2ac, 0x2ab, 0x2aa, 0x2a9, 0x2a7, 0x2a6, 0x2a5, 0x2a4, 0x2a3, 0x2a1, 0x2a0, 0x29f, 0x29e, 0x29d, 0x29b, 0x29a, 0x299, 0x298, 0x297, 0x295, 0x294, 0x293, 0x292, 0x291, 0x28f, 0x28e, 0x28d, 0x28c, 0x28b, 0x28a, 0x288, 0x287, 0x286, 0x285, 0x284, 0x283, 0x281, 0x280, 0x27f, 0x27e, 0x27d, 0x27c, 0x27a, 0x279, 0x278, 0x277, 0x276, 0x275, 0x274, 0x272, 0x271, 0x270, 0x26f, 0x26e, 0x26d, 0x26c, 0x26b, 0x26a, 0x268, 0x267, 0x266, 0x265, 0x264, 0x263, 0x262, 0x261, 0x260, 0x25e, 0x25d, 0x25c, 0x25b, 0x25a, 0x259, 0x258, 0x257, 0x256, 0x255, 0x254, 0x253, 0x251, 0x250, 0x24f, 0x24e, 0x24d, 0x24c, 0x24b, 0x24a, 0x249, 0x248, 0x247, 0x246, 0x245, 0x244, 0x243, 0x242, 0x241, 0x240, 0x23e, 0x23d, 0x23c, 0x23b, 0x23a, 0x239, 0x238, 0x237, 0x236, 0x235, 0x234, 0x233, 0x232, 0x231, 0x230, 0x22f, 0x22e, 0x22d, 0x22c, 0x22b, 0x22a, 0x229, 0x228, 0x227, 0x226, 0x225, 0x224, 0x223, 0x222, 0x221, 0x220, 0x21f, 0x21e, 0x21d, 0x21c, 0x21b, 0x21a, 0x219, 0x218, 0x217, 0x216, 0x216, 0x215, 0x214, 0x213, 0x212, 0x211, 0x210, 0x20f, 0x20e, 0x20d, 0x20c, 0x20b, 0x20a, 0x209, 0x208, 0x207, 0x206, 0x205, 0x204, 0x204, 0x203, 0x202, 0x201, 0x200, 0x1ff, 0x1fe, 0x1fd, 0x1fc, 0x1fb, 0x1fa, 0x1f9, 0x1f8, 0x1f8, 0x1f7, 0x1f6, 0x1f5, 0x1f4, 0x1f3, 0x1f2, 0x1f1, 0x1f0, 0x1ef, 0x1ef, 0x1ee, 0x1ed, 0x1ec, 0x1eb, 0x1ea, 0x1e9, 0x1e8, 0x1e7, 0x1e7, 0x1e6, 0x1e5, 0x1e4, 0x1e3, 0x1e2, 0x1e1, 0x1e0, 0x1e0, 0x1df, 0x1de, 0x1dd, 0x1dc, 0x1db, 0x1da, 0x1da, 0x1d9, 0x1d8, 0x1d7, 0x1d6, 0x1d5, 0x1d4, 0x1d4, 0x1d3, 0x1d2, 0x1d1, 0x1d0, 0x1cf, 0x1cf, 0x1ce, 0x1cd, 0x1cc, 0x1cb, 0x1ca, 0x1ca, 0x1c9, 0x1c8, 0x1c7, 0x1c6, 0x1c6, 0x1c5, 0x1c4, 0x1c3, 0x1c2, 0x1c1, 0x1c1, 0x1c0, 0x1bf, 0x1be, 0x1bd, 0x1bd, 0x1bc, 0x1bb, 0x1ba, 0x1b9, 0x1b9, 0x1b8, 0x1b7, 0x1b6, 0x1b5, 0x1b5, 0x1b4, 0x1b3, 0x1b2, 0x1b1, 0x1b1, 0x1b0, 0x1af, 0x1ae, 0x1ae, 0x1ad, 0x1ac, 0x1ab, 0x1ab, 0x1aa, 0x1a9, 0x1a8, 0x1a7, 0x1a7, 0x1a6, 0x1a5, 0x1a4, 0x1a4, 0x1a3, 0x1a2, 0x1a1, 0x1a1, 0x1a0, 0x19f, 0x19e, 0x19e, 0x19d, 0x19c, 0x19b, 0x19b, 0x19a, 0x199, 0x198, 0x198, 0x197, 0x196, 0x195, 0x195, 0x194, 0x193, 0x193, 0x192, 0x191, 0x190, 0x190, 0x18f, 0x18e, 0x18e, 0x18d, 0x18c, 0x18b, 0x18b, 0x18a, 0x189, 0x189, 0x188, 0x187, 0x186, 0x186, 0x185, 0x184, 0x184, 0x183, 0x182, 0x182, 0x181, 0x180, 0x17f, 0x17f, 0x17e, 0x17d, 0x17d, 0x17c, 0x17b, 0x17b, 0x17a, 0x179, 0x179, 0x178, 0x177, 0x177, 0x176, 0x175, 0x175, 0x174, 0x173, 0x172, 0x172, 0x171, 0x170, 0x170, 0x16f, 0x16f, 0x16e, 0x16d, 0x16d, 0x16c, 0x16b, 0x16b, 0x16a, 0x169, 0x169, 0x168, 0x167, 0x167, 0x166, 0x165, 0x165, 0x164, 0x163, 0x163, 0x162, 0x162, 0x161, 0x160, 0x160, 0x15f, 0x15e, 0x15e, 0x15d, 0x15c, 0x15c, 0x15b, 0x15b, 0x15a, 0x159, 0x159, 0x158, 0x157, 0x157, 0x156, 0x156, 0x155, 0x154, 0x154, 0x153, 0x153, 0x152, 0x151, 0x151, 0x150, 0x14f, 0x14f, 0x14e, 0x14e, 0x14d, 0x14c, 0x14c, 0x14b, 0x14b, 0x14a, 0x149, 0x149, 0x148, 0x148, 0x147, 0x147, 0x146, 0x145, 0x145, 0x144, 0x144, 0x143, 0x142, 0x142, 0x141, 0x141, 0x140, 0x140, 0x13f, 0x13e, 0x13e, 0x13d, 0x13d, 0x13c, 0x13c, 0x13b, 0x13a, 0x13a, 0x139, 0x139, 0x138, 0x138, 0x137, 0x136, 0x136, 0x135, 0x135, 0x134, 0x134, 0x133, 0x133, 0x132, 0x131, 0x131, 0x130, 0x130, 0x12f, 0x12f, 0x12e, 0x12e, 0x12d, 0x12d, 0x12c, 0x12b, 0x12b, 0x12a, 0x12a, 0x129, 0x129, 0x128, 0x128, 0x127, 0x127, 0x126, 0x126, 0x125, 0x124, 0x124, 0x123, 0x123, 0x122, 0x122, 0x121, 0x121, 0x120, 0x120, 0x11f, 0x11f, 0x11e, 0x11e, 0x11d, 0x11d, 0x11c, 0x11c, 0x11b, 0x11b, 0x11a, 0x11a, 0x119, 0x119, 0x118, 0x118, 0x117, 0x117, 0x116, 0x116, 0x115, 0x115, 0x114, 0x114, 0x113, 0x113, 0x112, 0x112, 0x111, 0x111, 0x110, 0x110, 0x10f, 0x10f, 0x10e, 0x10e, 0x10d, 0x10d, 0x10c, 0x10c, 0x10b, 0x10b, 0x10a, 0x10a, 0x109, 0x109, 0x108, 0x108, 0x107, 0x107, 0x106, 0x106, 0x106, 0x105, 0x105, 0x104, 0x104, 0x103, 0x103, 0x102, 0x102, 0x101, 0x101, 0x100, 0x100, 0x0ff, 0x0ff, 0x0ff, 0x0fe, 0x0fe, 0x0fd, 0x0fd, 0x0fc, 0x0fc, 0x0fb, 0x0fb, 0x0fa, 0x0fa, 0x0fa, 0x0f9, 0x0f9, 0x0f8, 0x0f8, 0x0f7, 0x0f7, 0x0f6, 0x0f6, 0x0f5, 0x0f5, 0x0f5, 0x0f4, 0x0f4, 0x0f3, 0x0f3, 0x0f2, 0x0f2, 0x0f2, 0x0f1, 0x0f1, 0x0f0, 0x0f0, 0x0ef, 0x0ef, 0x0ef, 0x0ee, 0x0ee, 0x0ed, 0x0ed, 0x0ec, 0x0ec, 0x0ec, 0x0eb, 0x0eb, 0x0ea, 0x0ea, 0x0e9, 0x0e9, 0x0e9, 0x0e8, 0x0e8, 0x0e7, 0x0e7, 0x0e6, 0x0e6, 0x0e6, 0x0e5, 0x0e5, 0x0e4, 0x0e4, 0x0e4, 0x0e3, 0x0e3, 0x0e2, 0x0e2, 0x0e2, 0x0e1, 0x0e1, 0x0e0, 0x0e0, 0x0e0, 0x0df, 0x0df, 0x0de, 0x0de, 0x0dd, 0x0dd, 0x0dd, 0x0dc, 0x0dc, 0x0dc, 0x0db, 0x0db, 0x0da, 0x0da, 0x0da, 0x0d9, 0x0d9, 0x0d8, 0x0d8, 0x0d8, 0x0d7, 0x0d7, 0x0d6, 0x0d6, 0x0d6, 0x0d5, 0x0d5, 0x0d4, 0x0d4, 0x0d4, 0x0d3, 0x0d3, 0x0d3, 0x0d2, 0x0d2, 0x0d1, 0x0d1, 0x0d1, 0x0d0, 0x0d0, 0x0d0, 0x0cf, 0x0cf, 0x0ce, 0x0ce, 0x0ce, 0x0cd, 0x0cd, 0x0cd, 0x0cc, 0x0cc, 0x0cb, 0x0cb, 0x0cb, 0x0ca, 0x0ca, 0x0ca, 0x0c9, 0x0c9, 0x0c9, 0x0c8, 0x0c8, 0x0c7, 0x0c7, 0x0c7, 0x0c6, 0x0c6, 0x0c6, 0x0c5, 0x0c5, 0x0c5, 0x0c4, 0x0c4, 0x0c4, 0x0c3, 0x0c3, 0x0c3, 0x0c2, 0x0c2, 0x0c1, 0x0c1, 0x0c1, 0x0c0, 0x0c0, 0x0c0, 0x0bf, 0x0bf, 0x0bf, 0x0be, 0x0be, 0x0be, 0x0bd, 0x0bd, 0x0bd, 0x0bc, 0x0bc, 0x0bc, 0x0bb, 0x0bb, 0x0bb, 0x0ba, 0x0ba, 0x0ba, 0x0b9, 0x0b9, 0x0b9, 0x0b8, 0x0b8, 0x0b8, 0x0b7, 0x0b7, 0x0b7, 0x0b6, 0x0b6, 0x0b6, 0x0b5, 0x0b5, 0x0b5, 0x0b4, 0x0b4, 0x0b4, 0x0b3, 0x0b3, 0x0b3, 0x0b2, 0x0b2, 0x0b2, 0x0b1, 0x0b1, 0x0b1, 0x0b0, 0x0b0, 0x0b0, 0x0af, 0x0af, 0x0af, 0x0af, 0x0ae, 0x0ae, 0x0ae, 0x0ad, 0x0ad, 0x0ad, 0x0ac, 0x0ac, 0x0ac, 0x0ab, 0x0ab, 0x0ab, 0x0aa, 0x0aa, 0x0aa, 0x0aa, 0x0a9, 0x0a9, 0x0a9, 0x0a8, 0x0a8, 0x0a8, 0x0a7, 0x0a7, 0x0a7, 0x0a7, 0x0a6, 0x0a6, 0x0a6, 0x0a5, 0x0a5, 0x0a5, 0x0a4, 0x0a4, 0x0a4, 0x0a4, 0x0a3, 0x0a3, 0x0a3, 0x0a2, 0x0a2, 0x0a2, 0x0a2, 0x0a1, 0x0a1, 0x0a1, 0x0a0, 0x0a0, 0x0a0, 0x09f, 0x09f, 0x09f, 0x09f, 0x09e, 0x09e, 0x09e, 0x09d, 0x09d, 0x09d, 0x09d, 0x09c, 0x09c, 0x09c, 0x09b, 0x09b, 0x09b, 0x09b, 0x09a, 0x09a, 0x09a, 0x09a, 0x099, 0x099, 0x099, 0x098, 0x098, 0x098, 0x098, 0x097, 0x097, 0x097, 0x097, 0x096, 0x096, 0x096, 0x095, 0x095, 0x095, 0x095, 0x094, 0x094, 0x094, 0x094, 0x093, 0x093, 0x093, 0x093, 0x092, 0x092, 0x092, 0x091, 0x091, 0x091, 0x091, 0x090, 0x090, 0x090, 0x090, 0x08f, 0x08f, 0x08f, 0x08f, 0x08e, 0x08e, 0x08e, 0x08e, 0x08d, 0x08d, 0x08d, 0x08d, 0x08c, 0x08c, 0x08c, 0x08c, 0x08b, 0x08b, 0x08b, 0x08b, 0x08a, 0x08a, 0x08a, 0x08a, 0x089, 0x089, 0x089, 0x089, 0x088, 0x088, 0x088, 0x088, 0x087, 0x087, 0x087, 0x087, 0x086, 0x086, 0x086, 0x086, 0x085, 0x085, 0x085, 0x085, 0x084, 0x084, 0x084, 0x084, 0x083, 0x083, 0x083, 0x083, 0x083, 0x082, 0x082, 0x082, 0x082, 0x081, 0x081, 0x081, 0x081, 0x080, 0x080, 0x080, 0x080, 0x07f, 0x07f, 0x07f, 0x07f, 0x07f, 0x07e, 0x07e, 0x07e, 0x07e, 0x07d, 0x07d, 0x07d, 0x07d, 0x07d, 0x07c, 0x07c, 0x07c, 0x07c, 0x07b, 0x07b, 0x07b, 0x07b, 0x07b, 0x07a, 0x07a, 0x07a, 0x07a, 0x079, 0x079, 0x079, 0x079, 0x079, 0x078, 0x078, 0x078, 0x078, 0x077, 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f }; static_assert(sizeof(centTableSSGSquare) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, "Invalid SSG square cent table size."); const uint16_t centTableSSGTriangle[3072] = { 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001 }; static_assert(sizeof(centTableSSGTriangle) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, "Invalid SSG triangle cent table size."); const uint16_t centTableSSGSaw[3072] = { 0x0ef, 0x0ee, 0x0ee, 0x0ed, 0x0ed, 0x0ec, 0x0ec, 0x0ec, 0x0eb, 0x0eb, 0x0ea, 0x0ea, 0x0e9, 0x0e9, 0x0e9, 0x0e8, 0x0e8, 0x0e7, 0x0e7, 0x0e6, 0x0e6, 0x0e6, 0x0e5, 0x0e5, 0x0e4, 0x0e4, 0x0e4, 0x0e3, 0x0e3, 0x0e2, 0x0e2, 0x0e2, 0x0e1, 0x0e1, 0x0e0, 0x0e0, 0x0e0, 0x0df, 0x0df, 0x0de, 0x0de, 0x0dd, 0x0dd, 0x0dd, 0x0dc, 0x0dc, 0x0dc, 0x0db, 0x0db, 0x0da, 0x0da, 0x0da, 0x0d9, 0x0d9, 0x0d8, 0x0d8, 0x0d8, 0x0d7, 0x0d7, 0x0d6, 0x0d6, 0x0d6, 0x0d5, 0x0d5, 0x0d4, 0x0d4, 0x0d4, 0x0d3, 0x0d3, 0x0d3, 0x0d2, 0x0d2, 0x0d1, 0x0d1, 0x0d1, 0x0d0, 0x0d0, 0x0d0, 0x0cf, 0x0cf, 0x0ce, 0x0ce, 0x0ce, 0x0cd, 0x0cd, 0x0cd, 0x0cc, 0x0cc, 0x0cb, 0x0cb, 0x0cb, 0x0ca, 0x0ca, 0x0ca, 0x0c9, 0x0c9, 0x0c9, 0x0c8, 0x0c8, 0x0c7, 0x0c7, 0x0c7, 0x0c6, 0x0c6, 0x0c6, 0x0c5, 0x0c5, 0x0c5, 0x0c4, 0x0c4, 0x0c4, 0x0c3, 0x0c3, 0x0c3, 0x0c2, 0x0c2, 0x0c1, 0x0c1, 0x0c1, 0x0c0, 0x0c0, 0x0c0, 0x0bf, 0x0bf, 0x0bf, 0x0be, 0x0be, 0x0be, 0x0bd, 0x0bd, 0x0bd, 0x0bc, 0x0bc, 0x0bc, 0x0bb, 0x0bb, 0x0bb, 0x0ba, 0x0ba, 0x0ba, 0x0b9, 0x0b9, 0x0b9, 0x0b8, 0x0b8, 0x0b8, 0x0b7, 0x0b7, 0x0b7, 0x0b6, 0x0b6, 0x0b6, 0x0b5, 0x0b5, 0x0b5, 0x0b4, 0x0b4, 0x0b4, 0x0b3, 0x0b3, 0x0b3, 0x0b2, 0x0b2, 0x0b2, 0x0b1, 0x0b1, 0x0b1, 0x0b0, 0x0b0, 0x0b0, 0x0af, 0x0af, 0x0af, 0x0af, 0x0ae, 0x0ae, 0x0ae, 0x0ad, 0x0ad, 0x0ad, 0x0ac, 0x0ac, 0x0ac, 0x0ab, 0x0ab, 0x0ab, 0x0aa, 0x0aa, 0x0aa, 0x0aa, 0x0a9, 0x0a9, 0x0a9, 0x0a8, 0x0a8, 0x0a8, 0x0a7, 0x0a7, 0x0a7, 0x0a7, 0x0a6, 0x0a6, 0x0a6, 0x0a5, 0x0a5, 0x0a5, 0x0a4, 0x0a4, 0x0a4, 0x0a4, 0x0a3, 0x0a3, 0x0a3, 0x0a2, 0x0a2, 0x0a2, 0x0a2, 0x0a1, 0x0a1, 0x0a1, 0x0a0, 0x0a0, 0x0a0, 0x09f, 0x09f, 0x09f, 0x09f, 0x09e, 0x09e, 0x09e, 0x09d, 0x09d, 0x09d, 0x09d, 0x09c, 0x09c, 0x09c, 0x09b, 0x09b, 0x09b, 0x09b, 0x09a, 0x09a, 0x09a, 0x09a, 0x099, 0x099, 0x099, 0x098, 0x098, 0x098, 0x098, 0x097, 0x097, 0x097, 0x097, 0x096, 0x096, 0x096, 0x095, 0x095, 0x095, 0x095, 0x094, 0x094, 0x094, 0x094, 0x093, 0x093, 0x093, 0x093, 0x092, 0x092, 0x092, 0x091, 0x091, 0x091, 0x091, 0x090, 0x090, 0x090, 0x090, 0x08f, 0x08f, 0x08f, 0x08f, 0x08e, 0x08e, 0x08e, 0x08e, 0x08d, 0x08d, 0x08d, 0x08d, 0x08c, 0x08c, 0x08c, 0x08c, 0x08b, 0x08b, 0x08b, 0x08b, 0x08a, 0x08a, 0x08a, 0x08a, 0x089, 0x089, 0x089, 0x089, 0x088, 0x088, 0x088, 0x088, 0x087, 0x087, 0x087, 0x087, 0x086, 0x086, 0x086, 0x086, 0x085, 0x085, 0x085, 0x085, 0x084, 0x084, 0x084, 0x084, 0x083, 0x083, 0x083, 0x083, 0x083, 0x082, 0x082, 0x082, 0x082, 0x081, 0x081, 0x081, 0x081, 0x080, 0x080, 0x080, 0x080, 0x07f, 0x07f, 0x07f, 0x07f, 0x07f, 0x07e, 0x07e, 0x07e, 0x07e, 0x07d, 0x07d, 0x07d, 0x07d, 0x07d, 0x07c, 0x07c, 0x07c, 0x07c, 0x07b, 0x07b, 0x07b, 0x07b, 0x07b, 0x07a, 0x07a, 0x07a, 0x07a, 0x079, 0x079, 0x079, 0x079, 0x079, 0x078, 0x078, 0x078, 0x078, 0x077, 0x077, 0x077, 0x077, 0x077, 0x076, 0x076, 0x076, 0x076, 0x076, 0x075, 0x075, 0x075, 0x075, 0x074, 0x074, 0x074, 0x074, 0x074, 0x073, 0x073, 0x073, 0x073, 0x073, 0x072, 0x072, 0x072, 0x072, 0x072, 0x071, 0x071, 0x071, 0x071, 0x071, 0x070, 0x070, 0x070, 0x070, 0x070, 0x06f, 0x06f, 0x06f, 0x06f, 0x06f, 0x06e, 0x06e, 0x06e, 0x06e, 0x06e, 0x06d, 0x06d, 0x06d, 0x06d, 0x06d, 0x06c, 0x06c, 0x06c, 0x06c, 0x06c, 0x06b, 0x06b, 0x06b, 0x06b, 0x06b, 0x06a, 0x06a, 0x06a, 0x06a, 0x06a, 0x069, 0x069, 0x069, 0x069, 0x069, 0x069, 0x068, 0x068, 0x068, 0x068, 0x068, 0x067, 0x067, 0x067, 0x067, 0x067, 0x066, 0x066, 0x066, 0x066, 0x066, 0x066, 0x065, 0x065, 0x065, 0x065, 0x065, 0x064, 0x064, 0x064, 0x064, 0x064, 0x064, 0x063, 0x063, 0x063, 0x063, 0x063, 0x062, 0x062, 0x062, 0x062, 0x062, 0x062, 0x061, 0x061, 0x061, 0x061, 0x061, 0x061, 0x060, 0x060, 0x060, 0x060, 0x060, 0x060, 0x05f, 0x05f, 0x05f, 0x05f, 0x05f, 0x05e, 0x05e, 0x05e, 0x05e, 0x05e, 0x05e, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05d, 0x05c, 0x05c, 0x05c, 0x05c, 0x05c, 0x05c, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05b, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x05a, 0x059, 0x059, 0x059, 0x059, 0x059, 0x059, 0x058, 0x058, 0x058, 0x058, 0x058, 0x058, 0x057, 0x057, 0x057, 0x057, 0x057, 0x057, 0x056, 0x056, 0x056, 0x056, 0x056, 0x056, 0x056, 0x055, 0x055, 0x055, 0x055, 0x055, 0x055, 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x054, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, 0x053, 0x052, 0x052, 0x052, 0x052, 0x052, 0x052, 0x051, 0x051, 0x051, 0x051, 0x051, 0x051, 0x051, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x050, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04f, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04e, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04d, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04c, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04b, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x04a, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x049, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x048, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x047, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x046, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x045, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x044, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x043, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x042, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x041, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03f, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03e, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03d, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03c, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03b, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x03a, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x039, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x038, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x037, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x036, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x035, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x034, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x033, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x032, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x031, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x030, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02f, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02e, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02d, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02c, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02b, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x02a, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x029, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x028, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x027, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x026, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x025, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x024, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x023, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x022, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x021, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01f, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01e, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01d, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01c, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01b, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x01a, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x019, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x018, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x017, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x016, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x015, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x014, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x013, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x012, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x011, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00e, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00d, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00c, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00b, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x00a, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x009, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x007, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x006, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001 }; static_assert(sizeof(centTableSSGSaw) == sizeof(uint16_t) * Note::ABS_PITCH_RANGE, "Invalid SSG saw cent table size."); } uint16_t calculateFNumber(int absPitch, int finePitch) { uint16_t p = centTableFM[absPitch]; return (finePitch ? (finePitch > 0 ? p + finePitch : p - static_cast(-finePitch)) : p); } uint16_t calculateSSGSquareTP(int absPitch, int finePitch) { uint16_t p = centTableSSGSquare[absPitch]; return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) : p); } uint16_t calculateSSGTriangleEP(int absPitch, int finePitch) { uint16_t p = centTableSSGTriangle[absPitch]; return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) : p); } uint16_t calculateSSGSawEP(int absPitch, int finePitch) { uint16_t p = centTableSSGSaw[absPitch]; return (finePitch ? (finePitch > 0 ? p - finePitch : p + static_cast(-finePitch)) : p); } } Note::Note(int noteNum) : request_eval_(false) { if (noteNum < 0) { octave_ = 0; name_ = NoteName::C; pitch_ = 0; return; } octave_ = noteNum / 12; if (octave_ > OCTAVE_RANGE - 1) { octave_ = OCTAVE_RANGE - 1; name_ = NoteName::B; pitch_ = SEMINOTE_PITCH - 1; return; } name_ = static_cast(noteNum % 12); pitch_ = 0; } void Note::evaluateState(int octave, int note, int pitch) { pitch_ = pitch % SEMINOTE_PITCH; note += pitch / SEMINOTE_PITCH; name_ = ((note % 12) + 12) % 12;; octave_ = octave + std::floor(note / 12.); if (octave_ < 0 || (!octave_ && !name_ && pitch_ < 0)) { octave_ = 0; name_ = NoteName::C; pitch_ = 0; } else if (OCTAVE_RANGE <= octave_) { octave_ = OCTAVE_RANGE - 1; name_ = NoteName::B; pitch_ = SEMINOTE_PITCH - 1; } request_eval_ = false; } BambooTracker-0.4.6/BambooTracker/note.hpp000066400000000000000000000116161401124043500204240ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include class Note; namespace note_utils { uint16_t calculateFNumber(int absPitch, int finePitch); uint16_t calculateSSGSquareTP(int absPitch, int finePitch); uint16_t calculateSSGTriangleEP(int absPitch, int finePitch); uint16_t calculateSSGSawEP(int absPitch, int finePitch); } class Note { public: enum NoteName { C = 0, CS, D, DS, E, F, FS, G, GS, A, AS, B }; static constexpr int OCTAVE_RANGE = 8; static constexpr int DEFAULT_OCTAVE = 4; static constexpr int NOTE_NUMBER_RANGE = OCTAVE_RANGE * 12; // 96 static constexpr int DEFAULT_NOTE_NUM = DEFAULT_OCTAVE * 12; // C4 static constexpr int SEMINOTE_PITCH = 32; static constexpr int ABS_PITCH_RANGE = NOTE_NUMBER_RANGE * SEMINOTE_PITCH; // 3072 Note(int octave, NoteName name, int pitch = 0); explicit Note(int noteNum = DEFAULT_NOTE_NUM); void setOctave(int octave) noexcept; int getOctave(); void setNoteName(NoteName name) noexcept; NoteName getNotename(); void setPitch(int pitch) noexcept; int getPitch(); int getAbsolutePicth(); int getNoteNumber(); void add(const Note& note); void add(int octave, NoteName name, int pitch = 0); void addPitch(int pitch); void addNoteNumber(int nn); friend bool operator==(Note& a, Note& b); friend bool operator!=(Note& a, Note& b) { return !(a == b); } friend Note operator+(const Note& a, const Note& b); friend Note operator+(const Note& a, int b); friend Note operator+(int a, const Note& b); Note& operator+=(const Note& b); Note& operator+=(int b); private: int octave_; int name_; int pitch_; bool request_eval_; Note(int octave, int name, int pitch); void evaluateState(); void evaluateState(int octave, int note, int pitch); }; inline Note::Note(int octave, NoteName name, int pitch) : octave_(octave), name_(name), pitch_(pitch), request_eval_(true) { } inline Note::Note(int octave, int name, int pitch) : octave_(octave), name_(name), pitch_(pitch), request_eval_(true) { } inline void Note::setOctave(int octave) noexcept { octave_ = octave; request_eval_ = true; } inline int Note::getOctave() { if (request_eval_) evaluateState(); return octave_; } inline void Note::setNoteName(NoteName name) noexcept { name_ = name; request_eval_ = true; } inline Note::NoteName Note::getNotename() { if (request_eval_) evaluateState(); return static_cast(name_); } inline void Note::setPitch(int pitch) noexcept { pitch_ = pitch; request_eval_ = true; } inline int Note::getPitch() { if (request_eval_) evaluateState(); return pitch_; } inline int Note::getAbsolutePicth() { if (request_eval_) evaluateState(); return pitch_ + SEMINOTE_PITCH * (name_ + 12 * octave_); } inline int Note::getNoteNumber() { if (request_eval_) evaluateState(); return 12 * octave_ + name_; } inline void Note::add(const Note& note) { add(note.octave_, static_cast(note.name_), note.pitch_); } inline void Note::add(int octave, NoteName name, int pitch) { octave_ += octave; name_ += name; pitch_ += pitch; request_eval_ = true; } inline void Note::addPitch(int pitch) { pitch_ += pitch; request_eval_ = true; } inline void Note::addNoteNumber(int nn) { name_ += nn; request_eval_ = true; } inline void Note::evaluateState() { evaluateState(octave_, name_, pitch_); } inline bool operator==(Note& a, Note& b) { if (a.request_eval_) a.evaluateState(); if (b.request_eval_) b.evaluateState(); return (a.octave_ == b.octave_ && a.name_ == b.name_ && a.pitch_ == b.pitch_); } inline Note operator+(const Note& a, const Note& b) { return Note(a) += b; } inline Note operator+(const Note& a, int b) { return Note(a) += b; } inline Note operator+(int a, const Note& b) { return Note(b) += a; } inline Note& Note::operator+=(const Note& b) { add(b); return *this; } inline Note& Note::operator+=(int b) { addPitch(b); return *this; } BambooTracker-0.4.6/BambooTracker/opna_controller.cpp000066400000000000000000003142011401124043500226460ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "opna_controller.hpp" #include #include #include "note.hpp" #include "utils.hpp" namespace { constexpr int UNUSED_VALUE = -1; const std::unordered_map> FM_ENV_PARAMS_OP = { { FMOperatorType::All, { FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, FMEnvelopeParameter::DT1, FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, FMEnvelopeParameter::DT2, FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, FMEnvelopeParameter::DT3, FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, FMEnvelopeParameter::DT4 }}, { FMOperatorType::Op1, { FMEnvelopeParameter::AL, FMEnvelopeParameter::FB, FMEnvelopeParameter::AR1, FMEnvelopeParameter::DR1, FMEnvelopeParameter::SR1, FMEnvelopeParameter::RR1, FMEnvelopeParameter::SL1, FMEnvelopeParameter::TL1, FMEnvelopeParameter::KS1, FMEnvelopeParameter::ML1, FMEnvelopeParameter::DT1 }}, { FMOperatorType::Op2, { FMEnvelopeParameter::AR2, FMEnvelopeParameter::DR2, FMEnvelopeParameter::SR2, FMEnvelopeParameter::RR2, FMEnvelopeParameter::SL2, FMEnvelopeParameter::TL2, FMEnvelopeParameter::KS2, FMEnvelopeParameter::ML2, FMEnvelopeParameter::DT2 }}, { FMOperatorType::Op3, { FMEnvelopeParameter::AR3, FMEnvelopeParameter::DR3, FMEnvelopeParameter::SR3, FMEnvelopeParameter::RR3, FMEnvelopeParameter::SL3, FMEnvelopeParameter::TL3, FMEnvelopeParameter::KS3, FMEnvelopeParameter::ML3, FMEnvelopeParameter::DT3 }}, { FMOperatorType::Op4, { FMEnvelopeParameter::AR4, FMEnvelopeParameter::DR4, FMEnvelopeParameter::SR4, FMEnvelopeParameter::RR4, FMEnvelopeParameter::SL4, FMEnvelopeParameter::TL4, FMEnvelopeParameter::KS4, FMEnvelopeParameter::ML4, FMEnvelopeParameter::DT4 }} }; } OPNAController::OPNAController(chip::OpnaEmulator emu, int clock, int rate, int duration) : mode_(SongType::Standard), storePointADPCM_(0) { constexpr size_t DRAM_SIZE = 262144; // 256KiB opna_ = std::make_unique(emu, clock, rate, duration, DRAM_SIZE, std::make_unique(), std::make_unique()); for (size_t inch = 0; inch < 6; ++inch) { fmOpEnables_[inch] = 0xf; for (auto ep : FM_ENV_PARAMS_OP.at(FMOperatorType::All)) opSeqItFM_[inch].emplace(ep, nullptr); } for (auto& fm : fm_) fm.isMute = false; for (auto& ssg : ssg_) ssg.isMute = false; for (auto& rhy : rhythm_) rhy.isMute = false; isMuteADPCM_ = false; resetState(); outputHistory_.reset(new int16_t[2 * bt_defs::OUTPUT_HISTORY_SIZE]{}); outputHistoryReady_.reset(new int16_t[2 * bt_defs::OUTPUT_HISTORY_SIZE]{}); outputHistoryIndex_ = 0; } /********** Reset and initialize **********/ void OPNAController::reset() { opna_->reset(); resetState(); std::fill(&outputHistory_[0], &outputHistory_[2 * bt_defs::OUTPUT_HISTORY_SIZE], 0); std::fill(&outputHistoryReady_[0], &outputHistoryReady_[2 * bt_defs::OUTPUT_HISTORY_SIZE], 0); } void OPNAController::resetState() { opna_->setRegister(0x29, 0x80); // Init interrupt / YM2608 mode registerSetBuf_.clear(); initFM(); initSSG(); initRhythm(); initADPCM(); } /********** Forward instrument sequence **********/ void OPNAController::tickEvent(SoundSource src, int ch) { switch (src) { case SoundSource::FM: tickEventFM(fm_[ch]); break; case SoundSource::SSG: tickEventSSG(ssg_[ch]); break; case SoundSource::RHYTHM: break; case SoundSource::ADPCM: tickEventADPCM(); break; } } /********** Direct register set **********/ void OPNAController::sendRegister(int address, int value) { registerSetBuf_.push_back(std::make_pair(address, value)); } /********** DRAM **********/ size_t OPNAController::getDRAMSize() const { return opna_->getDRAMSize(); } /********** Update register states after tick process **********/ void OPNAController::updateRegisterStates() { updateKeyOnOffStatusRhythm(); // Check direct register set if (!registerSetBuf_.empty()) { for (auto& unit : registerSetBuf_) { opna_->setRegister(static_cast(unit.first), static_cast(unit.second)); } registerSetBuf_.clear(); } } /********** Real chip interface **********/ void OPNAController::useSCCI(scci::SoundInterfaceManager* manager) { opna_->useSCCI(manager); } bool OPNAController::isUsedSCCI() const { return opna_->isUsedSCCI(); } void OPNAController::useC86CTL(C86ctlBase* base) { opna_->useC86CTL(base); } bool OPNAController::isUsedC86CTL() const { return opna_->isUsedC86CTL(); } /********** Stream samples **********/ void OPNAController::getStreamSamples(int16_t* container, size_t nSamples) { opna_->mix(container, nSamples); size_t nHistory = std::min(nSamples, bt_defs::OUTPUT_HISTORY_SIZE); fillOutputHistory(&container[2 * (nSamples - nHistory)], nHistory); } void OPNAController::getOutputHistory(int16_t* container) { std::lock_guard lock(outputHistoryReadyMutex_); int16_t *history = outputHistoryReady_.get(); std::copy(history, &history[2 * bt_defs::OUTPUT_HISTORY_SIZE], container); } void OPNAController::fillOutputHistory(const int16_t* outputs, size_t nSamples) { int16_t *history = outputHistory_.get(); size_t historyIndex = outputHistoryIndex_; // copy as many as possible to the back size_t backCapacity = bt_defs::OUTPUT_HISTORY_SIZE - historyIndex; size_t nBack = std::min(nSamples, backCapacity); std::copy(outputs, &outputs[2 * nBack], &history[2 * historyIndex]); // copy the rest to the front std::copy(&outputs[2 * nBack], &outputs[2 * nSamples], history); // update the write position historyIndex = (historyIndex + nSamples) % bt_defs::OUTPUT_HISTORY_SIZE; outputHistoryIndex_ = historyIndex; // if no one holds the ready buffer, update the contents std::unique_lock lock(outputHistoryReadyMutex_, std::try_to_lock); if (lock.owns_lock()) { int16_t* dst = outputHistoryReady_.get(); // copy the back, and then the front std::copy(&history[2 * historyIndex], &history[2 * bt_defs::OUTPUT_HISTORY_SIZE], dst); std::copy(&history[0], &history[2 * historyIndex], &dst[2 * (bt_defs::OUTPUT_HISTORY_SIZE - historyIndex)]); } } /********** Chip mode **********/ void OPNAController::setMode(SongType mode) { mode_ = mode; reset(); } /********** Mute **********/ void OPNAController::setMuteState(SoundSource src, int chInSrc, bool isMute) { switch (src) { case SoundSource::FM: setMuteFMState(chInSrc, isMute); break; case SoundSource::SSG: setMuteSSGState(chInSrc, isMute); break; case SoundSource::RHYTHM: setMuteRhythmState(chInSrc, isMute); break; case SoundSource::ADPCM: setMuteADPCMState(isMute); break; } } bool OPNAController::isMute(SoundSource src, int chInSrc) { switch (src) { case SoundSource::FM: return isMuteFM(chInSrc); case SoundSource::SSG: return isMuteSSG(chInSrc); case SoundSource::RHYTHM: return isMuteRhythm(chInSrc); case SoundSource::ADPCM: return isMuteADPCM(); default: throw std::invalid_argument("Invalid sound source"); } } /********** Stream details **********/ int OPNAController::getRate() const { return opna_->getRate(); } void OPNAController::setRate(int rate) { opna_->setRate(rate); } int OPNAController::getDuration() const { return static_cast(opna_->getMaxDuration()); } void OPNAController::setDuration(int duration) { opna_->setMaxDuration(static_cast(duration)); } void OPNAController::setMasterVolume(int percentage) { opna_->setMasterVolume(percentage); } void OPNAController::setExportContainer(std::shared_ptr cntr) { opna_->setRegisterWriteLogger(cntr); } /********** Internal common process **********/ void OPNAController::checkRealToneByArpeggio(const ArpeggioIterInterface& arpItr, const EchoBuffer& echoBuf, Note& baseNote, bool& shouldSetTone) { if (arpItr->hasEnded()) return; switch (arpItr->type()) { case SequenceType::AbsoluteSequence: { Note ln = echoBuf.latest(); ln.addNoteNumber(arpItr->data().data - Note::DEFAULT_NOTE_NUM); baseNote = std::move(ln); break; } case SequenceType::FixedSequence: { baseNote = Note(arpItr->data().data); break; } case SequenceType::RelativeSequence: { baseNote.addNoteNumber(arpItr->data().data - Note::DEFAULT_NOTE_NUM); break; } default: return ; } shouldSetTone = true; } void OPNAController::checkPortamento(const ArpeggioIterInterface& arpItr, int prtm, bool hasKeyOnBefore, bool isTonePrtm, EchoBuffer& echoBuf, Note& baseNote, bool& shouldSetTone) { if ((!arpItr || arpItr->hasEnded()) && prtm && hasKeyOnBefore) { if (isTonePrtm) { Note bufNote = echoBuf.latest(); int dif = bufNote.getAbsolutePicth() - baseNote.getAbsolutePicth(); if (dif > 0) { baseNote += std::min(dif, prtm); shouldSetTone = true; } else if (dif < 0) { baseNote += std::max(dif, -prtm); shouldSetTone = true; } } else { baseNote += prtm; shouldSetTone = true; } } } void OPNAController::checkRealToneByPitch(const std::unique_ptr::Iterator>& ptItr, int& sumPitch, bool& shouldSetTone) { if (ptItr->hasEnded()) return; switch (ptItr->type()) { case SequenceType::AbsoluteSequence: sumPitch = ptItr->data().data - SEQ_PITCH_CENTER; break; case SequenceType::RelativeSequence: sumPitch += (ptItr->data().data - SEQ_PITCH_CENTER); break; default: return; } shouldSetTone = true; } //---------- FM ----------// namespace { /// IS_CARRIER[operator][algorithm] constexpr bool IS_CARRIER[4][8] = { { false, false, false, false, false, false, false, true }, { false, false, false, false, true, true, true, true }, { false, false, false, false, false, true, true, true }, { true, true, true, true, true, true, true, true }, }; constexpr uint8_t FM_KEYOFF_MASK[6] = { 0, 1, 2, 4, 5, 6 }; const std::unordered_map FM3_KEY_OFF_MASK = { { 2, 0xe }, { 6, 0xd }, { 7, 0xb }, { 8, 0x7 } }; const std::unordered_map FM3_CH_TO_OP_NUM = { { 2, 0u }, { 6, 1u }, { 7, 2u }, { 8, 3u } }; constexpr int FM3_OP_NUM_TO_CH[4] = { 2, 6, 7, 8 }; constexpr FMEnvelopeParameter PARAM_DT[4] = { FMEnvelopeParameter::DT1, FMEnvelopeParameter::DT2, FMEnvelopeParameter::DT3, FMEnvelopeParameter::DT4 }; constexpr FMEnvelopeParameter PARAM_TL[4] = { FMEnvelopeParameter::TL1, FMEnvelopeParameter::TL2, FMEnvelopeParameter::TL3, FMEnvelopeParameter::TL4 }; constexpr FMEnvelopeParameter PARAM_ML[4] = { FMEnvelopeParameter::ML1, FMEnvelopeParameter::ML2, FMEnvelopeParameter::ML3, FMEnvelopeParameter::ML4 }; constexpr FMEnvelopeParameter PARAM_KS[4] = { FMEnvelopeParameter::KS1, FMEnvelopeParameter::KS2, FMEnvelopeParameter::KS3, FMEnvelopeParameter::KS4 }; constexpr FMEnvelopeParameter PARAM_AR[4] = { FMEnvelopeParameter::AR1, FMEnvelopeParameter::AR2, FMEnvelopeParameter::AR3, FMEnvelopeParameter::AR4 }; constexpr FMEnvelopeParameter PARAM_DR[4] = { FMEnvelopeParameter::DR1, FMEnvelopeParameter::DR2, FMEnvelopeParameter::DR3, FMEnvelopeParameter::DR4 }; constexpr FMEnvelopeParameter PARAM_SR[4] = { FMEnvelopeParameter::SR1, FMEnvelopeParameter::SR2, FMEnvelopeParameter::SR3, FMEnvelopeParameter::SR4 }; constexpr FMEnvelopeParameter PARAM_SL[4] = { FMEnvelopeParameter::SL1, FMEnvelopeParameter::SL2, FMEnvelopeParameter::SL3, FMEnvelopeParameter::SL4 }; constexpr FMEnvelopeParameter PARAM_RR[4] = { FMEnvelopeParameter::RR1, FMEnvelopeParameter::RR2, FMEnvelopeParameter::RR3, FMEnvelopeParameter::RR4 }; constexpr FMEnvelopeParameter PARAM_SSGEG[4] = { FMEnvelopeParameter::SSGEG1, FMEnvelopeParameter::SSGEG2, FMEnvelopeParameter::SSGEG3, FMEnvelopeParameter::SSGEG4 }; constexpr FMLFOParameter PARAM_AM[4] = { FMLFOParameter::AM1, FMLFOParameter::AM2, FMLFOParameter::AM3, FMLFOParameter::AM4 }; constexpr uint32_t OP_OFFSET[4] = { 0u, 8u, 4u, 12u }; std::vector getOperatorsInLevel(int level, int al) { switch (level) { case 0: switch (al) { case 0: case 1: case 2: case 3: return { 3 }; case 4: return { 1, 3 }; case 5: case 6: return { 1, 2, 3 }; case 7: return { 0, 1, 2, 3 }; default: throw std::invalid_argument("Invalid algorithm."); } case 1: switch (al) { case 0: case 1: return { 2 }; case 2: return { 0, 2 }; case 3: return { 1, 2 }; case 4: return { 0, 2 }; case 5: case 6: return { 0 }; case 7: return {}; default: throw std::invalid_argument("Invalid algorithm."); } case 2: switch (al) { case 0: return { 1 }; case 1: return { 0, 1 }; case 2: return { 1 }; case 3: return { 0 }; case 4: case 5: case 6: case 7: return {}; default: throw std::invalid_argument("Invalid algorithm."); } case 3: switch (al) { case 0: return { 0 }; case 1: case 2: case 3: case 4: case 5: case 6: case 7: return {}; default: throw std::invalid_argument("Invalid algorithm."); } default: throw std::invalid_argument("Invalid operator level."); } } inline uint8_t judgeSSGEGRegisterValue(int v) { return (v == -1) ? 0 : (0x08 + static_cast(v)); } } /********** Key on-off **********/ void OPNAController::keyOnFM(int ch, const Note& note, bool isJam) { auto& fm = fm_[ch]; if (fm.isMute) return; fm.echoBuf.push(note); bool isTonePrtm = fm.isTonePrtm && fm.hasKeyOnBefore; if (isTonePrtm) { fm.baseNote += (fm.nsSum + fm.transpose); } else { fm.baseNote = fm.echoBuf.latest(); fm.neverSetBaseNote = false; fm.ptSum = 0; fm.volSldSum = 0; } if (fm.oneshotVol != UNUSED_VALUE) { setVolumeFM(ch, fm.baseVol); } if (!fm.hasSetNs) { fm.nsItr.reset(); } fm.hasSetNs = false; fm.shouldSetTone = true; fm.nsSum = 0; fm.transpose = 0; setFrontFMSequences(fm); fm.hasPreSetTickEvent = isJam; if (!isTonePrtm) { uint8_t chdata = FM_KEYOFF_MASK[fm.inCh]; switch (mode_) { case SongType::Standard: { if (fm.isKeyOn) opna_->setRegister(0x28, chdata); // Key off else fm.isKeyOn = true; opna_->setRegister(0x28, static_cast(fmOpEnables_[ch] << 4) | chdata); break; } case SongType::FM3chExpanded: { uint8_t slot = 0; switch (ch) { case 2: case 6: case 7: case 8: { bool prev = fm.isKeyOn; fm.isKeyOn = true; slot = getFM3SlotValidStatus(); if (prev) { // Key off uint8_t flags = static_cast(((slot & FM3_KEY_OFF_MASK.at(ch)) << 4)) | chdata; opna_->setRegister(0x28, flags); } break; } default: slot = fmOpEnables_[ch]; if (fm.isKeyOn) opna_->setRegister(0x28, chdata); // Key off else fm.isKeyOn = true; break; } opna_->setRegister(0x28, static_cast(slot << 4) | chdata); break; } } } fm.hasKeyOnBefore = true; } void OPNAController::keyOnFM(int ch, int echoBuf) { auto& fm = fm_[ch]; if (static_cast(echoBuf) < fm.echoBuf.size()) keyOnFM(ch, fm.echoBuf[echoBuf]); } void OPNAController::keyOffFM(int ch, bool isJam) { auto& fm = fm_[ch]; if (!fm.isKeyOn) { tickEventFM(fm); return; } releaseStartFMSequences(fm); fm.hasPreSetTickEvent = isJam; fm.isKeyOn = false; uint8_t chdata = FM_KEYOFF_MASK[fm.inCh]; switch (mode_) { case SongType::Standard: { opna_->setRegister(0x28, chdata); break; } case SongType::FM3chExpanded: { uint8_t slot = (fm.inCh == 2) ? static_cast(getFM3SlotValidStatus() << 4) : 0; opna_->setRegister(0x28, slot | chdata); break; } } } // Change register only void OPNAController::resetFMChannelEnvelope(int ch) { keyOffFM(ch); auto& fm = fm_[ch]; fm.hasResetEnv = true; if (mode_ == SongType::FM3chExpanded && fm.inCh == 2) { FMEnvelopeParameter rr = PARAM_RR[FM3_CH_TO_OP_NUM.at(ch)]; int prev = envFM_[2]->getParameterValue(rr); writeFMEnveropeParameterToRegister(2, rr, 127); envFM_[2]->setParameterValue(rr, prev); } else { auto& env = envFM_[ch]; for (const FMEnvelopeParameter& rr : PARAM_RR) { int prev = env->getParameterValue(rr); writeFMEnveropeParameterToRegister(ch, rr, 127); env->setParameterValue(rr, prev); } } } /********** Set instrument **********/ /// NOTE: inst != nullptr void OPNAController::setInstrumentFM(int ch, std::shared_ptr inst) { auto& fm = fm_[ch]; FMOperatorType opType = fm.opType; size_t inch = fm.inCh; auto& refInst = refInstFM_[inch]; if (!refInst || !refInst->isRegisteredWithManager() || refInst->getNumber() != inst->getNumber()) { refInst = inst; writeFMEnvelopeToRegistersFromInstrument(inch); fmOpEnables_[inch] = static_cast(inst->getOperatorEnabled(0)) | static_cast(inst->getOperatorEnabled(1) << 1) | static_cast(inst->getOperatorEnabled(2) << 2) | static_cast(inst->getOperatorEnabled(3) << 3); } else { if (isFBCtrlFM_[inch]) { isFBCtrlFM_[inch] = false; writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::FB, inst->getEnvelopeParameter(FMEnvelopeParameter::FB)); } for (int op = 0; op < 4; ++op) { if (isTLCtrlFM_[inch][op] || isBrightFM_[inch][op]) { isTLCtrlFM_[inch][op] = false; isBrightFM_[inch][op] = false; FMEnvelopeParameter tl = PARAM_TL[op]; writeFMEnveropeParameterToRegister(inch, tl, inst->getEnvelopeParameter(tl)); } if (isMLCtrlFM_[inch][op]) { isMLCtrlFM_[inch][op] = false; FMEnvelopeParameter ml = PARAM_ML[op]; writeFMEnveropeParameterToRegister(inch, ml, inst->getEnvelopeParameter(ml)); } if (isARCtrlFM_[inch][op]) { isARCtrlFM_[inch][op] = false; FMEnvelopeParameter ar = PARAM_AR[op]; writeFMEnveropeParameterToRegister(inch, ar, inst->getEnvelopeParameter(ar)); } if (isDRCtrlFM_[inch][op]) { isDRCtrlFM_[inch][op] = false; FMEnvelopeParameter dr = PARAM_DR[op]; writeFMEnveropeParameterToRegister(inch, dr, inst->getEnvelopeParameter(dr)); } if (isRRCtrlFM_[inch][op]) { isRRCtrlFM_[inch][op] = false; FMEnvelopeParameter rr = PARAM_RR[op]; writeFMEnveropeParameterToRegister(inch, rr, inst->getEnvelopeParameter(rr)); } } restoreFMEnvelopeFromReset(ch); } if (fm.isKeyOn && lfoStartCntFM_[inch] == UNUSED_VALUE) writeFMLFOAllRegisters(inch); for (auto& p : FM_ENV_PARAMS_OP.at(opType)) { if (inst->getOperatorSequenceEnabled(p)) { opSeqItFM_[inch].at(p) = inst->getOperatorSequenceSequenceIterator(p); switch (p) { case FMEnvelopeParameter::FB: isFBCtrlFM_[inch] = false; break; case FMEnvelopeParameter::TL1: isTLCtrlFM_[inch][0] = false; isBrightFM_[inch][0] = false; break; case FMEnvelopeParameter::TL2: isTLCtrlFM_[inch][1] = false; isBrightFM_[inch][1] = false; break; case FMEnvelopeParameter::TL3: isTLCtrlFM_[inch][2] = false; isBrightFM_[inch][2] = false; break; case FMEnvelopeParameter::TL4: isTLCtrlFM_[inch][3] = false; isBrightFM_[inch][3] = false; break; case FMEnvelopeParameter::ML1: isMLCtrlFM_[inch][0] = false; break; case FMEnvelopeParameter::ML2: isMLCtrlFM_[inch][1] = false; break; case FMEnvelopeParameter::ML3: isMLCtrlFM_[inch][2] = false; break; case FMEnvelopeParameter::ML4: isMLCtrlFM_[inch][3] = false; break; case FMEnvelopeParameter::AR1: isARCtrlFM_[inch][0] = false; break; case FMEnvelopeParameter::AR2: isARCtrlFM_[inch][1] = false; break; case FMEnvelopeParameter::AR3: isARCtrlFM_[inch][2] = false; break; case FMEnvelopeParameter::AR4: isARCtrlFM_[inch][3] = false; break; case FMEnvelopeParameter::DR1: isDRCtrlFM_[inch][0] = false; break; case FMEnvelopeParameter::DR2: isDRCtrlFM_[inch][1] = false; break; case FMEnvelopeParameter::DR3: isDRCtrlFM_[inch][2] = false; break; case FMEnvelopeParameter::DR4: isDRCtrlFM_[inch][3] = false; break; case FMEnvelopeParameter::RR1: isRRCtrlFM_[inch][0] = false; break; case FMEnvelopeParameter::RR2: isRRCtrlFM_[inch][1] = false; break; case FMEnvelopeParameter::RR3: isRRCtrlFM_[inch][2] = false; break; case FMEnvelopeParameter::RR4: isRRCtrlFM_[inch][3] = false; break; default: break; } } else { opSeqItFM_[inch].at(p).reset(); } } if (!fm.isArpEff) { if (inst->getArpeggioEnabled(opType)) fm.arpItr = inst->getArpeggioSequenceIterator(opType); else fm.arpItr.reset(); } if (inst->getPitchEnabled(opType)) fm.ptItr = inst->getPitchSequenceIterator(opType); else fm.ptItr.reset(); setInstrumentFMProperties(fm); checkLFOUsedByInstrument(); } void OPNAController::updateInstrumentFM(int instNum) { int cnt = static_cast(Song::getFMChannelCount(mode_)); for (int ch = 0; ch < cnt; ++ch) { auto& fm = fm_[ch]; size_t inch = fm.inCh; auto& inst = refInstFM_[inch]; if (inst && inst->isRegisteredWithManager() && inst->getNumber() == instNum) { writeFMEnvelopeToRegistersFromInstrument(inch); if (fm.isKeyOn && lfoStartCntFM_[inch] == UNUSED_VALUE) writeFMLFOAllRegisters(inch); FMOperatorType opType = fm.opType; for (auto& p : FM_ENV_PARAMS_OP.at(opType)) { if (!inst->getOperatorSequenceEnabled(p)) opSeqItFM_[inch].at(p).reset(); } if (!inst->getArpeggioEnabled(opType)) fm.arpItr.reset(); if (!inst->getPitchEnabled(opType)) fm.ptItr.reset(); setInstrumentFMProperties(fm); } } checkLFOUsedByInstrument(); } void OPNAController::updateInstrumentFMEnvelopeParameter(int envNum, FMEnvelopeParameter param) { for (int ch = 0; ch < 6; ++ch) { if (refInstFM_[ch] && refInstFM_[ch]->getEnvelopeNumber() == envNum) { writeFMEnveropeParameterToRegister(ch, param, refInstFM_[ch]->getEnvelopeParameter(param)); } } } void OPNAController::setInstrumentFMOperatorEnabled(int envNum, int opNum) { int chsize = static_cast(Song::getFMChannelCount(mode_)); for (int ch = 0; ch < chsize; ++ch) { auto& fm = fm_[ch]; size_t inch = fm.inCh; if (refInstFM_[inch] && refInstFM_[inch]->getEnvelopeNumber() == envNum) { bool enabled = refInstFM_[inch]->getOperatorEnabled(opNum); envFM_[inch]->setOperatorEnabled(opNum, enabled); if (enabled) { fmOpEnables_[inch] |= (1 << opNum); } else { fmOpEnables_[inch] &= ~(1 << opNum); } if (fm.isKeyOn) { uint8_t chdata = FM_KEYOFF_MASK[inch]; switch (mode_) { case SongType::Standard: { opna_->setRegister(0x28, static_cast(fmOpEnables_[inch] << 4) | chdata); break; } case SongType::FM3chExpanded: { uint8_t slot = (inch == 2) ? getFM3SlotValidStatus() : fmOpEnables_[inch]; opna_->setRegister(0x28, static_cast(slot << 4) | chdata); break; } } } } } } void OPNAController::updateInstrumentFMLFOParameter(int lfoNum, FMLFOParameter param) { for (int ch = 0; ch < 6; ++ch) { if (refInstFM_[ch] && refInstFM_[ch]->getLFOEnabled() && refInstFM_[ch]->getLFONumber() == lfoNum) { writeFMLFORegister(ch, param); } } } /********** Set volume **********/ void OPNAController::setVolumeFM(int ch, int volume) { auto& fm = fm_[ch]; fm.baseVol = volume; fm.oneshotVol = UNUSED_VALUE; if (refInstFM_[fm.inCh]) updateFMVolume(fm); // Change TL } void OPNAController::setOneshotVolumeFM(int ch, int volume) { auto& fm = fm_[ch]; fm.oneshotVol = volume; if (refInstFM_[fm.inCh]) updateFMVolume(fm); // Change TL } void OPNAController::updateFMVolume(FMChannel& fm) { size_t inch = fm.inCh; switch (fm.opType) { case FMOperatorType::All: for (const FMEnvelopeParameter& tl : PARAM_TL) writeFMEnveropeParameterToRegister(inch, tl, refInstFM_[inch]->getEnvelopeParameter(tl)); break; case FMOperatorType::Op1: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL1, refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL1)); break; case FMOperatorType::Op2: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL2, refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL2)); break; case FMOperatorType::Op3: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL3, refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL3)); break; case FMOperatorType::Op4: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::TL4, refInstFM_[inch]->getEnvelopeParameter(FMEnvelopeParameter::TL4)); break; } } void OPNAController::setMasterVolumeFM(double dB) { opna_->setVolumeFM(dB); } /********** Set effect **********/ void OPNAController::setPanFM(int ch, int value) { size_t inch = fm_[ch].inCh; panFM_[inch] = static_cast(value); uint32_t bch = getFMChannelOffset(ch); // Bank and channel offset uint8_t data = static_cast(value << 6); auto& inst = refInstFM_[inch]; if (inst && inst->getLFOEnabled()) { data |= (inst->getLFOParameter(FMLFOParameter::AMS) << 4); data |= inst->getLFOParameter(FMLFOParameter::PMS); } opna_->setRegister(0xb4 + bch, data); } void OPNAController::setArpeggioEffectFM(int ch, int second, int third) { auto& fm = fm_[ch]; if (second || third) { fm.arpItr = std::make_unique(second, third); fm.isArpEff = true; } else { size_t inch = fm.inCh; if (refInstFM_[inch]) { FMOperatorType op = fm.opType; if (!refInstFM_[inch]->getArpeggioEnabled(op)) fm.arpItr.reset(); else fm.arpItr = refInstFM_[inch]->getArpeggioSequenceIterator(op); } fm.isArpEff = false; } } void OPNAController::setPortamentoEffectFM(int ch, int depth, bool isTonePortamento) { auto& fm = fm_[ch]; fm.prtmDepth = depth; fm.isTonePrtm = depth ? isTonePortamento : false; } void OPNAController::setVibratoEffectFM(int ch, int period, int depth) { auto& fm = fm_[ch]; if (period && depth) fm.vibItr = std::make_unique(period, depth); else fm.vibItr.reset(); } void OPNAController::setTremoloEffectFM(int ch, int period, int depth) { auto& fm = fm_[ch]; if (period && depth) fm.treItr = std::make_unique(period, depth); else fm.treItr.reset(); } void OPNAController::setVolumeSlideFM(int ch, int depth, bool isUp) { fm_[ch].volSld = isUp ? -depth : depth; } void OPNAController::setDetuneFM(int ch, int pitch) { auto& fm = fm_[ch]; fm.detune = pitch; fm.shouldSetTone = true; } void OPNAController::setFineDetuneFM(int ch, int pitch) { auto& fm = fm_[ch]; fm.fdetune = pitch; fm.shouldSetTone = true; } void OPNAController::setNoteSlideFM(int ch, int speed, int seminote) { auto& fm = fm_[ch]; if (seminote) { fm.nsItr = std::make_unique(speed, seminote); fm.hasSetNs = true; } else fm.nsItr.reset(); } void OPNAController::setTransposeEffectFM(int ch, int seminote) { auto& fm = fm_[ch]; fm.transpose += (seminote * Note::SEMINOTE_PITCH); fm.shouldSetTone = true; } void OPNAController::setFBControlFM(int ch, int value) { size_t inch = fm_[ch].inCh; writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::FB, value); isFBCtrlFM_[inch] = true; opSeqItFM_[inch].at(FMEnvelopeParameter::FB).reset(); } void OPNAController::setTLControlFM(int ch, int op, int value) { size_t inch = fm_[ch].inCh; FMEnvelopeParameter param = PARAM_TL[op]; writeFMEnveropeParameterToRegister(inch, param, value); isTLCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); } void OPNAController::setMLControlFM(int ch, int op, int value) { size_t inch = fm_[ch].inCh; FMEnvelopeParameter param = PARAM_ML[op]; writeFMEnveropeParameterToRegister(inch, param, value); isMLCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); } void OPNAController::setARControlFM(int ch, int op, int value) { size_t inch = fm_[ch].inCh; FMEnvelopeParameter param = PARAM_AR[op]; writeFMEnveropeParameterToRegister(inch, param, value); isARCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); } void OPNAController::setDRControlFM(int ch, int op, int value) { size_t inch = fm_[ch].inCh; FMEnvelopeParameter param = PARAM_DR[op]; writeFMEnveropeParameterToRegister(inch, param, value); isDRCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); } void OPNAController::setRRControlFM(int ch, int op, int value) { size_t inch = fm_[ch].inCh; FMEnvelopeParameter param = PARAM_RR[op]; writeFMEnveropeParameterToRegister(inch, param, value); isRRCtrlFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); } void OPNAController::setBrightnessFM(int ch, int value) { size_t inch = fm_[ch].inCh; std::vector ops = getOperatorsInLevel(1, envFM_[inch]->getParameterValue(FMEnvelopeParameter::AL)); for (auto& op : ops) { FMEnvelopeParameter param = PARAM_TL[op]; int v = utils::clamp(envFM_[inch]->getParameterValue(param) + value, 0, 127); writeFMEnveropeParameterToRegister(inch, param, v); isBrightFM_[inch][op] = true; opSeqItFM_[inch].at(param).reset(); } } /********** For state retrieve **********/ void OPNAController::haltSequencesFM(int ch) { auto& fm = fm_[ch]; for (auto& p : FM_ENV_PARAMS_OP.at(fm.opType)) { if (auto& itr = opSeqItFM_[fm.inCh].at(p)) itr->end(); } if (auto& treItr = fm.treItr) treItr->end(); if (auto& arpItr = fm.arpItr) arpItr->end(); if (auto& ptItr = fm.ptItr) ptItr->end(); if (auto& vibItr = fm.vibItr) vibItr->end(); if (auto& nsItr = fm.nsItr) nsItr->end(); } /********** Chip details **********/ bool OPNAController::isKeyOnFM(int ch) const { return fm_[ch].isKeyOn; } bool OPNAController::isTonePortamentoFM(int ch) const { return fm_[ch].isTonePrtm; } bool OPNAController::enableFMEnvelopeReset(int ch) const { auto& fm = fm_[ch]; return envFM_[fm.ch] ? fm.isEnabledEnvReset : true; } Note OPNAController::getFMLatestNote(int ch) const { return fm_[ch].echoBuf.latest(); } /***********************************/ void OPNAController::initFM() { lfoFreq_ = UNUSED_VALUE; uint8_t mode = 0; switch (mode_) { case SongType::Standard: mode = 0; break; case SongType::FM3chExpanded: mode = 0x40; break; } opna_->setRegister(0x27, mode); for (int inch = 0; inch < 6; ++inch) { // Init envelope envFM_[inch] = std::make_unique(-1); refInstFM_[inch].reset(); // Init pan uint32_t bch = getFMChannelOffset(inch); panFM_[inch] = 3; opna_->setRegister(0xb4 + bch, 0xc0); // Init sequence for (auto& p : opSeqItFM_[inch]) { p.second.reset(); } lfoStartCntFM_[inch] = UNUSED_VALUE; isFBCtrlFM_[inch] = false; for (int op = 0; op < 4; ++op) { isTLCtrlFM_[inch][op] = false; isMLCtrlFM_[inch][op] = false; isARCtrlFM_[inch][op] = false; isDRCtrlFM_[inch][op] = false; isRRCtrlFM_[inch][op] = false; isBrightFM_[inch][op] = false; } } size_t fmch = Song::getFMChannelCount(mode_); for (size_t ch = 0; ch < fmch; ++ch) { auto& fm = fm_[ch]; fm.ch = ch; if (ch < 6) fm.inCh = ch; else if (mode_ == SongType::FM3chExpanded && 6 <= ch && ch < 9) fm.inCh = 2; // Init operators key off fm.isKeyOn = false; fm.hasKeyOnBefore = false; fm.echoBuf.clear(); fm.neverSetBaseNote = true; fm.baseVol = 0; // Init volume fm.oneshotVol = UNUSED_VALUE; fm.isEnabledEnvReset = false; fm.hasResetEnv = false; // Set operator type if (mode_ == SongType::FM3chExpanded && fm.inCh == 2) { switch (fm.ch) { case 2: fm.opType = FMOperatorType::Op1; break; case 6: fm.opType = FMOperatorType::Op2; break; case 7: fm.opType = FMOperatorType::Op3; break; case 8: fm.opType = FMOperatorType::Op4; break; default: throw std::out_of_range("out of range."); } } else { fm.opType = FMOperatorType::All; } // Init sequence fm.hasPreSetTickEvent = false; fm.arpItr.reset(); fm.ptItr.reset(); fm.ptSum = 0; fm.shouldSetTone = false; // Effect fm.isArpEff = false; fm.prtmDepth = 0; fm.isTonePrtm = false; fm.vibItr.reset(); fm.treItr.reset(); fm.volSld = 0; fm.volSldSum = 0; fm.detune = 0; fm.fdetune = 0; fm.nsItr.reset(); fm.nsSum = 0; fm.hasSetNs = false; fm.transpose = 0; } } void OPNAController::setMuteFMState(int ch, bool isMute) { auto& fm = fm_[ch]; fm.isMute = isMute; if (isMute) { resetFMChannelEnvelope(ch); } else { size_t inch = fm.inCh; switch (fm.opType) { case FMOperatorType::All: for (const FMEnvelopeParameter& rr : PARAM_RR) writeFMEnveropeParameterToRegister(inch, rr, envFM_[inch]->getParameterValue(rr)); break; case FMOperatorType::Op1: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR1, envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR1)); break; case FMOperatorType::Op2: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR2, envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR2)); break; case FMOperatorType::Op3: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR3, envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR3)); break; case FMOperatorType::Op4: writeFMEnveropeParameterToRegister(inch, FMEnvelopeParameter::RR4, envFM_[inch]->getParameterValue(FMEnvelopeParameter::RR4)); break; } } } bool OPNAController::isMuteFM(int ch) { return fm_[ch].isMute; } uint32_t OPNAController::getFMChannelOffset(int ch, bool forPitch) const { if (mode_ == SongType::FM3chExpanded && forPitch) { switch (ch) { case 0: case 1: return static_cast(ch); case 3: case 4: case 5: return static_cast(0x100 + ch % 3); case 2: // FM3-OP1 return 9; case 6: // FM3-OP2 return 10; case 7: // FM3-OP3 return 8; case 8: // FM3-OP4 return 2; default: return 0; } } else { switch (fm_[ch].inCh) { case 0: case 1: case 2: return static_cast(ch); case 3: case 4: case 5: return static_cast(0x100 + ch % 3); default: return 0; } } } void OPNAController::writeFMEnvelopeToRegistersFromInstrument(size_t inch) { uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset uint8_t data1, data2; int al; auto& inst = refInstFM_[inch]; auto& env = envFM_[inch]; data1 = static_cast(inst->getEnvelopeParameter(FMEnvelopeParameter::FB)); env->setParameterValue(FMEnvelopeParameter::FB, data1); data1 <<= 3; al = inst->getEnvelopeParameter(FMEnvelopeParameter::AL); env->setParameterValue(FMEnvelopeParameter::AL, al); data1 += al; opna_->setRegister(0xb0 + bch, data1); bool isExpandedCh = (mode_ == SongType::FM3chExpanded && inch == 2); for (size_t op = 0; op < 4; ++op) { uint32_t offset = bch + OP_OFFSET[op]; FMEnvelopeParameter dt = PARAM_DT[op]; FMEnvelopeParameter ml = PARAM_ML[op]; data1 = static_cast(inst->getEnvelopeParameter(dt)); env->setParameterValue(dt, data1); data1 <<= 4; data2 = static_cast(inst->getEnvelopeParameter(ml)); env->setParameterValue(ml, data2); data1 |= data2; opna_->setRegister(0x30 + offset, data1); FMEnvelopeParameter tl = PARAM_TL[op]; data1 = static_cast(inst->getEnvelopeParameter(tl)); // Adjust volume if (isExpandedCh) data1 = calculateTL(FM3_OP_NUM_TO_CH[op], data1); else if (IS_CARRIER[op][al]) data1 = calculateTL(inch, data1); env->setParameterValue(tl, data1); opna_->setRegister(0x40 + offset, data1); FMEnvelopeParameter ks = PARAM_KS[op]; FMEnvelopeParameter ar = PARAM_AR[op]; data1 = static_cast(inst->getEnvelopeParameter(ks)); env->setParameterValue(ks, data1); data1 <<= 6; data2 = static_cast(inst->getEnvelopeParameter(ar)); env->setParameterValue(ar, data2); data1 |= data2; opna_->setRegister(0x50 + offset, data1); FMEnvelopeParameter dr = PARAM_DR[op]; data1 = inst->getLFOEnabled() ? static_cast(inst->getLFOParameter(PARAM_AM[op])) : 0; data1 <<= 7; data2 = static_cast(inst->getEnvelopeParameter(dr)); env->setParameterValue(dr, data2); data1 |= data2; opna_->setRegister(0x60 + offset, data1); FMEnvelopeParameter sr = PARAM_SR[op]; data1 = static_cast(inst->getEnvelopeParameter(sr)); env->setParameterValue(sr, data2); opna_->setRegister(0x70 + offset, data1); FMEnvelopeParameter sl = PARAM_SL[op]; FMEnvelopeParameter rr = PARAM_RR[op]; data1 = static_cast(inst->getEnvelopeParameter(sl)); env->setParameterValue(sl, data1); data1 <<= 4; data2 = static_cast(inst->getEnvelopeParameter(rr)); env->setParameterValue(rr, data2); data1 |= data2; opna_->setRegister(0x80 + offset, data1); FMEnvelopeParameter ssgeg = PARAM_SSGEG[op]; int tmp = inst->getEnvelopeParameter(ssgeg); env->setParameterValue(ssgeg, tmp); data1 = judgeSSGEGRegisterValue(tmp); opna_->setRegister(0x90 + offset, data1); } } void OPNAController::writeFMEnveropeParameterToRegister(size_t inch, FMEnvelopeParameter param, int value) { uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset uint8_t data; int tmp; auto& env = envFM_[inch]; env->setParameterValue(param, value); switch (param) { case FMEnvelopeParameter::AL: case FMEnvelopeParameter::FB: data = static_cast(env->getParameterValue(FMEnvelopeParameter::FB) << 3); data += env->getParameterValue(FMEnvelopeParameter::AL); opna_->setRegister(0xb0 + bch, data); break; case FMEnvelopeParameter::DT1: case FMEnvelopeParameter::ML1: data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT1) << 4); data |= env->getParameterValue(FMEnvelopeParameter::ML1); opna_->setRegister(0x30 + bch, data); break; case FMEnvelopeParameter::TL1: data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL1)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(2, data); env->setParameterValue(FMEnvelopeParameter::TL1, data); // Update } else if (IS_CARRIER[0][env->getParameterValue(FMEnvelopeParameter::AL)]) { data = calculateTL(inch, data); env->setParameterValue(FMEnvelopeParameter::TL1, data); // Update } opna_->setRegister(0x40 + bch, data); break; case FMEnvelopeParameter::KS1: case FMEnvelopeParameter::AR1: data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS1) << 6); data |= env->getParameterValue(FMEnvelopeParameter::AR1); opna_->setRegister(0x50 + bch, data); break; case FMEnvelopeParameter::DR1: if (refInstFM_[inch] && refInstFM_[inch]->getLFOEnabled()) { data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM1) << 7); } else { data = 0; } data |= env->getParameterValue(FMEnvelopeParameter::DR1); opna_->setRegister(0x60 + bch, data); break; case FMEnvelopeParameter::SR1: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR1)); opna_->setRegister(0x70 + bch, data); break; case FMEnvelopeParameter::SL1: case FMEnvelopeParameter::RR1: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL1) << 4); data |= env->getParameterValue(FMEnvelopeParameter::RR1); opna_->setRegister(0x80 + bch, data); break; case::FMEnvelopeParameter::SSGEG1: tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG1); data = (tmp == -1) ? 0 : static_cast(0x08 + tmp); opna_->setRegister(0x90 + bch, data); break; case FMEnvelopeParameter::DT2: case FMEnvelopeParameter::ML2: data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT2) << 4); data |= env->getParameterValue(FMEnvelopeParameter::ML2); opna_->setRegister(0x30 + bch + 8, data); break; case FMEnvelopeParameter::TL2: data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL2)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(6, data); env->setParameterValue(FMEnvelopeParameter::TL2, data); // Update } else if (IS_CARRIER[1][env->getParameterValue(FMEnvelopeParameter::AL)]) { data = calculateTL(inch, data); env->setParameterValue(FMEnvelopeParameter::TL2, data); // Update } opna_->setRegister(0x40 + bch + 8, data); break; case FMEnvelopeParameter::KS2: case FMEnvelopeParameter::AR2: data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS2) << 6); data |= env->getParameterValue(FMEnvelopeParameter::AR2); opna_->setRegister(0x50 + bch + 8, data); break; case FMEnvelopeParameter::DR2: if (refInstFM_[inch] && refInstFM_[inch]->getLFOEnabled()) { data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM2) << 7); } else { data = 0; } data |= env->getParameterValue(FMEnvelopeParameter::DR2); opna_->setRegister(0x60 + bch + 8, data); break; case FMEnvelopeParameter::SR2: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR2)); opna_->setRegister(0x70 + bch + 8, data); break; case FMEnvelopeParameter::SL2: case FMEnvelopeParameter::RR2: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL2) << 4); data |= env->getParameterValue(FMEnvelopeParameter::RR2); opna_->setRegister(0x80 + bch + 8, data); break; case FMEnvelopeParameter::SSGEG2: tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG2); data = (tmp == -1) ? 0 : static_cast(0x08 + tmp); opna_->setRegister(0x90 + bch + 8, data); break; case FMEnvelopeParameter::DT3: case FMEnvelopeParameter::ML3: data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT3) << 4); data |= env->getParameterValue(FMEnvelopeParameter::ML3); opna_->setRegister(0x30 + bch + 4, data); break; case FMEnvelopeParameter::TL3: data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL3)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(7, data); env->setParameterValue(FMEnvelopeParameter::TL3, data); // Update } else if (IS_CARRIER[2][env->getParameterValue(FMEnvelopeParameter::AL)]) { data = calculateTL(inch, data); env->setParameterValue(FMEnvelopeParameter::TL3, data); // Update } opna_->setRegister(0x40 + bch + 4, data); break; case FMEnvelopeParameter::KS3: case FMEnvelopeParameter::AR3: data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS3) << 6); data |= env->getParameterValue(FMEnvelopeParameter::AR3); opna_->setRegister(0x50 + bch + 4, data); break; case FMEnvelopeParameter::DR3: if (refInstFM_[inch] && refInstFM_[inch]->getLFOEnabled()) { data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM3) << 7); } else { data = 0; } data |= env->getParameterValue(FMEnvelopeParameter::DR3); opna_->setRegister(0x60 + bch + 4, data); break; case FMEnvelopeParameter::SR3: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR3)); opna_->setRegister(0x70 + bch + 4, data); break; case FMEnvelopeParameter::SL3: case FMEnvelopeParameter::RR3: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL3) << 4); data |= env->getParameterValue(FMEnvelopeParameter::RR3); opna_->setRegister(0x80 + bch + 4, data); break; case FMEnvelopeParameter::SSGEG3: tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG3); data = (tmp == -1) ? 0 : static_cast(0x08 + tmp); opna_->setRegister(0x90 + bch + 4, data); break; case FMEnvelopeParameter::DT4: case FMEnvelopeParameter::ML4: data = static_cast(env->getParameterValue(FMEnvelopeParameter::DT4) << 4); data |= env->getParameterValue(FMEnvelopeParameter::ML4); opna_->setRegister(0x30 + bch + 12, data); break; case FMEnvelopeParameter::TL4: data = static_cast(env->getParameterValue(FMEnvelopeParameter::TL4)); // Adjust volume if (mode_ == SongType::FM3chExpanded && inch == 2) { data = calculateTL(8, data); env->setParameterValue(FMEnvelopeParameter::TL4, data); // Update } else { data = calculateTL(inch, data); env->setParameterValue(FMEnvelopeParameter::TL4, data); // Update } opna_->setRegister(0x40 + bch + 12, data); break; case FMEnvelopeParameter::KS4: case FMEnvelopeParameter::AR4: data = static_cast(env->getParameterValue(FMEnvelopeParameter::KS4) << 6); data |= env->getParameterValue(FMEnvelopeParameter::AR4); opna_->setRegister(0x50 + bch + 12, data); break; case FMEnvelopeParameter::DR4: if (refInstFM_[inch] && refInstFM_[inch]->getLFOEnabled()) { data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM4) << 7); } else { data = 0; } data |= env->getParameterValue(FMEnvelopeParameter::DR4); opna_->setRegister(0x60 + bch + 12, data); break; case FMEnvelopeParameter::SR4: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SR4)); opna_->setRegister(0x70 + bch + 12, data); break; case FMEnvelopeParameter::SL4: case FMEnvelopeParameter::RR4: data = static_cast(env->getParameterValue(FMEnvelopeParameter::SL4) << 4); data |= env->getParameterValue(FMEnvelopeParameter::RR4); opna_->setRegister(0x80 + bch + 12, data); break; case FMEnvelopeParameter::SSGEG4: tmp = env->getParameterValue(FMEnvelopeParameter::SSGEG4); data = judgeSSGEGRegisterValue(tmp); opna_->setRegister(0x90 + bch + 12, data); break; } } void OPNAController::restoreFMEnvelopeFromReset(int ch) { auto& fm = fm_[ch]; size_t inch = fm.inCh; auto& inst = refInstFM_[inch]; if (fm.hasResetEnv == false || !inst) return; switch (mode_) { case SongType::Standard: { if (inst->getEnvelopeResetEnabled(FMOperatorType::All)) { fm.hasResetEnv = false; for (const FMEnvelopeParameter& rr : PARAM_RR) { writeFMEnveropeParameterToRegister(inch, rr, inst->getEnvelopeParameter(rr)); } } break; } case SongType::FM3chExpanded: { if (inst->getEnvelopeResetEnabled(fm.opType)) { if (inch == 2) { FMEnvelopeParameter param = PARAM_RR[FM3_CH_TO_OP_NUM.at(ch)]; fm.hasResetEnv = false; writeFMEnveropeParameterToRegister(2, param, refInstFM_[2]->getEnvelopeParameter(param)); } else { fm_[inch].hasResetEnv = false; for (const FMEnvelopeParameter& rr : PARAM_RR) { writeFMEnveropeParameterToRegister(inch, rr, inst->getEnvelopeParameter(rr)); } } } break; } } } void OPNAController::writeFMLFOAllRegisters(size_t inch) { if (!refInstFM_[inch]->getLFOEnabled() || lfoStartCntFM_[inch] > 0) { // Clear data uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset opna_->setRegister(0xb4 + bch, static_cast(panFM_[inch] << 6)); for (size_t op = 0; op < 4; ++op) opna_->setRegister(0x60 + bch + OP_OFFSET[op], static_cast(envFM_[inch]->getParameterValue(PARAM_DR[op]))); } else { writeFMLFORegister(inch, FMLFOParameter::FREQ); writeFMLFORegister(inch, FMLFOParameter::PMS); writeFMLFORegister(inch, FMLFOParameter::AMS); for (const FMLFOParameter& am : PARAM_AM) writeFMLFORegister(inch, am); lfoStartCntFM_[inch] = UNUSED_VALUE; } } void OPNAController::writeFMLFORegister(size_t inch, FMLFOParameter param) { uint32_t bch = getFMChannelOffset(inch); // Bank and channel offset uint8_t data; switch (param) { case FMLFOParameter::FREQ: lfoFreq_ = refInstFM_[inch]->getLFOParameter(FMLFOParameter::FREQ); opna_->setRegister(0x22, static_cast(lfoFreq_ | (1 << 3))); break; case FMLFOParameter::PMS: case FMLFOParameter::AMS: data = static_cast(panFM_[inch] << 6); data |= (refInstFM_[inch]->getLFOParameter(FMLFOParameter::AMS) << 4); data |= refInstFM_[inch]->getLFOParameter(FMLFOParameter::PMS); opna_->setRegister(0xb4 + bch, data); break; case FMLFOParameter::AM1: data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM1) << 7); data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR1); opna_->setRegister(0x60 + bch, data); break; case FMLFOParameter::AM2: data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM2) << 7); data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR2); opna_->setRegister(0x60 + bch + 8, data); break; case FMLFOParameter::AM3: data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM3) << 7); data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR3); opna_->setRegister(0x60 + bch + 4, data); break; case FMLFOParameter::AM4: data = static_cast(refInstFM_[inch]->getLFOParameter(FMLFOParameter::AM4) << 7); data |= envFM_[inch]->getParameterValue(FMEnvelopeParameter::DR4); opna_->setRegister(0x60 + bch + 12, data); break; default: break; } } void OPNAController::checkLFOUsedByInstrument() { for (const auto& inst : refInstFM_) { if (inst && inst->getLFOEnabled()) return; } // Turn off if no instrument uses LFO if (lfoFreq_ != UNUSED_VALUE) { lfoFreq_ = UNUSED_VALUE; opna_->setRegister(0x22, 0); } } void OPNAController::setFrontFMSequences(FMChannel& fm) { if (fm.isMute) return; size_t inch = fm.inCh; auto& inst = refInstFM_[inch]; if (inst && inst->getLFOEnabled()) { lfoStartCntFM_[inch] = inst->getLFOParameter(FMLFOParameter::Count); writeFMLFOAllRegisters(inch); } else { lfoStartCntFM_[inch] = UNUSED_VALUE; } checkOperatorSequenceFM(fm, 1); if (auto& treItr = fm.treItr) treItr->front(); fm.volSldSum += fm.volSld; checkVolumeEffectFM(fm); if (auto& arpItr = fm.arpItr) { arpItr->front(); checkRealToneFMByArpeggio(fm); } checkPortamentoFM(fm); if (auto& ptItr = fm.ptItr) { ptItr->front(); checkRealToneFMByPitch(fm); } if (auto& vibItr = fm.vibItr) { vibItr->front(); fm.shouldSetTone = true; } if (auto& nsItr = fm.nsItr) { nsItr->front(); if (!nsItr->hasEnded()) { fm.nsSum += nsItr->data().data; fm.shouldSetTone = true; } } writePitchFM(fm); } void OPNAController::releaseStartFMSequences(FMChannel& fm) { if (fm.isMute) return; size_t inch = fm.inCh; if (lfoStartCntFM_[inch] > 0) { --lfoStartCntFM_[inch]; writeFMLFOAllRegisters(inch); } checkOperatorSequenceFM(fm, 2); if (auto& treItr = fm.treItr) treItr->release(); fm.volSldSum += fm.volSld; checkVolumeEffectFM(fm); if (auto& arpItr = fm.arpItr) { arpItr->release(); checkRealToneFMByArpeggio(fm); } checkPortamentoFM(fm); if (auto& ptItr = fm.ptItr) { ptItr->release(); checkRealToneFMByPitch(fm); } if (auto& vibItr = fm.vibItr) { vibItr->release(); fm.shouldSetTone = true; } if (auto& nsItr = fm.nsItr) { nsItr->release(); if (!nsItr->hasEnded()) { fm.nsSum += nsItr->data().data; fm.shouldSetTone = true; } } if (fm.shouldSetTone) writePitchFM(fm); } void OPNAController::tickEventFM(FMChannel& fm) { if (fm.hasPreSetTickEvent) { fm.hasPreSetTickEvent = false; } else { if (fm.isMute) return; size_t inch = fm.inCh; if (lfoStartCntFM_[inch] > 0) { --lfoStartCntFM_[inch]; writeFMLFOAllRegisters(inch); } checkOperatorSequenceFM(fm, 0); if (auto& treItr = fm.treItr) treItr->next(); fm.volSldSum += fm.volSld; checkVolumeEffectFM(fm); if (auto& arpItr = fm.arpItr) { arpItr->next(); checkRealToneFMByArpeggio(fm); } checkPortamentoFM(fm); if (auto& ptItr = fm.ptItr) { ptItr->next(); checkRealToneFMByPitch(fm); } if (auto& vibItr = fm.vibItr) { vibItr->next(); fm.shouldSetTone = true; } if (auto& nsItr = fm.nsItr) { nsItr->next(); if (!nsItr->hasEnded()) { fm.nsSum += nsItr->data().data; fm.shouldSetTone = true; } } if (fm.shouldSetTone) writePitchFM(fm); } } void OPNAController::checkOperatorSequenceFM(FMChannel& fm, int type) { size_t inch = fm.inCh; for (auto& p : FM_ENV_PARAMS_OP.at(fm.opType)) { if (auto& it = opSeqItFM_[inch].at(p)) { switch (type) { case 0: it->next(); break; case 1: it->front(); break; case 2: it->release(); break; default: throw std::out_of_range("The range of type is 0-2."); } if (!it->hasEnded()) { int d = it->data().data; if (d != envFM_[inch]->getParameterValue(p)) { writeFMEnveropeParameterToRegister(inch, p, d); } } } } } void OPNAController::checkVolumeEffectFM(FMChannel& fm) { int v; if (auto& treItr = fm.treItr) { v = treItr->data().data + fm.volSldSum; } else { if (fm.volSld) v = fm.volSldSum; else return; } uint32_t bch = getFMChannelOffset(fm.ch); switch (fm.opType) { case FMOperatorType::All: { int al = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::AL); if (IS_CARRIER[0][al]) { // Operator 1 int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL1) + v; opna_->setRegister(0x40 + bch, static_cast(utils::clamp(data, 0 ,127))); } if (IS_CARRIER[1][al]) { // Operator 2 int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL2) + v; opna_->setRegister(0x40 + bch + 8, static_cast(utils::clamp(data, 0 ,127))); } if (IS_CARRIER[2][al]) { // Operator 3 int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL3) + v; opna_->setRegister(0x40 + bch + 4, static_cast(utils::clamp(data, 0 ,127))); } { // Operator 4 (absolutely carrier) int data = envFM_[fm.ch]->getParameterValue(FMEnvelopeParameter::TL4) + v; opna_->setRegister(0x40 + bch + 12, static_cast(utils::clamp(data, 0 ,127))); } break; } case FMOperatorType::Op1: { int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL1) + v; opna_->setRegister(0x40 + bch, static_cast(utils::clamp(data, 0 ,127))); break; } case FMOperatorType::Op2: { int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL2) + v; opna_->setRegister(0x40 + bch + 8, static_cast(utils::clamp(data, 0 ,127))); break; } case FMOperatorType::Op3: { int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL3) + v; opna_->setRegister(0x40 + bch + 4, static_cast(utils::clamp(data, 0 ,127))); break; } case FMOperatorType::Op4: { int data = envFM_[fm.inCh]->getParameterValue(FMEnvelopeParameter::TL4) + v; opna_->setRegister(0x40 + bch + 12, static_cast(utils::clamp(data, 0 ,127))); break; } } } void OPNAController::writePitchFM(FMChannel& fm) { if (fm.neverSetBaseNote) return; Note&& note = fm.baseNote + (fm.ptSum + (fm.vibItr ? fm.vibItr->data().data : 0) + fm.detune + fm.nsSum + fm.transpose); uint16_t p = note_utils::calculateFNumber(note.getAbsolutePicth(), fm.fdetune); uint32_t offset = getFMChannelOffset(fm.ch, true); opna_->setRegister(0xa4 + offset, p >> 8); opna_->setRegister(0xa0 + offset, p & 0x00ff); fm.shouldSetTone = false; } void OPNAController::setInstrumentFMProperties(FMChannel& fm) { fm.isEnabledEnvReset = refInstFM_[fm.inCh]->getEnvelopeResetEnabled(fm.opType); } uint8_t OPNAController::calculateTL(int ch, uint8_t data) const { auto& fm = fm_[ch]; int v = (fm.oneshotVol == UNUSED_VALUE) ? fm.baseVol : fm.oneshotVol; return (data > 127 - v) ? 127 : static_cast(data + v); } //---------- SSG ----------// namespace { constexpr int AUTO_ENV_SHAPE_TYPE[15] = { 17, 17, 17, 21, 21, 21, 21, 16, 17, 18, 19, 20, 21, 22, 23 }; namespace ToneNoiseState { enum : uint8_t { CLEAR_TN = 0, TONE_TN = 1, NOISE_TN = 2, ALL_TN = 3 }; } inline uint8_t SSGToneFlag(size_t ch) { return (1u << ch); } inline uint8_t SSGNoiseFlag(size_t ch) { return (8u << ch); } } /********** Key on-off **********/ void OPNAController::keyOnSSG(int ch, const Note& note, bool isJam) { auto& ssg = ssg_[ch]; if (ssg.isMute) return; ssg.echoBuf.push(note); if (ssg.isTonePrtm && ssg.hasKeyOnBefore) { ssg.baseNote += (ssg.nsSum + ssg.transpose); } else { ssg.baseNote = ssg.echoBuf.latest(); ssg.neverSetBaseNote = false; ssg.ptSum = 0; ssg.volSldSum = 0; ssg.oneshotVol = UNUSED_VALUE; } if (!ssg.hasSetNs) { ssg.nsItr.reset(); } ssg.hasSetNs = false; ssg.shouldSetTone = true; ssg.nsSum = 0; ssg.transpose = 0; { ssg.isInKeyOnProcess_ = true; setFrontSSGSequences(ssg); ssg.isInKeyOnProcess_ = false; } ssg.shouldSkip1stTickExec = isJam; ssg.isKeyOn = true; ssg.hasKeyOnBefore = true; } void OPNAController::keyOnSSG(int ch, int echoBuf) { auto& ssg = ssg_[ch]; if (static_cast(echoBuf) < ssg.echoBuf.size()) keyOnSSG(ch, ssg.echoBuf[echoBuf]); } void OPNAController::keyOffSSG(int ch, bool isJam) { auto& ssg = ssg_[ch]; if (!ssg.isKeyOn) { tickEventSSG(ssg); return; } releaseStartSSGSequences(ssg); ssg.shouldSkip1stTickExec = isJam; ssg.isKeyOn = false; } /********** Set instrument **********/ /// NOTE: inst != nullptr void OPNAController::setInstrumentSSG(int ch, std::shared_ptr inst) { auto& ssg = ssg_[ch]; ssg.refInst = inst; if (inst->getWaveformEnabled()) ssg.wfItr = inst->getWaveformSequenceIterator(); else ssg.wfItr.reset(); if (inst->getToneNoiseEnabled()) ssg.tnItr = inst->getToneNoiseSequenceIterator(); else ssg.tnItr.reset(); if (inst->getEnvelopeEnabled()) ssg.envItr = inst->getEnvelopeSequenceIterator(); else ssg.envItr.reset(); if (!ssg.isArpEff) { if (inst->getArpeggioEnabled()) ssg.arpItr = inst->getArpeggioSequenceIterator(); else ssg.arpItr.reset(); } if (inst->getPitchEnabled()) ssg.ptItr = inst->getPitchSequenceIterator(); else ssg.ptItr.reset(); } void OPNAController::updateInstrumentSSG(int instNum) { for (auto& ssg : ssg_) { if (ssg.refInst && ssg.refInst->isRegisteredWithManager() && ssg.refInst->getNumber() == instNum) { if (!ssg.refInst->getWaveformEnabled()) ssg.wfItr.reset(); if (!ssg.refInst->getToneNoiseEnabled()) ssg.tnItr.reset(); if (!ssg.refInst->getEnvelopeEnabled()) ssg.envItr.reset(); if (!ssg.refInst->getArpeggioEnabled()) ssg.arpItr.reset(); if (!ssg.refInst->getPitchEnabled()) ssg.ptItr.reset(); } } } /********** Set volume **********/ void OPNAController::setVolumeSSG(int ch, int volume) { if (volume < bt_defs::NSTEP_SSG_VOLUME) { auto& ssg = ssg_[ch]; ssg.baseVol = volume; ssg.oneshotVol = UNUSED_VALUE; if (ssg.isKeyOn) setRealVolumeSSG(ssg); } } void OPNAController::setOneshotVolumeSSG(int ch, int volume) { if (volume < bt_defs::NSTEP_SSG_VOLUME) { auto& ssg = ssg_[ch]; ssg.oneshotVol = volume; if (ssg.isKeyOn) setRealVolumeSSG(ssg); } } void OPNAController::setRealVolumeSSG(SSGChannel& ssg) { if (SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isHardEnv) { ssg.shouldSetEnv = false; return; } int volume = (ssg.oneshotVol == UNUSED_VALUE) ? ssg.baseVol : ssg.oneshotVol; if (auto& envItr = ssg.envItr) { int d = envItr->data().data; if (0 <= d && d < 16) { volume -= (15 - d); } } if (auto& treItr = ssg.treItr) volume += treItr->data().data; volume += ssg.volSldSum; volume = utils::clamp(volume, 0, 15); opna_->setRegister(0x08 + ssg.ch, static_cast(volume)); ssg.shouldSetEnv = false; } void OPNAController::setMasterVolumeSSG(double dB) { opna_->setVolumeSSG(dB); } /********** Set effect **********/ void OPNAController::setArpeggioEffectSSG(int ch, int second, int third) { auto& ssg = ssg_[ch]; if (second || third) { ssg.arpItr = std::make_unique(second, third); ssg.isArpEff = true; } else { if (!ssg.refInst || !ssg.refInst->getArpeggioEnabled()) ssg.arpItr.reset(); else ssg.arpItr = ssg.refInst->getArpeggioSequenceIterator(); ssg.isArpEff = false; } } void OPNAController::setPortamentoEffectSSG(int ch, int depth, bool isTonePortamento) { auto& ssg = ssg_[ch]; ssg.prtmDepth = depth; ssg.isTonePrtm = depth ? isTonePortamento : false; } void OPNAController::setVibratoEffectSSG(int ch, int period, int depth) { auto& ssg = ssg_[ch]; if (period && depth) ssg.vibItr = std::make_unique(period, depth); else ssg.vibItr.reset(); } void OPNAController::setTremoloEffectSSG(int ch, int period, int depth) { auto& ssg = ssg_[ch]; if (period && depth) ssg.treItr = std::make_unique(period, depth); else ssg.treItr.reset(); } void OPNAController::setVolumeSlideSSG(int ch, int depth, bool isUp) { ssg_[ch].volSld = isUp ? depth : -depth; } void OPNAController::setDetuneSSG(int ch, int pitch) { auto& ssg = ssg_[ch]; ssg.detune = pitch; ssg.shouldSetTone = true; } void OPNAController::setFineDetuneSSG(int ch, int pitch) { auto& ssg = ssg_[ch]; ssg.fdetune = pitch; ssg.shouldSetTone = true; } void OPNAController::setNoteSlideSSG(int ch, int speed, int seminote) { auto& ssg = ssg_[ch]; if (seminote) { ssg.nsItr = std::make_unique(speed, seminote); ssg.hasSetNs = true; } else ssg.nsItr.reset(); } void OPNAController::setTransposeEffectSSG(int ch, int seminote) { auto& ssg = ssg_[ch]; ssg.transpose += (seminote * Note::SEMINOTE_PITCH); ssg.shouldSetTone = true; } void OPNAController::setToneNoiseMixSSG(int ch, int value) { auto& ssg = ssg_[ch]; // Tone if (value & ToneNoiseState::TONE_TN) mixerSSG_ &= ~SSGToneFlag(ssg.ch); else mixerSSG_ |= SSGToneFlag(ssg.ch); // Noise if (value & ToneNoiseState::NOISE_TN) mixerSSG_ &= ~SSGNoiseFlag(ssg.ch); else mixerSSG_ |= SSGNoiseFlag(ssg.ch); ssg.hasRequestedTnEffSet = true; } void OPNAController::setNoisePitchSSG(int ch, int pitch) { (void)ch; noisePeriodSSG_ = 31 - static_cast(pitch); // Reverse order opna_->setRegister(0x06, noisePeriodSSG_); } void OPNAController::setHardEnvelopePeriod(int ch, bool high, int period) { auto& ssg = ssg_[ch]; bool sendable = ssg.isHardEnv && (ssg.envState.type == SSGEnvelopeUnit::RawSubdata); if (high) { hardEnvPeriodHighSSG_ = period; if (sendable) { int sub = (period << 8) | (ssg.envState.subdata & 0x00ff); ssg.envState = SSGEnvelopeUnit::makeRawUnit(ssg.envState.data, sub); opna_->setRegister(0x0c, static_cast(period)); } } else { hardEnvPeriodLowSSG_ = period; if (sendable) { int sub = (ssg.envState.subdata & 0xff00) | period; ssg.envState = SSGEnvelopeUnit::makeRawUnit(ssg.envState.data, sub); opna_->setRegister(0x0b, static_cast(period)); } } } void OPNAController::setAutoEnvelopeSSG(int ch, int shift, int shape) { auto& ssg = ssg_[ch]; if (shape) { opna_->setRegister(0x0d, static_cast(shape)); int d = AUTO_ENV_SHAPE_TYPE[shape - 1]; opna_->setRegister(0x08 + ssg.ch, 0x10); ssg.isHardEnv = true; if (shift == -8) { // Raw ssg.envState = SSGEnvelopeUnit::makeRawUnit(d, (hardEnvPeriodHighSSG_ << 8) | hardEnvPeriodLowSSG_); opna_->setRegister(0x0c, static_cast(hardEnvPeriodHighSSG_)); opna_->setRegister(0x0b, static_cast(hardEnvPeriodLowSSG_)); ssg.shouldSetEnv = false; ssg.shouldSetHardEnvFreq = false; } else { ssg.envState = SSGEnvelopeUnit::makeShiftUnit(d, shift); ssg.shouldSetEnv = true; ssg.shouldSetHardEnvFreq = true; } } else { ssg.isHardEnv = false; ssg.envState = SSGEnvelopeUnit(); // Clear hard envelope in setRealVolumeSSG ssg.shouldSetEnv = true; ssg.shouldSetHardEnvFreq = false; } ssg.envItr.reset(); } /********** For state retrieve **********/ void OPNAController::haltSequencesSSG(int ch) { auto& ssg = ssg_[ch]; if (auto& wfItr = ssg.wfItr) wfItr->end(); if (auto& treItr = ssg.treItr) treItr->end(); if (auto& envItr = ssg.envItr) envItr->end(); if (auto& tnItr = ssg.tnItr) tnItr->end(); if (auto& arpItr = ssg.arpItr) arpItr->end(); if (auto& ptItr = ssg.ptItr) ptItr->end(); if (auto& vibItr = ssg.vibItr) vibItr->end(); if (auto& nsItr = ssg.nsItr) nsItr->end(); } /********** Chip details **********/ bool OPNAController::isKeyOnSSG(int ch) const { return ssg_[ch].isKeyOn; } bool OPNAController::isTonePortamentoSSG(int ch) const { return ssg_[ch].isTonePrtm; } Note OPNAController::getSSGLatestNote(int ch) const { return ssg_[ch].echoBuf.latest(); } /***********************************/ void OPNAController::initSSG() { mixerSSG_ = 0xff; opna_->setRegister(0x07, mixerSSG_); noisePeriodSSG_ = 0; opna_->setRegister(0x06, noisePeriodSSG_); hardEnvPeriodHighSSG_ = 0; hardEnvPeriodLowSSG_ = 0; for (size_t ch = 0; ch < 3; ++ch) { auto& ssg = ssg_[ch]; ssg.ch = ch; ssg.isKeyOn = false; ssg.hasKeyOnBefore = false; ssg.isInKeyOnProcess_ = false; ssg.refInst.reset(); // Init envelope ssg.echoBuf.clear(); ssg.neverSetBaseNote = true; ssg.baseVol = bt_defs::NSTEP_SSG_VOLUME - 1; // Init volume ssg.oneshotVol = UNUSED_VALUE; ssg.isHardEnv = false; // Init sequence ssg.shouldSkip1stTickExec = false; ssg.wfItr.reset(); ssg.wfChState = SSGWaveformUnit::makeOnlyDataUnit(SSGWaveformType::UNSET); ssg.envItr.reset(); ssg.envState = SSGEnvelopeUnit(); ssg.tnItr.reset(); ssg.arpItr.reset(); ssg.ptItr.reset(); ssg.ptSum = 0; ssg.shouldSetEnv = false; ssg.shouldSetSqMaskFreq = false; ssg.shouldSetHardEnvFreq = false; ssg.shouldUpdateMixState = false; ssg.shouldSetTone = false; // Effect ssg.isArpEff = false; ssg.prtmDepth = 0; ssg.isTonePrtm = false; ssg.vibItr.reset(); ssg.treItr.reset(); ssg.volSld = 0; ssg.volSldSum = 0; ssg.detune = 0; ssg.fdetune = 0; ssg.nsItr.reset(); ssg.nsSum = 0; ssg.hasSetNs = false; ssg.transpose = 0; ssg.hasRequestedTnEffSet = false; } } void OPNAController::setMuteSSGState(int ch, bool isMute) { auto& ssg = ssg_[ch]; ssg.isMute = isMute; if (isMute) { opna_->setRegister(0x08 + ssg.ch, 0); ssg.isKeyOn = false; } } bool OPNAController::isMuteSSG(int ch) { return ssg_[ch].isMute; } void OPNAController::setFrontSSGSequences(SSGChannel& ssg) { if (ssg.isMute) return; if (auto& wfItr = ssg.wfItr) { wfItr->front(); writeWaveformSSGToRegister(ssg); } else writeSquareWaveform(ssg); if (auto& treItr = ssg.treItr) { treItr->front(); ssg.shouldSetEnv = true; } if (ssg.volSld) { ssg.volSldSum += ssg.volSld; ssg.shouldSetEnv = true; } if (auto& envItr = ssg.envItr) { envItr->front(); writeEnvelopeSSGToRegister(ssg); } else setRealVolumeSSG(ssg); if (ssg.hasRequestedTnEffSet) { // Reflect mixer effect writeMixerSSGToRegisterByEffect(ssg); } else if (auto& tnItr = ssg.tnItr) { tnItr->front(); writeMixerSSGToRegisterBySequence(ssg); } else if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); if (auto& arpItr = ssg.arpItr) { arpItr->front(); checkRealToneSSGByArpeggio(ssg); } checkPortamentoSSG(ssg); if (auto& ptItr = ssg.ptItr) { ptItr->front(); checkRealToneSSGByPitch(ssg); } if (auto& vibItr = ssg.vibItr) { vibItr->front(); ssg.shouldSetTone = true; } if (auto& nsItr = ssg.nsItr) { nsItr->front(); if (!nsItr->hasEnded()) { ssg.nsSum += nsItr->data().data; ssg.shouldSetTone = true; } } writePitchSSG(ssg); } void OPNAController::releaseStartSSGSequences(SSGChannel& ssg) { if (ssg.isMute) return; if (auto& wfItr = ssg.wfItr) { wfItr->release(); writeWaveformSSGToRegister(ssg); } if (auto& treItr = ssg.treItr) { treItr->release(); ssg.shouldSetEnv = true; } if (ssg.volSld) { ssg.volSldSum += ssg.volSld; ssg.shouldSetEnv = true; } if (auto& envItr = ssg.envItr) { envItr->release(); if (envItr->hasEnded()) { // Silence opna_->setRegister(0x08 + ssg.ch, 0); ssg.shouldSetEnv = false; ssg.isHardEnv = false; } else writeEnvelopeSSGToRegister(ssg); } else { // Silence opna_->setRegister(0x08 + ssg.ch, 0); ssg.shouldSetEnv = false; ssg.isHardEnv = false; } if (ssg.hasRequestedTnEffSet) { // Reflect mixer effect writeMixerSSGToRegisterByEffect(ssg); } else if (auto& tnItr = ssg.tnItr) { tnItr->release(); writeMixerSSGToRegisterBySequence(ssg); } else if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); if (auto& arpItr = ssg.arpItr) { arpItr->release(); checkRealToneSSGByArpeggio(ssg); } checkPortamentoSSG(ssg); if (auto& ptItr = ssg.ptItr) { ptItr->release(); checkRealToneSSGByPitch(ssg); } if (auto& vibItr = ssg.vibItr) { vibItr->release(); ssg.shouldSetTone = true; } if (auto& nsItr = ssg.nsItr) { nsItr->release(); if (!nsItr->hasEnded()) { ssg.nsSum += nsItr->data().data; ssg.shouldSetTone = true; } } if (ssg.shouldSetTone || ssg.shouldSetHardEnvFreq || ssg.shouldSetSqMaskFreq) writePitchSSG(ssg); } void OPNAController::tickEventSSG(SSGChannel& ssg) { if (ssg.shouldSkip1stTickExec) { ssg.shouldSkip1stTickExec = false; } else { if (ssg.isMute) return; if (auto& wfItr = ssg.wfItr) { wfItr->next(); writeWaveformSSGToRegister(ssg); } if (auto& treItr = ssg.treItr) { treItr->next(); ssg.shouldSetEnv = true; } if (ssg.volSld) { ssg.volSldSum += ssg.volSld; ssg.shouldSetEnv = true; } if (auto& envItr = ssg.envItr) { envItr->next(); writeEnvelopeSSGToRegister(ssg); } else if (ssg.shouldSetEnv) { setRealVolumeSSG(ssg); } if (ssg.hasRequestedTnEffSet) { // Reflect mixer effect writeMixerSSGToRegisterByEffect(ssg); } else if (auto& tnItr = ssg.tnItr) { tnItr->next(); writeMixerSSGToRegisterBySequence(ssg); } else if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); if (auto& arpItr = ssg.arpItr) { arpItr->next(); checkRealToneSSGByArpeggio(ssg); } checkPortamentoSSG(ssg); if (auto& ptItr = ssg.ptItr) { ptItr->next(); checkRealToneSSGByPitch(ssg); } if (auto& vibItr = ssg.vibItr) { vibItr->next(); ssg.shouldSetTone = true; } if (auto& nsItr = ssg.nsItr) { nsItr->next(); if (!nsItr->hasEnded()) { ssg.nsSum += nsItr->data().data; ssg.shouldSetTone = true; } } if (ssg.shouldSetTone || ssg.shouldSetHardEnvFreq || ssg.shouldSetSqMaskFreq) writePitchSSG(ssg); } } void OPNAController::writeWaveformSSGToRegister(SSGChannel& ssg) { auto& wfItr = ssg.wfItr; if (wfItr->hasEnded()) return; SSGWaveformUnit&& data = wfItr->data(); switch (data.data) { case SSGWaveformType::SQUARE: { writeSquareWaveform(ssg); return; } case SSGWaveformType::TRIANGLE: { if (ssg.wfChState.data == SSGWaveformType::TRIANGLE && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { ssg.shouldSetEnv = false; return; } switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: break; default: ssg.shouldUpdateMixState = true; break; } // Reset phase switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SQM_TRIANGLE: if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0e); break; default: opna_->setRegister(0x0d, 0x0e); break; } if (ssg.isHardEnv) { ssg.isHardEnv = false; } else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { opna_->setRegister(0x08 + ssg.ch, 0x10); } ssg.shouldSetEnv = false; ssg.shouldSetTone = true; ssg.shouldSetSqMaskFreq = false; break; } case SSGWaveformType::SAW: { if (ssg.wfChState.data == SSGWaveformType::SAW && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { ssg.shouldSetEnv = false; return; } switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: break; default: ssg.shouldUpdateMixState = true; break; } // Reset phase switch (ssg.wfChState.data) { case SSGWaveformType::SAW: case SSGWaveformType::SQM_SAW: if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0c); break; default: opna_->setRegister(0x0d, 0x0c); break; } if (ssg.isHardEnv) { ssg.isHardEnv = false; } else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { opna_->setRegister(0x08 + ssg.ch, 0x10); } ssg.shouldSetEnv = false; ssg.shouldSetTone = true; ssg.shouldSetSqMaskFreq = false; break; } case SSGWaveformType::INVSAW: { if (ssg.wfChState.data == SSGWaveformType::INVSAW && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { ssg.shouldSetEnv = false; return; } switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: break; default: ssg.shouldUpdateMixState = true; break; } // Reset phase switch (ssg.wfChState.data) { case SSGWaveformType::INVSAW: case SSGWaveformType::SQM_INVSAW: if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x08); break; default: opna_->setRegister(0x0d, 0x08); break; } if (ssg.isHardEnv) { ssg.isHardEnv = false; } else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { opna_->setRegister(0x08 + ssg.ch, 0x10); } ssg.shouldSetEnv = false; ssg.shouldSetTone = true; ssg.shouldSetSqMaskFreq = false; break; } case SSGWaveformType::SQM_TRIANGLE: { if (ssg.wfChState == data && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { ssg.shouldSetEnv = false; return; } switch (ssg.wfChState.data) { case SSGWaveformType::UNSET: case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: ssg.shouldUpdateMixState = true; break; default: break; } if (ssg.wfChState.subdata != data.subdata) { if (data.type == SSGWaveformUnit::RatioSubdata) { // Set frequency of square mask in pitch process since it depends on pitch ssg.shouldSetSqMaskFreq = true; } else { // Raw data uint16_t pitch = static_cast(data.subdata); size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); ssg.shouldSetSqMaskFreq = false; } } else { ssg.shouldSetSqMaskFreq = false; } // Reset phase switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SQM_TRIANGLE: if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0e); break; default: opna_->setRegister(0x0d, 0x0e); break; } if (ssg.isHardEnv) { ssg.isHardEnv = false; } else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { opna_->setRegister(0x08 + ssg.ch, 0x10); } ssg.shouldSetEnv = false; ssg.shouldSetTone = true; break; } case SSGWaveformType::SQM_SAW: { if (ssg.wfChState == data && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { ssg.shouldSetEnv = false; return; } switch (ssg.wfChState.data) { case SSGWaveformType::UNSET: case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: ssg.shouldUpdateMixState = true; break; default: break; } if (ssg.wfChState.subdata != data.subdata) { if (data.type == SSGWaveformUnit::RatioSubdata) { // Set frequency of square mask in pitch process since it depends on pitch ssg.shouldSetSqMaskFreq = true; } else { // Raw data uint16_t pitch = static_cast(data.subdata); size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); ssg.shouldSetSqMaskFreq = false; } } else { ssg.shouldSetSqMaskFreq = false; } // Reset phase switch (ssg.wfChState.data) { case SSGWaveformType::SAW: case SSGWaveformType::SQM_SAW: if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x0c); break; default: opna_->setRegister(0x0d, 0x0c); break; } if (ssg.isHardEnv) { ssg.isHardEnv = false; } else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { opna_->setRegister(0x08 + ssg.ch, 0x10); } ssg.shouldSetEnv = false; ssg.shouldSetTone = true; break; } case SSGWaveformType::SQM_INVSAW: { if (ssg.wfChState == data && !ssg.isInKeyOnProcess_ && ssg.isKeyOn) { ssg.shouldSetEnv = false; return; } switch (ssg.wfChState.data) { case SSGWaveformType::UNSET: case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: ssg.shouldUpdateMixState = true; break; default: break; } if (ssg.wfChState.subdata != data.subdata) { if (data.type == SSGWaveformUnit::RatioSubdata) { // Set frequency of square mask in pitch process since it depends on pitch ssg.shouldSetSqMaskFreq = true; } else { // Raw data uint16_t pitch = static_cast(data.subdata); size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); ssg.shouldSetSqMaskFreq = false; } } else { ssg.shouldSetSqMaskFreq = false; } // Reset phase switch (ssg.wfChState.data) { case SSGWaveformType::INVSAW: case SSGWaveformType::SQM_INVSAW: if (ssg.isInKeyOnProcess_) opna_->setRegister(0x0d, 0x08); break; default: opna_->setRegister(0x0d, 0x08); break; } if (ssg.isHardEnv) { ssg.isHardEnv = false; } else if (!SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data) || ssg.isInKeyOnProcess_) { opna_->setRegister(0x08 + ssg.ch, 0x10); } ssg.shouldSetEnv = false; ssg.shouldSetTone = true; break; } default: return; } ssg.wfChState = std::move(data); // Clear current envelope state // since the register of volume and hardware envelope frequency is used by waveform sequence ssg.envState = SSGEnvelopeUnit(); } void OPNAController::writeSquareWaveform(SSGChannel& ssg) { if (ssg.wfChState.data == SSGWaveformType::SQUARE) { if (ssg.isInKeyOnProcess_) { ssg.shouldSetEnv = true; ssg.shouldSetTone = true; } return; } switch (ssg.wfChState.data) { case SSGWaveformType::SQM_TRIANGLE: case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: break; default: ssg.shouldUpdateMixState = true; break; } ssg.shouldSetEnv = true; ssg.shouldSetTone = true; ssg.shouldSetSqMaskFreq = false; ssg.wfChState = SSGWaveformUnit::makeOnlyDataUnit(SSGWaveformType::SQUARE); } void OPNAController::writeEnvelopeSSGToRegister(SSGChannel& ssg) { // Skip if waveform settings use hardware envelope if (SSGWaveformType::testHardEnvelopeOccupancity(ssg.wfChState.data)) return; auto& envItr = ssg.envItr; if (envItr->hasEnded()) { if (ssg.shouldSetEnv) setRealVolumeSSG(ssg); return; } SSGEnvelopeUnit&& data = envItr->data(); if (data.data < 16) { // Software envelope ssg.isHardEnv = false; ssg.envState = std::move(data); setRealVolumeSSG(ssg); } else { // Hardware envelope if (!ssg.isHardEnv) { opna_->setRegister(0x08 + ssg.ch, 0x10); ssg.isHardEnv = true; } if (ssg.envState.subdata != data.subdata) { ssg.envState.type = data.type; ssg.envState.subdata = data.subdata; if (data.type == SSGEnvelopeUnit::RatioSubdata) { // Set frequency of hardware envelope in pitch process since it depends on pitch ssg.shouldSetHardEnvFreq = true; } else { // Raw data opna_->setRegister(0x0b, 0x00ff & ssg.envState.subdata); opna_->setRegister(0x0c, static_cast(ssg.envState.subdata >> 8)); ssg.shouldSetHardEnvFreq = false; } } if (ssg.envState.data != data.data || ssg.isInKeyOnProcess_) { opna_->setRegister(0x0d, static_cast(data.data - 16 + 8)); // Reset phase ssg.envState.data = data.data; if (data.type == SSGEnvelopeUnit::RatioSubdata) { // Set frequency of hardware envelope in pitch process since it depends on pitch ssg.shouldSetHardEnvFreq = true; } } } ssg.shouldSetEnv = false; } void OPNAController::writeMixerSSGToRegisterByEffect(SSGChannel& ssg) { ssg.tnItr.reset(); opna_->setRegister(0x07, mixerSSG_); ssg.hasRequestedTnEffSet = false; ssg.shouldUpdateMixState = false; } void OPNAController::writeMixerSSGToRegisterBySequence(SSGChannel& ssg) { auto& tnItr = ssg.tnItr; if (tnItr->hasEnded()) { if (ssg.shouldUpdateMixState) writeMixerSSGToRegisterByNoReference(ssg); return; } int type = tnItr->data().data; uint8_t prevMixer = mixerSSG_; if (!type) { // tone switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: mixerSSG_ |= SSGToneFlag(ssg.ch); // Off for buzzer effects break; default: mixerSSG_ &= ~SSGToneFlag(ssg.ch); break; } mixerSSG_ |= SSGNoiseFlag(ssg.ch); } else if (type == 65) { // None mixerSSG_ |= SSGToneFlag(ssg.ch); mixerSSG_ |= SSGNoiseFlag(ssg.ch); } else if (type > 32) { // Tone&Noise switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: mixerSSG_ |= SSGToneFlag(ssg.ch); break; default: mixerSSG_ &= ~SSGToneFlag(ssg.ch); break; } mixerSSG_ &= ~SSGNoiseFlag(ssg.ch); uint8_t p = static_cast(64 - type); // Reverse order if (noisePeriodSSG_ != p) { noisePeriodSSG_ = p; opna_->setRegister(0x06, p); } } else { // Noise mixerSSG_ |= SSGToneFlag(ssg.ch); mixerSSG_ &= ~SSGNoiseFlag(ssg.ch); uint8_t p = static_cast(32 - type); // Reverse order if (noisePeriodSSG_ != p) { noisePeriodSSG_ = p; opna_->setRegister(0x06, p); } } if (mixerSSG_ != prevMixer) opna_->setRegister(0x07, mixerSSG_); ssg.shouldUpdateMixState = false; } void OPNAController::writeMixerSSGToRegisterByNoReference(SSGChannel& ssg) { switch (ssg.wfChState.data) { case SSGWaveformType::TRIANGLE: case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: mixerSSG_ |= SSGToneFlag(ssg.ch); break; default: mixerSSG_ &= ~SSGToneFlag(ssg.ch); break; } opna_->setRegister(0x07, mixerSSG_); ssg.shouldUpdateMixState = false; } void OPNAController::writePitchSSG(SSGChannel& ssg) { if (ssg.neverSetBaseNote) return; int p = (ssg.baseNote + (ssg.ptSum + (ssg.vibItr ? ssg.vibItr->data().data : 0) + ssg.detune + ssg.nsSum + ssg.transpose)).getAbsolutePicth(); switch (ssg.wfChState.data) { case SSGWaveformType::SQUARE: { uint16_t pitch = note_utils::calculateSSGSquareTP(p, ssg.fdetune); if (ssg.shouldSetTone) { size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, pitch & 0xff); opna_->setRegister(0x01 + offset, pitch >> 8); // Forced call in case of changes in tone processing writeAutoEnvelopePitchSSG(ssg, pitch); } else if (ssg.shouldSetHardEnvFreq) { writeAutoEnvelopePitchSSG(ssg, pitch); } break; } case SSGWaveformType::TRIANGLE: if (ssg.shouldSetTone) { uint16_t pitch = note_utils::calculateSSGTriangleEP(p, ssg.fdetune); opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); } break; case SSGWaveformType::SAW: case SSGWaveformType::INVSAW: if (ssg.shouldSetTone){ uint16_t pitch = note_utils::calculateSSGSawEP(p, ssg.fdetune); opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); } break; case SSGWaveformType::SQM_TRIANGLE: { uint16_t pitch = note_utils::calculateSSGTriangleEP(p, ssg.fdetune); if (ssg.shouldSetTone) { opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); // Forced call in case of changes in tone processing if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { writeSquareMaskPitchSSG(ssg, pitch, true); } } else if (ssg.shouldSetSqMaskFreq) { if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { writeSquareMaskPitchSSG(ssg, pitch, true); } } break; } case SSGWaveformType::SQM_SAW: case SSGWaveformType::SQM_INVSAW: { uint16_t pitch = note_utils::calculateSSGSawEP(p, ssg.fdetune); if (ssg.shouldSetTone) { opna_->setRegister(0x0b, pitch & 0x00ff); opna_->setRegister(0x0c, pitch >> 8); // Forced call in case of changes in tone processing if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { writeSquareMaskPitchSSG(ssg, pitch, false); } } else if (ssg.shouldSetSqMaskFreq) { if (ssg.wfChState.type == SSGWaveformUnit::RatioSubdata) { writeSquareMaskPitchSSG(ssg, pitch, false); } } break; } default: break; } ssg.shouldSetTone = false; ssg.shouldSetEnv = false; ssg.shouldSetHardEnvFreq = false; ssg.shouldSetSqMaskFreq = false; } void OPNAController::writeAutoEnvelopePitchSSG(SSGChannel& ssg, double tonePitch) { // Multiple frequency if triangle int div = (ssg.envState.data == 18 || ssg.envState.data == 22) ? 32 : 16; switch (ssg.envState.type) { case SSGEnvelopeUnit::RatioSubdata: { int r1, r2; ssg.envState.getSubdataAsRatio(r1, r2); uint16_t period = static_cast(std::round(tonePitch * r1 / (r2 * div))); opna_->setRegister(0x0b, 0x00ff & period); opna_->setRegister(0x0c, static_cast(period >> 8)); break; } case SSGEnvelopeUnit::ShiftSubdata: { uint16_t period = static_cast(std::round(tonePitch / div)); int rshift; ssg.envState.getSubdataAsShift(rshift); rshift -= 4; // Adjust rate to that of 0CC-FamiTracker if (rshift < 0) period <<= -rshift; else period >>= rshift; opna_->setRegister(0x0b, 0x00ff & period); opna_->setRegister(0x0c, static_cast(period >> 8)); break; } default: break; } } void OPNAController::writeSquareMaskPitchSSG(SSGChannel& ssg, double tonePitch, bool isTriangle) { int mul = isTriangle ? 32 : 16; // Multiple frequency if triangle int r1, r2; ssg.wfChState.getSubdataAsRatio(r1, r2); // Calculate mask period uint16_t period = static_cast(std::round(r1 * mul * tonePitch / r2)); size_t offset = ssg.ch << 1; opna_->setRegister(0x00 + offset, period & 0x00ff); opna_->setRegister(0x01 + offset, period >> 8); } //---------- Rhythm ----------// namespace { inline uint8_t makePanAndVolumeRegVal(uint8_t panState, int volume) { return static_cast((panState << 6) | volume); } } /********** Key on/off **********/ void OPNAController::setKeyOnFlagRhythm(int ch) { auto& rhy = rhythm_[ch]; if (rhy.isMute) return; if (rhy.oneshotVol != UNUSED_VALUE) setVolumeRhythm(ch, rhy.baseVol); keyOnRequestFlagsRhythm_ |= static_cast(1 << ch); } void OPNAController::setKeyOffFlagRhythm(int ch) { keyOffRequestFlagsRhythm_ |= static_cast(1 << ch); } /********** Set volume **********/ void OPNAController::setVolumeRhythm(int ch, int volume) { if (volume < bt_defs::NSTEP_RHYTHM_VOLUME) { auto& rhy = rhythm_[ch]; rhy.baseVol = volume; rhy.oneshotVol = UNUSED_VALUE; opna_->setRegister(0x18 + static_cast(ch), makePanAndVolumeRegVal(rhy.panState, volume)); } } void OPNAController::setOneshotVolumeRhythm(int ch, int volume) { if (volume < bt_defs::NSTEP_RHYTHM_VOLUME) { auto& rhy = rhythm_[ch]; rhy.oneshotVol = volume; opna_->setRegister(0x18 + static_cast(ch), makePanAndVolumeRegVal(rhy.panState, volume)); } } void OPNAController::setMasterVolumeRhythm(int volume) { masterVolRhythm_ = volume; opna_->setRegister(0x11, static_cast(volume)); } /********** Set effect **********/ void OPNAController::setPanRhythm(int ch, int value) { auto& rhy = rhythm_[ch]; rhy.panState = static_cast(value); int volume = (rhy.oneshotVol == UNUSED_VALUE) ? rhy.baseVol : rhy.oneshotVol; opna_->setRegister(0x18 + static_cast(ch), makePanAndVolumeRegVal(value, volume)); } /***********************************/ void OPNAController::initRhythm() { keyOnRequestFlagsRhythm_ = 0; keyOffRequestFlagsRhythm_ = 0; masterVolRhythm_ = 0x3f; opna_->setRegister(0x11, 0x3f); // Rhythm total volume for (size_t ch = 0; ch < 6; ++ch) { auto& rhy = rhythm_[ch]; rhy.baseVol = bt_defs::NSTEP_RHYTHM_VOLUME - 1; rhy.oneshotVol = UNUSED_VALUE; // Init pan rhy.panState = 3; opna_->setRegister(0x18 + ch, 0xdf); } } void OPNAController::setMuteRhythmState(int ch, bool isMute) { rhythm_[ch].isMute = isMute; if (isMute) { setKeyOffFlagRhythm(ch); updateKeyOnOffStatusRhythm(); } } bool OPNAController::isMuteRhythm(int ch) { return rhythm_[ch].isMute; } void OPNAController::updateKeyOnOffStatusRhythm() { if (keyOnRequestFlagsRhythm_) { opna_->setRegister(0x10, keyOnRequestFlagsRhythm_); keyOnRequestFlagsRhythm_ = 0; } if (keyOffRequestFlagsRhythm_) { opna_->setRegister(0x10, 0x80 | keyOffRequestFlagsRhythm_); keyOffRequestFlagsRhythm_ = 0; } } //---------- ADPCM ----------// /********** Key on-off **********/ void OPNAController::keyOnADPCM(const Note& note, bool isJam) { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; echoBufADPCM_.push(note); bool isTonePrtm = hasTonePrtmADPCM_ && hasKeyOnBeforeADPCM_; if (isTonePrtm) { baseNoteADPCM_ += (nsSumADPCM_ + transposeADPCM_); } else { baseNoteADPCM_ = echoBufADPCM_.latest(); neverSetBaseNoteADPCM_ = false; ptSumADPCM_ = 0; volSldSumADPCM_ = 0; oneshotVolADPCM_ = UNUSED_VALUE; } if (hasSetNsADPCM_) hasSetNsADPCM_ = false; else nsItADPCM_.reset(); shouldSetToneADPCM_ = true; nsSumADPCM_ = 0; transposeADPCM_ = 0; setFrontADPCMSequences(); shouldSkip1stTickExecADPCM_ = isJam; if (!isTonePrtm) { opna_->setRegister(0x101, 0x02); opna_->setRegister(0x100, 0xa1); if (refInstADPCM_) { triggerSamplePlayADPCM(refInstADPCM_->getSampleStartAddress(), refInstADPCM_->getSampleStopAddress(), refInstADPCM_->isSampleRepeatable()); } else if (hasStartRequestedKit_) { // valid key in refInstKit_ int key = baseNoteADPCM_.getNoteNumber(); triggerSamplePlayADPCM(refInstKit_->getSampleStartAddress(key), refInstKit_->getSampleStopAddress(key), refInstKit_->isSampleRepeatable(key)); hasStartRequestedKit_ = false; } isKeyOnADPCM_ = true; } hasKeyOnBeforeADPCM_ = true; } void OPNAController::keyOnADPCM(int echoBuf) { if (static_cast(echoBuf) < echoBufADPCM_.size()) keyOnADPCM(echoBufADPCM_[echoBuf]); } void OPNAController::keyOffADPCM(bool isJam) { if (!isKeyOnADPCM_) { tickEventADPCM(); return; } releaseStartADPCMSequences(); shouldSkip1stTickExecADPCM_ = isJam; isKeyOnADPCM_ = false; } /********** Set instrument **********/ /// NOTE: inst != nullptr void OPNAController::setInstrumentADPCM(std::shared_ptr inst) { refInstADPCM_ = inst; refInstKit_.reset(); if (inst->getEnvelopeEnabled()) envItrADPCM_ = inst->getEnvelopeSequenceIterator(); else envItrADPCM_.reset(); if (!hasArpEffADPCM_) { if (inst->getArpeggioEnabled()) arpItrADPCM_ = inst->getArpeggioSequenceIterator(); else arpItrADPCM_.reset(); } if (inst->getPitchEnabled()) ptItrADPCM_ = inst->getPitchSequenceIterator(); else ptItrADPCM_.reset(); } void OPNAController::updateInstrumentADPCM(int instNum) { if (refInstADPCM_ && refInstADPCM_->isRegisteredWithManager() && refInstADPCM_->getNumber() == instNum) { if (!refInstADPCM_->getEnvelopeEnabled()) envItrADPCM_.reset(); if (!refInstADPCM_->getArpeggioEnabled()) arpItrADPCM_.reset(); if (!refInstADPCM_->getPitchEnabled()) ptItrADPCM_.reset(); } } /// NOTE: inst != nullptr void OPNAController::setInstrumentDrumkit(std::shared_ptr inst) { refInstKit_ = inst; refInstADPCM_.reset(); envItrADPCM_.reset(); arpItrADPCM_.reset(); ptItrADPCM_.reset(); } void OPNAController::updateInstrumentDrumkit(int instNum, int key) { (void)instNum; (void)key; } void OPNAController::clearSamplesADPCM() { storePointADPCM_ = 0; startAddrADPCM_ = std::numeric_limits::max(); stopAddrADPCM_ = startAddrADPCM_; } bool OPNAController::storeSampleADPCM(const std::vector& sample, size_t& startAddr, size_t& stopAddr) { opna_->setRegister(0x110, 0x80); opna_->setRegister(0x100, 0x61); opna_->setRegister(0x100, 0x60); opna_->setRegister(0x101, 0x02); size_t dramLim = (opna_->getDRAMSize() - 1) >> 5; // By 32 bytes opna_->setRegister(0x10c, dramLim & 0xff); opna_->setRegister(0x10d, (dramLim >> 8) & 0xff); bool stored = false; if (storePointADPCM_ < dramLim) { startAddr = storePointADPCM_; opna_->setRegister(0x102, startAddr & 0xff); opna_->setRegister(0x103, (startAddr >> 8) & 0xff); stopAddr = startAddr + ((sample.size() - 1) >> 5); // By 32 bytes stopAddr = std::min(stopAddr, dramLim); opna_->setRegister(0x104, stopAddr & 0xff); opna_->setRegister(0x105, (stopAddr >> 8) & 0xff); storePointADPCM_ = stopAddr + 1; size_t size = sample.size(); for (size_t i = 0; i < size; ++i) { opna_->setRegister(0x108, sample[i]); } stored = true; } opna_->setRegister(0x100, 0x00); opna_->setRegister(0x110, 0x80); return stored; } /********** Set volume **********/ void OPNAController::setVolumeADPCM(int volume) { if (volume < bt_defs::NSTEP_ADPCM_VOLUME) { baseVolADPCM_ = volume; oneshotVolADPCM_ = UNUSED_VALUE; if (isKeyOnADPCM_) setRealVolumeADPCM(); } } void OPNAController::setOneshotVolumeADPCM(int volume) { if (volume < bt_defs::NSTEP_ADPCM_VOLUME) { oneshotVolADPCM_ = volume; if (isKeyOnADPCM_) setRealVolumeADPCM(); } } void OPNAController::setRealVolumeADPCM() { int volume = (oneshotVolADPCM_ == UNUSED_VALUE) ? baseVolADPCM_ : oneshotVolADPCM_; if (envItrADPCM_) { int type = envItrADPCM_->data().data; if (type >= 0) volume -= (bt_defs::NSTEP_ADPCM_VOLUME - 1 - type); } if (treItrADPCM_) volume += treItrADPCM_->data().data; volume += volSldSumADPCM_; volume = utils::clamp(volume, 0, bt_defs::NSTEP_ADPCM_VOLUME - 1); opna_->setRegister(0x10b, static_cast(volume)); shouldWriteEnvADPCM_ = false; } /********** Set effect **********/ void OPNAController::setPanADPCM(int value) { panStateADPCM_ = static_cast(value << 6); opna_->setRegister(0x101, panStateADPCM_ | 0x02); } void OPNAController::setArpeggioEffectADPCM(int second, int third) { if (refInstKit_) return; if (second || third) { arpItrADPCM_ = std::make_unique(second, third); hasArpEffADPCM_ = true; } else { if (!refInstADPCM_ || !refInstADPCM_->getArpeggioEnabled()) arpItrADPCM_.reset(); else arpItrADPCM_ = refInstADPCM_->getArpeggioSequenceIterator(); hasArpEffADPCM_ = false; } } void OPNAController::setPortamentoEffectADPCM(int depth, bool isTonePortamento) { if (refInstKit_) return; prtmDepthADPCM_ = depth; hasTonePrtmADPCM_ = depth ? isTonePortamento : false; } void OPNAController::setVibratoEffectADPCM(int period, int depth) { if (refInstKit_) return; if (period && depth) vibItrADPCM_ = std::make_unique(period, depth); else vibItrADPCM_.reset(); } void OPNAController::setTremoloEffectADPCM(int period, int depth) { if (period && depth) treItrADPCM_ = std::make_unique(period, depth); else treItrADPCM_.reset(); } void OPNAController::setVolumeSlideADPCM(int depth, bool isUp) { volSldADPCM_ = isUp ? depth : -depth; } void OPNAController::setDetuneADPCM(int pitch) { if (refInstKit_) return; detuneADPCM_ = pitch; shouldSetToneADPCM_ = true; } void OPNAController::setFineDetuneADPCM(int pitch) { if (refInstKit_) return; fdetuneADPCM_ = pitch; shouldSetToneADPCM_ = true; } void OPNAController::setNoteSlideADPCM(int speed, int seminote) { if (refInstKit_) return; if (seminote) { nsItADPCM_ = std::make_unique(speed, seminote); hasSetNsADPCM_ = true; } else nsItADPCM_.reset(); } void OPNAController::setTransposeEffectADPCM(int seminote) { transposeADPCM_ += (seminote * Note::SEMINOTE_PITCH); shouldSetToneADPCM_ = true; } /********** For state retrieve **********/ void OPNAController::haltSequencesADPCM() { if (treItrADPCM_) treItrADPCM_->end(); if (envItrADPCM_) envItrADPCM_->end(); if (arpItrADPCM_) arpItrADPCM_->end(); if (ptItrADPCM_) ptItrADPCM_->end(); if (vibItrADPCM_) vibItrADPCM_->end(); if (nsItADPCM_) nsItADPCM_->end(); } /********** Chip details **********/ bool OPNAController::isKeyOnADPCM() const { return isKeyOnADPCM_; } bool OPNAController::isTonePortamentoADPCM() const { return hasTonePrtmADPCM_; } Note OPNAController::getADPCMLatestNote() const { return echoBufADPCM_.latest(); } size_t OPNAController::getADPCMStoredSize() const { return storePointADPCM_ << 5; } /***********************************/ void OPNAController::initADPCM() { isKeyOnADPCM_ = false; hasKeyOnBeforeADPCM_ = false; refInstADPCM_.reset(); // Init envelope refInstKit_.reset(); echoBufADPCM_.clear(); neverSetBaseNoteADPCM_ = true; baseVolADPCM_ = bt_defs::NSTEP_ADPCM_VOLUME - 1; // Init volume oneshotVolADPCM_ = UNUSED_VALUE; panStateADPCM_ = 0xc0; shouldWriteEnvADPCM_ = false; shouldSetToneADPCM_ = false; startAddrADPCM_ = std::numeric_limits::max(); stopAddrADPCM_ = startAddrADPCM_; hasStartRequestedKit_ = false; // Init sequence shouldSkip1stTickExecADPCM_ = false; envItrADPCM_.reset(); arpItrADPCM_.reset(); ptItrADPCM_.reset(); ptSumADPCM_ = 0; // Effect hasArpEffADPCM_ = false; prtmDepthADPCM_ = 0; hasTonePrtmADPCM_ = false; vibItrADPCM_.reset(); treItrADPCM_.reset(); volSldADPCM_ = 0; volSldSumADPCM_ = 0; detuneADPCM_ = 0; fdetuneADPCM_ = 0; nsItADPCM_.reset(); nsSumADPCM_ = 0; hasSetNsADPCM_ = false; transposeADPCM_ = 0; opna_->setRegister(0x100, 0xa1); // Stop synthesis // Limit address size_t dramLim = (opna_->getDRAMSize() - 1) >> 5; // By 32 bytes opna_->setRegister(0x10c, dramLim & 0xff); opna_->setRegister(0x10d, (dramLim >> 8) & 0xff); } void OPNAController::setMuteADPCMState(bool isMute) { isMuteADPCM_ = isMute; if (isMute) { opna_->setRegister(0x10b, 0); isKeyOnADPCM_ = false; } } bool OPNAController::isMuteADPCM() { return isMuteADPCM_; } void OPNAController::setFrontADPCMSequences() { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; if (treItrADPCM_) { treItrADPCM_->front(); shouldWriteEnvADPCM_ = true; } if (volSldADPCM_) { volSldSumADPCM_ += volSldADPCM_; shouldWriteEnvADPCM_ = true; } if (envItrADPCM_) { envItrADPCM_->front(); writeEnvelopeADPCMToRegister(); } else setRealVolumeADPCM(); if (arpItrADPCM_) { arpItrADPCM_->front(); checkRealToneADPCMByArpeggio(); } checkPortamentoADPCM(); if (ptItrADPCM_) { ptItrADPCM_->front(); checkRealToneADPCMByPitch(); } if (vibItrADPCM_) { vibItrADPCM_->front(); /* shouldSetToneADPCM_ = true; */ } if (nsItADPCM_) { nsItADPCM_->front(); if (!nsItADPCM_->hasEnded()) { nsSumADPCM_ += nsItADPCM_->data().data; /* shouldSetToneADPCM_ = true; */ } } writePitchADPCM(); } void OPNAController::releaseStartADPCMSequences() { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; if (treItrADPCM_) { treItrADPCM_->release(); shouldWriteEnvADPCM_ = true; } if (volSldADPCM_) { volSldSumADPCM_ += volSldADPCM_; shouldWriteEnvADPCM_ = true; } if (envItrADPCM_) { envItrADPCM_->release(); if (!envItrADPCM_->hasEnded()) { opna_->setRegister(0x10b, 0); // Silence shouldWriteEnvADPCM_ = false; } else writeEnvelopeADPCMToRegister(); } else { opna_->setRegister(0x10b, 0); // Silence shouldWriteEnvADPCM_ = false; } if (arpItrADPCM_) { arpItrADPCM_->release(); checkRealToneADPCMByArpeggio(); } checkPortamentoADPCM(); if (ptItrADPCM_) { ptItrADPCM_->release(); checkRealToneADPCMByPitch(); } if (vibItrADPCM_) { vibItrADPCM_->release(); shouldSetToneADPCM_ = true; } if (nsItADPCM_) { nsItADPCM_->release(); if (!nsItADPCM_->hasEnded()) { nsSumADPCM_ += nsItADPCM_->data().data; shouldSetToneADPCM_ = true; } } if (shouldSetToneADPCM_) writePitchADPCM(); hasStartRequestedKit_ = false; // Always silent at relase in drumkit } void OPNAController::tickEventADPCM() { if (shouldSkip1stTickExecADPCM_) { shouldSkip1stTickExecADPCM_ = false; } else { if (isMuteADPCM_ || (!refInstADPCM_ && !refInstKit_)) return; if (treItrADPCM_) { treItrADPCM_->next(); shouldWriteEnvADPCM_ = true; } if (volSldADPCM_) { volSldSumADPCM_ += volSldADPCM_; shouldWriteEnvADPCM_ = true; } if (envItrADPCM_) { envItrADPCM_->next(); writeEnvelopeADPCMToRegister(); } else if (shouldWriteEnvADPCM_) { setRealVolumeADPCM(); } if (arpItrADPCM_) { arpItrADPCM_->next(); checkRealToneADPCMByArpeggio(); } checkPortamentoADPCM(); if (ptItrADPCM_) { ptItrADPCM_->next(); checkRealToneADPCMByPitch(); } if (vibItrADPCM_) { vibItrADPCM_->next(); shouldSetToneADPCM_ = true; } if (nsItADPCM_) { nsItADPCM_->next(); if (!nsItADPCM_->hasEnded()) { nsSumADPCM_ += nsItADPCM_->data().data; shouldSetToneADPCM_ = true; } } if (shouldSetToneADPCM_) writePitchADPCM(); if (hasStartRequestedKit_) { opna_->setRegister(0x101, 0x02); opna_->setRegister(0x100, 0xa1); int key = baseNoteADPCM_.getNoteNumber(); triggerSamplePlayADPCM(refInstKit_->getSampleStartAddress(key), refInstKit_->getSampleStopAddress(key), refInstKit_->isSampleRepeatable(key)); hasStartRequestedKit_ = false; } } } void OPNAController::writeEnvelopeADPCMToRegister() { if (!envItrADPCM_->hasEnded() || shouldWriteEnvADPCM_) { setRealVolumeADPCM(); } } void OPNAController::writePitchADPCM() { if (neverSetBaseNoteADPCM_) return; if (refInstADPCM_) { int p = (baseNoteADPCM_ + (ptSumADPCM_ + (vibItrADPCM_ ? vibItrADPCM_->data().data : 0) + detuneADPCM_ + nsSumADPCM_ + transposeADPCM_)).getAbsolutePicth(); int diff = p - Note::SEMINOTE_PITCH * refInstADPCM_->getSampleRootKeyNumber(); writePitchADPCMToRegister(diff, refInstADPCM_->getSampleRootDeltaN()); } else if (refInstKit_) { int key = utils::clamp(baseNoteADPCM_.getNoteNumber() + transposeADPCM_ / Note::SEMINOTE_PITCH, 0, Note::NOTE_NUMBER_RANGE - 1); if (refInstKit_->getSampleEnabled(key)) { int diff = Note::SEMINOTE_PITCH * refInstKit_->getPitch(key); writePitchADPCMToRegister(diff, refInstKit_->getSampleRootDeltaN(key)); hasStartRequestedKit_ = true; } } shouldSetToneADPCM_ = false; } void OPNAController::writePitchADPCMToRegister(int pitchDiff, int rtDeltaN) { int deltan = static_cast(std::round(rtDeltaN * std::pow(2., pitchDiff / 384.))) + fdetuneADPCM_; opna_->setRegister(0x109, deltan & 0xff); opna_->setRegister(0x10a, (deltan >> 8) & 0xff); } void OPNAController::triggerSamplePlayADPCM(size_t startAddress, size_t stopAddress, bool repeatable) { uint8_t repeatFlag = repeatable ? 0x10 : 0; opna_->setRegister(0x100, 0x21 | repeatFlag); if (startAddress != startAddrADPCM_) { opna_->setRegister(0x102, startAddress & 0xff); opna_->setRegister(0x103, (startAddress >> 8) & 0xff); startAddrADPCM_ = startAddress; } if (stopAddress != stopAddrADPCM_) { opna_->setRegister(0x104, stopAddress & 0xff); opna_->setRegister(0x105, (stopAddress >> 8) & 0xff); stopAddrADPCM_ = stopAddress; } opna_->setRegister(0x100, 0xa0 | repeatFlag); opna_->setRegister(0x101, panStateADPCM_ | 0x02); } BambooTracker-0.4.6/BambooTracker/opna_controller.hpp000066400000000000000000000403331401124043500226550ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include "song.hpp" #include "instrument.hpp" #include "effect_iterator.hpp" #include "note.hpp" #include "echo_buffer.hpp" #include "chip/opna.hpp" #include "enum_hash.hpp" #include "bamboo_tracker_defs.hpp" class OPNAController { public: OPNAController(chip::OpnaEmulator emu, int clock, int rate, int duration); // Reset and initialize void reset(); // Forward instrument sequence void tickEvent(SoundSource src, int ch); // Direct register set void sendRegister(int address, int value); // DRAM size_t getDRAMSize() const; // Update register states after tick process void updateRegisterStates(); // Real chip interface void useSCCI(scci::SoundInterfaceManager* manager); bool isUsedSCCI() const; void useC86CTL(C86ctlBase* base); bool isUsedC86CTL() const; // Stream samples void getStreamSamples(int16_t* container, size_t nSamples); void getOutputHistory(int16_t* history); // Chip mode void setMode(SongType mode); SongType getMode() const noexcept; // Mute void setMuteState(SoundSource src, int chInSrc, bool isMute); bool isMute(SoundSource src, int chInSrc); // Stream details int getRate() const; void setRate(int rate); int getDuration() const; void setDuration(int duration); void setMasterVolume(int percentage); // Export void setExportContainer(std::shared_ptr cntr = nullptr); private: std::unique_ptr opna_; SongType mode_; std::vector> registerSetBuf_; void resetState(); std::unique_ptr outputHistory_; size_t outputHistoryIndex_; std::unique_ptr outputHistoryReady_; std::mutex outputHistoryReadyMutex_; void fillOutputHistory(const int16_t* outputs, size_t nSamples); using ArpeggioIterInterface = std::unique_ptr>; void checkRealToneByArpeggio(const ArpeggioIterInterface& arpItr, const EchoBuffer& echoBuf, Note& baseNote, bool& shouldSetTone); void checkPortamento(const ArpeggioIterInterface& arpItr, int prtm, bool hasKeyOnBefore, bool isTonePrtm, EchoBuffer& echoBuf, Note& baseNote, bool& shouldSetTone); void checkRealToneByPitch(const std::unique_ptr::Iterator>& ptItr, int& sumPitch, bool& shouldSetTone); /*----- FM -----*/ public: // Key on-off void keyOnFM(int ch, const Note& note, bool isJam = false); void keyOnFM(int ch, int echoBuf); void keyOffFM(int ch, bool isJam = false); // Set instrument void setInstrumentFM(int ch, std::shared_ptr inst); void updateInstrumentFM(int instNum); void updateInstrumentFMEnvelopeParameter(int envNum, FMEnvelopeParameter param); void setInstrumentFMOperatorEnabled(int envNum, int opNum); void updateInstrumentFMLFOParameter(int lfoNum, FMLFOParameter param); void resetFMChannelEnvelope(int ch); void restoreFMEnvelopeFromReset(int ch); // Set volume void setVolumeFM(int ch, int volume); void setOneshotVolumeFM(int ch, int volume); void setMasterVolumeFM(double dB); // Set effect void setPanFM(int ch, int value); void setArpeggioEffectFM(int ch, int second, int third); void setPortamentoEffectFM(int ch, int depth, bool isTonePortamento = false); void setVibratoEffectFM(int ch, int period, int depth); void setTremoloEffectFM(int ch, int period, int depth); void setVolumeSlideFM(int ch, int depth, bool isUp); void setDetuneFM(int ch, int pitch); void setFineDetuneFM(int ch, int pitch); void setNoteSlideFM(int ch, int speed, int seminote); void setTransposeEffectFM(int ch, int seminote); void setFBControlFM(int ch, int value); void setTLControlFM(int ch, int op, int value); void setMLControlFM(int ch, int op, int value); void setARControlFM(int ch, int op, int value); void setDRControlFM(int ch, int op, int value); void setRRControlFM(int ch, int op, int value); void setBrightnessFM(int ch, int value); // For state retrieve void haltSequencesFM(int ch); // Chip details bool isKeyOnFM(int ch) const; bool isTonePortamentoFM(int ch) const; bool enableFMEnvelopeReset(int ch) const; Note getFMLatestNote(int ch) const; private: // Seperate FM 3ch in FM3chEx mode struct FMChannel { size_t ch, inCh; bool isKeyOn, hasKeyOnBefore; EchoBuffer echoBuf; bool neverSetBaseNote; Note baseNote; int baseVol, oneshotVol; bool isMute; bool isEnabledEnvReset, hasResetEnv; bool hasPreSetTickEvent; bool shouldSetTone; ArpeggioIterInterface arpItr; PitchIter ptItr; int ptSum; bool isArpEff; int prtmDepth; bool isTonePrtm; std::unique_ptr vibItr; std::unique_ptr treItr; int volSld, volSldSum; int detune, fdetune; std::unique_ptr nsItr; int nsSum; bool hasSetNs; int transpose; FMOperatorType opType; }; FMChannel fm_[9]; // Share FM 3ch in FM3chEx mode std::shared_ptr refInstFM_[6]; std::unique_ptr envFM_[6]; uint8_t fmOpEnables_[6]; /// bit0: right on/off /// bit1: left on/off uint8_t panFM_[6]; int lfoFreq_; int lfoStartCntFM_[6]; std::unordered_map opSeqItFM_[6]; bool isFBCtrlFM_[6], isTLCtrlFM_[6][4], isMLCtrlFM_[6][4], isARCtrlFM_[6][4]; bool isDRCtrlFM_[6][4], isRRCtrlFM_[6][4]; bool isBrightFM_[6][4]; void initFM(); void setMuteFMState(int ch, bool isMuteFM); bool isMuteFM(int ch); uint32_t getFMChannelOffset(int ch, bool forPitch = false) const; void updateFMVolume(FMChannel& fm); void writeFMEnvelopeToRegistersFromInstrument(size_t inch); void writeFMEnveropeParameterToRegister(size_t inch, FMEnvelopeParameter param, int value); void writeFMLFOAllRegisters(size_t inch); void writeFMLFORegister(size_t inch, FMLFOParameter param); void checkLFOUsedByInstrument(); void setFrontFMSequences(FMChannel& fm); void releaseStartFMSequences(FMChannel& fm); void tickEventFM(FMChannel& fm); void checkOperatorSequenceFM(FMChannel& fm, int type); void checkVolumeEffectFM(FMChannel& fm); void checkRealToneFMByArpeggio(FMChannel& fm); void checkPortamentoFM(FMChannel& fm); void checkRealToneFMByPitch(FMChannel& fm); void writePitchFM(FMChannel& fm); void setInstrumentFMProperties(FMChannel& fm); uint8_t getFM3SlotValidStatus() const; uint8_t calculateTL(int ch, uint8_t data) const; /*----- SSG -----*/ public: // Key on-off void keyOnSSG(int ch, const Note& note, bool isJam = false); void keyOnSSG(int ch, int echoBuf); void keyOffSSG(int ch, bool isJam = false); // Set instrument void setInstrumentSSG(int ch, std::shared_ptr inst); void updateInstrumentSSG(int instNum); // Set volume void setVolumeSSG(int ch, int volume); void setOneshotVolumeSSG(int ch, int volume); void setMasterVolumeSSG(double dB); // Set effect void setArpeggioEffectSSG(int ch, int second, int third); void setPortamentoEffectSSG(int ch, int depth, bool isTonePortamento = false); void setVibratoEffectSSG(int ch, int period, int depth); void setTremoloEffectSSG(int ch, int period, int depth); void setVolumeSlideSSG(int ch, int depth, bool isUp); void setDetuneSSG(int ch, int pitch); void setFineDetuneSSG(int ch, int pitch); void setNoteSlideSSG(int ch, int speed, int seminote); void setTransposeEffectSSG(int ch, int seminote); void setToneNoiseMixSSG(int ch, int value); void setNoisePitchSSG(int ch, int pitch); void setHardEnvelopePeriod(int ch, bool high, int period); void setAutoEnvelopeSSG(int ch, int shift, int shape); void haltSequencesSSG(int ch); // Chip details bool isKeyOnSSG(int ch) const; bool isTonePortamentoSSG(int ch) const; Note getSSGLatestNote(int ch) const; private: struct SSGChannel { size_t ch; std::shared_ptr refInst; bool isKeyOn, hasKeyOnBefore, isInKeyOnProcess_; EchoBuffer echoBuf; bool neverSetBaseNote; Note baseNote; int baseVol, oneshotVol; bool isMute; bool shouldSkip1stTickExec; bool shouldSetEnv; bool shouldSetSqMaskFreq; bool shouldSetHardEnvFreq; bool shouldUpdateMixState; bool shouldSetTone; SSGWaveformIter wfItr; SSGWaveformUnit wfChState; SSGEnvelopeIter envItr; SSGEnvelopeUnit envState; bool isHardEnv; SSGToneNoiseIter tnItr; bool hasRequestedTnEffSet; ArpeggioIterInterface arpItr; PitchIter ptItr; int ptSum; bool isArpEff; int prtmDepth; bool isTonePrtm; std::unique_ptr vibItr; std::unique_ptr treItr; int volSld, volSldSum; int detune, fdetune; std::unique_ptr nsItr; int nsSum; bool hasSetNs; int transpose; } ssg_[3]; /// Flag "on" is key "off" uint8_t mixerSSG_; uint8_t noisePeriodSSG_; int hardEnvPeriodHighSSG_, hardEnvPeriodLowSSG_; void initSSG(); void setMuteSSGState(int ch, bool isMuteFM); bool isMuteSSG(int ch); void setFrontSSGSequences(SSGChannel& ssg); void releaseStartSSGSequences(SSGChannel& ssg); void tickEventSSG(SSGChannel& ssg); void writeWaveformSSGToRegister(SSGChannel& ssg); void writeSquareWaveform(SSGChannel& ssg); void writeEnvelopeSSGToRegister(SSGChannel& ssg); void setRealVolumeSSG(SSGChannel& ssg); void writeMixerSSGToRegisterByEffect(SSGChannel& ssg); void writeMixerSSGToRegisterBySequence(SSGChannel& ssg); void writeMixerSSGToRegisterByNoReference(SSGChannel& ssg); void checkRealToneSSGByArpeggio(SSGChannel& ssg); void checkPortamentoSSG(SSGChannel& ssg); void checkRealToneSSGByPitch(SSGChannel& ssg); void writePitchSSG(SSGChannel& ssg); void writeAutoEnvelopePitchSSG(SSGChannel& ssg, double tonePitch); void writeSquareMaskPitchSSG(SSGChannel& ssg, double tonePitch, bool isTriangle); /*----- Rhythm -----*/ public: // Key on/off void setKeyOnFlagRhythm(int ch); void setKeyOffFlagRhythm(int ch); // Set volume void setVolumeRhythm(int ch, int volume); void setOneshotVolumeRhythm(int ch, int volume); void setMasterVolumeRhythm(int volume); // Set effect void setPanRhythm(int ch, int value); private: struct RhythmChannel { int baseVol, oneshotVol; /// bit0: right on/off /// bit1: left on/off uint8_t panState; bool isMute; } rhythm_[6]; uint8_t keyOnRequestFlagsRhythm_, keyOffRequestFlagsRhythm_; int masterVolRhythm_; void initRhythm(); void setMuteRhythmState(int ch, bool isMute); bool isMuteRhythm(int ch); void updateKeyOnOffStatusRhythm(); /*----- ADPCM/Drumkit -----*/ public: // Key on-off void keyOnADPCM(const Note& note, bool isJam = false); void keyOnADPCM(int echoBuf); void keyOffADPCM(bool isJam = false); // Set instrument void setInstrumentADPCM(std::shared_ptr inst); void updateInstrumentADPCM(int instNum); void setInstrumentDrumkit(std::shared_ptr inst); void updateInstrumentDrumkit(int instNum, int key); void clearSamplesADPCM(); /// [Return] true if sample assignment is success bool storeSampleADPCM(const std::vector& sample, size_t& startAddr, size_t& stopAddr); // Set volume void setVolumeADPCM(int volume); void setOneshotVolumeADPCM(int volume); // Set effect void setPanADPCM(int value); void setArpeggioEffectADPCM(int second, int third); void setPortamentoEffectADPCM(int depth, bool isTonePortamento = false); void setVibratoEffectADPCM(int period, int depth); void setTremoloEffectADPCM(int period, int depth); void setVolumeSlideADPCM(int depth, bool isUp); void setDetuneADPCM(int pitch); void setFineDetuneADPCM(int pitch); void setNoteSlideADPCM(int speed, int seminote); void setTransposeEffectADPCM(int seminote); // For state retrieve void haltSequencesADPCM(); // Chip details bool isKeyOnADPCM() const; bool isTonePortamentoADPCM() const; Note getADPCMLatestNote() const; size_t getADPCMStoredSize() const; private: std::shared_ptr refInstADPCM_; std::shared_ptr refInstKit_; bool isKeyOnADPCM_, hasKeyOnBeforeADPCM_; EchoBuffer echoBufADPCM_; bool neverSetBaseNoteADPCM_; Note baseNoteADPCM_; int baseVolADPCM_, oneshotVolADPCM_; uint8_t panStateADPCM_; bool isMuteADPCM_; bool shouldSkip1stTickExecADPCM_; // Use to execute key on/off process in jamming bool shouldWriteEnvADPCM_; bool shouldSetToneADPCM_; size_t startAddrADPCM_, stopAddrADPCM_; // By 32 bytes size_t storePointADPCM_; // Move by 32 bytes ADPCMEnvelopeIter envItrADPCM_; ArpeggioIterInterface arpItrADPCM_; PitchIter ptItrADPCM_; int ptSumADPCM_; bool hasArpEffADPCM_; int prtmDepthADPCM_; bool hasTonePrtmADPCM_; std::unique_ptr vibItrADPCM_; std::unique_ptr treItrADPCM_; int volSldADPCM_, volSldSumADPCM_; int detuneADPCM_, fdetuneADPCM_; std::unique_ptr nsItADPCM_; int nsSumADPCM_; bool hasSetNsADPCM_; int transposeADPCM_; bool hasStartRequestedKit_; void initADPCM(); void setMuteADPCMState(bool isMuteFM); bool isMuteADPCM(); void setFrontADPCMSequences(); void releaseStartADPCMSequences(); void tickEventADPCM(); void writeEnvelopeADPCMToRegister(); void checkRealToneADPCMByArpeggio(); void checkPortamentoADPCM(); void checkRealToneADPCMByPitch(); void writePitchADPCM(); void writePitchADPCMToRegister(int pitchDiff, int rtDeltaN); void setRealVolumeADPCM(); void triggerSamplePlayADPCM(size_t startAddress, size_t stopAddress, bool repeatable); }; //----------------------------------------------------------------------------- inline SongType OPNAController::getMode() const noexcept { return mode_; } /*----- FM -----*/ inline void OPNAController::checkRealToneFMByArpeggio(FMChannel& fm) { checkRealToneByArpeggio(fm.arpItr, fm.echoBuf, fm.baseNote, fm.shouldSetTone); } inline void OPNAController::checkPortamentoFM(FMChannel& fm) { checkPortamento(fm.arpItr, fm.prtmDepth, fm.hasKeyOnBefore, fm.isTonePrtm, fm.echoBuf, fm.baseNote, fm.shouldSetTone); } inline void OPNAController::checkRealToneFMByPitch(FMChannel& fm) { checkRealToneByPitch(fm.ptItr, fm.ptSum, fm.shouldSetTone); } inline uint8_t OPNAController::getFM3SlotValidStatus() const { return fmOpEnables_[2] & (static_cast(fm_[2].isKeyOn) | (static_cast(fm_[6].isKeyOn) << 1) | (static_cast(fm_[7].isKeyOn) << 2) | (static_cast(fm_[8].isKeyOn) << 3)); } /*----- SSG -----*/ inline void OPNAController::checkRealToneSSGByArpeggio(OPNAController::SSGChannel& ssg) { checkRealToneByArpeggio(ssg.arpItr, ssg.echoBuf, ssg.baseNote, ssg.shouldSetTone); } inline void OPNAController::checkPortamentoSSG(OPNAController::SSGChannel& ssg) { checkPortamento(ssg.arpItr, ssg.prtmDepth, ssg.hasKeyOnBefore, ssg.isTonePrtm, ssg.echoBuf, ssg.baseNote, ssg.shouldSetTone); } inline void OPNAController::checkRealToneSSGByPitch(OPNAController::SSGChannel& ssg) { checkRealToneByPitch(ssg.ptItr, ssg.ptSum, ssg.shouldSetTone); } /*----- ADPCM/Drumkit -----*/ inline void OPNAController::checkRealToneADPCMByArpeggio() { checkRealToneByArpeggio(arpItrADPCM_, echoBufADPCM_, baseNoteADPCM_, shouldSetToneADPCM_); } inline void OPNAController::checkPortamentoADPCM() { checkPortamento(arpItrADPCM_, prtmDepthADPCM_, hasKeyOnBeforeADPCM_, hasTonePrtmADPCM_, echoBufADPCM_, baseNoteADPCM_, shouldSetToneADPCM_); } inline void OPNAController::checkRealToneADPCMByPitch() { checkRealToneByPitch(ptItrADPCM_, ptSumADPCM_, shouldSetToneADPCM_); } BambooTracker-0.4.6/BambooTracker/playback.cpp000066400000000000000000001745461401124043500212540ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "playback.hpp" #include #include "opna_controller.hpp" #include "instruments_manager.hpp" #include "tick_counter.hpp" #include "note.hpp" #include "utils.hpp" EffectMemory::EffectMemory() { mem_.reserve(Step::N_EFFECT); } void EffectMemory::enqueue(const Effect& eff) { auto itr = utils::findIf(mem_, [type = eff.type](const Effect& a) { return a.type == type; }); if (itr != mem_.end()) mem_.erase(itr); mem_.push_back(eff); } void EffectMemory::clear() { mem_.clear(); } //----------------------------------------------- namespace { namespace PlayStateFlag { enum : uint8_t { Clear = 0, // Read state Playing = 1 << 0, ReadFirstStep = 1 << 1, // Play type LoopPattern = 1 << 2, PlayStep = 1 << 3 }; } } PlaybackManager::PlaybackManager(std::shared_ptr opnaCtrl, std::weak_ptr instMan, std::weak_ptr tickCounter, std::weak_ptr mod, bool isRetrieveChannel) : opnaCtrl_(opnaCtrl), instMan_(instMan), tickCounter_(tickCounter), mod_(mod), curSongNum_(0), playingPos_(Position::INVALID, Position::INVALID), nextReadPos_(Position::INVALID, Position::INVALID), isFindNextStep_(false), isRetrieveChannel_(isRetrieveChannel) { songStyle_ = mod.lock()->getSong(curSongNum_).getStyle(); clearEffectMaps(); clearNoteDelayCounts(); clearDelayBeyondStepCounts(); } void PlaybackManager::setSong(std::weak_ptr mod, int songNum) { mod_ = mod; curSongNum_ = songNum; songStyle_ = mod_.lock()->getSong(curSongNum_).getStyle(); /* opna mode is changed in BambooTracker class */ size_t fmch = Song::getFMChannelCount(songStyle_.type); isNoteDelay_[SoundSource::FM] = std::vector(fmch); effOnKeyOnMem_[SoundSource::FM] = std::vector(fmch); effOnStepBeginMem_[SoundSource::FM] = std::vector(fmch); directRegisterSets_[SoundSource::FM] = DirectRegisterSetSource(fmch); ntDlyCntFM_ = std::vector(fmch); ntCutDlyCntFM_ = std::vector(fmch); volDlyCntFM_ = std::vector(fmch); volDlyValueFM_ = std::vector(fmch, -1); tposeDlyCntFM_ = std::vector(fmch); tposeDlyValueFM_ = std::vector(fmch); isNoteDelay_[SoundSource::SSG] = std::vector(3); effOnKeyOnMem_[SoundSource::SSG] = std::vector(3); effOnStepBeginMem_[SoundSource::SSG] = std::vector(3); directRegisterSets_[SoundSource::SSG] = DirectRegisterSetSource(3); ntDlyCntSSG_ = std::vector(3); ntCutDlyCntSSG_ = std::vector(3); volDlyCntSSG_ = std::vector(3); volDlyValueSSG_ = std::vector(3, -1); tposeDlyCntSSG_ = std::vector(3); tposeDlyValueSSG_ = std::vector(3); isNoteDelay_[SoundSource::RHYTHM] = std::vector(6); effOnKeyOnMem_[SoundSource::RHYTHM] = std::vector(6); effOnStepBeginMem_[SoundSource::RHYTHM] = std::vector(6); directRegisterSets_[SoundSource::RHYTHM] = DirectRegisterSetSource(6); ntDlyCntRhythm_ = std::vector(6); ntCutDlyCntRhythm_ = std::vector(6); volDlyCntRhythm_ = std::vector(6); volDlyValueRhythm_ = std::vector(6, -1); isNoteDelay_[SoundSource::ADPCM] = std::vector(1); effOnKeyOnMem_[SoundSource::ADPCM] = std::vector(1); effOnStepBeginMem_[SoundSource::ADPCM] = std::vector(1); directRegisterSets_[SoundSource::ADPCM] = DirectRegisterSetSource(1); ntDlyCntADPCM_ = 0; ntCutDlyCntADPCM_ = 0; volDlyCntADPCM_ = 0; volDlyValueADPCM_ = -1; } /********** Play song **********/ void PlaybackManager::startPlaySong(int order) { std::lock_guard lock(mutex_); startPlay(); playStateFlags_ = PlayStateFlag::Playing; playingPos_.set(order, 0); findNextStep(); if (isRetrieveChannel_) retrieveChannelStates(); } void PlaybackManager::startPlayFromStart() { std::lock_guard lock(mutex_); startPlay(); playStateFlags_ = PlayStateFlag::Playing; playingPos_.set(0, 0); findNextStep(); } void PlaybackManager::startPlayPattern(int order) { std::lock_guard lock(mutex_); startPlay(); playStateFlags_ = PlayStateFlag::Playing | PlayStateFlag::LoopPattern; playingPos_.set(order, 0); findNextStep(); if (isRetrieveChannel_) retrieveChannelStates(); } void PlaybackManager::startPlayFromPosition(int order, int step) { std::lock_guard lock(mutex_); startPlay(); playStateFlags_ = PlayStateFlag::Playing; playingPos_.set(order, step); findNextStep(); if (isRetrieveChannel_) retrieveChannelStates(); } void PlaybackManager::playStep(int order, int step) { std::lock_guard lock(mutex_); bool isPlaying = isPlayingStep(); if (!isPlaying) { opnaCtrl_->reset(); Song& song = mod_.lock()->getSong(curSongNum_); tickCounter_.lock()->setTempo(song.getTempo()); tickCounter_.lock()->setSpeed(song.getSpeed()); tickCounter_.lock()->setGroove(mod_.lock()->getGroove(song.getGroove())); tickCounter_.lock()->setGrooveState(song.isUsedTempo() ? GrooveState::Invalid : GrooveState::ValidByGlobal); } tickCounter_.lock()->resetCount(); tickCounter_.lock()->setPlayState(true); clearEffectMaps(); clearNoteDelayCounts(); clearDelayBeyondStepCounts(); playStateFlags_ = PlayStateFlag::PlayStep; playingPos_.set(order, step); if (!isPlaying && isRetrieveChannel_) retrieveChannelStates(); } void PlaybackManager::startPlay() { opnaCtrl_->reset(); Song& song = mod_.lock()->getSong(curSongNum_); tickCounter_.lock()->setTempo(song.getTempo()); tickCounter_.lock()->setSpeed(song.getSpeed()); tickCounter_.lock()->setGroove(mod_.lock()->getGroove(song.getGroove())); tickCounter_.lock()->setGrooveState(song.isUsedTempo() ? GrooveState::Invalid : GrooveState::ValidByGlobal); tickCounter_.lock()->resetCount(); tickCounter_.lock()->setPlayState(true); clearEffectMaps(); clearNoteDelayCounts(); clearDelayBeyondStepCounts(); } void PlaybackManager::stopPlaySong() { std::lock_guard lock(mutex_); stopPlay(); } void PlaybackManager::stopPlay() { // No mutex to call from PlaybackManager::streamCountUp opnaCtrl_->reset(); tickCounter_.lock()->setPlayState(false); playStateFlags_ = PlayStateFlag::Clear; playingPos_.invalidate(); } bool PlaybackManager::isPlaySong() const noexcept { return playStateFlags_ & PlayStateFlag::Playing; } bool PlaybackManager::isPlayingStep() const noexcept { return playStateFlags_ & PlayStateFlag::PlayStep; } int PlaybackManager::getPlayingOrderNumber() const noexcept { return playingPos_.order; } int PlaybackManager::getPlayingStepNumber() const noexcept { return playingPos_.step; } /********** Stream events **********/ int PlaybackManager::streamCountUp() { std::lock_guard lock(mutex_); int state = tickCounter_.lock()->countUp(); if (state > 0) { // Tick process in playback checkValidPosition(); tickProcess(state); } else if (!state) { // Step process in playback checkValidPosition(); if (stepDown()) { stepProcess(); if (!isFindNextStep_) findNextStep(); } else if (!isPlayingStep()) { stopPlay(); } } else { // Stop playback for (auto& attrib : songStyle_.trackAttribs) { opnaCtrl_->tickEvent(attrib.source, attrib.channelInSource); } } return state; } bool PlaybackManager::stepDown() { if (playStateFlags_ & PlayStateFlag::ReadFirstStep) { // Foward current step if (isPlayingStep()) { return false; } else { if (!nextReadPos_.isValid()) { isFindNextStep_ = false; return false; } else { playingPos_ = nextReadPos_; } } } else { // First read playStateFlags_ |= PlayStateFlag::ReadFirstStep; } return true; } void PlaybackManager::findNextStep() { // Init nextReadPos_ = playingPos_; // Search int ptnSize = static_cast(getPatternSizeFromOrderNumber(curSongNum_, nextReadPos_.order)); if (!ptnSize || nextReadPos_.step >= ptnSize - 1) { if (!(playStateFlags_ & PlayStateFlag::LoopPattern)) { // Loop pattern if (nextReadPos_.order >= static_cast(getOrderSize(curSongNum_)) - 1) { nextReadPos_.order = 0; } else { ++nextReadPos_.order; } } nextReadPos_.step = 0; } else { ++nextReadPos_.step; } isFindNextStep_ = true; } void PlaybackManager::checkValidPosition() { auto& song = mod_.lock()->getSong(curSongNum_); int orderSize = static_cast(song.getOrderSize()); if (playingPos_.order >= orderSize) { playingPos_.set(0, 0); nextReadPos_ = playingPos_; } else if (playingPos_.step >= static_cast(song.getPatternSizeFromOrderNumber(playingPos_.order))) { if (playingPos_.order == orderSize - 1) { playingPos_.set(0, 0); nextReadPos_ = playingPos_; } else { ++playingPos_.order; playingPos_.step = 0; nextReadPos_ = playingPos_; } } } /// Register update order: volume -> instrument -> effect -> key on void PlaybackManager::stepProcess() { clearNoteDelayCounts(); updateDelayEventCounts(); auto& song = mod_.lock()->getSong(curSongNum_); // Store effects from the step to map for (auto& attrib : songStyle_.trackAttribs) { auto& step = song.getTrack(attrib.number) .getPatternFromOrderNumber(playingPos_.order).getStep(playingPos_.step); size_t uch = static_cast(attrib.channelInSource); effOnKeyOnMem_[attrib.source].at(uch).clear(); directRegisterSets_[attrib.source].at(uch).clear(); using storeFunc = bool (PlaybackManager::*)(int, const Effect&); static const std::unordered_map storeEffectToMap = { { SoundSource::FM, &PlaybackManager::storeEffectToMapFM }, { SoundSource::SSG, &PlaybackManager::storeEffectToMapSSG }, { SoundSource::RHYTHM, &PlaybackManager::storeEffectToMapRhythm }, { SoundSource::ADPCM, &PlaybackManager::storeEffectToMapADPCM } }; bool isDelay = false; for (int i = 0; i < Step::N_EFFECT; ++i) { Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); isDelay |= (this->*storeEffectToMap.at(attrib.source))(attrib.channelInSource, eff); } isNoteDelay_[attrib.source].at(uch) = isDelay; } // Execute step events bool isNextSet = executeStoredEffectsGlobal(); for (auto& attrib : songStyle_.trackAttribs) { auto& step = song.getTrack(attrib.number) .getPatternFromOrderNumber(playingPos_.order).getStep(playingPos_.step); switch (attrib.source) { case SoundSource::FM: if (isNoteDelay_[SoundSource::FM].at(attrib.channelInSource)) { // Set effect executeStoredEffectsFM(attrib.channelInSource); checkFMNoteDelayAndEnvelopeReset(step, attrib.channelInSource); opnaCtrl_->tickEvent(SoundSource::FM, attrib.channelInSource); } else { executeFMStepEvents(step, attrib.channelInSource); } break; case SoundSource::SSG: if (isNoteDelay_[SoundSource::SSG].at(attrib.channelInSource)) { // Set effect executeStoredEffectsSSG(attrib.channelInSource); opnaCtrl_->tickEvent(SoundSource::SSG, attrib.channelInSource); } else { executeSSGStepEvents(step, attrib.channelInSource); } break; case SoundSource::RHYTHM: if (isNoteDelay_[SoundSource::RHYTHM].at(attrib.channelInSource)) { // Set effect executeStoredEffectsRhythm(attrib.channelInSource); opnaCtrl_->tickEvent(SoundSource::RHYTHM, attrib.channelInSource); } else { executeRhythmStepEvents(step, attrib.channelInSource); } break; case SoundSource::ADPCM: if (isNoteDelay_[SoundSource::ADPCM].at(attrib.channelInSource)) { // Set effect executeStoredEffectsADPCM(); opnaCtrl_->tickEvent(SoundSource::ADPCM, attrib.channelInSource); } else { executeADPCMStepEvents(step); } break; } } opnaCtrl_->updateRegisterStates(); // Update for other changes isFindNextStep_ = isNextSet; } void PlaybackManager::executeFMStepEvents(const Step& step, int ch, bool calledByNoteDelay) { if (!calledByNoteDelay && !step.isEmptyNote()) clearFMDelayBeyondStepCounts(ch); // Except no key event // Set volume int vol = step.getVolume(); if (step.hasVolume() && vol < bt_defs::NSTEP_FM_VOLUME) { opnaCtrl_->setVolumeFM(ch, vol); } // Set instrument if (step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) opnaCtrl_->setInstrumentFM(ch, inst); } else { opnaCtrl_->restoreFMEnvelopeFromReset(ch); } // Set effect executeStoredEffectsFM(ch); // Set key int noteNum = step.getNoteNumber(); switch (noteNum) { case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkFMDelayEventsInTick(step, ch); opnaCtrl_->tickEvent(SoundSource::FM, ch); } break; case Step::NOTE_KEY_OFF: opnaCtrl_->keyOffFM(ch); break; case Step::NOTE_ECHO0: opnaCtrl_->keyOnFM(ch, 0); break; case Step::NOTE_ECHO1: opnaCtrl_->keyOnFM(ch, 1); break; case Step::NOTE_ECHO2: opnaCtrl_->keyOnFM(ch, 2); break; case Step::NOTE_ECHO3: opnaCtrl_->keyOnFM(ch, 3); break; default: // Key on opnaCtrl_->keyOnFM(ch, Note(noteNum)); break; } } void PlaybackManager::executeSSGStepEvents(const Step& step, int ch, bool calledByNoteDelay) { if (!calledByNoteDelay && !step.isEmptyNote()) clearSSGDelayBeyondStepCounts(ch); // Except no key event // Set volume int vol = step.getVolume(); if (step.hasVolume() && vol < bt_defs::NSTEP_SSG_VOLUME) { opnaCtrl_->setVolumeSSG(ch, vol); } // Set instrument if (step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) opnaCtrl_->setInstrumentSSG(ch, inst); } // Set effect executeStoredEffectsSSG(ch); // Set key int noteNum = step.getNoteNumber(); switch (noteNum) { case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkSSGDelayEventsInTick(step, ch); opnaCtrl_->tickEvent(SoundSource::SSG, ch); } break; case Step::NOTE_KEY_OFF: opnaCtrl_->keyOffSSG(ch); break; case Step::NOTE_ECHO0: opnaCtrl_->keyOnSSG(ch, 0); break; case Step::NOTE_ECHO1: opnaCtrl_->keyOnSSG(ch, 1); break; case Step::NOTE_ECHO2: opnaCtrl_->keyOnSSG(ch, 2); break; case Step::NOTE_ECHO3: opnaCtrl_->keyOnSSG(ch, 3); break; default: // Key on opnaCtrl_->keyOnSSG(ch, Note(noteNum)); break; } } void PlaybackManager::executeRhythmStepEvents(const Step& step, int ch, bool calledByNoteDelay) { if (!calledByNoteDelay && !step.isEmptyNote()) clearRhythmDelayBeyondStepCounts(ch); // Except no key event // Set volume int vol = step.getVolume(); if (step.hasVolume() && vol < bt_defs::NSTEP_RHYTHM_VOLUME) { opnaCtrl_->setVolumeRhythm(ch, vol); } // Set effect executeStoredEffectsRhythm(ch); // Set key switch (step.getNoteNumber()) { case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkRhythmDelayEventsInTick(step, ch); opnaCtrl_->tickEvent(SoundSource::RHYTHM, ch); } break; case Step::NOTE_KEY_OFF: opnaCtrl_->setKeyOffFlagRhythm(ch); break; default: // Key on & Echo opnaCtrl_->setKeyOnFlagRhythm(ch); break; } } void PlaybackManager::executeADPCMStepEvents(const Step& step, bool calledByNoteDelay) { if (!calledByNoteDelay && !step.isEmptyNote()) clearADPCMDelayBeyondStepCounts(); // Except no key event // Set volume int vol = step.getVolume(); if (step.hasVolume() && vol < bt_defs::NSTEP_ADPCM_VOLUME) { opnaCtrl_->setVolumeADPCM(vol); } // Set instrument if (step.hasInstrument()) { auto inst = instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()); if (inst->getType() == InstrumentType::ADPCM) opnaCtrl_->setInstrumentADPCM(std::dynamic_pointer_cast(inst)); else if (inst->getType() == InstrumentType::Drumkit) opnaCtrl_->setInstrumentDrumkit(std::dynamic_pointer_cast(inst)); } // Set effect executeStoredEffectsADPCM(); // Set key int noteNum = step.getNoteNumber(); switch (noteNum) { case Step::NOTE_NONE: if (!calledByNoteDelay) { // When this is called by note delay, tick event will be updated in readTick checkADPCMDelayEventsInTick(step); opnaCtrl_->tickEvent(SoundSource::ADPCM, 0); } break; case Step::NOTE_KEY_OFF: opnaCtrl_->keyOffADPCM(); break; case Step::NOTE_ECHO0: opnaCtrl_->keyOnADPCM(0); break; case Step::NOTE_ECHO1: opnaCtrl_->keyOnADPCM(1); break; case Step::NOTE_ECHO2: opnaCtrl_->keyOnADPCM(2); break; case Step::NOTE_ECHO3: opnaCtrl_->keyOnADPCM(3); break; default: // Key on opnaCtrl_->keyOnADPCM(Note(noteNum)); break; } } bool PlaybackManager::executeStoredEffectsGlobal() { bool changedNextPos = false; // Read step end based effects for (const Effect& eff : posChangeEffMem_) { switch (eff.type) { case EffectType::PositionJump: if (!(playStateFlags_ & PlayStateFlag::LoopPattern)) { // Skip when loop pattern changedNextPos |= effPositionJump(eff.value); } break; case EffectType::SongEnd: if (!(playStateFlags_ & PlayStateFlag::LoopPattern)) { // Skip when loop pattern effSongEnd(); changedNextPos = true; } break; case EffectType::PatternBreak: if (!(playStateFlags_ & PlayStateFlag::LoopPattern)) { // Skip when loop pattern changedNextPos |= effPatternBreak(eff.value); } break; default: break; } } posChangeEffMem_.clear(); // Read step beginning based effects for (const Effect& eff : playbackSpeedEffMem_) { switch (eff.type) { case EffectType::SpeedTempoChange: if (eff.value < 0x20) effSpeedChange(eff.value); else effTempoChange(eff.value); break; case EffectType::Groove: if (eff.value < static_cast(mod_.lock()->getGrooveCount())) effGrooveChange(eff.value); break; default: break; } } playbackSpeedEffMem_.clear(); return changedNextPos; } bool PlaybackManager::storeEffectToMapFM(int ch, const Effect& eff) { switch (eff.type) { case EffectType::Arpeggio: case EffectType::PortamentoUp: case EffectType::PortamentoDown: case EffectType::TonePortamento: case EffectType::Vibrato: case EffectType::Tremolo: case EffectType::Pan: case EffectType::VolumeSlide: case EffectType::Detune: case EffectType::FineDetune: case EffectType::NoteSlideUp: case EffectType::NoteSlideDown: case EffectType::NoteCut: case EffectType::TransposeDelay: case EffectType::VolumeDelay: case EffectType::FBControl: case EffectType::TLControl: case EffectType::MLControl: case EffectType::ARControl: case EffectType::DRControl: case EffectType::RRControl: case EffectType::Brightness: effOnKeyOnMem_[SoundSource::FM].at(static_cast(ch)).enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { effOnStepBeginMem_[SoundSource::FM].at(static_cast(ch)).enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::FM, ch, eff); return false; } } void PlaybackManager::executeStoredEffectsFM(int ch) { size_t uch = static_cast(ch); bool isNoteDelay = false; // Read step beginning based effects auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::FM].at(uch); for (const auto& eff : stepBeginBasedEffs) { switch (eff.type) { case EffectType::NoteDelay: ntDlyCntFM_.at(uch) = eff.value; isNoteDelay = true; break; default: break; } } stepBeginBasedEffs.clear(); // Read note on and step beginning based effects if (!isNoteDelay) { auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::FM].at(uch); for (auto& eff : keyOnBasedEffs) { switch (eff.type) { case EffectType::Arpeggio: opnaCtrl_->setArpeggioEffectFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::PortamentoUp: opnaCtrl_->setPortamentoEffectFM(ch, eff.value); break; case EffectType::PortamentoDown: opnaCtrl_->setPortamentoEffectFM(ch, -eff.value); break; case EffectType::TonePortamento: opnaCtrl_->setPortamentoEffectFM(ch, eff.value, true); break; case EffectType::Vibrato: opnaCtrl_->setVibratoEffectFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::Tremolo: opnaCtrl_->setTremoloEffectFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::Pan: if (-1 < eff.value && eff.value < 4) opnaCtrl_->setPanFM(ch, eff.value); break; case EffectType::VolumeSlide: { int hi = eff.value >> 4; int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideFM(ch, hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideFM(ch, low, false); // Slide down break; } case EffectType::Detune: opnaCtrl_->setDetuneFM(ch, eff.value - 0x80); break; case EffectType::FineDetune: opnaCtrl_->setFineDetuneFM(ch, eff.value - 0x80); break; case EffectType::NoteSlideUp: opnaCtrl_->setNoteSlideFM(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::NoteSlideDown: opnaCtrl_->setNoteSlideFM(ch, eff.value >> 4, -(eff.value & 0x0f)); break; case EffectType::NoteCut: ntCutDlyCntFM_.at(uch) = eff.value; break; case EffectType::TransposeDelay: tposeDlyCntFM_.at(uch) = (eff.value & 0x70) >> 4; tposeDlyValueFM_.at(uch) = ((eff.value & 0x80) ? -1 : 1) * (eff.value & 0x0f); break; case EffectType::VolumeDelay: { int count = eff.value >> 8; if (count > 0) { volDlyCntFM_.at(uch) = count; volDlyValueFM_.at(uch) = eff.value & 0x00ff; } break; } case EffectType::FBControl: if (-1 < eff.value && eff.value < 8) opnaCtrl_->setFBControlFM(ch, eff.value); break; case EffectType::TLControl: { int op = eff.value >> 8; int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 128) opnaCtrl_->setTLControlFM(ch, op - 1, val); break; } case EffectType::MLControl: { int op = eff.value >> 4; int val = eff.value & 0x0f; if (0 < op && op < 5 && -1 < val && val < 16) opnaCtrl_->setMLControlFM(ch, op - 1, val); break; } case EffectType::ARControl: { int op = eff.value >> 8; int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 32) opnaCtrl_->setARControlFM(ch, op - 1, val); break; } case EffectType::DRControl: { int op = eff.value >> 8; int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 32) opnaCtrl_->setDRControlFM(ch, op - 1, val); break; } case EffectType::RRControl: { int op = eff.value >> 4; int val = eff.value & 0x0f; if (0 < op && op < 5 && -1 < val && val < 16) opnaCtrl_->setRRControlFM(ch, op - 1, val); break; } case EffectType::Brightness: { if (0 < eff.value) opnaCtrl_->setBrightnessFM(ch, eff.value - 0x80); break; } default: break; } } keyOnBasedEffs.clear(); executeDirectRegisterSetEffect(directRegisterSets_[SoundSource::FM].at(uch)); } } bool PlaybackManager::storeEffectToMapSSG(int ch, const Effect& eff) { switch (eff.type) { case EffectType::Arpeggio: case EffectType::PortamentoUp: case EffectType::PortamentoDown: case EffectType::TonePortamento: case EffectType::Vibrato: case EffectType::Tremolo: case EffectType::VolumeSlide: case EffectType::Detune: case EffectType::FineDetune: case EffectType::NoteSlideUp: case EffectType::NoteSlideDown: case EffectType::NoteCut: case EffectType::TransposeDelay: case EffectType::ToneNoiseMix: case EffectType::NoisePitch: case EffectType::VolumeDelay: case EffectType::HardEnvHighPeriod: case EffectType::HardEnvLowPeriod: case EffectType::AutoEnvelope: effOnKeyOnMem_[SoundSource::SSG].at(static_cast(ch)).enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { effOnStepBeginMem_[SoundSource::SSG].at(static_cast(ch)).enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::SSG, ch, eff); return false; } } void PlaybackManager::executeStoredEffectsSSG(int ch) { size_t uch = static_cast(ch); bool isNoteDelay = false; // Read step beginning based effects auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::SSG].at(uch); for (const auto& eff : stepBeginBasedEffs) { switch (eff.type) { case EffectType::NoteDelay: ntDlyCntSSG_.at(uch) = eff.value; isNoteDelay = true; break; default: break; } } stepBeginBasedEffs.clear(); // Read note on and step beginning based effects if (!isNoteDelay) { auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::SSG].at(uch); for (const auto& eff : keyOnBasedEffs) { switch (eff.type) { case EffectType::Arpeggio: opnaCtrl_->setArpeggioEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::PortamentoUp: opnaCtrl_->setPortamentoEffectSSG(ch, eff.value); break; case EffectType::PortamentoDown: opnaCtrl_->setPortamentoEffectSSG(ch, -eff.value); break; case EffectType::TonePortamento: opnaCtrl_->setPortamentoEffectSSG(ch, eff.value, true); break; case EffectType::Vibrato: opnaCtrl_->setVibratoEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::Tremolo: opnaCtrl_->setTremoloEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::VolumeSlide: { int hi = eff.value >> 4; int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideSSG(ch, hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideSSG(ch, low, false); // Slide down break; } case EffectType::Detune: opnaCtrl_->setDetuneSSG(ch, eff.value - 0x80); break; case EffectType::FineDetune: opnaCtrl_->setFineDetuneSSG(ch, eff.value - 0x80); break; case EffectType::NoteSlideUp: opnaCtrl_->setNoteSlideSSG(ch, eff.value >> 4, eff.value & 0x0f); break; case EffectType::NoteSlideDown: opnaCtrl_->setNoteSlideSSG(ch, eff.value >> 4, -(eff.value & 0x0f)); break; case EffectType::NoteCut: ntCutDlyCntSSG_.at(uch) = eff.value; break; case EffectType::TransposeDelay: tposeDlyCntSSG_.at(uch) = (eff.value & 0x70) >> 4; tposeDlyValueSSG_.at(uch) = ((eff.value & 0x80) ? -1 : 1) * (eff.value & 0x0f); break; case EffectType::ToneNoiseMix: if (-1 < eff.value && eff.value < 4) opnaCtrl_->setToneNoiseMixSSG(ch, eff.value); break; case EffectType::NoisePitch: if (-1 < eff.value && eff.value < 32) opnaCtrl_->setNoisePitchSSG(ch, eff.value); break; case EffectType::HardEnvHighPeriod: opnaCtrl_->setHardEnvelopePeriod(ch, true, eff.value); break; case EffectType::HardEnvLowPeriod: opnaCtrl_->setHardEnvelopePeriod(ch, false, eff.value); break; case EffectType::VolumeDelay: { int count = eff.value >> 8; if (count > 0) { volDlyCntSSG_.at(uch) = count; volDlyValueSSG_.at(uch) = eff.value & 0x00ff; } break; } case EffectType::AutoEnvelope: opnaCtrl_->setAutoEnvelopeSSG(ch, (eff.value >> 4) - 8, eff.value & 0x0f); break; default: break; } } keyOnBasedEffs.clear(); executeDirectRegisterSetEffect(directRegisterSets_[SoundSource::SSG].at(uch)); } } bool PlaybackManager::storeEffectToMapRhythm(int ch, const Effect& eff) { switch (eff.type) { case EffectType::Pan: case EffectType::NoteCut: case EffectType::MasterVolume: case EffectType::VolumeDelay: effOnKeyOnMem_[SoundSource::RHYTHM].at(static_cast(ch)).enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { effOnStepBeginMem_[SoundSource::RHYTHM].at(static_cast(ch)).enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::RHYTHM, ch, eff); return false; } } void PlaybackManager::executeStoredEffectsRhythm(int ch) { size_t uch = static_cast(ch); bool isNoteDelay = false; // Read step beginning based effects auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::RHYTHM].at(uch); for (const auto& eff : stepBeginBasedEffs) { switch (eff.type) { case EffectType::NoteDelay: ntDlyCntRhythm_.at(uch) = eff.value; isNoteDelay = true; break; default: break; } } stepBeginBasedEffs.clear(); // Read key on and step beginning based effects if (!isNoteDelay) { auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::RHYTHM].at(uch); for (const auto& eff : keyOnBasedEffs) { switch (eff.type) { case EffectType::Pan: if (-1 < eff.value && eff.value < 4) opnaCtrl_->setPanRhythm(ch, eff.value); break; case EffectType::NoteCut: ntCutDlyCntRhythm_.at(uch) = eff.value; break; case EffectType::MasterVolume: if (-1 < eff.value && eff.value < 64) opnaCtrl_->setMasterVolumeRhythm(eff.value); break; case EffectType::VolumeDelay: { int count = eff.value >> 8; if (count > 0) { volDlyCntRhythm_.at(uch) = count; volDlyValueRhythm_.at(uch) = eff.value & 0x00ff; } break; } default: break; } } keyOnBasedEffs.clear(); executeDirectRegisterSetEffect(directRegisterSets_[SoundSource::RHYTHM].at(uch)); } } bool PlaybackManager::storeEffectToMapADPCM(int ch, const Effect& eff) { switch (eff.type) { case EffectType::Arpeggio: case EffectType::PortamentoUp: case EffectType::PortamentoDown: case EffectType::TonePortamento: case EffectType::Vibrato: case EffectType::Tremolo: case EffectType::Pan: case EffectType::VolumeSlide: case EffectType::Detune: case EffectType::FineDetune: case EffectType::NoteSlideUp: case EffectType::NoteSlideDown: case EffectType::NoteCut: case EffectType::TransposeDelay: case EffectType::VolumeDelay: effOnKeyOnMem_[SoundSource::ADPCM].front().enqueue(eff); return false; case EffectType::SpeedTempoChange: case EffectType::Groove: playbackSpeedEffMem_.enqueue(eff); return false; case EffectType::NoteDelay: if (eff.value < tickCounter_.lock()->getSpeed()) { effOnStepBeginMem_[SoundSource::ADPCM].front().enqueue(eff); return true; } return false; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: posChangeEffMem_.enqueue(eff); return false; default: storeDirectRegisterSetEffectToQueue(SoundSource::ADPCM, ch, eff); return false; } } void PlaybackManager::executeStoredEffectsADPCM() { bool isNoteDelay = false; // Read step beginning based effects auto& stepBeginBasedEffs = effOnStepBeginMem_[SoundSource::ADPCM].front(); for (const auto& eff : stepBeginBasedEffs) { switch (eff.type) { case EffectType::NoteDelay: ntDlyCntADPCM_ = eff.value; isNoteDelay = true; break; default: break; } } stepBeginBasedEffs.clear(); // Read note on and step beginning based effects if (!isNoteDelay) { auto& keyOnBasedEffs = effOnKeyOnMem_[SoundSource::ADPCM].front(); for (const auto& eff : keyOnBasedEffs) { switch (eff.type) { case EffectType::Arpeggio: opnaCtrl_->setArpeggioEffectADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::PortamentoUp: opnaCtrl_->setPortamentoEffectADPCM(eff.value); break; case EffectType::PortamentoDown: opnaCtrl_->setPortamentoEffectADPCM(-eff.value); break; case EffectType::TonePortamento: opnaCtrl_->setPortamentoEffectADPCM(eff.value, true); break; case EffectType::Vibrato: opnaCtrl_->setVibratoEffectADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::Tremolo: opnaCtrl_->setTremoloEffectADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::Pan: if (-1 < eff.value && eff.value < 4) opnaCtrl_->setPanADPCM(eff.value); break; case EffectType::VolumeSlide: { int hi = eff.value >> 4; int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideADPCM(hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideADPCM(low, false); // Slide down break; } case EffectType::Detune: opnaCtrl_->setDetuneADPCM(eff.value - 0x80); break; case EffectType::FineDetune: opnaCtrl_->setFineDetuneADPCM(eff.value - 0x80); break; case EffectType::NoteSlideUp: opnaCtrl_->setNoteSlideADPCM(eff.value >> 4, eff.value & 0x0f); break; case EffectType::NoteSlideDown: opnaCtrl_->setNoteSlideADPCM(eff.value >> 4, -(eff.value & 0x0f)); break; case EffectType::NoteCut: ntCutDlyCntADPCM_ = eff.value; break; case EffectType::TransposeDelay: tposeDlyCntADPCM_ = (eff.value & 0x70) >> 4; tposeDlyValueADPCM_ = ((eff.value & 0x80) ? -1 : 1) * (eff.value & 0x0f); break; case EffectType::VolumeDelay: { int count = eff.value >> 8; if (count > 0) { volDlyCntADPCM_ = count; volDlyValueADPCM_ = eff.value & 0x00ff; } break; } default: break; } } keyOnBasedEffs.clear(); executeDirectRegisterSetEffect(directRegisterSets_[SoundSource::ADPCM].front()); } } void PlaybackManager::storeDirectRegisterSetEffectToQueue(SoundSource src, int ch, const Effect& eff) { size_t uch = static_cast(ch); switch (eff.type) { case EffectType::RegisterAddress0: if (-1 < eff.value && eff.value < 0x6c) { directRegisterSets_.at(src).at(uch).push_back({ eff.value, 0, false }); } break; case EffectType::RegisterAddress1: if (-1 < eff.value && eff.value < 0x6c) { directRegisterSets_.at(src).at(uch).push_back({ 0x100 | eff.value, 0, false }); } break; case EffectType::RegisterValue: { DirectRegisterSetQueue& queue = directRegisterSets_.at(src).at(uch); if (!queue.empty() && -1 < eff.value) { RegisterUnit& unit = queue.back(); unit.value = eff.value; unit.hasCompleted = true; } break; } default: break; } } void PlaybackManager::executeDirectRegisterSetEffect(DirectRegisterSetQueue& queue) { for (const RegisterUnit& unit : queue) { if (unit.hasCompleted) opnaCtrl_->sendRegister(unit.address, unit.value); } queue.clear(); } bool PlaybackManager::effPositionJump(int nextOrder) { if (nextOrder < static_cast(getOrderSize(curSongNum_))) { nextReadPos_.set(nextOrder, 0); return true; } return false; } void PlaybackManager::effSongEnd() { nextReadPos_.invalidate(); } bool PlaybackManager::effPatternBreak(int nextStep) { if (playingPos_.order == static_cast(getOrderSize(curSongNum_)) - 1 && nextStep < static_cast(getPatternSizeFromOrderNumber(curSongNum_, 0))) { nextReadPos_.set(0, nextStep); return true; } else if (nextStep < static_cast(getPatternSizeFromOrderNumber(curSongNum_, playingPos_.order + 1))) { nextReadPos_.set(playingPos_.order + 1, nextStep); return true; } return false; } void PlaybackManager::effSpeedChange(int speed) { tickCounter_.lock()->setSpeed(speed ? speed : 1); tickCounter_.lock()->setGrooveState(GrooveState::Invalid); } void PlaybackManager::effTempoChange(int tempo) { tickCounter_.lock()->setTempo(tempo); tickCounter_.lock()->setGrooveState(GrooveState::Invalid); } void PlaybackManager::effGrooveChange(int num) { tickCounter_.lock()->setGroove(mod_.lock()->getGroove(num)); tickCounter_.lock()->setGrooveState(GrooveState::ValidByLocal); } void PlaybackManager::tickProcess(int rest) { if (!(playStateFlags_ & PlayStateFlag::ReadFirstStep)) return; // When it has not read first step updateDelayEventCounts(); auto& song = mod_.lock()->getSong(curSongNum_); for (auto& attrib : songStyle_.trackAttribs) { auto& curStep = song.getTrack(attrib.number) .getPatternFromOrderNumber(playingPos_.order).getStep(playingPos_.step); int ch = attrib.channelInSource; switch (attrib.source) { case SoundSource::FM: checkFMDelayEventsInTick(curStep, ch); break; case SoundSource::SSG: checkSSGDelayEventsInTick(curStep, ch); break; case SoundSource::RHYTHM: checkRhythmDelayEventsInTick(curStep, ch); break; case SoundSource::ADPCM: checkADPCMDelayEventsInTick(curStep); break; } if (rest == 1 && nextReadPos_.isValid() && attrib.source == SoundSource::FM && !isPlayingStep()) { // Channel envelope reset before next key on auto& step = song.getTrack(attrib.number) .getPatternFromOrderNumber(nextReadPos_.order).getStep(nextReadPos_.step); for (int i = 0; i < Step::N_EFFECT; ++i) { auto&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); if (eff.type == EffectType::NoteDelay && eff.value > 0) { // Note delay check opnaCtrl_->tickEvent(attrib.source, ch); return; } } envelopeResetEffectFM(step, ch); } else { opnaCtrl_->tickEvent(attrib.source, ch); } } opnaCtrl_->updateRegisterStates(); } void PlaybackManager::checkFMDelayEventsInTick(const Step& step, int ch) { size_t uch = static_cast(ch); // Check volume delay if (!volDlyCntFM_.at(uch)) opnaCtrl_->setOneshotVolumeFM(ch, volDlyValueFM_.at(uch)); // Check note cut if (!ntCutDlyCntFM_.at(uch)) opnaCtrl_->keyOffFM(ch); // Check transpose delay if (!tposeDlyCntFM_.at(uch)) opnaCtrl_->setTransposeEffectFM(ch, tposeDlyValueFM_.at(uch)); // Check note delay and envelope reset checkFMNoteDelayAndEnvelopeReset(step, ch); } void PlaybackManager::checkFMNoteDelayAndEnvelopeReset(const Step& step, int ch) { int cnt = ntDlyCntFM_.at(static_cast(ch)); if (!cnt) { executeFMStepEvents(step, ch, true); } else if (cnt == 1) { // Channel envelope reset before next key on envelopeResetEffectFM(step, ch); } } void PlaybackManager::envelopeResetEffectFM(const Step& step, int ch) { if (!(step.isEmptyNote() || step.hasKeyOff()) && opnaCtrl_->enableFMEnvelopeReset(ch)) { // Key on or echo buffer access for (int i = 0; i < Step::N_EFFECT; ++i) { auto&& eff = effect_utils::validateEffect(SoundSource::FM, step.getEffect(i)); // "SoundSource::FM" is dummy if (eff.type == EffectType::TonePortamento) { if (eff.value) opnaCtrl_->tickEvent(SoundSource::FM, ch); else opnaCtrl_->resetFMChannelEnvelope(ch); return; } } if (opnaCtrl_->isTonePortamentoFM(ch)) opnaCtrl_->tickEvent(SoundSource::FM, ch); else opnaCtrl_->resetFMChannelEnvelope(ch); } else { opnaCtrl_->tickEvent(SoundSource::FM, ch); } } void PlaybackManager::checkSSGDelayEventsInTick(const Step& step, int ch) { size_t uch = static_cast(ch); // Check volume delay if (!volDlyCntSSG_.at(uch)) opnaCtrl_->setOneshotVolumeSSG(ch, volDlyValueSSG_.at(uch)); // Check note cut if (!ntCutDlyCntSSG_.at(uch)) opnaCtrl_->keyOffSSG(ch); // Check transpose delay if (!tposeDlyCntSSG_.at(uch)) opnaCtrl_->setTransposeEffectSSG(ch, tposeDlyValueSSG_.at(uch)); // Check note delay if (!ntDlyCntSSG_.at(uch)) executeSSGStepEvents(step, ch, true); } void PlaybackManager::checkRhythmDelayEventsInTick(const Step& step, int ch) { size_t uch = static_cast(ch); // Check volume delay if (!volDlyCntRhythm_.at(uch)) opnaCtrl_->setOneshotVolumeRhythm(ch, volDlyValueRhythm_.at(uch)); // Check note cut if (!ntCutDlyCntRhythm_.at(uch)) opnaCtrl_->setKeyOnFlagRhythm(ch); // Check note delay if (!ntDlyCntRhythm_.at(uch)) executeRhythmStepEvents(step, ch, true); } void PlaybackManager::checkADPCMDelayEventsInTick(const Step& step) { // Check volume delay if (!volDlyCntADPCM_) opnaCtrl_->setOneshotVolumeADPCM(volDlyValueADPCM_); // Check note cut if (!ntCutDlyCntADPCM_) opnaCtrl_->keyOffADPCM(); // Check transpose delay if (!tposeDlyCntADPCM_) opnaCtrl_->setTransposeEffectADPCM(tposeDlyValueADPCM_); // Check note delay if (!ntDlyCntADPCM_) executeADPCMStepEvents(step, true); } void PlaybackManager::clearEffectMaps() { playbackSpeedEffMem_.clear(); posChangeEffMem_.clear(); for (auto& maps: effOnKeyOnMem_) { for (EffectMemory& mem : maps.second) mem.clear(); } for (auto& maps: effOnStepBeginMem_) { for (EffectMemory& mem : maps.second) mem.clear(); } for (auto& maps: directRegisterSets_) { for (DirectRegisterSetQueue& queue : maps.second) queue.clear(); } } void PlaybackManager::clearNoteDelayCounts() { std::fill(ntDlyCntFM_.begin(), ntDlyCntFM_.end(), -1); std::fill(ntDlyCntSSG_.begin(), ntDlyCntSSG_.end(), -1); std::fill(ntDlyCntRhythm_.begin(), ntDlyCntRhythm_.end(), -1); ntDlyCntADPCM_ = -1; } void PlaybackManager::clearDelayBeyondStepCounts() { std::fill(ntCutDlyCntFM_.begin(), ntCutDlyCntFM_.end(), -1); std::fill(volDlyCntFM_.begin(), volDlyCntFM_.end(), -1); std::fill(volDlyValueFM_.begin(), volDlyValueFM_.end(), -1); std::fill(tposeDlyCntFM_.begin(), tposeDlyCntFM_.end(), -1); std::fill(tposeDlyValueFM_.begin(), tposeDlyValueFM_.end(), 0); std::fill(ntCutDlyCntSSG_.begin(), ntCutDlyCntSSG_.end(), -1); std::fill(volDlyCntSSG_.begin(), volDlyCntSSG_.end(), -1); std::fill(volDlyValueSSG_.begin(), volDlyValueSSG_.end(), -1); std::fill(tposeDlyCntSSG_.begin(), tposeDlyCntSSG_.end(), -1); std::fill(tposeDlyValueSSG_.begin(), tposeDlyValueSSG_.end(), 0); std::fill(ntCutDlyCntRhythm_.begin(), ntCutDlyCntRhythm_.end(), -1); std::fill(volDlyCntRhythm_.begin(), volDlyCntRhythm_.end(), -1); std::fill(volDlyValueRhythm_.begin(), volDlyValueRhythm_.end(), -1); ntCutDlyCntADPCM_ = -1; volDlyCntADPCM_ = -1; volDlyValueADPCM_ = -1; tposeDlyCntADPCM_ = -1; tposeDlyValueADPCM_ = 0; } void PlaybackManager::clearFMDelayBeyondStepCounts(int ch) { size_t uch = static_cast(ch); ntCutDlyCntFM_.at(uch) = -1; volDlyCntFM_.at(uch) = -1; volDlyValueFM_.at(uch) = -1; tposeDlyCntFM_.at(uch) = -1; tposeDlyValueFM_.at(uch) = 0; } void PlaybackManager::clearSSGDelayBeyondStepCounts(int ch) { size_t uch = static_cast(ch); ntCutDlyCntSSG_.at(uch) = -1; volDlyCntSSG_.at(uch) = -1; volDlyValueSSG_.at(uch) = -1; tposeDlyCntSSG_.at(uch) = -1; tposeDlyValueSSG_.at(uch) = 0; } void PlaybackManager::clearRhythmDelayBeyondStepCounts(int ch) { size_t uch = static_cast(ch); ntCutDlyCntRhythm_.at(uch) = -1; volDlyCntRhythm_.at(uch) = -1; volDlyValueRhythm_.at(uch) = -1; } void PlaybackManager::clearADPCMDelayBeyondStepCounts() { ntCutDlyCntADPCM_ = -1; volDlyCntADPCM_ = -1; volDlyValueADPCM_ = -1; tposeDlyCntADPCM_ = -1; tposeDlyValueADPCM_ = 0; } void PlaybackManager::updateDelayEventCounts() { auto f = [](int x) { return (x == -1) ? x : --x; }; std::transform(ntDlyCntFM_.begin(), ntDlyCntFM_.end(), ntDlyCntFM_.begin(), f); std::transform(ntDlyCntSSG_.begin(), ntDlyCntSSG_.end(), ntDlyCntSSG_.begin(), f); std::transform(ntDlyCntRhythm_.begin(), ntDlyCntRhythm_.end(), ntDlyCntRhythm_.begin(), f); --ntDlyCntADPCM_; std::transform(ntCutDlyCntFM_.begin(), ntCutDlyCntFM_.end(), ntCutDlyCntFM_.begin(), f); std::transform(ntCutDlyCntSSG_.begin(), ntCutDlyCntSSG_.end(), ntCutDlyCntSSG_.begin(), f); std::transform(ntCutDlyCntRhythm_.begin(), ntCutDlyCntRhythm_.end(), ntCutDlyCntRhythm_.begin(), f); --ntCutDlyCntADPCM_; std::transform(volDlyCntFM_.begin(), volDlyCntFM_.end(), volDlyCntFM_.begin(), f); std::transform(volDlyCntSSG_.begin(), volDlyCntSSG_.end(), volDlyCntSSG_.begin(), f); std::transform(volDlyCntRhythm_.begin(), volDlyCntRhythm_.end(), volDlyCntRhythm_.begin(), f); --volDlyCntADPCM_; std::transform(tposeDlyCntFM_.begin(), tposeDlyCntFM_.end(), tposeDlyCntFM_.begin(), f); std::transform(tposeDlyCntSSG_.begin(), tposeDlyCntSSG_.end(), tposeDlyCntSSG_.begin(), f); --tposeDlyCntADPCM_; } void PlaybackManager::checkPlayPosition(int maxStepSize) { if (isPlaySong() && playingPos_.step >= maxStepSize) { playingPos_.step = maxStepSize - 1; findNextStep(); } } void PlaybackManager::setChannelRetrieving(bool enabled) { isRetrieveChannel_ = enabled; } void PlaybackManager::retrieveChannelStates() { size_t fmch = Song::getFMChannelCount(songStyle_.type); // Skip echo buffer due to takes time to search std::vector isSetInstFM(fmch, false), isSetVolFM(fmch, false), isSetArpFM(fmch, false); std::vector isSetPrtFM(fmch, false), isSetVibFM(fmch, false), isSetTreFM(fmch, false); std::vector isSetPanFM(fmch, false), isSetVolSldFM(fmch, false), isSetDtnFM(fmch, false); std::vector isSetFBCtrlFM(fmch, false), isSetTLCtrlFM(fmch, false), isSetMLCtrlFM(fmch, false); std::vector isSetARCtrlFM(fmch, false), isSetDRCtrlFM(fmch, false), isSetRRCtrlFM(fmch, false); std::vector isSetBrightFM(fmch, false), isSetFiDtnFM(fmch, false); std::vector isSetInstSSG(3, false), isSetVolSSG(3, false), isSetArpSSG(3, false), isSetPrtSSG(3, false); std::vector isSetVibSSG(3, false), isSetTreSSG(3, false), isSetVolSldSSG(3, false), isSetDtnSSG(3, false); std::vector isSetTNMixSSG(3, false), isSetFiDtnSSG(3, false); std::vector isSetVolRhythm(6, false), isSetPanRhythm(6, false); bool isSetInstADPCM(false), isSetVolADPCM(false), isSetArpADPCM(false), isSetPrtADPCM(false); bool isSetVibADPCM(false), isSetTreADPCM(false), isSetPanADPCM(false), isSetVolSldADPCM(false); bool isSetDtnADPCM(false), isSetFiDtnADPCM(false); bool isSetMVolRhythm = false; bool isSetNoisePitchSSG = false; bool isSetHardEnvPeriodHighSSG = false; bool isSetHardEnvPeriodLowSSG = false; bool isSetAutoEnvSSG = false; /// bit0: step /// bit1: tempo /// bit2: groove uint8_t speedStates = 0; Position pos = playingPos_; bool isPrevPos = false; Song& song = mod_.lock()->getSong(curSongNum_); while (true) { for (auto it = songStyle_.trackAttribs.rbegin(), e = songStyle_.trackAttribs.rend(); it != e; ++it) { Step& step = song.getTrack(it->number).getPatternFromOrderNumber(pos.order).getStep(pos.step); int ch = it->channelInSource; size_t uch = static_cast(ch); switch (it->source) { case SoundSource::FM: { // Volume int vol = step.getVolume(); if (!isSetVolFM[uch] && step.hasVolume() && vol < bt_defs::NSTEP_FM_VOLUME) { isSetVolFM[uch] = true; if (isPrevPos) opnaCtrl_->setVolumeFM(ch, vol); } // Instrument if (!isSetInstFM[uch] && step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) { isSetInstFM[uch] = true; if (isPrevPos) opnaCtrl_->setInstrumentFM(ch, inst); } } // Effects for (int i = Step::N_EFFECT - 1; i > -1; --i) { Effect eff = effect_utils::validateEffect(SoundSource::FM, step.getEffect(i)); switch (eff.type) { case EffectType::Arpeggio: if (!isSetArpFM[uch]) { isSetArpFM[uch] = true; if (isPrevPos) opnaCtrl_->setArpeggioEffectFM(ch, eff.value >> 4, eff.value & 0x0f); } break; case EffectType::PortamentoUp: if (!isSetPrtFM[uch]) { isSetPrtFM[uch] = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectFM(ch, eff.value); } break; case EffectType::PortamentoDown: if (!isSetPrtFM[uch]) { isSetPrtFM[uch] = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectFM(ch, -eff.value); } break; case EffectType::TonePortamento: if (!isSetPrtFM[uch]) { isSetPrtFM[uch] = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectFM(ch, eff.value, true); } break; case EffectType::Vibrato: if (!isSetVibFM[uch]) { isSetVibFM[uch] = true; if (isPrevPos) opnaCtrl_->setVibratoEffectFM(ch, eff.value >> 4, eff.value & 0x0f); } break; case EffectType::Tremolo: if (!isSetTreFM[uch]) { isSetTreFM[uch] = true; if (isPrevPos) opnaCtrl_->setTremoloEffectFM(ch, eff.value >> 4, eff.value & 0x0f); } break; case EffectType::Pan: if (-1 < eff.value && eff.value < 4 && !isSetPanFM[uch]) { isSetPanFM[uch] = true; if (isPrevPos) opnaCtrl_->setPanFM(ch, eff.value); } break; case EffectType::VolumeSlide: if (!isSetVolSldFM[uch]) { isSetVolSldFM[uch] = true; if (isPrevPos) { int hi = eff.value >> 4; int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideFM(ch, hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideFM(ch, low, false); // Slide down } } break; case EffectType::SpeedTempoChange: if (!(speedStates & 0x4)) { if (eff.value < 0x20) { // Speed change if (!(speedStates & 0x1)) { speedStates |= 0x1; if (isPrevPos) effSpeedChange(eff.value); } } else if (!(speedStates & 0x2)) { // Tempo change speedStates |= 0x2; if (isPrevPos) effTempoChange(eff.value); } } break; case EffectType::Groove: if (eff.value < static_cast(mod_.lock()->getGrooveCount()) && !speedStates) { speedStates |= 0x4; if (isPrevPos) effGrooveChange(eff.value); } break; case EffectType::Detune: if (!isSetDtnFM[uch]) { isSetDtnFM[uch] = true; if (isPrevPos) opnaCtrl_->setDetuneFM(ch, eff.value - 0x80); } break; case EffectType::FineDetune: if (!isSetFiDtnFM[uch]) { isSetFiDtnFM[uch] = true; if (isPrevPos) opnaCtrl_->setFineDetuneFM(ch, eff.value - 0x80); } break; case EffectType::FBControl: if (!isSetFBCtrlFM[uch]) { isSetFBCtrlFM[uch] = true; if (isPrevPos) { if (-1 < eff.value && eff.value < 8) opnaCtrl_->setFBControlFM(ch, eff.value); } } break; case EffectType::TLControl: if (!isSetTLCtrlFM[uch]) { isSetTLCtrlFM[uch] = true; if (isPrevPos) { int op = eff.value >> 8; int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 128) opnaCtrl_->setTLControlFM(ch, op - 1, val); } } break; case EffectType::MLControl: if (!isSetMLCtrlFM[uch]) { isSetMLCtrlFM[uch] = true; if (isPrevPos) { int op = eff.value >> 4; int val = eff.value & 0x0f; if (0 < op && op < 5 && -1 < val && val < 16) opnaCtrl_->setMLControlFM(ch, op - 1, val); } } break; case EffectType::ARControl: if (!isSetARCtrlFM[uch]) { isSetARCtrlFM[uch] = true; if (isPrevPos) { int op = eff.value >> 8; int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 32) opnaCtrl_->setARControlFM(ch, op - 1, val); } } break; case EffectType::DRControl: if (!isSetDRCtrlFM[uch]) { isSetDRCtrlFM[uch] = true; if (isPrevPos) { int op = eff.value >> 8; int val = eff.value & 0x00ff; if (0 < op && op < 5 && -1 < val && val < 32) opnaCtrl_->setDRControlFM(ch, op - 1, val); } } break; case EffectType::RRControl: if (!isSetRRCtrlFM[uch]) { isSetRRCtrlFM[uch] = true; if (isPrevPos) { int op = eff.value >> 4; int val = eff.value & 0x0f; if (0 < op && op < 5 && -1 < val && val < 16) opnaCtrl_->setRRControlFM(ch, op - 1, val); } } break; case EffectType::Brightness: if (!isSetBrightFM[uch]) { isSetBrightFM[uch] = true; if (isPrevPos) { if (0 < eff.value) opnaCtrl_->setBrightnessFM(ch, eff.value - 0x80); } } break; default: break; } } break; } case SoundSource::SSG: { // Volume int vol = step.getVolume(); if (!isSetVolSSG[uch] && step.hasVolume() && vol < bt_defs::NSTEP_SSG_VOLUME) { isSetVolSSG[uch] = true; if (isPrevPos) opnaCtrl_->setVolumeSSG(ch, vol); } // Instrument if (!isSetInstSSG[uch] && step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) { isSetInstSSG[uch] = true; if (isPrevPos) opnaCtrl_->setInstrumentSSG(ch, inst); } } // Effects for (int i = Step::N_EFFECT - 1; i > -1; --i) { Effect eff = effect_utils::validateEffect(SoundSource::SSG, step.getEffect(i)); switch (eff.type) { case EffectType::Arpeggio: if (!isSetArpSSG[uch]) { isSetArpSSG[uch] = true; if (isPrevPos) opnaCtrl_->setArpeggioEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); } break; case EffectType::PortamentoUp: if (!isSetPrtSSG[uch]) { isSetPrtSSG[uch] = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectSSG(ch, eff.value); } break; case EffectType::PortamentoDown: if (!isSetPrtSSG[uch]) { isSetPrtSSG[uch] = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectSSG(ch, -eff.value); } break; case EffectType::TonePortamento: if (!isSetPrtSSG[uch]) { isSetPrtSSG[uch] = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectSSG(ch, eff.value, true); } break; case EffectType::Vibrato: if (!isSetVibSSG[uch]) { isSetVibSSG[uch] = true; if (isPrevPos) opnaCtrl_->setVibratoEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); } break; case EffectType::Tremolo: if (!isSetTreSSG[uch]) { isSetTreSSG[uch] = true; if (isPrevPos) opnaCtrl_->setTremoloEffectSSG(ch, eff.value >> 4, eff.value & 0x0f); } break; case EffectType::VolumeSlide: if (!isSetVolSldSSG[uch]) { isSetVolSldSSG[uch] = true; if (isPrevPos) { int hi = eff.value >> 4; int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideSSG(ch, hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideSSG(ch, low, false); // Slide down } } break; case EffectType::SpeedTempoChange: if (!(speedStates & 0x4)) { if (eff.value < 0x20) { // Speed change if (!(speedStates & 0x1)) { speedStates |= 0x1; if (isPrevPos) effSpeedChange(eff.value); } } else if (!(speedStates & 0x2)) { // Tempo change speedStates |= 0x2; if (isPrevPos) effTempoChange(eff.value); } } break; case EffectType::Groove: if (eff.value < static_cast(mod_.lock()->getGrooveCount()) && !speedStates) { speedStates |= 0x4; if (isPrevPos) effGrooveChange(eff.value); } break; case EffectType::Detune: if (!isSetDtnSSG[uch]) { isSetDtnSSG[uch] = true; if (isPrevPos) opnaCtrl_->setDetuneSSG(ch, eff.value - 0x80); } break; case EffectType::FineDetune: if (!isSetFiDtnSSG[uch]) { isSetFiDtnSSG[uch] = true; if (isPrevPos) opnaCtrl_->setFineDetuneSSG(ch, eff.value - 0x80); } break; case EffectType::ToneNoiseMix: if (-1 < eff.value && eff.value < 4 && !isSetTNMixSSG[uch]) { isSetTNMixSSG[uch] = true; if (isPrevPos) opnaCtrl_->setToneNoiseMixSSG(ch, eff.value); } break; case EffectType::NoisePitch: if (-1 < eff.value && eff.value < 32 && !isSetNoisePitchSSG) { isSetNoisePitchSSG = true; if (isPrevPos) opnaCtrl_->setNoisePitchSSG(ch, eff.value); } break; case EffectType::HardEnvHighPeriod: if (!isSetHardEnvPeriodHighSSG) { isSetHardEnvPeriodHighSSG = true; if (isPrevPos) opnaCtrl_->setHardEnvelopePeriod(ch, true, eff.value); } break; case EffectType::HardEnvLowPeriod: if (!isSetHardEnvPeriodLowSSG) { isSetHardEnvPeriodLowSSG = true; if (isPrevPos) opnaCtrl_->setHardEnvelopePeriod(ch, false, eff.value); } break; case EffectType::AutoEnvelope: if (!isSetAutoEnvSSG) { isSetAutoEnvSSG = true; if (isPrevPos) opnaCtrl_->setAutoEnvelopeSSG(ch, (eff.value >> 4) - 8, eff.value & 0x0f); } break; default: break; } } break; } case SoundSource::RHYTHM: { // Volume int vol = step.getVolume(); if (!isSetVolRhythm[uch] && step.hasVolume() && vol < bt_defs::NSTEP_RHYTHM_VOLUME) { isSetVolRhythm[uch] = true; if (isPrevPos) opnaCtrl_->setVolumeRhythm(ch, vol); } // Effects for (int i = Step::N_EFFECT - 1; i > -1; --i) { Effect eff = effect_utils::validateEffect(SoundSource::RHYTHM, step.getEffect(i)); switch (eff.type) { case EffectType::Pan: if (-1 < eff.value && eff.value < 4 && !isSetPanRhythm[uch]) { isSetPanRhythm[uch] = true; if (isPrevPos) opnaCtrl_->setPanRhythm(ch, eff.value); } break; case EffectType::SpeedTempoChange: if (!(speedStates & 0x4)) { if (eff.value < 0x20) { // Speed change if (!(speedStates & 0x1)) { speedStates |= 0x1; if (isPrevPos) effSpeedChange(eff.value); } } else if (!(speedStates & 0x2)) { // Tempo change speedStates |= 0x2; if (isPrevPos) effTempoChange(eff.value); } } break; case EffectType::Groove: if (eff.value < static_cast(mod_.lock()->getGrooveCount()) && !speedStates) { speedStates |= 0x4; if (isPrevPos) effGrooveChange(eff.value); } break; case EffectType::MasterVolume: if (-1 < eff.value && eff.value < 64 && !isSetMVolRhythm) { isSetMVolRhythm = true; if (isPrevPos) opnaCtrl_->setMasterVolumeRhythm(eff.value); } break; default: break; } } break; } case SoundSource::ADPCM: { // Volume int vol = step.getVolume(); if (!isSetVolADPCM && step.hasVolume() && vol < bt_defs::NSTEP_ADPCM_VOLUME) { isSetVolADPCM = true; if (isPrevPos) opnaCtrl_->setVolumeADPCM(vol); } // Instrument if (!isSetInstADPCM && step.hasInstrument()) { if (auto inst = std::dynamic_pointer_cast( instMan_.lock()->getInstrumentSharedPtr(step.getInstrumentNumber()))) { isSetInstADPCM = true; if (isPrevPos) opnaCtrl_->setInstrumentADPCM(inst); } } // Effects for (int i = Step::N_EFFECT - 1; i > -1; --i) { Effect eff = effect_utils::validateEffect(SoundSource::ADPCM, step.getEffect(i)); switch (eff.type) { case EffectType::Arpeggio: if (!isSetArpADPCM) { isSetArpADPCM = true; if (isPrevPos) opnaCtrl_->setArpeggioEffectADPCM(eff.value >> 4, eff.value & 0x0f); } break; case EffectType::PortamentoUp: if (!isSetPrtADPCM) { isSetPrtADPCM = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectADPCM(eff.value); } break; case EffectType::PortamentoDown: if (!isSetPrtADPCM) { isSetPrtADPCM = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectADPCM(-eff.value); } break; case EffectType::TonePortamento: if (!isSetPrtADPCM) { isSetPrtADPCM = true; if (isPrevPos) opnaCtrl_->setPortamentoEffectADPCM(eff.value, true); } break; case EffectType::Vibrato: if (!isSetVibADPCM) { isSetVibADPCM = true; if (isPrevPos) opnaCtrl_->setVibratoEffectADPCM(eff.value >> 4, eff.value & 0x0f); } break; case EffectType::Tremolo: if (!isSetTreADPCM) { isSetTreADPCM = true; if (isPrevPos) opnaCtrl_->setTremoloEffectADPCM(eff.value >> 4, eff.value & 0x0f); } break; case EffectType::Pan: if (-1 < eff.value && eff.value < 4 && !isSetPanADPCM) { isSetPanADPCM = true; if (isPrevPos) opnaCtrl_->setPanADPCM(eff.value); } break; case EffectType::VolumeSlide: if (!isSetVolSldADPCM) { isSetVolSldADPCM = true; if (isPrevPos) { int hi = eff.value >> 4; int low = eff.value & 0x0f; if (hi && !low) opnaCtrl_->setVolumeSlideADPCM(hi, true); // Slide up else if (!hi) opnaCtrl_->setVolumeSlideADPCM(low, false); // Slide down } } break; case EffectType::SpeedTempoChange: if (!(speedStates & 0x4)) { if (eff.value < 0x20 && !(speedStates & 0x1)) { // Speed change speedStates |= 0x1; if (isPrevPos) effSpeedChange(eff.value); } else if (!(speedStates & 0x2)) { // Tempo change speedStates |= 0x2; if (isPrevPos) effTempoChange(eff.value); } } break; case EffectType::Groove: if (eff.value < static_cast(mod_.lock()->getGrooveCount()) && !speedStates) { speedStates |= 0x4; if (isPrevPos) effGrooveChange(eff.value); } break; case EffectType::Detune: if (!isSetDtnADPCM) { isSetDtnADPCM = true; if (isPrevPos) opnaCtrl_->setDetuneADPCM(eff.value - 0x80); } break; case EffectType::FineDetune: if (!isSetFiDtnADPCM) { isSetFiDtnADPCM = true; if (isPrevPos) opnaCtrl_->setFineDetuneADPCM(eff.value - 0x80); } break; default: break; } } break; } } } // Move position isPrevPos = true; if (--pos.step < 0) { if (--pos.order < 0) break; pos.step = static_cast(getPatternSizeFromOrderNumber(curSongNum_, pos.order)) - 1; } } // Sequence reset for (size_t ch = 0; ch < fmch; ++ch) { opnaCtrl_->haltSequencesFM(static_cast(ch)); } for (size_t ch = 0; ch < 3; ++ch) { opnaCtrl_->haltSequencesSSG(static_cast(ch)); } opnaCtrl_->haltSequencesADPCM(); } size_t PlaybackManager::getOrderSize(int songNum) const { return mod_.lock()->getSong(songNum).getOrderSize(); } size_t PlaybackManager::getPatternSizeFromOrderNumber(int songNum, int orderNum) const { return mod_.lock()->getSong(songNum).getPatternSizeFromOrderNumber(orderNum); } BambooTracker-0.4.6/BambooTracker/playback.hpp000066400000000000000000000152411401124043500212430ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include "module.hpp" #include "effect.hpp" #include "enum_hash.hpp" #include "bamboo_tracker_defs.hpp" class OPNAController; class InstrumentsManager; class TickCounter; class EffectMemory { public: EffectMemory(); void enqueue(const Effect& eff); void clear(); using container_type = std::vector; using reference = container_type::reference; using const_reference = container_type::const_reference; using iterator = container_type::iterator; using const_iterator = container_type::const_iterator; using value_type = container_type::value_type; iterator begin() noexcept { return mem_.begin(); } const_iterator begin() const noexcept { return mem_.begin(); } const_iterator cbegin() const noexcept { return mem_.cbegin(); } iterator end() noexcept { return mem_.end(); } const_iterator end() const noexcept { return mem_.end(); } const_iterator cend() const noexcept { return mem_.cend(); } private: std::vector mem_; }; class PlaybackManager { public: PlaybackManager(std::shared_ptr opnaCtrl, std::weak_ptr instMan, std::weak_ptr tickCounter, std::weak_ptr mod, bool isRetrieveChannel); void setSong(std::weak_ptr mod, int songNum); // Play song void startPlaySong(int order); void startPlayFromStart(); void startPlayPattern(int order); void startPlayFromPosition(int order, int step); void playStep(int order, int step); void stopPlaySong(); bool isPlaySong() const noexcept; bool isPlayingStep() const noexcept; int getPlayingOrderNumber() const noexcept; int getPlayingStepNumber() const noexcept; // Stream events /// 0<: Tick /// 0: Step /// -1: Stop int streamCountUp(); void checkPlayPosition(int maxStepSize); void setChannelRetrieving(bool enabled); private: std::shared_ptr opnaCtrl_; std::weak_ptr instMan_; std::weak_ptr tickCounter_; std::weak_ptr mod_; std::mutex mutex_; int curSongNum_; SongStyle songStyle_; struct Position { int order, step; static constexpr int INVALID = -1; Position(int o, int s) : order(o), step(s) {} void set(int o, int s) // Faster than making and copying a new instance { order = o; step = s; } void invalidate() noexcept { set(INVALID, INVALID); } bool isValid() const noexcept { return (order > INVALID && step > INVALID); } } playingPos_, nextReadPos_; uint8_t playStateFlags_; // Play song bool isFindNextStep_; void startPlay(); void stopPlay(); bool stepDown(); void findNextStep(); void checkValidPosition(); void stepProcess(); std::unordered_map> isNoteDelay_; void executeFMStepEvents(const Step& step, int ch, bool calledByNoteDelay = false); void executeSSGStepEvents(const Step& step, int ch, bool calledByNoteDelay = false); void executeRhythmStepEvents(const Step& step, int ch, bool calledByNoteDelay = false); void executeADPCMStepEvents(const Step& step, bool calledByNoteDelay = false); EffectMemory playbackSpeedEffMem_, posChangeEffMem_; std::unordered_map> effOnKeyOnMem_, effOnStepBeginMem_; struct RegisterUnit { int address, value; bool hasCompleted; }; using DirectRegisterSetQueue = std::vector; using DirectRegisterSetSource = std::vector; std::unordered_map directRegisterSets_; bool executeStoredEffectsGlobal(); bool storeEffectToMapFM(int ch, const Effect& eff); void executeStoredEffectsFM(int ch); bool storeEffectToMapSSG(int ch, const Effect& eff); void executeStoredEffectsSSG(int ch); bool storeEffectToMapRhythm(int ch, const Effect& eff); void executeStoredEffectsRhythm(int ch); bool storeEffectToMapADPCM(int ch, const Effect& eff); void executeStoredEffectsADPCM(); void storeDirectRegisterSetEffectToQueue(SoundSource src, int ch, const Effect& eff); void executeDirectRegisterSetEffect(DirectRegisterSetQueue& queue); bool effPositionJump(int nextOrder); void effSongEnd(); bool effPatternBreak(int nextStep); void effSpeedChange(int speed); void effTempoChange(int tempo); void effGrooveChange(int num); void tickProcess(int rest); void checkFMDelayEventsInTick(const Step& step, int ch); void checkFMNoteDelayAndEnvelopeReset(const Step& step, int ch); void envelopeResetEffectFM(const Step& step, int ch); void checkSSGDelayEventsInTick(const Step& step, int ch); void checkRhythmDelayEventsInTick(const Step& step, int ch); void checkADPCMDelayEventsInTick(const Step& step); std::vector ntDlyCntFM_, ntCutDlyCntFM_, volDlyCntFM_; std::vector ntDlyCntSSG_, ntCutDlyCntSSG_, volDlyCntSSG_; std::vector ntDlyCntRhythm_, ntCutDlyCntRhythm_, volDlyCntRhythm_; int ntDlyCntADPCM_, ntCutDlyCntADPCM_, volDlyCntADPCM_; std::vector volDlyValueFM_, volDlyValueSSG_, volDlyValueRhythm_; int volDlyValueADPCM_; std::vector tposeDlyCntFM_, tposeDlyCntSSG_; int tposeDlyCntADPCM_; std::vector tposeDlyValueFM_, tposeDlyValueSSG_; int tposeDlyValueADPCM_; void clearEffectMaps(); void clearNoteDelayCounts(); void clearDelayBeyondStepCounts(); void clearFMDelayBeyondStepCounts(int ch); void clearSSGDelayBeyondStepCounts(int ch); void clearRhythmDelayBeyondStepCounts(int ch); void clearADPCMDelayBeyondStepCounts(); void updateDelayEventCounts(); bool isRetrieveChannel_; void retrieveChannelStates(); size_t getOrderSize(int songNum) const; size_t getPatternSizeFromOrderNumber(int songNum, int orderNum) const; }; BambooTracker-0.4.6/BambooTracker/precise_timer.cpp000066400000000000000000000032371401124043500223040ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "precise_timer.hpp" #include PreciseTimer::~PreciseTimer() { stop(); } void PreciseTimer::setFunction(std::function func) { func_ = func; } void PreciseTimer::setInterval(const int microsec) { time_.store(microsec); } void PreciseTimer::start() { isContinue_.store(true); thread_ = std::thread([&] { while (isContinue_.load()) { std::this_thread::sleep_for(std::chrono::microseconds(time_.load())); func_(); } }); } void PreciseTimer::stop() { if (isContinue_.load()) { isContinue_.store(false); thread_.join(); } } BambooTracker-0.4.6/BambooTracker/precise_timer.hpp000066400000000000000000000027131401124043500223070ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include class PreciseTimer { public: ~PreciseTimer(); void setFunction(std::function func); void setInterval(const int microsec); void start(); void stop(); private: std::atomic_int time_; std::function func_; std::thread thread_; std::atomic_bool isContinue_; }; BambooTracker-0.4.6/BambooTracker/resources/000077500000000000000000000000001401124043500207535ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/resources/doc/000077500000000000000000000000001401124043500215205ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/resources/doc/doc.qrc000066400000000000000000000001631401124043500227740ustar00rootroot00000000000000 keyboard_shortcuts.html BambooTracker-0.4.6/BambooTracker/resources/doc/keyboard_shortcuts.html000066400000000000000000000076251401124043500263360ustar00rootroot00000000000000 Keyboard shortcuts

General

Key Command
Ctrl+N Create new module
Ctrl+O Open module
Ctrl+S Save module
Ctrl+P Open module property dialog
Ctrl+I Open current instrument editor
Return Play/stop song
F1 Show effect list dialog
F5 Play from start
F6 Play pattern
F7 Play from current position
F8 Stop song
Space Toggle jam/edit mode
Alt+L Focus on instrument list
Alt+O Focus on order list
Alt+P Focus on pattern editor
F12 Kill sound

Instrument list

Key Command
Insert Add instrument
Delete Remove instrument
Ctrl+I Open current instrument editor

Order list

Key Command
Ctrl+C Copy
Ctrl+V Paste
Ctrl+A Select track/all
Ctrl+D Duplicate order
Alt+D Clone order
Home Jump to first order
End Jump to last order
PageUp Jump to upper oder
PageDown Jump to lower oder
Insert Insert order below
Delete Delete order
Escape Deselect

Pattern editor

Key Command
Ctrl+C Copy
Ctrl+X Cut
Ctrl+V Paste
Ctrl+M Paste and mix
Ctrl+A Select track/all
Ctrl+G Interpolate
Ctrl+R Reverse
Ctrl+F1 Decrease note
Ctrl+F2 Increase note
Ctrl+F3 Decrease octave
Ctrl+F4 Increase octave
Alt+F9 Toggle track
Alt+F10 Solo track
Alt+S Replace instrument
Tab Jump to right track
BackTab Jump to left track
Home Jump to first step
End Jump to last step
PageUp Jump to upper step
PageDown Jump to lower step
Ctrl+Up Jump to upper 1st highlighted step
Ctrl+Down Jump to lower 1st highlighted step
Insert Insert step
BackSpace Delete the step above
Delete Delete commands
Escape Deselection
- Key off
* (numpad) Increase octave/echo buffer number
/ (numpad) Decrease octave/echo buffer number
^ Echo buffer access
BambooTracker-0.4.6/BambooTracker/resources/icon/000077500000000000000000000000001401124043500217035ustar00rootroot00000000000000BambooTracker-0.4.6/BambooTracker/resources/icon/BambooTracker.icns000066400000000000000000000206371401124043500253040ustar00rootroot00000000000000icns!info bplist00X$versionX$objectsY$archiverT$topU$null WNS.keysZNS.objectsV$class TnameTiconZ$classnameX$classes\NSDictionaryXNSObject_NSKeyedArchiverTroot#-27=CJR]dfhjlnsx}ic05 'ARGB3333̀3f3f3f3f3f3f3f33̅333݇333f333f33 3f̀3f3f3f3f3f33ff3̃33ff33ff33ff3 3f3ff3f3f3f33ff3ww"3w"3ww""wwww3w""ww"wwww3""ww"ww3"ww3"3f3"w"""3ww"3w"ww""ww3"ww"ww"ww3"ww"ww"ww3"ww"ww3"3f3""3ww"w"ww"ww""ww"ww"ww"ww"ww"ww"ww"ww "ww"ww"ww"ww3""""3ww"ww"ww"ww""ww"ww"ww"ww"ww"ww"ww"ww""ww"ww"ww"ww""""̅3333̀3f3f3f3f3f3f3f33̅333U333U̙"333U̙"33 3U̙"̀3f3f3f3f3f33"U"3ݙ33݀ݙ3f333f3f333f3f33 3f33ff3f3f3f33ff3f3̙3ff3̙fffff3̙fff3ffff3ff3f3ffff3f3ff3fff3fff3ff3ff3f3ffff3fffffffffff ffff3ffffffff3ffffffffffff#ffff̙̙f̙f̙f̙f̅3333̀3f3f3f3f3f3f3f33̅333U333UDD333UDD33 3UDD̀3f3f3f3f3f33DUD3U"33UU""UU33"UU"UU33"UU"UU3 3"UU3"3f3f3f33"3UUDDD3UDD3UUDDDUUDDDUU3UDUUDDDUUDUUD3DDDUUDDDUUD3DD UUDD3D3f3DUD3UUDD3UDUUDDDUUDD3DUUDUUDDDUUDD3DUUDUUDDDUUDD3DUUD UUDD3D3f3D3UUDDUDUUDDUUDDDUUDUUDUUDUUDDDUUDUUDUUDUUDDDUUDUUDUUD UUDD3D3UUDDUUDDUUDDUUDDDUUDUUDUUDUUDDDUUDUUDUUDUUDDDUUDUUDUUD UUDD̙Dic08 PNG  IHDRkXTPLTE333fDf3fDfffD"DwU"DfUU̙"U̻^ @IDATxْ8Мg2.BAN JKPRUz)yS\Y 1)y3_Y 1)y3eGRe\iR n0W ;wO&hSw R^1(T?Y"fq5>^_l{oM@O>}1=g4 W& ?SM b_W_g;^0=b=@)pc٧4cic7<)x~ c?~ /4v`~~@ U80oٿ|(S?)}'2&p:?d0  0 Fʾ rsKG׸L˳UӸ^ѯ =(&@&a-  1PT3ufhy 0OUyV8*o[ 4@hy 0OUyV8*o[ 4@hy 0OUy֧/%H GG?"Jɯ p&`7F6"7oH0#`1J))?epc ':!&1'oBI˸~6?+I;=QGE?ڧu$~Wa?I;]#nڹ;㇝L;]=D"A}.@ ݬM^0wAӏ{gx0i#`P88뫣>.N^.^.嫅-2lA @# 0"Rʬ5@}0&$K'/z tG @|)8y'ūQ0yp0@8Ίh~]zeӰZB& 1`LIi3i$xr⋾ZB&#;0yF̸In_-  "ΓQU]-u''@ 2dƭND5^8~#C2GюO9B}oFI]> lI =>A:jȟ_UV[͛6p V _͋}±TO@OՉ[͋A±T>}~0zV$Vu[X}{@=g5/nEUV8&'`uVbp,G`0Jj^CUzzn Nj^ bU> _@r/D7LIIɟ_t \S 4d[§i8j諅O+p >oW W }&ޢ>%N)@VLE_-|J^SxZ8 Y3})ypNg-jS 4d[§i8j諅O+p >oW W }&oB 4ߟH'ťOÇ!M&0dΏQ]\J~${œR'ᄻ'\2J(..%x=Ivq)œp `.cd_<ɞt~$I8 |'q̲K[t&:C_)8  ? K[t&:C_)8  ? K[t&:C_)8  ? K[t&:C_)8 *oZ%ޢd8 OKE?jpV x~ > 4>%/ }iX-|J^-)@V,ӰZ[SYa)yYNjSoOg§%ޢd8 OKE?jpV x~ > 4>%/ } ?i!R򓟚'yBS|O8&$FGO~jdO&H;H~N|O8ɞL%eVȫy+c X{&h l{oU[mό;W}qHN{Y[83 |v3!}g}Iۙ{g/>*?3ʸ(}[fMO6[NR\]1w3ۀ]k212>{u]@W+pH;nG9tfhmAḠ[N`w|YyOY}  :N ~BHSq.*@ό+tP7xf\ ` ,3 ]+Td1 WX$)n9vXvO5>$y`b! T ^ `̸B'n ,f+dFN YVȌ+tbBf\7xH32 +@W ^ `̸B'n ,f[gXCGǤx 1h@cv6IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/BambooTracker.ico000066400000000000000000003066561401124043500251320ustar00rootroot00000000000000 -V (N.@@ (Bv6  x hFPNG  IHDR\rfsRGBgAMA a pHYsod-IDATx^эfrA.ȅr] ¸ dK. r -d܍$`\pS}e/iM&TZse*㗡DJknL57QҚ?~jiM&TZse*㗡DJknL57QҚ?~jiM&TZse*㗡DJknL57QҚ?~jiM&TZse*㗡DJknL57QҚϟ?5=Ǐ5'giIYZsz֜5'giIYZЛ,9I=KkNRҚԳ$,9I=Kkz5'giIYZsz֜5'giBoг$,9I=KkNRҚԳ$,YMz֜5'giIYZsz֜5 @ҚԳ$,9I=KkNRҚԳf7]?YZsz֜5'giIYZsz,=KkNRҚԳ$,9I=KkNRҚthh57QҚ(S?럿?/i&#~2tiJknL57QңLK4?~457QҚ(S}&zG_Ɉ ]Қ(SiM>=ң/dď_.MiM&Tzt}їf2/C&TZse*=DK3㗡KSZse*2gG_zХ)2DJ3ѣ/=LFeҔDJknLGSztGP/ۯi&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&#~2tiJknL57Qң/=ң^fї}i&?+I?~%9I=KkNRҚԳ$,YQ~NRң^(sMz֜5'giIYZУ+=ړԳ\z5'giIYZsz,J$,=2t}giIYZsz֜5 =ң=I=Kz̥7]?YZsz֜5'giBhORң^(sMz֜5'giIYZУ+=ړԳ\z5'giIYZsz,J$,=2t}giIYZsz֜5 =ң=I=Kz̥7]?럟ڿ_%e.EiMt;sz\o5L4KoGQZse*yΜ;Wz5L4KoGQZse*yΜ;Wz5L4KoGQZse*yΜ;Wz5L4KoGQZse*yΜ;Wz5L4KoGQZse*yΜ;Wz5L4KoGQZse*yΜ;Wz5L4KoGQZse*yΜ;Wz5m4jֿ~4RQDJwtgJwn;Y5 4jZj)M(JknL;Y3;Pҝ,LFZ ]B5KC- &z57Qҝ,ݙҝ[fNdi&#~|MC-. mZ|=Қ(SNL-Tt'Kw4?BPPKC-eEiMt'KwtY;YɈ_PKKC[fi2D&T;Ss ,ҝ,dďiХ-T4PKoGQZse*ҝ)ݹjdNf24RYji7ѣ(2dΔB5Kwt'K3kj)tih , 4RQDJwtgJwn;Y5 4jZj)M(JknL;Y3;Pҝ,LFZ ]B5KC- &z57Qҝ,ݙҝ[fNdi&#~|MC-. mZ|=Қ(SNL-Tt'Kw4?BPPKC-eEiMt'KwtY;YɈ_PKKC[fi2D&T;Ss ,ҝ,dďiХ-T4PKoGQZse*ҝ)ݹjdNf24RYji7ѣ(2dΔB5Kwt'K3kj)tih , 4RQDJwtgJwn;Y5 4jZj)M(JknL;Y3;Pҝ,LFZ ]B5KC- &z57Qҝ,ݙҝ[fNdi&#~|MC-. mZ|=Қ(SNL-Tt'Kw4?BPPKC-eEiMt'KwtY;YɈ_PKKC[fi2D&T;Ss ,ҝ,dďiХ-T4PKoGQZse*ҝ)ݹjdNf24RYji7ѣ(2dΔB5Kwt'K3_PK. $, m'㗴$,9I=Kk;uzBKo>'җ֜5'giBwtNRҝ[(sM$]ҚԳ$,YNIYs e.2K_Zsz֜5 ݉ҝ:I=Kwn̥7]?PtKkNRҚԳf;QS'g-|.}iIYZsz,t'Jw$,ݹ2t}|@Oҥ/9I=KkNRҚDN;Pқ(I5'giIYZН(ݩԳt\zcKk|}~Ak5'LJgRI)SgN̥;Y_SPNRPKC-e>IfGQZsΤt&2zIY\5* $, 4RtKkz5'LJgRI)SgN̥;Y_SPNRPKC-e>IfGQZsΤt&2zIY\5* $, 4RtKkz5'LJgRI)SgN̥;Y_SPNRPKC-e>IfGQZsΤt&2zIY\5* $, 4RtKkz5'LJgRI)SgN̥;Y_SPNRPKC-e>IfGQZsΤt&2zIY\5m4j.Z\B=z5=μ4|=2B5K{~PЙ5 4jZj)sfhO \ї֔T:L0jTTs ,ACg64RYjaŚ=-sG_ZSSK3y'S)S-Teو_PKKC[fq2kPυ}iMiO3/3fhOL;PҞ9tf#~|MC-. mšZ\B=z5=μ4|=2B5K{~PЙ5 4jZj)sfhO \ї֔T:L0jTTs ,ACg64RYjaŚ=-sG_ZSSK3y'S)S-Teو_PKKC[fq2kPυ}iMiO3/3fhOL;PҞ9tf#~|MC-. mš5 |G_:R$yqOL3)\fiIGZ ]B5C- AkB=BtfI:BJgRPҞ0ҙ5 4jZքz.34}Jt=2Τs=?`&3kj)tih ,0 \gh =ҙ2?`'̋{ e*IB5K{~LJg>4RYjaZ`z3+e~OҙT:Rυj|ďiХ-T8P&s/KgV=3/)t& ,3)_PKKC[fq>hM=C3_їά{>Ig^S(SLJ=YfR:?BPP C}КP{fУ/Y)|μPҙz.T̤t#~|MC-. mš;i'gRBg =KwtgJ{Zf1S!SGZ ]B5C- wLOң/Τl=zdΔPbBҙ5 4jZ酪G_Ia ق{*,ҝ)iLL3kj)tih ,0I3=I8ž:TY;SB5 Jg>4RYjafz}q&=/tf гt'Kwj32|ďiХ-T8P'$=L {^SgNLiO ,f*d*_PKKC[fqNIzřЙ-Bҝ,ݙҞYTT:?BPP C4ӓ3)y3[pO;Y3=-Tt擟?+4M~z>`h e>Ǐ5'iK[ Dw"$|О:қ(Iz5'iG_z Dw"$|О:қ(Iz5'iG_z Dw"$|О:қ(Iz5'iG_z Dw"$|О:қ(Iz5'iG_z Dw"$|О:қ(Iz5'iG_z Dw"$|О:қ(Iz5'iG_z Dw"$|О:қ(Iz5'iG_z Dw"$|О:қ?FKqz4RRfLKgZZSyz\3k UIYji7ѣ/3љδ$,t'Kg>4Գ4PKoG_g3-iiMIYsN|ď)Ti('gi2D4DgZ:ҚRϓԳҝ,_SPNRPKC-e}iδt5'giϥ;Y:?PZ|=L?iLKkJ=ORҞKwt#~|MJC9I=KC- &z~&:ҙ֔z=dGBrzZj)MK3Lt3-)I$,4Ig2ARϓA{LKo>'ї֜ęf;L&Tyz>hiM$=Қ8'J=ORy3-2G_Zsg3 SI=t7]?PKkNB3t&d*h $|Оz7]?QОNRy=B5C{:I=dMGT3A{^fhO'PОNRy7Yz $|ОI=/T3A{Mt}|D5C{:I=jtz>h $|Оz7]?QОNRy=B5C{:I=dM#sC#NRҡZYIYTTYIYTTYIYTTz#~|MJC9I=KC- TԳTԳTԳ&GBrzZjfiO'g)S)SfiO'g)S)SfiO'g)S)SM5* $, 4RҞNRRRRҞNRRRRҞNRRRқk UIYji=LL=LL=LL794Գ4PK5K{:I=KJJ5K{:I=KJJ5K{:I=KJJorď)Ti('gijtz22jtz22jtz22_SPNRPKC-,$,e*e*,$,e*e*,$,e*e*?MBP?_ZTYB5 JJ=K{^f1S!S)Sgi ,f*d*?BPP C-e*,yLLL=/TԳj32_PKKC[fq2zPbBRRҞYTTTYB5 JorďiХ-T8PKJ=K{^f1S!S)Sgi ,f*d*e*,yLL794RYjaL=/TԳj322zPbBқkj)tih ,0RRҞYTTTYB5 JJ=K{^f1S!SM5 4jZj)Sgi ,f*d*e*,yLLL=/T&GZ ]B5C- Գj322zPbBRRҞYTTz#~|MC-. mšZTYB5 JJ=K{^f1S!S)Sgi ,f*d*?BPP C-e*,yLLL=/TԳj32_PKKC[fq2zPbBRRҞYTTTYB5 JorďiХ-T8PKJ=K{^f1S!S)Sgi ,f*d*e*,yLL794RYjaL=/TԳj322zPbBқkj)tih ,0RRҞYTTTYB5 JJ=K{^f1S!SM5 4jZj)Sgi ,f*d*e*,yLLL=/T&GZ ]B5C- Գj322zPbBRRҞYTTz#~|MC-. mšZTYB5 JJ=K{^f1S!S)Sgi ,f*d*?BPP C-e*,yLLL=/TԳj32_PKKC[fq2zPbBRRҞYTTTYB5 JorďiХ-T8PKJ=K{^f1S!S)Sgi ,f*d*e*,yLL794RYjaL=/TԳj322zPbBқkj)tih ,0RRҞYTTTYB5 JJ=K{^f1S!SM5 4jZj)Sgi ,f*d*e*,yLLL=/T&GZ ]B5C- Գj322zPbBRRҞYTTz?PJk0sL5I9PҚ$(Si̓t#9PҚ$(Si̓tzA{:Hor(Si̓tzA{:I=TZ=7zA{:I=TZ=e*yОқ\||D=TZ=e*yОNRρ2e*yОNRρ2 if_arrow_redo_4989.png if_arrow_undo_4998.png if_cog_5175.png if_cut_red_5249.png if_disk_5276.png if_folder_5362.png if_page_white_5568.png if_page_white_copy_5579.png if_paste_plain_5629.png if_resultset_last_5687.png if_resultset_next_5688.png if_shape_square_5750.png if_table_edit_5800.png inst_ssg.png inst_fm.png BambooTracker.ico iconfinder_add_4941.png iconfinder_cross_5233.png iconfinder_page_copy_5551.png iconfinder_page_white_get_5593.png iconfinder_page_white_put_5609.png iconfinder_pencil_5631.png iconfinder_stop_5785.png iconfinder_arrow_down_4982.png iconfinder_arrow_up_4999.png iconfinder_asterisk_yellow_5001.png iconfinder_page_add_5548.png iconfinder_page_delete_5552.png iconfinder_textfield_rename_5877.png inst_adpcm.png inst_kit.png iconfinder_wrench_orange_5931.png iconfinder_shape_flip_horizontal_5740.png iconfinder_magifier_zoom_out_5483.png iconfinder_magnifier_zoom_in_5485.png iconfinder_eye_5333.png iconfinder_chart_line_edit_5152.png BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_add_4941.png000066400000000000000000000013351401124043500260240ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<oIDAT8˥Ka[/Y()%X(olNۖskn.-h;8fEP"jïMGˈ}yພ羹$I.tulu AX:𼂒ZHh1DnZJOJB{Z?`2`S=N$ő=;a &jw qJG#<"N2h8޵`6xցn_+ ~Zto}`x%XЛ͈ hXѿƻ/}BJ_G&|Qr-6Aރ EL⬡\U3:WUh[C6+ 6.f *K͸ܝFq ou4܄?d|XҥMvD` *_[ #A20liR|xq`4w=\uQ m+G|%$5Թ5RO*YGMUO Gqj4ְ(X& s1c˭(LVf RdjQ '-1ATA>U j4,pV"4L$e@.ArBY a~myY])Q8tNLܞt2"I o=CSd)__AF(IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_arrow_down_4982.png000066400000000000000000000005731401124043500274650ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe< IDAT8˥?KQi H*.EA!XX vc!v{;,ӻ\}Ï7Ibc0u2B^h gw`4+@jɥ s5"H hPǁh"H.EBFݜ& 5EɃZn+;l8r9~c$ h>*k>hLLjƳRwUd:<$hݸIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_arrow_up_4999.png000066400000000000000000000005641401124043500271520ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥?/Qϲhg5j%'F,[ LH 3(vw=fcoN{O~9VM4Q7ܿ)v/WQ=&bpSO ^'&^:\˨6eND!& 9꒣_|?\ srx,g*,(F#d[OaAA*P p1O+C$`)*w`A#0$ *?b&NRIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_asterisk_yellow_5001.png000066400000000000000000000013471401124043500305030ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<yIDAT8˥KTaƟa-j9ijD ETQ@1 I&V);D-sD"̏QgRk9w5Z<9/Hb! ʼp,Ͼ3N?j5G+C.f%L*o3M`)8D{F[l9Z͹U}sud43bQ[RoIvr7ޤ q5c\|z*a$][ ,[RI"l12x'|FjaY/r A*f?LE ELwChP_ c5Pp)9fA7s 9/L~>7dS!{/V?@l*ydLކ ]Gp*{" 3tRI}/7K VtV.ۥPc >o"4TSH{Z6,<9 F}pL*f<҆-B]+,I+.~'sl#!lZuZѺl+wx !BNJH/:ePm%𴸗 L:%IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_chart_line_edit_5152.png000066400000000000000000000013161401124043500304030ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<`IDAT8c?%,M_| zw_3:*WrlNC/-좕B'{ u_a46ҽbߡE%D47;ٻƩ;8ˣ}>6[ӕS@*Z Qk>~͵hB\9uxZvYb J Cيٽ?BYvn&kft$,d9Zap\^ Y7 QJF 9=Q4 ؜Io SBpsI) Fv(@yՎވc\@ %% Z2h'@d(<|áaJuM@O⤁LGjd!X8Af 5J i K->w62ƾWH}:mP]XB0QX=ib_g=!Ftt…clrIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_eye_5333.png000066400000000000000000000013561401124043500260550ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8SKOQt:}L!-,@pcTFcT6,L4#\ 66ntF+DDn}Qܹ3z[!q“|{/߹"QBC=_t}R2|ۀ#S<~/~RWeB`B>۲qu:Fashl]?䷮Ҁ&LDmDX[tģ+ڈfl h*a`9ee;iՐ{DOx' 06m`yV&8nC >LɶU[nER*Ѡt 8 )Z,znq-r9EQY|PK V욃~p)ZmhE3&Q(6lc al0%l2$'ZM6}b h1\SUPa!Fp&۔)A>Zcbfrܟ;S D#фzT/_lsX0i#`>{nm%FȮ{O=ۏsu#R^j$Są69|Lw< tH.8iNK<#|fUoml`eAIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_magifier_zoom_out_5483.png000066400000000000000000000012211401124043500310060ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<#IDAT8˥oHae^'", ZD(z^XًQFT(ˎgednn?Ά9w^6fv݊`E֋{xT?Nj^fZ&la σBIFx"Ȥ@%d0) LRԗ<i&g.;pDddfD<lY ֱ/hbRT7%`8*|1ZD؊uߘ2*\n: ym|nێ Z6U:!#ʭC;ێdwx}3֋HԬDIN?ɂTd 2OzsQ^.C1H"勻-)>El4:Dokn H(_L@qw4W4M.LXzn zG,iˏBlSopN㋷荔x 0[;Z[M[Ol6wYá_TXmXcBzXP@V}np t[c⚲|mEIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_magnifier_zoom_in_5485.png000066400000000000000000000012501401124043500307670ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<:IDAT8˥[hqƇ]D]YhXtUXZjHc!$D LkXk[+ۚ}n$sjtI}/oɯ3kݦ%4(0~ _ <=qfz!xWAԮl4seUjbHk]Y͝˗:te%k~IIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_add_5548.png000066400000000000000000000013431401124043500270230ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<uIDATMU=ߙ;cYA!Y؟H*FEnZ)U-KTrYbj?DVN){ʅt% }6q2=V}.vDžw}z᷶/ bPfvf`džY:LI@A JD(JΜ#w o^7=  BFh}RBdq\ֆX=)jCL5QB ZBP!AD6&:@ч,!dLP Z]ABP$)H ~bԜ/G&FdHВ:}soUkZq7+qj^a(d4'?/g:x#m;+ǽgvf}7->hgߞ1+qkKENݯ_4K'S5 XcZ*JJ#~0[@jӲ^~18--&;T߶řlw^~p=,+2tD] "(669]f߅I@A JD(JwɆ^߹V7=  BFh}RBd4jcS1Xe }O$hɍ#N}ྦྷ3K?}bB8L A##=}-`xU6o_ qu]ђ>郖[6༮ akrԋoZ%Wλ1G_4\::]*,/_w4E+;}V IL}oG)g$[?/`/} -%nQP`IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_white_get_5593.png000066400000000000000000000010041401124043500302440ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕MKQM!Z M [.PladdFqѢ@ ms5?0ssaka`0%0Xl4f# %Hj ۔J% iYJ :$$ Z $ORX,"A@D"Y# (P.f{ZjD!`m7;JdA7j UB jfB z.υ$ĿT C] (@Np>]P8c6 #֍+ޥ/>?>/'nqgջ7lQ+LGՂAsXzߋkf껰Ȇij}/bg<7G!Z#ɠlrs`%Co4tQЛ1IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_page_white_put_5609.png000066400000000000000000000010131401124043500302730ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕KKaE"!hQAIe1- J \ jBA Rh-\HhA(::ۜ309g822P h0,0@ YU8.U**rv%@C\.eki$ID"Qx$tI@ Ţx',RѨ(0 #JZ 2 bXKKՙBL}KqP(YL*y$jfB\HB-*vױ}o9 ӭBT%h (L<ѧǨvdx:ٿ {Њ7'\;TS;s`ۄ#\0?vaV2+:.ŷw - ZeR:ؕߓֱ+(\ҴphIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_pencil_5631.png000066400000000000000000000007021401124043500265400ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<TIDAT8˭?Hq][ p@\hZr0+ʐܲHlHH*$:2,S *A = _B\x7ÖZc<oozڈBЊ" 9!B!>gvڀ,;SRNXjD|D62 ( 8`~vPOoCvs 8 ]|aɄ[ z/Nİ*tuXİ*QT.;kDbՀ̱ <ݏy#~=庠q]}fĝZtlT3R%~>#w -;06m7wU\&IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_shape_flip_horizontal_5740.png000066400000000000000000000006231401124043500316540ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<%IDAT8=JQ31>4-M .z nf@D .BA4)Li)I9‰`Y;1OJ3ݍma9 =1!cʓM2y^$&Ak&J)LccJNv#DF- PVʝUkrL X̔nf6Co ? 0 c J)H) g'ι-x [@(űGqn,'4j'kɱyY rdB$sޭ8؟IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/iconfinder_wrench_orange_5931.png000066400000000000000000000011101401124043500301040ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕kSQ8H)N]uA,wjazcbb6& Qcn^b4b2e l xW }$9Xt:$n"0T8J)N!h4ʉF|p8|2X,f[͵`0S~OZ% } iz=nHXt:4 "BrLRtv_T'-lu&f_uRl6 u]e 5~lޢnz{ejjUiw|}?$&x r|TN%*4Ç l;'T^ٗG <"pFୌw!yhݔeE6  ܙצ PJ:C\DW^,<<4 A ѹ]s6ޑIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_arrow_redo_4989.png000066400000000000000000000011611401124043500257260ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥OHQ7Zcf9UԢت fdPlQ RȕnE"à6e-r#apd6Yc|lDe{yDQؿ<>3;uDٖ47KgAg)g$ujUO 2kp s܍ { t?<(7YՔLX]`$7id? 6 \O_ٿ눊>IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_cog_5175.png000066400000000000000000000010001401124043500243070ustar00rootroot00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(UQMKa^ tT2 .EQС:EeQPR麖QRq%!O^wm!c333 To^%ۚ\Y|gz),{ b,پ7g| fʨ'TǂLGUM W0FDNC \`BASJk4$0SrI ZHd (cO:/B&69I#M:X61đ|hK5AJTȐ9Zf) 9+<OQ  **4KrHk?WDj L>c!i9BL%^0n9ֈs>i ֓i붻k>Gj ]IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_cut_red_5249.png000066400000000000000000000012121401124043500251730ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8c?.=йnBI03vEf3Db'r"'Lb#U^.8Y&6Mǻai n^S)S6,9:yV h__sSb}>wՇ)&e@pƖsWHZЭ7^kZYMt lMskHNSl3t=7? jIS'24`3n'qXMdayVgHg4఍ћzAR3/8/]|5ԿB~7/VZ;iot꡻^hdrѧ6.҂' q(`5`$q[灊VkojآVN2Sڙc }lj]܌ lܽnrcпe}N`Lɠ+?TrIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_disk_5276.png000066400000000000000000000011541401124043500245050ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT=]Uе;C`DD Ve h*B"6VV"c vba"NB ޙ{~jo|x&,K*o=]߻_v<}tu.R[Ҝ9Xsy|o~ |u_Bk6Yf7bQڴ^Й<T::֊Ǧ7Uݕuwٞ ,6}4'kٞF%*T⏿NHZ'}&NGۯ_鋾ﴌ*UD:tHُZkƦ/F%8A#хJpFQ$Q5:H0' QEDDK$e]t4IJDUS*!BxKOƈ5Hup9Q pҫj'ɬ4nx93W3j7˜oO$*yHI$H=m?!|nmIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_folder_5362.png000066400000000000000000000010311401124043500250140ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8œA[EtQg7wALCWA0P017p2=:A3cb2'pܪ$Vme@ ݬ2OTO1W/`z8% ;O;9P#9B}^nO;Ǫo~ d~E dpɳ__jMMIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_paste_plain_5629.png000066400000000000000000000011351401124043500260530ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍ?hSQ//&%h?IDH ., .\B7ApN.:H:b'KEjim޻|ɋi{n$ s私O FT*V:z;z-/A@Xz晱R$a]$1sq HXidgAĖP8! \n#<𴃧 '">`bPw |?2MZrw 3R$$ 8m6K}!E(8nt:HY_ !ׁ9[PV^ Pp xR1.; ,;ہ!(>$D;x&)BSDVgwxvu \yv*)DQ4q6;;33;0Ezdk7IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_resultset_last_5687.png000066400000000000000000000010141401124043500266310ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8œkQ7; *,E/A^TI%XX,,?H6ɨA QL&q>X&_q81)%v]j1W٫G ;E)}z>wOu{G%lj٘=*l__v$>lIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_resultset_next_5688.png000066400000000000000000000006131401124043500266510ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8c?%4~nv xf%e@gמx}Ë Sl@ނGWK_%<\dD`Z~'UKX#꺝([m/ػ@J:t6%y]Dr4JDo]x濨d%$1ED*뿀IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_shape_square_5750.png000066400000000000000000000005411401124043500262270ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8œ1N@DNJ13$$\r;]^kl4Ngv'{wR(Q˴3x~zDĀ& |^hTtDI%¥V9X$ p1lĠR47/u6!ͣAZtl3֠*kFrKzW9 P`XquguB`~#NH>k$X =o7oWem,OIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/if_table_edit_5800.png000066400000000000000000000013501401124043500256360ustar00rootroot00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<zIDAT8˥YHQeƲ1k2 6+mq Z^" "|Z Az A_ ,*u&\(4BetԱt(AQtss 5lkGOk UR @( 4B["F*HnPZ K7>nL0Jt_A(W \NW)w'2i@܇Uo1+R*|wdmKGmdG:7CoHn`D^1{Rs61i5m.T(-]?ei0#b#sjk8]oԄ ؖBj^9H 6􌉁V1wo6&PV`P&eP b/<0W#E,dm55IO2ʼnf3l)8WoPq-ӠiWG'Dz^*,f*o̫S" 2kt/Fߴ]RInb{ưL2.aRM*(%'HGM>Խ)[x6E`&8;N]^."9{;S LLV7 IIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/inst_fm.png000066400000000000000000000002151401124043500240460ustar00rootroot00000000000000PNG  IHDRaTIDAT8Ocd0Ra8_?zx0~a 1EY!l066[,d/\B5/P 7EHnL3d@IENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/inst_kit.png000066400000000000000000000002071401124043500242340ustar00rootroot00000000000000PNG  IHDRaNIDAT8Ocd0Ra8^?(o0qX+D7fP ux @. &za4%RF\RIENDB`BambooTracker-0.4.6/BambooTracker/resources/icon/inst_ssg.png000066400000000000000000000002251401124043500242410ustar00rootroot00000000000000PNG  IHDRa\IDAT8Ocd0Ra HBFAȚ`|5"lE2%aNG[X,#:] pIHd(D4IENDB`BambooTracker-0.4.6/BambooTracker/resources/resources.pri000066400000000000000000000003771401124043500235100ustar00rootroot00000000000000# QMake wrapper over compiled-in Qt Resource definitions RESOURCES += \ $$PWD/doc/doc.qrc \ $$PWD/icon/icon.qrc # Platform-specific app icon win32 { RC_ICONS = $$PWD/icon/BambooTracker.ico } else:osx { ICON = $$PWD/icon/BambooTracker.icns } BambooTracker-0.4.6/BambooTracker/song_length_calculator.cpp000066400000000000000000000142531401124043500241720ustar00rootroot00000000000000/* * Copyright (C) 2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "song_length_calculator.hpp" #include #include #include #include "module.hpp" #include "effect.hpp" #include "enum_hash.hpp" namespace { inline double calculateStrictStepTicks(int rate, int tempo, int speed) { return 2.5 * rate * speed / tempo; } } SongLengthCalculator::SongLengthCalculator(Module& mod, int songNum) : mod_(mod), songNum_(songNum) { } double SongLengthCalculator::approximateLengthBySecond() const { Song& song = mod_.getSong(songNum_); std::unordered_set visitedOrder; double tickCnt = 0.; const int rate = static_cast(mod_.getTickFrequency()); int tempo = song.getTempo(); int speed = song.getSpeed(); std::vector groove = mod_.getGroove(song.getGroove()); double stepTicks = calculateStrictStepTicks(rate, tempo, speed); size_t grooveIdx = 0; bool isTempo = song.isUsedTempo(); std::vector attribs = song.getTrackAttributes(); int orderNum = 0; int stepNum = 0; int maxOrder = static_cast(song.getOrderSize()); while (!visitedOrder.count(orderNum)) { visitedOrder.insert(orderNum); int maxStep = static_cast(song.getPatternSizeFromOrderNumber(orderNum)); std::unordered_map jumpEffMap; for (; stepNum < maxStep; ++stepNum) { std::unordered_map speedEffMap; // Load effects for (const TrackAttribute& attrib : attribs) { Step& step = song.getTrack(attrib.number).getPatternFromOrderNumber(orderNum).getStep(stepNum); for (int e = 0; e < Step::N_EFFECT; ++e) { const Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(e)); switch (eff.type) { case EffectType::SpeedTempoChange: case EffectType::Groove: speedEffMap[eff.type] = eff.value; break; case EffectType::PositionJump: case EffectType::SongEnd: case EffectType::PatternBreak: if (stepNum == maxStep - 1) jumpEffMap[eff.type] = eff.value; // Read only last step break; default: break; } } } // Update playback state auto&& it = speedEffMap.find(EffectType::SpeedTempoChange); if (it != speedEffMap.end()) { if (it->second < 0x20 && speed != it->second) { speed = it->second; isTempo = true; stepTicks = calculateStrictStepTicks(rate, tempo, speed); } else if (tempo != it->second) { tempo = it->second; isTempo = true; stepTicks = calculateStrictStepTicks(rate, tempo, speed); } } for (const auto& eff : speedEffMap) { switch (eff.first) { case EffectType::Groove: if (eff.second < static_cast(mod_.getGrooveCount())) { groove = mod_.getGroove(eff.second); isTempo = false; grooveIdx = 0; } break; default: break; } } // Add step ticks if (isTempo) { tickCnt += stepTicks; } else { tickCnt += groove[grooveIdx]; ++grooveIdx %= groove.size(); } } // Update order position ++orderNum %= maxOrder; stepNum = 0; for (auto eff : jumpEffMap) { switch (eff.first) { case EffectType::PositionJump: if (eff.second < maxOrder) { orderNum = eff.second; } break; case EffectType::SongEnd: orderNum = 0; // To break order loop break; case EffectType::PatternBreak: if (eff.second < static_cast(song.getPatternSizeFromOrderNumber(orderNum))) { stepNum = eff.second; } break; default: break; } } } // Calculate time by seconds return tickCnt / rate; } void SongLengthCalculator::totalStepCount(size_t &introSize, size_t &loopSize) const { Song& song = mod_.getSong(songNum_); std::vector attribs = song.getTrackAttributes(); size_t totalStepCnt = 0; std::unordered_map stepCntLogMap; int lastOrder = static_cast(song.getOrderSize()) - 1; int curOrder = 0; for (int curStep = 0; !stepCntLogMap.count(curOrder); ) { // Count up size_t ptnFullSize = song.getPatternSizeFromOrderNumber(curOrder); stepCntLogMap[curOrder] = totalStepCnt; totalStepCnt += (ptnFullSize - curStep); // Check next order position for (const auto& attrib : attribs) { const Step& step = song.getTrack(attrib.number) .getPatternFromOrderNumber(curOrder).getStep(ptnFullSize - 1); for (int i = 0; i < Step::N_EFFECT; ++i) { Effect&& eff = effect_utils::validateEffect(attrib.source, step.getEffect(i)); switch (eff.type) { case EffectType::PositionJump: if (eff.value <= lastOrder) { curOrder = eff.value; curStep = 0; } break; case EffectType::SongEnd: // No loop introSize = totalStepCnt; loopSize = 0; return; case EffectType::PatternBreak: if (curOrder == lastOrder && eff.value < static_cast(song.getPatternSizeFromOrderNumber(0))) { curOrder = 0; curStep = eff.value; } else if (eff.value < static_cast(song.getPatternSizeFromOrderNumber(curOrder + 1))) { ++curOrder; curStep = eff.value; } break; default: break; } } } } // Calculate counts introSize = stepCntLogMap[curOrder]; loopSize = totalStepCnt - introSize; } BambooTracker-0.4.6/BambooTracker/song_length_calculator.hpp000066400000000000000000000026121401124043500241730ustar00rootroot00000000000000/* * Copyright (C) 2020-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include class Module; class SongLengthCalculator { public: SongLengthCalculator(Module& mod, int songNum); double approximateLengthBySecond() const; void totalStepCount(size_t& introSize, size_t& loopSize) const; private: Module& mod_; const int songNum_; }; BambooTracker-0.4.6/BambooTracker/tick_counter.cpp000066400000000000000000000070341401124043500221420ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "tick_counter.hpp" TickCounter::TickCounter() : isPlaySong_(false), tempo_(150), // Dummy set tickRate_(60), // NTSC nextGroovePos_(-1), defStepSize_(6) // Dummy set { updateTickDifference(); } void TickCounter::setInterruptRate(uint32_t rate) { tickRate_ = static_cast(rate); updateTickDifference(); } void TickCounter::setTempo(int tempo) { tempo_ = tempo; updateTickDifference(); tickDiffSum_ = 0; resetRest(); } int TickCounter::getTempo() const noexcept { return tempo_; } void TickCounter::setSpeed(int speed) { defStepSize_ = speed; updateTickDifference(); tickDiffSum_ = 0; resetRest(); } int TickCounter::getSpeed() const noexcept { return defStepSize_; } void TickCounter::setGroove(const std::vector& seq) { grooves_ = seq; if (nextGroovePos_ != -1) nextGroovePos_ = 0; } void TickCounter::setGrooveState(GrooveState state) { switch (state) { case GrooveState::ValidByGlobal: nextGroovePos_ = static_cast(grooves_.size()) - 1; resetRest(); break; case GrooveState::ValidByLocal: nextGroovePos_ = 0; resetRest(); --restTickToNextStep_; // Count down by step head break; case GrooveState::Invalid: nextGroovePos_ = -1; resetRest(); break; } } bool TickCounter::getGrooveEnabled() const noexcept { return (nextGroovePos_ != -1); } void TickCounter::setPlayState(bool isPlaySong) noexcept { isPlaySong_ = isPlaySong; } int TickCounter::countUp() { if (isPlaySong_) { int ret = restTickToNextStep_; if (!restTickToNextStep_) { // When head of step, calculate real step size resetRest(); } --restTickToNextStep_; // Count down to next step return ret; } else { return -1; } } void TickCounter::updateTickDifference() { float strictTicksPerStepByBpm = 10.0f * tickRate_ * defStepSize_ / (tempo_ << 2); tickDiff_ = strictTicksPerStepByBpm - static_cast(defStepSize_); } void TickCounter::resetCount() { restTickToNextStep_ = 0; tickDiffSum_ = 0; } void TickCounter::resetRest() { if (nextGroovePos_ == -1) { tickDiffSum_ += tickDiff_; int castedTickDifSum = static_cast(tickDiffSum_); restTickToNextStep_ = defStepSize_ + castedTickDifSum; if (restTickToNextStep_ < 1) restTickToNextStep_ = 1; // Prevent wait count changing to 0 (freeze) tickDiffSum_ -= castedTickDifSum; } else { restTickToNextStep_ = grooves_.at(static_cast(nextGroovePos_)); nextGroovePos_ = (nextGroovePos_ + 1) % static_cast(grooves_.size()); } } BambooTracker-0.4.6/BambooTracker/tick_counter.hpp000066400000000000000000000037521401124043500221520ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include enum class GrooveState { ValidByGlobal, ValidByLocal, Invalid }; class TickCounter { public: TickCounter(); void setInterruptRate(uint32_t rate); void setTempo(int tempo); int getTempo() const noexcept; void setSpeed(int speed); int getSpeed() const noexcept; void setGroove(const std::vector& seq); void setGrooveState(GrooveState state); bool getGrooveEnabled() const noexcept; void setPlayState(bool isPlaySong) noexcept; /// Reuturn: /// -1: not tick or step /// 0: head of step /// 0<: rest tick count to next step int countUp(); void resetCount(); private: bool isPlaySong_; int tempo_; int tickRate_; std::vector grooves_; int nextGroovePos_; int defStepSize_; int restTickToNextStep_; float tickDiff_; float tickDiffSum_; void updateTickDifference(); void resetRest(); }; BambooTracker-0.4.6/BambooTracker/utils.hpp000066400000000000000000000101531401124043500206120ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Rerrah * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include namespace utils { template inline T clamp(T value, T low, T high) { return std::min(std::max(value, low), high); } template inline auto find(InputContainer& input, const T& value) { return std::find(input.begin(), input.end(), value); } template inline auto findIf(InputContainer& input, Predicate&& pred) { return std::find_if(input.begin(), input.end(), std::forward(pred)); } template , class InputIterator, class Predicate> inline auto findIndicesIf(InputIterator first, InputIterator last, Predicate pred) { using T = typename OutputContainer::value_type; OutputContainer idcs; for (auto it = first; (it = std::find_if(it, last, pred)) != last; ++it) { idcs.push_back(static_cast(std::distance(first, it))); } return idcs; } template , class InputContainer, class Predicate> inline auto findIndicesIf(const InputContainer& input, Predicate&& pred) { return findIndicesIf(input.begin(), input.end(), std::forward(pred)); } template