├── .gitignore ├── VST ├── FileOpenDialog.hpp ├── CMakeLists.txt ├── VSTEditor.hpp ├── VSTBackend.hpp └── AudioGroupFilePresenter.hpp ├── Editor ├── platforms │ ├── mac │ │ ├── mainicon.icns │ │ └── Info.plist │ ├── win │ │ ├── mainicon.ico │ │ ├── amuse-gui.rc │ │ └── amuse-gui.manifest │ └── freedesktop │ │ ├── 128x128 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── 16x16 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── 256x256 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── 32x32 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── 48x48 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── 512x512 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── 64x64 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── 1024x1024 │ │ └── apps │ │ │ └── amuse-gui.png │ │ ├── amuse-gui.desktop │ │ ├── CMakeLists.txt │ │ └── mkqticon.c ├── resources │ ├── translation_res.qrc │ ├── resources.qrc │ ├── IconStop.svg │ ├── IconRemove.svg │ ├── IconBack.svg │ ├── IconForward.svg │ ├── IconBackDisabled.svg │ ├── IconForwardDisabled.svg │ ├── IconAdd.svg │ ├── IconVolume0.svg │ ├── IconSoundMacroTarget.svg │ ├── IconSoundMacro.svg │ ├── IconSoundMacroTargetDisabled.svg │ ├── IconSoundMacroDelete.svg │ ├── IconSoundMacroDeleteHovered.svg │ ├── IconGroup.svg │ ├── IconNew.svg │ ├── IconADSR.svg │ ├── IconVolume1.svg │ ├── IconSample.svg │ ├── IconLayers.svg │ ├── IconNewSoundMacro.svg │ ├── IconVolume2.svg │ ├── IconNewGroup.svg │ ├── IconCurve.svg │ ├── IconNewADSR.svg │ ├── IconVolume3.svg │ ├── IconNewLayers.svg │ ├── IconNewCurve.svg │ ├── IconSoundGroup.svg │ ├── IconA.svg │ ├── IconB.svg │ ├── IconKeymap.svg │ ├── IconOpen.svg │ ├── IconSongGroup.svg │ ├── IconNewSoundGroup.svg │ └── IconNewSongGroup.svg ├── MacOSExtras.mm ├── NewSoundMacroDialog.hpp ├── CurveEditor.hpp ├── MIDIReader.hpp ├── Common.hpp ├── ADSREditor.hpp ├── StatusBarWidget.hpp ├── Common.cpp ├── KeyboardWidget.hpp ├── main.cpp ├── StatusBarWidget.cpp ├── KeymapEditor.hpp ├── SampleEditor.hpp └── LayersEditor.hpp ├── include ├── amuse │ ├── VolumeTable.hpp │ ├── SongConverter.hpp │ ├── N64MusyXCodec.hpp │ ├── EffectBase.hpp │ ├── IBackendSubmix.hpp │ ├── amuse.hpp │ ├── Listener.hpp │ ├── Envelope.hpp │ ├── Entity.hpp │ ├── Studio.hpp │ ├── ContainerRegistry.hpp │ ├── DSPCodec.hpp │ ├── IBackendVoice.hpp │ ├── DirectoryEnumerator.hpp │ ├── Emitter.hpp │ ├── IBackendVoiceAllocator.hpp │ └── AudioGroupData.hpp └── switch_math.hpp ├── standalone_bootstrap.sh ├── standalone_bootstrap.bat ├── AudioUnit ├── AudioUnitViewController.hpp ├── AmuseExtension.entitlements.in ├── AmuseContainer.entitlements.in ├── ContainerInfo.plist ├── AudioUnitViewController.xib ├── AudioUnitBackend.hpp ├── AmuseContainingApp.hpp └── ExtensionInfo.plist ├── .clang-format ├── lib ├── Listener.cpp ├── Studio.cpp ├── AudioGroupData.cpp ├── Submix.cpp ├── EffectDelay.cpp └── VolumeTable.cpp └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /VST/FileOpenDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | std::wstring openDB(); 6 | -------------------------------------------------------------------------------- /Editor/platforms/mac/mainicon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/mac/mainicon.icns -------------------------------------------------------------------------------- /Editor/platforms/win/mainicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/win/mainicon.ico -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/128x128/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/128x128/apps/amuse-gui.png -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/16x16/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/16x16/apps/amuse-gui.png -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/256x256/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/256x256/apps/amuse-gui.png -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/32x32/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/32x32/apps/amuse-gui.png -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/48x48/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/48x48/apps/amuse-gui.png -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/512x512/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/512x512/apps/amuse-gui.png -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/64x64/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/64x64/apps/amuse-gui.png -------------------------------------------------------------------------------- /Editor/resources/translation_res.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | lang_de.qm 4 | 5 | 6 | -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/1024x1024/apps/amuse-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxioDL/amuse/HEAD/Editor/platforms/freedesktop/1024x1024/apps/amuse-gui.png -------------------------------------------------------------------------------- /include/amuse/VolumeTable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace amuse { 4 | float LookupVolume(float vol); 5 | float LookupDLSVolume(float vol); 6 | } // namespace amuse 7 | -------------------------------------------------------------------------------- /standalone_bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git clone https://github.com/AxioDL/boo.git 3 | (cd boo && git submodule update --recursive --init) 4 | 5 | git clone https://github.com/libAthena/athena.git 6 | (cd athena && git submodule update --recursive --init) 7 | -------------------------------------------------------------------------------- /standalone_bootstrap.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | git clone https://github.com/AxioDL/boo.git 3 | pushd boo 4 | git submodule update --recursive --init 5 | popd 6 | 7 | git clone https://github.com/libAthena/athena.git 8 | pushd athena 9 | git submodule update --recursive --init 10 | popd 11 | -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/amuse-gui.desktop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xdg-open 2 | [Desktop Entry] 3 | Name=Amuse 4 | GenericName=MusyX Game Audio Editor 5 | Comment=Edit Audio Data of MusyX Sound Groups 6 | Exec=amuse-gui 7 | Icon=amuse-gui 8 | Terminal=false 9 | Type=Application 10 | Categories=Audio 11 | -------------------------------------------------------------------------------- /Editor/MacOSExtras.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | void MacOSSetDarkAppearance() 4 | { 5 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 6 | if ([NSApp respondsToSelector:@selector(setAppearance:)]) 7 | [NSApp setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]]; 8 | #endif 9 | } 10 | -------------------------------------------------------------------------------- /include/switch_math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* Properly forward math defines to std:: */ 4 | #ifdef __SWITCH__ 5 | 6 | #include 7 | #include 8 | 9 | #undef exp2 10 | #undef log2 11 | #undef fabs 12 | 13 | namespace std { 14 | using ::exp2; 15 | using ::fabs; 16 | using ::log2; 17 | } // namespace std 18 | #endif 19 | -------------------------------------------------------------------------------- /include/amuse/SongConverter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace amuse { 7 | 8 | class SongConverter { 9 | public: 10 | static std::vector SongToMIDI(const unsigned char* data, int& versionOut, bool& isBig); 11 | static std::vector MIDIToSong(const std::vector& data, int version, bool big); 12 | }; 13 | } // namespace amuse 14 | -------------------------------------------------------------------------------- /include/amuse/N64MusyXCodec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr int16_t N64MusyXSampClamp(int32_t val) { 6 | if (val < -32768) 7 | val = -32768; 8 | else if (val > 32767) 9 | val = 32767; 10 | return val; 11 | } 12 | 13 | unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], unsigned lastSample); 14 | 15 | unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], 16 | unsigned firstSample, unsigned lastSample); 17 | -------------------------------------------------------------------------------- /AudioUnit/AudioUnitViewController.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #import 4 | #import "AudioGroupFilePresenter.hpp" 5 | 6 | @class AmuseAudioUnit; 7 | 8 | @interface GroupBrowserDelegate : NSObject { 9 | AmuseAudioUnit* m_audioUnit; 10 | } 11 | - (id)initWithAudioUnit:(AmuseAudioUnit*)au; 12 | @end 13 | 14 | @interface AudioUnitViewController : AUViewController { 15 | @public 16 | AmuseAudioUnit* m_audioUnit; 17 | IBOutlet NSBrowser* m_groupBrowser; 18 | GroupBrowserDelegate* m_groupBrowserDelegate; 19 | } 20 | @end 21 | -------------------------------------------------------------------------------- /include/amuse/EffectBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace amuse { 4 | struct ChannelMap; 5 | 6 | enum class EffectType { Invalid, ReverbStd, ReverbHi, Delay, Chorus, EffectTypeMAX }; 7 | 8 | class EffectBaseTypeless { 9 | public: 10 | virtual ~EffectBaseTypeless() = default; 11 | virtual void resetOutputSampleRate(double sampleRate) = 0; 12 | virtual EffectType Isa() const = 0; 13 | }; 14 | 15 | template 16 | class EffectBase : public EffectBaseTypeless { 17 | public: 18 | virtual void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) = 0; 19 | }; 20 | } // namespace amuse 21 | -------------------------------------------------------------------------------- /AudioUnit/AmuseExtension.entitlements.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.application-identifier 6 | @APPLE_TEAM_ID@.@APPLE_BUNDLE_ID@ 7 | com.apple.developer.team-identifier 8 | @APPLE_TEAM_ID@ 9 | com.apple.security.app-sandbox 10 | 11 | com.apple.security.application-groups 12 | 13 | group.com.axiodl.Amuse.AudioGroups 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /include/amuse/IBackendSubmix.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace amuse { 4 | 5 | enum class SubmixFormat { Int16, Int32, Float }; 6 | 7 | /** Client-implemented submix instance */ 8 | class IBackendSubmix { 9 | public: 10 | virtual ~IBackendSubmix() = default; 11 | 12 | /** Set send level for submix (AudioChannel enum for array index) */ 13 | virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew) = 0; 14 | 15 | /** Amuse gets fixed sample rate of submix this way */ 16 | virtual double getSampleRate() const = 0; 17 | 18 | /** Amuse gets fixed sample format of submix this way */ 19 | virtual SubmixFormat getSampleFormat() const = 0; 20 | }; 21 | } // namespace amuse 22 | -------------------------------------------------------------------------------- /AudioUnit/AmuseContainer.entitlements.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.application-identifier 6 | @APPLE_TEAM_ID@.@APPLE_BUNDLE_ID@ 7 | com.apple.developer.team-identifier 8 | @APPLE_TEAM_ID@ 9 | com.apple.security.app-sandbox 10 | 11 | com.apple.security.files.user-selected.read-only 12 | 13 | com.apple.security.application-groups 14 | 15 | group.com.axiodl.Amuse.AudioGroups 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /include/amuse/amuse.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "amuse/AudioGroup.hpp" 4 | #include "amuse/AudioGroupData.hpp" 5 | #include "amuse/AudioGroupPool.hpp" 6 | #include "amuse/AudioGroupProject.hpp" 7 | #include "amuse/AudioGroupSampleDirectory.hpp" 8 | #include "amuse/ContainerRegistry.hpp" 9 | #include "amuse/EffectChorus.hpp" 10 | #include "amuse/EffectDelay.hpp" 11 | #include "amuse/EffectReverb.hpp" 12 | #include "amuse/Emitter.hpp" 13 | #include "amuse/Engine.hpp" 14 | #include "amuse/Envelope.hpp" 15 | #include "amuse/Listener.hpp" 16 | #include "amuse/Sequencer.hpp" 17 | #include "amuse/SoundMacroState.hpp" 18 | #include "amuse/SongConverter.hpp" 19 | #include "amuse/SongState.hpp" 20 | #include "amuse/Submix.hpp" 21 | #include "amuse/Voice.hpp" 22 | -------------------------------------------------------------------------------- /Editor/NewSoundMacroDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct SoundMacroTemplateEntry { 12 | const char* m_name; 13 | size_t m_length; 14 | const uint32_t* m_data; 15 | }; 16 | 17 | class NewSoundMacroDialog : public QDialog { 18 | Q_OBJECT 19 | QLineEdit m_le; 20 | QComboBox m_combo; 21 | QDialogButtonBox m_buttonBox; 22 | 23 | public: 24 | explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR); 25 | ~NewSoundMacroDialog() override; 26 | 27 | QString getName() const { return m_le.text(); } 28 | const SoundMacroTemplateEntry* getSelectedTemplate() const; 29 | }; 30 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | ColumnLimit: 120 4 | UseTab: Never 5 | --- 6 | Language: Cpp 7 | DerivePointerAlignment: false 8 | PointerAlignment: Left 9 | AlignAfterOpenBracket: Align 10 | AlignConsecutiveAssignments: false 11 | IndentCaseLabels: false 12 | AllowShortBlocksOnASingleLine: true 13 | AlignOperands: true 14 | AlignTrailingComments: true 15 | AlwaysBreakBeforeMultilineStrings: true 16 | AlwaysBreakTemplateDeclarations: true 17 | BreakConstructorInitializersBeforeComma: true 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakAfterDefinitionReturnType: None 20 | AllowShortFunctionsOnASingleLine: All 21 | Cpp11BracedListStyle: true 22 | NamespaceIndentation: None 23 | BinPackArguments: true 24 | BinPackParameters: true 25 | SortIncludes: false 26 | AccessModifierOffset: -2 27 | ConstructorInitializerIndentWidth: 0 28 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 29 | -------------------------------------------------------------------------------- /include/amuse/Listener.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "amuse/Emitter.hpp" 4 | 5 | namespace amuse { 6 | class Listener { 7 | friend class Emitter; 8 | friend class Engine; 9 | Vector3f m_pos = {}; 10 | Vector3f m_dir = {}; 11 | Vector3f m_heading = {}; 12 | Vector3f m_up = {}; 13 | Vector3f m_right = {}; 14 | float m_volume; 15 | float m_frontDiff; 16 | float m_backDiff; 17 | float m_soundSpeed; 18 | bool m_dirty = true; 19 | 20 | public: 21 | Listener(float volume, float frontDiff, float backDiff, float soundSpeed) 22 | : m_volume(std::clamp(volume, 0.f, 1.f)), m_frontDiff(frontDiff), m_backDiff(backDiff), m_soundSpeed(soundSpeed) {} 23 | void setVectors(const float* pos, const float* dir, const float* heading, const float* up); 24 | void setVolume(float vol) { 25 | m_volume = std::clamp(vol, 0.f, 1.f); 26 | m_dirty = true; 27 | } 28 | }; 29 | } // namespace amuse 30 | -------------------------------------------------------------------------------- /Editor/platforms/win/amuse-gui.rc: -------------------------------------------------------------------------------- 1 | #include "winver.h" 2 | #define IDI_ICON1 101 3 | 4 | IDI_ICON1 ICON DISCARDABLE "mainicon.ico" 5 | 6 | VS_VERSION_INFO VERSIONINFO 7 | FILEVERSION 1,0,0,0 8 | PRODUCTVERSION 1,0,0,0 9 | FILEFLAGS 0x0L 10 | FILEFLAGSMASK 0x3fL 11 | FILEOS 0x00040004L 12 | FILETYPE 0x1L 13 | FILESUBTYPE 0x0L 14 | BEGIN 15 | BLOCK "StringFileInfo" 16 | BEGIN 17 | BLOCK "000004b0" 18 | BEGIN 19 | VALUE "CompanyName", "Antidote / Jackoalan" 20 | VALUE "FileDescription", "Amuse" 21 | VALUE "FileVersion", "BETA" 22 | VALUE "LegalCopyright", "Copyright (C) 2015-2018 Antidote / Jackoalan" 23 | VALUE "InternalName", "amuse" 24 | VALUE "OriginalFilename", "amuse-gui.exe" 25 | VALUE "ProductName", "Amuse" 26 | VALUE "ProductVersion", "BETA" 27 | END 28 | END 29 | BLOCK "VarFileInfo" 30 | BEGIN 31 | VALUE "Translation", 0x0, 1200 32 | END 33 | END 34 | -------------------------------------------------------------------------------- /VST/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(VST3_SDK_ROOT "" CACHE PATH "Path to VST 3.x SDK directory containing 'public.sdk' and 'plugininterfaces'") 2 | if (WIN32 AND (EXISTS ${VST3_SDK_ROOT})) 3 | message(STATUS "Found VST SDK; building plugin") 4 | include_directories(${VST3_SDK_ROOT} ${VST3_SDK_ROOT}/public.sdk/source/vst2.x) 5 | set(VST2_DIR ${VST3_SDK_ROOT}/public.sdk/source/vst2.x) 6 | add_definitions(${BOO_SYS_DEFINES}) 7 | add_library(amuse-vst SHARED 8 | VSTBackend.hpp VSTBackend.cpp 9 | VSTEditor.hpp VSTEditor.cpp 10 | AudioGroupFilePresenter.hpp AudioGroupFilePresenter.cpp 11 | ${VST2_DIR}/vstplugmain.cpp 12 | ${VST2_DIR}/audioeffect.cpp 13 | ${VST2_DIR}/audioeffectx.cpp 14 | FileOpenDialog.hpp FileOpenDialog.cpp) 15 | target_link_libraries(amuse-vst amuse boo soxr ${ZLIB_LIBRARIES} lzokay Winmm soxr 16 | Msimg32 Shlwapi logvisor athena-core) 17 | set_target_properties(amuse-vst PROPERTIES LINK_FLAGS "/EXPORT:VSTPluginMain") 18 | endif() 19 | -------------------------------------------------------------------------------- /lib/Listener.cpp: -------------------------------------------------------------------------------- 1 | #include "amuse/Listener.hpp" 2 | 3 | namespace amuse { 4 | 5 | static constexpr Vector3f Cross(const Vector3f& a, const Vector3f& b) { 6 | return { 7 | a[1] * b[2] - a[2] * b[1], 8 | a[2] * b[0] - a[0] * b[2], 9 | a[0] * b[1] - a[1] * b[0], 10 | }; 11 | } 12 | 13 | void Listener::setVectors(const float* pos, const float* dir, const float* heading, const float* up) { 14 | for (int i = 0; i < 3; ++i) { 15 | if (!std::isnan(pos[i])) { 16 | m_pos[i] = pos[i]; 17 | } else { 18 | m_pos[i] = 0.f; 19 | } 20 | if (!std::isnan(dir[i])) { 21 | m_dir[i] = dir[i]; 22 | } else { 23 | m_dir[i] = 0.f; 24 | } 25 | if (!std::isnan(heading[i])) { 26 | m_heading[i] = heading[i]; 27 | } else { 28 | m_heading[i] = 0.f; 29 | } 30 | if (std::isnan(up[i])) { 31 | m_up[i] = up[i]; 32 | } else { 33 | m_heading[i] = 0.f; 34 | } 35 | } 36 | 37 | m_heading = Normalize(m_heading); 38 | m_up = Normalize(m_up); 39 | m_right = Normalize(Cross(m_heading, m_up)); 40 | m_dirty = true; 41 | } 42 | 43 | } // namespace amuse 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015-2018 Antidote / Jackoalan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/Studio.cpp: -------------------------------------------------------------------------------- 1 | #include "amuse/Studio.hpp" 2 | 3 | #include 4 | 5 | #include "amuse/Engine.hpp" 6 | 7 | namespace amuse { 8 | 9 | #ifndef NDEBUG 10 | bool Studio::_cyclicCheck(const Studio* leaf) const { 11 | return std::any_of(m_studiosOut.cbegin(), m_studiosOut.cend(), [leaf](const auto& studio) { 12 | return leaf == studio.m_targetStudio.get() || studio.m_targetStudio->_cyclicCheck(leaf); 13 | }); 14 | } 15 | #endif 16 | 17 | Studio::Studio(Engine& engine, bool mainOut) : m_engine(engine), m_master(engine), m_auxA(engine), m_auxB(engine) { 18 | if (mainOut && engine.m_defaultStudioReady) 19 | addStudioSend(engine.getDefaultStudio(), 1.f, 1.f, 1.f); 20 | } 21 | 22 | void Studio::addStudioSend(ObjToken studio, float dry, float auxA, float auxB) { 23 | m_studiosOut.emplace_back(std::move(studio), dry, auxA, auxB); 24 | 25 | /* Cyclic check */ 26 | assert(!_cyclicCheck(this)); 27 | } 28 | 29 | void Studio::resetOutputSampleRate(double sampleRate) { 30 | m_master.resetOutputSampleRate(sampleRate); 31 | m_auxA.resetOutputSampleRate(sampleRate); 32 | m_auxB.resetOutputSampleRate(sampleRate); 33 | } 34 | } // namespace amuse 35 | -------------------------------------------------------------------------------- /lib/AudioGroupData.cpp: -------------------------------------------------------------------------------- 1 | #include "amuse/AudioGroupData.hpp" 2 | 3 | namespace amuse { 4 | 5 | IntrusiveAudioGroupData::~IntrusiveAudioGroupData() { 6 | if (m_owns) { 7 | delete[] m_pool; 8 | delete[] m_proj; 9 | delete[] m_sdir; 10 | delete[] m_samp; 11 | } 12 | } 13 | 14 | IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept 15 | : AudioGroupData(other.m_proj, other.m_projSz, other.m_pool, other.m_poolSz, other.m_sdir, other.m_sdirSz, other.m_samp, 16 | other.m_sampSz, other.m_fmt, other.m_absOffs) { 17 | m_owns = other.m_owns; 18 | other.m_owns = false; 19 | } 20 | 21 | IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupData&& other) noexcept { 22 | if (m_owns) { 23 | delete[] m_pool; 24 | delete[] m_proj; 25 | delete[] m_sdir; 26 | delete[] m_samp; 27 | } 28 | 29 | m_owns = other.m_owns; 30 | other.m_owns = false; 31 | 32 | m_proj = other.m_proj; 33 | m_pool = other.m_pool; 34 | m_sdir = other.m_sdir; 35 | m_samp = other.m_samp; 36 | m_fmt = other.m_fmt; 37 | m_absOffs = other.m_absOffs; 38 | 39 | return *this; 40 | } 41 | } // namespace amuse 42 | -------------------------------------------------------------------------------- /Editor/platforms/mac/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleExecutable 6 | amuse-gui 7 | CFBundleGetInfoString 8 | Version BETA 9 | CFBundleShortVersionString 10 | BETA 11 | NSHumanReadableCopyright 12 | 2015-2018 Antidote / Jackoalan 13 | CFBundleIconFile 14 | mainicon.icns 15 | CFBundleIdentifier 16 | com.axiodl.Amuse 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | Amuse 21 | CFBundleVersion 22 | BETA 23 | CFBundlePackageType 24 | APPL 25 | CFBundleSignature 26 | ???? 27 | NSPrincipalClass 28 | NSApplication 29 | NSHighResolutionCapable 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Editor/platforms/win/amuse-gui.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | Amuse 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | true/PM 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(amuse-mkqticon mkqticon.c) 2 | target_link_libraries(amuse-mkqticon ${PNG_LIBRARIES} ${ZLIB_LIBRARIES}) 3 | 4 | macro(declare_qticon_target) 5 | add_custom_command(OUTPUT ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin 6 | COMMAND $ 7 | ARGS ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin 8 | DEPENDS 9 | ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/128x128/apps/amuse-gui.png 10 | ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/64x64/apps/amuse-gui.png 11 | ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/48x48/apps/amuse-gui.png 12 | ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/32x32/apps/amuse-gui.png 13 | ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/16x16/apps/amuse-gui.png 14 | WORKING_DIRECTORY ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop 15 | COMMENT "Generating mainicon_qt.bin") 16 | bintoc(mainicon_qt.cpp ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin MAINICON_QT) 17 | endmacro() 18 | -------------------------------------------------------------------------------- /include/amuse/Envelope.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace amuse { 6 | class Voice; 7 | 8 | struct ADSR; 9 | struct ADSRDLS; 10 | 11 | /** Per-sample state tracker for ADSR envelope data */ 12 | class Envelope { 13 | public: 14 | enum class State { Attack, Decay, Sustain, Release, Complete }; 15 | 16 | private: 17 | State m_phase = State::Attack; /**< Current envelope state */ 18 | double m_attackTime = 0.01; /**< Time of attack in seconds */ 19 | double m_decayTime = 0.0; /**< Time of decay in seconds */ 20 | double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */ 21 | double m_releaseTime = 0.01; /**< Time of release in seconds */ 22 | double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */ 23 | double m_curTime = 0.0; /**< Current time of envelope stage in seconds */ 24 | bool m_adsrSet = false; 25 | 26 | public: 27 | void reset(const ADSR* adsr); 28 | void reset(const ADSRDLS* adsr, int8_t note, int8_t vel); 29 | void keyOff(const Voice& vox); 30 | void keyOff(); 31 | float advance(double dt, const Voice& vox); 32 | float advance(double dt); 33 | bool isComplete(const Voice& vox) const; 34 | bool isAdsrSet() const { return m_adsrSet; } 35 | }; 36 | } // namespace amuse 37 | -------------------------------------------------------------------------------- /include/amuse/Entity.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "amuse/Common.hpp" 5 | 6 | namespace amuse { 7 | class AudioGroup; 8 | class Engine; 9 | 10 | /** Common 'engine child' class */ 11 | class Entity : public IObj { 12 | /* Only the Engine will manage Entity lifetimes, 13 | * but shared_ptrs are issued to the client so it can safely track state */ 14 | friend class Engine; 15 | friend struct SoundMacroState; 16 | 17 | protected: 18 | bool m_destroyed = false; 19 | void _destroy() { 20 | assert(!m_destroyed); 21 | m_destroyed = true; 22 | } 23 | Engine& m_engine; 24 | const AudioGroup& m_audioGroup; 25 | GroupId m_groupId; 26 | ObjectId m_objectId; /* if applicable */ 27 | public: 28 | Entity(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid = ObjectId()) 29 | : m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid) {} 30 | ~Entity() override { 31 | /* Ensure proper destruction procedure followed */ 32 | assert(m_destroyed); 33 | } 34 | 35 | Engine& getEngine() { return m_engine; } 36 | const AudioGroup& getAudioGroup() const { return m_audioGroup; } 37 | GroupId getGroupId() const { return m_groupId; } 38 | ObjectId getObjectId() const { return m_objectId; } 39 | bool isDestroyed() const { return m_destroyed; } 40 | }; 41 | 42 | } // namespace amuse 43 | -------------------------------------------------------------------------------- /include/amuse/Studio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "amuse/Common.hpp" 6 | #include "amuse/Entity.hpp" 7 | #include "amuse/Submix.hpp" 8 | 9 | namespace amuse { 10 | struct StudioSend; 11 | 12 | class Studio { 13 | friend class Engine; 14 | Engine& m_engine; 15 | Submix m_master; 16 | Submix m_auxA; 17 | Submix m_auxB; 18 | 19 | std::list m_studiosOut; 20 | #ifndef NDEBUG 21 | bool _cyclicCheck(const Studio* leaf) const; 22 | #endif 23 | 24 | public: 25 | Studio(Engine& engine, bool mainOut); 26 | 27 | /** Register a target Studio to send this Studio's mixing busses */ 28 | void addStudioSend(ObjToken studio, float dry, float auxA, float auxB); 29 | 30 | /** Advise submixes of changing sample rate */ 31 | void resetOutputSampleRate(double sampleRate); 32 | 33 | Submix& getMaster() { return m_master; } 34 | Submix& getAuxA() { return m_auxA; } 35 | Submix& getAuxB() { return m_auxB; } 36 | 37 | Engine& getEngine() { return m_engine; } 38 | }; 39 | 40 | struct StudioSend { 41 | ObjToken m_targetStudio; 42 | float m_dryLevel; 43 | float m_auxALevel; 44 | float m_auxBLevel; 45 | StudioSend(ObjToken studio, float dry, float auxA, float auxB) 46 | : m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB) {} 47 | }; 48 | 49 | } // namespace amuse 50 | -------------------------------------------------------------------------------- /include/amuse/ContainerRegistry.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "amuse/AudioGroupData.hpp" 9 | #include "amuse/Common.hpp" 10 | 11 | namespace amuse { 12 | 13 | class ContainerRegistry { 14 | public: 15 | enum class Type { 16 | Invalid = -1, 17 | Raw4 = 0, 18 | MetroidPrime, 19 | MetroidPrime2, 20 | RogueSquadronPC, 21 | RogueSquadronN64, 22 | Factor5N64Rev, 23 | RogueSquadron2, 24 | RogueSquadron3 25 | }; 26 | struct SongData { 27 | std::unique_ptr m_data; 28 | size_t m_size; 29 | int16_t m_groupId; 30 | int16_t m_setupId; 31 | SongData(std::unique_ptr&& data, size_t size, int16_t groupId, int16_t setupId) 32 | : m_data(std::move(data)), m_size(size), m_groupId(groupId), m_setupId(setupId) {} 33 | }; 34 | static const char* TypeToName(Type tp); 35 | static Type DetectContainerType(const char* path); 36 | static std::vector> LoadContainer(const char* path); 37 | static std::vector> LoadContainer(const char* path, 38 | Type& typeOut); 39 | static std::vector> LoadSongs(const char* path); 40 | }; 41 | } // namespace amuse 42 | -------------------------------------------------------------------------------- /AudioUnit/ContainerInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeRole 11 | Viewer 12 | LSItemContentTypes 13 | 14 | public.data 15 | 16 | 17 | 18 | CFBundleExecutable 19 | amuse-au-container 20 | CFBundleIconFile 21 | 22 | CFBundleIdentifier 23 | com.axiodl.Amuse 24 | CFBundleInfoDictionaryVersion 25 | 6.0 26 | CFBundleName 27 | Amuse 28 | CFBundlePackageType 29 | APPL 30 | CFBundleShortVersionString 31 | 1.0 32 | CFBundleSignature 33 | ???? 34 | CFBundleVersion 35 | 1 36 | LSMinimumSystemVersion 37 | 10.11 38 | NSPrincipalClass 39 | NSApplication 40 | NSMainNibFile 41 | AmuseContainerMainMenu 42 | 43 | 44 | -------------------------------------------------------------------------------- /VST/VSTEditor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | 7 | #include "aeffeditor.h" 8 | 9 | namespace amuse { 10 | class VSTBackend; 11 | 12 | /** Editor UI class */ 13 | class VSTEditor : public AEffEditor { 14 | friend class VSTBackend; 15 | friend class AudioGroupFilePresenter; 16 | friend struct AudioGroupCollection; 17 | 18 | VSTBackend& m_backend; 19 | ERect m_windowRect = {0, 0, 420, 600}; 20 | 21 | HWND m_rootView = 0; 22 | HWND m_collectionTree = 0; 23 | HWND m_collectionAdd = 0; 24 | HWND m_collectionRemove = 0; 25 | HWND m_groupListView = 0; 26 | HWND m_pageListView = 0; 27 | 28 | int m_selCollectionIdx = -1; 29 | int m_selFileIdx = -1; 30 | int m_selGroupIdx = -1; 31 | int m_selPageIdx = -1; 32 | int m_lastLParam = -1; 33 | 34 | HTREEITEM m_deferredCollectionSel = 0; 35 | 36 | static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); 37 | static LRESULT CALLBACK ColHeaderWindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); 38 | 39 | void _reselectColumns(); 40 | 41 | public: 42 | VSTEditor(VSTBackend& backend); 43 | 44 | bool getRect(ERect** rect); 45 | bool open(void* ptr); 46 | void close(); 47 | void update(); 48 | 49 | void addAction(); 50 | void removeAction(); 51 | 52 | void selectCollection(LPARAM idx); 53 | void selectGroup(int idx); 54 | void selectPage(int idx); 55 | void reselectPage(); 56 | void selectNormalPage(int idx); 57 | void selectDrumPage(int idx); 58 | }; 59 | } // namespace amuse 60 | -------------------------------------------------------------------------------- /include/amuse/DSPCodec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr int16_t DSPSampClamp(int32_t val) { 6 | if (val < -32768) 7 | val = -32768; 8 | else if (val > 32767) 9 | val = 32767; 10 | return val; 11 | } 12 | 13 | unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2, 14 | unsigned lastSample); 15 | unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, 16 | int16_t* prev2, unsigned lastSample); 17 | unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, 18 | int16_t* prev2, unsigned lastSample); 19 | 20 | unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, 21 | int16_t* prev2, unsigned firstSample, unsigned lastSample); 22 | 23 | unsigned DSPDecompressFrameStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2, 24 | unsigned lastSample); 25 | 26 | unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2, 27 | unsigned firstSample, unsigned lastSample); 28 | 29 | void DSPCorrelateCoefs(const short* source, int samples, short coefsOut[8][2]); 30 | 31 | void DSPEncodeFrame(short pcmInOut[16], int sampleCount, unsigned char adpcmOut[8], const short coefsIn[8][2]); 32 | -------------------------------------------------------------------------------- /AudioUnit/AudioUnitViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /include/amuse/IBackendVoice.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace amuse { 7 | class IBackendSubmix; 8 | 9 | /** Same channel enums from boo, used for matrix coefficient table index */ 10 | enum class AudioChannel { 11 | FrontLeft, 12 | FrontRight, 13 | RearLeft, 14 | RearRight, 15 | FrontCenter, 16 | LFE, 17 | SideLeft, 18 | SideRight, 19 | Unknown = 0xff 20 | }; 21 | 22 | constexpr size_t NumChannels = 8; 23 | 24 | /** Same structure from boo, used to represent interleaved speaker layout */ 25 | struct ChannelMap { 26 | unsigned m_channelCount = 0; 27 | AudioChannel m_channels[NumChannels] = {}; 28 | }; 29 | 30 | /** Client-implemented voice instance */ 31 | class IBackendVoice { 32 | public: 33 | virtual ~IBackendVoice() = default; 34 | 35 | /** Set new sample rate into platform voice (may result in artifacts while playing) */ 36 | virtual void resetSampleRate(double sampleRate) = 0; 37 | 38 | /** Reset channel-gains to silence and unbind all submixes */ 39 | virtual void resetChannelLevels() = 0; 40 | 41 | /** Set channel-gains for audio source (AudioChannel enum for array index) */ 42 | virtual void setChannelLevels(IBackendSubmix* submix, const std::array& coefs, bool slew) = 0; 43 | 44 | /** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */ 45 | virtual void setPitchRatio(double ratio, bool slew) = 0; 46 | 47 | /** Instructs platform to begin consuming sample data; invoking callback as needed */ 48 | virtual void start() = 0; 49 | 50 | /** Instructs platform to stop consuming sample data */ 51 | virtual void stop() = 0; 52 | }; 53 | } // namespace amuse 54 | -------------------------------------------------------------------------------- /AudioUnit/AudioUnitBackend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __APPLE__ 3 | 4 | #include 5 | 6 | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 7 | #define AMUSE_HAS_AUDIO_UNIT 1 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include "amuse/BooBackend.hpp" 14 | #include "amuse/Engine.hpp" 15 | #include "amuse/IBackendVoice.hpp" 16 | #include "amuse/IBackendSubmix.hpp" 17 | #include "amuse/IBackendVoiceAllocator.hpp" 18 | #import "AudioGroupFilePresenter.hpp" 19 | 20 | @class AudioUnitViewController; 21 | 22 | namespace amuse { 23 | 24 | /** Backend voice allocator implementation for AudioUnit mixer */ 25 | class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator { 26 | public: 27 | AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {} 28 | }; 29 | 30 | void RegisterAudioUnit(); 31 | } // namespace amuse 32 | 33 | @interface AmuseAudioUnit : AUAudioUnit { 34 | @public 35 | AudioUnitViewController* m_viewController; 36 | std::unique_ptr m_booBackend; 37 | std::optional m_voxAlloc; 38 | std::optional m_engine; 39 | AudioGroupFilePresenter* m_filePresenter; 40 | AUAudioUnitBus* m_outBus; 41 | AUAudioUnitBusArray* m_outs; 42 | } 43 | - (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription 44 | error:(NSError* __nullable* __nonnull)outError 45 | viewController:(AudioUnitViewController* __nonnull)vc; 46 | - (void)requestAudioGroup:(AudioGroupToken* _Nonnull)group; 47 | @end 48 | 49 | #endif 50 | #endif 51 | -------------------------------------------------------------------------------- /Editor/platforms/freedesktop/mkqticon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static const int DIMS[] = 7 | { 8 | 16, 9 | 32, 10 | 48, 11 | 64, 12 | 128, 13 | 256, 14 | 0 15 | }; 16 | 17 | int main(int argc, char* argv[]) 18 | { 19 | if (argc < 2) 20 | { 21 | fprintf(stderr, "Usage: makeqticon \n"); 22 | return 1; 23 | } 24 | 25 | FILE* ofp = fopen(argv[1], "wb"); 26 | if (!ofp) 27 | { 28 | fprintf(stderr, "'%s' is not able to be opened for writing as a regular file\n", argv[1]); 29 | return 1; 30 | } 31 | 32 | char command[2048]; 33 | 34 | for (const int* d = DIMS ; *d != 0 ; ++d) 35 | { 36 | printf("Rendering main icon @%dx%d\n", *d, *d); 37 | fflush(stdout); 38 | 39 | snprintf(command, 2048, "%dx%d/apps/amuse-gui.png", *d, *d); 40 | FILE* fp = fopen(command, "rb"); 41 | if (!fp) 42 | { 43 | fprintf(stderr, "unable to open '%s' for reading\n", command); 44 | fclose(ofp); 45 | return 1; 46 | } 47 | 48 | fseek(fp, 0, SEEK_END); 49 | uint32_t size = (uint32_t)ftell(fp); 50 | fseek(fp, 0, SEEK_SET); 51 | 52 | fwrite(&size, 1, 4, ofp); 53 | 54 | uint8_t buf[1024]; 55 | while (size > 0) 56 | { 57 | long thisSize = size; 58 | if (thisSize > 1024) 59 | thisSize = 1024; 60 | 61 | fread(buf, 1, (size_t)thisSize, fp); 62 | fwrite(buf, 1, (size_t)thisSize, ofp); 63 | size -= thisSize; 64 | } 65 | 66 | fclose(fp); 67 | } 68 | 69 | fclose(ofp); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /include/amuse/DirectoryEnumerator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "amuse/Common.hpp" 8 | 9 | namespace amuse { 10 | 11 | /** 12 | * @brief Case-insensitive comparator for std::map sorting 13 | */ 14 | struct CaseInsensitiveCompare { 15 | // Allow heterogeneous lookup with maps that use this comparator. 16 | using is_transparent = void; 17 | 18 | bool operator()(std::string_view lhs, std::string_view rhs) const { 19 | return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char lhs, char rhs) { 20 | return std::tolower(static_cast(lhs)) < std::tolower(static_cast(rhs)); 21 | }); 22 | } 23 | }; 24 | 25 | class DirectoryEnumerator { 26 | public: 27 | enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted }; 28 | struct Entry { 29 | std::string m_path; 30 | std::string m_name; 31 | size_t m_fileSz; 32 | bool m_isDir; 33 | 34 | Entry(std::string path, std::string name, size_t sz, bool isDir) 35 | : m_path(std::move(path)), m_name(std::move(name)), m_fileSz(sz), m_isDir(isDir) {} 36 | }; 37 | 38 | private: 39 | std::vector m_entries; 40 | 41 | public: 42 | DirectoryEnumerator(std::string_view path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false, 43 | bool reverse = false, bool noHidden = false); 44 | 45 | explicit operator bool() const { return !m_entries.empty(); } 46 | [[nodiscard]] size_t size() const { return m_entries.size(); } 47 | [[nodiscard]] std::vector::const_iterator begin() const { return m_entries.cbegin(); } 48 | [[nodiscard]] std::vector::const_iterator end() const { return m_entries.cend(); } 49 | }; 50 | } // namespace amuse 51 | -------------------------------------------------------------------------------- /include/amuse/Emitter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "amuse/Common.hpp" 8 | #include "amuse/Entity.hpp" 9 | #include "amuse/Voice.hpp" 10 | 11 | namespace amuse { 12 | class Listener; 13 | 14 | using Vector3f = std::array; 15 | 16 | constexpr float Dot(const Vector3f& a, const Vector3f& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } 17 | 18 | inline float Length(const Vector3f& a) { 19 | if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON) 20 | return 0.f; 21 | return std::sqrt(Dot(a, a)); 22 | } 23 | 24 | inline Vector3f Normalize(const Vector3f& in) { 25 | const float dist = Length(in); 26 | 27 | if (dist == 0.f) { 28 | return {}; 29 | } 30 | 31 | return {in[0] / dist, in[1] / dist, in[2] / dist}; 32 | } 33 | 34 | /** Voice wrapper with positional-3D level control */ 35 | class Emitter : public Entity { 36 | friend class Engine; 37 | 38 | ObjToken m_vox; 39 | Vector3f m_pos = {}; 40 | Vector3f m_dir = {}; 41 | float m_maxDist; 42 | float m_maxVol = 1.f; 43 | float m_minVol; 44 | float m_falloff; 45 | bool m_doppler; 46 | bool m_dirty = true; 47 | Voice::VolumeCache m_attCache; 48 | 49 | void _destroy(); 50 | float _attenuationCurve(float dist) const; 51 | void _update(); 52 | 53 | public: 54 | ~Emitter() override; 55 | Emitter(Engine& engine, const AudioGroup& group, ObjToken vox, float maxDist, float minVol, float falloff, 56 | bool doppler); 57 | 58 | void setVectors(const float* pos, const float* dir); 59 | void setMaxVol(float maxVol) { 60 | m_maxVol = std::clamp(maxVol, 0.f, 1.f); 61 | m_dirty = true; 62 | } 63 | 64 | ObjToken getVoice() const { return m_vox; } 65 | }; 66 | } // namespace amuse 67 | -------------------------------------------------------------------------------- /include/amuse/IBackendVoiceAllocator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace amuse { 7 | class Engine; 8 | class IBackendSubmix; 9 | class IBackendVoice; 10 | class Submix; 11 | class Voice; 12 | 13 | /** Same enum as boo for describing speaker-configuration */ 14 | enum class AudioChannelSet { Stereo, Quad, Surround51, Surround71, Unknown = 0xff }; 15 | 16 | /** Handle to MIDI-reader, implementation opaque */ 17 | class IMIDIReader { 18 | public: 19 | virtual ~IMIDIReader() = default; 20 | virtual void pumpReader(double dt) = 0; 21 | }; 22 | 23 | /** Client-implemented voice allocator */ 24 | class IBackendVoiceAllocator { 25 | public: 26 | virtual ~IBackendVoiceAllocator() = default; 27 | 28 | /** Amuse obtains a new voice from the platform this way */ 29 | virtual std::unique_ptr allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) = 0; 30 | 31 | /** Amuse obtains a new submix from the platform this way */ 32 | virtual std::unique_ptr allocateSubmix(Submix& clientSmx, bool mainOut, int busId) = 0; 33 | 34 | /** Amuse obtains a list of all MIDI devices this way */ 35 | virtual std::vector> enumerateMIDIDevices() = 0; 36 | 37 | /** Amuse obtains an interactive MIDI-in connection from the OS this way */ 38 | virtual std::unique_ptr allocateMIDIReader(Engine& engine) = 0; 39 | 40 | /** Amuse obtains speaker-configuration from the platform this way */ 41 | virtual AudioChannelSet getAvailableSet() = 0; 42 | 43 | /** Set volume of main mix out */ 44 | virtual void setVolume(float vol) = 0; 45 | 46 | /** Amuse registers for key callback events from the mixing engine this way */ 47 | virtual void setCallbackInterface(Engine* engine) = 0; 48 | }; 49 | } // namespace amuse 50 | -------------------------------------------------------------------------------- /AudioUnit/AmuseContainingApp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #import 4 | #import "AudioGroupFilePresenter.hpp" 5 | #include 6 | #include 7 | 8 | @interface DataOutlineView : NSOutlineView { 9 | @public 10 | IBOutlet NSButton* removeDataButton; 11 | IBOutlet NSMenuItem* deleteMenuItem; 12 | } 13 | @end 14 | 15 | @interface SamplesTableController : NSObject { 16 | AudioGroupFilePresenter* presenter; 17 | } 18 | - (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present; 19 | @end 20 | 21 | @interface SFXTableController : NSObject { 22 | AudioGroupFilePresenter* presenter; 23 | } 24 | - (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present; 25 | @end 26 | 27 | @interface AppDelegate : NSObject { 28 | IBOutlet NSWindow* mainWindow; 29 | IBOutlet NSOutlineView* dataOutline; 30 | IBOutlet NSSearchField* dataSearchField; 31 | IBOutlet NSTableView* sfxTable; 32 | IBOutlet NSTableView* samplesTable; 33 | IBOutlet NSTextView* creditsView; 34 | 35 | IBOutlet NSButton* removeDataButton; 36 | IBOutlet NSMenuItem* removeDataMenu; 37 | 38 | AudioGroupFilePresenter* groupFilePresenter; 39 | 40 | SamplesTableController* samplesController; 41 | SFXTableController* sfxController; 42 | 43 | @public 44 | std::unique_ptr booEngine; 45 | std::optional amuseAllocator; 46 | std::optional amuseEngine; 47 | std::shared_ptr activeSFXVox; 48 | } 49 | - (BOOL)importURL:(NSURL*)url; 50 | - (void)outlineView:(DataOutlineView*)ov selectionChanged:(id)item; 51 | - (void)reloadTables; 52 | - (void)startSFX:(int)sfxId; 53 | - (void)startSample:(int)sampId; 54 | @end 55 | -------------------------------------------------------------------------------- /Editor/resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | IconImport.svg 4 | IconKeymap.svg 5 | IconLayers.svg 6 | IconNew.svg 7 | IconNewKeymap.svg 8 | IconNewLayers.svg 9 | IconNewSongGroup.svg 10 | IconNewSoundGroup.svg 11 | IconNewSoundMacro.svg 12 | IconOpen.svg 13 | IconSongGroup.svg 14 | IconSoundGroup.svg 15 | IconSoundMacro.svg 16 | IconGroup.svg 17 | IconNewGroup.svg 18 | IconADSR.svg 19 | IconNewADSR.svg 20 | IconCurve.svg 21 | IconNewCurve.svg 22 | IconSoundMacroDelete.svg 23 | IconSoundMacroDeleteHovered.svg 24 | IconSoundMacroTarget.svg 25 | IconSoundMacroTargetDisabled.svg 26 | IconKill.svg 27 | IconSample.svg 28 | IconPaintbrush.svg 29 | IconAdd.svg 30 | IconRemove.svg 31 | IconStop.svg 32 | IconVolume0.svg 33 | IconVolume1.svg 34 | IconVolume2.svg 35 | IconVolume3.svg 36 | IconFX.svg 37 | IconB.svg 38 | IconA.svg 39 | IconBack.svg 40 | IconForward.svg 41 | IconBackDisabled.svg 42 | IconForwardDisabled.svg 43 | 44 | 45 | FaceGrey.svg 46 | keyboard.svg 47 | keyboard_last.svg 48 | 49 | 50 | -------------------------------------------------------------------------------- /Editor/CurveEditor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "EditorWidget.hpp" 10 | #include "ProjectModel.hpp" 11 | 12 | #include 13 | 14 | class CurveEditor; 15 | 16 | class QLabel; 17 | class QLineEdit; 18 | class QPushButton; 19 | 20 | class CurveView : public QWidget { 21 | Q_OBJECT 22 | friend class CurveControls; 23 | amuse::ObjToken m_node; 24 | QFont m_gridFont; 25 | std::array m_percentTexts; 26 | std::array m_percentTextsCenter; 27 | CurveEditor* getEditor() const; 28 | 29 | public: 30 | explicit CurveView(QWidget* parent = Q_NULLPTR); 31 | ~CurveView() override; 32 | 33 | void loadData(ProjectModel::CurveNode* node); 34 | void unloadData(); 35 | ProjectModel::INode* currentNode() const; 36 | 37 | void paintEvent(QPaintEvent* ev) override; 38 | void mousePressEvent(QMouseEvent* ev) override; 39 | void mouseMoveEvent(QMouseEvent* ev) override; 40 | }; 41 | 42 | class CurveControls : public QFrame { 43 | Q_OBJECT 44 | friend class CurveView; 45 | QLineEdit* m_lineEdit; 46 | QLabel* m_errLabel; 47 | QPushButton* m_setExpr; 48 | QJSEngine m_engine; 49 | CurveEditor* getEditor() const; 50 | 51 | public: 52 | explicit CurveControls(QWidget* parent = Q_NULLPTR); 53 | ~CurveControls() override; 54 | 55 | void loadData(); 56 | void unloadData(); 57 | void resizeEvent(QResizeEvent* ev) override; 58 | 59 | public slots: 60 | void exprCommit(); 61 | }; 62 | 63 | class CurveEditor : public EditorWidget { 64 | Q_OBJECT 65 | friend class CurveView; 66 | friend class CurveControls; 67 | CurveView* m_curveView; 68 | CurveControls* m_controls; 69 | 70 | public: 71 | explicit CurveEditor(QWidget* parent = Q_NULLPTR); 72 | ~CurveEditor() override; 73 | 74 | bool loadData(ProjectModel::CurveNode* node); 75 | void unloadData() override; 76 | ProjectModel::INode* currentNode() const override; 77 | }; 78 | -------------------------------------------------------------------------------- /Editor/MIDIReader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace amuse { 13 | class Engine; 14 | } 15 | 16 | class MIDIReader : public amuse::BooBackendMIDIReader { 17 | std::unordered_map> m_chanVoxs; 18 | std::unordered_set> m_keyoffVoxs; 19 | amuse::ObjToken m_lastVoice; 20 | 21 | public: 22 | MIDIReader(amuse::Engine& engine, bool useLock); 23 | 24 | void noteOff(uint8_t chan, uint8_t key, uint8_t velocity) override; 25 | void noteOn(uint8_t chan, uint8_t key, uint8_t velocity) override; 26 | void notePressure(uint8_t chan, uint8_t key, uint8_t pressure) override; 27 | void controlChange(uint8_t chan, uint8_t control, uint8_t value) override; 28 | void programChange(uint8_t chan, uint8_t program) override; 29 | void channelPressure(uint8_t chan, uint8_t pressure) override; 30 | void pitchBend(uint8_t chan, int16_t pitch) override; 31 | 32 | void allSoundOff(uint8_t chan) override; 33 | void resetAllControllers(uint8_t chan) override; 34 | void localControl(uint8_t chan, bool on) override; 35 | void allNotesOff(uint8_t chan) override; 36 | void omniMode(uint8_t chan, bool on) override; 37 | void polyMode(uint8_t chan, bool on) override; 38 | 39 | void sysex(const void* data, size_t len) override; 40 | void timeCodeQuarterFrame(uint8_t message, uint8_t value) override; 41 | void songPositionPointer(uint16_t pointer) override; 42 | void songSelect(uint8_t song) override; 43 | void tuneRequest() override; 44 | 45 | void startSeq() override; 46 | void continueSeq() override; 47 | void stopSeq() override; 48 | 49 | void reset() override; 50 | }; 51 | 52 | class VoiceAllocator : public amuse::BooBackendVoiceAllocator { 53 | public: 54 | VoiceAllocator(boo::IAudioVoiceEngine& booEngine); 55 | std::unique_ptr allocateMIDIReader(amuse::Engine& engine) override; 56 | }; 57 | -------------------------------------------------------------------------------- /Editor/Common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class MainWindow; 11 | extern MainWindow* g_MainWindow; 12 | 13 | class QDir; 14 | class QRectF; 15 | class QTransform; 16 | 17 | class UIMessenger : public QObject { 18 | Q_OBJECT 19 | public: 20 | using QObject::QObject; 21 | signals: 22 | QMessageBox::StandardButton information(const QString& title, const QString& text, 23 | QMessageBox::StandardButtons buttons = QMessageBox::Ok, 24 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 25 | int question(const QString& title, const QString& text, const QString& button0Text, 26 | const QString& button1Text = QString(), const QString& button2Text = QString(), 27 | int defaultButtonNumber = 0, int escapeButtonNumber = -1); 28 | QMessageBox::StandardButton 29 | question(const QString& title, const QString& text, 30 | QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), 31 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 32 | QMessageBox::StandardButton warning(const QString& title, const QString& text, 33 | QMessageBox::StandardButtons buttons = QMessageBox::Ok, 34 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 35 | QMessageBox::StandardButton critical(const QString& title, const QString& text, 36 | QMessageBox::StandardButtons buttons = QMessageBox::Ok, 37 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); 38 | }; 39 | 40 | std::string QStringToUTF8(const QString& str); 41 | QString UTF8ToQString(const std::string& str); 42 | 43 | bool MkPath(const QString& path, UIMessenger& messenger); 44 | bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger); 45 | 46 | void ShowInGraphicalShell(QWidget* parent, const QString& pathIn); 47 | QString ShowInGraphicalShellString(); 48 | 49 | /* Used for generating transform matrices to map SVG coordinate space */ 50 | QTransform RectToRect(const QRectF& from, const QRectF& to); 51 | -------------------------------------------------------------------------------- /lib/Submix.cpp: -------------------------------------------------------------------------------- 1 | #include "amuse/Submix.hpp" 2 | 3 | namespace amuse { 4 | 5 | Submix::Submix(Engine& engine) : m_root(engine) {} 6 | 7 | EffectChorus& Submix::makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period) { 8 | return makeEffect(baseDelay, variation, period); 9 | } 10 | 11 | EffectChorus& Submix::makeChorus(const EffectChorusInfo& info) { return makeEffect(info); } 12 | 13 | EffectDelay& Submix::makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput) { 14 | return makeEffect(initDelay, initFeedback, initOutput); 15 | } 16 | 17 | EffectDelay& Submix::makeDelay(const EffectDelayInfo& info) { return makeEffect(info); } 18 | 19 | EffectReverbStd& Submix::makeReverbStd(float coloration, float mix, float time, float damping, float preDelay) { 20 | return makeEffect(coloration, mix, time, damping, preDelay); 21 | } 22 | 23 | EffectReverbStd& Submix::makeReverbStd(const EffectReverbStdInfo& info) { return makeEffect(info); } 24 | 25 | EffectReverbHi& Submix::makeReverbHi(float coloration, float mix, float time, float damping, float preDelay, 26 | float crosstalk) { 27 | return makeEffect(coloration, mix, time, damping, preDelay, crosstalk); 28 | } 29 | 30 | EffectReverbHi& Submix::makeReverbHi(const EffectReverbHiInfo& info) { return makeEffect(info); } 31 | 32 | void Submix::applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const { 33 | for (const std::unique_ptr& effect : m_effectStack) 34 | ((EffectBase&)*effect).applyEffect(audio, frameCount, chanMap); 35 | } 36 | 37 | void Submix::applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const { 38 | for (const std::unique_ptr& effect : m_effectStack) 39 | ((EffectBase&)*effect).applyEffect(audio, frameCount, chanMap); 40 | } 41 | 42 | void Submix::applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const { 43 | for (const std::unique_ptr& effect : m_effectStack) 44 | ((EffectBase&)*effect).applyEffect(audio, frameCount, chanMap); 45 | } 46 | 47 | void Submix::resetOutputSampleRate(double sampleRate) { 48 | for (const std::unique_ptr& effect : m_effectStack) 49 | effect->resetOutputSampleRate(sampleRate); 50 | } 51 | } // namespace amuse 52 | -------------------------------------------------------------------------------- /Editor/resources/IconStop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Editor/resources/IconRemove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 69 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Editor/resources/IconBack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Editor/resources/IconForward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Editor/resources/IconBackDisabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Editor/resources/IconForwardDisabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Editor/resources/IconAdd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 51 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 69 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Editor/resources/IconVolume0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /AudioUnit/ExtensionInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Amuse 9 | CFBundleExecutable 10 | amuse-au 11 | CFBundleIdentifier 12 | com.axiodl.Amuse.AudioUnit 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | Amuse 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleSupportedPlatforms 24 | 25 | MacOSX 26 | 27 | CFBundleVersion 28 | 1 29 | LSMinimumSystemVersion 30 | 10.11 31 | NSExtension 32 | 33 | NSExtensionAttributes 34 | 35 | AudioComponents 36 | 37 | 38 | description 39 | Amuse 40 | factoryFunction 41 | AudioUnitViewController 42 | manufacturer 43 | AXDL 44 | name 45 | AXDL: Amuse 46 | sandboxSafe 47 | 48 | subtype 49 | amus 50 | tags 51 | 52 | Synthesizer 53 | 54 | type 55 | aumu 56 | version 57 | 67072 58 | 59 | 60 | NSExtensionServiceRoleType 61 | NSExtensionServiceRoleTypeEditor 62 | 63 | NSExtensionPointIdentifier 64 | com.apple.AudioUnit-UI 65 | NSExtensionPrincipalClass 66 | AudioUnitViewController 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /VST/VSTBackend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "audioeffectx.h" 4 | #include "VSTEditor.hpp" 5 | #include 6 | #include 7 | 8 | #include "amuse/BooBackend.hpp" 9 | #include "amuse/Engine.hpp" 10 | #include "amuse/IBackendVoice.hpp" 11 | #include "amuse/IBackendSubmix.hpp" 12 | #include "amuse/IBackendVoiceAllocator.hpp" 13 | #include "AudioGroupFilePresenter.hpp" 14 | 15 | namespace amuse { 16 | class VSTBackend; 17 | 18 | /** Backend voice allocator implementation for AudioUnit mixer */ 19 | class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator { 20 | public: 21 | VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {} 22 | }; 23 | 24 | /** Actual plugin implementation class */ 25 | class VSTBackend : public AudioEffectX { 26 | std::mutex m_lock; 27 | std::unique_ptr m_booBackend; 28 | std::optional m_voxAlloc; 29 | std::optional m_engine; 30 | std::shared_ptr m_curSeq; 31 | int m_reqGroup = -1; 32 | int m_curGroup = -1; 33 | const AudioGroupDataCollection* m_curData = nullptr; 34 | size_t m_curFrame = 0; 35 | std::wstring m_userDir; 36 | int m_routeChannel = -1; 37 | AudioGroupFilePresenter m_filePresenter; 38 | VSTEditor m_editor; 39 | 40 | public: 41 | VSTBackend(audioMasterCallback cb); 42 | ~VSTBackend(); 43 | AEffEditor* getEditor(); 44 | VstInt32 processEvents(VstEvents* events); 45 | void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames); 46 | VstInt32 canDo(char* text); 47 | VstPlugCategory getPlugCategory(); 48 | bool getEffectName(char* text); 49 | bool getProductString(char* text); 50 | bool getVendorString(char* text); 51 | bool getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text); 52 | bool getOutputProperties(VstInt32 index, VstPinProperties* properties); 53 | VstInt32 getNumMidiInputChannels(); 54 | void setSampleRate(float sampleRate); 55 | void setBlockSize(VstInt32 blockSize); 56 | 57 | amuse::Engine& getAmuseEngine() { return *m_engine; } 58 | std::wstring_view getUserDir() const { return m_userDir; } 59 | AudioGroupFilePresenter& getFilePresenter() { return m_filePresenter; } 60 | 61 | void loadGroupFile(int collectionIdx, int fileIdx); 62 | void setGroup(int groupIdx, bool immediate); 63 | void _setNormalProgram(int programNo); 64 | void setNormalProgram(int programNo); 65 | void _setDrumProgram(int programNo); 66 | void setDrumProgram(int programNo); 67 | 68 | VstInt32 getChunk(void** data, bool isPreset); 69 | VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset); 70 | }; 71 | } // namespace amuse 72 | -------------------------------------------------------------------------------- /lib/EffectDelay.cpp: -------------------------------------------------------------------------------- 1 | #include "amuse/EffectDelay.hpp" 2 | 3 | #include 4 | 5 | #include "amuse/Common.hpp" 6 | #include "amuse/IBackendVoice.hpp" 7 | 8 | namespace amuse { 9 | 10 | template 11 | EffectDelayImp::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate) { 12 | initDelay = std::clamp(initDelay, 10u, 5000u); 13 | initFeedback = std::clamp(initFeedback, 0u, 100u); 14 | initOutput = std::clamp(initOutput, 0u, 100u); 15 | 16 | x3c_delay.fill(initDelay); 17 | x48_feedback.fill(initFeedback); 18 | x54_output.fill(initOutput); 19 | 20 | _setup(sampleRate); 21 | } 22 | 23 | template 24 | EffectDelayImp::EffectDelayImp(const EffectDelayInfo& info, double sampleRate) { 25 | for (size_t i = 0; i < NumChannels; ++i) { 26 | x3c_delay[i] = std::clamp(info.delay[i], 10u, 5000u); 27 | x48_feedback[i] = std::clamp(info.feedback[i], 0u, 100u); 28 | x54_output[i] = std::clamp(info.output[i], 0u, 100u); 29 | } 30 | 31 | _setup(sampleRate); 32 | } 33 | 34 | template 35 | void EffectDelayImp::_setup(double sampleRate) { 36 | m_sampsPerMs = std::ceil(sampleRate / 1000.0); 37 | m_blockSamples = m_sampsPerMs * 5; 38 | 39 | _update(); 40 | } 41 | 42 | template 43 | void EffectDelayImp::_update() { 44 | for (size_t i = 0; i < NumChannels; ++i) { 45 | x0_currentSize[i] = ((x3c_delay[i] - 5) * m_sampsPerMs + 159) / 160; 46 | xc_currentPos[i] = 0; 47 | x18_currentFeedback[i] = x48_feedback[i] * 128 / 100; 48 | x24_currentOutput[i] = x54_output[i] * 128 / 100; 49 | 50 | x30_chanLines[i] = std::make_unique(m_blockSamples * x0_currentSize[i]); 51 | } 52 | 53 | m_dirty = false; 54 | } 55 | 56 | template 57 | void EffectDelayImp::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) { 58 | if (m_dirty) 59 | _update(); 60 | 61 | for (size_t f = 0; f < frameCount;) { 62 | for (unsigned c = 0; c < chanMap.m_channelCount; ++c) { 63 | T* chanAud = audio + c; 64 | for (unsigned i = 0; i < m_blockSamples && f < frameCount; ++i, ++f) { 65 | T& liveSamp = chanAud[chanMap.m_channelCount * i]; 66 | T& samp = x30_chanLines[c][xc_currentPos[c] * m_blockSamples + i]; 67 | samp = ClampFull(samp * x18_currentFeedback[c] / 128 + liveSamp); 68 | liveSamp = samp * x24_currentOutput[c] / 128; 69 | } 70 | xc_currentPos[c] = (xc_currentPos[c] + 1) % x0_currentSize[c]; 71 | } 72 | audio += chanMap.m_channelCount * m_blockSamples; 73 | } 74 | } 75 | 76 | template class EffectDelayImp; 77 | template class EffectDelayImp; 78 | template class EffectDelayImp; 79 | } // namespace amuse 80 | -------------------------------------------------------------------------------- /Editor/ADSREditor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "EditorWidget.hpp" 11 | #include "ProjectModel.hpp" 12 | 13 | #include 14 | 15 | class ADSREditor; 16 | 17 | class QCheckBox; 18 | class QDoubleSpinBox; 19 | class QLabel; 20 | 21 | class ADSRView : public QWidget { 22 | Q_OBJECT 23 | friend class ADSRControls; 24 | amuse::ObjToken m_node; 25 | QFont m_gridFont; 26 | std::array m_percentTexts; 27 | std::vector m_timeTexts; 28 | int m_dragPoint = -1; 29 | uint64_t m_cycleIdx = 0; 30 | ADSREditor* getEditor() const; 31 | 32 | public: 33 | explicit ADSRView(QWidget* parent = Q_NULLPTR); 34 | ~ADSRView() override; 35 | 36 | void loadData(ProjectModel::ADSRNode* node); 37 | void unloadData(); 38 | ProjectModel::INode* currentNode() const; 39 | 40 | void paintEvent(QPaintEvent* ev) override; 41 | void mousePressEvent(QMouseEvent* ev) override; 42 | void mouseReleaseEvent(QMouseEvent* ev) override; 43 | void mouseMoveEvent(QMouseEvent* ev) override; 44 | }; 45 | 46 | class ADSRControls : public QFrame { 47 | Q_OBJECT 48 | friend class ADSRView; 49 | QDoubleSpinBox* m_attack; 50 | QDoubleSpinBox* m_decay; 51 | QDoubleSpinBox* m_sustain; 52 | QDoubleSpinBox* m_release; 53 | QCheckBox* m_dls; 54 | QLabel* m_velToAttackLab; 55 | QDoubleSpinBox* m_velToAttack; 56 | QLabel* m_keyToDecayLab; 57 | QDoubleSpinBox* m_keyToDecay; 58 | bool m_enableUpdate = true; 59 | ADSREditor* getEditor() const; 60 | void setAttackAndDecay(double attack, double decay, uint64_t cycleCount); 61 | void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount); 62 | void setRelease(double release, uint64_t cycleCount); 63 | 64 | public: 65 | explicit ADSRControls(QWidget* parent = Q_NULLPTR); 66 | ~ADSRControls() override; 67 | 68 | void loadData(); 69 | void unloadData(); 70 | 71 | public slots: 72 | void attackChanged(double val); 73 | void decayChanged(double val); 74 | void sustainChanged(double val); 75 | void releaseChanged(double val); 76 | void dlsStateChanged(int state); 77 | void velToAttackChanged(double val); 78 | void keyToDecayChanged(double val); 79 | }; 80 | 81 | class ADSREditor : public EditorWidget { 82 | Q_OBJECT 83 | friend class ADSRView; 84 | friend class ADSRControls; 85 | ADSRView* m_adsrView; 86 | ADSRControls* m_controls; 87 | 88 | public: 89 | explicit ADSREditor(QWidget* parent = Q_NULLPTR); 90 | ~ADSREditor() override; 91 | 92 | bool loadData(ProjectModel::ADSRNode* node); 93 | void unloadData() override; 94 | ProjectModel::INode* currentNode() const override; 95 | }; 96 | -------------------------------------------------------------------------------- /Editor/resources/IconSoundMacroTarget.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 47 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 60 | 65 | 72 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Editor/resources/IconSoundMacro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Editor/resources/IconSoundMacroTargetDisabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 47 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 60 | 65 | 72 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Editor/resources/IconSoundMacroDelete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 47 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 60 | 65 | 71 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Editor/resources/IconSoundMacroDeleteHovered.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 47 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 60 | 65 | 71 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Editor/resources/IconGroup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Editor/resources/IconNew.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Editor/resources/IconADSR.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 42 | 47 | 48 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 74 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Editor/resources/IconVolume1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Editor/resources/IconSample.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 42 | 47 | 48 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Editor/resources/IconLayers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 72 | 77 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Editor/StatusBarWidget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class StatusBarFocus; 12 | 13 | class FXButton : public QPushButton { 14 | Q_OBJECT 15 | public: 16 | explicit FXButton(QWidget* parent = Q_NULLPTR); 17 | void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); } 18 | void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); } 19 | void focusOutEvent(QFocusEvent* event) override { event->ignore(); } 20 | void keyPressEvent(QKeyEvent* event) override { event->ignore(); } 21 | }; 22 | 23 | class StatusBarWidget : public QStatusBar { 24 | friend class StatusBarFocus; 25 | Q_OBJECT 26 | QLabel m_normalMessage; 27 | QPushButton m_killButton; 28 | FXButton m_fxButton; 29 | std::array m_volumeIcons; 30 | QLabel m_volumeIcon; 31 | QSlider m_volumeSlider; 32 | QLabel m_aIcon; 33 | QSlider m_aSlider; 34 | QLabel m_bIcon; 35 | QSlider m_bSlider; 36 | int m_lastVolIdx = 0; 37 | QLabel m_voiceCount; 38 | int m_cachedVoiceCount = -1; 39 | StatusBarFocus* m_curFocus = nullptr; 40 | void setKillVisible(bool vis) { 41 | m_killButton.setVisible(vis); 42 | m_voiceCount.setVisible(vis); 43 | } 44 | 45 | public: 46 | explicit StatusBarWidget(QWidget* parent = Q_NULLPTR); 47 | ~StatusBarWidget() override; 48 | 49 | void setNormalMessage(const QString& message) { m_normalMessage.setText(message); } 50 | void setVoiceCount(int voices); 51 | 52 | template 53 | void connectKillClicked(const Receiver* receiver, void (Receiver::*method)()) { 54 | connect(&m_killButton, &QPushButton::clicked, receiver, method); 55 | } 56 | template 57 | void connectFXPressed(const Receiver* receiver, void (Receiver::*method)()) { 58 | connect(&m_fxButton, &FXButton::pressed, receiver, method); 59 | } 60 | void setFXDown(bool down) { m_fxButton.setDown(down); } 61 | 62 | template 63 | void connectVolumeSlider(const Receiver* receiver, void (Receiver::*method)(int)) { 64 | connect(&m_volumeSlider, qOverload(&QSlider::valueChanged), receiver, method); 65 | } 66 | template 67 | void connectASlider(const Receiver* receiver, void (Receiver::*method)(int)) { 68 | connect(&m_aSlider, qOverload(&QSlider::valueChanged), receiver, method); 69 | } 70 | template 71 | void connectBSlider(const Receiver* receiver, void (Receiver::*method)(int)) { 72 | connect(&m_bSlider, qOverload(&QSlider::valueChanged), receiver, method); 73 | } 74 | void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); } 75 | 76 | private slots: 77 | void volumeChanged(int vol); 78 | }; 79 | 80 | class StatusBarFocus : public QObject { 81 | Q_OBJECT 82 | QString m_message; 83 | 84 | public: 85 | explicit StatusBarFocus(StatusBarWidget* statusWidget) : QObject(statusWidget) {} 86 | ~StatusBarFocus() override { exit(); } 87 | void setMessage(const QString& message); 88 | void enter(); 89 | void exit(); 90 | }; 91 | -------------------------------------------------------------------------------- /Editor/resources/IconNewSoundMacro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Editor/resources/IconVolume2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 78 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /Editor/resources/IconNewGroup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Editor/resources/IconCurve.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 42 | 47 | 48 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 74 | 80 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Editor/resources/IconNewADSR.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 42 | 47 | 48 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 74 | 80 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Editor/Common.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.hpp" 2 | #include "MainWindow.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | std::string QStringToUTF8(const QString& str) { 11 | return str.toUtf8().toStdString(); 12 | } 13 | 14 | QString UTF8ToQString(const std::string& str) { 15 | return QString::fromStdString(str); 16 | } 17 | 18 | bool MkPath(const QString& path, UIMessenger& messenger) { 19 | QFileInfo fInfo(path); 20 | return MkPath(fInfo.dir(), fInfo.fileName(), messenger); 21 | } 22 | 23 | bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger) { 24 | if (!dir.mkpath(file)) { 25 | QString msg = QString(MainWindow::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file); 26 | messenger.critical(MainWindow::tr("Unable to create directory"), msg); 27 | return false; 28 | } 29 | return true; 30 | } 31 | 32 | void ShowInGraphicalShell(QWidget* parent, const QString& pathIn) { 33 | const QFileInfo fileInfo(pathIn); 34 | // Mac, Windows support folder or file. 35 | #if defined(Q_OS_WIN) 36 | QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path")); 37 | QString explorer; 38 | for (QString path : paths.split(QStringLiteral(";"))) { 39 | QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe")); 40 | if (finfo.exists()) { 41 | explorer = finfo.filePath(); 42 | break; 43 | } 44 | } 45 | if (explorer.isEmpty()) { 46 | QMessageBox::warning(parent, MainWindow::tr("Launching Windows Explorer Failed"), 47 | MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer.")); 48 | return; 49 | } 50 | QStringList param; 51 | if (!fileInfo.isDir()) 52 | param += QLatin1String("/select,"); 53 | param += QDir::toNativeSeparators(fileInfo.canonicalFilePath()); 54 | QProcess::startDetached(explorer, param); 55 | #elif defined(Q_OS_MAC) 56 | QStringList scriptArgs; 57 | scriptArgs << QLatin1String("-e") 58 | << QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"") 59 | .arg(fileInfo.canonicalFilePath()); 60 | QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs); 61 | scriptArgs.clear(); 62 | scriptArgs << QLatin1String("-e") << QLatin1String("tell application \"Finder\" to activate"); 63 | QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs); 64 | #else 65 | // we cannot select a file here, because no file browser really supports it... 66 | const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath(); 67 | QProcess browserProc; 68 | const QStringList browserArgs = QStringList() << QStringLiteral("%1").arg(QFileInfo(folder).path()); 69 | browserProc.startDetached(QStringLiteral("xdg-open"), browserArgs); 70 | #endif 71 | } 72 | 73 | QString ShowInGraphicalShellString() { 74 | #if defined(Q_OS_WIN) 75 | return MainWindow::tr("Show in Explorer"); 76 | #elif defined(Q_OS_MAC) 77 | return MainWindow::tr("Show in Finder"); 78 | #else 79 | return MainWindow::tr("Show in Browser"); 80 | #endif 81 | } 82 | 83 | QTransform RectToRect(const QRectF& from, const QRectF& to) { 84 | QPolygonF orig(from); 85 | orig.pop_back(); 86 | QPolygonF resize(to); 87 | resize.pop_back(); 88 | QTransform ret; 89 | QTransform::quadToQuad(orig, resize, ret); 90 | return ret; 91 | } 92 | -------------------------------------------------------------------------------- /Editor/KeyboardWidget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern const std::array NaturalKeyNames; 12 | extern const std::array SharpKeyNames; 13 | extern const std::array KeyStrings; 14 | extern const std::array NaturalKeyNumbers; 15 | extern const std::array SharpKeyNumbers; 16 | 17 | class KeyboardWidget; 18 | class StatusBarFocus; 19 | 20 | class KeyboardOctave : public QSvgWidget { 21 | Q_OBJECT 22 | int m_octave; 23 | std::array m_natural; 24 | std::array m_sharp; 25 | QTransform m_widgetToSvg; 26 | 27 | public: 28 | explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR); 29 | int getOctave() const { return m_octave; } 30 | int getKey(const QPoint& localPos) const; 31 | void resizeEvent(QResizeEvent* event) override; 32 | }; 33 | 34 | class KeyboardWidget : public QWidget { 35 | Q_OBJECT 36 | std::array m_widgets{}; 37 | StatusBarFocus* m_statusFocus = nullptr; 38 | int m_lastOctave = -1; 39 | int m_lastKey = -1; 40 | bool m_holding = false; 41 | 42 | std::pair _getOctaveAndKey(QMouseEvent* event) const; 43 | void _startKey(int octave, int key); 44 | void _stopKey(); 45 | void _moveOnKey(int octave, int key); 46 | void _pressOnKey(int octave, int key); 47 | 48 | public: 49 | explicit KeyboardWidget(QWidget* parent = Q_NULLPTR); 50 | ~KeyboardWidget() override; 51 | 52 | void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; } 53 | 54 | void mouseMoveEvent(QMouseEvent* event) override; 55 | void mousePressEvent(QMouseEvent* event) override; 56 | void mouseReleaseEvent(QMouseEvent* event) override; 57 | void enterEvent(QEnterEvent* event) override; 58 | void leaveEvent(QEvent* event) override; 59 | void wheelEvent(QWheelEvent* event) override; 60 | void showEvent(QShowEvent* event) override; 61 | 62 | signals: 63 | void notePressed(int key); 64 | void noteReleased(); 65 | }; 66 | 67 | class KeyboardSlider : public QSlider { 68 | Q_OBJECT 69 | protected: 70 | StatusBarFocus* m_statusFocus = nullptr; 71 | virtual QString stringOfValue(int value) const = 0; 72 | 73 | public: 74 | explicit KeyboardSlider(QWidget* parent = Q_NULLPTR); 75 | ~KeyboardSlider() override; 76 | 77 | void enterEvent(QEnterEvent* event) override; 78 | void leaveEvent(QEvent* event) override; 79 | void setStatusFocus(StatusBarFocus* statusFocus); 80 | void sliderChange(SliderChange change) override; 81 | }; 82 | 83 | class VelocitySlider : public KeyboardSlider { 84 | Q_OBJECT 85 | QString stringOfValue(int value) const override; 86 | 87 | public: 88 | explicit VelocitySlider(QWidget* parent = Q_NULLPTR); 89 | }; 90 | 91 | class ModulationSlider : public KeyboardSlider { 92 | Q_OBJECT 93 | QString stringOfValue(int value) const override; 94 | 95 | public: 96 | explicit ModulationSlider(QWidget* parent = Q_NULLPTR); 97 | }; 98 | 99 | class PitchSlider : public KeyboardSlider { 100 | Q_OBJECT 101 | QString stringOfValue(int value) const override; 102 | 103 | public: 104 | explicit PitchSlider(QWidget* parent = Q_NULLPTR); 105 | void mouseReleaseEvent(QMouseEvent* ev) override; 106 | void wheelEvent(QWheelEvent* ev) override { ev->ignore(); } 107 | }; 108 | -------------------------------------------------------------------------------- /Editor/resources/IconVolume3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 78 | 84 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Editor/resources/IconNewLayers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 72 | 77 | 82 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Editor/resources/IconNewCurve.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 42 | 47 | 48 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 74 | 80 | 86 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Editor/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "MainWindow.hpp" 6 | #include "SongGroupEditor.hpp" 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std::literals; 12 | 13 | #ifdef __APPLE__ 14 | void MacOSSetDarkAppearance(); 15 | #endif 16 | 17 | extern "C" const uint8_t MAINICON_QT[]; 18 | 19 | static QIcon MakeAppIcon() { 20 | QIcon ret; 21 | 22 | const uint8_t* ptr = MAINICON_QT; 23 | for (int i = 0; i < 6; ++i) { 24 | uint32_t size = *reinterpret_cast(ptr); 25 | ptr += 4; 26 | 27 | QPixmap pm; 28 | pm.loadFromData(ptr, size); 29 | ret.addPixmap(pm); 30 | ptr += size; 31 | } 32 | 33 | return ret; 34 | } 35 | 36 | MainWindow* g_MainWindow = nullptr; 37 | 38 | int main(int argc, char* argv[]) { 39 | QApplication::setAttribute(Qt::AA_Use96Dpi); 40 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) 41 | QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 42 | QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 43 | #endif 44 | QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create(QStringLiteral("Fusion")))); 45 | QApplication a(argc, argv); 46 | QApplication::setWindowIcon(MakeAppIcon()); 47 | 48 | a.setOrganizationName(QStringLiteral("AxioDL")); 49 | a.setApplicationName(QStringLiteral("Amuse")); 50 | 51 | QPalette darkPalette; 52 | darkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); 53 | darkPalette.setColor(QPalette::WindowText, Qt::white); 54 | darkPalette.setColor(QPalette::Base, QColor(42, 42, 42)); 55 | darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25, 25, 25, 53)); 56 | darkPalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); 57 | darkPalette.setColor(QPalette::ToolTipBase, QColor(42, 42, 42)); 58 | darkPalette.setColor(QPalette::ToolTipText, Qt::white); 59 | darkPalette.setColor(QPalette::Text, Qt::white); 60 | darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255, 255, 255, 120)); 61 | darkPalette.setColor(QPalette::Disabled, QPalette::Light, QColor(0, 0, 0, 0)); 62 | darkPalette.setColor(QPalette::Button, QColor(53, 53, 53)); 63 | darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53, 53, 53, 53)); 64 | darkPalette.setColor(QPalette::ButtonText, Qt::white); 65 | darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255, 255, 255, 120)); 66 | darkPalette.setColor(QPalette::BrightText, Qt::red); 67 | darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); 68 | darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); 69 | darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(42, 130, 218, 53)); 70 | darkPalette.setColor(QPalette::HighlightedText, Qt::white); 71 | darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255, 255, 255, 120)); 72 | a.setPalette(darkPalette); 73 | 74 | #ifdef __APPLE__ 75 | MacOSSetDarkAppearance(); 76 | #endif 77 | 78 | logvisor::RegisterConsoleLogger(); 79 | logvisor::RegisterStandardExceptions(); 80 | 81 | Q_INIT_RESOURCE(translation_res); 82 | QTranslator translator; 83 | if (translator.load(QLocale(), QStringLiteral("lang"), QStringLiteral("_"), QStringLiteral(":/translations"))) { 84 | a.installTranslator(&translator); 85 | } 86 | 87 | MainWindow w; 88 | g_MainWindow = &w; 89 | w.show(); 90 | 91 | QCommandLineParser parser; 92 | parser.process(a); 93 | QStringList args = parser.positionalArguments(); 94 | if (!args.empty()) 95 | w.openProject(args.back()); 96 | 97 | return a.exec(); 98 | } 99 | -------------------------------------------------------------------------------- /Editor/resources/IconSoundGroup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 42 | 47 | 48 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Editor/resources/IconA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 50 | 51 | 53 | 54 | 56 | image/svg+xml 57 | 59 | 60 | 61 | 62 | 63 | 68 | 72 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Editor/resources/IconB.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 50 | 51 | 53 | 54 | 56 | image/svg+xml 57 | 59 | 60 | 61 | 62 | 63 | 68 | 72 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Editor/StatusBarWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "StatusBarWidget.hpp" 2 | 3 | #include 4 | 5 | FXButton::FXButton(QWidget* parent) : QPushButton(parent) { 6 | setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg"))); 7 | setToolTip(tr("Access studio setup window for experimenting with audio effects")); 8 | } 9 | 10 | StatusBarWidget::StatusBarWidget(QWidget* parent) 11 | : QStatusBar(parent), m_volumeSlider(Qt::Horizontal), m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal) { 12 | addWidget(&m_normalMessage); 13 | m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg"))); 14 | m_killButton.setVisible(false); 15 | m_killButton.setToolTip(tr("Immediately kill active voices")); 16 | m_voiceCount.setVisible(false); 17 | for (size_t i = 0; i < m_volumeIcons.size(); i++) { 18 | m_volumeIcons[i] = QIcon(QStringLiteral(":/icons/IconVolume%1.svg").arg(i)); 19 | } 20 | m_aIcon.setFixedSize(16, 16); 21 | m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16)); 22 | QString aTip = tr("Aux A send level for all voices"); 23 | m_aIcon.setToolTip(aTip); 24 | m_aSlider.setRange(0, 100); 25 | m_aSlider.setFixedWidth(100); 26 | m_aSlider.setToolTip(aTip); 27 | m_bIcon.setFixedSize(16, 16); 28 | m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16)); 29 | QString bTip = tr("Aux B send level for all voices"); 30 | m_bIcon.setToolTip(bTip); 31 | m_bSlider.setRange(0, 100); 32 | m_bSlider.setFixedWidth(100); 33 | m_bSlider.setToolTip(bTip); 34 | m_volumeIcon.setFixedSize(16, 16); 35 | m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16)); 36 | QString volTip = tr("Master volume level"); 37 | m_volumeIcon.setToolTip(volTip); 38 | connect(&m_volumeSlider, qOverload(&QSlider::valueChanged), this, &StatusBarWidget::volumeChanged); 39 | m_volumeSlider.setRange(0, 100); 40 | m_volumeSlider.setFixedWidth(100); 41 | m_volumeSlider.setToolTip(volTip); 42 | addPermanentWidget(&m_voiceCount); 43 | addPermanentWidget(&m_killButton); 44 | addPermanentWidget(&m_fxButton); 45 | addPermanentWidget(&m_aIcon); 46 | addPermanentWidget(&m_aSlider); 47 | addPermanentWidget(&m_bIcon); 48 | addPermanentWidget(&m_bSlider); 49 | addPermanentWidget(&m_volumeIcon); 50 | addPermanentWidget(&m_volumeSlider); 51 | } 52 | 53 | StatusBarWidget::~StatusBarWidget() = default; 54 | 55 | void StatusBarWidget::setVoiceCount(int voices) { 56 | if (voices != m_cachedVoiceCount) { 57 | m_voiceCount.setText(QString::number(voices)); 58 | m_cachedVoiceCount = voices; 59 | setKillVisible(voices != 0); 60 | } 61 | } 62 | 63 | void StatusBarWidget::volumeChanged(int vol) { 64 | const int idx = int(std::round(vol * (3.f / 100.f))); 65 | 66 | if (idx == m_lastVolIdx) { 67 | return; 68 | } 69 | 70 | m_lastVolIdx = idx; 71 | m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16)); 72 | } 73 | 74 | void StatusBarFocus::setMessage(const QString& message) { 75 | m_message = message; 76 | if (StatusBarWidget* widget = qobject_cast(parent())) { 77 | if (widget->m_curFocus == this) { 78 | if (m_message.isEmpty()) 79 | widget->clearMessage(); 80 | else 81 | widget->showMessage(m_message); 82 | } 83 | } 84 | } 85 | 86 | void StatusBarFocus::enter() { 87 | if (StatusBarWidget* widget = qobject_cast(parent())) { 88 | widget->m_curFocus = this; 89 | if (m_message.isEmpty()) 90 | widget->clearMessage(); 91 | else 92 | widget->showMessage(m_message); 93 | } 94 | } 95 | 96 | void StatusBarFocus::exit() { 97 | if (StatusBarWidget* widget = qobject_cast(parent())) { 98 | if (widget->m_curFocus == this) { 99 | widget->clearMessage(); 100 | widget->m_curFocus = nullptr; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Editor/resources/IconKeymap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 74 | 79 | 84 | 89 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /VST/AudioGroupFilePresenter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define WIN32_LEAN_AND_MEAN 10 | #include 11 | #include 12 | 13 | namespace amuse { 14 | class VSTBackend; 15 | class VSTEditor; 16 | class AudioGroupFilePresenter; 17 | 18 | struct AudioGroupDataCollection { 19 | std::wstring m_path; 20 | std::wstring m_name; 21 | 22 | std::vector m_projData; 23 | std::vector m_poolData; 24 | std::vector m_sdirData; 25 | std::vector m_sampData; 26 | 27 | struct MetaData { 28 | amuse::DataFormat fmt; 29 | uint32_t absOffs; 30 | uint32_t active; 31 | MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn) 32 | : FMT_STRING(fmtIn), absOffs(absOffsIn), active(activeIn) {} 33 | MetaData(athena::io::FileReader& r) 34 | : FMT_STRING(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {} 35 | }; 36 | std::optional m_metaData; 37 | 38 | std::optional m_loadedData; 39 | const amuse::AudioGroup* m_loadedGroup; 40 | struct GroupToken { 41 | int m_groupId; 42 | const amuse::SongGroupIndex* m_song = nullptr; 43 | const amuse::SFXGroupIndex* m_sfx = nullptr; 44 | GroupToken(int id, const amuse::SongGroupIndex* song) : m_groupId(id), m_song(song) {} 45 | GroupToken(int id, const amuse::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {} 46 | }; 47 | std::vector m_groupTokens; 48 | 49 | bool loadProj(); 50 | bool loadPool(); 51 | bool loadSdir(); 52 | bool loadSamp(); 53 | bool loadMeta(); 54 | 55 | AudioGroupDataCollection(std::wstring_view path, std::wstring_view name); 56 | bool isDataComplete() const { 57 | return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData; 58 | } 59 | bool _attemptLoad(); 60 | bool _indexData(); 61 | 62 | void addToEngine(amuse::Engine& engine); 63 | void removeFromEngine(amuse::Engine& engine) const; 64 | }; 65 | 66 | struct AudioGroupCollection { 67 | using GroupIterator = std::map>::iterator; 68 | std::wstring m_path; 69 | std::wstring m_name; 70 | 71 | std::map> m_groups; 72 | std::vector m_iteratorVec; 73 | 74 | AudioGroupCollection(std::wstring_view path, std::wstring_view name); 75 | void addCollection(std::vector>&& collection); 76 | void update(AudioGroupFilePresenter& presenter); 77 | void populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx); 78 | }; 79 | 80 | class AudioGroupFilePresenter { 81 | friend class VSTBackend; 82 | 83 | public: 84 | using CollectionIterator = std::map>::iterator; 85 | 86 | private: 87 | VSTBackend& m_backend; 88 | std::map> m_audioGroupCollections; 89 | std::vector m_iteratorVec; 90 | 91 | public: 92 | AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {} 93 | void update(); 94 | void populateCollectionColumn(VSTEditor& editor); 95 | void populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx); 96 | void populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx); 97 | void addCollection(std::wstring_view name, 98 | std::vector>&& collection); 99 | void removeCollection(unsigned idx); 100 | VSTBackend& getBackend() { return m_backend; } 101 | }; 102 | } // namespace amuse 103 | -------------------------------------------------------------------------------- /Editor/resources/IconOpen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 79 | 85 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /lib/VolumeTable.cpp: -------------------------------------------------------------------------------- 1 | #include "amuse/VolumeTable.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace amuse { 8 | 9 | constexpr std::array VolumeTable{ 10 | 0.000000f, 0.000031f, 0.000153f, 0.000397f, 0.000702f, 0.001129f, 0.001648f, 0.002228f, 0.002930f, 0.003723f, 11 | 0.004608f, 0.005585f, 0.006653f, 0.007843f, 0.009125f, 0.010498f, 0.011963f, 0.013550f, 0.015198f, 0.016999f, 12 | 0.018860f, 0.020844f, 0.022919f, 0.025117f, 0.027406f, 0.029817f, 0.032319f, 0.034944f, 0.037660f, 0.040468f, 13 | 0.043428f, 0.046480f, 0.049623f, 0.052889f, 0.056276f, 0.059786f, 0.063387f, 0.067110f, 0.070956f, 0.074923f, 14 | 0.078982f, 0.083163f, 0.087466f, 0.091922f, 0.096469f, 0.101138f, 0.105930f, 0.110843f, 0.115879f, 0.121036f, 15 | 0.126347f, 0.131748f, 0.137303f, 0.142979f, 0.148778f, 0.154729f, 0.160772f, 0.166997f, 0.173315f, 0.179785f, 16 | 0.186407f, 0.193121f, 0.200018f, 0.207007f, 0.214179f, 0.221473f, 0.228919f, 0.236488f, 0.244209f, 0.252083f, 17 | 0.260079f, 0.268258f, 0.276559f, 0.285012f, 0.293649f, 0.302408f, 0.311319f, 0.320383f, 0.329600f, 0.339000f, 18 | 0.348521f, 0.358226f, 0.368084f, 0.378094f, 0.388287f, 0.398633f, 0.409131f, 0.419813f, 0.430647f, 0.441664f, 19 | 0.452864f, 0.464217f, 0.475753f, 0.487442f, 0.499313f, 0.511399f, 0.523606f, 0.536027f, 0.548631f, 0.561419f, 20 | 0.574389f, 0.587542f, 0.600879f, 0.614399f, 0.628132f, 0.642018f, 0.656148f, 0.670431f, 0.684927f, 0.699637f, 21 | 0.714530f, 0.729637f, 0.744926f, 0.760430f, 0.776147f, 0.792077f, 0.808191f, 0.824549f, 0.841090f, 0.857845f, 22 | 0.874844f, 0.892056f, 0.909452f, 0.927122f, 0.945006f, 0.963073f, 0.981414f, 1.000000f, 1.000000f, 23 | }; 24 | 25 | constexpr std::array DLSVolumeTable{ 26 | 0.000000f, 0.000062f, 0.000248f, 0.000558f, 0.000992f, 0.001550f, 0.002232f, 0.003038f, 0.003968f, 0.005022f, 27 | 0.006200f, 0.007502f, 0.008928f, 0.010478f, 0.012152f, 0.013950f, 0.015872f, 0.017918f, 0.020088f, 0.022382f, 28 | 0.024800f, 0.027342f, 0.030008f, 0.032798f, 0.035712f, 0.038750f, 0.041912f, 0.045198f, 0.048608f, 0.052142f, 29 | 0.055800f, 0.059582f, 0.063488f, 0.067518f, 0.071672f, 0.075950f, 0.080352f, 0.084878f, 0.089528f, 0.094302f, 30 | 0.099200f, 0.104222f, 0.109368f, 0.114638f, 0.120032f, 0.125550f, 0.131192f, 0.136958f, 0.142848f, 0.148862f, 31 | 0.155000f, 0.161262f, 0.167648f, 0.174158f, 0.180792f, 0.187550f, 0.194432f, 0.201438f, 0.208568f, 0.215822f, 32 | 0.223200f, 0.230702f, 0.238328f, 0.246078f, 0.253953f, 0.261951f, 0.270073f, 0.278319f, 0.286689f, 0.295183f, 33 | 0.303801f, 0.312543f, 0.321409f, 0.330399f, 0.339513f, 0.348751f, 0.358113f, 0.367599f, 0.377209f, 0.386943f, 34 | 0.396801f, 0.406783f, 0.416889f, 0.427119f, 0.437473f, 0.447951f, 0.458553f, 0.469279f, 0.480129f, 0.491103f, 35 | 0.502201f, 0.513423f, 0.524769f, 0.536239f, 0.547833f, 0.559551f, 0.571393f, 0.583359f, 0.595449f, 0.607663f, 36 | 0.620001f, 0.632463f, 0.645049f, 0.657759f, 0.670593f, 0.683551f, 0.696633f, 0.709839f, 0.723169f, 0.736623f, 37 | 0.750202f, 0.763904f, 0.777730f, 0.791680f, 0.805754f, 0.819952f, 0.834274f, 0.848720f, 0.863290f, 0.877984f, 38 | 0.892802f, 0.907744f, 0.922810f, 0.938000f, 0.953314f, 0.968752f, 0.984314f, 1.000000f, 1.000000f, 39 | }; 40 | 41 | float LookupVolume(float vol) { 42 | vol = std::clamp(vol * 127.f, 0.f, 127.f); 43 | const float f = std::floor(vol); 44 | const float c = std::ceil(vol); 45 | 46 | if (f == c) { 47 | return VolumeTable[int(f)]; 48 | } 49 | 50 | const float t = vol - f; 51 | return (1.f - t) * VolumeTable[int(f)] + t * VolumeTable[int(c)]; 52 | } 53 | 54 | float LookupDLSVolume(float vol) { 55 | vol = std::clamp(vol * 127.f, 0.f, 127.f); 56 | const float f = std::floor(vol); 57 | const float c = std::ceil(vol); 58 | 59 | if (f == c) { 60 | return DLSVolumeTable[int(f)]; 61 | } 62 | 63 | const float t = vol - f; 64 | return (1.f - t) * DLSVolumeTable[int(f)] + t * DLSVolumeTable[int(c)]; 65 | } 66 | 67 | } // namespace amuse 68 | -------------------------------------------------------------------------------- /Editor/KeymapEditor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "EditorWidget.hpp" 14 | #include "ProjectModel.hpp" 15 | 16 | #include 17 | #include 18 | 19 | class KeymapEditor; 20 | class QCheckBox; 21 | class QScrollArea; 22 | class QSpinBox; 23 | 24 | class PaintButton : public QPushButton { 25 | Q_OBJECT 26 | public: 27 | explicit PaintButton(QWidget* parent = Q_NULLPTR); 28 | void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); } 29 | void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); } 30 | void focusOutEvent(QFocusEvent* event) override { event->ignore(); } 31 | void keyPressEvent(QKeyEvent* event) override { event->ignore(); } 32 | }; 33 | 34 | using PaintPalette = std::array; 35 | 36 | class KeymapView : public QWidget { 37 | Q_OBJECT 38 | friend class KeymapControls; 39 | friend class KeymapEditor; 40 | amuse::ObjToken m_node; 41 | QSvgRenderer m_octaveRenderer; 42 | QSvgRenderer m_lastOctaveRenderer; 43 | std::array m_natural; 44 | std::array m_sharp; 45 | QTransform m_widgetToSvg; 46 | QFont m_keyFont; 47 | std::array m_keyTexts; 48 | std::array m_keyPalettes; 49 | KeymapEditor* getEditor() const; 50 | int getKey(const QPoint& localPos) const; 51 | void drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const PaintPalette& keyPalette, int o, 52 | int k) const; 53 | 54 | public: 55 | explicit KeymapView(QWidget* parent = Q_NULLPTR); 56 | ~KeymapView() override; 57 | 58 | void loadData(ProjectModel::KeymapNode* node); 59 | void unloadData(); 60 | ProjectModel::INode* currentNode() const; 61 | 62 | void paintEvent(QPaintEvent* ev) override; 63 | void mousePressEvent(QMouseEvent* ev) override; 64 | void mouseMoveEvent(QMouseEvent* ev) override; 65 | void wheelEvent(QWheelEvent* event) override; 66 | }; 67 | 68 | class KeymapControls : public QFrame { 69 | Q_OBJECT 70 | friend class KeymapView; 71 | friend class KeymapEditor; 72 | FieldProjectNode* m_macro; 73 | QSpinBox* m_transpose; 74 | QSpinBox* m_pan; 75 | QCheckBox* m_surround; 76 | QSpinBox* m_prioOffset; 77 | PaintButton* m_paintButton; 78 | bool m_enableUpdate = true; 79 | KeymapEditor* getEditor() const; 80 | void setPaintIdx(int idx); 81 | void setKeymap(const amuse::Keymap& km); 82 | 83 | public: 84 | explicit KeymapControls(QWidget* parent = Q_NULLPTR); 85 | ~KeymapControls() override; 86 | 87 | void loadData(ProjectModel::KeymapNode* node); 88 | void unloadData(); 89 | 90 | public slots: 91 | void controlChanged(); 92 | void paintButtonPressed(); 93 | }; 94 | 95 | class KeymapEditor : public EditorWidget { 96 | Q_OBJECT 97 | friend class KeymapView; 98 | friend class KeymapControls; 99 | QScrollArea* m_scrollArea; 100 | KeymapView* m_kmView; 101 | KeymapControls* m_controls; 102 | PaintPalette m_paintPalette; 103 | amuse::Keymap m_controlKeymap; 104 | std::unordered_map> m_configToIdx; 105 | std::bitset<129> m_idxBitmap; 106 | bool m_inPaint = false; 107 | void _touch(); 108 | void touchKey(int key, bool bulk = false); 109 | void touchControl(const amuse::Keymap& km); 110 | int allocateConfigIdx(uint64_t key); 111 | void deallocateConfigIdx(uint64_t key); 112 | int getConfigIdx(uint64_t key) const; 113 | 114 | public: 115 | explicit KeymapEditor(QWidget* parent = Q_NULLPTR); 116 | ~KeymapEditor() override; 117 | 118 | bool loadData(ProjectModel::KeymapNode* node); 119 | void unloadData() override; 120 | ProjectModel::INode* currentNode() const override; 121 | void keyPressEvent(QKeyEvent* event) override; 122 | }; 123 | -------------------------------------------------------------------------------- /Editor/resources/IconSongGroup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 78 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Editor/resources/IconNewSoundGroup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 79 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /include/amuse/AudioGroupData.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "amuse/Common.hpp" 6 | 7 | namespace amuse { 8 | 9 | /** Simple pointer-container of the four Audio Group chunks */ 10 | class AudioGroupData { 11 | friend class Engine; 12 | 13 | protected: 14 | unsigned char* m_proj; 15 | size_t m_projSz; 16 | unsigned char* m_pool; 17 | size_t m_poolSz; 18 | unsigned char* m_sdir; 19 | size_t m_sdirSz; 20 | unsigned char* m_samp; 21 | size_t m_sampSz; 22 | 23 | DataFormat m_fmt; 24 | bool m_absOffs; 25 | 26 | AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir, 27 | size_t sdirSz, unsigned char* samp, size_t sampSz, DataFormat fmt, bool absOffs) 28 | : m_proj(proj) 29 | , m_projSz(projSz) 30 | , m_pool(pool) 31 | , m_poolSz(poolSz) 32 | , m_sdir(sdir) 33 | , m_sdirSz(sdirSz) 34 | , m_samp(samp) 35 | , m_sampSz(sampSz) 36 | , m_fmt(fmt) 37 | , m_absOffs(absOffs) {} 38 | 39 | public: 40 | AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir, 41 | size_t sdirSz, unsigned char* samp, size_t sampSz, GCNDataTag) 42 | : m_proj(proj) 43 | , m_projSz(projSz) 44 | , m_pool(pool) 45 | , m_poolSz(poolSz) 46 | , m_sdir(sdir) 47 | , m_sdirSz(sdirSz) 48 | , m_samp(samp) 49 | , m_sampSz(sampSz) 50 | , m_fmt(DataFormat::GCN) 51 | , m_absOffs(true) {} 52 | AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir, 53 | size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, N64DataTag) 54 | : m_proj(proj) 55 | , m_projSz(projSz) 56 | , m_pool(pool) 57 | , m_poolSz(poolSz) 58 | , m_sdir(sdir) 59 | , m_sdirSz(sdirSz) 60 | , m_samp(samp) 61 | , m_sampSz(sampSz) 62 | , m_fmt(DataFormat::N64) 63 | , m_absOffs(absOffs) {} 64 | AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir, 65 | size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, PCDataTag) 66 | : m_proj(proj) 67 | , m_projSz(projSz) 68 | , m_pool(pool) 69 | , m_poolSz(poolSz) 70 | , m_sdir(sdir) 71 | , m_sdirSz(sdirSz) 72 | , m_samp(samp) 73 | , m_sampSz(sampSz) 74 | , m_fmt(DataFormat::PC) 75 | , m_absOffs(absOffs) {} 76 | 77 | const unsigned char* getProj() const { return m_proj; } 78 | const unsigned char* getPool() const { return m_pool; } 79 | const unsigned char* getSdir() const { return m_sdir; } 80 | const unsigned char* getSamp() const { return m_samp; } 81 | 82 | unsigned char* getProj() { return m_proj; } 83 | unsigned char* getPool() { return m_pool; } 84 | unsigned char* getSdir() { return m_sdir; } 85 | unsigned char* getSamp() { return m_samp; } 86 | 87 | size_t getProjSize() const { return m_projSz; } 88 | size_t getPoolSize() const { return m_poolSz; } 89 | size_t getSdirSize() const { return m_sdirSz; } 90 | size_t getSampSize() const { return m_sampSz; } 91 | 92 | explicit operator bool() const { 93 | return m_proj != nullptr && m_pool != nullptr && m_sdir != nullptr && m_samp != nullptr; 94 | } 95 | 96 | DataFormat getDataFormat() const { return m_fmt; } 97 | bool getAbsoluteProjOffsets() const { return m_absOffs; } 98 | }; 99 | 100 | /** A buffer-owning version of AudioGroupData */ 101 | class IntrusiveAudioGroupData : public AudioGroupData { 102 | bool m_owns = true; 103 | 104 | public: 105 | using AudioGroupData::AudioGroupData; 106 | ~IntrusiveAudioGroupData(); 107 | 108 | IntrusiveAudioGroupData(const IntrusiveAudioGroupData&) = delete; 109 | IntrusiveAudioGroupData& operator=(const IntrusiveAudioGroupData&) = delete; 110 | 111 | IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept; 112 | IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other) noexcept; 113 | 114 | void dangleOwnership() { m_owns = false; } 115 | }; 116 | } // namespace amuse 117 | -------------------------------------------------------------------------------- /Editor/resources/IconNewSongGroup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 49 | 50 | 52 | 53 | 55 | image/svg+xml 56 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 78 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Editor/SampleEditor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "EditorWidget.hpp" 10 | #include "ProjectModel.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | class SampleEditor; 17 | 18 | class QCheckBox; 19 | class QPushButton; 20 | class QScrollArea; 21 | class QSlider; 22 | 23 | class SampleView : public QWidget { 24 | Q_OBJECT 25 | friend class SampleControls; 26 | qreal m_baseSamplesPerPx = 100.0; 27 | qreal m_samplesPerPx = 100.0; 28 | qreal m_zoomFactor = 1.0; 29 | amuse::ObjToken m_node; 30 | amuse::ObjToken m_sample; 31 | amuse::ObjToken m_playbackMacro; 32 | const unsigned char* m_sampleData = nullptr; 33 | qreal m_curSamplePos = 0.0; 34 | int16_t m_prev1 = 0; 35 | int16_t m_prev2 = 0; 36 | QFont m_rulerFont; 37 | int m_displaySamplePos = -1; 38 | enum class DragState { None, Start, End }; 39 | DragState m_dragState = DragState::None; 40 | void seekToSample(qreal sample); 41 | std::pair, std::pair> iterateSampleInterval(qreal interval); 42 | void calculateSamplesPerPx(); 43 | SampleEditor* getEditor() const; 44 | 45 | public: 46 | explicit SampleView(QWidget* parent = Q_NULLPTR); 47 | ~SampleView() override; 48 | 49 | bool loadData(ProjectModel::SampleNode* node); 50 | void unloadData(); 51 | ProjectModel::INode* currentNode() const; 52 | amuse::SampleEntryData* entryData() const; 53 | const amuse::SoundMacro* soundMacro() const; 54 | void setSamplePos(int pos); 55 | void updateSampleRange(int oldSamp, int newSamp); 56 | 57 | void paintEvent(QPaintEvent* ev) override; 58 | void resetZoom(); 59 | void setZoom(int zVal); 60 | void showEvent(QShowEvent* ev) override; 61 | void mousePressEvent(QMouseEvent* ev) override; 62 | void mouseReleaseEvent(QMouseEvent* ev) override; 63 | void mouseMoveEvent(QMouseEvent* ev) override; 64 | void wheelEvent(QWheelEvent* ev) override; 65 | }; 66 | 67 | class SampleControls : public QFrame { 68 | Q_OBJECT 69 | QString m_path; 70 | QSlider* m_zoomSlider; 71 | QCheckBox* m_loopCheck; 72 | QSpinBox* m_loopStart; 73 | QSpinBox* m_loopEnd; 74 | QSpinBox* m_basePitch; 75 | QPushButton* m_makeOtherVersion; 76 | QMetaObject::Connection m_makeOtherConn; 77 | QPushButton* m_showInBrowser; 78 | bool m_enableUpdate = true; 79 | bool m_enableFileWrite = true; 80 | 81 | public: 82 | explicit SampleControls(QWidget* parent = Q_NULLPTR); 83 | ~SampleControls() override; 84 | 85 | void doFileWrite(); 86 | void setFileWrite(bool w); 87 | void updateFileState(); 88 | void setLoopStartSample(int sample) { m_loopStart->setValue(sample); } 89 | void setLoopEndSample(int sample) { m_loopEnd->setValue(sample); } 90 | void loadData(bool reset); 91 | void unloadData(); 92 | 93 | public slots: 94 | void zoomSliderChanged(int val); 95 | void loopStateChanged(int state); 96 | void startValueChanged(int val); 97 | void endValueChanged(int val); 98 | void pitchValueChanged(int val); 99 | void makeWAVVersion(); 100 | void makeCompressedVersion(); 101 | void showInBrowser(); 102 | }; 103 | 104 | class SampleEditor : public EditorWidget { 105 | Q_OBJECT 106 | friend class SampleView; 107 | friend class SampleControls; 108 | friend class SampLoopUndoCommand; 109 | friend class SampPitchUndoCommand; 110 | QScrollArea* m_scrollArea; 111 | SampleView* m_sampleView; 112 | SampleControls* m_controls; 113 | 114 | public: 115 | explicit SampleEditor(QWidget* parent = Q_NULLPTR); 116 | ~SampleEditor() override; 117 | 118 | bool loadData(ProjectModel::SampleNode* node); 119 | void unloadData() override; 120 | ProjectModel::INode* currentNode() const override; 121 | const amuse::SoundMacro* soundMacro() const; 122 | void setSamplePos(int pos); 123 | 124 | void resizeEvent(QResizeEvent* ev) override; 125 | }; 126 | -------------------------------------------------------------------------------- /Editor/LayersEditor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "EditorWidget.hpp" 9 | #include "ProjectModel.hpp" 10 | 11 | #include 12 | #include 13 | 14 | class SoundMacroDelegate : public BaseObjectDelegate { 15 | Q_OBJECT 16 | protected: 17 | ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override; 18 | 19 | public: 20 | explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR); 21 | ~SoundMacroDelegate() override; 22 | 23 | QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; 24 | void setEditorData(QWidget* editor, const QModelIndex& index) const override; 25 | void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; 26 | 27 | private slots: 28 | void smIndexChanged(); 29 | }; 30 | 31 | class LayersModel : public QAbstractTableModel { 32 | Q_OBJECT 33 | friend class LayersEditor; 34 | friend class SoundMacroDelegate; 35 | friend class LayersTableView; 36 | amuse::ObjToken m_node; 37 | 38 | public: 39 | explicit LayersModel(QObject* parent = Q_NULLPTR); 40 | ~LayersModel() override; 41 | 42 | void loadData(ProjectModel::LayersNode* node); 43 | void unloadData(); 44 | 45 | int rowCount(const QModelIndex& parent = QModelIndex()) const override; 46 | int columnCount(const QModelIndex& parent = QModelIndex()) const override; 47 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 48 | bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; 49 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 50 | Qt::ItemFlags flags(const QModelIndex& index) const override; 51 | Qt::DropActions supportedDropActions() const override; 52 | Qt::DropActions supportedDragActions() const override; 53 | bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, 54 | const QModelIndex& parent) override; 55 | 56 | bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; 57 | bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, 58 | int destinationChild) override; 59 | bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; 60 | 61 | void _insertRow(int row, const amuse::LayerMapping& data); 62 | amuse::LayerMapping _removeRow(int row); 63 | }; 64 | 65 | class LayersTableView : public QTableView { 66 | Q_OBJECT 67 | SoundMacroDelegate m_smDelegate; 68 | RangedValueFactory<-128, 127> m_signedFactory; 69 | RangedValueFactory<0, 127> m_unsignedFactory; 70 | QStyledItemDelegate m_signedDelegate, m_unsignedDelegate; 71 | 72 | public: 73 | explicit LayersTableView(QWidget* parent = Q_NULLPTR); 74 | ~LayersTableView() override; 75 | 76 | void setModel(QAbstractItemModel* model) override; 77 | void deleteSelection(); 78 | }; 79 | 80 | class LayersEditor : public EditorWidget { 81 | Q_OBJECT 82 | friend class LayersModel; 83 | LayersModel m_model; 84 | LayersTableView m_tableView; 85 | AddRemoveButtons m_addRemoveButtons; 86 | 87 | public: 88 | explicit LayersEditor(QWidget* parent = Q_NULLPTR); 89 | ~LayersEditor() override; 90 | 91 | bool loadData(ProjectModel::LayersNode* node); 92 | void unloadData() override; 93 | ProjectModel::INode* currentNode() const override; 94 | void resizeEvent(QResizeEvent* ev) override; 95 | AmuseItemEditFlags itemEditFlags() const override; 96 | 97 | private slots: 98 | void rowsInserted(const QModelIndex& parent, int first, int last); 99 | void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row); 100 | void doAdd(); 101 | void doSelectionChanged(); 102 | void itemDeleteAction() override; 103 | }; 104 | --------------------------------------------------------------------------------