146 Commits
v1.15 ... hsh

Author SHA1 Message Date
4e07b3b685 Merge branch 'master' into hsh
# Conflicts:
#	include/amuse/Common.hpp
2021-04-19 01:20:00 -04:00
d96be61e29 Fix GC compatiblity by always exporting id tables (if empty just write 0xFFFF) 2021-03-18 22:50:28 -07:00
217e791b88 Fix AppleClang -Wrange-loop-analysis warnings 2021-03-05 09:26:26 -05:00
df7136d265 Voice: Fix -Wmaybe-uninitialized with GCC9 2021-03-01 18:47:03 -05:00
454424b8b3 AudioGroupSampleDirectory: Fix -Wmaybe-uninitialized with GCC9 2021-03-01 18:36:37 -05:00
2ec749e6c7 Upgrade to qt 6 2021-01-23 16:27:34 -08:00
d3c4f568bb Fix target_atdna ordering 2021-01-06 20:27:57 -05:00
1275293327 Add utility function to convert panning values from [0,127] to [-1,1] 2020-10-24 15:58:29 -07:00
fe04c9a137 devkitA64 compilation fixes 2020-10-21 00:30:37 -04:00
Jack Andersen
6da262355a Make audio overload types float-only, initial boo2 switchover 2020-09-28 10:42:51 -10:00
fa3188e569 Harden Listener against NaN as well 2020-09-27 22:07:01 -07:00
69bc5dd69f Harden setVectors against NaN values 2020-09-27 14:36:18 -07:00
63a58858e8 Fix deprecation warnings in amuse-gui 2020-09-08 16:23:55 -07:00
f172427991 amuse-mkqticon: Use target_include_directories 2020-06-13 17:17:14 -04:00
aa9ef6e866 Merge commit 'cae2e9c' 2020-06-12 05:42:32 -07:00
cae2e9c897 Fix libPNG 2020-06-12 05:42:07 -07:00
281af5d997 Correct LIBPNG -> PNG 2020-05-27 21:40:04 -04:00
70380d8fbc Add Homebrew Qt5 path hint 2020-05-24 12:02:39 -04:00
6c7094f6fb Fix casting error 2020-04-22 03:41:35 -07:00
Jack Andersen
5112228abd Update fmtlib 2020-04-11 12:49:30 -10:00
Jack Andersen
40efdcc38c Merge commit '92f44' 2020-04-10 18:59:32 -10:00
Jack Andersen
92f44407c6 Exclude CMake CXX standard from MSVC 2020-04-10 18:59:16 -10:00
Lioncash
99f00a7cba EffectReverb: Make use of std::array where applicable
Makes the array types more strongly typed and allows removing the use of
hardcoded array sizes.
2020-03-27 17:18:40 -04:00
Lioncash
5b6d736cfb EffectChorus: Make use of std::array where applicable
Same behavior, but with stronger typing.
2020-03-27 16:45:04 -04:00
Lioncash
be754a44a4 EffectDelay: Make use of std::array where applicable
Makes the arrays more strongly typed and allows dehardcoding some array
sizes.
2020-03-27 16:31:38 -04:00
Lioncash
5de0035adb EffectDelay: Make use of std::make_unique
Same behavior, but without the need for raw new or separately
memsetting.
2020-03-27 15:51:37 -04:00
9c75aeccbe Fix C header export 2020-03-12 00:54:22 -07:00
b14b091f70 Remove redefinition of hash<QString> 2020-01-17 20:03:52 -05:00
Jack Andersen
c27bb5c435 Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/amuse 2019-09-30 21:36:35 -10:00
Jack Andersen
5d4abb5fb1 Code style improvements 2019-09-30 21:34:12 -10:00
ae26ed2fda Compile fixes 2019-09-14 10:07:52 -07:00
757defee7a Merge pull request #43 from lioncash/array2
AudioGroupPool: Use std::array where applicable
2019-09-10 23:53:38 -07:00
02da440523 Merge pull request #41 from lioncash/include
VolumeTable: Remove unnecessary Common include
2019-09-10 23:53:29 -07:00
0dca047352 Merge pull request #40 from lioncash/const
Studio: Make _cyclicCheck a const member function
2019-09-10 23:53:19 -07:00
95ffcc2d16 Merge pull request #39 from lioncash/magic
Sequencer: Minor cleanup
2019-09-10 23:53:08 -07:00
035f407be7 Merge pull request #42 from lioncash/cmake
CMakeLists: Add MSVC standards conformance flags
2019-09-10 23:52:57 -07:00
Lioncash
321a229dfd AudioGroupPool: Use std::array where applicable
Makes the array types strongly-typed and also allows for size querying.
2019-09-10 21:09:10 -04:00
Lioncash
60dad60448 CMakeLists: Add MSVC standards conformance flags
Applies flags to make MSVC's compiler be more standards compliant.
2019-09-10 20:43:37 -04:00
Lioncash
a8c9d555fe VolumeTable: Use std::array where applicable
Makes the arrays strongly typed.
2019-09-10 20:29:44 -04:00
Lioncash
7bc23b7190 VolumeTable: Remove unnecessary Common include
We can just include <algorithm> directly to eliminate an indirect
include.
2019-09-10 20:27:10 -04:00
Lioncash
b92674e127 Studio: std::move studio instance in addStudioSend()
Avoids an unnecessary atomic reference count increment and decrement
2019-09-10 20:23:29 -04:00
Lioncash
04fbc328e9 Studio: Make _cyclicCheck a const member function
This doesn't actually modify the internal state of the studio instance,
so we can mark it as a const member function.
2019-09-10 20:20:37 -04:00
Lioncash
1b37122b12 Sequencer: std::move studio instances in constructor
Avoids an unnecessary atomic reference count increment and decrement
2019-09-10 20:08:38 -04:00
Lioncash
2c75668e0a Sequencer: Default ChannelState's destructor 2019-09-10 20:06:31 -04:00
Lioncash
9ec78d6b9f Sequencer: Always initialize ChannelState channel ID
Provides a deterministic initial state for the default constructor case.
2019-09-10 20:05:05 -04:00
Lioncash
5f76f0ecbf Sequencer: Use std::array for m_ctrlVals
This is the only array in the header that isn't using std::array.
2019-09-10 20:03:26 -04:00
Lioncash
f50d67eff0 Sequencer: Eliminate magic values where applicable
Many of them are just representative of the sizes of an array, so we can
just query the array instead.
2019-09-10 19:48:31 -04:00
6c07ec907a Merge pull request #38 from lioncash/athena
General: Be explicit about athena's Endian/SeekOrigin type
2019-09-08 17:51:10 -07:00
Lioncash
b6f0201fbb General: Be explicit about athena's Endian type
Allows this code to still compile if the enum is ever changed into an
enum class.
2019-09-08 17:22:38 -04:00
Lioncash
d10a0ac7f1 General: Be explicit about athena's SeekOrigin type
Allows the code to still compile if the enum is turned into an enum
class.
2019-09-08 16:59:29 -04:00
d88b14c38a Merge pull request #37 from lioncash/emitter
Emitter: Use std::array where applicable
2019-09-08 00:10:40 -07:00
8b411b35a1 Merge pull request #36 from lioncash/bool
General: Make operator bool() instances explicit
2019-09-08 00:10:33 -07:00
Lioncash
29e7d8bc1e Emitter: Use std::array where applicable
Makes the data more strongly-typed and prevents implicit array->pointer
decay
2019-09-07 20:55:26 -04:00
Lioncash
7600f8ad27 General: Make operator bool() instances explicit
Prevents error-prone implicit conversions to bool.
2019-09-07 11:27:43 -04:00
0be0ca2911 Merge pull request #35 from lioncash/clamp
General: Amend clamp parameter order
2019-09-07 07:04:40 -07:00
Lioncash
50c4b5cdab General: Amend clamp parameter order
Batch replace on switching to standardized variants assumed common
ordering, which evidently wasn't the case
2019-09-07 06:40:20 -04:00
5da58eb1da Merge pull request #34 from lioncash/adsr
ADSREditor: Use std::array for m_percentTexts
2019-08-30 00:55:16 -07:00
Lioncash
344dbea31a ADSREditor: Use std::array for m_percentTexts
Same behavior, but allows for stronger typing and dehardcoding of sizes.
2019-08-30 02:24:41 -04:00
9b482ba013 Merge pull request #30 from lioncash/include
Editor: Amend includes of ProjectModel.hpp
2019-08-28 22:30:35 -07:00
5ee32c105b Merge pull request #32 from lioncash/status-bar
StatusBarWidget: Use std::array where applicable
2019-08-28 22:29:44 -07:00
af3f2a8d0c Merge pull request #33 from lioncash/studio
StudioSetupWidget: Use std::array where applicable
2019-08-28 22:29:36 -07:00
8d944ac5b0 Merge pull request #31 from lioncash/array
Editor/KeyboardWidget: Make use of std::array where applicable
2019-08-28 22:29:27 -07:00
Lioncash
792199897c StudioSetupWidget: Move definition of EffectIntrospection into the cpp file
Nothing in the header file requires the complete definition, so we can
use a forward declaration and move the type into the cpp file.
2019-08-28 01:31:27 -04:00
Lioncash
bfdb504457 StudioSetupWidget: Use std::array where applicable
Makes the type of the array strongly-typed.
2019-08-28 01:15:31 -04:00
Lioncash
9c2c2be8eb StatusBarWidget: Use std::array where applicable
A fairly basic replacement to make the type of the array strongly typed.
2019-08-28 00:17:54 -04:00
Lioncash
e6af69f882 Editor/KeyboardWidget: Make use of std::array where applicable
Makes the types of the lookup tables and widget arrays strongly typed.
This also makes it a little more straightforward to dehardcode some
magic values related to the array sizes.
2019-08-28 00:06:57 -04:00
Lioncash
ce70df2e8f Editor: Amend includes of ProjectModel.hpp
This was missed when organizing includes in commit
ef86e69421 (my bad!)
2019-08-27 23:40:16 -04:00
1604cbeb70 Linux compile fixes 2019-08-27 18:02:30 -07:00
3b7d577f73 Merge pull request #29 from lioncash/gui
Editor: Include headers where necessary
2019-08-27 18:00:02 -07:00
Lioncash
ef86e69421 Editor: Include headers where necessary
Avoids propagating dependencies through indirect inclusions, and also
gets rid of some header dependencies entirely.
2019-08-27 20:57:02 -04:00
107ddeb5a9 Merge pull request #28 from lioncash/revert
Revert "Revert "Editor/LayersEditor: Simplify moveRows implementation""
2019-08-26 20:00:23 -07:00
Lioncash
bf872e9037 Editor/LayersEditor: Use regular iterators instead of move iterators
Same behavior, but compatible with GCC
2019-08-26 22:41:40 -04:00
Lioncash
a178d55007 Revert "Revert "Editor/LayersEditor: Simplify moveRows implementation""
This reverts commit 4fdb2bde8c.
2019-08-26 22:40:14 -04:00
8569cab4d7 Merge pull request #27 from AxioDL/revert-23-rotate
Revert "Editor/LayersEditor: Simplify moveRows implementation"
2019-08-26 18:31:36 -07:00
4fdb2bde8c Revert "Editor/LayersEditor: Simplify moveRows implementation" 2019-08-26 18:31:18 -07:00
80da83eb16 Merge pull request #25 from lioncash/array
Envelope: Make lookup table const
2019-08-26 15:54:04 -07:00
8f810e571c Merge pull request #24 from lioncash/emplace
General: Use emplace_back where applicable
2019-08-26 15:53:55 -07:00
8f8add1eb1 Merge pull request #23 from lioncash/rotate
Editor/LayersEditor: Simplify moveRows implementation
2019-08-26 15:53:46 -07:00
401c977378 Merge pull request #26 from lioncash/clamp
Common: Replace amuse::clamp with std::clamp
2019-08-26 15:53:35 -07:00
Lioncash
eaf62288a2 Common: Replace amuse::clamp with std::clamp
We can use the standardized facility for this now with C++17
2019-08-26 05:14:21 -04:00
Lioncash
e31dfc6f98 Envelope: Make lookup table const
This is only ever read from, so we can allow the compiler to toss it
into the read-only segment.
2019-08-26 04:38:36 -04:00
Lioncash
176493c539 General: Use emplace_back where applicable
Same thing, less reading.
2019-08-26 03:41:29 -04:00
Lioncash
7ef7562b06 Editor/LayersEditor: Simplify moveRows implementation
We can leverage std::rotate to make shuffling around the mappings less
complex by defining a begin, end, and a pivot point and using them with
it.
2019-08-26 02:35:28 -04:00
88c017926b Merge pull request #22 from lioncash/str
Editor/ProjectModel: Use std::move where applicable
2019-08-25 23:09:14 -07:00
Lioncash
a380d4da21 Editor/ProjectModel: Use std::move where applicable
Amends the node interfaces to utilize std::move where applicable. This
allows avoiding some string copies altogether, as well as some IObj
atomic reference count increments and decrements.
2019-08-26 00:51:09 -04:00
9cc47aa803 Merge pull request #21 from lioncash/flags
Editor/CMakeLists: Add a few Qt compilation definitions to enforce correctness
2019-08-25 20:25:15 -07:00
Lioncash
8b4a096b50 Editor/CMakeLists: Add QT_NO_CAST_FROM_BYTEARRAY
Disables implicit conversions from QByteArray, instead requiring such
conversions to be explicit.
2019-08-25 23:06:55 -04:00
Lioncash
f269b66f85 Editor/CMakeLists: Add QT_NO_PROCESS_COMBINED_ARGUMENT_START
Disables an error-prone start() overload for QProcess. This prevents
cases where arguments to a process may be split unintentionally.
2019-08-25 22:59:47 -04:00
Lioncash
694ecdd9c3 Editor/CMakeLists: Add QT_NO_URL_CAST_FROM_STRING
Prevents implicit conversion from QString to QUrl in order to avoid
potentially incorrect conversions from being performed. This makes all
such conversions explicit.
2019-08-25 22:56:50 -04:00
Lioncash
e3dc475d7d Editor/CMakeLists: Add QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
Requires narrowing conversions to be made explicitly.
2019-08-25 22:54:20 -04:00
Lioncash
da54ade80b Editor/CMakeLists: Add QT_NO_CAST_FROM/TO_ASCII
Prevents implicit conversions to QString, requiring the conversions to
be made explicitly. Prevents potentially incompatible encodings from
being intermixed with one another.
2019-08-25 22:53:16 -04:00
Lioncash
658c33eb0d Editor/CMakeLists: Add QT_USE_QSTRINGBUILDER definition 2019-08-25 21:37:48 -04:00
ea84a7e35b Merge pull request #20 from lioncash/cmake
Editor/CMakeLists: Enable AUTOMOC, AUTORCC, and AUTOUI
2019-08-25 18:24:33 -07:00
Lioncash
56a7d842b5 CMakeLists: Set CMAKE_AUTORCC
Allows the build system to automatically handle qrc resource files
automatically without having to do all the manual book keeping.
2019-08-25 18:55:00 -04:00
Lioncash
f452516f12 CMakeLists: Set CMAKE_AUTOUIC
Allows the build system to automatically handle invoking uic for UI
files. This is beneficial since CMake will cache runs of uic, avoiding
running it again if its not necessary (no modifications were made to the
file, etc). This also means we don't need to keep track of all the UI
files explicitly in the CMake file.
2019-08-25 18:33:41 -04:00
Lioncash
f140972920 CMakeLists: Set CMAKE_AUTOMOC
This allows the build system to automatically invoke moc for us when
building the GUI. Now we don't need to manually keep track of which
files need to be wrapped, simplifying the CMake file a little bit more.
2019-08-25 18:33:41 -04:00
Lioncash
07b4f02d1d CMakeLists: Migrate off separate variables for source files where applicable
We can append the sources to the target with target_sources() instead.
2019-08-25 18:33:41 -04:00
Lioncash
771abb3390 CMakeLists: Organize GUI source listings
Alphabetizes and sorts the entries for linear readability.
2019-08-25 18:33:37 -04:00
6850f0b2c9 Add missing include 2019-08-25 09:25:27 -07:00
c08dd97cbf Merge pull request #19 from lioncash/key-array
Editor/KeymapEditor: Use std::array where applicable
2019-08-25 09:00:31 -07:00
8da26720d1 Merge pull request #18 from lioncash/fwd
General: Clean up inclusions
2019-08-25 09:00:23 -07:00
db9e911a23 Merge pull request #17 from lioncash/curve
CurveEditor: Use std::array where applicable
2019-08-25 09:00:15 -07:00
2b09d86df4 Merge pull request #16 from lioncash/const
ADSREditor: Make colors const
2019-08-25 09:00:07 -07:00
3310bf5b6e Merge pull request #15 from lioncash/cmake
CMakeLists: Migrate Qt checking over to using components
2019-08-25 08:59:50 -07:00
Lioncash
db805075d9 Editor/KeymapEditor: Use std::array where applicable
Strongly enforces the type of the array and allows removing quite a few
hardcoded size values.
2019-08-25 08:23:23 -04:00
Lioncash
d7aaff25d0 General: Clean up inclusions
Ensures that each header includes all of its dependencies that can't be
forward declared.
2019-08-25 07:34:27 -04:00
Lioncash
b1ab67f8c2 CurveEditor: Use std::array where applicable
Allows simplifying how redo/undo data gets moved around and also
eliminates a few hardcoded magic values for sizes.
2019-08-25 06:08:15 -04:00
Lioncash
532f3a5265 ADSREditor: Use const where applicable
Enforces immutability and makes for less mental book-keeping in drawing
code.
2019-08-25 05:30:37 -04:00
Lioncash
c736fdd00d ADSREditor: Make colors const
These aren't intended to be modified by anything, so these can be made
const.
2019-08-25 05:11:55 -04:00
Lioncash
a016b72ea6 CMakeLists: Migrate Qt checking over to using components
Qt has since made their library usable with the components system within
CMake, so we can just use that to simplify the library finding and linking.
2019-08-25 05:02:32 -04:00
bc8218e183 Merge pull request #14 from lioncash/cmake
CMakeLists: Add source files directly to amuse target
2019-08-25 01:39:02 -07:00
cec062797e Merge pull request #13 from lioncash/qt
Editor: Use Qt-5 signal/slot connections where applicable
2019-08-25 01:38:49 -07:00
Lioncash
ddc5661a38 CMakeLists: Migrate off directly modifying CMAKE_CXX_FLAGS
We can just use add_compile_options to append the compilation flag to
the directory property.
2019-08-25 04:28:27 -04:00
Lioncash
6d7665ecd6 CMakeLists: Add source files directly to amuse target
Same thing, less to read.
2019-08-25 04:26:04 -04:00
Lioncash
43eff31412 Editor: Use Qt 5 signal/slot connections where applicable
Migrates the UI signals and slots over to the new Qt 5 syntax. This
syntax is nicer, as the compiler can report errors at compile-time, as
opposed to the other method which would require a runtime error to
indicate any issues with the signals and slots.
2019-08-25 04:13:36 -04:00
cbf331823f Merge pull request #12 from lioncash/override
General: Make use of override where applicable
2019-08-24 23:47:53 -07:00
Lioncash
67c64a2d4e General: Make use of override where applicable
Continues the use of override.
2019-08-25 01:28:36 -04:00
82d1a8d214 Merge pull request #11 from lioncash/shadow
General: Eliminate instances of shadowing
2019-08-24 21:38:45 -07:00
Lioncash
051e4b1704 General: Eliminate instances of shadowing
Avoids instances of local variable shadowing (which also silences some
-Wshadow warnings).
2019-08-25 00:33:58 -04:00
520061a3bb Merge pull request #10 from lioncash/noexcept
Common: Make interfaces constexpr and noexcept where applicable
2019-08-24 21:16:04 -07:00
Lioncash
a8f4c1d34a Common: Less strict memory ordering requirements for IObj
Applies the same relaxed restrictions as applied to boo in
4d91a1b3c3
and
84f62a0f2c
2019-08-25 00:09:48 -04:00
Lioncash
c46bb3f72b Common: Make interfaces constexpr and noexcept where applicable
Many of these types are trivial helper types. These can be made
constexpr and noexcept to allow using them with other interfaces in a
more flexible manner.
2019-08-25 00:04:01 -04:00
529efa72b4 Fixes for override 2019-08-10 17:48:04 -07:00
Jack Andersen
1e35db90aa Windows build fixes 2019-07-27 15:18:09 -10:00
Jack Andersen
b0e4973c64 Massive fmtlib refactor 2019-07-19 18:23:25 -10:00
Jack Andersen
a74caa5fb0 New lzokay API 2019-06-14 14:36:28 -10:00
Jack Andersen
2a3444400e Better CMake dependency handling 2019-06-11 16:02:52 -10:00
Jack Andersen
1b10016369 Fix some compilation warnings 2019-04-06 18:59:49 -10:00
Jack Andersen
aeaba9366d Support for compiling against libc++ 2019-04-06 18:55:39 -10:00
Jack Andersen
e04603bb7d Fix SoundMacro generation for SampleView 2019-03-09 23:13:17 -10:00
Jack Andersen
aa5abd5ff5 Multiple StartSample bug fix 2019-03-08 22:58:02 -10:00
Jack Andersen
a4b8946ee2 Ensure all ObjectIDs are unique project-wide 2019-02-28 10:34:26 -10:00
Jack Andersen
9f2bb1b371 Implicit switch fallthrough refactor 2019-02-17 19:45:24 -10:00
Jack Andersen
54ef2dd73b Correct SCALEVOLUME behavior 2019-02-07 15:07:46 -10:00
Jack Andersen
fc2f8542c1 Windows compile fix 2019-01-28 22:26:40 -10:00
Jack Andersen
d80f1346bb Fix unwanted cutoff bug 2019-01-22 21:52:00 -10:00
Jack Andersen
7719459ac7 Switch to lzokay library 2018-12-19 17:45:26 -10:00
Jack Andersen
c1c47c51eb Update translation file 2018-12-07 20:45:52 -10:00
Jack Andersen
a7a408cc66 New code style refactor 2018-12-07 19:20:09 -10:00
Jack Andersen
b4c073c373 More accurate attenuation curve 2018-10-25 21:44:56 -10:00
Jack Andersen
885a2d2154 Windows build fixes 2018-10-14 10:10:33 -10:00
Jack Andersen
dbd48a39cc Convert to pragma once 2018-10-06 17:40:25 -10:00
Jack Andersen
4c7971f53a Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/amuse 2018-10-06 16:55:07 -10:00
Jack Andersen
38b9f57cd0 NX build fixes 2018-10-06 16:54:56 -10:00
Jack Andersen
92cd385f47 Update README.md 2018-09-30 23:13:59 -07:00
9e6d97564b Switch build fixes 2018-09-25 15:39:22 -07:00
128 changed files with 32417 additions and 38597 deletions

View File

@@ -1,5 +1,5 @@
---
IndentWidth: 4
BasedOnStyle: LLVM
ColumnLimit: 120
UseTab: Never
---
@@ -8,7 +8,6 @@ DerivePointerAlignment: false
PointerAlignment: Left
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
BreakBeforeBraces: Allman
IndentCaseLabels: false
AllowShortBlocksOnASingleLine: true
AlignOperands: true
@@ -16,7 +15,6 @@ AlignTrailingComments: true
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BreakConstructorInitializersBeforeComma: true
BreakStringLiterals: true
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
AllowShortFunctionsOnASingleLine: All
@@ -25,6 +23,6 @@ NamespaceIndentation: None
BinPackArguments: true
BinPackParameters: true
SortIncludes: false
AccessModifierOffset: -4
AccessModifierOffset: -2
ConstructorInitializerIndentWidth: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true

View File

@@ -1,55 +1,50 @@
#ifndef __AMUSE_AUDIOUNIT_CONTAININGAPP_HPP__
#define __AMUSE_AUDIOUNIT_CONTAININGAPP_HPP__
#pragma once
#import <AppKit/AppKit.h>
#import "AudioGroupFilePresenter.hpp"
#include <amuse/BooBackend.hpp>
#include <boo/audiodev/IAudioVoiceEngine.hpp>
@interface DataOutlineView : NSOutlineView
{
@interface DataOutlineView : NSOutlineView {
@public
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* deleteMenuItem;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* deleteMenuItem;
}
@end
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
{
AudioGroupFilePresenter* presenter;
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
AudioGroupFilePresenter* presenter;
}
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
@end
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
{
AudioGroupFilePresenter* presenter;
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
AudioGroupFilePresenter* presenter;
}
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
@end
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient>
{
IBOutlet NSWindow* mainWindow;
IBOutlet NSOutlineView* dataOutline;
IBOutlet NSSearchField* dataSearchField;
IBOutlet NSTableView* sfxTable;
IBOutlet NSTableView* samplesTable;
IBOutlet NSTextView* creditsView;
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient> {
IBOutlet NSWindow* mainWindow;
IBOutlet NSOutlineView* dataOutline;
IBOutlet NSSearchField* dataSearchField;
IBOutlet NSTableView* sfxTable;
IBOutlet NSTableView* samplesTable;
IBOutlet NSTextView* creditsView;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* removeDataMenu;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* removeDataMenu;
AudioGroupFilePresenter* groupFilePresenter;
AudioGroupFilePresenter* groupFilePresenter;
SamplesTableController* samplesController;
SFXTableController* sfxController;
SamplesTableController* samplesController;
SFXTableController* sfxController;
@public
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
std::experimental::optional<amuse::Engine> amuseEngine;
std::shared_ptr<amuse::Voice> activeSFXVox;
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
std::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
std::optional<amuse::Engine> amuseEngine;
std::shared_ptr<amuse::Voice> activeSFXVox;
}
- (BOOL)importURL:(NSURL*)url;
- (void)outlineView:(DataOutlineView*)ov selectionChanged:(id)item;
@@ -57,5 +52,3 @@
- (void)startSFX:(int)sfxId;
- (void)startSample:(int)sampId;
@end
#endif // __AMUSE_AUDIOUNIT_CONTAININGAPP_HPP__

View File

@@ -1,11 +1,10 @@
#ifndef __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__
#define __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__
#pragma once
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#include <map>
#include <string>
#include "optional.hpp"
#include <optional>
#include <amuse/amuse.hpp>
#include <athena/FileReader.hpp>
@@ -20,142 +19,127 @@
- (amuse::Engine&)getAmuseEngine;
@end
struct AudioGroupDataCollection
{
std::string m_name;
NSURL* m_proj;
NSURL* m_pool;
NSURL* m_sdir;
NSURL* m_samp;
NSURL* m_meta;
struct AudioGroupDataCollection {
std::string m_name;
NSURL* m_proj;
NSURL* m_pool;
NSURL* m_sdir;
NSURL* m_samp;
NSURL* m_meta;
AudioGroupDataToken* m_token;
AudioGroupDataToken* m_token;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
struct MetaData
{
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
{
}
MetaData(athena::io::FileReader& r)
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
{
}
};
std::experimental::optional<MetaData> m_metaData;
struct MetaData {
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: FMT_STRING(fmtIn), absOffs(absOffsIn), active(activeIn) {}
MetaData(athena::io::FileReader& r)
: FMT_STRING(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
};
std::optional<MetaData> m_metaData;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
std::vector<AudioGroupToken*> m_groupTokens;
std::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
std::vector<AudioGroupToken*> m_groupTokens;
void moveURL(NSURL* oldUrl, NSURL* newUrl);
void moveURL(NSURL* oldUrl, NSURL* newUrl);
bool loadProj(AudioGroupFilePresenter* presenter);
bool loadPool(AudioGroupFilePresenter* presenter);
bool loadSdir(AudioGroupFilePresenter* presenter);
bool loadSamp(AudioGroupFilePresenter* presenter);
bool loadMeta(AudioGroupFilePresenter* presenter);
bool loadProj(AudioGroupFilePresenter* presenter);
bool loadPool(AudioGroupFilePresenter* presenter);
bool loadSdir(AudioGroupFilePresenter* presenter);
bool loadSamp(AudioGroupFilePresenter* presenter);
bool loadMeta(AudioGroupFilePresenter* presenter);
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
bool isDataComplete() const
{
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad(AudioGroupFilePresenter* presenter);
bool _indexData(AudioGroupFilePresenter* presenter);
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
bool isDataComplete() const {
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad(AudioGroupFilePresenter* presenter);
bool _indexData(AudioGroupFilePresenter* presenter);
void enable(AudioGroupFilePresenter* presenter);
void disable(AudioGroupFilePresenter* presenter);
void enable(AudioGroupFilePresenter* presenter);
void disable(AudioGroupFilePresenter* presenter);
};
struct AudioGroupCollection
{
NSURL* m_url;
struct AudioGroupCollection {
NSURL* m_url;
AudioGroupCollectionToken* m_token;
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
AudioGroupCollectionToken* m_token;
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
AudioGroupCollection(NSURL* url);
void addCollection(AudioGroupFilePresenter* presenter,
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter* presenter);
bool doSearch(std::string_view str);
bool doActiveFilter();
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
AudioGroupCollection(NSURL* url);
void addCollection(AudioGroupFilePresenter* presenter,
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter* presenter);
bool doSearch(std::string_view str);
bool doActiveFilter();
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
};
@interface AudioGroupDataToken : NSObject
{
@interface AudioGroupDataToken : NSObject {
@public
AudioGroupDataCollection* m_collection;
AudioGroupDataCollection* m_collection;
}
- (id)initWithDataCollection:(AudioGroupDataCollection*)collection;
@end
@interface AudioGroupCollectionToken : NSObject
{
@interface AudioGroupCollectionToken : NSObject {
@public
AudioGroupCollection* m_collection;
AudioGroupCollection* m_collection;
}
- (id)initWithCollection:(AudioGroupCollection*)collection;
@end
@interface AudioGroupSFXToken : NSObject
{
@interface AudioGroupSFXToken : NSObject {
@public
NSAttributedString* m_name;
int m_loadId;
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
NSAttributedString* m_name;
int m_loadId;
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
}
- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx;
@end
@interface AudioGroupSampleToken : NSObject
{
@interface AudioGroupSampleToken : NSObject {
@public
NSAttributedString* m_name;
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
NSAttributedString* m_name;
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
}
- (id)
initWithName:(NSAttributedString*)name
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>*)
sample;
- (id)initWithName:(NSAttributedString*)name
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry,
amuse::AudioGroupSampleDirectory::ADPCMParms>*)sample;
@end
@interface AudioGroupToken : NSObject
{
@interface AudioGroupToken : NSObject {
@public
NSString* m_name;
int m_id;
const amuse::SongGroupIndex* m_song;
const amuse::SFXGroupIndex* m_sfx;
NSString* m_name;
int m_id;
const amuse::SongGroupIndex* m_song;
const amuse::SFXGroupIndex* m_sfx;
}
- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group;
- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group;
@end
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate>
{
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate> {
@public
id<AudioGroupClient> m_audioGroupClient;
NSURL* m_groupURL;
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
NSOutlineView* m_lastOutlineView;
NSString* m_searchStr;
id<AudioGroupClient> m_audioGroupClient;
NSURL* m_groupURL;
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
NSOutlineView* m_lastOutlineView;
NSString* m_searchStr;
std::vector<AudioGroupSFXToken*> m_sfxTableData;
std::vector<AudioGroupSampleToken*> m_sampleTableData;
std::vector<AudioGroupSFXToken*> m_sfxTableData;
std::vector<AudioGroupSampleToken*> m_sampleTableData;
}
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
- (BOOL)addCollectionName:(std::string&&)name
@@ -165,5 +149,3 @@ initWithName:(NSAttributedString*)name
- (void)setSearchFilter:(NSString*)str;
- (void)removeSelectedItem;
@end
#endif // __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__

View File

@@ -167,6 +167,7 @@ bool AudioGroupCollection::doSearch(std::string_view str)
m_filterGroups.clear();
m_filterGroups.reserve(m_groups.size());
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
// TODO: Heterogeneous lookup when C++20 available
if (str.empty() || StrToLower(it->first).find(str.data()) != std::string::npos)
{
m_filterGroups.push_back(it);
@@ -486,7 +487,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter)
if (!coord)
return false;
NSError* err;
__block std::experimental::optional<MetaData>& ret = m_metaData;
__block std::optional<MetaData>& ret = m_metaData;
[coord coordinateReadingItemAtURL:m_meta
options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err
byAccessor:^(NSURL* newUrl)

View File

@@ -1,5 +1,4 @@
#ifndef __AMUSE_AUDIOUNIT_BACKEND_HPP__
#define __AMUSE_AUDIOUNIT_BACKEND_HPP__
#pragma once
#ifdef __APPLE__
#include <Availability.h>
@@ -9,7 +8,7 @@
#include <AudioUnit/AudioUnit.h>
#include "optional.hpp"
#include <optional>
#include "amuse/BooBackend.hpp"
#include "amuse/Engine.hpp"
@@ -20,29 +19,26 @@
@class AudioUnitViewController;
namespace amuse
{
namespace amuse {
/** Backend voice allocator implementation for AudioUnit mixer */
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator
{
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator {
public:
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
};
void RegisterAudioUnit();
}
} // namespace amuse
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient>
{
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient> {
@public
AudioUnitViewController* m_viewController;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
AudioGroupFilePresenter* m_filePresenter;
AUAudioUnitBus* m_outBus;
AUAudioUnitBusArray* m_outs;
AudioUnitViewController* m_viewController;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
std::optional<amuse::Engine> m_engine;
AudioGroupFilePresenter* m_filePresenter;
AUAudioUnitBus* m_outBus;
AUAudioUnitBusArray* m_outs;
}
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
error:(NSError* __nullable* __nonnull)outError
@@ -52,4 +48,3 @@ void RegisterAudioUnit();
#endif
#endif
#endif // __AMUSE_AUDIOUNIT_BACKEND_HPP__

View File

@@ -1,25 +1,20 @@
#ifndef __AMUSE_AUDIOUNIT_VIEWCONTROLLER_HPP__
#define __AMUSE_AUDIOUNIT_VIEWCONTROLLER_HPP__
#pragma once
#import <CoreAudioKit/CoreAudioKit.h>
#import "AudioGroupFilePresenter.hpp"
@class AmuseAudioUnit;
@interface GroupBrowserDelegate : NSObject <NSBrowserDelegate>
{
AmuseAudioUnit* m_audioUnit;
@interface GroupBrowserDelegate : NSObject <NSBrowserDelegate> {
AmuseAudioUnit* m_audioUnit;
}
- (id)initWithAudioUnit:(AmuseAudioUnit*)au;
@end
@interface AudioUnitViewController : AUViewController <AUAudioUnitFactory>
{
@interface AudioUnitViewController : AUViewController <AUAudioUnitFactory> {
@public
AmuseAudioUnit* m_audioUnit;
IBOutlet NSBrowser* m_groupBrowser;
GroupBrowserDelegate* m_groupBrowserDelegate;
AmuseAudioUnit* m_audioUnit;
IBOutlet NSBrowser* m_groupBrowser;
GroupBrowserDelegate* m_groupBrowserDelegate;
}
@end
#endif // __AMUSE_AUDIOUNIT_VIEWCONTROLLER_HPP__

View File

@@ -67,7 +67,7 @@ if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VE
AmuseContainingApp.mm AudioGroupFilePresenter.mm
PROPERTIES COMPILE_FLAGS -fobjc-arc)
target_link_libraries(amuse-au-container amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY}
${AVFOUNDATION_LIBRARY} ${ZLIB_LIBRARIES} ${LZO_LIB} ${BOO_SYS_LIBS} logvisor athena-core)
${AVFOUNDATION_LIBRARY} ${ZLIB_LIBRARIES} lzokay ${BOO_SYS_LIBS} logvisor athena-core)
set(APPLE_BUNDLE_ID "com.axiodl.Amuse")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainer.entitlements.in

View File

@@ -2,110 +2,144 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(amuse)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(POLICY CMP0072)
cmake_policy(SET CMP0072 NEW)
endif()
if (NOT MSVC)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing")
add_compile_options(-Wno-narrowing)
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/boo AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/athena)
message(STATUS "Preparing standalone build")
add_subdirectory(boo)
add_subdirectory(athena)
include_directories(athena/include)
endif()
atdna(atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp)
atdna(atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp)
atdna(atdna_AudioGroupSampleDirectory.cpp include/amuse/AudioGroupSampleDirectory.hpp)
set(SOURCES
lib/AudioGroup.cpp
lib/AudioGroupData.cpp
lib/AudioGroupPool.cpp
lib/AudioGroupProject.cpp
lib/AudioGroupSampleDirectory.cpp
lib/DirectoryEnumerator.cpp
lib/Emitter.cpp
lib/Engine.cpp
lib/Envelope.cpp
lib/Listener.cpp
lib/Sequencer.cpp
lib/SoundMacroState.cpp
lib/SongConverter.cpp
lib/SongState.cpp
lib/Voice.cpp
lib/Submix.cpp
lib/Studio.cpp
lib/EffectReverb.cpp
lib/EffectChorus.cpp
lib/EffectDelay.cpp
lib/ContainerRegistry.cpp
lib/Common.cpp
lib/DSPCodec.cpp
lib/N64MusyXCodec.cpp
lib/VolumeTable.cpp
atdna_AudioGroupPool.cpp
atdna_AudioGroupProject.cpp
atdna_AudioGroupSampleDirectory.cpp)
set(HEADERS
include/amuse/AudioGroup.hpp
include/amuse/AudioGroupData.hpp
include/amuse/AudioGroupPool.hpp
include/amuse/AudioGroupProject.hpp
include/amuse/AudioGroupSampleDirectory.hpp
include/amuse/DirectoryEnumerator.hpp
include/amuse/Emitter.hpp
include/amuse/Engine.hpp
include/amuse/Entity.hpp
include/amuse/Envelope.hpp
include/amuse/Listener.hpp
include/amuse/Sequencer.hpp
include/amuse/SoundMacroState.hpp
include/amuse/SongConverter.hpp
include/amuse/SongState.hpp
include/amuse/Voice.hpp
include/amuse/Submix.hpp
include/amuse/Studio.hpp
include/amuse/IBackendSubmix.hpp
include/amuse/IBackendVoice.hpp
include/amuse/IBackendVoiceAllocator.hpp
include/amuse/EffectBase.hpp
include/amuse/EffectReverb.hpp
include/amuse/EffectChorus.hpp
include/amuse/EffectDelay.hpp
include/amuse/ContainerRegistry.hpp
include/amuse/Common.hpp
include/amuse/amuse.hpp
include/amuse/DSPCodec.hpp
include/amuse/N64MusyXCodec.hpp
include/amuse/VolumeTable.hpp)
unset(EXTRAS)
if(TARGET boo)
include_directories(${BOO_INCLUDE_DIR} ${BOO_INCLUDE_DIR}/../lib ${BOO_INCLUDE_DIR}/../soxr/src
${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${LZO_INCLUDE_DIR})
list(APPEND EXTRAS lib/BooBackend.cpp include/amuse/BooBackend.hpp)
endif()
include_directories(include)
set(AMUSE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE PATH "amuse include path" FORCE)
add_library(amuse
${SOURCES}
${HEADERS}
${EXTRAS})
lib/AudioGroup.cpp
lib/AudioGroupData.cpp
lib/AudioGroupPool.cpp
lib/AudioGroupProject.cpp
lib/AudioGroupSampleDirectory.cpp
lib/Common.cpp
lib/ContainerRegistry.cpp
lib/DirectoryEnumerator.cpp
lib/DSPCodec.cpp
lib/EffectChorus.cpp
lib/EffectDelay.cpp
lib/EffectReverb.cpp
lib/Emitter.cpp
lib/Engine.cpp
lib/Envelope.cpp
lib/Listener.cpp
lib/N64MusyXCodec.cpp
lib/Sequencer.cpp
lib/SongConverter.cpp
lib/SongState.cpp
lib/SoundMacroState.cpp
lib/Studio.cpp
lib/Submix.cpp
lib/Voice.cpp
lib/VolumeTable.cpp
include/amuse/amuse.hpp
include/amuse/AudioGroup.hpp
include/amuse/AudioGroupData.hpp
include/amuse/AudioGroupPool.hpp
include/amuse/AudioGroupProject.hpp
include/amuse/AudioGroupSampleDirectory.hpp
include/amuse/Common.hpp
include/amuse/ContainerRegistry.hpp
include/amuse/DirectoryEnumerator.hpp
include/amuse/DSPCodec.hpp
include/amuse/EffectBase.hpp
include/amuse/EffectChorus.hpp
include/amuse/EffectDelay.hpp
include/amuse/EffectReverb.hpp
include/amuse/Emitter.hpp
include/amuse/Engine.hpp
include/amuse/Entity.hpp
include/amuse/Envelope.hpp
include/amuse/IBackendSubmix.hpp
include/amuse/IBackendVoice.hpp
include/amuse/IBackendVoiceAllocator.hpp
include/amuse/Listener.hpp
include/amuse/N64MusyXCodec.hpp
include/amuse/Sequencer.hpp
include/amuse/SongConverter.hpp
include/amuse/SoundMacroState.hpp
include/amuse/SongState.hpp
include/amuse/Submix.hpp
include/amuse/Studio.hpp
include/amuse/Voice.hpp
include/amuse/VolumeTable.hpp
)
target_include_directories(amuse PUBLIC include)
target_link_libraries(amuse PUBLIC
athena-core
lzokay
logvisor
fmt
${ZLIB_LIBRARIES}
)
target_atdna(amuse atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp)
target_atdna(amuse atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp)
target_atdna(amuse atdna_AudioGroupSampleDirectory.cpp include/amuse/AudioGroupSampleDirectory.hpp)
if(NX)
target_sources(amuse PRIVATE include/switch_math.hpp)
endif()
if(TARGET boo2)
target_sources(amuse PRIVATE lib/BooBackend.cpp include/amuse/BooBackend.hpp)
target_link_libraries(amuse PUBLIC boo2)
endif()
target_atdna(amuse atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp)
target_atdna(amuse atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp)
target_atdna(amuse atdna_AudioGroupSampleDirectory.cpp include/amuse/AudioGroupSampleDirectory.hpp)
if (MSVC)
target_compile_options(amuse PRIVATE
# Enforce various standards compliant behavior.
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
# Enable standard volatile semantics.
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
# Reports the proper value for the __cplusplus preprocessor macro.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
# Use latest C++ standard.
$<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Flags for MSVC (not clang-cl)
target_compile_options(amuse PRIVATE
# Allow constexpr variables to have explicit external linkage.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>
# Assume that new throws exceptions, allowing better code generation.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:throwingNew>
)
endif()
else()
target_compile_options(amuse PRIVATE -Wno-unknown-pragmas)
endif()
if(COMMAND add_sanitizers)
add_sanitizers(amuse)
endif()
if(COMMAND cotire)
set_target_properties(amuse PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
cotire(amuse)
endif()
if(TARGET boo AND NOT WINDOWS_STORE)
if(TARGET boo AND NOT WINDOWS_STORE AND NOT NX)
# AudioUnit Target (OS X only)
add_subdirectory(AudioUnit)
@@ -116,15 +150,15 @@ if(TARGET boo AND NOT WINDOWS_STORE)
# Player
add_executable(amuseplay WIN32 driver/amuseplay.cpp)
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} ${LZO_LIB})
target_link_libraries(amuseplay amuse logvisor)
# Converter
add_executable(amuseconv driver/amuseconv.cpp)
target_link_libraries(amuseconv amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} ${LZO_LIB})
target_link_libraries(amuseconv amuse logvisor)
# Renderer
add_executable(amuserender driver/amuserender.cpp)
target_link_libraries(amuserender amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} ${LZO_LIB})
target_link_libraries(amuserender amuse logvisor)
if(COMMAND add_sanitizers)
add_sanitizers(amuseplay)
@@ -133,11 +167,11 @@ if(TARGET boo AND NOT WINDOWS_STORE)
endif()
# Editor
find_package(Qt5Widgets)
if (Qt5Widgets_FOUND)
message(STATUS "Qt5 found, amuse-gui will be built")
find_package(Qt6 COMPONENTS Widgets PATHS /usr/local/opt/qt)
if (Qt6Widgets_FOUND)
message(STATUS "Qt6 found, amuse-gui will be built")
add_subdirectory(Editor)
else()
message(STATUS "Qt5 not found, amuse-gui will not be built")
message(STATUS "Qt6 not found, amuse-gui will not be built")
endif()
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -1,84 +1,95 @@
#ifndef AMUSE_ADSR_EDITOR_HPP
#define AMUSE_ADSR_EDITOR_HPP
#pragma once
#include <array>
#include <cstdint>
#include <vector>
#include <QFrame>
#include <QStaticText>
#include "EditorWidget.hpp"
#include <QFrame>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QLabel>
#include <QStaticText>
#include "ProjectModel.hpp"
#include <amuse/Common.hpp>
class ADSREditor;
class ADSRView : public QWidget
{
Q_OBJECT
friend class ADSRControls;
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
std::vector<QStaticText> m_timeTexts;
int m_dragPoint = -1;
uint64_t m_cycleIdx = 0;
ADSREditor* getEditor() const;
public:
explicit ADSRView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
class QCheckBox;
class QDoubleSpinBox;
class QLabel;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseReleaseEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
class ADSRView : public QWidget {
Q_OBJECT
friend class ADSRControls;
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
QFont m_gridFont;
std::array<QStaticText, 11> m_percentTexts;
std::vector<QStaticText> m_timeTexts;
int m_dragPoint = -1;
uint64_t m_cycleIdx = 0;
ADSREditor* getEditor() const;
public:
explicit ADSRView(QWidget* parent = Q_NULLPTR);
~ADSRView() override;
void loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev) override;
void mousePressEvent(QMouseEvent* ev) override;
void mouseReleaseEvent(QMouseEvent* ev) override;
void mouseMoveEvent(QMouseEvent* ev) override;
};
class ADSRControls : public QFrame
{
Q_OBJECT
friend class ADSRView;
QDoubleSpinBox* m_attack;
QDoubleSpinBox* m_decay;
QDoubleSpinBox* m_sustain;
QDoubleSpinBox* m_release;
QCheckBox* m_dls;
QLabel* m_velToAttackLab;
QDoubleSpinBox* m_velToAttack;
QLabel* m_keyToDecayLab;
QDoubleSpinBox* m_keyToDecay;
bool m_enableUpdate = true;
ADSREditor* getEditor() const;
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
void setRelease(double release, uint64_t cycleCount);
class ADSRControls : public QFrame {
Q_OBJECT
friend class ADSRView;
QDoubleSpinBox* m_attack;
QDoubleSpinBox* m_decay;
QDoubleSpinBox* m_sustain;
QDoubleSpinBox* m_release;
QCheckBox* m_dls;
QLabel* m_velToAttackLab;
QDoubleSpinBox* m_velToAttack;
QLabel* m_keyToDecayLab;
QDoubleSpinBox* m_keyToDecay;
bool m_enableUpdate = true;
ADSREditor* getEditor() const;
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
void setRelease(double release, uint64_t cycleCount);
public:
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
~ADSRControls() override;
void loadData();
void unloadData();
public slots:
void attackChanged(double val);
void decayChanged(double val);
void sustainChanged(double val);
void releaseChanged(double val);
void dlsStateChanged(int state);
void velToAttackChanged(double val);
void keyToDecayChanged(double val);
void attackChanged(double val);
void decayChanged(double val);
void sustainChanged(double val);
void releaseChanged(double val);
void dlsStateChanged(int state);
void velToAttackChanged(double val);
void keyToDecayChanged(double val);
};
class ADSREditor : public EditorWidget
{
Q_OBJECT
friend class ADSRView;
friend class ADSRControls;
ADSRView* m_adsrView;
ADSRControls* m_controls;
class ADSREditor : public EditorWidget {
Q_OBJECT
friend class ADSRView;
friend class ADSRControls;
ADSRView* m_adsrView;
ADSRControls* m_controls;
public:
explicit ADSREditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
explicit ADSREditor(QWidget* parent = Q_NULLPTR);
~ADSREditor() override;
bool loadData(ProjectModel::ADSRNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
};
#endif //AMUSE_ADSR_EDITOR_HPP

View File

@@ -1,87 +1,154 @@
cmake_minimum_required(VERSION 3.10)
# Automatically handle invoking autorcc, moc, and uic.
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Xml REQUIRED)
find_package(Qt5Svg REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5LinguistTools REQUIRED)
find_package(Qt6 COMPONENTS LinguistTools Network Qml SvgWidgets Widgets Xml REQUIRED)
configure_file(resources/translation_res.qrc translation_res.qrc @ONLY)
set(TRANSLATIONS
resources/lang_de.ts
)
QT6_CREATE_TRANSLATION(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../lib ${TRANSLATIONS})
add_executable(amuse-gui WIN32 MACOSX_BUNDLE
ADSREditor.cpp
ADSREditor.hpp
Common.cpp
Common.hpp
CurveEditor.cpp
CurveEditor.hpp
EditorWidget.cpp
EditorWidget.hpp
KeyboardWidget.cpp
KeyboardWidget.hpp
KeymapEditor.cpp
KeymapEditor.hpp
LayersEditor.cpp
LayersEditor.hpp
MainWindow.cpp
MainWindow.hpp
MainWindow.ui
MIDIReader.cpp
MIDIReader.hpp
NewSoundMacroDialog.cpp
NewSoundMacroDialog.hpp
ProjectModel.cpp
ProjectModel.hpp
SampleEditor.cpp
SampleEditor.hpp
SongGroupEditor.cpp
SongGroupEditor.hpp
SoundGroupEditor.cpp
SoundGroupEditor.hpp
SoundMacroEditor.cpp
SoundMacroEditor.hpp
StatusBarWidget.cpp
StatusBarWidget.hpp
StudioSetupWidget.cpp
StudioSetupWidget.hpp
main.cpp
resources/resources.qrc
${CMAKE_CURRENT_BINARY_DIR}/translation_res.qrc
${QM_FILES}
)
target_compile_definitions(amuse-gui PRIVATE
# Disable implicit conversions from ASCII to QString.
-DQT_NO_CAST_FROM_ASCII
-DQT_NO_CAST_TO_ASCII
# Disable implicit conversions of QByteArray to const char* or const void*
-DQT_NO_CAST_FROM_BYTEARRAY
# Disable narrowing conversions in signal/slot connect() calls.
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
# Disable unsafe overloads of QProcess' start() function.
-DQT_NO_PROCESS_COMBINED_ARGUMENT_START
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
-DQT_NO_URL_CAST_FROM_STRING
# Allows for more efficient string concatenation, resulting in less temporaries.
-DQT_USE_QSTRINGBUILDER
)
if (MSVC)
target_compile_options(amuse-gui PRIVATE
# Enforce various standards compliant behavior.
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
# Enable standard volatile semantics.
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
# Reports the proper value for the __cplusplus preprocessor macro.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
# Use latest C++ standard.
$<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Flags for MSVC (not clang-cl)
target_compile_options(amuse-gui PRIVATE
# Allow constexpr variables to have explicit external linkage.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>
# Assume that new throws exceptions, allowing better code generation.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:throwingNew>
)
endif()
endif()
if(WIN32)
list(APPEND PLAT_SRCS platforms/win/amuse-gui.rc platforms/win/amuse-gui.manifest)
target_sources(amuse-gui PRIVATE
platforms/win/amuse-gui.rc
platforms/win/amuse-gui.manifest
)
elseif(APPLE)
list(APPEND PLAT_SRCS platforms/mac/mainicon.icns MacOSExtras.mm)
target_sources(amuse-gui PRIVATE
MacOSExtras.mm
platforms/mac/mainicon.icns
)
set_source_files_properties(platforms/mac/mainicon.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
MACOSX_PACKAGE_LOCATION Resources
)
endif()
add_subdirectory(platforms/freedesktop)
declare_qticon_target()
list(APPEND PLAT_SRCS mainicon_qt.cpp)
target_sources(amuse-gui PRIVATE mainicon_qt.cpp)
configure_file(resources/translation_res.qrc translation_res.qrc @ONLY)
set(TRANSLATIONS
resources/lang_de.ts)
QT5_CREATE_TRANSLATION(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../lib ${TRANSLATIONS})
QT5_ADD_RESOURCES(qrc_resources.cpp resources/resources.qrc)
QT5_ADD_RESOURCES(qrc_translation_res.cpp ${CMAKE_CURRENT_BINARY_DIR}/translation_res.qrc OPTIONS -no-compress)
QT5_WRAP_UI(MAIN_WINDOW_UI MainWindow.ui)
QT5_WRAP_CPP(AMUSE_MOC
Common.hpp
MainWindow.hpp
KeyboardWidget.hpp
StatusBarWidget.hpp
ProjectModel.hpp
EditorWidget.hpp
SoundMacroEditor.hpp
ADSREditor.hpp
CurveEditor.hpp
KeymapEditor.hpp
LayersEditor.hpp
SampleEditor.hpp
SoundGroupEditor.hpp
SongGroupEditor.hpp
NewSoundMacroDialog.hpp
StudioSetupWidget.hpp)
add_executable(amuse-gui WIN32 MACOSX_BUNDLE
Common.hpp Common.cpp
MainWindow.ui ${MAIN_WINDOW_UI} MainWindow.hpp MainWindow.cpp
KeyboardWidget.hpp KeyboardWidget.cpp
StatusBarWidget.hpp StatusBarWidget.cpp
ProjectModel.hpp ProjectModel.cpp
EditorWidget.hpp EditorWidget.cpp
SoundMacroEditor.hpp SoundMacroEditor.cpp
ADSREditor.hpp ADSREditor.cpp
CurveEditor.hpp CurveEditor.cpp
KeymapEditor.hpp KeymapEditor.cpp
LayersEditor.hpp LayersEditor.cpp
SampleEditor.hpp SampleEditor.cpp
SoundGroupEditor.hpp SoundGroupEditor.cpp
SongGroupEditor.hpp SongGroupEditor.cpp
NewSoundMacroDialog.hpp NewSoundMacroDialog.cpp
StudioSetupWidget.hpp StudioSetupWidget.cpp
MIDIReader.hpp MIDIReader.cpp
resources/resources.qrc qrc_resources.cpp
${QM_FILES} qrc_translation_res.cpp
${AMUSE_MOC} ${PLAT_SRCS}
main.cpp)
if(COMMAND add_sanitizers)
add_sanitizers(amuse-gui)
endif()
set_target_properties(amuse-gui PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist")
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist"
)
target_link_libraries(amuse-gui ${PLAT_LIBS}
${Qt5Widgets_LIBRARIES}
${Qt5Network_LIBRARIES}
${Qt5Xml_LIBRARIES}
${Qt5Svg_LIBRARIES}
${Qt5Qml_LIBRARIES}
amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml xxhash ${ZLIB_LIBRARIES} ${LZO_LIB})
target_link_libraries(amuse-gui
${PLAT_LIBS}
Qt6::Network
Qt6::Qml
Qt6::SvgWidgets
Qt6::Widgets
Qt6::Xml
amuse
athena-core
boo
${BOO_SYS_LIBS}
logvisor
lzokay
xxhash
${ZLIB_LIBRARIES}
)

View File

@@ -1,108 +1,99 @@
#include "Common.hpp"
#include "MainWindow.hpp"
#include <QDir>
#include <QMessageBox>
#include <QObject>
#include <QProcess>
#include <QTransform>
boo::SystemString QStringToSysString(const QString& str)
{
boo::SystemString QStringToSysString(const QString& str) {
#ifdef _WIN32
return (wchar_t*)str.utf16();
return (wchar_t*)str.utf16();
#else
return str.toUtf8().toStdString();
return str.toUtf8().toStdString();
#endif
}
QString SysStringToQString(const boo::SystemString& str)
{
QString SysStringToQString(const boo::SystemString& str) {
#ifdef _WIN32
return QString::fromStdWString(str);
return QString::fromStdWString(str);
#else
return QString::fromStdString(str);
return QString::fromStdString(str);
#endif
}
bool MkPath(const QString& path, UIMessenger& messenger)
{
QFileInfo fInfo(path);
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
bool MkPath(const QString& path, UIMessenger& messenger) {
QFileInfo fInfo(path);
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
}
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger)
{
if (!dir.mkpath(file))
{
QString msg = QString(MainWindow::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
messenger.critical(MainWindow::tr("Unable to create directory"), msg);
return false;
}
return true;
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger) {
if (!dir.mkpath(file)) {
QString msg = QString(MainWindow::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
messenger.critical(MainWindow::tr("Unable to create directory"), msg);
return false;
}
return true;
}
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn)
{
const QFileInfo fileInfo(pathIn);
// Mac, Windows support folder or file.
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn) {
const QFileInfo fileInfo(pathIn);
// Mac, Windows support folder or file.
#if defined(Q_OS_WIN)
QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path"));
QString explorer;
for (QString path : paths.split(QStringLiteral(";")))
{
QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe"));
if (finfo.exists())
{
explorer = finfo.filePath();
break;
}
QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path"));
QString explorer;
for (QString path : paths.split(QStringLiteral(";"))) {
QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe"));
if (finfo.exists()) {
explorer = finfo.filePath();
break;
}
if (explorer.isEmpty()) {
QMessageBox::warning(parent,
MainWindow::tr("Launching Windows Explorer Failed"),
MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer."));
return;
}
QStringList param;
if (!fileInfo.isDir())
param += QLatin1String("/select,");
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
QProcess::startDetached(explorer, param);
}
if (explorer.isEmpty()) {
QMessageBox::warning(parent, MainWindow::tr("Launching Windows Explorer Failed"),
MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer."));
return;
}
QStringList param;
if (!fileInfo.isDir())
param += QLatin1String("/select,");
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
QProcess::startDetached(explorer, param);
#elif defined(Q_OS_MAC)
QStringList scriptArgs;
scriptArgs << QLatin1String("-e")
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
.arg(fileInfo.canonicalFilePath());
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
scriptArgs.clear();
scriptArgs << QLatin1String("-e")
<< QLatin1String("tell application \"Finder\" to activate");
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
QStringList scriptArgs;
scriptArgs << QLatin1String("-e")
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
.arg(fileInfo.canonicalFilePath());
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
scriptArgs.clear();
scriptArgs << QLatin1String("-e") << QLatin1String("tell application \"Finder\" to activate");
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
#else
// we cannot select a file here, because no file browser really supports it...
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
QProcess browserProc;
const QString browserArgs = QStringLiteral("xdg-open \"%1\"").arg(QFileInfo(folder).path());
browserProc.startDetached(browserArgs);
// we cannot select a file here, because no file browser really supports it...
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
QProcess browserProc;
const QStringList browserArgs = QStringList() << QStringLiteral("%1").arg(QFileInfo(folder).path());
browserProc.startDetached(QStringLiteral("xdg-open"), browserArgs);
#endif
}
QString ShowInGraphicalShellString()
{
QString ShowInGraphicalShellString() {
#if defined(Q_OS_WIN)
return MainWindow::tr("Show in Explorer");
return MainWindow::tr("Show in Explorer");
#elif defined(Q_OS_MAC)
return MainWindow::tr("Show in Finder");
return MainWindow::tr("Show in Finder");
#else
return MainWindow::tr("Show in Browser");
return MainWindow::tr("Show in Browser");
#endif
}
QTransform RectToRect(const QRectF& from, const QRectF& to)
{
QPolygonF orig(from);
orig.pop_back();
QPolygonF resize(to);
resize.pop_back();
QTransform ret;
QTransform::quadToQuad(orig, resize, ret);
return ret;
QTransform RectToRect(const QRectF& from, const QRectF& to) {
QPolygonF orig(from);
orig.pop_back();
QPolygonF resize(to);
resize.pop_back();
QTransform ret;
QTransform::quadToQuad(orig, resize, ret);
return ret;
}

View File

@@ -1,40 +1,40 @@
#ifndef AMUSE_COMMON_HPP
#define AMUSE_COMMON_HPP
#pragma once
#include <functional>
#include "boo/System.hpp"
#include <QString>
#include <QDir>
#include <QMessageBox>
#include <QString>
#include <boo/System.hpp>
class MainWindow;
extern MainWindow* g_MainWindow;
class UIMessenger : public QObject
{
Q_OBJECT
class QDir;
class QRectF;
class QTransform;
class UIMessenger : public QObject {
Q_OBJECT
public:
using QObject::QObject;
using QObject::QObject;
signals:
QMessageBox::StandardButton information(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
int question(const QString &title,
const QString& text,
const QString& button0Text,
const QString& button1Text = QString(),
const QString& button2Text = QString(),
int defaultButtonNumber = 0,
int escapeButtonNumber = -1);
QMessageBox::StandardButton question(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons =
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton warning(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton critical(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton information(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
int question(const QString& title, const QString& text, const QString& button0Text,
const QString& button1Text = QString(), const QString& button2Text = QString(),
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
QMessageBox::StandardButton
question(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton warning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton critical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
};
boo::SystemString QStringToSysString(const QString& str);
@@ -46,23 +46,5 @@ bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger);
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn);
QString ShowInGraphicalShellString();
static QLatin1String StringViewToQString(std::string_view sv)
{
return QLatin1String(sv.data(), int(sv.size()));
}
/* Used for generating transform matrices to map SVG coordinate space */
QTransform RectToRect(const QRectF& from, const QRectF& to);
namespace std
{
template<> struct hash<QString>
{
std::size_t operator()(const QString& s) const noexcept
{
return qHash(s);
}
};
}
#endif //AMUSE_COMMON_HPP

View File

@@ -1,326 +1,303 @@
#include "CurveEditor.hpp"
#include "MainWindow.hpp"
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPainter>
#include <QMouseEvent>
#include <QJSValueIterator>
class CurveEditUndoCommand : public EditorUndoCommand
{
uint8_t m_redoData[128];
uint8_t m_undoData[128];
bool m_usedExpr;
bool m_undid = false;
#include <algorithm>
#include <array>
#include "MainWindow.hpp"
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QJSValueIterator>
#include <QMouseEvent>
#include <QPainter>
#include <QPushButton>
class CurveEditUndoCommand : public EditorUndoCommand {
public:
CurveEditUndoCommand(const uint8_t* redoData, bool usedExpr,
amuse::ObjToken<ProjectModel::CurveNode> node)
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")), m_usedExpr(usedExpr)
{
std::memcpy(m_redoData, redoData, 128);
using RedoData = std::array<uint8_t, 128>;
using UndoData = std::array<uint8_t, 128>;
CurveEditUndoCommand(const RedoData& redoData, bool usedExpr, amuse::ObjToken<ProjectModel::CurveNode> node)
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")), m_redoData{redoData}, m_usedExpr(usedExpr) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::Curve) {
auto& curve = static_cast<amuse::Curve&>(table);
curve.data.assign(m_undoData.cbegin(), m_undoData.cend());
}
void undo()
{
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::Curve)
{
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
curve.data.resize(128);
std::memcpy(curve.data.data(), m_undoData, 128);
}
EditorUndoCommand::undo();
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::Curve) {
auto& curve = static_cast<amuse::Curve&>(table);
curve.data.resize(128);
std::copy(curve.data.cbegin(), curve.data.cend(), m_undoData.begin());
curve.data.assign(m_redoData.cbegin(), m_redoData.cend());
}
void redo()
{
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::Curve)
{
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
curve.data.resize(128);
std::memcpy(m_undoData, curve.data.data(), 128);
std::memcpy(curve.data.data(), m_redoData, 128);
}
if (m_undid)
EditorUndoCommand::redo();
if (m_undid) {
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other)
{
if (other->id() == id() && !m_usedExpr && !static_cast<const CurveEditUndoCommand*>(other)->m_usedExpr)
{
std::memcpy(m_redoData, static_cast<const CurveEditUndoCommand*>(other)->m_redoData, 128);
return true;
}
return false;
}
bool mergeWith(const QUndoCommand* other) override {
const auto* const command = static_cast<const CurveEditUndoCommand*>(other);
if (other->id() == id() && !m_usedExpr && !command->m_usedExpr) {
m_redoData = command->m_redoData;
return true;
}
int id() const { return int(Id::CurveEdit); }
return false;
}
int id() const override { return int(Id::CurveEdit); }
private:
RedoData m_redoData;
UndoData m_undoData;
bool m_usedExpr;
bool m_undid = false;
};
CurveEditor* CurveView::getEditor() const
{
return qobject_cast<CurveEditor*>(parentWidget());
}
CurveEditor* CurveView::getEditor() const { return qobject_cast<CurveEditor*>(parentWidget()); }
void CurveView::loadData(ProjectModel::CurveNode* node)
{
m_node = node;
}
void CurveView::loadData(ProjectModel::CurveNode* node) { m_node = node; }
void CurveView::unloadData()
{
m_node.reset();
}
void CurveView::unloadData() { m_node.reset(); }
ProjectModel::INode* CurveView::currentNode() const
{
return m_node.get();
}
ProjectModel::INode* CurveView::currentNode() const { return m_node.get(); }
void CurveView::paintEvent(QPaintEvent* ev)
{
if (!m_node)
return;
amuse::ITable& table = **m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve)
return;
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
void CurveView::paintEvent(QPaintEvent* ev) {
if (!m_node) {
return;
}
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
amuse::ITable& table = **m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve) {
return;
}
auto& curve = static_cast<amuse::Curve&>(table);
qreal deviceRatio = devicePixelRatioF();
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
painter.setFont(m_gridFont);
qreal yIncrement = (height() - 16.0) / 10.0;
for (int i = 0; i < 11; ++i)
{
qreal thisY = i * yIncrement;
qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0));
painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]);
painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY));
const qreal deviceRatio = devicePixelRatioF();
const qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
painter.setFont(m_gridFont);
const qreal yIncrement = (height() - 16.0) / 10.0;
for (size_t i = 0; i < m_percentTexts.size(); ++i) {
const qreal thisY = i * yIncrement;
const qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0));
painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]);
painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY));
}
qreal xIncrement = (width() - 30.0) / 10.0;
for (size_t i = 0; i < m_percentTextsCenter.size(); ++i) {
const qreal thisX = i * xIncrement + 30.0;
const qreal textX = thisX - (i == 10 ? 30.0 : 15.0);
painter.drawStaticText(QPointF(textX, height() - 16.0), m_percentTextsCenter[i]);
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
}
size_t i;
xIncrement = (width() - 30.0) / 127.0;
QPointF lastPt;
painter.setPen(QPen(Qt::white, penWidth * 3.0));
for (i = 0; i < curve.data.size(); ++i) {
const QPointF thisPt(i * xIncrement + 30.0, (height() - 16.0) - (height() - 16.0) * (curve.data[i] / 127.0));
if (i != 0) {
painter.drawLine(lastPt, thisPt);
}
qreal xIncrement = (width() - 30.0) / 10.0;
for (int i = 0; i < 11; ++i)
{
qreal thisX = i * xIncrement + 30.0;
qreal textX = thisX - (i == 10 ? 30.0 : 15.0);
painter.drawStaticText(QPointF(textX, height() - 16.0), m_percentTextsCenter[i]);
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
lastPt = thisPt;
}
for (; i < 128; ++i) {
const QPointF thisPt(i * xIncrement + 30.0, height() - 16.0);
if (i != 0) {
painter.drawLine(lastPt, thisPt);
}
lastPt = thisPt;
}
}
int i;
xIncrement = (width() - 30.0) / 127.0;
QPointF lastPt;
painter.setPen(QPen(Qt::white, penWidth * 3.0));
for (i = 0; i < curve.data.size(); ++i)
{
QPointF thisPt(i * xIncrement + 30.0, (height() - 16.0) - (height() - 16.0) * (curve.data[i] / 127.0));
if (i)
painter.drawLine(lastPt, thisPt);
lastPt = thisPt;
}
for (; i < 128; ++i)
{
QPointF thisPt(i * xIncrement + 30.0, height() - 16.0);
if (i)
painter.drawLine(lastPt, thisPt);
lastPt = thisPt;
void CurveView::mousePressEvent(QMouseEvent* ev) { mouseMoveEvent(ev); }
void CurveView::mouseMoveEvent(QMouseEvent* ev) {
CurveView* view = getEditor()->m_curveView;
amuse::ITable& table = **view->m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve) {
return;
}
const qreal xIncrement = (width() - 30.0) / 127.0;
const int idx = int(std::round((ev->position().x() - 30.0) / xIncrement));
if (idx < 0 || idx > 127) {
return;
}
const int val = 127 - std::clamp(0, int(std::round(ev->position().y() / (height() - 16.0) * 127.0)), 127);
CurveEditUndoCommand::RedoData newData;
auto& curve = static_cast<amuse::Curve&>(table);
curve.data.resize(newData.size());
std::copy(curve.data.cbegin(), curve.data.cend(), newData.begin());
newData[idx] = uint8_t(val);
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, false, m_node));
update();
}
CurveView::CurveView(QWidget* parent) : QWidget(parent) {
for (size_t i = 0; i < m_percentTexts.size(); ++i) {
m_percentTexts[i].setText(QStringLiteral("%1%").arg(100 - i * 10));
m_percentTexts[i].setTextOption(QTextOption(Qt::AlignVCenter | Qt::AlignRight));
m_percentTexts[i].setTextWidth(28.0);
m_percentTextsCenter[i].setText(QStringLiteral("%1%").arg(i * 10));
m_percentTextsCenter[i].setTextOption(QTextOption(Qt::AlignBaseline | Qt::AlignCenter));
m_percentTextsCenter[i].setTextWidth(28.0);
}
m_gridFont.setPointSize(8);
}
CurveView::~CurveView() = default;
CurveEditor* CurveControls::getEditor() const { return qobject_cast<CurveEditor*>(parentWidget()); }
void CurveControls::loadData() {
m_lineEdit->setDisabled(false);
m_setExpr->setDisabled(false);
}
void CurveControls::unloadData() {
m_lineEdit->setDisabled(true);
m_setExpr->setDisabled(true);
}
void CurveControls::exprCommit() {
CurveView* view = getEditor()->m_curveView;
amuse::ITable& table = **view->m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve) {
return;
}
const QString progText = m_lineEdit->text();
CurveEditUndoCommand::RedoData newData;
auto& curve = static_cast<amuse::Curve&>(table);
curve.data.resize(newData.size());
std::copy(curve.data.cbegin(), curve.data.cend(), newData.begin());
bool notANumber = false;
for (size_t i = 0; i < newData.size(); ++i) {
m_engine.globalObject().setProperty(QStringLiteral("x"), i / 127.0);
QJSValue val = m_engine.evaluate(progText);
if (val.isError()) {
m_errLabel->setText(val.toString());
return;
} else if (val.isNumber()) {
newData[i] = uint8_t(std::clamp(0, int(std::round(val.toNumber() * 127.0)), 127));
} else {
notANumber = true;
newData[i] = 0;
}
}
if (notANumber)
m_errLabel->setText(tr("Did not evaluate as a number"));
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, true, view->m_node));
view->update();
}
void CurveView::mousePressEvent(QMouseEvent* ev)
{
mouseMoveEvent(ev);
void CurveControls::resizeEvent(QResizeEvent* ev) { m_errLabel->setGeometry(22, 78, width() - 44, 14); }
CurveControls::CurveControls(QWidget* parent) : QFrame(parent) {
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(100);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
QHBoxLayout* mainLayout = new QHBoxLayout;
QGridLayout* leftLayout = new QGridLayout;
leftLayout->addWidget(new QLabel(tr("Expression")), 0, 0);
m_lineEdit = new QLineEdit;
m_lineEdit->setDisabled(true);
QPalette palette = m_lineEdit->palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
m_lineEdit->setPalette(palette);
connect(m_lineEdit, &QLineEdit::returnPressed, this, &CurveControls::exprCommit);
leftLayout->addWidget(m_lineEdit, 1, 0);
m_setExpr = new QPushButton(tr("Apply"));
m_setExpr->setDisabled(true);
connect(m_setExpr, &QPushButton::clicked, this, &CurveControls::exprCommit);
leftLayout->addWidget(m_setExpr, 1, 1);
m_errLabel = new QLabel(this);
QFont font = m_errLabel->font();
font.setPointSize(8);
m_errLabel->setFont(font);
palette.setColor(QPalette::Text, Qt::red);
m_errLabel->setPalette(palette);
leftLayout->setColumnMinimumWidth(0, 500);
leftLayout->setColumnStretch(0, 1);
leftLayout->setColumnMinimumWidth(1, 75);
leftLayout->setColumnStretch(1, 0);
leftLayout->setRowMinimumHeight(0, 22);
leftLayout->setRowMinimumHeight(1, 37);
leftLayout->setContentsMargins(10, 6, 10, 14);
mainLayout->addLayout(leftLayout);
setLayout(mainLayout);
QJSValueIterator it(m_engine.globalObject().property(QStringLiteral("Math")));
QString docStr =
tr("Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available:\n");
bool needsComma = false;
while (it.hasNext()) {
it.next();
m_engine.globalObject().setProperty(it.name(), it.value());
if (needsComma)
docStr += QStringLiteral(", ");
needsComma = true;
docStr += it.name();
}
m_lineEdit->setToolTip(docStr);
}
void CurveView::mouseMoveEvent(QMouseEvent* ev)
{
CurveView* view = getEditor()->m_curveView;
amuse::ITable& table = **view->m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve)
return;
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
CurveControls::~CurveControls() = default;
qreal xIncrement = (width() - 30.0) / 127.0;
int idx = int(std::round((ev->localPos().x() - 30.0) / xIncrement));
if (idx < 0 || idx > 127)
return;
int val = 127 - amuse::clamp(0, int(std::round(ev->localPos().y() / (height() - 16.0) * 127.0)), 127);
curve.data.resize(128);
uint8_t newData[128];
std::memcpy(newData, curve.data.data(), 128);
newData[idx] = uint8_t(val);
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, false, m_node));
update();
bool CurveEditor::loadData(ProjectModel::CurveNode* node) {
m_curveView->loadData(node);
m_controls->loadData();
return true;
}
CurveView::CurveView(QWidget* parent)
: QWidget(parent)
{
for (int i = 0; i < 11; ++i)
{
m_percentTexts[i].setText(QStringLiteral("%1%").arg(100 - i * 10));
m_percentTexts[i].setTextOption(QTextOption(Qt::AlignVCenter | Qt::AlignRight));
m_percentTexts[i].setTextWidth(28.0);
m_percentTextsCenter[i].setText(QStringLiteral("%1%").arg(i * 10));
m_percentTextsCenter[i].setTextOption(QTextOption(Qt::AlignBaseline | Qt::AlignCenter));
m_percentTextsCenter[i].setTextWidth(28.0);
}
m_gridFont.setPointSize(8);
void CurveEditor::unloadData() {
m_curveView->unloadData();
m_controls->unloadData();
}
CurveEditor* CurveControls::getEditor() const
{
return qobject_cast<CurveEditor*>(parentWidget());
}
void CurveControls::loadData()
{
m_lineEdit->setDisabled(false);
m_setExpr->setDisabled(false);
}
void CurveControls::unloadData()
{
m_lineEdit->setDisabled(true);
m_setExpr->setDisabled(true);
}
void CurveControls::exprCommit()
{
CurveView* view = getEditor()->m_curveView;
amuse::ITable& table = **view->m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve)
return;
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
QString progText = m_lineEdit->text();
curve.data.resize(128);
uint8_t newData[128];
std::memcpy(newData, curve.data.data(), 128);
bool notANumber = false;
for (int i = 0; i < 128; ++i)
{
m_engine.globalObject().setProperty(QStringLiteral("x"), i / 127.0);
QJSValue val = m_engine.evaluate(progText);
if (val.isError())
{
m_errLabel->setText(val.toString());
return;
}
else if (val.isNumber())
{
newData[i] = uint8_t(amuse::clamp(0, int(std::round(val.toNumber() * 127.0)), 127));
}
else
{
notANumber = true;
newData[i] = 0;
}
}
if (notANumber)
m_errLabel->setText(tr("Did not evaluate as a number"));
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, true, view->m_node));
view->update();
}
void CurveControls::resizeEvent(QResizeEvent* ev)
{
m_errLabel->setGeometry(22, 78, width() - 44, 14);
}
CurveControls::CurveControls(QWidget* parent)
: QFrame(parent)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(100);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
QHBoxLayout* mainLayout = new QHBoxLayout;
QGridLayout* leftLayout = new QGridLayout;
leftLayout->addWidget(new QLabel(tr("Expression")), 0, 0);
m_lineEdit = new QLineEdit;
m_lineEdit->setDisabled(true);
QPalette palette = m_lineEdit->palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
m_lineEdit->setPalette(palette);
connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(exprCommit()));
leftLayout->addWidget(m_lineEdit, 1, 0);
m_setExpr = new QPushButton(tr("Apply"));
m_setExpr->setDisabled(true);
connect(m_setExpr, SIGNAL(clicked(bool)), this, SLOT(exprCommit()));
leftLayout->addWidget(m_setExpr, 1, 1);
m_errLabel = new QLabel(this);
QFont font = m_errLabel->font();
font.setPointSize(8);
m_errLabel->setFont(font);
palette.setColor(QPalette::Text, Qt::red);
m_errLabel->setPalette(palette);
leftLayout->setColumnMinimumWidth(0, 500);
leftLayout->setColumnStretch(0, 1);
leftLayout->setColumnMinimumWidth(1, 75);
leftLayout->setColumnStretch(1, 0);
leftLayout->setRowMinimumHeight(0, 22);
leftLayout->setRowMinimumHeight(1, 37);
leftLayout->setContentsMargins(10, 6, 10, 14);
mainLayout->addLayout(leftLayout);
setLayout(mainLayout);
QJSValueIterator it(m_engine.globalObject().property(QStringLiteral("Math")));
QString docStr = tr("Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available:\n");
bool needsComma = false;
while (it.hasNext())
{
it.next();
m_engine.globalObject().setProperty(it.name(), it.value());
if (needsComma)
docStr += QStringLiteral(", ");
needsComma = true;
docStr += it.name();
}
m_lineEdit->setToolTip(docStr);
}
bool CurveEditor::loadData(ProjectModel::CurveNode* node)
{
m_curveView->loadData(node);
m_controls->loadData();
return true;
}
void CurveEditor::unloadData()
{
m_curveView->unloadData();
m_controls->unloadData();
}
ProjectModel::INode* CurveEditor::currentNode() const
{
return m_curveView->currentNode();
}
ProjectModel::INode* CurveEditor::currentNode() const { return m_curveView->currentNode(); }
CurveEditor::CurveEditor(QWidget* parent)
: EditorWidget(parent), m_curveView(new CurveView), m_controls(new CurveControls)
{
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_curveView);
layout->addWidget(m_controls);
setLayout(layout);
: EditorWidget(parent), m_curveView(new CurveView), m_controls(new CurveControls) {
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_curveView);
layout->addWidget(m_controls);
setLayout(layout);
}
CurveEditor::~CurveEditor() = default;

View File

@@ -1,66 +1,77 @@
#ifndef AMUSE_CURVE_EDITOR_HPP
#define AMUSE_CURVE_EDITOR_HPP
#pragma once
#include <array>
#include <QFrame>
#include <QJSEngine>
#include <QStaticText>
#include "EditorWidget.hpp"
#include <QFrame>
#include <QLabel>
#include <QStaticText>
#include <QLineEdit>
#include <QPushButton>
#include <QJSEngine>
#include "ProjectModel.hpp"
#include <amuse/Common.hpp>
class CurveEditor;
class CurveView : public QWidget
{
Q_OBJECT
friend class CurveControls;
amuse::ObjToken<ProjectModel::CurveNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
QStaticText m_percentTextsCenter[11];
CurveEditor* getEditor() const;
public:
explicit CurveView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
class QLabel;
class QLineEdit;
class QPushButton;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
class CurveView : public QWidget {
Q_OBJECT
friend class CurveControls;
amuse::ObjToken<ProjectModel::CurveNode> m_node;
QFont m_gridFont;
std::array<QStaticText, 11> m_percentTexts;
std::array<QStaticText, 11> m_percentTextsCenter;
CurveEditor* getEditor() const;
public:
explicit CurveView(QWidget* parent = Q_NULLPTR);
~CurveView() override;
void loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev) override;
void mousePressEvent(QMouseEvent* ev) override;
void mouseMoveEvent(QMouseEvent* ev) override;
};
class CurveControls : public QFrame
{
Q_OBJECT
friend class CurveView;
QLineEdit* m_lineEdit;
QLabel* m_errLabel;
QPushButton* m_setExpr;
QJSEngine m_engine;
CurveEditor* getEditor() const;
class CurveControls : public QFrame {
Q_OBJECT
friend class CurveView;
QLineEdit* m_lineEdit;
QLabel* m_errLabel;
QPushButton* m_setExpr;
QJSEngine m_engine;
CurveEditor* getEditor() const;
public:
explicit CurveControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
void resizeEvent(QResizeEvent* ev);
explicit CurveControls(QWidget* parent = Q_NULLPTR);
~CurveControls() override;
void loadData();
void unloadData();
void resizeEvent(QResizeEvent* ev) override;
public slots:
void exprCommit();
void exprCommit();
};
class CurveEditor : public EditorWidget
{
Q_OBJECT
friend class CurveView;
friend class CurveControls;
CurveView* m_curveView;
CurveControls* m_controls;
class CurveEditor : public EditorWidget {
Q_OBJECT
friend class CurveView;
friend class CurveControls;
CurveView* m_curveView;
CurveControls* m_controls;
public:
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
};
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
~CurveEditor() override;
#endif //AMUSE_CURVE_EDITOR_HPP
bool loadData(ProjectModel::CurveNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
};

View File

@@ -3,322 +3,281 @@
#include <QStandardItemModel>
#include <QHBoxLayout>
EditorWidget::EditorWidget(QWidget* parent)
: QWidget(parent)
{
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) {}
EditorWidget::~EditorWidget() = default;
void EditorUndoCommand::undo() { g_MainWindow->openEditor(m_node.get()); }
void EditorUndoCommand::redo() { g_MainWindow->openEditor(m_node.get()); }
FieldSlider::FieldSlider(QWidget* parent) : QWidget(parent), m_slider(Qt::Horizontal) {
setFixedHeight(22);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(&m_slider);
layout->addWidget(&m_value);
layout->setContentsMargins(QMargins());
setLayout(layout);
m_value.setFixedWidth(42);
connect(&m_slider, qOverload<int>(&QSlider::valueChanged), this, &FieldSlider::doValueChanged);
m_slider.setValue(0);
}
void EditorUndoCommand::undo()
{
g_MainWindow->openEditor(m_node.get());
void FieldSlider::doValueChanged(int value) {
m_value.setText(QString::number(value));
emit valueChanged(value);
}
void EditorUndoCommand::redo()
{
g_MainWindow->openEditor(m_node.get());
FieldDoubleSlider::FieldDoubleSlider(QWidget* parent) : QWidget(parent), m_slider(Qt::Horizontal) {
setFixedHeight(22);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(&m_slider);
layout->addWidget(&m_value);
layout->setContentsMargins(QMargins());
setLayout(layout);
m_value.setFixedWidth(42);
connect(&m_slider, qOverload<int>(&QSlider::valueChanged), this, &FieldDoubleSlider::doValueChanged);
m_slider.setRange(0, 1000);
m_slider.setValue(0);
}
FieldSlider::FieldSlider(QWidget* parent)
: QWidget(parent), m_slider(Qt::Horizontal)
{
setFixedHeight(22);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(&m_slider);
layout->addWidget(&m_value);
layout->setContentsMargins(QMargins());
setLayout(layout);
m_value.setFixedWidth(42);
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
m_slider.setValue(0);
double FieldDoubleSlider::value() const {
double t = m_slider.value() / 1000.0;
return t * (m_max - m_min) + m_min;
}
void FieldDoubleSlider::setValue(double value) {
if (value < m_min)
value = m_min;
else if (value > m_max)
value = m_max;
double t = (value - m_min) / (m_max - m_min);
m_slider.setValue(int(t * 1000.0));
doValueChanged(int(t * 1000.0));
}
void FieldDoubleSlider::setRange(double min, double max) {
m_min = min;
m_max = max;
double curValue = value();
if (curValue < min)
setValue(min);
else if (curValue > max)
setValue(max);
}
void FieldSlider::doValueChanged(int value)
{
m_value.setText(QString::number(value));
emit valueChanged(value);
}
FieldDoubleSlider::FieldDoubleSlider(QWidget* parent)
: QWidget(parent), m_slider(Qt::Horizontal)
{
setFixedHeight(22);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(&m_slider);
layout->addWidget(&m_value);
layout->setContentsMargins(QMargins());
setLayout(layout);
m_value.setFixedWidth(42);
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
m_slider.setRange(0, 1000);
m_slider.setValue(0);
}
double FieldDoubleSlider::value() const
{
double t = m_slider.value() / 1000.0;
return t * (m_max - m_min) + m_min;
}
void FieldDoubleSlider::setValue(double value)
{
if (value < m_min)
value = m_min;
else if (value > m_max)
value = m_max;
double t = (value - m_min) / (m_max - m_min);
m_slider.setValue(int(t * 1000.0));
doValueChanged(int(t * 1000.0));
}
void FieldDoubleSlider::setRange(double min, double max)
{
m_min = min; m_max = max;
double curValue = value();
if (curValue < min)
setValue(min);
else if (curValue > max)
setValue(max);
}
void FieldDoubleSlider::doValueChanged(int value)
{
double t = value / 1000.0;
t = t * (m_max - m_min) + m_min;
m_value.setText(QString::number(t, 'g', 2));
emit valueChanged(t);
void FieldDoubleSlider::doValueChanged(int value) {
double t = value / 1000.0;
t = t * (m_max - m_min) + m_min;
m_value.setText(QString::number(t, 'g', 2));
emit valueChanged(t);
}
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
: QWidget(parent), m_comboBox(this), m_button(this)
{
: QWidget(parent), m_comboBox(this), m_button(this) {
m_button.setDisabled(true);
m_button.setFixedSize(30, 30);
m_comboBox.setFixedHeight(30);
connect(&m_comboBox, qOverload<int>(&FieldComboBox::currentIndexChanged), this,
&FieldProjectNode::_currentIndexChanged);
connect(&m_button, &QPushButton::clicked, this, &FieldProjectNode::openCurrent);
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
m_button.setIcon(icon);
QHBoxLayout* layout = new QHBoxLayout;
layout->setContentsMargins({});
layout->setSpacing(0);
layout->addWidget(&m_comboBox);
layout->addWidget(&m_button);
setLayout(layout);
setCollection(collection);
}
FieldProjectNode::~FieldProjectNode() = default;
void FieldProjectNode::setCollection(ProjectModel::CollectionNode* collection) {
m_collection = collection;
if (!collection) {
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
m_button.setDisabled(true);
m_button.setFixedSize(30, 30);
m_comboBox.setFixedHeight(30);
connect(&m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
m_button.setIcon(icon);
QHBoxLayout* layout = new QHBoxLayout;
layout->setContentsMargins({});
layout->setSpacing(0);
layout->addWidget(&m_comboBox);
layout->addWidget(&m_button);
setLayout(layout);
setCollection(collection);
return;
}
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getNullProxy());
m_comboBox.setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
}
void FieldProjectNode::setCollection(ProjectModel::CollectionNode* collection)
{
m_collection = collection;
if (!collection)
{
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
m_button.setDisabled(true);
return;
}
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getNullProxy());
m_comboBox.setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
void FieldProjectNode::_currentIndexChanged(int index) {
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
}
void FieldProjectNode::_currentIndexChanged(int index)
{
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
ProjectModel::BasePoolObjectNode* FieldProjectNode::currentNode() const {
int index = m_comboBox.currentIndex();
if (index == 0)
return nullptr;
else
return m_collection->nodeOfIndex(index - 1);
}
ProjectModel::BasePoolObjectNode* FieldProjectNode::currentNode() const
{
int index = m_comboBox.currentIndex();
if (index == 0)
return nullptr;
else
return m_collection->nodeOfIndex(index - 1);
bool FieldProjectNode::event(QEvent* ev) {
if (ev->type() == QEvent::User) {
showPopup();
return true;
}
return QWidget::event(ev);
}
bool FieldProjectNode::event(QEvent* ev)
{
if (ev->type() == QEvent::User)
{
showPopup();
return true;
}
return QWidget::event(ev);
}
void FieldProjectNode::openCurrent()
{
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
void FieldProjectNode::openCurrent() {
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
}
FieldPageObjectNode::FieldPageObjectNode(ProjectModel::GroupNode* group, QWidget* parent)
: QWidget(parent), m_comboBox(this), m_button(this)
{
: QWidget(parent), m_comboBox(this), m_button(this) {
m_button.setDisabled(true);
m_button.setFixedSize(30, 30);
m_comboBox.setFixedHeight(30);
connect(&m_comboBox, qOverload<int>(&FieldComboBox::currentIndexChanged), this,
&FieldPageObjectNode::_currentIndexChanged);
connect(&m_button, &QPushButton::clicked, this, &FieldPageObjectNode::openCurrent);
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
m_button.setIcon(icon);
QHBoxLayout* layout = new QHBoxLayout;
layout->setContentsMargins({});
layout->setSpacing(0);
layout->addWidget(&m_comboBox);
layout->addWidget(&m_button);
setLayout(layout);
setGroup(group);
}
FieldPageObjectNode::~FieldPageObjectNode() = default;
void FieldPageObjectNode::setGroup(ProjectModel::GroupNode* group) {
m_group = group;
if (!group) {
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
m_button.setDisabled(true);
m_button.setFixedSize(30, 30);
m_comboBox.setFixedHeight(30);
connect(&m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
m_button.setIcon(icon);
QHBoxLayout* layout = new QHBoxLayout;
layout->setContentsMargins({});
layout->setSpacing(0);
layout->addWidget(&m_comboBox);
layout->addWidget(&m_button);
setLayout(layout);
setGroup(group);
return;
}
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getPageObjectProxy());
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
}
void FieldPageObjectNode::setGroup(ProjectModel::GroupNode* group)
{
m_group = group;
if (!group)
{
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
m_button.setDisabled(true);
return;
}
void FieldPageObjectNode::_currentIndexChanged(int index) {
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
}
ProjectModel::BasePoolObjectNode* FieldPageObjectNode::currentNode() const {
int index = m_comboBox.currentIndex();
if (index == 0) {
return nullptr;
} else {
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getPageObjectProxy());
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
return static_cast<ProjectModel::BasePoolObjectNode*>(model->node(model->getPageObjectProxy()->mapToSource(
model->getPageObjectProxy()->index(index, 0, m_comboBox.rootModelIndex()))));
}
}
void FieldPageObjectNode::_currentIndexChanged(int index)
{
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
bool FieldPageObjectNode::event(QEvent* ev) {
if (ev->type() == QEvent::User) {
showPopup();
return true;
}
return QWidget::event(ev);
}
ProjectModel::BasePoolObjectNode* FieldPageObjectNode::currentNode() const
{
int index = m_comboBox.currentIndex();
if (index == 0)
{
return nullptr;
}
else
{
ProjectModel* model = g_MainWindow->projectModel();
return static_cast<ProjectModel::BasePoolObjectNode*>(
model->node(model->getPageObjectProxy()->mapToSource(
model->getPageObjectProxy()->index(index, 0, m_comboBox.rootModelIndex()))));
}
}
bool FieldPageObjectNode::event(QEvent* ev)
{
if (ev->type() == QEvent::User)
{
showPopup();
return true;
}
return QWidget::event(ev);
}
void FieldPageObjectNode::openCurrent()
{
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
void FieldPageObjectNode::openCurrent() {
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
}
AddRemoveButtons::AddRemoveButtons(QWidget* parent)
: QWidget(parent), m_addAction(tr("Add Row")), m_addButton(this),
m_removeAction(tr("Remove Row")), m_removeButton(this)
{
setFixedSize(64, 32);
: QWidget(parent)
, m_addAction(tr("Add Row"))
, m_addButton(this)
, m_removeAction(tr("Remove Row"))
, m_removeButton(this) {
setFixedSize(64, 32);
m_addAction.setIcon(QIcon(QStringLiteral(":/icons/IconAdd.svg")));
m_addButton.setDefaultAction(&m_addAction);
m_addButton.setFixedSize(32, 32);
m_addButton.move(0, 0);
m_addAction.setIcon(QIcon(QStringLiteral(":/icons/IconAdd.svg")));
m_addButton.setDefaultAction(&m_addAction);
m_addButton.setFixedSize(32, 32);
m_addButton.move(0, 0);
m_removeAction.setIcon(QIcon(QStringLiteral(":/icons/IconRemove.svg")));
m_removeButton.setDefaultAction(&m_removeAction);
m_removeButton.setFixedSize(32, 32);
m_removeButton.move(32, 0);
m_removeAction.setEnabled(false);
m_removeAction.setIcon(QIcon(QStringLiteral(":/icons/IconRemove.svg")));
m_removeButton.setDefaultAction(&m_removeAction);
m_removeButton.setFixedSize(32, 32);
m_removeButton.move(32, 0);
m_removeAction.setEnabled(false);
}
static QIcon ListingDeleteIcon;
static QIcon ListingDeleteHoveredIcon;
void ListingDeleteButton::enterEvent(QEvent* event)
{
setIcon(ListingDeleteHoveredIcon);
void ListingDeleteButton::enterEvent(QEnterEvent* event) { setIcon(ListingDeleteHoveredIcon); }
void ListingDeleteButton::leaveEvent(QEvent* event) { setIcon(ListingDeleteIcon); }
ListingDeleteButton::ListingDeleteButton(QWidget* parent) : QPushButton(parent) {
if (ListingDeleteIcon.isNull())
ListingDeleteIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg"));
if (ListingDeleteHoveredIcon.isNull())
ListingDeleteHoveredIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDeleteHovered.svg"));
setVisible(false);
setFixedSize(21, 21);
setFlat(true);
setToolTip(tr("Delete this SoundMacro"));
setIcon(ListingDeleteIcon);
}
void ListingDeleteButton::leaveEvent(QEvent* event)
{
setIcon(ListingDeleteIcon);
}
bool BaseObjectDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index) {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
if (ev->button() == Qt::RightButton) {
ProjectModel::INode* node = getNode(model, index);
ListingDeleteButton::ListingDeleteButton(QWidget* parent)
: QPushButton(parent)
{
if (ListingDeleteIcon.isNull())
ListingDeleteIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg"));
if (ListingDeleteHoveredIcon.isNull())
ListingDeleteHoveredIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDeleteHovered.svg"));
ContextMenu* menu = new ContextMenu;
setVisible(false);
setFixedSize(21, 21);
setFlat(true);
setToolTip(tr("Delete this SoundMacro"));
setIcon(ListingDeleteIcon);
}
QAction* openEditorAction = new QAction(tr("Open in Editor"), menu);
openEditorAction->setData(QVariant::fromValue((void*)node));
openEditorAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
connect(openEditorAction, &QAction::triggered, this, &BaseObjectDelegate::doOpenEditor);
menu->addAction(openEditorAction);
bool BaseObjectDelegate::editorEvent(QEvent *event, QAbstractItemModel* model,
const QStyleOptionViewItem &option, const QModelIndex &index)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
if (ev->button() == Qt::RightButton)
{
ProjectModel::INode* node = getNode(model, index);
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
findUsagesAction->setData(QVariant::fromValue((void*)node));
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
connect(findUsagesAction, &QAction::triggered, this, &BaseObjectDelegate::doFindUsages);
menu->addAction(findUsagesAction);
ContextMenu* menu = new ContextMenu;
QAction* openEditorAction = new QAction(tr("Open in Editor"), menu);
openEditorAction->setData(QVariant::fromValue((void*)node));
openEditorAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
connect(openEditorAction, SIGNAL(triggered()), this, SLOT(doOpenEditor()));
menu->addAction(openEditorAction);
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
findUsagesAction->setData(QVariant::fromValue((void*)node));
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(doFindUsages()));
menu->addAction(findUsagesAction);
menu->popup(ev->globalPos());
}
menu->popup(ev->globalPosition().toPoint());
}
return false;
}
return false;
}
void BaseObjectDelegate::doOpenEditor()
{
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->openEditor(node);
void BaseObjectDelegate::doOpenEditor() {
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->openEditor(node);
}
void BaseObjectDelegate::doFindUsages()
{
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->findUsages(node);
void BaseObjectDelegate::doFindUsages() {
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->findUsages(node);
}

View File

@@ -1,254 +1,256 @@
#ifndef AMUSE_EDITOR_WIDGET_HPP
#define AMUSE_EDITOR_WIDGET_HPP
#pragma once
#include <QWidget>
#include <QUndoCommand>
#include <QApplication>
#include <QSpinBox>
#include <QComboBox>
#include <QWheelEvent>
#include <QItemEditorFactory>
#include <QToolButton>
#include <QAction>
#include <QPushButton>
#include <QComboBox>
#include <QItemEditorFactory>
#include <QLabel>
#include <QMenu>
#include <QPushButton>
#include <QSpinBox>
#include <QStyledItemDelegate>
#include <QToolButton>
#include <QUndoCommand>
#include <QWheelEvent>
#include <QWidget>
#include "ProjectModel.hpp"
class EditorWidget : public QWidget
{
Q_OBJECT
#include <amuse/Common.hpp>
class EditorWidget : public QWidget {
Q_OBJECT
public:
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
virtual bool valid() const { return true; }
virtual void unloadData() {}
virtual ProjectModel::INode* currentNode() const { return nullptr; }
virtual void setEditorEnabled(bool en) { setEnabled(en); }
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
~EditorWidget() override;
virtual bool valid() const { return true; }
virtual void unloadData() {}
virtual ProjectModel::INode* currentNode() const { return nullptr; }
virtual void setEditorEnabled(bool en) { setEnabled(en); }
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
public slots:
virtual void itemCutAction() {}
virtual void itemCopyAction() {}
virtual void itemPasteAction() {}
virtual void itemDeleteAction() {}
virtual void itemCutAction() {}
virtual void itemCopyAction() {}
virtual void itemPasteAction() {}
virtual void itemDeleteAction() {}
};
class EditorUndoCommand : public QUndoCommand
{
class EditorUndoCommand : public QUndoCommand {
protected:
amuse::ObjToken<ProjectModel::INode> m_node;
enum class Id
{
SMChangeVal,
SampLoop,
SampPitch,
ADSRAttack,
ADSRDecay,
ADSRSustain,
ADSRAttackAndDecay,
ADSRDecayAndSustain,
ADSRRelease,
ADSRDLS,
ADSRVelToAttack,
ADSRKeyToDecay,
CurveEdit
};
amuse::ObjToken<ProjectModel::INode> m_node;
enum class Id {
SMChangeVal,
SampLoop,
SampPitch,
ADSRAttack,
ADSRDecay,
ADSRSustain,
ADSRAttackAndDecay,
ADSRDecayAndSustain,
ADSRRelease,
ADSRDLS,
ADSRVelToAttack,
ADSRKeyToDecay,
CurveEdit
};
public:
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node,
const QString& text, QUndoCommand* parent = nullptr)
: QUndoCommand(text, parent), m_node(node) {}
void undo();
void redo();
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node, const QString& text, QUndoCommand* parent = nullptr)
: QUndoCommand(text, parent), m_node(node) {}
void undo() override;
void redo() override;
};
class FieldSpinBox : public QSpinBox
{
Q_OBJECT
class FieldSpinBox : public QSpinBox {
Q_OBJECT
public:
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR)
: QSpinBox(parent) {}
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR) : QSpinBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
};
class FieldSlider : public QWidget
{
Q_OBJECT
QSlider m_slider;
QLabel m_value;
class FieldSlider : public QWidget {
Q_OBJECT
QSlider m_slider;
QLabel m_value;
public:
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
int value() const { return m_slider.value(); }
void setValue(int value) { m_slider.setValue(value); doValueChanged(value); }
void setRange(int min, int max) { m_slider.setRange(min, max); }
int value() const { return m_slider.value(); }
void setValue(int value) {
m_slider.setValue(value);
doValueChanged(value);
}
void setRange(int min, int max) { m_slider.setRange(min, max); }
private slots:
void doValueChanged(int value);
void doValueChanged(int value);
signals:
void valueChanged(int value);
void valueChanged(int value);
};
class FieldDoubleSlider : public QWidget
{
Q_OBJECT
QSlider m_slider;
QLabel m_value;
double m_min = 0.0;
double m_max = 1.0;
class FieldDoubleSlider : public QWidget {
Q_OBJECT
QSlider m_slider;
QLabel m_value;
double m_min = 0.0;
double m_max = 1.0;
public:
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
double value() const;
void setValue(double value);
void setRange(double min, double max);
double value() const;
void setValue(double value);
void setRange(double min, double max);
private slots:
void doValueChanged(int value);
void doValueChanged(int value);
signals:
void valueChanged(double value);
void valueChanged(double value);
};
class FieldComboBox : public QComboBox
{
Q_OBJECT
class FieldComboBox : public QComboBox {
Q_OBJECT
public:
explicit FieldComboBox(QWidget* parent = Q_NULLPTR)
: QComboBox(parent) {}
explicit FieldComboBox(QWidget* parent = Q_NULLPTR) : QComboBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
};
class FieldProjectNode : public QWidget
{
Q_OBJECT
ProjectModel::CollectionNode* m_collection;
FieldComboBox m_comboBox;
QPushButton m_button;
class FieldProjectNode : public QWidget {
Q_OBJECT
ProjectModel::CollectionNode* m_collection;
FieldComboBox m_comboBox;
QPushButton m_button;
public:
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
void setCollection(ProjectModel::CollectionNode* collection);
ProjectModel::CollectionNode* collection() const { return m_collection; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev);
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
~FieldProjectNode() override;
void setCollection(ProjectModel::CollectionNode* collection);
ProjectModel::CollectionNode* collection() const { return m_collection; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev) override;
private slots:
void _currentIndexChanged(int);
void _currentIndexChanged(int);
public slots:
void openCurrent();
void openCurrent();
signals:
void currentIndexChanged(int);
void currentIndexChanged(int);
};
class FieldPageObjectNode : public QWidget
{
Q_OBJECT
ProjectModel::GroupNode* m_group;
FieldComboBox m_comboBox;
QPushButton m_button;
class FieldPageObjectNode : public QWidget {
Q_OBJECT
ProjectModel::GroupNode* m_group;
FieldComboBox m_comboBox;
QPushButton m_button;
public:
explicit FieldPageObjectNode(ProjectModel::GroupNode* group = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
void setGroup(ProjectModel::GroupNode* group);
ProjectModel::GroupNode* group() const { return m_group; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
QModelIndex rootModelIndex() const { return m_comboBox.rootModelIndex(); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev);
explicit FieldPageObjectNode(ProjectModel::GroupNode* group = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
~FieldPageObjectNode() override;
void setGroup(ProjectModel::GroupNode* group);
ProjectModel::GroupNode* group() const { return m_group; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
QModelIndex rootModelIndex() const { return m_comboBox.rootModelIndex(); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev) override;
private slots:
void _currentIndexChanged(int);
void _currentIndexChanged(int);
public slots:
void openCurrent();
void openCurrent();
signals:
void currentIndexChanged(int);
void currentIndexChanged(int);
};
template <class T>
class EditorFieldNode : public T
{
bool m_deferPopupOpen = true;
class EditorFieldNode : public T {
bool m_deferPopupOpen = true;
public:
using T::T;
bool shouldPopupOpen()
{
bool ret = m_deferPopupOpen;
m_deferPopupOpen = false;
return ret;
}
using T::T;
bool shouldPopupOpen() {
bool ret = m_deferPopupOpen;
m_deferPopupOpen = false;
return ret;
}
};
using EditorFieldProjectNode = EditorFieldNode<FieldProjectNode>;
using EditorFieldPageObjectNode = EditorFieldNode<FieldPageObjectNode>;
template <int MIN, int MAX>
class RangedValueFactory : public QItemEditorFactory
{
class RangedValueFactory : public QItemEditorFactory {
public:
QWidget* createEditor(int userType, QWidget *parent) const
{
QSpinBox* sb = new QSpinBox(parent);
sb->setFrame(false);
sb->setMinimum(MIN);
sb->setMaximum(MAX);
return sb;
}
QWidget* createEditor(int userType, QWidget* parent) const override {
QSpinBox* sb = new QSpinBox(parent);
sb->setFrame(false);
sb->setMinimum(MIN);
sb->setMaximum(MAX);
return sb;
}
};
class AddRemoveButtons : public QWidget
{
Q_OBJECT
QAction m_addAction;
QToolButton m_addButton;
QAction m_removeAction;
QToolButton m_removeButton;
class AddRemoveButtons : public QWidget {
Q_OBJECT
QAction m_addAction;
QToolButton m_addButton;
QAction m_removeAction;
QToolButton m_removeButton;
public:
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
QAction* addAction() { return &m_addAction; }
QAction* removeAction() { return &m_removeAction; }
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
QAction* addAction() { return &m_addAction; }
QAction* removeAction() { return &m_removeAction; }
};
class ListingDeleteButton : public QPushButton
{
Q_OBJECT
class ListingDeleteButton : public QPushButton {
Q_OBJECT
public:
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
};
class ContextMenu : public QMenu
{
class ContextMenu : public QMenu {
public:
void hideEvent(QHideEvent* ev)
{
QMenu::hideEvent(ev);
deleteLater();
}
void hideEvent(QHideEvent* ev) override {
QMenu::hideEvent(ev);
deleteLater();
}
};
class BaseObjectDelegate : public QStyledItemDelegate
{
Q_OBJECT
class BaseObjectDelegate : public QStyledItemDelegate {
Q_OBJECT
protected:
virtual ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const = 0;
public:
explicit BaseObjectDelegate(QObject* parent = Q_NULLPTR) : QStyledItemDelegate(parent) {}
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
private slots:
void doOpenEditor();
void doFindUsages();
};
virtual ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const = 0;
#endif //AMUSE_EDITOR_WIDGET_HPP
public:
explicit BaseObjectDelegate(QObject* parent = Q_NULLPTR) : QStyledItemDelegate(parent) {}
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index) override;
private slots:
void doOpenEditor();
void doFindUsages();
};

View File

@@ -1,271 +1,223 @@
#include "KeyboardWidget.hpp"
#include <QApplication>
#include <QHBoxLayout>
#include <QSvgRenderer>
#include <QMouseEvent>
#include <QScrollArea>
#include <QApplication>
#include <QScrollBar>
#include <QSvgRenderer>
const QString NaturalKeyNames[] =
{
QStringLiteral("C"),
QStringLiteral("D"),
QStringLiteral("E"),
QStringLiteral("F"),
QStringLiteral("G"),
QStringLiteral("A"),
QStringLiteral("B")
#include "Common.hpp"
#include "StatusBarWidget.hpp"
const std::array<QString, 7> NaturalKeyNames{
QStringLiteral("C"), QStringLiteral("D"), QStringLiteral("E"), QStringLiteral("F"),
QStringLiteral("G"), QStringLiteral("A"), QStringLiteral("B"),
};
const QString SharpKeyNames[] =
{
QStringLiteral("Cs"),
QStringLiteral("Ds"),
QStringLiteral("Fs"),
QStringLiteral("Gs"),
QStringLiteral("As")
const std::array<QString, 5> SharpKeyNames{
QStringLiteral("Cs"), QStringLiteral("Ds"), QStringLiteral("Fs"), QStringLiteral("Gs"), QStringLiteral("As"),
};
const QString KeyStrings[] =
{
QStringLiteral("C"),
QStringLiteral("C#"),
QStringLiteral("D"),
QStringLiteral("D#"),
QStringLiteral("E"),
QStringLiteral("F"),
QStringLiteral("F#"),
QStringLiteral("G"),
QStringLiteral("G#"),
QStringLiteral("A"),
QStringLiteral("A#"),
QStringLiteral("B")
const std::array<QString, 12> KeyStrings{
QStringLiteral("C"), QStringLiteral("C#"), QStringLiteral("D"), QStringLiteral("D#"),
QStringLiteral("E"), QStringLiteral("F"), QStringLiteral("F#"), QStringLiteral("G"),
QStringLiteral("G#"), QStringLiteral("A"), QStringLiteral("A#"), QStringLiteral("B"),
};
const int NaturalKeyNumbers[] =
{
0, 2, 4, 5, 7, 9, 11
const std::array<int, 7> NaturalKeyNumbers{
0, 2, 4, 5, 7, 9, 11,
};
const int SharpKeyNumbers[] =
{
1, 3, 6, 8, 10
const std::array<int, 5> SharpKeyNumbers{
1, 3, 6, 8, 10,
};
KeyboardOctave::KeyboardOctave(int octave, const QString& svgPath, QWidget* parent)
: QSvgWidget(svgPath, parent), m_octave(octave)
{
for (int i = 0; i < 7; ++i)
if (renderer()->elementExists(NaturalKeyNames[i]))
m_natural[i] = renderer()->matrixForElement(NaturalKeyNames[i]).
mapRect(renderer()->boundsOnElement(NaturalKeyNames[i]));
: QSvgWidget(svgPath, parent), m_octave(octave) {
for (size_t i = 0; i < NaturalKeyNames.size(); ++i) {
const auto& naturalKeyName = NaturalKeyNames[i];
for (int i = 0; i < 5; ++i)
if (renderer()->elementExists(SharpKeyNames[i]))
m_sharp[i] = renderer()->matrixForElement(SharpKeyNames[i]).
mapRect(renderer()->boundsOnElement(SharpKeyNames[i]));
/* The parent keyboard manages all mouse events */
setAttribute(Qt::WA_TransparentForMouseEvents);
}
int KeyboardOctave::getKey(const QPoint& localPos) const
{
QPointF localPoint = m_widgetToSvg.map(localPos);
for (int i = 0; i < 5; ++i)
if (m_sharp[i].contains(localPoint))
return SharpKeyNumbers[i];
for (int i = 0; i < 7; ++i)
if (m_natural[i].contains(localPoint))
return NaturalKeyNumbers[i];
return -1;
}
void KeyboardOctave::resizeEvent(QResizeEvent *event)
{
m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF());
}
KeyboardWidget::KeyboardWidget(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
for (int i = 0; i < 10; ++i)
{
m_widgets[i] = new KeyboardOctave(i, QStringLiteral(":/bg/keyboard.svg"), this);
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[i]);
if (renderer()->elementExists(naturalKeyName)) {
m_natural[i] = renderer()->transformForElement(naturalKeyName).mapRect(renderer()->boundsOnElement(naturalKeyName));
}
}
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[10]);
for (size_t i = 0; i < SharpKeyNames.size(); ++i) {
const auto& sharpKeyName = SharpKeyNames[i];
setLayout(layout);
setMouseTracking(true);
}
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const
{
for (KeyboardOctave* oct : m_widgets)
{
QPoint localPos = oct->mapFromParent(event->pos());
if (oct->rect().contains(localPos))
return {oct->getOctave(), oct->getKey(localPos)};
if (renderer()->elementExists(sharpKeyName)) {
m_sharp[i] = renderer()->transformForElement(sharpKeyName).mapRect(renderer()->boundsOnElement(sharpKeyName));
}
return {-1, -1};
}
/* The parent keyboard manages all mouse events */
setAttribute(Qt::WA_TransparentForMouseEvents);
}
void KeyboardWidget::_startKey(int octave, int key)
{
emit notePressed(octave * 12 + key);
}
int KeyboardOctave::getKey(const QPoint& localPos) const {
const QPointF localPoint = m_widgetToSvg.map(localPos);
void KeyboardWidget::_stopKey()
{
emit noteReleased();
}
void KeyboardWidget::_moveOnKey(int octave, int key)
{
if (m_lastOctave != octave || m_lastKey != key)
{
m_lastOctave = octave;
m_lastKey = key;
if (m_statusFocus)
m_statusFocus->setMessage(QStringLiteral("%1%2 (%3)").
arg(KeyStrings[key]).arg(octave - 1).arg(octave * 12 + key));
if (m_holding)
_startKey(octave, key);
for (size_t i = 0; i < m_sharp.size(); ++i) {
if (m_sharp[i].contains(localPoint)) {
return SharpKeyNumbers[i];
}
}
for (size_t i = 0; i < m_natural.size(); ++i) {
if (m_natural[i].contains(localPoint)) {
return NaturalKeyNumbers[i];
}
}
return -1;
}
void KeyboardWidget::_pressOnKey(int octave, int key)
{
_moveOnKey(octave, key);
m_holding = true;
void KeyboardOctave::resizeEvent(QResizeEvent* event) { m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF()); }
KeyboardWidget::KeyboardWidget(QWidget* parent) : QWidget(parent) {
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
for (size_t i = 0; i < m_widgets.size() - 1; ++i) {
m_widgets[i] = new KeyboardOctave(int(i), QStringLiteral(":/bg/keyboard.svg"), this);
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[i]);
}
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[10]);
setLayout(layout);
setMouseTracking(true);
}
KeyboardWidget::~KeyboardWidget() = default;
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const {
for (KeyboardOctave* oct : m_widgets) {
QPoint localPos = oct->mapFromParent(event->pos());
if (oct->rect().contains(localPos))
return {oct->getOctave(), oct->getKey(localPos)};
}
return {-1, -1};
}
void KeyboardWidget::_startKey(int octave, int key) { emit notePressed(octave * 12 + key); }
void KeyboardWidget::_stopKey() { emit noteReleased(); }
void KeyboardWidget::_moveOnKey(int octave, int key) {
if (m_lastOctave == octave && m_lastKey == key) {
return;
}
m_lastOctave = octave;
m_lastKey = key;
if (m_statusFocus) {
m_statusFocus->setMessage(QStringLiteral("%1%2 (%3)").arg(KeyStrings[key]).arg(octave - 1).arg(octave * 12 + key));
}
if (m_holding) {
_startKey(octave, key);
}
}
void KeyboardWidget::mouseMoveEvent(QMouseEvent* event)
{
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_moveOnKey(ok.first, ok.second);
void KeyboardWidget::_pressOnKey(int octave, int key) {
_moveOnKey(octave, key);
m_holding = true;
_startKey(octave, key);
}
void KeyboardWidget::mousePressEvent(QMouseEvent* event)
{
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_pressOnKey(ok.first, ok.second);
void KeyboardWidget::mouseMoveEvent(QMouseEvent* event) {
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_moveOnKey(ok.first, ok.second);
}
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event)
{
_stopKey();
m_holding = false;
void KeyboardWidget::mousePressEvent(QMouseEvent* event) {
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_pressOnKey(ok.first, ok.second);
}
void KeyboardWidget::enterEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->enter();
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event) {
_stopKey();
m_holding = false;
}
void KeyboardWidget::leaveEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->exit();
void KeyboardWidget::enterEvent(QEnterEvent* event) {
if (m_statusFocus)
m_statusFocus->enter();
}
void KeyboardWidget::wheelEvent(QWheelEvent* event)
{
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
{
/* Send wheel event directly to the scroll bar */
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
}
void KeyboardWidget::leaveEvent(QEvent* event) {
if (m_statusFocus)
m_statusFocus->exit();
}
void KeyboardWidget::showEvent(QShowEvent* event)
{
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
{
/* Scroll to C1 */
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
}
void KeyboardWidget::wheelEvent(QWheelEvent* event) {
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
/* Send wheel event directly to the scroll bar */
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
}
}
KeyboardSlider::KeyboardSlider(QWidget* parent)
: QSlider(parent)
{}
void KeyboardSlider::enterEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->enter();
void KeyboardWidget::showEvent(QShowEvent* event) {
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
/* Scroll to C1 */
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
}
}
void KeyboardSlider::leaveEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->exit();
KeyboardSlider::KeyboardSlider(QWidget* parent) : QSlider(parent) {}
KeyboardSlider::~KeyboardSlider() = default;
void KeyboardSlider::enterEvent(QEnterEvent* event) {
if (m_statusFocus)
m_statusFocus->enter();
}
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus)
{
m_statusFocus = statusFocus;
void KeyboardSlider::leaveEvent(QEvent* event) {
if (m_statusFocus)
m_statusFocus->exit();
}
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus) {
m_statusFocus = statusFocus;
QString str = stringOfValue(value());
m_statusFocus->setMessage(str);
setToolTip(str);
}
void KeyboardSlider::sliderChange(SliderChange change) {
QSlider::sliderChange(change);
if (m_statusFocus && change == QAbstractSlider::SliderValueChange) {
QString str = stringOfValue(value());
m_statusFocus->setMessage(str);
setToolTip(str);
}
}
void KeyboardSlider::sliderChange(SliderChange change)
{
QSlider::sliderChange(change);
if (m_statusFocus && change == QAbstractSlider::SliderValueChange)
{
QString str = stringOfValue(value());
m_statusFocus->setMessage(str);
setToolTip(str);
}
}
VelocitySlider::VelocitySlider(QWidget* parent)
: KeyboardSlider(parent)
{}
QString VelocitySlider::stringOfValue(int value) const
{
return tr("Velocity: %1").arg(value);
}
ModulationSlider::ModulationSlider(QWidget* parent)
: KeyboardSlider(parent)
{}
QString ModulationSlider::stringOfValue(int value) const
{
return tr("Modulation: %1").arg(value);
}
PitchSlider::PitchSlider(QWidget* parent)
: KeyboardSlider(parent)
{}
QString PitchSlider::stringOfValue(int value) const
{
return tr("Pitch: %1").arg(value / 2048.0, 0, 'g', 2);
}
void PitchSlider::mouseReleaseEvent(QMouseEvent *ev)
{
KeyboardSlider::mouseReleaseEvent(ev);
setValue(0);
VelocitySlider::VelocitySlider(QWidget* parent) : KeyboardSlider(parent) {}
QString VelocitySlider::stringOfValue(int value) const { return tr("Velocity: %1").arg(value); }
ModulationSlider::ModulationSlider(QWidget* parent) : KeyboardSlider(parent) {}
QString ModulationSlider::stringOfValue(int value) const { return tr("Modulation: %1").arg(value); }
PitchSlider::PitchSlider(QWidget* parent) : KeyboardSlider(parent) {}
QString PitchSlider::stringOfValue(int value) const { return tr("Pitch: %1").arg(value / 2048.0, 0, 'g', 2); }
void PitchSlider::mouseReleaseEvent(QMouseEvent* ev) {
KeyboardSlider::mouseReleaseEvent(ev);
setValue(0);
}

View File

@@ -1,106 +1,107 @@
#ifndef AMUSE_KEYBOARD_WIDGET_HPP
#define AMUSE_KEYBOARD_WIDGET_HPP
#pragma once
#include <array>
#include <QWidget>
#include <QSvgWidget>
#include <QSlider>
#include <QString>
#include <QSvgWidget>
#include <QWheelEvent>
#include "StatusBarWidget.hpp"
#include "Common.hpp"
#include <QWidget>
extern const QString NaturalKeyNames[7];
extern const QString SharpKeyNames[5];
extern const QString KeyStrings[12];
extern const int NaturalKeyNumbers[7];
extern const int SharpKeyNumbers[5];
extern const std::array<QString, 7> NaturalKeyNames;
extern const std::array<QString, 5> SharpKeyNames;
extern const std::array<QString, 12> KeyStrings;
extern const std::array<int, 7> NaturalKeyNumbers;
extern const std::array<int, 5> SharpKeyNumbers;
class KeyboardWidget;
class StatusBarFocus;
class KeyboardOctave : public QSvgWidget {
Q_OBJECT
int m_octave;
std::array<QRectF, 7> m_natural;
std::array<QRectF, 5> m_sharp;
QTransform m_widgetToSvg;
class KeyboardOctave : public QSvgWidget
{
Q_OBJECT
int m_octave;
QRectF m_natural[7];
QRectF m_sharp[5];
QTransform m_widgetToSvg;
public:
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
int getOctave() const { return m_octave; }
int getKey(const QPoint& localPos) const;
void resizeEvent(QResizeEvent *event);
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
int getOctave() const { return m_octave; }
int getKey(const QPoint& localPos) const;
void resizeEvent(QResizeEvent* event) override;
};
class KeyboardWidget : public QWidget
{
Q_OBJECT
KeyboardOctave* m_widgets[11];
StatusBarFocus* m_statusFocus = nullptr;
int m_lastOctave = -1;
int m_lastKey = -1;
bool m_holding = false;
class KeyboardWidget : public QWidget {
Q_OBJECT
std::array<KeyboardOctave*, 11> m_widgets{};
StatusBarFocus* m_statusFocus = nullptr;
int m_lastOctave = -1;
int m_lastKey = -1;
bool m_holding = false;
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
void _startKey(int octave, int key);
void _stopKey();
void _moveOnKey(int octave, int key);
void _pressOnKey(int octave, int key);
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
void _startKey(int octave, int key);
void _stopKey();
void _moveOnKey(int octave, int key);
void _pressOnKey(int octave, int key);
public:
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
~KeyboardWidget() override;
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void wheelEvent(QWheelEvent *event);
void showEvent(QShowEvent *event);
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
void showEvent(QShowEvent* event) override;
signals:
void notePressed(int key);
void noteReleased();
void notePressed(int key);
void noteReleased();
};
class KeyboardSlider : public QSlider
{
Q_OBJECT
class KeyboardSlider : public QSlider {
Q_OBJECT
protected:
StatusBarFocus* m_statusFocus = nullptr;
virtual QString stringOfValue(int value) const = 0;
StatusBarFocus* m_statusFocus = nullptr;
virtual QString stringOfValue(int value) const = 0;
public:
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void setStatusFocus(StatusBarFocus* statusFocus);
void sliderChange(SliderChange change);
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
~KeyboardSlider() override;
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
void setStatusFocus(StatusBarFocus* statusFocus);
void sliderChange(SliderChange change) override;
};
class VelocitySlider : public KeyboardSlider
{
Q_OBJECT
QString stringOfValue(int value) const;
public:
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
class VelocitySlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const override;
public:
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
};
class ModulationSlider : public KeyboardSlider
{
Q_OBJECT
QString stringOfValue(int value) const;
class ModulationSlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const override;
public:
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
};
class PitchSlider : public KeyboardSlider
{
Q_OBJECT
QString stringOfValue(int value) const;
public:
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent *ev);
void wheelEvent(QWheelEvent* ev) { ev->ignore(); }
};
class PitchSlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const override;
#endif //AMUSE_KEYBOARD_WIDGET_HPP
public:
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* ev) override;
void wheelEvent(QWheelEvent* ev) override { ev->ignore(); }
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,109 +1,122 @@
#ifndef AMUSE_KEYMAP_EDITOR_HPP
#define AMUSE_KEYMAP_EDITOR_HPP
#pragma once
#include <array>
#include <bitset>
#include <cstdint>
#include <unordered_map>
#include <QFrame>
#include <QPushButton>
#include <QStaticText>
#include <QSvgRenderer>
#include "EditorWidget.hpp"
#include <QFrame>
#include <QLabel>
#include <QStaticText>
#include <QSpinBox>
#include <QPushButton>
#include <QSvgRenderer>
#include <QScrollArea>
#include <QCheckBox>
#include <bitset>
#include "ProjectModel.hpp"
#include <amuse/AudioGroupPool.hpp>
#include <amuse/Common.hpp>
class KeymapEditor;
class QCheckBox;
class QScrollArea;
class QSpinBox;
class PaintButton : public QPushButton
{
Q_OBJECT
class PaintButton : public QPushButton {
Q_OBJECT
public:
explicit PaintButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
explicit PaintButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
void focusOutEvent(QFocusEvent* event) override { event->ignore(); }
void keyPressEvent(QKeyEvent* event) override { event->ignore(); }
};
class KeymapView : public QWidget
{
Q_OBJECT
friend class KeymapControls;
friend class KeymapEditor;
amuse::ObjToken<ProjectModel::KeymapNode> m_node;
QSvgRenderer m_octaveRenderer;
QSvgRenderer m_lastOctaveRenderer;
QRectF m_natural[7];
QRectF m_sharp[5];
QTransform m_widgetToSvg;
QFont m_keyFont;
QStaticText m_keyTexts[128];
int m_keyPalettes[128];
KeymapEditor* getEditor() const;
int getKey(const QPoint& localPos) const;
void drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth,
const QColor* keyPalette, int o, int k) const;
public:
explicit KeymapView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::KeymapNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
using PaintPalette = std::array<QColor, 129>;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
void wheelEvent(QWheelEvent* event);
class KeymapView : public QWidget {
Q_OBJECT
friend class KeymapControls;
friend class KeymapEditor;
amuse::ObjToken<ProjectModel::KeymapNode> m_node;
QSvgRenderer m_octaveRenderer;
QSvgRenderer m_lastOctaveRenderer;
std::array<QRectF, 7> m_natural;
std::array<QRectF, 5> m_sharp;
QTransform m_widgetToSvg;
QFont m_keyFont;
std::array<QStaticText, 128> m_keyTexts;
std::array<int, 128> m_keyPalettes;
KeymapEditor* getEditor() const;
int getKey(const QPoint& localPos) const;
void drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const PaintPalette& keyPalette, int o,
int k) const;
public:
explicit KeymapView(QWidget* parent = Q_NULLPTR);
~KeymapView() override;
void loadData(ProjectModel::KeymapNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev) override;
void mousePressEvent(QMouseEvent* ev) override;
void mouseMoveEvent(QMouseEvent* ev) override;
void wheelEvent(QWheelEvent* event) override;
};
class KeymapControls : public QFrame
{
Q_OBJECT
friend class KeymapView;
friend class KeymapEditor;
FieldProjectNode* m_macro;
QSpinBox* m_transpose;
QSpinBox* m_pan;
QCheckBox* m_surround;
QSpinBox* m_prioOffset;
PaintButton* m_paintButton;
bool m_enableUpdate = true;
KeymapEditor* getEditor() const;
void setPaintIdx(int idx);
void setKeymap(const amuse::Keymap& km);
class KeymapControls : public QFrame {
Q_OBJECT
friend class KeymapView;
friend class KeymapEditor;
FieldProjectNode* m_macro;
QSpinBox* m_transpose;
QSpinBox* m_pan;
QCheckBox* m_surround;
QSpinBox* m_prioOffset;
PaintButton* m_paintButton;
bool m_enableUpdate = true;
KeymapEditor* getEditor() const;
void setPaintIdx(int idx);
void setKeymap(const amuse::Keymap& km);
public:
explicit KeymapControls(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::KeymapNode* node);
void unloadData();
explicit KeymapControls(QWidget* parent = Q_NULLPTR);
~KeymapControls() override;
void loadData(ProjectModel::KeymapNode* node);
void unloadData();
public slots:
void controlChanged();
void paintButtonPressed();
void controlChanged();
void paintButtonPressed();
};
class KeymapEditor : public EditorWidget
{
Q_OBJECT
friend class KeymapView;
friend class KeymapControls;
QScrollArea* m_scrollArea;
KeymapView* m_kmView;
KeymapControls* m_controls;
QColor m_paintPalette[129];
amuse::Keymap m_controlKeymap;
std::unordered_map<uint64_t, std::pair<int, int>> m_configToIdx;
std::bitset<129> m_idxBitmap;
bool m_inPaint = false;
void _touch();
void touchKey(int key, bool bulk = false);
void touchControl(const amuse::Keymap& km);
int allocateConfigIdx(uint64_t key);
void deallocateConfigIdx(uint64_t key);
int getConfigIdx(uint64_t key) const;
class KeymapEditor : public EditorWidget {
Q_OBJECT
friend class KeymapView;
friend class KeymapControls;
QScrollArea* m_scrollArea;
KeymapView* m_kmView;
KeymapControls* m_controls;
PaintPalette m_paintPalette;
amuse::Keymap m_controlKeymap;
std::unordered_map<uint64_t, std::pair<int, int>> m_configToIdx;
std::bitset<129> m_idxBitmap;
bool m_inPaint = false;
void _touch();
void touchKey(int key, bool bulk = false);
void touchControl(const amuse::Keymap& km);
int allocateConfigIdx(uint64_t key);
void deallocateConfigIdx(uint64_t key);
int getConfigIdx(uint64_t key) const;
public:
explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::KeymapNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void keyPressEvent(QKeyEvent* event);
};
explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
~KeymapEditor() override;
#endif //AMUSE_KEYMAP_EDITOR_HPP
bool loadData(ProjectModel::KeymapNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
void keyPressEvent(QKeyEvent* event) override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,92 +1,103 @@
#ifndef AMUSE_LAYERS_EDITOR_HPP
#define AMUSE_LAYERS_EDITOR_HPP
#pragma once
#include <QAbstractTableModel>
#include <QAction>
#include <QStyledItemDelegate>
#include <QTableView>
#include "EditorWidget.hpp"
#include <QAbstractTableModel>
#include <QTableView>
#include <QAction>
#include <QToolButton>
#include <QStyledItemDelegate>
#include "ProjectModel.hpp"
class SoundMacroDelegate : public BaseObjectDelegate
{
Q_OBJECT
#include <amuse/AudioGroupPool.hpp>
#include <amuse/Common.hpp>
class SoundMacroDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override;
public:
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
~SoundMacroDelegate() override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
private slots:
void smIndexChanged();
void smIndexChanged();
};
class LayersModel : public QAbstractTableModel
{
Q_OBJECT
friend class LayersEditor;
friend class SoundMacroDelegate;
friend class LayersTableView;
amuse::ObjToken<ProjectModel::LayersNode> m_node;
class LayersModel : public QAbstractTableModel {
Q_OBJECT
friend class LayersEditor;
friend class SoundMacroDelegate;
friend class LayersTableView;
amuse::ObjToken<ProjectModel::LayersNode> m_node;
public:
explicit LayersModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::LayersNode* node);
void unloadData();
explicit LayersModel(QObject* parent = Q_NULLPTR);
~LayersModel() override;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
Qt::DropActions supportedDropActions() const;
Qt::DropActions supportedDragActions() const;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent);
void loadData(ProjectModel::LayersNode* node);
void unloadData();
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
const QModelIndex& destinationParent, int destinationChild);
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
const QModelIndex& parent) override;
void _insertRow(int row, const amuse::LayerMapping& data);
amuse::LayerMapping _removeRow(int row);
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent,
int destinationChild) override;
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
void _insertRow(int row, const amuse::LayerMapping& data);
amuse::LayerMapping _removeRow(int row);
};
class LayersTableView : public QTableView
{
Q_OBJECT
SoundMacroDelegate m_smDelegate;
RangedValueFactory<-128, 127> m_signedFactory;
RangedValueFactory<0, 127> m_unsignedFactory;
QStyledItemDelegate m_signedDelegate, m_unsignedDelegate;
class LayersTableView : public QTableView {
Q_OBJECT
SoundMacroDelegate m_smDelegate;
RangedValueFactory<-128, 127> m_signedFactory;
RangedValueFactory<0, 127> m_unsignedFactory;
QStyledItemDelegate m_signedDelegate, m_unsignedDelegate;
public:
explicit LayersTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
explicit LayersTableView(QWidget* parent = Q_NULLPTR);
~LayersTableView() override;
void setModel(QAbstractItemModel* model) override;
void deleteSelection();
};
class LayersEditor : public EditorWidget
{
Q_OBJECT
friend class LayersModel;
LayersModel m_model;
LayersTableView m_tableView;
AddRemoveButtons m_addRemoveButtons;
class LayersEditor : public EditorWidget {
Q_OBJECT
friend class LayersModel;
LayersModel m_model;
LayersTableView m_tableView;
AddRemoveButtons m_addRemoveButtons;
public:
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::LayersNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void resizeEvent(QResizeEvent* ev);
AmuseItemEditFlags itemEditFlags() const;
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
~LayersEditor() override;
bool loadData(ProjectModel::LayersNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
void resizeEvent(QResizeEvent* ev) override;
AmuseItemEditFlags itemEditFlags() const override;
private slots:
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void itemDeleteAction();
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void itemDeleteAction() override;
};
#endif //AMUSE_LAYERS_EDITOR_HPP

View File

@@ -1,140 +1,121 @@
#include "MIDIReader.hpp"
#include "MainWindow.hpp"
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock)
: amuse::BooBackendMIDIReader(engine, useLock) {}
#include <amuse/Engine.hpp>
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
return;
}
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock) : amuse::BooBackendMIDIReader(engine, useLock) {}
auto keySearch = m_chanVoxs.find(key);
if (keySearch == m_chanVoxs.cend())
return;
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
return;
}
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
auto keySearch = m_chanVoxs.find(key);
if (keySearch == m_chanVoxs.cend())
return;
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
}
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
return;
}
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
return;
}
if (m_lastVoice && m_lastVoice->isDestroyed())
m_lastVoice.reset();
if (m_lastVoice && m_lastVoice->isDestroyed())
m_lastVoice.reset();
/* If portamento is enabled for voice, pre-empt spawning new voices */
if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice)
{
uint8_t lastNote = lastVoice->getLastNote();
if (lastVoice->doPortamento(key))
{
m_chanVoxs.erase(lastNote);
m_chanVoxs[key] = lastVoice;
return;
}
/* If portamento is enabled for voice, pre-empt spawning new voices */
if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice) {
uint8_t lastNote = lastVoice->getLastNote();
if (lastVoice->doPortamento(key)) {
m_chanVoxs.erase(lastNote);
m_chanVoxs[key] = lastVoice;
return;
}
}
/* Ensure keyoff sent first */
auto keySearch = m_chanVoxs.find(key);
if (keySearch != m_chanVoxs.cend())
{
if (keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
keySearch->second->setPedal(false);
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
}
/* Ensure keyoff sent first */
auto keySearch = m_chanVoxs.find(key);
if (keySearch != m_chanVoxs.cend()) {
if (keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
keySearch->second->setPedal(false);
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
}
amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
if (newVox)
{
m_chanVoxs[key] = newVox;
m_lastVoice = newVox;
}
amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
if (newVox) {
m_chanVoxs[key] = newVox;
m_lastVoice = newVox;
}
}
void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
return;
}
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
return;
}
if (control == 1)
{
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
}
else if (control == 64)
{
g_MainWindow->setSustain(value >= 0x40);
}
else
{
for (auto& v : m_engine.getActiveVoices())
v->setCtrlValue(control, value);
}
g_MainWindow->m_ctrlVals[control] = value;
if (control == 1) {
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
} else if (control == 64) {
g_MainWindow->setSustain(value >= 0x40);
} else {
for (auto& v : m_engine.getActiveVoices())
v->setCtrlValue(control, value);
}
g_MainWindow->m_ctrlVals[control] = value;
}
void MIDIReader::programChange(uint8_t chan, uint8_t program)
{
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setChanProgram(chan, program);
void MIDIReader::programChange(uint8_t chan, uint8_t program) {
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setChanProgram(chan, program);
}
void MIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch)
{
float pWheel = (pitch - 0x2000) / float(0x2000);
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setPitchWheel(chan, pWheel);
else
g_MainWindow->m_ui.pitchSlider->setValue(int(pWheel * 2048.f));
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch) {
float pWheel = (pitch - 0x2000) / float(0x2000);
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setPitchWheel(chan, pWheel);
else
g_MainWindow->m_ui.pitchSlider->setValue(int(pWheel * 2048.f));
}
void MIDIReader::allSoundOff(uint8_t chan)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->kill();
return;
}
void MIDIReader::allSoundOff(uint8_t chan) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->kill();
return;
}
for (auto& v : m_engine.getActiveVoices())
v->kill();
for (auto& v : m_engine.getActiveVoices())
v->kill();
}
void MIDIReader::resetAllControllers(uint8_t /*chan*/) {}
void MIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
void MIDIReader::allNotesOff(uint8_t chan)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->kill();
return;
}
void MIDIReader::allNotesOff(uint8_t chan) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->kill();
return;
}
for (auto& v : m_engine.getActiveVoices())
v->kill();
for (auto& v : m_engine.getActiveVoices())
v->kill();
}
void MIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
@@ -159,11 +140,8 @@ void MIDIReader::stopSeq() {}
void MIDIReader::reset() {}
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine)
: amuse::BooBackendVoiceAllocator(booEngine) {}
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine) : amuse::BooBackendVoiceAllocator(booEngine) {}
std::unique_ptr<amuse::IMIDIReader>
VoiceAllocator::allocateMIDIReader(amuse::Engine& engine)
{
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
std::unique_ptr<amuse::IMIDIReader> VoiceAllocator::allocateMIDIReader(amuse::Engine& engine) {
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
}

View File

@@ -1,52 +1,56 @@
#ifndef AMUSE_MIDI_READER_HPP
#define AMUSE_MIDI_READER_HPP
#pragma once
#include "amuse/Voice.hpp"
#include "amuse/BooBackend.hpp"
#include "amuse/Common.hpp"
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <unordered_set>
class MIDIReader : public amuse::BooBackendMIDIReader
{
std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
amuse::ObjToken<amuse::Voice> m_lastVoice;
#include <amuse/BooBackend.hpp>
#include <amuse/Common.hpp>
#include <amuse/Voice.hpp>
namespace amuse {
class Engine;
}
class MIDIReader : public amuse::BooBackendMIDIReader {
std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
amuse::ObjToken<amuse::Voice> m_lastVoice;
public:
MIDIReader(amuse::Engine& engine, bool useLock);
MIDIReader(amuse::Engine& engine, bool useLock);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
void programChange(uint8_t chan, uint8_t program);
void channelPressure(uint8_t chan, uint8_t pressure);
void pitchBend(uint8_t chan, int16_t pitch);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity) override;
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity) override;
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure) override;
void controlChange(uint8_t chan, uint8_t control, uint8_t value) override;
void programChange(uint8_t chan, uint8_t program) override;
void channelPressure(uint8_t chan, uint8_t pressure) override;
void pitchBend(uint8_t chan, int16_t pitch) override;
void allSoundOff(uint8_t chan);
void resetAllControllers(uint8_t chan);
void localControl(uint8_t chan, bool on);
void allNotesOff(uint8_t chan);
void omniMode(uint8_t chan, bool on);
void polyMode(uint8_t chan, bool on);
void allSoundOff(uint8_t chan) override;
void resetAllControllers(uint8_t chan) override;
void localControl(uint8_t chan, bool on) override;
void allNotesOff(uint8_t chan) override;
void omniMode(uint8_t chan, bool on) override;
void polyMode(uint8_t chan, bool on) override;
void sysex(const void* data, size_t len);
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
void songPositionPointer(uint16_t pointer);
void songSelect(uint8_t song);
void tuneRequest();
void sysex(const void* data, size_t len) override;
void timeCodeQuarterFrame(uint8_t message, uint8_t value) override;
void songPositionPointer(uint16_t pointer) override;
void songSelect(uint8_t song) override;
void tuneRequest() override;
void startSeq();
void continueSeq();
void stopSeq();
void startSeq() override;
void continueSeq() override;
void stopSeq() override;
void reset();
void reset() override;
};
class VoiceAllocator : public amuse::BooBackendVoiceAllocator
{
class VoiceAllocator : public amuse::BooBackendVoiceAllocator {
public:
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine);
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine) override;
};
#endif // AMUSE_MIDI_READER_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -1,309 +1,313 @@
#ifndef AMUSE_MAINWINDOW_HPP
#define AMUSE_MAINWINDOW_HPP
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <QMainWindow>
#include <QUndoStack>
#include <QProgressDialog>
#include <QThread>
#include <QStyledItemDelegate>
#include <QSortFilterProxyModel>
#include <QLinkedList>
#include <QFileDialog>
#include "ui_MainWindow.h"
#include "amuse/Engine.hpp"
#include "amuse/BooBackend.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "ProjectModel.hpp"
#include <QMainWindow>
#include <QMessageBox>
#include <QStyledItemDelegate>
#include <QThread>
#include "Common.hpp"
#include "EditorWidget.hpp"
#include "MIDIReader.hpp"
#include "StudioSetupWidget.hpp"
#include "ProjectModel.hpp"
#include "ui_MainWindow.h"
#include <amuse/Common.hpp>
#include <amuse/Sequencer.hpp>
#include <amuse/Voice.hpp>
#define MaxRecentFiles 4
namespace amuse {
class Engine;
}
namespace boo2 {
struct IAudioVoiceEngine;
}
namespace Ui {
class MainWindow;
}
class MainWindow;
class SongGroupEditor;
class SoundGroupEditor;
class SoundMacroEditor;
class ADSREditor;
class CurveEditor;
class KeymapEditor;
class LayersEditor;
class MainWindow;
class SampleEditor;
class SongGroupEditor;
class SoundGroupEditor;
class SoundMacroEditor;
class StudioSetupWidget;
class VoiceAllocator;
enum BackgroundTaskId
{
TaskOpen,
TaskImport,
TaskExport,
TaskReloadSamples
};
class QProgressDialog;
class QUndoStack;
enum BackgroundTaskId { TaskOpen, TaskImport, TaskExport, TaskReloadSamples };
class BackgroundTask : public QObject {
Q_OBJECT
int m_id;
std::function<void(BackgroundTask&)> m_task;
UIMessenger m_threadMessenger;
bool m_cancelled = false;
class BackgroundTask : public QObject
{
Q_OBJECT
int m_id;
std::function<void(BackgroundTask&)> m_task;
UIMessenger m_threadMessenger;
bool m_cancelled = false;
public:
explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
: m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
bool isCanceled() const { QCoreApplication::processEvents(); return m_cancelled; }
UIMessenger& uiMessenger() { return m_threadMessenger; }
explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
: m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
bool isCanceled() const {
QCoreApplication::processEvents();
return m_cancelled;
}
UIMessenger& uiMessenger() { return m_threadMessenger; }
signals:
void setMinimum(int minimum);
void setMaximum(int maximum);
void setValue(int value);
void setLabelText(const QString& text);
void finished(int id);
void setMinimum(int minimum);
void setMaximum(int maximum);
void setValue(int value);
void setLabelText(const QString& text);
void finished(int id);
public slots:
void run() { m_task(*this); emit finished(m_id); }
void cancel() { m_cancelled = true; }
void run() {
m_task(*this);
emit finished(m_id);
}
void cancel() { m_cancelled = true; }
};
class TreeDelegate : public QStyledItemDelegate
{
Q_OBJECT
MainWindow& m_window;
public:
explicit TreeDelegate(MainWindow& window, QObject* parent = Q_NULLPTR)
: QStyledItemDelegate(parent), m_window(window) {}
bool editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index);
public slots:
void doExportGroup();
void doFindUsages();
void doCut();
void doCopy();
void doPaste();
void doDuplicate();
void doDelete();
void doRename();
};
class MainWindow : public QMainWindow
{
friend class MIDIReader;
friend class ProjectModel;
friend class GroupNodeUndoCommand;
friend class TreeDelegate;
Q_OBJECT
Ui::MainWindow m_ui;
QAction* m_goBack;
QAction* m_goForward;
QLinkedList<ProjectModel::INode*> m_navList;
QLinkedList<ProjectModel::INode*>::iterator m_navIt;
QAction* m_clearRecentFileAct;
QAction* m_recentFileActs[MaxRecentFiles];
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
QWidget* m_faceSvg;
SongGroupEditor* m_songGroupEditor = nullptr;
SoundGroupEditor* m_soundGroupEditor = nullptr;
SoundMacroEditor* m_soundMacroEditor = nullptr;
ADSREditor* m_adsrEditor = nullptr;
CurveEditor* m_curveEditor = nullptr;
KeymapEditor* m_keymapEditor = nullptr;
LayersEditor* m_layersEditor = nullptr;
SampleEditor* m_sampleEditor = nullptr;
StudioSetupWidget* m_studioSetup = nullptr;
QFileDialog m_openDirectoryDialog;
QFileDialog m_openFileDialog;
QFileDialog m_newFileDialog;
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<VoiceAllocator> m_voxAllocator;
std::unique_ptr<amuse::Engine> m_engine;
amuse::ObjToken<amuse::Voice> m_lastSound;
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
int m_velocity = 90;
float m_pitch = 0.f;
int8_t m_ctrlVals[128] = {};
float m_auxAVol = 0.f;
float m_auxBVol = 0.f;
bool m_uiDisabled = false;
bool m_clipboardAmuseData = false;
QUndoStack* m_undoStack;
QMetaObject::Connection m_cutConn;
QMetaObject::Connection m_copyConn;
QMetaObject::Connection m_pasteConn;
QMetaObject::Connection m_deleteConn;
QMetaObject::Connection m_canEditConn;
BackgroundTask* m_backgroundTask = nullptr;
QProgressDialog* m_backgroundDialog = nullptr;
QThread m_backgroundThread;
uint64_t m_timerFireCount = 0;
int m_peakVoices = 0;
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
void updateWindowTitle();
void updateRecentFileActions();
void updateNavigationButtons();
bool setProjectPath(const QString& path);
void refreshAudioIO();
void refreshMIDIIO();
void timerEvent(QTimerEvent* ev);
void setSustain(bool sustain);
void keyPressEvent(QKeyEvent* ev);
void keyReleaseEvent(QKeyEvent* ev);
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task);
bool _setEditor(EditorWidget* widget, bool appendNav = true);
class TreeDelegate : public QStyledItemDelegate {
Q_OBJECT
MainWindow& m_window;
public:
explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow();
bool openProject(const QString& path);
bool openEditor(ProjectModel::SongGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundMacroNode* node, bool appendNav = true);
bool openEditor(ProjectModel::ADSRNode* node, bool appendNav = true);
bool openEditor(ProjectModel::CurveNode* node, bool appendNav = true);
bool openEditor(ProjectModel::KeymapNode* node, bool appendNav = true);
bool openEditor(ProjectModel::LayersNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SampleNode* node, bool appendNav = true);
bool openEditor(ProjectModel::INode* node, bool appendNav = true);
void closeEditor();
ProjectModel::INode* getEditorNode() const;
EditorWidget* getEditorWidget() const;
amuse::ObjToken<amuse::Voice> startEditorVoice(uint8_t key, uint8_t vel);
amuse::ObjToken<amuse::Voice> startSFX(amuse::GroupId groupId, amuse::SFXId sfxId);
amuse::ObjToken<amuse::Sequencer> startSong(amuse::GroupId groupId, amuse::SongId songId,
const unsigned char* arrData);
void pushUndoCommand(EditorUndoCommand* cmd);
void updateFocus();
void aboutToDeleteNode(ProjectModel::INode* node);
bool askAboutSave();
void closeEvent(QCloseEvent* ev);
void showEvent(QShowEvent* ev);
QString getGroupName(ProjectModel::GroupNode* group) const;
ProjectModel::GroupNode* getSelectedGroupNode() const;
QString getSelectedGroupName() const;
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
ProjectModel* projectModel() const { return m_projectModel; }
UIMessenger& uiMessenger() { return m_mainMessenger; }
void setItemEditFlags(AmuseItemEditFlags flags);
void setItemNewEnabled(bool enabled);
AmuseItemEditFlags outlineEditFlags();
bool isUiDisabled() const { return m_uiDisabled; }
void findUsages(ProjectModel::INode* node);
explicit TreeDelegate(MainWindow& window, QObject* parent = Q_NULLPTR)
: QStyledItemDelegate(parent), m_window(window) {}
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index) override;
public slots:
void newAction();
void _newAction(const QString& path);
void openAction();
void _openAction(const QString& path);
void openRecentFileAction();
void clearRecentFilesAction();
void saveAction();
void revertAction();
void reloadSampleDataAction();
void importAction();
void _importAction(const QString& path);
void importSongsAction();
void _importSongsAction(const QString& path);
void exportAction();
void importHeadersAction();
void _importHeadersAction(const QString& path);
void exportHeadersAction();
void _exportHeadersAction(const QString& path);
void newSubprojectAction();
void newSFXGroupAction();
void newSongGroupAction();
void newSoundMacroAction();
void newADSRAction();
void newCurveAction();
void newKeymapAction();
void newLayersAction();
void goForward();
void goBack();
void aboutToShowAudioIOMenu();
void aboutToShowMIDIIOMenu();
void setAudioIO();
void setMIDIIO(bool checked);
void aboutAmuseAction();
void aboutQtAction();
void notePressed(int key);
void noteReleased();
void velocityChanged(int vel);
void modulationChanged(int mod);
void pitchChanged(int pitch);
void killSounds();
void fxPressed();
void volumeChanged(int vol);
void auxAChanged(int vol);
void auxBChanged(int vol);
void outlineCutAction();
void outlineCopyAction();
void outlinePasteAction();
void outlineDeleteAction();
void onFocusChanged(QWidget* old, QWidget* now);
void onClipboardChanged();
void outlineItemActivated(const QModelIndex& index);
void onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void onTextSelect();
void onTextDelete();
void cleanChanged(bool clean);
void studioSetupHidden();
void studioSetupShown();
void onBackgroundTaskFinished(int id);
QMessageBox::StandardButton msgInformation(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
int msgQuestion(const QString &title,
const QString& text,
const QString& button0Text,
const QString& button1Text = QString(),
const QString& button2Text = QString(),
int defaultButtonNumber = 0,
int escapeButtonNumber = -1);
QMessageBox::StandardButton msgQuestion(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons =
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgWarning(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgCritical(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
void doExportGroup();
void doFindUsages();
void doCut();
void doCopy();
void doPaste();
void doDuplicate();
void doDelete();
void doRename();
};
class MainWindow : public QMainWindow {
friend class MIDIReader;
friend class ProjectModel;
friend class GroupNodeUndoCommand;
friend class TreeDelegate;
Q_OBJECT
Ui::MainWindow m_ui;
QAction* m_goBack;
QAction* m_goForward;
std::list<ProjectModel::INode*> m_navList;
std::list<ProjectModel::INode*>::iterator m_navIt;
QAction* m_clearRecentFileAct;
QAction* m_recentFileActs[MaxRecentFiles];
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
QWidget* m_faceSvg;
SongGroupEditor* m_songGroupEditor = nullptr;
SoundGroupEditor* m_soundGroupEditor = nullptr;
SoundMacroEditor* m_soundMacroEditor = nullptr;
ADSREditor* m_adsrEditor = nullptr;
CurveEditor* m_curveEditor = nullptr;
KeymapEditor* m_keymapEditor = nullptr;
LayersEditor* m_layersEditor = nullptr;
SampleEditor* m_sampleEditor = nullptr;
StudioSetupWidget* m_studioSetup = nullptr;
QFileDialog m_openDirectoryDialog;
QFileDialog m_openFileDialog;
QFileDialog m_newFileDialog;
#endif //AMUSE_MAINWINDOW_HPP
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<VoiceAllocator> m_voxAllocator;
std::unique_ptr<amuse::Engine> m_engine;
amuse::ObjToken<amuse::Voice> m_lastSound;
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
int m_velocity = 90;
float m_pitch = 0.f;
int8_t m_ctrlVals[128] = {};
float m_auxAVol = 0.f;
float m_auxBVol = 0.f;
bool m_uiDisabled = false;
bool m_clipboardAmuseData = false;
QUndoStack* m_undoStack;
QMetaObject::Connection m_cutConn;
QMetaObject::Connection m_copyConn;
QMetaObject::Connection m_pasteConn;
QMetaObject::Connection m_deleteConn;
QMetaObject::Connection m_canEditConn;
BackgroundTask* m_backgroundTask = nullptr;
QProgressDialog* m_backgroundDialog = nullptr;
QThread m_backgroundThread;
uint64_t m_timerFireCount = 0;
int m_peakVoices = 0;
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
void updateWindowTitle();
void updateRecentFileActions();
void updateNavigationButtons();
bool setProjectPath(const QString& path);
void refreshAudioIO();
void refreshMIDIIO();
void timerEvent(QTimerEvent* ev) override;
void setSustain(bool sustain);
void keyPressEvent(QKeyEvent* ev) override;
void keyReleaseEvent(QKeyEvent* ev) override;
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task);
bool _setEditor(EditorWidget* widget, bool appendNav = true);
public:
explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow() override;
bool openProject(const QString& path);
bool openEditor(ProjectModel::SongGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundMacroNode* node, bool appendNav = true);
bool openEditor(ProjectModel::ADSRNode* node, bool appendNav = true);
bool openEditor(ProjectModel::CurveNode* node, bool appendNav = true);
bool openEditor(ProjectModel::KeymapNode* node, bool appendNav = true);
bool openEditor(ProjectModel::LayersNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SampleNode* node, bool appendNav = true);
bool openEditor(ProjectModel::INode* node, bool appendNav = true);
void closeEditor();
ProjectModel::INode* getEditorNode() const;
EditorWidget* getEditorWidget() const;
amuse::ObjToken<amuse::Voice> startEditorVoice(uint8_t key, uint8_t vel);
amuse::ObjToken<amuse::Voice> startSFX(amuse::GroupId groupId, amuse::SFXId sfxId);
amuse::ObjToken<amuse::Sequencer> startSong(amuse::GroupId groupId, amuse::SongId songId,
const unsigned char* arrData);
void pushUndoCommand(EditorUndoCommand* cmd);
void updateFocus();
void aboutToDeleteNode(ProjectModel::INode* node);
bool askAboutSave();
void closeEvent(QCloseEvent* ev) override;
void showEvent(QShowEvent* ev) override;
QString getGroupName(ProjectModel::GroupNode* group) const;
ProjectModel::GroupNode* getSelectedGroupNode() const;
QString getSelectedGroupName() const;
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
ProjectModel* projectModel() const { return m_projectModel; }
UIMessenger& uiMessenger() { return m_mainMessenger; }
void setItemEditFlags(AmuseItemEditFlags flags);
void setItemNewEnabled(bool enabled);
AmuseItemEditFlags outlineEditFlags();
bool isUiDisabled() const { return m_uiDisabled; }
void findUsages(ProjectModel::INode* node);
public slots:
void newAction();
void _newAction(const QString& path);
void openAction();
void _openAction(const QString& path);
void openRecentFileAction();
void clearRecentFilesAction();
void saveAction();
void revertAction();
void reloadSampleDataAction();
void importAction();
void _importAction(const QString& path);
void importSongsAction();
void _importSongsAction(const QString& path);
void exportAction();
void importHeadersAction();
void _importHeadersAction(const QString& path);
void exportHeadersAction();
void _exportHeadersAction(const QString& path);
void newSubprojectAction();
void newSFXGroupAction();
void newSongGroupAction();
void newSoundMacroAction();
void newADSRAction();
void newCurveAction();
void newKeymapAction();
void newLayersAction();
void goForward();
void goBack();
void aboutToShowAudioIOMenu();
void aboutToShowMIDIIOMenu();
void setAudioIO();
void setMIDIIO(bool checked);
void aboutAmuseAction();
void aboutQtAction();
void notePressed(int key);
void noteReleased();
void velocityChanged(int vel);
void modulationChanged(int mod);
void pitchChanged(int pitch);
void killSounds();
void fxPressed();
void volumeChanged(int vol);
void auxAChanged(int vol);
void auxBChanged(int vol);
void outlineCutAction();
void outlineCopyAction();
void outlinePasteAction();
void outlineDeleteAction();
void onFocusChanged(QWidget* old, QWidget* now);
void onClipboardChanged();
void outlineItemActivated(const QModelIndex& index);
void onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void onTextSelect();
void onTextDelete();
void cleanChanged(bool clean);
void studioSetupHidden();
void studioSetupShown();
void onBackgroundTaskFinished(int id);
QMessageBox::StandardButton msgInformation(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
int msgQuestion(const QString& title, const QString& text, const QString& button0Text,
const QString& button1Text = QString(), const QString& button2Text = QString(),
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
QMessageBox::StandardButton
msgQuestion(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgWarning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgCritical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
};

View File

@@ -3,285 +3,157 @@
#include <QLabel>
#include "amuse/Common.hpp"
static const uint32_t BasicMacro[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07010001, 0x0000FFFF,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t BasicMacro[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF,
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t Looped[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000001, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t Looped[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x38000000,
0xE8030000, 0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopRelease[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001FA00,
0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F500000, 0x00013200,
0x07000000, 0x00003200,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000000, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopRelease[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001FA00, 0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000, 0x0F500000, 0x00013200, 0x07000000, 0x00003200, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopSoftRelease[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00016400,
0x07000000, 0x00006400,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000000, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopSoftRelease[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopSoftReleaseNoClick[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x00010100,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00016400,
0x07000000, 0x00006400,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000000, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopSoftReleaseNoClick[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000,
0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopADSR[] =
{
0x38000000, 0xC07A1000,
0x0C000000, 0x00000000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x12000000, 0x00000000,
0x07000001, 0x0000E803,
0x38000000, 0xE8030000,
0x07000001, 0x0000E803,
0x00000000, 0x00000000,
static const uint32_t LoopADSR[] = {
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x12000000, 0x00000000, 0x07000001, 0x0000E803,
0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 0x00000000,
};
static const uint32_t LoopADSRSoftRelease[] =
{
0x38000000, 0xC07A1000,
0x0C000000, 0x00000000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00017800,
0x07000001, 0x00007800,
0x12000000, 0x00000000,
0x38000000, 0xE8030000,
0x07000001, 0x0000E803,
0x00000000, 0x00000000,
static const uint32_t LoopADSRSoftRelease[] = {
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00017800, 0x07000001, 0x00007800,
0x12000000, 0x00000000, 0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 0x00000000,
};
static const uint32_t LoopHold[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00016400,
0x07000001, 0x00006400,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000001, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopHold[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000001, 0x00006400, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShot[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShot[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000001,
0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShotFixedNote[] =
{
0x38000000, 0xC07A1000,
0x193C0000, 0x00010000,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShotFixedNote[] = {
0x38000000, 0xC07A1000, 0x193C0000, 0x00010000, 0x10000000, 0x00000000,
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShotNoClick[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x00010100,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShotNoClick[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000,
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShotFixedNoClick[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x00010100,
0x193C0000, 0x00010000,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShotFixedNoClick[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x193C0000, 0x00010000, 0x10000000,
0x00000000, 0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t Bubbles[] =
{
0x38000000, 0xC07A1000,
0x0D600000, 0x00010000,
0x10000000, 0x00000000,
0x1D08E803, 0x00010000,
0x1E0544FD, 0x00010000,
0x0F000000, 0x0001F401,
0x07000000, 0x0000F401,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t Bubbles[] = {
0x38000000, 0xC07A1000, 0x0D600000, 0x00010000, 0x10000000, 0x00000000, 0x1D08E803,
0x00010000, 0x1E0544FD, 0x00010000, 0x0F000000, 0x0001F401, 0x07000000, 0x0000F401,
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t DownTrigger[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07000000, 0x00001200,
0x11000000, 0x00000000,
0x07000000, 0x00000100,
0x18FE0000, 0x00010000,
0x05000000, 0x01000C00,
0x0F000000, 0x00016400,
0x07000001, 0x00006400,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t DownTrigger[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000000, 0x00001200, 0x11000000, 0x00000000,
0x07000000, 0x00000100, 0x18FE0000, 0x00010000, 0x05000000, 0x01000C00, 0x0F000000, 0x00016400,
0x07000001, 0x00006400, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LongFadeInAndStop[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x0001E803,
0x10000000, 0x00000000,
0x07000001, 0x0000E803,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LongFadeInAndStop[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x0001E803, 0x10000000, 0x00000000, 0x07000001,
0x0000E803, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t FadeInAndStop[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x0001C800,
0x10000000, 0x00000000,
0x07000001, 0x0000C800,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t FadeInAndStop[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x0001C800, 0x10000000, 0x00000000, 0x07000001,
0x0000C800, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t RandomTrigger[] =
{
0x38000000, 0xC07A1000,
0x0F000000, 0x0001F401,
0x195A0000, 0x00010000,
0x10000000, 0x00000000,
0x1750005A, 0x01000000,
0x07000001, 0x00002300,
0x05000000, 0x03001400,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t RandomTrigger[] = {
0x38000000, 0xC07A1000, 0x0F000000, 0x0001F401, 0x195A0000, 0x00010000, 0x10000000,
0x00000000, 0x1750005A, 0x01000000, 0x07000001, 0x00002300, 0x05000000, 0x03001400,
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t SimplePlaySample[] =
{
0x10000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t SimplePlaySample[] = {
0x10000000,
0x00000000,
0x00000000,
0x00000000,
};
static const SoundMacroTemplateEntry Entries[] =
{
static const SoundMacroTemplateEntry Entries[] = {
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Basic Macro"), sizeof(BasicMacro), BasicMacro},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Looped"), sizeof(Looped), Looped},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Release"), sizeof(LoopRelease), LoopRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release"), sizeof(LoopSoftRelease), LoopSoftRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release No Click"), sizeof(LoopSoftReleaseNoClick), LoopSoftReleaseNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release No Click"), sizeof(LoopSoftReleaseNoClick),
LoopSoftReleaseNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR"), sizeof(LoopADSR), LoopADSR},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR Soft Release"), sizeof(LoopADSRSoftRelease), LoopADSRSoftRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR Soft Release"), sizeof(LoopADSRSoftRelease),
LoopADSRSoftRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Hold"), sizeof(LoopHold), LoopHold},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot"), sizeof(OneShot), OneShot},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed Note"), sizeof(OneShotFixedNote), OneShotFixedNote},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot No Click"), sizeof(OneShotNoClick), OneShotNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed No Click"), sizeof(OneShotFixedNoClick), OneShotFixedNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed No Click"), sizeof(OneShotFixedNoClick),
OneShotFixedNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Bubbles"), sizeof(Bubbles), Bubbles},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Down Trigger"), sizeof(DownTrigger), DownTrigger},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Long Fade in and Stop"), sizeof(LongFadeInAndStop), LongFadeInAndStop},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Fade in and Stop"), sizeof(FadeInAndStop), FadeInAndStop},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Random Trigger"), sizeof(RandomTrigger), RandomTrigger},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Simple Play Sample"), sizeof(SimplePlaySample), SimplePlaySample}
};
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Simple Play Sample"), sizeof(SimplePlaySample), SimplePlaySample}};
const SoundMacroTemplateEntry* NewSoundMacroDialog::getSelectedTemplate() const
{
return &Entries[m_combo.currentIndex()];
const SoundMacroTemplateEntry* NewSoundMacroDialog::getSelectedTemplate() const {
return &Entries[m_combo.currentIndex()];
}
NewSoundMacroDialog::NewSoundMacroDialog(const QString& groupName, QWidget* parent)
: QDialog(parent),
m_le(QString::fromStdString(amuse::SoundMacroId::CurNameDB->generateDefaultName(amuse::NameDB::Type::SoundMacro))),
m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal)
{
setWindowTitle(tr("New Sound Macro"));
: QDialog(parent)
, m_le(QString::fromStdString(amuse::SoundMacroId::CurNameDB->generateDefaultName(amuse::NameDB::Type::SoundMacro)))
, m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal) {
setWindowTitle(tr("New Sound Macro"));
int idx = 0;
for (const auto& ent : Entries)
m_combo.addItem(tr(ent.m_name), idx++);
m_combo.setCurrentIndex(0);
int idx = 0;
for (const auto& ent : Entries)
m_combo.addItem(tr(ent.m_name), idx++);
m_combo.setCurrentIndex(0);
QObject::connect(&m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
QObject::connect(&m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(&m_buttonBox, &QDialogButtonBox::accepted, this, &NewSoundMacroDialog::accept);
connect(&m_buttonBox, &QDialogButtonBox::rejected, this, &NewSoundMacroDialog::reject);
QVBoxLayout* layout = new QVBoxLayout;
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(new QLabel(tr("What should the new macro in %1 be named?").arg(groupName)));
layout->addWidget(&m_le);
layout->addWidget(new QLabel(tr("Sound Macro Template")));
layout->addWidget(&m_combo);
layout->addWidget(&m_buttonBox);
layout->addWidget(new QLabel(tr("What should the new macro in %1 be named?").arg(groupName)));
layout->addWidget(&m_le);
layout->addWidget(new QLabel(tr("Sound Macro Template")));
layout->addWidget(&m_combo);
layout->addWidget(&m_buttonBox);
setLayout(layout);
setLayout(layout);
}
NewSoundMacroDialog::~NewSoundMacroDialog() = default;

View File

@@ -1,28 +1,29 @@
#ifndef AMUSE_NEW_SOUND_MACRO_DIALOG_HPP
#define AMUSE_NEW_SOUND_MACRO_DIALOG_HPP
#pragma once
#include <cstddef>
#include <cstdint>
#include <QDialog>
#include <QLineEdit>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
struct SoundMacroTemplateEntry
{
const char* m_name;
size_t m_length;
const uint32_t* m_data;
struct SoundMacroTemplateEntry {
const char* m_name;
size_t m_length;
const uint32_t* m_data;
};
class NewSoundMacroDialog : public QDialog
{
Q_OBJECT
QLineEdit m_le;
QComboBox m_combo;
QDialogButtonBox m_buttonBox;
class NewSoundMacroDialog : public QDialog {
Q_OBJECT
QLineEdit m_le;
QComboBox m_combo;
QDialogButtonBox m_buttonBox;
public:
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
QString getName() const { return m_le.text(); }
const SoundMacroTemplateEntry* getSelectedTemplate() const;
};
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
~NewSoundMacroDialog() override;
#endif // AMUSE_NEW_SOUND_MACRO_DIALOG_HPP
QString getName() const { return m_le.text(); }
const SoundMacroTemplateEntry* getSelectedTemplate() const;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,516 +1,486 @@
#ifndef AMUSE_PROJECT_MODEL_HPP
#define AMUSE_PROJECT_MODEL_HPP
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <QAbstractItemModel>
#include <QIdentityProxyModel>
#include <QSortFilterProxyModel>
#include <QDir>
#include <QIcon>
#include <map>
#include <QIdentityProxyModel>
#include <QSortFilterProxyModel>
#include "Common.hpp"
#include "NewSoundMacroDialog.hpp"
#include "amuse/AudioGroup.hpp"
#include "amuse/AudioGroupData.hpp"
#include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupPool.hpp"
#include "amuse/AudioGroupSampleDirectory.hpp"
class ProjectModel;
#include <amuse/AudioGroup.hpp>
#include <amuse/AudioGroupData.hpp>
#include <amuse/AudioGroupProject.hpp>
#include <amuse/AudioGroupPool.hpp>
#include <amuse/AudioGroupSampleDirectory.hpp>
#include <amuse/Common.hpp>
class EditorUndoCommand;
class ProjectModel;
enum AmuseItemEditFlags
{
AmuseItemNone = 0,
AmuseItemCut = 1,
AmuseItemCopy = 2,
AmuseItemPaste = 4,
AmuseItemDelete = 8,
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
struct SoundMacroTemplateEntry;
enum AmuseItemEditFlags {
AmuseItemNone = 0,
AmuseItemCut = 1,
AmuseItemCopy = 2,
AmuseItemPaste = 4,
AmuseItemDelete = 8,
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
};
class OutlineFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
QRegExp m_usageKey;
class OutlineFilterProxyModel : public QSortFilterProxyModel {
Q_OBJECT
QRegularExpression m_usageKey;
public:
explicit OutlineFilterProxyModel(ProjectModel* source);
explicit OutlineFilterProxyModel(ProjectModel* source);
public slots:
void setFilterRegExp(const QString &pattern);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
void setFilterRegExp(const QString& pattern);
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
};
class NullItemProxyModel : public QIdentityProxyModel
{
Q_OBJECT
class NullItemProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit NullItemProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
explicit NullItemProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
int rowCount(const QModelIndex& parent) const override;
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
QVariant data(const QModelIndex& proxyIndex, int role) const override;
};
class PageObjectProxyModel : public QIdentityProxyModel
{
Q_OBJECT
class PageObjectProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit PageObjectProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const;
explicit PageObjectProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
QModelIndex parent(const QModelIndex& child) const override;
int rowCount(const QModelIndex& parent) const override;
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
QVariant data(const QModelIndex& proxyIndex, int role) const override;
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const override;
};
class ProjectModel : public QAbstractItemModel
{
Q_OBJECT
class ProjectModel : public QAbstractItemModel {
Q_OBJECT
public:
enum class ImportMode
{
Original,
WAVs,
Both
};
enum class ImportMode { Original, WAVs, Both };
struct NameUndoRegistry
{
std::unordered_map<amuse::SongId, std::string> m_songIDs;
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
void registerSongName(amuse::SongId id) const;
void unregisterSongName(amuse::SongId id);
void registerSFXName(amuse::SongId id) const;
void unregisterSFXName(amuse::SongId id);
void clear()
{
m_songIDs.clear();
m_sfxIDs.clear();
}
};
struct NameUndoRegistry {
std::unordered_map<amuse::SongId, std::string> m_songIDs;
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
void registerSongName(amuse::SongId id) const;
void unregisterSongName(amuse::SongId id);
void registerSFXName(amuse::SongId id) const;
void unregisterSFXName(amuse::SongId id);
void clear() {
m_songIDs.clear();
m_sfxIDs.clear();
}
};
private:
QDir m_dir;
OutlineFilterProxyModel m_outlineProxy;
NullItemProxyModel m_nullProxy;
PageObjectProxyModel m_pageObjectProxy;
QDir m_dir;
amuse::ProjectDatabase m_projectDatabase;
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>> m_groups;
amuse::ProjectDatabase m_projectDatabase;
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>> m_groups;
struct Song
{
QString m_path;
int m_refCount = 0;
};
std::unordered_map<amuse::SongId, Song> m_midiFiles;
struct Song {
QString m_path;
int m_refCount = 0;
};
std::unordered_map<amuse::SongId, Song> m_midiFiles;
public:
class INode : public amuse::IObj
{
friend class ProjectModel;
virtual void _sortChildren();
public:
enum class Type
{
Null,
Root,
Group, // Top-level group
SongGroup,
SoundGroup,
Collection, // Classified object collection, one of the following:
SoundMacro,
ADSR,
Curve,
Keymap,
Layer,
Sample
};
protected:
QString m_name;
INode* m_parent = nullptr;
int m_row = -1;
std::vector<amuse::IObjToken<INode>> m_children;
amuse::IObjToken<INode> m_nullChild;
public:
virtual ~INode() = default;
INode(const QString& name);
INode(INode* parent) : m_parent(parent), m_row(0)
{
/* ONLY USED BY NULL NODE! */
}
class INode : public amuse::IObj {
friend class ProjectModel;
virtual void _sortChildren();
int childCount() const { return int(m_children.size()); }
INode* child(int row) const
{
if (row == m_children.size())
return nullChild();
return m_children[row].get();
}
INode* nullChild() const { return m_nullChild.get(); }
INode* parent() const { return m_parent; }
int row() const { return m_row; }
void reindexRows(int row)
{
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
(*it)->m_row = row++;
m_nullChild->m_row = row;
}
void insertChild(amuse::ObjToken<INode> n)
{
assert(n->m_parent == nullptr && "Inserting already-parented node");
n->m_parent = this;
int row = hypotheticalIndex(n->name());
m_children.insert(m_children.begin() + row, n.get());
reindexRows(row);
}
amuse::ObjToken<INode> removeChild(INode* n)
{
amuse::ObjToken<INode> ret = n;
int row = ret->row();
assert(ret.get() == m_children.at(row).get() && "Removing non-child from node");
m_children.erase(m_children.begin() + row);
reindexRows(row);
ret->m_parent = nullptr;
ret->m_row = -1;
return ret;
}
void reserve(size_t sz) { m_children.reserve(sz); }
template<class T, class... _Args>
T& makeChild(_Args&&... args)
{
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
insertChild(tok.get());
return static_cast<T&>(*tok);
}
template<class T, class... _Args>
T& _appendChild(_Args&&... args)
{
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
tok->m_parent = this;
tok->m_row = m_children.size();
m_children.push_back(tok.get());
m_nullChild->m_row = m_children.size();
return static_cast<T&>(*tok);
}
INode* findChild(const QString& name) const
{
int idx = hypotheticalIndex(name);
if (idx >= m_children.size())
return nullptr;
INode* ret = m_children[idx].get();
if (ret->name() == name)
return ret;
return nullptr;
}
bool depthTraverse(const std::function<bool(INode* node)>& func)
{
for (auto& n : m_children)
if (!n->depthTraverse(func))
break;
return func(this);
}
bool oneLevelTraverse(const std::function<bool(INode* node)>& func)
{
for (auto& n : m_children)
if (!func(n.get()))
return false;
return true;
}
const QString& name() const { return m_name; }
virtual int hypotheticalIndex(const QString& name) const;
virtual amuse::NameDB* getNameDb() const { return nullptr; }
virtual Type type() const = 0;
virtual QString text() const = 0;
virtual QIcon icon() const = 0;
virtual Qt::ItemFlags flags() const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; }
virtual AmuseItemEditFlags editFlags() const { return AmuseItemNone; }
virtual void registerNames(const NameUndoRegistry& registry) const {}
virtual void unregisterNames(NameUndoRegistry& registry) const {}
public:
enum class Type {
Null,
Root,
Group, // Top-level group
SongGroup,
SoundGroup,
Collection, // Classified object collection, one of the following:
SoundMacro,
ADSR,
Curve,
Keymap,
Layer,
Sample
};
struct NullNode final : INode
{
NullNode(INode* parent) : INode(parent) {}
Type type() const { return Type::Null; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
};
struct RootNode final : INode
{
RootNode() : INode(QStringLiteral("<root>")) {}
protected:
QString m_name;
INode* m_parent = nullptr;
int m_row = -1;
std::vector<amuse::IObjToken<INode>> m_children;
amuse::IObjToken<INode> m_nullChild;
Type type() const { return Type::Root; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
};
struct CollectionNode;
struct BasePoolObjectNode;
struct GroupNode final : INode
{
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
GroupNode(const QString& name) : INode(name) {}
GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
: INode(it->first), m_it(it) {}
public:
~INode() override = default;
explicit INode(QString name);
int hypotheticalIndex(const QString& name) const;
void _sortChildren();
// ONLY USED BY NULL NODE!
explicit INode(INode* parent) : m_parent(parent), m_row(0) {}
static QIcon Icon;
Type type() const { return Type::Group; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemNoCut; }
int childCount() const { return int(m_children.size()); }
INode* child(int row) const {
if (row == m_children.size())
return nullChild();
return m_children[row].get();
}
INode* nullChild() const { return m_nullChild.get(); }
INode* parent() const { return m_parent; }
int row() const { return m_row; }
CollectionNode* getCollectionOfType(Type tp) const;
amuse::AudioGroupDatabase* getAudioGroup() const { return m_it->second.get(); }
BasePoolObjectNode* pageObjectNodeOfId(amuse::ObjectId id) const;
};
struct SongGroupNode final : INode
{
amuse::GroupId m_id;
amuse::ObjToken<amuse::SongGroupIndex> m_index;
SongGroupNode(const QString& name, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(name), m_index(index) {}
SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
void reindexRows(int row) {
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
(*it)->m_row = row++;
m_nullChild->m_row = row;
}
static QIcon Icon;
Type type() const { return Type::SongGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
void insertChild(amuse::ObjToken<INode> n) {
assert(n->m_parent == nullptr && "Inserting already-parented node");
n->m_parent = this;
int row = hypotheticalIndex(n->name());
m_children.insert(m_children.begin() + row, n.get());
reindexRows(row);
}
amuse::ObjToken<INode> removeChild(INode* n) {
amuse::ObjToken<INode> ret = n;
int row = ret->row();
assert(ret.get() == m_children.at(row).get() && "Removing non-child from node");
m_children.erase(m_children.begin() + row);
reindexRows(row);
ret->m_parent = nullptr;
ret->m_row = -1;
return ret;
}
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
void reserve(size_t sz) { m_children.reserve(sz); }
template <class T, class... _Args>
T& makeChild(_Args&&... args) {
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
insertChild(tok.get());
return static_cast<T&>(*tok);
}
template <class T, class... _Args>
T& _appendChild(_Args&&... args) {
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
tok->m_parent = this;
tok->m_row = m_children.size();
m_children.push_back(tok.get());
m_nullChild->m_row = m_children.size();
return static_cast<T&>(*tok);
}
void registerNames(const NameUndoRegistry& registry) const
{
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_midiSetups)
registry.registerSongName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const
{
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_midiSetups)
registry.unregisterSongName(p.first);
}
};
struct SoundGroupNode final : INode
{
amuse::GroupId m_id;
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
SoundGroupNode(const QString& name, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(name), m_index(index) {}
SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
INode* findChild(const QString& name) const {
int idx = hypotheticalIndex(name);
if (idx >= m_children.size())
return nullptr;
INode* ret = m_children[idx].get();
if (ret->name() == name)
return ret;
return nullptr;
}
static QIcon Icon;
Type type() const { return Type::SoundGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
bool depthTraverse(const std::function<bool(INode* node)>& func) {
for (auto& n : m_children)
if (!n->depthTraverse(func))
break;
return func(this);
}
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
bool oneLevelTraverse(const std::function<bool(INode* node)>& func) {
for (auto& n : m_children)
if (!func(n.get()))
return false;
return true;
}
void registerNames(const NameUndoRegistry& registry) const
{
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_sfxEntries)
registry.registerSFXName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const
{
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_sfxEntries)
registry.unregisterSFXName(p.first);
}
};
struct CollectionNode final : INode
{
QIcon m_icon;
Type m_collectionType;
CollectionNode(const QString& name, const QIcon& icon, Type collectionType)
: INode(name), m_icon(icon), m_collectionType(collectionType) {}
const QString& name() const { return m_name; }
virtual int hypotheticalIndex(const QString& name) const;
Type type() const { return Type::Collection; }
QString text() const { return m_name; }
QIcon icon() const { return m_icon; }
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
virtual amuse::NameDB* getNameDb() const { return nullptr; }
Type collectionType() const { return m_collectionType; }
int indexOfId(amuse::ObjectId id) const;
amuse::ObjectId idOfIndex(int idx) const;
BasePoolObjectNode* nodeOfIndex(int idx) const;
BasePoolObjectNode* nodeOfId(amuse::ObjectId id) const;
};
struct BasePoolObjectNode : INode
{
amuse::ObjectId m_id;
BasePoolObjectNode(const QString& name) : INode(name) {}
BasePoolObjectNode(amuse::ObjectId id, const QString& name)
: INode(name), m_id(id) {}
amuse::ObjectId id() const { return m_id; }
QString text() const { return m_name; }
QIcon icon() const { return {}; }
};
template <class ID, class T, INode::Type TP>
struct PoolObjectNode final : BasePoolObjectNode
{
amuse::ObjToken<T> m_obj;
PoolObjectNode(const QString& name, amuse::ObjToken<T> obj) : BasePoolObjectNode(name), m_obj(obj) {}
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
: BasePoolObjectNode(id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
virtual Type type() const = 0;
virtual QString text() const = 0;
virtual QIcon icon() const = 0;
virtual Qt::ItemFlags flags() const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; }
virtual AmuseItemEditFlags editFlags() const { return AmuseItemNone; }
Type type() const { return TP; }
AmuseItemEditFlags editFlags() const { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
virtual void registerNames(const NameUndoRegistry& registry) const {}
virtual void unregisterNames(NameUndoRegistry& registry) const {}
};
struct NullNode final : INode {
explicit NullNode(INode* parent) : INode(parent) {}
void registerNames(const NameUndoRegistry& registry) const
{
ID::CurNameDB->registerPair(text().toUtf8().data(), m_id);
}
void unregisterNames(NameUndoRegistry& registry) const
{
ID::CurNameDB->remove(m_id);
}
amuse::NameDB* getNameDb() const
{
return ID::CurNameDB;
}
};
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
using KeymapNode = PoolObjectNode<amuse::KeymapId, std::array<amuse::Keymap, 128>, INode::Type::Keymap>;
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
Type type() const override { return Type::Null; }
QString text() const override { return {}; }
QIcon icon() const override { return {}; }
};
struct RootNode final : INode {
RootNode() : INode(QStringLiteral("<root>")) {}
amuse::ObjToken<RootNode> m_root;
Type type() const override { return Type::Root; }
QString text() const override { return {}; }
QIcon icon() const override { return {}; }
Qt::ItemFlags flags() const override { return Qt::ItemIsEnabled; }
};
struct CollectionNode;
struct BasePoolObjectNode;
struct GroupNode final : INode {
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
explicit GroupNode(QString name) : INode(std::move(name)) {}
explicit GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
: INode(it->first), m_it(it) {}
bool m_needsReset = false;
void _buildGroupNodeCollections(GroupNode& gn);
void _buildGroupNode(GroupNode& gn, amuse::AudioGroup& group);
void _resetModelData();
void _resetSongRefCount();
QString MakeDedupedSubprojectName(const QString& origName);
static QString MakeDedupedName(const QString& origName, amuse::NameDB* db);
int hypotheticalIndex(const QString& name) const override;
void _sortChildren() override;
static QIcon Icon;
Type type() const override { return Type::Group; }
QString text() const override { return m_name; }
QIcon icon() const override { return Icon; }
AmuseItemEditFlags editFlags() const override { return AmuseItemNoCut; }
CollectionNode* getCollectionOfType(Type tp) const;
amuse::AudioGroupDatabase* getAudioGroup() const { return m_it->second.get(); }
BasePoolObjectNode* pageObjectNodeOfId(amuse::ObjectId id) const;
};
struct SongGroupNode final : INode {
amuse::GroupId m_id;
amuse::ObjToken<amuse::SongGroupIndex> m_index;
explicit SongGroupNode(QString name, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(std::move(name)), m_index(std::move(index)) {}
explicit SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(QString::fromUtf8(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()))
, m_id(id)
, m_index(std::move(index)) {}
static QIcon Icon;
Type type() const override { return Type::SongGroup; }
QString text() const override { return m_name; }
QIcon icon() const override { return Icon; }
AmuseItemEditFlags editFlags() const override { return AmuseItemAll; }
amuse::NameDB* getNameDb() const override { return amuse::GroupId::CurNameDB; }
void registerNames(const NameUndoRegistry& registry) const override {
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_midiSetups)
registry.registerSongName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const override {
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_midiSetups)
registry.unregisterSongName(p.first);
}
};
struct SoundGroupNode final : INode {
amuse::GroupId m_id;
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
explicit SoundGroupNode(QString name, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(std::move(name)), m_index(std::move(index)) {}
explicit SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(QString::fromUtf8(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()))
, m_id(id)
, m_index(std::move(index)) {}
static QIcon Icon;
Type type() const override { return Type::SoundGroup; }
QString text() const override { return m_name; }
QIcon icon() const override { return Icon; }
AmuseItemEditFlags editFlags() const override { return AmuseItemAll; }
amuse::NameDB* getNameDb() const override { return amuse::GroupId::CurNameDB; }
void registerNames(const NameUndoRegistry& registry) const override {
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_sfxEntries)
registry.registerSFXName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const override {
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_sfxEntries)
registry.unregisterSFXName(p.first);
}
};
struct CollectionNode final : INode {
QIcon m_icon;
Type m_collectionType;
explicit CollectionNode(QString name, QIcon icon, Type collectionType)
: INode(std::move(name)), m_icon(std::move(icon)), m_collectionType(collectionType) {}
Type type() const override { return Type::Collection; }
QString text() const override { return m_name; }
QIcon icon() const override { return m_icon; }
Qt::ItemFlags flags() const override { return Qt::ItemIsEnabled; }
Type collectionType() const { return m_collectionType; }
int indexOfId(amuse::ObjectId id) const;
amuse::ObjectId idOfIndex(int idx) const;
BasePoolObjectNode* nodeOfIndex(int idx) const;
BasePoolObjectNode* nodeOfId(amuse::ObjectId id) const;
};
struct BasePoolObjectNode : INode {
amuse::ObjectId m_id;
explicit BasePoolObjectNode(QString name) : INode(std::move(name)) {}
explicit BasePoolObjectNode(amuse::ObjectId id, QString name) : INode(std::move(name)), m_id(id) {}
amuse::ObjectId id() const { return m_id; }
QString text() const override { return m_name; }
QIcon icon() const override { return {}; }
};
template <class ID, class T, INode::Type TP>
struct PoolObjectNode final : BasePoolObjectNode {
amuse::ObjToken<T> m_obj;
PoolObjectNode(QString name, amuse::ObjToken<T> obj) : BasePoolObjectNode(std::move(name)), m_obj(std::move(obj)) {}
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
: BasePoolObjectNode(id, QString::fromUtf8(ID::CurNameDB->resolveNameFromId(id).data())), m_obj(std::move(obj)) {}
Type type() const override { return TP; }
AmuseItemEditFlags editFlags() const override { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
void registerNames(const NameUndoRegistry& registry) const override {
ID::CurNameDB->registerPair(text().toUtf8().data(), m_id);
}
void unregisterNames(NameUndoRegistry& registry) const override { ID::CurNameDB->remove(m_id); }
amuse::NameDB* getNameDb() const override { return ID::CurNameDB; }
};
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
using KeymapNode = PoolObjectNode<amuse::KeymapId, std::array<amuse::Keymap, 128>, INode::Type::Keymap>;
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
amuse::ObjToken<RootNode> m_root;
bool m_needsReset = false;
void _buildGroupNodeCollections(GroupNode& gn);
void _buildGroupNode(GroupNode& gn, amuse::AudioGroup& group);
void _resetModelData();
void _resetSongRefCount();
QString MakeDedupedSubprojectName(const QString& origName);
static QString MakeDedupedName(const QString& origName, amuse::NameDB* db);
private:
OutlineFilterProxyModel m_outlineProxy;
NullItemProxyModel m_nullProxy;
PageObjectProxyModel m_pageObjectProxy;
public:
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
~ProjectModel() override;
bool clearProjectData();
bool openGroupData(const QString& groupName, UIMessenger& messenger);
void openSongsData();
void importSongsData(const QString& path);
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger);
void saveSongsIndex();
bool saveToFile(UIMessenger& messenger);
QStringList getGroupList() const;
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
bool clearProjectData();
bool openGroupData(QString groupName, UIMessenger& messenger);
void openSongsData();
void importSongsData(const QString& path);
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data, ImportMode mode,
UIMessenger& messenger);
void saveSongsIndex();
bool saveToFile(UIMessenger& messenger);
QStringList getGroupList() const;
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
void updateNodeNames();
bool ensureModelData();
void updateNodeNames();
bool ensureModelData();
QModelIndex proxyCreateIndex(int arow, int acolumn, void *adata) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex index(INode* node) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex& index) const;
INode* node(const QModelIndex& index) const;
GroupNode* getGroupNode(INode* node) const;
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
RootNode* rootNode() const { return m_root.get(); }
QModelIndex proxyCreateIndex(int arow, int acolumn, void* adata) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
QModelIndex index(INode* node) const;
QModelIndex parent(const QModelIndex& child) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
INode* node(const QModelIndex& index) const;
GroupNode* getGroupNode(INode* node) const;
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
RootNode* rootNode() const { return m_root.get(); }
void _postAddNode(INode* n, const NameUndoRegistry& registry);
void _preDelNode(INode* n, NameUndoRegistry& registry);
void _addNode(GroupNode* node, std::unique_ptr<amuse::AudioGroupDatabase>&& data, const NameUndoRegistry& registry);
std::unique_ptr<amuse::AudioGroupDatabase> _delNode(GroupNode* node, NameUndoRegistry& registry);
GroupNode* newSubproject(const QString& name);
template <class NT, class T>
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name);
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SongGroupNode* newSongGroup(GroupNode* group, const QString& name);
template <class NT, class T>
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name,
const SoundMacroTemplateEntry* templ = nullptr);
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
ADSRNode* newADSR(GroupNode* group, const QString& name);
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
CurveNode* newCurve(GroupNode* group, const QString& name);
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
KeymapNode* newKeymap(GroupNode* group, const QString& name);
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
LayersNode* newLayers(GroupNode* group, const QString& name);
void _renameNode(INode* node, const QString& name);
void _postAddNode(INode* n, const NameUndoRegistry& registry);
void _preDelNode(INode* n, NameUndoRegistry& registry);
void _addNode(GroupNode* node, std::unique_ptr<amuse::AudioGroupDatabase>&& data, const NameUndoRegistry& registry);
std::unique_ptr<amuse::AudioGroupDatabase> _delNode(GroupNode* node, NameUndoRegistry& registry);
GroupNode* newSubproject(QString name);
template <class NT, class T>
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundGroupNode* newSoundGroup(GroupNode* group, QString name);
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SongGroupNode* newSongGroup(GroupNode* group, QString name);
template <class NT, class T>
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundMacroNode* newSoundMacro(GroupNode* group, QString name, const SoundMacroTemplateEntry* templ = nullptr);
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
ADSRNode* newADSR(GroupNode* group, QString name);
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
CurveNode* newCurve(GroupNode* group, QString name);
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
KeymapNode* newKeymap(GroupNode* group, QString name);
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
LayersNode* newLayers(GroupNode* group, QString name);
void _renameNode(INode* node, const QString& name);
template <class NT>
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
template <class NT>
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
template <class NT>
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
template <class NT>
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList& indexes) const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
QStringList mimeTypes() const override;
QMimeData* mimeData(const QModelIndexList& indexes) const override;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
const QModelIndex& parent) override;
void cut(const QModelIndex& index);
void copy(const QModelIndex& index);
void paste(const QModelIndex& index);
QModelIndex duplicate(const QModelIndex& index);
void del(const QModelIndex& index);
void cut(const QModelIndex& index);
void copy(const QModelIndex& index);
void paste(const QModelIndex& index);
QModelIndex duplicate(const QModelIndex& index);
void del(const QModelIndex& index);
const QDir& dir() const { return m_dir; }
QString path() const { return m_dir.path(); }
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
const QDir& dir() const { return m_dir; }
QString path() const { return m_dir.path(); }
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
QString getMIDIPathOfSong(amuse::SongId id) const;
void setMIDIPathOfSong(amuse::SongId id, const QString& path);
std::pair<amuse::SongId, std::string> bootstrapSongId();
void allocateSongId(amuse::SongId id, std::string_view name);
void deallocateSongId(amuse::SongId oldId);
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
QString getMIDIPathOfSong(amuse::SongId id) const;
void setMIDIPathOfSong(amuse::SongId id, QString path);
std::pair<amuse::SongId, std::string> bootstrapSongId();
void allocateSongId(amuse::SongId id, std::string_view name);
void deallocateSongId(amuse::SongId oldId);
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
void setIdDatabases(INode* context) const;
void setIdDatabases(INode* context) const;
};
#endif //AMUSE_PROJECT_MODEL_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -1,116 +1,125 @@
#ifndef AMUSE_SAMPLE_EDITOR_HPP
#define AMUSE_SAMPLE_EDITOR_HPP
#pragma once
#include <cstdint>
#include <utility>
#include <QFont>
#include <QWidget>
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <QScrollArea>
#include <QSlider>
#include <QCheckBox>
#include <QSpinBox>
#include <amuse/AudioGroupPool.hpp>
#include <amuse/AudioGroupSampleDirectory.hpp>
#include <amuse/Common.hpp>
class SampleEditor;
class SampleView : public QWidget
{
Q_OBJECT
friend class SampleControls;
qreal m_baseSamplesPerPx = 100.0;
qreal m_samplesPerPx = 100.0;
qreal m_zoomFactor = 1.0;
amuse::ObjToken<ProjectModel::SampleNode> m_node;
amuse::ObjToken<amuse::SampleEntryData> m_sample;
amuse::ObjToken<amuse::SoundMacro> m_playbackMacro;
const unsigned char* m_sampleData = nullptr;
qreal m_curSamplePos = 0.0;
int16_t m_prev1 = 0;
int16_t m_prev2 = 0;
QFont m_rulerFont;
int m_displaySamplePos = -1;
enum class DragState
{
None,
Start,
End
};
DragState m_dragState = DragState::None;
void seekToSample(qreal sample);
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> iterateSampleInterval(qreal interval);
void calculateSamplesPerPx();
SampleEditor* getEditor() const;
public:
explicit SampleView(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SampleNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
amuse::SampleEntryData* entryData() const;
const amuse::SoundMacro* soundMacro() const;
void setSamplePos(int pos);
void updateSampleRange(int oldSamp, int newSamp);
class QCheckBox;
class QPushButton;
class QScrollArea;
class QSlider;
void paintEvent(QPaintEvent* ev);
void resetZoom();
void setZoom(int zVal);
void showEvent(QShowEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseReleaseEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
void wheelEvent(QWheelEvent* ev);
class SampleView : public QWidget {
Q_OBJECT
friend class SampleControls;
qreal m_baseSamplesPerPx = 100.0;
qreal m_samplesPerPx = 100.0;
qreal m_zoomFactor = 1.0;
amuse::ObjToken<ProjectModel::SampleNode> m_node;
amuse::ObjToken<amuse::SampleEntryData> m_sample;
amuse::ObjToken<amuse::SoundMacro> m_playbackMacro;
const unsigned char* m_sampleData = nullptr;
qreal m_curSamplePos = 0.0;
int16_t m_prev1 = 0;
int16_t m_prev2 = 0;
QFont m_rulerFont;
int m_displaySamplePos = -1;
enum class DragState { None, Start, End };
DragState m_dragState = DragState::None;
void seekToSample(qreal sample);
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> iterateSampleInterval(qreal interval);
void calculateSamplesPerPx();
SampleEditor* getEditor() const;
public:
explicit SampleView(QWidget* parent = Q_NULLPTR);
~SampleView() override;
bool loadData(ProjectModel::SampleNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
amuse::SampleEntryData* entryData() const;
const amuse::SoundMacro* soundMacro() const;
void setSamplePos(int pos);
void updateSampleRange(int oldSamp, int newSamp);
void paintEvent(QPaintEvent* ev) override;
void resetZoom();
void setZoom(int zVal);
void showEvent(QShowEvent* ev) override;
void mousePressEvent(QMouseEvent* ev) override;
void mouseReleaseEvent(QMouseEvent* ev) override;
void mouseMoveEvent(QMouseEvent* ev) override;
void wheelEvent(QWheelEvent* ev) override;
};
class SampleControls : public QFrame
{
Q_OBJECT
QString m_path;
QSlider* m_zoomSlider;
QCheckBox* m_loopCheck;
QSpinBox* m_loopStart;
QSpinBox* m_loopEnd;
QSpinBox* m_basePitch;
QPushButton* m_makeOtherVersion;
QMetaObject::Connection m_makeOtherConn;
QPushButton* m_showInBrowser;
bool m_enableUpdate = true;
bool m_enableFileWrite = true;
class SampleControls : public QFrame {
Q_OBJECT
QString m_path;
QSlider* m_zoomSlider;
QCheckBox* m_loopCheck;
QSpinBox* m_loopStart;
QSpinBox* m_loopEnd;
QSpinBox* m_basePitch;
QPushButton* m_makeOtherVersion;
QMetaObject::Connection m_makeOtherConn;
QPushButton* m_showInBrowser;
bool m_enableUpdate = true;
bool m_enableFileWrite = true;
public:
explicit SampleControls(QWidget* parent = Q_NULLPTR);
void doFileWrite();
void setFileWrite(bool w);
void updateFileState();
void setLoopStartSample(int sample) { m_loopStart->setValue(sample); }
void setLoopEndSample(int sample) { m_loopEnd->setValue(sample); }
void loadData(bool reset);
void unloadData();
explicit SampleControls(QWidget* parent = Q_NULLPTR);
~SampleControls() override;
void doFileWrite();
void setFileWrite(bool w);
void updateFileState();
void setLoopStartSample(int sample) { m_loopStart->setValue(sample); }
void setLoopEndSample(int sample) { m_loopEnd->setValue(sample); }
void loadData(bool reset);
void unloadData();
public slots:
void zoomSliderChanged(int val);
void loopStateChanged(int state);
void startValueChanged(int val);
void endValueChanged(int val);
void pitchValueChanged(int val);
void makeWAVVersion();
void makeCompressedVersion();
void showInBrowser();
void zoomSliderChanged(int val);
void loopStateChanged(int state);
void startValueChanged(int val);
void endValueChanged(int val);
void pitchValueChanged(int val);
void makeWAVVersion();
void makeCompressedVersion();
void showInBrowser();
};
class SampleEditor : public EditorWidget
{
Q_OBJECT
friend class SampleView;
friend class SampleControls;
friend class SampLoopUndoCommand;
friend class SampPitchUndoCommand;
QScrollArea* m_scrollArea;
SampleView* m_sampleView;
SampleControls* m_controls;
class SampleEditor : public EditorWidget {
Q_OBJECT
friend class SampleView;
friend class SampleControls;
friend class SampLoopUndoCommand;
friend class SampPitchUndoCommand;
QScrollArea* m_scrollArea;
SampleView* m_sampleView;
SampleControls* m_controls;
public:
explicit SampleEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SampleNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
const amuse::SoundMacro* soundMacro() const;
void setSamplePos(int pos);
explicit SampleEditor(QWidget* parent = Q_NULLPTR);
~SampleEditor() override;
void resizeEvent(QResizeEvent* ev);
bool loadData(ProjectModel::SampleNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
const amuse::SoundMacro* soundMacro() const;
void setSamplePos(int pos);
void resizeEvent(QResizeEvent* ev) override;
};
#endif //AMUSE_SAMPLE_EDITOR_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -1,301 +1,324 @@
#ifndef AMUSE_SONG_GROUP_EDITOR_HPP
#define AMUSE_SONG_GROUP_EDITOR_HPP
#pragma once
#include "EditorWidget.hpp"
#include <array>
#include <cstdint>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
#include <QFileDialog>
#include <QLineEdit>
#include <QProxyStyle>
#include <QPushButton>
#include <QSplitter>
#include <QStyledItemDelegate>
#include <QTabWidget>
#include <QAbstractTableModel>
#include <QTableView>
#include <QToolButton>
#include <QAction>
#include <QSplitter>
#include <QListView>
#include <QLineEdit>
#include <QPushButton>
#include <QFileDialog>
#include <QProxyStyle>
#include "amuse/Sequencer.hpp"
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <amuse/AudioGroupProject.hpp>
#include <amuse/Common.hpp>
#include <amuse/Sequencer.hpp>
class SetupTableView;
class PageObjectDelegate : public BaseObjectDelegate
{
Q_OBJECT
class PageObjectDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override;
public:
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
~PageObjectDelegate() override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
private slots:
void objIndexChanged();
void objIndexChanged();
};
class MIDIFileFieldWidget : public QWidget
{
Q_OBJECT
QLineEdit m_le;
QPushButton m_button;
QFileDialog m_dialog;
class MIDIFileFieldWidget : public QWidget {
Q_OBJECT
QLineEdit m_le;
QPushButton m_button;
QFileDialog m_dialog;
public:
explicit MIDIFileFieldWidget(QWidget* parent = Q_NULLPTR);
QString path() const { return m_le.text(); }
void setPath(const QString& path) { m_le.setText(path); }
explicit MIDIFileFieldWidget(QWidget* parent = Q_NULLPTR);
~MIDIFileFieldWidget() override;
QString path() const { return m_le.text(); }
void setPath(const QString& path) { m_le.setText(path); }
public slots:
void buttonPressed();
void fileDialogOpened(const QString& path);
void buttonPressed();
void fileDialogOpened(const QString& path);
signals:
void pathChanged();
void pathChanged();
};
class MIDIFileDelegate : public QStyledItemDelegate
{
Q_OBJECT
QFileDialog m_fileDialogMid, m_fileDialogSng;
std::vector<uint8_t> m_exportData;
class MIDIFileDelegate : public QStyledItemDelegate {
Q_OBJECT
QFileDialog m_fileDialogMid, m_fileDialogSng;
std::vector<uint8_t> m_exportData;
public:
explicit MIDIFileDelegate(SetupTableView* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void destroyEditor(QWidget *editor, const QModelIndex &index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
explicit MIDIFileDelegate(SetupTableView* parent = Q_NULLPTR);
~MIDIFileDelegate() override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void destroyEditor(QWidget* editor, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index) override;
private slots:
void doExportMIDI();
void _doExportMIDI(const QString& path);
void doExportSNG();
void _doExportSNG(const QString& path);
void doExportMIDI();
void _doExportMIDI(const QString& path);
void doExportSNG();
void _doExportSNG(const QString& path);
public slots:
void pathChanged();
void pathChanged();
};
class PageModel : public QAbstractTableModel
{
Q_OBJECT
friend class SongGroupEditor;
friend class PageObjectDelegate;
friend class PageTableView;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator
{
using ItTp = std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const { return m_it->first < other.m_it->first; }
bool operator<(uint8_t other) const { return m_it->first < other; }
};
std::vector<Iterator> m_sorted;
bool m_drum;
std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfProgram(uint8_t prog) const;
int _hypotheticalIndexOfProgram(uint8_t prog) const;
class PageModel : public QAbstractTableModel {
Q_OBJECT
friend class SongGroupEditor;
friend class PageObjectDelegate;
friend class PageTableView;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator {
using ItTp = std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const { return m_it->first < other.m_it->first; }
bool operator<(uint8_t other) const { return m_it->first < other; }
};
std::vector<Iterator> m_sorted;
bool m_drum;
std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfProgram(uint8_t prog) const;
int _hypotheticalIndexOfProgram(uint8_t prog) const;
public:
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
~PageModel() override;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
};
class SetupListModel : public QAbstractTableModel
{
Q_OBJECT
friend class SongGroupEditor;
friend class MIDIFileDelegate;
friend class SetupTableView;
friend class SetupModel;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator
{
using ItTp = std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const
{
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const
{
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const
{
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSong(amuse::SongId id) const;
int _hypotheticalIndexOfSong(const std::string& songName) const;
class SetupListModel : public QAbstractTableModel {
Q_OBJECT
friend class SongGroupEditor;
friend class MIDIFileDelegate;
friend class SetupTableView;
friend class SetupModel;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator {
using ItTp = std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const {
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const {
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const {
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSong(amuse::SongId id) const;
int _hypotheticalIndexOfSong(const std::string& songName) const;
public:
explicit SetupListModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
explicit SetupListModel(QObject* parent = Q_NULLPTR);
~SetupListModel() override;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
int _insertRow(
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data);
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>
_removeRow(amuse::SongId id);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
int _insertRow(std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data);
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>> _removeRow(amuse::SongId id);
};
class SetupModel : public QAbstractTableModel
{
Q_OBJECT
friend class SongGroupEditor;
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
public:
explicit SetupModel(QObject* parent = Q_NULLPTR);
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
void unloadData();
class SetupModel : public QAbstractTableModel {
Q_OBJECT
friend class SongGroupEditor;
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
public:
explicit SetupModel(QObject* parent = Q_NULLPTR);
~SetupModel() override;
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
};
class PageTableView : public QTableView
{
Q_OBJECT
PageObjectDelegate m_poDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
class PageTableView : public QTableView {
Q_OBJECT
PageObjectDelegate m_poDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
public:
explicit PageTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
explicit PageTableView(QWidget* parent = Q_NULLPTR);
~PageTableView() override;
void setModel(QAbstractItemModel* model) override;
void deleteSelection();
};
class SetupTableView : public QSplitter
{
Q_OBJECT
friend class SongGroupEditor;
friend class SetupRowUndoCommand;
QTableView* m_listView;
QTableView* m_tableView;
MIDIFileDelegate m_midiDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate;
class SetupTableView : public QSplitter {
Q_OBJECT
friend class SongGroupEditor;
friend class SetupRowUndoCommand;
QTableView* m_listView;
QTableView* m_tableView;
MIDIFileDelegate m_midiDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate;
public:
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
void deleteSelection();
void showEvent(QShowEvent* event);
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
~SetupTableView() override;
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
void deleteSelection();
void showEvent(QShowEvent* event) override;
};
class ColoredTabBarStyle : public QProxyStyle
{
class ColoredTabBarStyle : public QProxyStyle {
public:
using QProxyStyle::QProxyStyle;
void drawControl(QStyle::ControlElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget = nullptr) const;
using QProxyStyle::QProxyStyle;
void drawControl(QStyle::ControlElement element, const QStyleOption* option, QPainter* painter,
const QWidget* widget = nullptr) const override;
};
class ColoredTabBar : public QTabBar
{
Q_OBJECT
class ColoredTabBar : public QTabBar {
Q_OBJECT
public:
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
};
class ColoredTabWidget : public QTabWidget
{
Q_OBJECT
ColoredTabBar m_tabBar;
class ColoredTabWidget : public QTabWidget {
Q_OBJECT
ColoredTabBar m_tabBar;
public:
explicit ColoredTabWidget(QWidget* parent = Q_NULLPTR);
explicit ColoredTabWidget(QWidget* parent = Q_NULLPTR);
};
class MIDIPlayerWidget : public QWidget
{
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SongId m_songId;
QString m_path;
std::vector<uint8_t> m_arrData;
amuse::ObjToken<amuse::Sequencer> m_seq;
class MIDIPlayerWidget : public QWidget {
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SongId m_songId;
QString m_path;
std::vector<uint8_t> m_arrData;
amuse::ObjToken<amuse::Sequencer> m_seq;
public:
explicit MIDIPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SongId id,
const QString& path, QWidget* parent = Q_NULLPTR);
~MIDIPlayerWidget();
amuse::SongId songId() const { return m_songId; }
amuse::Sequencer* sequencer() const { return m_seq.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
explicit MIDIPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SongId id, const QString& path,
QWidget* parent = Q_NULLPTR);
~MIDIPlayerWidget() override;
amuse::SongId songId() const { return m_songId; }
amuse::Sequencer* sequencer() const { return m_seq.get(); }
void stopped();
void resizeEvent(QResizeEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
public slots:
void clicked();
void clicked();
};
class SongGroupEditor : public EditorWidget
{
Q_OBJECT
friend class SetupModel;
PageModel m_normPages;
PageModel m_drumPages;
SetupListModel m_setupList;
SetupModel m_setup;
PageTableView* m_normTable;
PageTableView* m_drumTable;
SetupTableView* m_setupTable;
ColoredTabWidget m_tabs;
AddRemoveButtons m_addRemoveButtons;
class SongGroupEditor : public EditorWidget {
Q_OBJECT
friend class SetupModel;
PageModel m_normPages;
PageModel m_drumPages;
SetupListModel m_setupList;
SetupModel m_setup;
PageTableView* m_normTable;
PageTableView* m_drumTable;
SetupTableView* m_setupTable;
ColoredTabWidget m_tabs;
AddRemoveButtons m_addRemoveButtons;
public:
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SongGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
AmuseItemEditFlags itemEditFlags() const;
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
~SongGroupEditor() override;
bool loadData(ProjectModel::SongGroupNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
void setEditorEnabled(bool en) override {}
void resizeEvent(QResizeEvent* ev) override;
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
AmuseItemEditFlags itemEditFlags() const override;
private slots:
void doAdd();
void doSelectionChanged();
void doSetupSelectionChanged();
void currentTabChanged(int idx);
void normRowsInserted(const QModelIndex& parent, int first, int last);
void normRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void drumRowsInserted(const QModelIndex& parent, int first, int last);
void drumRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsInserted(const QModelIndex& parent, int first, int last);
void setupRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
void setupModelAboutToBeReset();
void setupDataChanged();
void itemDeleteAction();
void doAdd();
void doSelectionChanged();
void doSetupSelectionChanged();
void currentTabChanged(int idx);
void normRowsInserted(const QModelIndex& parent, int first, int last);
void normRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void drumRowsInserted(const QModelIndex& parent, int first, int last);
void drumRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsInserted(const QModelIndex& parent, int first, int last);
void setupRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
void setupModelAboutToBeReset();
void setupDataChanged();
void itemDeleteAction() override;
};
#endif //AMUSE_SONG_GROUP_EDITOR_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -1,133 +1,146 @@
#ifndef AMUSE_SOUND_GROUP_EDITOR_HPP
#define AMUSE_SOUND_GROUP_EDITOR_HPP
#pragma once
#include "EditorWidget.hpp"
#include <tuple>
#include <unordered_map>
#include <vector>
#include <QAction>
#include <QStyledItemDelegate>
#include <QTableView>
#include "amuse/Voice.hpp"
#include <QToolButton>
class SFXObjectDelegate : public BaseObjectDelegate
{
Q_OBJECT
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <amuse/AudioGroupProject.hpp>
#include <amuse/Common.hpp>
#include <amuse/Voice.hpp>
class SFXObjectDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override;
public:
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
~SFXObjectDelegate() override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
private slots:
void objIndexChanged();
void objIndexChanged();
};
class SFXModel : public QAbstractTableModel
{
Q_OBJECT
friend class SoundGroupEditor;
friend class SFXObjectDelegate;
friend class SFXTableView;
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
struct Iterator
{
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() const { return m_it.operator->(); }
bool operator<(const Iterator& other) const
{
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const
{
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const
{
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSFX(amuse::SFXId sfx) const;
int _hypotheticalIndexOfSFX(const std::string& sfxName) const;
class SFXModel : public QAbstractTableModel {
Q_OBJECT
friend class SoundGroupEditor;
friend class SFXObjectDelegate;
friend class SFXTableView;
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
struct Iterator {
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() const { return m_it.operator->(); }
bool operator<(const Iterator& other) const {
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const {
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const {
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSFX(amuse::SFXId sfx) const;
int _hypotheticalIndexOfSFX(const std::string& sfxName) const;
public:
explicit SFXModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
explicit SFXModel(QObject* parent = Q_NULLPTR);
~SFXModel() override;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
void loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
int _insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data);
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> _removeRow(amuse::SFXId sfx);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
int _insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data);
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> _removeRow(amuse::SFXId sfx);
};
class SFXTableView : public QTableView
{
Q_OBJECT
SFXObjectDelegate m_sfxDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_255Delegate;
class SFXTableView : public QTableView {
Q_OBJECT
SFXObjectDelegate m_sfxDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_255Delegate;
public:
explicit SFXTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
explicit SFXTableView(QWidget* parent = Q_NULLPTR);
~SFXTableView() override;
void setModel(QAbstractItemModel* model) override;
void deleteSelection();
};
class SFXPlayerWidget : public QWidget
{
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SFXId m_sfxId;
amuse::ObjToken<amuse::Voice> m_vox;
class SFXPlayerWidget : public QWidget {
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SFXId m_sfxId;
amuse::ObjToken<amuse::Voice> m_vox;
public:
explicit SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id,
QWidget* parent = Q_NULLPTR);
~SFXPlayerWidget();
amuse::SongId sfxId() const { return m_sfxId; }
amuse::Voice* voice() const { return m_vox.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
explicit SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id, QWidget* parent = Q_NULLPTR);
~SFXPlayerWidget() override;
amuse::SongId sfxId() const { return m_sfxId; }
amuse::Voice* voice() const { return m_vox.get(); }
void stopped();
void resizeEvent(QResizeEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
public slots:
void clicked();
void clicked();
};
class SoundGroupEditor : public EditorWidget
{
Q_OBJECT
SFXModel m_sfxs;
SFXTableView* m_sfxTable;
AddRemoveButtons m_addRemoveButtons;
class SoundGroupEditor : public EditorWidget {
Q_OBJECT
SFXModel m_sfxs;
SFXTableView* m_sfxTable;
AddRemoveButtons m_addRemoveButtons;
public:
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSFXListView() const { return m_sfxTable; }
AmuseItemEditFlags itemEditFlags() const;
private slots:
void rowsInserted(const QModelIndex &parent, int first, int last);
void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
void doAdd();
void doSelectionChanged();
void sfxDataChanged();
void itemDeleteAction();
};
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
~SoundGroupEditor() override;
#endif //AMUSE_SOUND_GROUP_EDITOR_HPP
bool loadData(ProjectModel::SoundGroupNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
void setEditorEnabled(bool en) override {}
void resizeEvent(QResizeEvent* ev) override;
QTableView* getSFXListView() const { return m_sfxTable; }
AmuseItemEditFlags itemEditFlags() const override;
private slots:
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void sfxDataChanged();
void itemDeleteAction() override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,190 +1,200 @@
#ifndef AMUSE_SOUND_MACRO_EDITOR_HPP
#define AMUSE_SOUND_MACRO_EDITOR_HPP
#pragma once
#include "EditorWidget.hpp"
#include <QStaticText>
#include <QVBoxLayout>
#include <QPropertyAnimation>
#include <QSplitter>
#include <QLabel>
#include <QMouseEvent>
#include <QSpinBox>
#include <QComboBox>
#include <QTreeWidget>
#include <QPushButton>
#include <QStaticText>
#include <QTreeWidget>
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <amuse/AudioGroupPool.hpp>
#include <amuse/Common.hpp>
class CatalogueItem;
class SoundMacroEditor;
class SoundMacroListing;
class CatalogueItem;
class TargetButton : public QPushButton
{
Q_OBJECT
class QLayoutItem;
class QPropertyAnimation;
class QSplitter;
class QVBoxLayout;
class TargetButton : public QPushButton {
Q_OBJECT
public:
explicit TargetButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
explicit TargetButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
void focusOutEvent(QFocusEvent* event) override { event->ignore(); }
void keyPressEvent(QKeyEvent* event) override { event->ignore(); }
};
class FieldSoundMacroStep : public QWidget
{
friend class CommandWidget;
Q_OBJECT
FieldProjectNode* m_macroField;
FieldSpinBox m_spinBox;
TargetButton m_targetButton;
SoundMacroEditor* getEditor() const;
SoundMacroListing* getListing() const;
class FieldSoundMacroStep : public QWidget {
friend class CommandWidget;
Q_OBJECT
FieldProjectNode* m_macroField;
FieldSpinBox m_spinBox;
TargetButton m_targetButton;
SoundMacroEditor* getEditor() const;
SoundMacroListing* getListing() const;
signals:
void valueChanged(int);
void valueChanged(int);
public slots:
void targetPressed();
void updateMacroField();
void targetPressed();
void updateMacroField();
public:
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
~FieldSoundMacroStep();
void setIndex(int index);
void cancel();
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
~FieldSoundMacroStep() override;
void setIndex(int index);
void cancel();
};
class CommandWidget : public QWidget
{
Q_OBJECT
friend class SoundMacroListing;
QFont m_numberFont;
QLabel m_titleLabel;
ListingDeleteButton m_deleteButton;
QStaticText m_numberText;
int m_index = -1;
amuse::SoundMacro::ICmd* m_cmd;
const amuse::SoundMacro::CmdIntrospection* m_introspection;
FieldSoundMacroStep* m_stepField = nullptr;
void setIndex(int index);
SoundMacroListing* getParent() const;
class CommandWidget : public QWidget {
Q_OBJECT
friend class SoundMacroListing;
QFont m_numberFont;
QLabel m_titleLabel;
ListingDeleteButton m_deleteButton;
QStaticText m_numberText;
int m_index = -1;
amuse::SoundMacro::ICmd* m_cmd;
const amuse::SoundMacro::CmdIntrospection* m_introspection;
FieldSoundMacroStep* m_stepField = nullptr;
void setIndex(int index);
SoundMacroListing* getParent() const;
private slots:
void boolChanged(int);
void numChanged(int);
void nodeChanged(int);
void deleteClicked();
void boolChanged(int);
void numChanged(int);
void nodeChanged(int);
void deleteClicked();
private:
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
public:
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
CommandWidget(QWidget* parent, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
void paintEvent(QPaintEvent* event);
QString getText() const { return m_titleLabel.text(); }
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
CommandWidget(QWidget* parent, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
~CommandWidget() override;
void paintEvent(QPaintEvent* event) override;
QString getText() const { return m_titleLabel.text(); }
};
class CommandWidgetContainer : public QWidget
{
Q_OBJECT
friend class SoundMacroListing;
CommandWidget* m_commandWidget;
QPropertyAnimation* m_animation = nullptr;
void animateOpen();
void animateClosed();
void snapOpen();
void snapClosed();
class CommandWidgetContainer : public QWidget {
Q_OBJECT
friend class SoundMacroListing;
CommandWidget* m_commandWidget;
QPropertyAnimation* m_animation = nullptr;
void animateOpen();
void animateClosed();
void snapOpen();
void snapClosed();
private slots:
void animationDestroyed();
void animationDestroyed();
public:
template <class..._Args>
CommandWidgetContainer(QWidget* parent, _Args&&... args);
template <class... _Args>
CommandWidgetContainer(QWidget* parent, _Args&&... args);
~CommandWidgetContainer() override;
};
class SoundMacroListing : public QWidget
{
Q_OBJECT
friend class CommandWidget;
friend class SoundMacroEditor;
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
QVBoxLayout* m_layout;
QLayoutItem* m_dragItem = nullptr;
int m_origIdx = -1;
int m_dragOpenIdx = -1;
CommandWidgetContainer* m_prevDragOpen = nullptr;
int m_autoscrollTimer = -1;
int m_autoscrollDelta = 0;
QWidget* m_autoscrollSource = nullptr;
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
void stopAutoscroll();
bool beginDrag(CommandWidget* widget);
void endDrag();
void cancelDrag();
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
void moveDrag(CommandWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
void insertDragout();
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
void deleteCommand(int index);
void reindex();
void clear();
class SoundMacroListing : public QWidget {
Q_OBJECT
friend class CommandWidget;
friend class SoundMacroEditor;
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
QVBoxLayout* m_layout;
QLayoutItem* m_dragItem = nullptr;
int m_origIdx = -1;
int m_dragOpenIdx = -1;
CommandWidgetContainer* m_prevDragOpen = nullptr;
int m_autoscrollTimer = -1;
int m_autoscrollDelta = 0;
QWidget* m_autoscrollSource = nullptr;
QMouseEvent* m_autoscrollEvent = nullptr;
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
void stopAutoscroll();
bool beginDrag(CommandWidget* widget);
void endDrag();
void cancelDrag();
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
void moveDrag(CommandWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
void insertDragout();
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
void deleteCommand(int index);
void reindex();
void clear();
public:
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void timerEvent(QTimerEvent* event);
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
~SoundMacroListing() override;
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void timerEvent(QTimerEvent* event) override;
};
class CatalogueItem : public QWidget
{
Q_OBJECT
amuse::SoundMacro::CmdOp m_op;
QLabel m_iconLab;
QLabel m_label;
class CatalogueItem : public QWidget {
Q_OBJECT
amuse::SoundMacro::CmdOp m_op;
QLabel m_iconLab;
QLabel m_label;
public:
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name,
const QString& doc, QWidget* parent = Q_NULLPTR);
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
QString getText() const { return m_label.text(); }
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name, const QString& doc,
QWidget* parent = Q_NULLPTR);
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
~CatalogueItem() override;
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
QString getText() const { return m_label.text(); }
};
class SoundMacroCatalogue : public QTreeWidget
{
Q_OBJECT
class SoundMacroCatalogue : public QTreeWidget {
Q_OBJECT
public:
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
};
class SoundMacroEditor : public EditorWidget
{
Q_OBJECT
friend class SoundMacroCatalogue;
friend class FieldSoundMacroStep;
QSplitter* m_splitter;
SoundMacroListing* m_listing;
SoundMacroCatalogue* m_catalogue;
CommandWidget* m_draggedCmd = nullptr;
CatalogueItem* m_draggedItem = nullptr;
FieldSoundMacroStep* m_targetField = nullptr;
QPoint m_draggedPt;
int m_dragInsertIdx = -1;
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
void beginStepTarget(FieldSoundMacroStep* stepField);
void endStepTarget();
public:
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
class SoundMacroEditor : public EditorWidget {
Q_OBJECT
friend class SoundMacroCatalogue;
friend class FieldSoundMacroStep;
QSplitter* m_splitter;
SoundMacroListing* m_listing;
SoundMacroCatalogue* m_catalogue;
CommandWidget* m_draggedCmd = nullptr;
CatalogueItem* m_draggedItem = nullptr;
FieldSoundMacroStep* m_targetField = nullptr;
QPoint m_draggedPt;
int m_dragInsertIdx = -1;
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
void beginStepTarget(FieldSoundMacroStep* stepField);
void endStepTarget();
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event);
public:
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
~SoundMacroEditor() override;
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
public slots:
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
};
#endif //AMUSE_SOUND_MACRO_EDITOR_HPP

View File

@@ -1,113 +1,103 @@
#include "StatusBarWidget.hpp"
FXButton::FXButton(QWidget* parent)
: QPushButton(parent)
{
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
#include <cmath>
FXButton::FXButton(QWidget* parent) : QPushButton(parent) {
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
}
StatusBarWidget::StatusBarWidget(QWidget* parent)
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal),
m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal)
{
addWidget(&m_normalMessage);
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
m_killButton.setVisible(false);
m_killButton.setToolTip(tr("Immediately kill active voices"));
m_voiceCount.setVisible(false);
m_volumeIcons[0] = QIcon(QStringLiteral(":/icons/IconVolume0.svg"));
m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1.svg"));
m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2.svg"));
m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3.svg"));
m_aIcon.setFixedSize(16, 16);
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
QString aTip = tr("Aux A send level for all voices");
m_aIcon.setToolTip(aTip);
m_aSlider.setRange(0, 100);
m_aSlider.setFixedWidth(100);
m_aSlider.setToolTip(aTip);
m_bIcon.setFixedSize(16, 16);
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
QString bTip = tr("Aux B send level for all voices");
m_bIcon.setToolTip(bTip);
m_bSlider.setRange(0, 100);
m_bSlider.setFixedWidth(100);
m_bSlider.setToolTip(bTip);
m_volumeIcon.setFixedSize(16, 16);
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
QString volTip = tr("Master volume level");
m_volumeIcon.setToolTip(volTip);
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
m_volumeSlider.setRange(0, 100);
m_volumeSlider.setFixedWidth(100);
m_volumeSlider.setToolTip(volTip);
addPermanentWidget(&m_voiceCount);
addPermanentWidget(&m_killButton);
addPermanentWidget(&m_fxButton);
addPermanentWidget(&m_aIcon);
addPermanentWidget(&m_aSlider);
addPermanentWidget(&m_bIcon);
addPermanentWidget(&m_bSlider);
addPermanentWidget(&m_volumeIcon);
addPermanentWidget(&m_volumeSlider);
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal), m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal) {
addWidget(&m_normalMessage);
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
m_killButton.setVisible(false);
m_killButton.setToolTip(tr("Immediately kill active voices"));
m_voiceCount.setVisible(false);
for (size_t i = 0; i < m_volumeIcons.size(); i++) {
m_volumeIcons[i] = QIcon(QStringLiteral(":/icons/IconVolume%1.svg").arg(i));
}
m_aIcon.setFixedSize(16, 16);
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
QString aTip = tr("Aux A send level for all voices");
m_aIcon.setToolTip(aTip);
m_aSlider.setRange(0, 100);
m_aSlider.setFixedWidth(100);
m_aSlider.setToolTip(aTip);
m_bIcon.setFixedSize(16, 16);
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
QString bTip = tr("Aux B send level for all voices");
m_bIcon.setToolTip(bTip);
m_bSlider.setRange(0, 100);
m_bSlider.setFixedWidth(100);
m_bSlider.setToolTip(bTip);
m_volumeIcon.setFixedSize(16, 16);
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
QString volTip = tr("Master volume level");
m_volumeIcon.setToolTip(volTip);
connect(&m_volumeSlider, qOverload<int>(&QSlider::valueChanged), this, &StatusBarWidget::volumeChanged);
m_volumeSlider.setRange(0, 100);
m_volumeSlider.setFixedWidth(100);
m_volumeSlider.setToolTip(volTip);
addPermanentWidget(&m_voiceCount);
addPermanentWidget(&m_killButton);
addPermanentWidget(&m_fxButton);
addPermanentWidget(&m_aIcon);
addPermanentWidget(&m_aSlider);
addPermanentWidget(&m_bIcon);
addPermanentWidget(&m_bSlider);
addPermanentWidget(&m_volumeIcon);
addPermanentWidget(&m_volumeSlider);
}
void StatusBarWidget::setVoiceCount(int voices)
{
if (voices != m_cachedVoiceCount)
{
m_voiceCount.setText(QString::number(voices));
m_cachedVoiceCount = voices;
setKillVisible(voices != 0);
}
StatusBarWidget::~StatusBarWidget() = default;
void StatusBarWidget::setVoiceCount(int voices) {
if (voices != m_cachedVoiceCount) {
m_voiceCount.setText(QString::number(voices));
m_cachedVoiceCount = voices;
setKillVisible(voices != 0);
}
}
void StatusBarWidget::volumeChanged(int vol)
{
int idx = int(std::round(vol * (3.f / 100.f)));
if (idx != m_lastVolIdx)
{
m_lastVolIdx = idx;
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
}
void StatusBarWidget::volumeChanged(int vol) {
const int idx = int(std::round(vol * (3.f / 100.f)));
if (idx == m_lastVolIdx) {
return;
}
m_lastVolIdx = idx;
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
}
void StatusBarFocus::setMessage(const QString& message)
{
m_message = message;
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
if (widget->m_curFocus == this)
{
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
void StatusBarFocus::setMessage(const QString& message) {
m_message = message;
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
if (widget->m_curFocus == this) {
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
}
}
void StatusBarFocus::enter()
{
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
widget->m_curFocus = this;
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
void StatusBarFocus::enter() {
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
widget->m_curFocus = this;
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
}
void StatusBarFocus::exit()
{
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
if (widget->m_curFocus == this)
{
widget->clearMessage();
widget->m_curFocus = nullptr;
}
void StatusBarFocus::exit() {
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
if (widget->m_curFocus == this) {
widget->clearMessage();
widget->m_curFocus = nullptr;
}
}
}

View File

@@ -1,77 +1,90 @@
#ifndef AMUSE_STATUSBAR_WIDGET_HPP
#define AMUSE_STATUSBAR_WIDGET_HPP
#pragma once
#include <array>
#include <QStatusBar>
#include <QLabel>
#include <QMouseEvent>
#include <QPushButton>
#include <QSlider>
#include <QMouseEvent>
#include <cmath>
#include <QStatusBar>
class StatusBarFocus;
class FXButton : public QPushButton
{
Q_OBJECT
class FXButton : public QPushButton {
Q_OBJECT
public:
explicit FXButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
explicit FXButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
void focusOutEvent(QFocusEvent* event) override { event->ignore(); }
void keyPressEvent(QKeyEvent* event) override { event->ignore(); }
};
class StatusBarWidget : public QStatusBar
{
friend class StatusBarFocus;
Q_OBJECT
QLabel m_normalMessage;
QPushButton m_killButton;
FXButton m_fxButton;
QIcon m_volumeIcons[4];
QLabel m_volumeIcon;
QSlider m_volumeSlider;
QLabel m_aIcon;
QSlider m_aSlider;
QLabel m_bIcon;
QSlider m_bSlider;
int m_lastVolIdx = 0;
QLabel m_voiceCount;
int m_cachedVoiceCount = -1;
StatusBarFocus* m_curFocus = nullptr;
void setKillVisible(bool vis) { m_killButton.setVisible(vis); m_voiceCount.setVisible(vis); }
class StatusBarWidget : public QStatusBar {
friend class StatusBarFocus;
Q_OBJECT
QLabel m_normalMessage;
QPushButton m_killButton;
FXButton m_fxButton;
std::array<QIcon, 4> m_volumeIcons;
QLabel m_volumeIcon;
QSlider m_volumeSlider;
QLabel m_aIcon;
QSlider m_aSlider;
QLabel m_bIcon;
QSlider m_bSlider;
int m_lastVolIdx = 0;
QLabel m_voiceCount;
int m_cachedVoiceCount = -1;
StatusBarFocus* m_curFocus = nullptr;
void setKillVisible(bool vis) {
m_killButton.setVisible(vis);
m_voiceCount.setVisible(vis);
}
public:
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
void setVoiceCount(int voices);
void connectKillClicked(const QObject* receiver, const char* method)
{ connect(&m_killButton, SIGNAL(clicked(bool)), receiver, method); }
void connectFXPressed(const QObject* receiver, const char* method)
{ connect(&m_fxButton, SIGNAL(pressed()), receiver, method); }
void setFXDown(bool down) { m_fxButton.setDown(down); }
void connectVolumeSlider(const QObject* receiver, const char* method)
{ connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method); }
void connectASlider(const QObject* receiver, const char* method)
{ connect(&m_aSlider, SIGNAL(valueChanged(int)), receiver, method); }
void connectBSlider(const QObject* receiver, const char* method)
{ connect(&m_bSlider, SIGNAL(valueChanged(int)), receiver, method); }
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
~StatusBarWidget() override;
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
void setVoiceCount(int voices);
template <typename Receiver>
void connectKillClicked(const Receiver* receiver, void (Receiver::*method)()) {
connect(&m_killButton, &QPushButton::clicked, receiver, method);
}
template <typename Receiver>
void connectFXPressed(const Receiver* receiver, void (Receiver::*method)()) {
connect(&m_fxButton, &FXButton::pressed, receiver, method);
}
void setFXDown(bool down) { m_fxButton.setDown(down); }
template <typename Receiver>
void connectVolumeSlider(const Receiver* receiver, void (Receiver::*method)(int)) {
connect(&m_volumeSlider, qOverload<int>(&QSlider::valueChanged), receiver, method);
}
template <typename Receiver>
void connectASlider(const Receiver* receiver, void (Receiver::*method)(int)) {
connect(&m_aSlider, qOverload<int>(&QSlider::valueChanged), receiver, method);
}
template <typename Receiver>
void connectBSlider(const Receiver* receiver, void (Receiver::*method)(int)) {
connect(&m_bSlider, qOverload<int>(&QSlider::valueChanged), receiver, method);
}
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
private slots:
void volumeChanged(int vol);
void volumeChanged(int vol);
};
class StatusBarFocus : public QObject
{
Q_OBJECT
QString m_message;
class StatusBarFocus : public QObject {
Q_OBJECT
QString m_message;
public:
explicit StatusBarFocus(StatusBarWidget* statusWidget)
: QObject(statusWidget) {}
~StatusBarFocus() { exit(); }
void setMessage(const QString& message);
void enter();
void exit();
explicit StatusBarFocus(StatusBarWidget* statusWidget) : QObject(statusWidget) {}
~StatusBarFocus() override { exit(); }
void setMessage(const QString& message);
void enter();
void exit();
};
#endif //AMUSE_STATUSBAR_WIDGET_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -1,205 +1,215 @@
#ifndef AMUSE_STUDIO_SETUP_WIDGET_HPP
#define AMUSE_STUDIO_SETUP_WIDGET_HPP
#pragma once
#include <array>
#include <string>
#include "EditorWidget.hpp"
#include <QWidget>
#include <QTreeWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QLayoutItem>
#include <QStaticText>
#include <QMouseEvent>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QScrollArea>
#include <QSplitter>
#include "amuse/Studio.hpp"
#include <QStaticText>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <QWidget>
#include "EditorWidget.hpp"
namespace amuse {
class EffectBaseTypeless;
class Studio;
class Submix;
enum class EffectType;
} // namespace amuse
class EffectListing;
struct EffectIntrospection
{
struct Field
{
enum class Type
{
Invalid,
UInt32,
UInt32x8,
Float
};
Type m_tp;
std::string_view m_name;
float m_min, m_max, m_default;
};
amuse::EffectType m_tp;
std::string_view m_name;
std::string_view m_description;
Field m_fields[7];
};
struct EffectIntrospection;
class Uint32X8Popup : public QFrame
{
Q_OBJECT
FieldSlider* m_sliders[8];
class Uint32X8Popup : public QFrame {
Q_OBJECT
public:
explicit Uint32X8Popup(int min, int max, QWidget* parent = Q_NULLPTR);
void setValue(int chanIdx, int val);
static constexpr int NumSliders = 8;
explicit Uint32X8Popup(int min, int max, QWidget* parent = Q_NULLPTR);
~Uint32X8Popup() override;
void setValue(int chanIdx, int val);
private slots:
void doValueChanged(int val);
void doValueChanged(int val);
signals:
void valueChanged(int chanIdx, int val);
};
void valueChanged(int chanIdx, int val);
class Uint32X8Button : public QPushButton
{
Q_OBJECT
Uint32X8Popup* m_popup;
public:
explicit Uint32X8Button(int min, int max, QWidget* parent = Q_NULLPTR);
void paintEvent(QPaintEvent* event);
Uint32X8Popup* popup() const { return m_popup; }
QStyleOptionComboBox comboStyleOption() const;
private slots:
void onPressed();
};
class EffectWidget : public QWidget
{
Q_OBJECT
friend class EffectListing;
QFont m_numberFont;
QLabel m_titleLabel;
ListingDeleteButton m_deleteButton;
QStaticText m_numberText;
int m_index = -1;
amuse::EffectBaseTypeless* m_effect;
const EffectIntrospection* m_introspection;
void setIndex(int index);
private slots:
void numChanged(int);
void numChanged(double);
void chanNumChanged(int, int);
void deleteClicked();
private:
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type);
public:
EffectListing* getParent() const;
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect);
explicit EffectWidget(QWidget* parent, amuse::EffectType op);
void paintEvent(QPaintEvent* event);
QString getText() const { return m_titleLabel.text(); }
std::array<FieldSlider*, NumSliders> m_sliders;
};
class EffectWidgetContainer : public QWidget
{
Q_OBJECT
friend class EffectListing;
EffectWidget* m_effectWidget;
QPropertyAnimation* m_animation = nullptr;
void animateOpen();
void animateClosed();
void snapOpen();
void snapClosed();
class Uint32X8Button : public QPushButton {
Q_OBJECT
Uint32X8Popup* m_popup;
public:
explicit Uint32X8Button(int min, int max, QWidget* parent = Q_NULLPTR);
~Uint32X8Button() override;
void paintEvent(QPaintEvent* event) override;
Uint32X8Popup* popup() const { return m_popup; }
QStyleOptionComboBox comboStyleOption() const;
private slots:
void animationDestroyed();
public:
template <class..._Args>
EffectWidgetContainer(QWidget* parent, _Args&&... args);
void onPressed();
};
class EffectListing : public QWidget
{
Q_OBJECT
friend class EffectWidget;
friend class StudioSetupWidget;
amuse::Submix* m_submix = nullptr;
QVBoxLayout* m_layout;
QLayoutItem* m_dragItem = nullptr;
int m_origIdx = -1;
int m_dragOpenIdx = -1;
EffectWidgetContainer* m_prevDragOpen = nullptr;
int m_autoscrollTimer = -1;
int m_autoscrollDelta = 0;
QWidget* m_autoscrollSource = nullptr;
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
void stopAutoscroll();
bool beginDrag(EffectWidget* widget);
void endDrag();
void cancelDrag();
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
void moveDrag(EffectWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
void insertDragout();
void insert(amuse::EffectType type, const QString& text);
void deleteEffect(int index);
void reindex();
void clear();
class EffectWidget : public QWidget {
Q_OBJECT
friend class EffectListing;
QFont m_numberFont;
QLabel m_titleLabel;
ListingDeleteButton m_deleteButton;
QStaticText m_numberText;
int m_index = -1;
amuse::EffectBaseTypeless* m_effect;
const EffectIntrospection* m_introspection;
void setIndex(int index);
private slots:
void numChanged(int);
void numChanged(double);
void chanNumChanged(int, int);
void deleteClicked();
private:
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type);
~EffectWidget() override;
public:
explicit EffectListing(QWidget* parent = Q_NULLPTR);
bool loadData(amuse::Submix* submix);
void unloadData();
void timerEvent(QTimerEvent* event);
EffectListing* getParent() const;
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect);
explicit EffectWidget(QWidget* parent, amuse::EffectType op);
void paintEvent(QPaintEvent* event) override;
QString getText() const { return m_titleLabel.text(); }
};
class EffectCatalogueItem : public QWidget
{
Q_OBJECT
amuse::EffectType m_type;
QLabel m_iconLab;
QLabel m_label;
class EffectWidgetContainer : public QWidget {
Q_OBJECT
friend class EffectListing;
EffectWidget* m_effectWidget;
QPropertyAnimation* m_animation = nullptr;
void animateOpen();
void animateClosed();
void snapOpen();
void snapClosed();
private slots:
void animationDestroyed();
public:
explicit EffectCatalogueItem(amuse::EffectType type, const QString& name,
const QString& doc, QWidget* parent = Q_NULLPTR);
explicit EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent = Q_NULLPTR);
amuse::EffectType getType() const { return m_type; }
QString getText() const { return m_label.text(); }
template <class... _Args>
EffectWidgetContainer(QWidget* parent, _Args&&... args);
~EffectWidgetContainer() override;
};
class EffectCatalogue : public QTreeWidget
{
Q_OBJECT
class EffectListing : public QWidget {
Q_OBJECT
friend class EffectWidget;
friend class StudioSetupWidget;
amuse::Submix* m_submix = nullptr;
QVBoxLayout* m_layout;
QLayoutItem* m_dragItem = nullptr;
int m_origIdx = -1;
int m_dragOpenIdx = -1;
EffectWidgetContainer* m_prevDragOpen = nullptr;
int m_autoscrollTimer = -1;
int m_autoscrollDelta = 0;
QWidget* m_autoscrollSource = nullptr;
QMouseEvent* m_autoscrollEvent = nullptr;
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
void stopAutoscroll();
bool beginDrag(EffectWidget* widget);
void endDrag();
void cancelDrag();
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
void moveDrag(EffectWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
void insertDragout();
void insert(amuse::EffectType type, const QString& text);
void deleteEffect(int index);
void reindex();
void clear();
public:
explicit EffectCatalogue(QWidget* parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
explicit EffectListing(QWidget* parent = Q_NULLPTR);
~EffectListing() override;
bool loadData(amuse::Submix* submix);
void unloadData();
void timerEvent(QTimerEvent* event) override;
};
class StudioSetupWidget : public QWidget
{
Q_OBJECT
friend class EffectCatalogue;
QSplitter* m_splitter;
QTabWidget* m_tabs;
EffectListing* m_listing[2];
EffectCatalogue* m_catalogue;
EffectWidget* m_draggedCmd = nullptr;
EffectCatalogueItem* m_draggedItem = nullptr;
QPoint m_draggedPt;
int m_dragInsertIdx = -1;
EffectListing* getCurrentListing() const;
void beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt);
void beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
class EffectCatalogueItem : public QWidget {
Q_OBJECT
amuse::EffectType m_type;
QLabel m_iconLab;
QLabel m_label;
public:
explicit StudioSetupWidget(QWidget* parent = Q_NULLPTR);
bool loadData(amuse::Studio* studio);
void unloadData();
explicit EffectCatalogueItem(amuse::EffectType type, const QString& name, const QString& doc,
QWidget* parent = Q_NULLPTR);
explicit EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent = Q_NULLPTR);
~EffectCatalogueItem() override;
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event);
amuse::EffectType getType() const { return m_type; }
QString getText() const { return m_label.text(); }
};
void hideEvent(QHideEvent* event);
void showEvent(QShowEvent* event);
void updateWindowPosition();
class EffectCatalogue : public QTreeWidget {
Q_OBJECT
public:
explicit EffectCatalogue(QWidget* parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
};
class StudioSetupWidget : public QWidget {
Q_OBJECT
friend class EffectCatalogue;
QSplitter* m_splitter;
QTabWidget* m_tabs;
std::array<EffectListing*, 2> m_listing{};
EffectCatalogue* m_catalogue;
EffectWidget* m_draggedCmd = nullptr;
EffectCatalogueItem* m_draggedItem = nullptr;
QPoint m_draggedPt;
int m_dragInsertIdx = -1;
EffectListing* getCurrentListing() const;
void beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt);
void beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
public:
explicit StudioSetupWidget(QWidget* parent = Q_NULLPTR);
~StudioSetupWidget() override;
bool loadData(amuse::Studio* studio);
void unloadData();
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;
void updateWindowPosition();
public slots:
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
signals:
void hidden();
void shown();
void hidden();
void shown();
};
#endif // AMUSE_STUDIO_SETUP_WIDGET_HPP

View File

@@ -17,106 +17,104 @@ void MacOSSetDarkAppearance();
extern "C" const uint8_t MAINICON_QT[];
static QIcon MakeAppIcon()
{
QIcon ret;
static QIcon MakeAppIcon() {
QIcon ret;
const uint8_t* ptr = MAINICON_QT;
for (int i = 0; i < 6; ++i)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(ptr);
ptr += 4;
const uint8_t* ptr = MAINICON_QT;
for (int i = 0; i < 6; ++i) {
uint32_t size = *reinterpret_cast<const uint32_t*>(ptr);
ptr += 4;
QPixmap pm;
pm.loadFromData(ptr, size);
ret.addPixmap(pm);
ptr += size;
}
QPixmap pm;
pm.loadFromData(ptr, size);
ret.addPixmap(pm);
ptr += size;
}
return ret;
return ret;
}
/* This is for adapting the get*Name methods */
class BooInterface : public boo::IApplication
{
std::vector<boo::SystemString> m_args;
void _deletedWindow(boo::IWindow* window) {}
class BooInterface : public boo::IApplication {
std::vector<boo::SystemString> m_args;
void _deletedWindow(boo::IWindow* window) override {}
public:
EPlatformType getPlatformType() const { return EPlatformType::Qt; }
EPlatformType getPlatformType() const override { return EPlatformType::Qt; }
int run() { return 0; }
boo::SystemStringView getUniqueName() const { return _S("amuse-gui"sv); }
boo::SystemStringView getFriendlyName() const { return _S("Amuse"sv); }
boo::SystemStringView getProcessName() const { return _S("amuse-gui"sv); }
const std::vector<boo::SystemString>& getArgs() const { return m_args; }
int run() override { return 0; }
boo::SystemStringView getUniqueName() const override { return _SYS_STR("amuse-gui"sv); }
boo::SystemStringView getFriendlyName() const override { return _SYS_STR("Amuse"sv); }
boo::SystemStringView getProcessName() const override { return _SYS_STR("amuse-gui"sv); }
const std::vector<boo::SystemString>& getArgs() const override { return m_args; }
/* Constructors/initializers for sub-objects */
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) { return {}; }
/* Constructors/initializers for sub-objects */
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) override { return {}; }
};
MainWindow* g_MainWindow = nullptr;
int main(int argc, char* argv[])
{
QApplication::setAttribute(Qt::AA_Use96Dpi);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
int main(int argc, char* argv[]) {
QApplication::setAttribute(Qt::AA_Use96Dpi);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
QApplication a(argc, argv);
QApplication::setWindowIcon(MakeAppIcon());
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create(QStringLiteral("Fusion"))));
QApplication a(argc, argv);
QApplication::setWindowIcon(MakeAppIcon());
a.setOrganizationName("AxioDL");
a.setApplicationName("Amuse");
a.setOrganizationName(QStringLiteral("AxioDL"));
a.setApplicationName(QStringLiteral("Amuse"));
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, QColor(53,53,53));
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, QColor(42,42,42));
darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25,25,25,53));
darkPalette.setColor(QPalette::AlternateBase, QColor(53,53,53));
darkPalette.setColor(QPalette::ToolTipBase, QColor(42,42,42));
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255,255,255,120));
darkPalette.setColor(QPalette::Disabled, QPalette::Light, QColor(0,0,0,0));
darkPalette.setColor(QPalette::Button, QColor(53,53,53));
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53,53,53,53));
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255,255,255,120));
darkPalette.setColor(QPalette::BrightText, Qt::red);
darkPalette.setColor(QPalette::Link, QColor(42,130,218));
darkPalette.setColor(QPalette::Highlight, QColor(42,130,218));
darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(42,130,218,53));
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255,255,255,120));
a.setPalette(darkPalette);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, QColor(53, 53, 53));
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, QColor(42, 42, 42));
darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25, 25, 25, 53));
darkPalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
darkPalette.setColor(QPalette::ToolTipBase, QColor(42, 42, 42));
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255, 255, 255, 120));
darkPalette.setColor(QPalette::Disabled, QPalette::Light, QColor(0, 0, 0, 0));
darkPalette.setColor(QPalette::Button, QColor(53, 53, 53));
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53, 53, 53, 53));
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255, 255, 255, 120));
darkPalette.setColor(QPalette::BrightText, Qt::red);
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(42, 130, 218, 53));
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255, 255, 255, 120));
a.setPalette(darkPalette);
#ifdef __APPLE__
MacOSSetDarkAppearance();
MacOSSetDarkAppearance();
#endif
logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions();
BooInterface booApp;
boo::APP = &booApp;
BooInterface booApp;
boo::APP = &booApp;
Q_INIT_RESOURCE(translation_res);
QTranslator translator;
if (translator.load(QLocale(), QLatin1String("lang"), QLatin1String("_"), QLatin1String(":/translations")))
a.installTranslator(&translator);
Q_INIT_RESOURCE(translation_res);
QTranslator translator;
if (translator.load(QLocale(), QStringLiteral("lang"), QStringLiteral("_"), QStringLiteral(":/translations"))) {
a.installTranslator(&translator);
}
MainWindow w;
g_MainWindow = &w;
w.show();
MainWindow w;
g_MainWindow = &w;
w.show();
QCommandLineParser parser;
parser.process(a);
QStringList args = parser.positionalArguments();
if (!args.empty())
w.openProject(args.back());
QCommandLineParser parser;
parser.process(a);
QStringList args = parser.positionalArguments();
if (!args.empty())
w.openProject(args.back());
return a.exec();
return a.exec();
}

View File

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 924 B

After

Width:  |  Height:  |  Size: 924 B

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,17 +1,17 @@
include_directories(${LIBPNG_INCLUDE_DIR})
add_executable(amuse-mkqticon mkqticon.c)
target_link_libraries(amuse-mkqticon ${PNG_LIB} ${ZLIB_LIBRARIES})
target_link_libraries(amuse-mkqticon ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
target_include_directories(amuse-mkqticon PRIVATE ${PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
macro(declare_qticon_target)
add_custom_command(OUTPUT ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin
COMMAND $<TARGET_FILE:amuse-mkqticon>
ARGS ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin
DEPENDS
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/128x128/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/64x64/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/48x48/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/32x32/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/16x16/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/128x128/apps/amuse-gui.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/64x64/apps/amuse-gui.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/48x48/apps/amuse-gui.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/32x32/apps/amuse-gui.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/16x16/apps/amuse-gui.png
WORKING_DIRECTORY ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop
COMMENT "Generating mainicon_qt.bin")
bintoc(mainicon_qt.cpp ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin MAINICON_QT)

View File

@@ -1,9 +1,10 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Name=Amuse
GenericName=MusyX Game Audio Editor
Comment=Edit Audio Data of MusyX Sound Groups
Exec=amuse-gui
Icon=amuse
Icon=amuse-gui
Terminal=false
Type=Application
Categories=Audio

View File

@@ -36,7 +36,7 @@ int main(int argc, char* argv[])
printf("Rendering main icon @%dx%d\n", *d, *d);
fflush(stdout);
snprintf(command, 2048, "%dx%d/apps/amuse.png", *d, *d);
snprintf(command, 2048, "%dx%d/apps/amuse-gui.png", *d, *d);
FILE* fp = fopen(command, "rb");
if (!fp)
{

File diff suppressed because it is too large Load Diff

View File

@@ -41,6 +41,51 @@ other audio applications to route their MIDI messages to. This enables
tracker, drum machine, and DAW applications to produce sampled audio
using Amuse directly.
### Converter Usage
`amuseconv <in-path> <out-path> [gcn|n64|pc]`
**Note:** `amuseconv` currently only supports SNG-to-MIDI conversion and vice-versa. To batch-extract MIDIs from one of the game files listed below, run `amuseconv <data-file> <destination-dir>`
### Player Usage
`amuseplay <data-file> [<songs-file>]`
_or_
Drag-n-drop data file on amuseplay executable _(Windows and many freedesktop file managers support this)_
### Song Renderer Usage
`amuserender <data-file> [<songs-file>] [-r <sample-rate-out>] [-v <volume 0.0-1.0>]`
_or_
Drag-n-drop data file on amuseplay executable _(Windows and many freedesktop file managers support this)_
**Note:** .wav file will be emitted at `<group-name>-<song-name>.wav`. If `-r` option is not specified, rate will default to 32KHz
### Currently Supported Game Containers
- _Indiana Jones and the Infernal Machine_ (N64) `N64 ROM file`
- _Metroid Prime_ (GCN) `AudioGrp.pak` `MidiData.pak`
- _Pass `AudioGrp.pak MidiData.pak` for listening to SongGroup 53 (sequenced ship sounds)_
- _Pass `AudioGrp.pak` for listening to any SFXGroup_
- _Metroid Prime 2: Echoes_ (GCN) `AudioGrp.pak`
- _Paper Mario: The Thousand Year Door_ (GCN) `pmario.proj` `pmario.slib`
- _Pass `pmario.proj pmario.slib` for listening to SongGroups (sequenced SFX events)_
- _Pass `pmario.proj` for listening to any SFXGroup_
- _Star Fox Adventures_ (GCN) `starfoxm.pro` `starfoxs.pro` `midi.wad`
- _Pass `starfoxm.pro midi.wad` for listening to SongGroup 0 songs only (music)_
- _Pass `starfoxs.pro midi.wad` for listening to other SongGroups (sequenced ambience)_
- _Pass `starfoxs.pro` for listening to any SFXGroup_
- _Star Wars Episode I: Battle for Naboo_ (N64) `N64 ROM file`
- _Star Wars: Rogue Squadron_ (N64) `N64 ROM file` (PC) `data.dat`
- _Star Wars Rogue Squadron II: Rogue Leader_ (GCN) `data.dat`
- _Star Wars Rogue Squadron III: Rebel Strike_ (GCN) `data.dat`
- Any other game with raw `.proj` `.pool` `.sdir` `.samp` files
- _Pass any one of them to Amuse, all in the same directory_
### Windows 7 Compatibility
Installing the MSVC++ 2015 runtime may be required if you haven't already installed/updated it:
https://www.microsoft.com/en-us/download/details.aspx?id=48145
#### Library
The Amuse API exposes full interactivity between a client application

View File

@@ -3,8 +3,7 @@
#include <Shellapi.h>
#include <Shlwapi.h>
namespace amuse
{
namespace amuse {
static const wchar_t* const GMNames[128] = {L"Acoustic Grand Piano",
L"Bright Acoustic Piano",
@@ -154,434 +153,379 @@ static const wchar_t* const GMPercNames[128] = {
L"Hi Wood Block", L"Low Wood Block", L"Mute Cuica", L"Open Cuica", L"Mute Triangle",
L"Open Triangle"};
bool AudioGroupDataCollection::loadProj()
{
std::wstring path = m_path + L"\\proj";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_projData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadProj() {
std::wstring path = m_path + L"\\proj";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_projData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadPool()
{
std::wstring path = m_path + L"\\pool";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_poolData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadPool() {
std::wstring path = m_path + L"\\pool";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_poolData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadSdir()
{
std::wstring path = m_path + L"\\sdir";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sdirData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadSdir() {
std::wstring path = m_path + L"\\sdir";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sdirData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadSamp()
{
std::wstring path = m_path + L"\\samp";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sampData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadSamp() {
std::wstring path = m_path + L"\\samp";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sampData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadMeta()
{
std::wstring path = m_path + L"\\meta";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::experimental::optional<MetaData>& ret = m_metaData;
ret.emplace(r);
return ret.operator bool();
bool AudioGroupDataCollection::loadMeta() {
std::wstring path = m_path + L"\\meta";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::optional<MetaData>& ret = m_metaData;
ret.emplace(r);
return ret.operator bool();
}
AudioGroupDataCollection::AudioGroupDataCollection(std::wstring_view path, std::wstring_view name)
: m_path(path), m_name(name)
{
: m_path(path), m_name(name) {}
bool AudioGroupDataCollection::_attemptLoad() {
if (m_metaData && m_loadedData && m_loadedGroup)
return true;
if (!loadProj())
return false;
if (!loadPool())
return false;
if (!loadSdir())
return false;
if (!loadSamp())
return false;
if (!loadMeta())
return false;
return _indexData();
}
bool AudioGroupDataCollection::_attemptLoad()
{
if (m_metaData && m_loadedData && m_loadedGroup)
return true;
if (!loadProj())
return false;
if (!loadPool())
return false;
if (!loadSdir())
return false;
if (!loadSamp())
return false;
if (!loadMeta())
return false;
bool AudioGroupDataCollection::_indexData() {
switch (m_metaData->fmt) {
case amuse::DataFormat::GCN:
default:
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(), m_sdirData.data(),
m_sdirData.size(), m_sampData.data(), m_sampData.size(), amuse::GCNDataTag{});
break;
case amuse::DataFormat::N64:
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(), m_sdirData.data(),
m_sdirData.size(), m_sampData.data(), m_sampData.size(), m_metaData->absOffs,
amuse::N64DataTag{});
break;
case amuse::DataFormat::PC:
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(), m_sdirData.data(),
m_sdirData.size(), m_sampData.data(), m_sampData.size(), m_metaData->absOffs,
amuse::PCDataTag{});
break;
}
return _indexData();
return m_loadedData.operator bool();
}
bool AudioGroupDataCollection::_indexData()
{
switch (m_metaData->fmt)
void AudioGroupDataCollection::addToEngine(amuse::Engine& engine) {
m_loadedGroup = engine.addAudioGroup(*m_loadedData);
m_groupTokens.clear();
if (m_loadedGroup) {
m_groupTokens.reserve(m_loadedGroup->getProj().songGroups().size() + m_loadedGroup->getProj().sfxGroups().size());
{
case amuse::DataFormat::GCN:
default:
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
amuse::GCNDataTag{});
break;
case amuse::DataFormat::N64:
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
m_metaData->absOffs, amuse::N64DataTag{});
break;
case amuse::DataFormat::PC:
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
m_metaData->absOffs, amuse::PCDataTag{});
break;
const auto& songGroups = m_loadedGroup->getProj().songGroups();
std::map<int, const amuse::SongGroupIndex*> sortGroups;
for (const auto& pair : songGroups)
sortGroups[pair.first] = &pair.second;
for (const auto& pair : sortGroups)
m_groupTokens.emplace_back(pair.first, pair.second);
}
return m_loadedData.operator bool();
}
void AudioGroupDataCollection::addToEngine(amuse::Engine& engine)
{
m_loadedGroup = engine.addAudioGroup(*m_loadedData);
m_groupTokens.clear();
if (m_loadedGroup)
{
m_groupTokens.reserve(m_loadedGroup->getProj().songGroups().size() +
m_loadedGroup->getProj().sfxGroups().size());
{
const auto& songGroups = m_loadedGroup->getProj().songGroups();
std::map<int, const amuse::SongGroupIndex*> sortGroups;
for (const auto& pair : songGroups)
sortGroups[pair.first] = &pair.second;
for (const auto& pair : sortGroups)
m_groupTokens.emplace_back(pair.first, pair.second);
}
{
const auto& sfxGroups = m_loadedGroup->getProj().sfxGroups();
std::map<int, const amuse::SFXGroupIndex*> sortGroups;
for (const auto& pair : sfxGroups)
sortGroups[pair.first] = &pair.second;
for (const auto& pair : sortGroups)
m_groupTokens.emplace_back(pair.first, pair.second);
}
const auto& sfxGroups = m_loadedGroup->getProj().sfxGroups();
std::map<int, const amuse::SFXGroupIndex*> sortGroups;
for (const auto& pair : sfxGroups)
sortGroups[pair.first] = &pair.second;
for (const auto& pair : sortGroups)
m_groupTokens.emplace_back(pair.first, pair.second);
}
}
}
void AudioGroupDataCollection::removeFromEngine(amuse::Engine& engine) const { engine.removeAudioGroup(*m_loadedData); }
AudioGroupCollection::AudioGroupCollection(std::wstring_view path, std::wstring_view name)
: m_path(path), m_name(name)
{
}
: m_path(path), m_name(name) {}
void AudioGroupCollection::addCollection(
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
{
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection)
{
std::wstring collectionPath = m_path + L'\\' + pair.first;
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection) {
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection) {
std::wstring collectionPath = m_path + L'\\' + pair.first;
amuse::IntrusiveAudioGroupData& dataIn = pair.second;
auto search = m_groups.find(pair.first);
if (search == m_groups.end())
{
search =
m_groups.emplace(pair.first, std::make_unique<AudioGroupDataCollection>(collectionPath, pair.first))
.first;
}
AudioGroupDataCollection& dataCollection = *search->second;
dataCollection.m_projData.resize(dataIn.getProjSize());
memmove(dataCollection.m_projData.data(), dataIn.getProj(), dataIn.getProjSize());
dataCollection.m_poolData.resize(dataIn.getPoolSize());
memmove(dataCollection.m_poolData.data(), dataIn.getPool(), dataIn.getPoolSize());
dataCollection.m_sdirData.resize(dataIn.getSdirSize());
memmove(dataCollection.m_sdirData.data(), dataIn.getSdir(), dataIn.getSdirSize());
dataCollection.m_sampData.resize(dataIn.getSampSize());
memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize());
dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true);
dataCollection._indexData();
amuse::IntrusiveAudioGroupData& dataIn = pair.second;
auto search = m_groups.find(pair.first);
if (search == m_groups.end()) {
search =
m_groups.emplace(pair.first, std::make_unique<AudioGroupDataCollection>(collectionPath, pair.first)).first;
}
AudioGroupDataCollection& dataCollection = *search->second;
dataCollection.m_projData.resize(dataIn.getProjSize());
memmove(dataCollection.m_projData.data(), dataIn.getProj(), dataIn.getProjSize());
dataCollection.m_poolData.resize(dataIn.getPoolSize());
memmove(dataCollection.m_poolData.data(), dataIn.getPool(), dataIn.getPoolSize());
dataCollection.m_sdirData.resize(dataIn.getSdirSize());
memmove(dataCollection.m_sdirData.data(), dataIn.getSdir(), dataIn.getSdirSize());
dataCollection.m_sampData.resize(dataIn.getSampSize());
memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize());
dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true);
dataCollection._indexData();
}
}
void AudioGroupCollection::update(AudioGroupFilePresenter& presenter)
{
std::wstring path = m_path + L"\\*";
void AudioGroupCollection::update(AudioGroupFilePresenter& presenter) {
std::wstring path = m_path + L"\\*";
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do
{
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do {
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::wstring nameStr(d.cFileName);
auto search = m_groups.find(nameStr);
if (search == m_groups.end())
{
search =
m_groups
.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
.first;
search->second->_attemptLoad();
}
}
} while (FindNextFileW(dir, &d));
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
std::wstring nameStr(d.cFileName);
auto search = m_groups.find(nameStr);
if (search == m_groups.end()) {
search =
m_groups.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
.first;
search->second->_attemptLoad();
}
}
} while (FindNextFileW(dir, &d));
FindClose(dir);
FindClose(dir);
}
void AudioGroupFilePresenter::update()
{
std::wstring path = m_backend.getUserDir() + L"\\*";
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>& theMap = m_audioGroupCollections;
void AudioGroupFilePresenter::update() {
std::wstring path = m_backend.getUserDir() + L"\\*";
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>& theMap = m_audioGroupCollections;
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do
{
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do {
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::wstring nameStr(d.cFileName);
auto search = theMap.find(nameStr);
if (search == theMap.end())
{
search = theMap
.emplace(nameStr, std::make_unique<AudioGroupCollection>(
m_backend.getUserDir() + L'\\' + nameStr, nameStr))
.first;
search->second->update(*this);
}
}
} while (FindNextFileW(dir, &d));
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
std::wstring nameStr(d.cFileName);
auto search = theMap.find(nameStr);
if (search == theMap.end()) {
search = theMap
.emplace(nameStr,
std::make_unique<AudioGroupCollection>(m_backend.getUserDir() + L'\\' + nameStr, nameStr))
.first;
search->second->update(*this);
}
}
} while (FindNextFileW(dir, &d));
FindClose(dir);
FindClose(dir);
}
void AudioGroupFilePresenter::addCollection(
std::wstring_view name, std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
{
std::wstring path = m_backend.getUserDir() + L'\\' + name;
AudioGroupCollection& insert =
*m_audioGroupCollections.emplace(name, std::make_unique<AudioGroupCollection>(path, name)).first->second;
CreateDirectory(insert.m_path.c_str(), nullptr);
insert.addCollection(std::move(collection));
std::wstring_view name, std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection) {
std::wstring path = m_backend.getUserDir() + L'\\' + name;
AudioGroupCollection& insert =
*m_audioGroupCollections.emplace(name, std::make_unique<AudioGroupCollection>(path, name)).first->second;
CreateDirectory(insert.m_path.c_str(), nullptr);
insert.addCollection(std::move(collection));
for (std::pair<const std::wstring, std::unique_ptr<AudioGroupDataCollection>>& pair : insert.m_groups)
{
std::wstring collectionPath = insert.m_path + L'\\' + pair.first;
CreateDirectory(collectionPath.c_str(), nullptr);
for (std::pair<const std::wstring, std::unique_ptr<AudioGroupDataCollection>>& pair : insert.m_groups) {
std::wstring collectionPath = insert.m_path + L'\\' + pair.first;
CreateDirectory(collectionPath.c_str(), nullptr);
FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb");
if (fp)
{
fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), fp);
fclose(fp);
}
fp = _wfopen((collectionPath + L"\\pool").c_str(), L"wb");
if (fp)
{
fwrite(pair.second->m_poolData.data(), 1, pair.second->m_poolData.size(), fp);
fclose(fp);
}
fp = _wfopen((collectionPath + L"\\sdir").c_str(), L"wb");
if (fp)
{
fwrite(pair.second->m_sdirData.data(), 1, pair.second->m_sdirData.size(), fp);
fclose(fp);
}
fp = _wfopen((collectionPath + L"\\samp").c_str(), L"wb");
if (fp)
{
fwrite(pair.second->m_sampData.data(), 1, pair.second->m_sampData.size(), fp);
fclose(fp);
}
fp = _wfopen((collectionPath + L"\\meta").c_str(), L"wb");
if (fp)
{
fwrite(&*pair.second->m_metaData, 1, sizeof(*pair.second->m_metaData), fp);
fclose(fp);
}
FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb");
if (fp) {
fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), fp);
fclose(fp);
}
}
void AudioGroupFilePresenter::removeCollection(unsigned idx)
{
if (idx < m_iteratorVec.size())
{
CollectionIterator& it = m_iteratorVec[idx];
std::wstring collectionPath = it->second->m_path + L'\0';
SHFILEOPSTRUCT op = {};
op.wFunc = FO_DELETE;
op.pFrom = collectionPath.c_str();
op.fFlags = FOF_NO_UI;
SHFileOperation(&op);
m_audioGroupCollections.erase(it);
fp = _wfopen((collectionPath + L"\\pool").c_str(), L"wb");
if (fp) {
fwrite(pair.second->m_poolData.data(), 1, pair.second->m_poolData.size(), fp);
fclose(fp);
}
}
void AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx)
{
TVINSERTSTRUCT ins = {};
ins.item.mask = TVIF_TEXT | TVIF_PARAM;
ins.hParent = colHandle;
ins.hInsertAfter = TVI_LAST;
m_iteratorVec.clear();
m_iteratorVec.reserve(m_groups.size());
for (auto it = m_groups.begin(); it != m_groups.end(); ++it)
{
ins.item.pszText = LPWSTR(it->first.c_str());
ins.item.lParam = LPARAM(0x80000000 | (parentIdx << 16) | m_iteratorVec.size());
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
if (editor.m_selCollectionIdx == parentIdx && editor.m_selFileIdx == m_iteratorVec.size())
editor.m_deferredCollectionSel = item;
m_iteratorVec.push_back(it);
fp = _wfopen((collectionPath + L"\\sdir").c_str(), L"wb");
if (fp) {
fwrite(pair.second->m_sdirData.data(), 1, pair.second->m_sdirData.size(), fp);
fclose(fp);
}
}
void AudioGroupFilePresenter::populateCollectionColumn(VSTEditor& editor)
{
TreeView_DeleteAllItems(editor.m_collectionTree);
TVINSERTSTRUCT ins = {};
ins.hParent = TVI_ROOT;
ins.hInsertAfter = TVI_LAST;
ins.item.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
m_iteratorVec.clear();
m_iteratorVec.reserve(m_audioGroupCollections.size());
for (auto it = m_audioGroupCollections.begin(); it != m_audioGroupCollections.end(); ++it)
{
ins.item.cChildren = it->second->m_groups.size() ? 1 : 0;
ins.item.pszText = LPWSTR(it->first.c_str());
ins.item.lParam = LPARAM(m_iteratorVec.size() << 16);
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
it->second->populateFiles(editor, item, m_iteratorVec.size());
if (editor.m_selCollectionIdx == m_iteratorVec.size() && editor.m_selFileIdx == -1)
editor.m_deferredCollectionSel = item;
m_iteratorVec.push_back(it);
fp = _wfopen((collectionPath + L"\\samp").c_str(), L"wb");
if (fp) {
fwrite(pair.second->m_sampData.data(), 1, pair.second->m_sampData.size(), fp);
fclose(fp);
}
fp = _wfopen((collectionPath + L"\\meta").c_str(), L"wb");
if (fp) {
fwrite(&*pair.second->m_metaData, 1, sizeof(*pair.second->m_metaData), fp);
fclose(fp);
}
}
}
void AudioGroupFilePresenter::populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx)
{
LVITEM item = {};
item.mask = LVIF_TEXT;
void AudioGroupFilePresenter::removeCollection(unsigned idx) {
if (idx < m_iteratorVec.size()) {
CollectionIterator& it = m_iteratorVec[idx];
std::wstring collectionPath = it->second->m_path + L'\0';
SHFILEOPSTRUCT op = {};
op.wFunc = FO_DELETE;
op.pFrom = collectionPath.c_str();
op.fFlags = FOF_NO_UI;
SHFileOperation(&op);
m_audioGroupCollections.erase(it);
}
}
ListView_DeleteAllItems(editor.m_groupListView);
ListView_DeleteAllItems(editor.m_pageListView);
if (collectionIdx < m_iteratorVec.size())
{
CollectionIterator& it = m_iteratorVec[collectionIdx];
if (fileIdx < it->second->m_iteratorVec.size())
{
size_t idx = 0;
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
for (AudioGroupDataCollection::GroupToken& gtok : git->second->m_groupTokens)
{
wchar_t name[256];
wnsprintf(name, 256, L"%d (%s)", gtok.m_groupId, gtok.m_song ? L"Song" : L"SFX");
item.pszText = name;
item.iItem = idx++;
ListView_InsertItem(editor.m_groupListView, &item);
}
void AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx) {
TVINSERTSTRUCT ins = {};
ins.item.mask = TVIF_TEXT | TVIF_PARAM;
ins.hParent = colHandle;
ins.hInsertAfter = TVI_LAST;
m_iteratorVec.clear();
m_iteratorVec.reserve(m_groups.size());
for (auto it = m_groups.begin(); it != m_groups.end(); ++it) {
ins.item.pszText = LPWSTR(it->first.c_str());
ins.item.lParam = LPARAM(0x80000000 | (parentIdx << 16) | m_iteratorVec.size());
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
if (editor.m_selCollectionIdx == parentIdx && editor.m_selFileIdx == m_iteratorVec.size())
editor.m_deferredCollectionSel = item;
m_iteratorVec.push_back(it);
}
}
void AudioGroupFilePresenter::populateCollectionColumn(VSTEditor& editor) {
TreeView_DeleteAllItems(editor.m_collectionTree);
TVINSERTSTRUCT ins = {};
ins.hParent = TVI_ROOT;
ins.hInsertAfter = TVI_LAST;
ins.item.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
m_iteratorVec.clear();
m_iteratorVec.reserve(m_audioGroupCollections.size());
for (auto it = m_audioGroupCollections.begin(); it != m_audioGroupCollections.end(); ++it) {
ins.item.cChildren = it->second->m_groups.size() ? 1 : 0;
ins.item.pszText = LPWSTR(it->first.c_str());
ins.item.lParam = LPARAM(m_iteratorVec.size() << 16);
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
it->second->populateFiles(editor, item, m_iteratorVec.size());
if (editor.m_selCollectionIdx == m_iteratorVec.size() && editor.m_selFileIdx == -1)
editor.m_deferredCollectionSel = item;
m_iteratorVec.push_back(it);
}
}
void AudioGroupFilePresenter::populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx) {
LVITEM item = {};
item.mask = LVIF_TEXT;
ListView_DeleteAllItems(editor.m_groupListView);
ListView_DeleteAllItems(editor.m_pageListView);
if (collectionIdx < m_iteratorVec.size()) {
CollectionIterator& it = m_iteratorVec[collectionIdx];
if (fileIdx < it->second->m_iteratorVec.size()) {
size_t idx = 0;
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
for (AudioGroupDataCollection::GroupToken& gtok : git->second->m_groupTokens) {
wchar_t name[256];
wnsprintf(name, 256, L"%d (%s)", gtok.m_groupId, gtok.m_song ? L"Song" : L"SFX");
item.pszText = name;
item.iItem = idx++;
ListView_InsertItem(editor.m_groupListView, &item);
}
}
}
}
void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx) {
LVITEM item = {};
item.mask = LVIF_TEXT | LVIF_PARAM;
ListView_DeleteAllItems(editor.m_pageListView);
if (collectionIdx < m_iteratorVec.size()) {
CollectionIterator& it = m_iteratorVec[collectionIdx];
if (fileIdx < it->second->m_iteratorVec.size()) {
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
if (groupIdx < git->second->m_groupTokens.size()) {
AudioGroupDataCollection::GroupToken& groupTok = git->second->m_groupTokens[groupIdx];
if (groupTok.m_song) {
std::map<uint8_t, const amuse::SongGroupIndex::PageEntry*> sortPages;
for (auto& pair : groupTok.m_song->m_normPages)
sortPages[pair.first] = pair.second;
size_t idx = 0;
for (auto& pair : sortPages) {
wchar_t name[256];
wnsprintf(name, 256, L"%d (%s)", pair.first, GMNames[pair.first] ? GMNames[pair.first] : L"???");
item.pszText = name;
item.iItem = idx++;
item.lParam = pair.first;
ListView_InsertItem(editor.m_pageListView, &item);
}
sortPages.clear();
for (auto& pair : groupTok.m_song->m_drumPages)
sortPages[pair.first] = pair.second;
for (auto& pair : sortPages) {
wchar_t name[256];
wnsprintf(name, 256, L"%d (%s)", pair.first, GMPercNames[pair.first] ? GMPercNames[pair.first] : L"???");
item.pszText = name;
item.iItem = idx++;
item.lParam = 0x80000000 | pair.first;
ListView_InsertItem(editor.m_pageListView, &item);
}
}
}
}
}
}
void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx)
{
LVITEM item = {};
item.mask = LVIF_TEXT | LVIF_PARAM;
ListView_DeleteAllItems(editor.m_pageListView);
if (collectionIdx < m_iteratorVec.size())
{
CollectionIterator& it = m_iteratorVec[collectionIdx];
if (fileIdx < it->second->m_iteratorVec.size())
{
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
if (groupIdx < git->second->m_groupTokens.size())
{
AudioGroupDataCollection::GroupToken& groupTok = git->second->m_groupTokens[groupIdx];
if (groupTok.m_song)
{
std::map<uint8_t, const amuse::SongGroupIndex::PageEntry*> sortPages;
for (auto& pair : groupTok.m_song->m_normPages)
sortPages[pair.first] = pair.second;
size_t idx = 0;
for (auto& pair : sortPages)
{
wchar_t name[256];
wnsprintf(name, 256, L"%d (%s)", pair.first,
GMNames[pair.first] ? GMNames[pair.first] : L"???");
item.pszText = name;
item.iItem = idx++;
item.lParam = pair.first;
ListView_InsertItem(editor.m_pageListView, &item);
}
sortPages.clear();
for (auto& pair : groupTok.m_song->m_drumPages)
sortPages[pair.first] = pair.second;
for (auto& pair : sortPages)
{
wchar_t name[256];
wnsprintf(name, 256, L"%d (%s)", pair.first,
GMPercNames[pair.first] ? GMPercNames[pair.first] : L"???");
item.pszText = name;
item.iItem = idx++;
item.lParam = 0x80000000 | pair.first;
ListView_InsertItem(editor.m_pageListView, &item);
}
}
}
}
}
}
}
} // namespace amuse

View File

@@ -1,9 +1,8 @@
#ifndef __AMUSE_AUDIOGROUPFILEPRESENTER_HPP__
#define __AMUSE_AUDIOGROUPFILEPRESENTER_HPP__
#pragma once
#include <map>
#include <memory>
#include "optional.hpp"
#include <optional>
#include <amuse/amuse.hpp>
#include <athena/FileReader.hpp>
@@ -11,106 +10,93 @@
#include <windows.h>
#include <CommCtrl.h>
namespace amuse
{
namespace amuse {
class VSTBackend;
class VSTEditor;
class AudioGroupFilePresenter;
struct AudioGroupDataCollection
{
std::wstring m_path;
std::wstring m_name;
struct AudioGroupDataCollection {
std::wstring m_path;
std::wstring m_name;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
struct MetaData
{
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
{
}
MetaData(athena::io::FileReader& r)
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
{
}
};
std::experimental::optional<MetaData> m_metaData;
struct MetaData {
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: FMT_STRING(fmtIn), absOffs(absOffsIn), active(activeIn) {}
MetaData(athena::io::FileReader& r)
: FMT_STRING(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
};
std::optional<MetaData> m_metaData;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
struct GroupToken
{
int m_groupId;
const amuse::SongGroupIndex* m_song = nullptr;
const amuse::SFXGroupIndex* m_sfx = nullptr;
GroupToken(int id, const amuse::SongGroupIndex* song) : m_groupId(id), m_song(song) {}
GroupToken(int id, const amuse::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {}
};
std::vector<GroupToken> m_groupTokens;
std::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
struct GroupToken {
int m_groupId;
const amuse::SongGroupIndex* m_song = nullptr;
const amuse::SFXGroupIndex* m_sfx = nullptr;
GroupToken(int id, const amuse::SongGroupIndex* song) : m_groupId(id), m_song(song) {}
GroupToken(int id, const amuse::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {}
};
std::vector<GroupToken> m_groupTokens;
bool loadProj();
bool loadPool();
bool loadSdir();
bool loadSamp();
bool loadMeta();
bool loadProj();
bool loadPool();
bool loadSdir();
bool loadSamp();
bool loadMeta();
AudioGroupDataCollection(std::wstring_view path, std::wstring_view name);
bool isDataComplete() const
{
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad();
bool _indexData();
AudioGroupDataCollection(std::wstring_view path, std::wstring_view name);
bool isDataComplete() const {
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad();
bool _indexData();
void addToEngine(amuse::Engine& engine);
void removeFromEngine(amuse::Engine& engine) const;
void addToEngine(amuse::Engine& engine);
void removeFromEngine(amuse::Engine& engine) const;
};
struct AudioGroupCollection
{
using GroupIterator = std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>>::iterator;
std::wstring m_path;
std::wstring m_name;
struct AudioGroupCollection {
using GroupIterator = std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>>::iterator;
std::wstring m_path;
std::wstring m_name;
std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<GroupIterator> m_iteratorVec;
std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<GroupIterator> m_iteratorVec;
AudioGroupCollection(std::wstring_view path, std::wstring_view name);
void addCollection(std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter& presenter);
void populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx);
AudioGroupCollection(std::wstring_view path, std::wstring_view name);
void addCollection(std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter& presenter);
void populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx);
};
class AudioGroupFilePresenter
{
friend class VSTBackend;
class AudioGroupFilePresenter {
friend class VSTBackend;
public:
using CollectionIterator = std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>::iterator;
using CollectionIterator = std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>::iterator;
private:
VSTBackend& m_backend;
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<CollectionIterator> m_iteratorVec;
VSTBackend& m_backend;
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<CollectionIterator> m_iteratorVec;
public:
AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {}
void update();
void populateCollectionColumn(VSTEditor& editor);
void populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx);
void populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx);
void addCollection(std::wstring_view name,
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void removeCollection(unsigned idx);
VSTBackend& getBackend() { return m_backend; }
AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {}
void update();
void populateCollectionColumn(VSTEditor& editor);
void populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx);
void populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx);
void addCollection(std::wstring_view name,
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void removeCollection(unsigned idx);
VSTBackend& getBackend() { return m_backend; }
};
}
#endif // __AMUSE_AUDIOGROUPFILEPRESENTER_HPP__
} // namespace amuse

View File

@@ -12,7 +12,7 @@ if (WIN32 AND (EXISTS ${VST3_SDK_ROOT}))
${VST2_DIR}/audioeffect.cpp
${VST2_DIR}/audioeffectx.cpp
FileOpenDialog.hpp FileOpenDialog.cpp)
target_link_libraries(amuse-vst amuse boo soxr ${ZLIB_LIBRARIES} ${LZO_LIB} Winmm soxr
target_link_libraries(amuse-vst amuse boo soxr ${ZLIB_LIBRARIES} lzokay Winmm soxr
Msimg32 Shlwapi logvisor athena-core)
set_target_properties(amuse-vst PROPERTIES LINK_FLAGS "/EXPORT:VSTPluginMain")
endif()

View File

@@ -18,8 +18,9 @@
#define CONTROL_GROUP 2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1 1
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST,
// because it is a child control under CONTROL_RADIOBUTTONLIST
#define CONTROL_RADIOBUTTON2 \
2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST,
// because it is a child control under CONTROL_RADIOBUTTONLIST
// IDs for the Task Dialog Buttons
#define IDC_BASICFILEOPEN 100
@@ -33,201 +34,179 @@ HWND ghMainWnd = 0;
HINSTANCE ghAppInst = 0;
RECT winRect;
class CDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents
{
class CDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {
public:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{0},
};
return QISearch(this, qit, riid, ppv);
}
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{0},
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
IFACEMETHODIMP_(ULONG) Release() {
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd);
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd);
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem);
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) { return S_OK; };
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem);
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) { return S_OK; };
CDialogEventHandler() : _cRef(1){};
CDialogEventHandler() : _cRef(1){};
private:
~CDialogEventHandler(){};
long _cRef;
~CDialogEventHandler(){};
long _cRef;
};
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv);
std::wstring openDB()
{
std::wstring ret;
CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
std::wstring openDB() {
std::wstring ret;
CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
// Cocreate the file open dialog object
IFileDialog* pfd = NULL;
// Cocreate the file open dialog object
IFileDialog* pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Stuff needed for later
const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}};
if (SUCCEEDED(hr)) {
// Stuff needed for later
const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}};
// Create event handling
IFileDialogEvents* pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
// Create event handling
IFileDialogEvents* pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook the event handler
DWORD dwCookie;
if (SUCCEEDED(hr)) {
// Hook the event handler
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
// Set options for the dialog
DWORD dwFlags;
if (SUCCEEDED(hr)) {
// Set options for the dialog
DWORD dwFlags;
// Get options first so we do not override
hr = pfd->GetOptions(&dwFlags);
// Get options first so we do not override
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
// Get shell items only
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr)) {
// Get shell items only
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr))
{
// Types of files to display (not default)
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
if (SUCCEEDED(hr)) {
// Types of files to display (not default)
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
if (SUCCEEDED(hr))
{
// Set default file type to display
// hr = pfd->SetDefaultExtension(L"sqlite");
if (SUCCEEDED(hr)) {
// Set default file type to display
// hr = pfd->SetDefaultExtension(L"sqlite");
// if (SUCCEEDED(hr))
//{
// Show dialog
hr = pfd->Show(NULL);
// if (SUCCEEDED(hr))
//{
// Show dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
// Get the result once the user clicks on open
IShellItem* result;
if (SUCCEEDED(hr)) {
// Get the result once the user clicks on open
IShellItem* result;
hr = pfd->GetResult(&result);
hr = pfd->GetResult(&result);
if (SUCCEEDED(hr))
{
// Print out the file name
PWSTR fName = NULL;
if (SUCCEEDED(hr)) {
// Print out the file name
PWSTR fName = NULL;
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
if (SUCCEEDED(hr))
{
ret.assign(fName);
CoTaskMemFree(fName);
}
if (SUCCEEDED(hr)) {
ret.assign(fName);
CoTaskMemFree(fName);
}
result->Release();
}
}
//}
}
}
result->Release();
}
}
//}
}
pfd->Unadvise(dwCookie);
}
}
}
pfde->Release();
pfd->Unadvise(dwCookie);
}
pfd->Release();
return ret;
pfde->Release();
}
pfd->Release();
return ret;
}
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv)
{
*ppv = NULL;
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv) {
*ppv = NULL;
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr)) {
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog* pfd)
{
IFileSaveDialog* pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr))
{
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr))
{
IPropertyDescriptionList* pdl = NULL;
}
pfsd->Release();
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog* pfd) {
IFileSaveDialog* pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr)) {
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr)) {
IPropertyDescriptionList* pdl = NULL;
}
return hr;
pfsd->Release();
}
return hr;
}
// IFileDialogControlEvents
// This method gets called when an dialog control item selection happens (radio-button selection. etc).
// For sample sake, let's react to this event by changing the dialog title.
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem)
{
IFileDialog* pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr))
{
if (dwIDCtl == CONTROL_RADIOBUTTONLIST)
{
switch (dwIDItem)
{
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem) {
IFileDialog* pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr)) {
if (dwIDCtl == CONTROL_RADIOBUTTONLIST) {
switch (dwIDItem) {
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
pfd->Release();
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
return hr;
pfd->Release();
}
return hr;
}

View File

@@ -1,8 +1,5 @@
#ifndef __AMUSE_FILEOPENDIALOG_HPP__
#define __AMUSE_FILEOPENDIALOG_HPP__
#pragma once
#include <string>
std::wstring openDB();
#endif // __AMUSE_FILEOPENDIALOG_HPP__

View File

@@ -6,389 +6,343 @@
#undef min
#undef max
struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
{
std::vector<float> m_interleavedBuf;
float** m_outputData = nullptr;
size_t m_renderFrames = 0;
size_t m_curBufFrame = 0;
struct VSTVoiceEngine : boo::BaseAudioVoiceEngine {
std::vector<float> m_interleavedBuf;
float** m_outputData = nullptr;
size_t m_renderFrames = 0;
size_t m_curBufFrame = 0;
boo::AudioChannelSet _getAvailableSet() { return boo::AudioChannelSet::Stereo; }
boo::AudioChannelSet _getAvailableSet() { return boo::AudioChannelSet::Stereo; }
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const { return {}; }
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const { return {}; }
boo::ReceiveFunctor* m_midiReceiver = nullptr;
boo::ReceiveFunctor* m_midiReceiver = nullptr;
struct MIDIIn : public boo::IMIDIIn
{
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) : IMIDIIn(virt, std::move(receiver)) {}
struct MIDIIn : public boo::IMIDIIn {
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) : IMIDIIn(virt, std::move(receiver)) {}
std::string description() const { return "VST MIDI"; }
};
std::string description() const { return "VST MIDI"; }
};
std::unique_ptr<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver)
{
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
m_midiReceiver = &ret->m_receiver;
return ret;
}
std::unique_ptr<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) {
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
m_midiReceiver = &ret->m_receiver;
return ret;
}
std::unique_ptr<boo::IMIDIOut> newVirtualMIDIOut() { return {}; }
std::unique_ptr<boo::IMIDIOut> newVirtualMIDIOut() { return {}; }
std::unique_ptr<boo::IMIDIInOut> newVirtualMIDIInOut(boo::ReceiveFunctor&& receiver) { return {}; }
std::unique_ptr<boo::IMIDIInOut> newVirtualMIDIInOut(boo::ReceiveFunctor&& receiver) { return {}; }
std::unique_ptr<boo::IMIDIIn> newRealMIDIIn(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
std::unique_ptr<boo::IMIDIIn> newRealMIDIIn(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
std::unique_ptr<boo::IMIDIOut> newRealMIDIOut(const char* name) { return {}; }
std::unique_ptr<boo::IMIDIOut> newRealMIDIOut(const char* name) { return {}; }
std::unique_ptr<boo::IMIDIInOut> newRealMIDIInOut(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
std::unique_ptr<boo::IMIDIInOut> newRealMIDIInOut(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
bool useMIDILock() const { return false; }
bool useMIDILock() const { return false; }
VSTVoiceEngine()
{
m_mixInfo.m_periodFrames = 1024;
m_mixInfo.m_sampleRate = 44100.0;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
m_mixInfo.m_bitsPerSample = 32;
_buildAudioRenderClient();
}
VSTVoiceEngine() {
m_mixInfo.m_periodFrames = 1024;
m_mixInfo.m_sampleRate = 44100.0;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
m_mixInfo.m_bitsPerSample = 32;
_buildAudioRenderClient();
}
void _buildAudioRenderClient()
{
m_mixInfo.m_channels = _getAvailableSet();
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
void _buildAudioRenderClient() {
m_mixInfo.m_channels = _getAvailableSet();
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
m_curBufFrame = m_5msFrames;
m_mixInfo.m_periodFrames = m_5msFrames;
m_interleavedBuf.resize(m_5msFrames * 2);
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
m_curBufFrame = m_5msFrames;
m_mixInfo.m_periodFrames = m_5msFrames;
m_interleavedBuf.resize(m_5msFrames * 2);
boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap;
chMapOut.m_channelCount = 2;
chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft;
chMapOut.m_channels[1] = boo::AudioChannel::FrontRight;
boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap;
chMapOut.m_channelCount = 2;
chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft;
chMapOut.m_channels[1] = boo::AudioChannel::FrontRight;
while (chMapOut.m_channelCount < chCount)
chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown;
}
while (chMapOut.m_channelCount < chCount)
chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown;
}
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames)
{
m_mixInfo.m_periodFrames = periodFrames;
m_mixInfo.m_sampleRate = sampleRate;
_buildAudioRenderClient();
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) {
m_mixInfo.m_periodFrames = periodFrames;
m_mixInfo.m_sampleRate = sampleRate;
_buildAudioRenderClient();
for (boo::AudioVoice* vox : m_activeVoices)
vox->_resetSampleRate(vox->m_sampleRateIn);
for (boo::AudioSubmix* smx : m_activeSubmixes)
smx->_resetOutputSampleRate();
}
for (boo::AudioVoice* vox : m_activeVoices)
vox->_resetSampleRate(vox->m_sampleRateIn);
for (boo::AudioSubmix* smx : m_activeSubmixes)
smx->_resetOutputSampleRate();
}
void pumpAndMixVoices()
{
for (size_t f = 0; f < m_renderFrames;)
{
if (m_curBufFrame == m_5msFrames)
{
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data());
m_curBufFrame = 0;
}
void pumpAndMixVoices() {
for (size_t f = 0; f < m_renderFrames;) {
if (m_curBufFrame == m_5msFrames) {
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data());
m_curBufFrame = 0;
}
size_t remRenderFrames = std::min(m_renderFrames - f, m_5msFrames - m_curBufFrame);
if (remRenderFrames)
{
for (size_t i = 0; i < 2; ++i)
{
float* bufOut = m_outputData[i];
for (size_t lf = 0; lf < remRenderFrames; ++lf)
bufOut[f + lf] = m_interleavedBuf[(m_curBufFrame + lf) * 2 + i];
}
m_curBufFrame += remRenderFrames;
f += remRenderFrames;
}
size_t remRenderFrames = std::min(m_renderFrames - f, m_5msFrames - m_curBufFrame);
if (remRenderFrames) {
for (size_t i = 0; i < 2; ++i) {
float* bufOut = m_outputData[i];
for (size_t lf = 0; lf < remRenderFrames; ++lf)
bufOut[f + lf] = m_interleavedBuf[(m_curBufFrame + lf) * 2 + i];
}
m_curBufFrame += remRenderFrames;
f += remRenderFrames;
}
}
}
double getCurrentSampleRate() const { return m_mixInfo.m_sampleRate; }
double getCurrentSampleRate() const { return m_mixInfo.m_sampleRate; }
};
namespace amuse
{
namespace amuse {
#define kBackendID CCONST('a', 'm', 'u', 's')
static logvisor::Module Log("amuse::AudioUnitBackend");
VSTBackend::VSTBackend(audioMasterCallback cb) : AudioEffectX(cb, 0, 0), m_filePresenter(*this), m_editor(*this)
{
isSynth();
setUniqueID(kBackendID);
setNumInputs(0);
setNumOutputs(2);
setEditor(&m_editor);
sizeWindow(600, 420);
programsAreChunks();
VSTBackend::VSTBackend(audioMasterCallback cb) : AudioEffectX(cb, 0, 0), m_filePresenter(*this), m_editor(*this) {
isSynth();
setUniqueID(kBackendID);
setNumInputs(0);
setNumOutputs(2);
setEditor(&m_editor);
sizeWindow(600, 420);
programsAreChunks();
m_booBackend = std::make_unique<VSTVoiceEngine>();
m_voxAlloc.emplace(*m_booBackend);
m_engine.emplace(*m_voxAlloc);
m_booBackend = std::make_unique<VSTVoiceEngine>();
m_voxAlloc.emplace(*m_booBackend);
m_engine.emplace(*m_voxAlloc);
WCHAR path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path)))
{
m_userDir = std::wstring(path) + L"\\Amuse";
CreateDirectory(m_userDir.c_str(), nullptr);
}
WCHAR path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path))) {
m_userDir = std::wstring(path) + L"\\Amuse";
CreateDirectory(m_userDir.c_str(), nullptr);
}
m_filePresenter.update();
m_filePresenter.update();
}
VSTBackend::~VSTBackend() { editor = nullptr; }
AEffEditor* VSTBackend::getEditor() { return &m_editor; }
VstInt32 VSTBackend::processEvents(VstEvents* events)
{
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
VstInt32 VSTBackend::processEvents(VstEvents* events) {
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
/* Handle group load request */
if (m_curGroup != m_reqGroup)
{
m_curGroup = m_reqGroup;
if (m_curSeq)
m_curSeq->kill();
m_curSeq = m_engine->seqPlay(m_reqGroup, -1, nullptr);
m_editor.reselectPage();
}
/* Handle group load request */
if (m_curGroup != m_reqGroup) {
m_curGroup = m_reqGroup;
if (m_curSeq)
m_curSeq->kill();
m_curSeq = m_engine->seqPlay(m_reqGroup, -1, nullptr);
m_editor.reselectPage();
}
if (engine.m_midiReceiver)
{
for (VstInt32 i = 0; i < events->numEvents; ++i)
{
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
if (evt->type == kVstMidiType)
{
if (m_routeChannel != -1)
{
evt->midiData[0] &= ~0xf;
evt->midiData[0] |= m_routeChannel & 0xf;
}
(*engine.m_midiReceiver)(
std::vector<uint8_t>(std::cbegin(evt->midiData), std::cbegin(evt->midiData) + evt->byteSize),
(m_curFrame + evt->deltaFrames) / sampleRate);
}
if (engine.m_midiReceiver) {
for (VstInt32 i = 0; i < events->numEvents; ++i) {
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
if (evt->type == kVstMidiType) {
if (m_routeChannel != -1) {
evt->midiData[0] &= ~0xf;
evt->midiData[0] |= m_routeChannel & 0xf;
}
(*engine.m_midiReceiver)(
std::vector<uint8_t>(std::cbegin(evt->midiData), std::cbegin(evt->midiData) + evt->byteSize),
(m_curFrame + evt->deltaFrames) / sampleRate);
}
}
}
return 1;
return 1;
}
void VSTBackend::processReplacing(float**, float** outputs, VstInt32 sampleFrames)
{
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
void VSTBackend::processReplacing(float**, float** outputs, VstInt32 sampleFrames) {
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
/* Output buffers */
engine.m_renderFrames = sampleFrames;
engine.m_outputData = outputs;
m_engine->pumpEngine();
/* Output buffers */
engine.m_renderFrames = sampleFrames;
engine.m_outputData = outputs;
m_engine->pumpEngine();
m_curFrame += sampleFrames;
m_curFrame += sampleFrames;
}
VstInt32 VSTBackend::canDo(char* text)
{
VstInt32 returnCode = 0;
VstInt32 VSTBackend::canDo(char* text) {
VstInt32 returnCode = 0;
if (!strcmp(text, "receiveVstEvents"))
returnCode = 1;
else if (!strcmp(text, "receiveVstMidiEvent"))
returnCode = 1;
if (!strcmp(text, "receiveVstEvents"))
returnCode = 1;
else if (!strcmp(text, "receiveVstMidiEvent"))
returnCode = 1;
return returnCode;
return returnCode;
}
VstPlugCategory VSTBackend::getPlugCategory() { return kPlugCategSynth; }
bool VSTBackend::getEffectName(char* text)
{
strcpy(text, "Amuse");
return true;
bool VSTBackend::getEffectName(char* text) {
strcpy(text, "Amuse");
return true;
}
bool VSTBackend::getProductString(char* text)
{
strcpy(text, "Amuse");
return true;
bool VSTBackend::getProductString(char* text) {
strcpy(text, "Amuse");
return true;
}
bool VSTBackend::getVendorString(char* text)
{
strcpy(text, "AxioDL");
return true;
bool VSTBackend::getVendorString(char* text) {
strcpy(text, "AxioDL");
return true;
}
bool VSTBackend::getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text)
{
strcpy(text, "Sampler");
return true;
bool VSTBackend::getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text) {
strcpy(text, "Sampler");
return true;
}
bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* properties)
{
bool returnCode = false;
if (index == 0)
{
strcpy(properties->label, "Amuse Out");
properties->flags = kVstPinIsStereo | kVstPinIsActive;
properties->arrangementType = kSpeakerArrStereo;
returnCode = true;
}
return returnCode;
bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* properties) {
bool returnCode = false;
if (index == 0) {
strcpy(properties->label, "Amuse Out");
properties->flags = kVstPinIsStereo | kVstPinIsActive;
properties->arrangementType = kSpeakerArrStereo;
returnCode = true;
}
return returnCode;
}
VstInt32 VSTBackend::getNumMidiInputChannels() { return 1; }
void VSTBackend::setSampleRate(float sampleRate)
{
AudioEffectX::setSampleRate(sampleRate);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(sampleRate, engine.mixInfo().m_periodFrames);
void VSTBackend::setSampleRate(float sampleRate) {
AudioEffectX::setSampleRate(sampleRate);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(sampleRate, engine.mixInfo().m_periodFrames);
}
void VSTBackend::setBlockSize(VstInt32 blockSize)
{
AudioEffectX::setBlockSize(blockSize);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(engine.mixInfo().m_sampleRate, blockSize);
void VSTBackend::setBlockSize(VstInt32 blockSize) {
AudioEffectX::setBlockSize(blockSize);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(engine.mixInfo().m_sampleRate, blockSize);
}
void VSTBackend::loadGroupFile(int collectionIdx, int fileIdx)
{
std::unique_lock<std::mutex> lk(m_lock);
void VSTBackend::loadGroupFile(int collectionIdx, int fileIdx) {
std::unique_lock<std::mutex> lk(m_lock);
if (m_curSeq)
{
if (m_curSeq) {
m_curSeq->kill();
m_curSeq.reset();
m_curGroup = -1;
m_reqGroup = -1;
}
if (collectionIdx < m_filePresenter.m_iteratorVec.size()) {
AudioGroupFilePresenter::CollectionIterator& it = m_filePresenter.m_iteratorVec[collectionIdx];
if (fileIdx < it->second->m_iteratorVec.size()) {
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
if (m_curData)
m_curData->removeFromEngine(*m_engine);
git->second->addToEngine(*m_engine);
m_curData = git->second.get();
}
}
}
void VSTBackend::setGroup(int groupIdx, bool immediate) {
std::unique_lock<std::mutex> lk(m_lock);
if (!m_curData)
return;
if (groupIdx < m_curData->m_groupTokens.size()) {
const AudioGroupDataCollection::GroupToken& groupTok = m_curData->m_groupTokens[groupIdx];
m_reqGroup = groupTok.m_groupId;
if (immediate) {
if (m_curSeq)
m_curSeq->kill();
m_curSeq.reset();
m_curGroup = -1;
m_reqGroup = -1;
m_curSeq = m_engine->seqPlay(groupTok.m_groupId, -1, nullptr);
}
}
}
if (collectionIdx < m_filePresenter.m_iteratorVec.size())
{
AudioGroupFilePresenter::CollectionIterator& it = m_filePresenter.m_iteratorVec[collectionIdx];
if (fileIdx < it->second->m_iteratorVec.size())
{
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
if (m_curData)
m_curData->removeFromEngine(*m_engine);
git->second->addToEngine(*m_engine);
m_curData = git->second.get();
}
void VSTBackend::_setNormalProgram(int programNo) {
if (!m_curSeq)
return;
m_curSeq->setChanProgram(0, programNo);
m_routeChannel = 0;
}
void VSTBackend::setNormalProgram(int programNo) {
std::unique_lock<std::mutex> lk(m_lock);
_setNormalProgram(programNo);
}
void VSTBackend::_setDrumProgram(int programNo) {
if (!m_curSeq)
return;
m_curSeq->setChanProgram(9, programNo);
m_routeChannel = 9;
}
void VSTBackend::setDrumProgram(int programNo) {
std::unique_lock<std::mutex> lk(m_lock);
_setDrumProgram(programNo);
}
VstInt32 VSTBackend::getChunk(void** data, bool) {
size_t allocSz = 14;
if (m_curData)
allocSz += (m_curData->m_path.size() - m_userDir.size() - 1) * 2;
uint8_t* buf = new uint8_t[allocSz];
if (m_curData)
memmove(buf, m_curData->m_path.data() + m_userDir.size() + 1, allocSz - 12);
else
*reinterpret_cast<wchar_t*>(buf) = L'\0';
uint32_t* intVals = reinterpret_cast<uint32_t*>(buf + allocSz - 12);
intVals[0] = 0;
intVals[1] = m_editor.m_selGroupIdx;
intVals[2] = m_editor.m_selPageIdx;
*data = buf;
return allocSz;
}
VstInt32 VSTBackend::setChunk(void* data, VstInt32 byteSize, bool) {
if (byteSize < 14)
return 0;
wchar_t* path = reinterpret_cast<wchar_t*>(data);
uint32_t* intVals = reinterpret_cast<uint32_t*>(path + wcslen(path) + 1);
std::wstring targetPath = m_userDir + L'\\' + path;
uint32_t groupIdx = intVals[1];
uint32_t pageIdx = intVals[2];
size_t colIdx = 0;
for (auto& collection : m_filePresenter.m_audioGroupCollections) {
size_t fileIdx = 0;
for (auto& file : collection.second->m_groups) {
if (file.second->m_path == targetPath) {
m_editor.selectCollection(LPARAM(0x80000000 | (colIdx << 16) | fileIdx));
m_editor.selectGroup(groupIdx);
m_editor.selectPage(pageIdx);
m_editor._reselectColumns();
}
++fileIdx;
}
++colIdx;
}
return 1;
}
void VSTBackend::setGroup(int groupIdx, bool immediate)
{
std::unique_lock<std::mutex> lk(m_lock);
if (!m_curData)
return;
if (groupIdx < m_curData->m_groupTokens.size())
{
const AudioGroupDataCollection::GroupToken& groupTok = m_curData->m_groupTokens[groupIdx];
m_reqGroup = groupTok.m_groupId;
if (immediate)
{
if (m_curSeq)
m_curSeq->kill();
m_curSeq = m_engine->seqPlay(groupTok.m_groupId, -1, nullptr);
}
}
}
void VSTBackend::_setNormalProgram(int programNo)
{
if (!m_curSeq)
return;
m_curSeq->setChanProgram(0, programNo);
m_routeChannel = 0;
}
void VSTBackend::setNormalProgram(int programNo)
{
std::unique_lock<std::mutex> lk(m_lock);
_setNormalProgram(programNo);
}
void VSTBackend::_setDrumProgram(int programNo)
{
if (!m_curSeq)
return;
m_curSeq->setChanProgram(9, programNo);
m_routeChannel = 9;
}
void VSTBackend::setDrumProgram(int programNo)
{
std::unique_lock<std::mutex> lk(m_lock);
_setDrumProgram(programNo);
}
VstInt32 VSTBackend::getChunk(void** data, bool)
{
size_t allocSz = 14;
if (m_curData)
allocSz += (m_curData->m_path.size() - m_userDir.size() - 1) * 2;
uint8_t* buf = new uint8_t[allocSz];
if (m_curData)
memmove(buf, m_curData->m_path.data() + m_userDir.size() + 1, allocSz - 12);
else
*reinterpret_cast<wchar_t*>(buf) = L'\0';
uint32_t* intVals = reinterpret_cast<uint32_t*>(buf + allocSz - 12);
intVals[0] = 0;
intVals[1] = m_editor.m_selGroupIdx;
intVals[2] = m_editor.m_selPageIdx;
*data = buf;
return allocSz;
}
VstInt32 VSTBackend::setChunk(void* data, VstInt32 byteSize, bool)
{
if (byteSize < 14)
return 0;
wchar_t* path = reinterpret_cast<wchar_t*>(data);
uint32_t* intVals = reinterpret_cast<uint32_t*>(path + wcslen(path) + 1);
std::wstring targetPath = m_userDir + L'\\' + path;
uint32_t groupIdx = intVals[1];
uint32_t pageIdx = intVals[2];
size_t colIdx = 0;
for (auto& collection : m_filePresenter.m_audioGroupCollections)
{
size_t fileIdx = 0;
for (auto& file : collection.second->m_groups)
{
if (!file.second->m_path.compare(targetPath))
{
m_editor.selectCollection(LPARAM(0x80000000 | (colIdx << 16) | fileIdx));
m_editor.selectGroup(groupIdx);
m_editor.selectPage(pageIdx);
m_editor._reselectColumns();
}
++fileIdx;
}
++colIdx;
}
return 1;
}
}
} // namespace amuse
AudioEffect* createEffectInstance(audioMasterCallback audioMaster) { return new amuse::VSTBackend(audioMaster); }

View File

@@ -1,10 +1,9 @@
#ifndef __AMUSE_VSTBACKEND_HPP__
#define __AMUSE_VSTBACKEND_HPP__
#pragma once
#include "audioeffectx.h"
#include "VSTEditor.hpp"
#include <memory>
#include "optional.hpp"
#include <optional>
#include "amuse/BooBackend.hpp"
#include "amuse/Engine.hpp"
@@ -13,65 +12,60 @@
#include "amuse/IBackendVoiceAllocator.hpp"
#include "AudioGroupFilePresenter.hpp"
namespace amuse
{
namespace amuse {
class VSTBackend;
/** Backend voice allocator implementation for AudioUnit mixer */
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator
{
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator {
public:
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
};
/** Actual plugin implementation class */
class VSTBackend : public AudioEffectX
{
std::mutex m_lock;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::VSTBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
std::shared_ptr<amuse::Sequencer> m_curSeq;
int m_reqGroup = -1;
int m_curGroup = -1;
const AudioGroupDataCollection* m_curData = nullptr;
size_t m_curFrame = 0;
std::wstring m_userDir;
int m_routeChannel = -1;
AudioGroupFilePresenter m_filePresenter;
VSTEditor m_editor;
class VSTBackend : public AudioEffectX {
std::mutex m_lock;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::optional<amuse::VSTBackendVoiceAllocator> m_voxAlloc;
std::optional<amuse::Engine> m_engine;
std::shared_ptr<amuse::Sequencer> m_curSeq;
int m_reqGroup = -1;
int m_curGroup = -1;
const AudioGroupDataCollection* m_curData = nullptr;
size_t m_curFrame = 0;
std::wstring m_userDir;
int m_routeChannel = -1;
AudioGroupFilePresenter m_filePresenter;
VSTEditor m_editor;
public:
VSTBackend(audioMasterCallback cb);
~VSTBackend();
AEffEditor* getEditor();
VstInt32 processEvents(VstEvents* events);
void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);
VstInt32 canDo(char* text);
VstPlugCategory getPlugCategory();
bool getEffectName(char* text);
bool getProductString(char* text);
bool getVendorString(char* text);
bool getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text);
bool getOutputProperties(VstInt32 index, VstPinProperties* properties);
VstInt32 getNumMidiInputChannels();
void setSampleRate(float sampleRate);
void setBlockSize(VstInt32 blockSize);
VSTBackend(audioMasterCallback cb);
~VSTBackend();
AEffEditor* getEditor();
VstInt32 processEvents(VstEvents* events);
void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);
VstInt32 canDo(char* text);
VstPlugCategory getPlugCategory();
bool getEffectName(char* text);
bool getProductString(char* text);
bool getVendorString(char* text);
bool getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text);
bool getOutputProperties(VstInt32 index, VstPinProperties* properties);
VstInt32 getNumMidiInputChannels();
void setSampleRate(float sampleRate);
void setBlockSize(VstInt32 blockSize);
amuse::Engine& getAmuseEngine() { return *m_engine; }
std::wstring_view getUserDir() const { return m_userDir; }
AudioGroupFilePresenter& getFilePresenter() { return m_filePresenter; }
amuse::Engine& getAmuseEngine() { return *m_engine; }
std::wstring_view getUserDir() const { return m_userDir; }
AudioGroupFilePresenter& getFilePresenter() { return m_filePresenter; }
void loadGroupFile(int collectionIdx, int fileIdx);
void setGroup(int groupIdx, bool immediate);
void _setNormalProgram(int programNo);
void setNormalProgram(int programNo);
void _setDrumProgram(int programNo);
void setDrumProgram(int programNo);
void loadGroupFile(int collectionIdx, int fileIdx);
void setGroup(int groupIdx, bool immediate);
void _setNormalProgram(int programNo);
void setNormalProgram(int programNo);
void _setDrumProgram(int programNo);
void setDrumProgram(int programNo);
VstInt32 getChunk(void** data, bool isPreset);
VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset);
VstInt32 getChunk(void** data, bool isPreset);
VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset);
};
}
#endif // __AMUSE_VSTBACKEND_HPP__
} // namespace amuse

View File

@@ -13,381 +13,344 @@ extern void* hInstance;
static WNDPROC OriginalListViewProc = 0;
static HBRUSH gGreyBorderBrush;
namespace amuse
{
namespace amuse {
VSTEditor::VSTEditor(VSTBackend& backend) : AEffEditor(&backend), m_backend(backend) {}
bool VSTEditor::getRect(ERect** rect)
{
*rect = &m_windowRect;
return true;
bool VSTEditor::getRect(ERect** rect) {
*rect = &m_windowRect;
return true;
}
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
VSTEditor& editor = *reinterpret_cast<VSTEditor*>(GetWindowLongPtrW(hwnd, 0));
switch (uMsg)
{
case WM_NOTIFY:
{
NMHDR& itemAct = *reinterpret_cast<LPNMHDR>(lParam);
switch (itemAct.code)
{
case HDN_BEGINTRACK:
return TRUE;
case NM_CLICK:
{
NMITEMACTIVATE& itemAct = *reinterpret_cast<LPNMITEMACTIVATE>(lParam);
if (itemAct.hdr.hwndFrom == editor.m_groupListView)
editor.selectGroup(itemAct.iItem);
else if (itemAct.hdr.hwndFrom == editor.m_pageListView)
editor.selectPage(itemAct.iItem);
return 0;
}
case TVN_SELCHANGED:
{
if (editor.m_deferredCollectionSel)
return 0;
NMTREEVIEW& itemAct = *reinterpret_cast<LPNMTREEVIEW>(lParam);
if (itemAct.hdr.hwndFrom == editor.m_collectionTree)
editor.selectCollection(itemAct.itemNew.lParam);
return 0;
}
case TVN_GETDISPINFO:
{
NMTVDISPINFO& treeDispInfo = *reinterpret_cast<LPNMTVDISPINFO>(lParam);
if (treeDispInfo.item.mask & TVIF_CHILDREN)
{
}
return 0;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
VSTEditor& editor = *reinterpret_cast<VSTEditor*>(GetWindowLongPtrW(hwnd, 0));
switch (uMsg) {
case WM_NOTIFY: {
NMHDR& itemAct = *reinterpret_cast<LPNMHDR>(lParam);
switch (itemAct.code) {
case HDN_BEGINTRACK:
return TRUE;
case NM_CLICK: {
NMITEMACTIVATE& itemAct = *reinterpret_cast<LPNMITEMACTIVATE>(lParam);
if (itemAct.hdr.hwndFrom == editor.m_groupListView)
editor.selectGroup(itemAct.iItem);
else if (itemAct.hdr.hwndFrom == editor.m_pageListView)
editor.selectPage(itemAct.iItem);
return 0;
}
case WM_COMMAND:
{
switch (HIWORD(wParam))
{
case BN_CLICKED:
{
HWND button = HWND(lParam);
if (button == editor.m_collectionAdd)
editor.addAction();
else if (button == editor.m_collectionRemove)
editor.removeAction();
return 0;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
case TVN_SELCHANGED: {
if (editor.m_deferredCollectionSel)
return 0;
NMTREEVIEW& itemAct = *reinterpret_cast<LPNMTREEVIEW>(lParam);
if (itemAct.hdr.hwndFrom == editor.m_collectionTree)
editor.selectCollection(itemAct.itemNew.lParam);
return 0;
}
case WM_ERASEBKGND:
{
RECT rect;
GetClientRect(hwnd, &rect);
FillRect(HDC(wParam), &rect, gGreyBorderBrush);
return 1;
case TVN_GETDISPINFO: {
NMTVDISPINFO& treeDispInfo = *reinterpret_cast<LPNMTVDISPINFO>(lParam);
if (treeDispInfo.item.mask & TVIF_CHILDREN) {}
return 0;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
case WM_COMMAND: {
switch (HIWORD(wParam)) {
case BN_CLICKED: {
HWND button = HWND(lParam);
if (button == editor.m_collectionAdd)
editor.addAction();
else if (button == editor.m_collectionRemove)
editor.removeAction();
return 0;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
case WM_ERASEBKGND: {
RECT rect;
GetClientRect(hwnd, &rect);
FillRect(HDC(wParam), &rect, gGreyBorderBrush);
return 1;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SETCURSOR:
return TRUE;
case WM_LBUTTONDBLCLK:
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_SETCURSOR:
return TRUE;
case WM_LBUTTONDBLCLK:
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
TRIVERTEX verts[] = {{rect.left, rect.top, 0x6000, 0x6000, 0x7000, 0xff00},
{rect.right, rect.bottom, 0x2000, 0x2000, 0x2800, 0xff00}};
GRADIENT_RECT grect = {0, 1};
GradientFill(dc, verts, 2, &grect, 1, GRADIENT_FILL_RECT_V);
TRIVERTEX verts[] = {{rect.left, rect.top, 0x6000, 0x6000, 0x7000, 0xff00},
{rect.right, rect.bottom, 0x2000, 0x2000, 0x2800, 0xff00}};
GRADIENT_RECT grect = {0, 1};
GradientFill(dc, verts, 2, &grect, 1, GRADIENT_FILL_RECT_V);
SetTextColor(dc, RGB(255, 255, 255));
SetBkMode(dc, TRANSPARENT);
SelectObject(dc, GetStockObject(ANSI_VAR_FONT));
rect.left += 6;
SetTextColor(dc, RGB(255, 255, 255));
SetBkMode(dc, TRANSPARENT);
SelectObject(dc, GetStockObject(ANSI_VAR_FONT));
rect.left += 6;
LPWSTR str = LPWSTR(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
DrawText(dc, str, -1, &rect, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
LPWSTR str = LPWSTR(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
DrawText(dc, str, -1, &rect, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
EndPaint(hwnd, &ps);
return 0;
}
}
EndPaint(hwnd, &ps);
return 0;
}
}
return CallWindowProc(OriginalListViewProc, hwnd, uMsg, wParam, lParam);
return CallWindowProc(OriginalListViewProc, hwnd, uMsg, wParam, lParam);
}
void VSTEditor::_reselectColumns()
{
if (m_deferredCollectionSel)
{
TreeView_SelectItem(m_collectionTree, m_deferredCollectionSel);
m_deferredCollectionSel = 0;
}
if (m_selGroupIdx != -1)
ListView_SetItemState(m_groupListView, m_selGroupIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
if (m_selPageIdx != -1)
ListView_SetItemState(m_pageListView, m_selPageIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
void VSTEditor::_reselectColumns() {
if (m_deferredCollectionSel) {
TreeView_SelectItem(m_collectionTree, m_deferredCollectionSel);
m_deferredCollectionSel = 0;
}
if (m_selGroupIdx != -1)
ListView_SetItemState(m_groupListView, m_selGroupIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
if (m_selPageIdx != -1)
ListView_SetItemState(m_pageListView, m_selPageIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
}
bool VSTEditor::open(void* ptr)
{
AEffEditor::open(ptr);
HWND hostView = HWND(ptr);
gGreyBorderBrush = CreateSolidBrush(RGB(100, 100, 100));
bool VSTEditor::open(void* ptr) {
AEffEditor::open(ptr);
HWND hostView = HWND(ptr);
gGreyBorderBrush = CreateSolidBrush(RGB(100, 100, 100));
WNDCLASSW notifyCls = {CS_HREDRAW | CS_VREDRAW,
WindowProc,
0,
8,
HINSTANCE(hInstance),
nullptr,
nullptr,
nullptr,
nullptr,
L"VSTNotify"};
RegisterClassW(&notifyCls);
WNDCLASSW notifyCls = {CS_HREDRAW | CS_VREDRAW,
WindowProc,
0,
8,
HINSTANCE(hInstance),
nullptr,
nullptr,
nullptr,
nullptr,
L"VSTNotify"};
RegisterClassW(&notifyCls);
m_rootView = CreateWindowW(L"VSTNotify", L"", WS_CHILD, 0, 0, m_windowRect.right, m_windowRect.bottom, hostView,
nullptr, HINSTANCE(hInstance), nullptr);
SetWindowLongPtrW(m_rootView, 0, LONG_PTR(this));
ShowWindow(m_rootView, SW_SHOW);
m_rootView = CreateWindowW(L"VSTNotify", L"", WS_CHILD, 0, 0, m_windowRect.right, m_windowRect.bottom, hostView,
nullptr, HINSTANCE(hInstance), nullptr);
SetWindowLongPtrW(m_rootView, 0, LONG_PTR(this));
ShowWindow(m_rootView, SW_SHOW);
TVINSERTSTRUCT treeItem = {};
treeItem.hParent = TVI_ROOT;
treeItem.hInsertAfter = TVI_LAST;
TVINSERTSTRUCT treeItem = {};
treeItem.hParent = TVI_ROOT;
treeItem.hInsertAfter = TVI_LAST;
treeItem.item.mask = TVIF_CHILDREN | TVIF_TEXT;
treeItem.item.cChildren = 1;
treeItem.item.pszText = L"Root A";
treeItem.item.mask = TVIF_CHILDREN | TVIF_TEXT;
treeItem.item.cChildren = 1;
treeItem.item.pszText = L"Root A";
LVCOLUMN column = {};
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
column.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH;
column.cx = 199;
LVCOLUMN column = {};
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
column.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH;
column.cx = 199;
m_collectionTree =
CreateWindowW(WC_TREEVIEW, L"",
WS_CHILD | WS_CLIPSIBLINGS | TVS_SHOWSELALWAYS | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS,
1, 25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
TreeView_SetBkColor(m_collectionTree, RGB(64, 64, 64));
TreeView_SetTextColor(m_collectionTree, RGB(255, 255, 255));
HTREEITEM rootItemA = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Root B";
HTREEITEM rootItemB = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemA;
treeItem.item.cChildren = 0;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemB;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
ShowWindow(m_collectionTree, SW_SHOW);
m_collectionTree =
CreateWindowW(WC_TREEVIEW, L"",
WS_CHILD | WS_CLIPSIBLINGS | TVS_SHOWSELALWAYS | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, 1,
25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
TreeView_SetBkColor(m_collectionTree, RGB(64, 64, 64));
TreeView_SetTextColor(m_collectionTree, RGB(255, 255, 255));
HTREEITEM rootItemA = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Root B";
HTREEITEM rootItemB = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemA;
treeItem.item.cChildren = 0;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemB;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
ShowWindow(m_collectionTree, SW_SHOW);
HWND cHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 1, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowLongPtrW(cHeader, GWLP_USERDATA, LONG_PTR(L"Collection"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(cHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(cHeader, SW_SHOW);
HWND cHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 1, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowLongPtrW(cHeader, GWLP_USERDATA, LONG_PTR(L"Collection"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(cHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(cHeader, SW_SHOW);
HWND gHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 201, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowLongPtrW(gHeader, GWLP_USERDATA, LONG_PTR(L"Group"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(gHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(gHeader, SW_SHOW);
HWND gHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 201, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowLongPtrW(gHeader, GWLP_USERDATA, LONG_PTR(L"Group"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(gHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(gHeader, SW_SHOW);
HWND pHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 401, 1, 198, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowLongPtrW(pHeader, GWLP_USERDATA, LONG_PTR(L"Page"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(pHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(pHeader, SW_SHOW);
HWND pHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 401, 1, 198, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowLongPtrW(pHeader, GWLP_USERDATA, LONG_PTR(L"Page"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(pHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(pHeader, SW_SHOW);
m_collectionAdd =
CreateWindowW(WC_BUTTON, L"+", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 1,
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowFont(m_collectionAdd, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionAdd, TRUE);
SetWindowPos(m_collectionAdd, HWND_TOP, 1, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_collectionAdd =
CreateWindowW(WC_BUTTON, L"+", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 1,
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowFont(m_collectionAdd, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionAdd, TRUE);
SetWindowPos(m_collectionAdd, HWND_TOP, 1, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_collectionRemove =
CreateWindowW(WC_BUTTON, L"-", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 26,
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowFont(m_collectionRemove, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionRemove, FALSE);
SetWindowPos(m_collectionRemove, HWND_TOP, 26, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_collectionRemove =
CreateWindowW(WC_BUTTON, L"-", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 26,
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
SetWindowFont(m_collectionRemove, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionRemove, FALSE);
SetWindowPos(m_collectionRemove, HWND_TOP, 26, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_groupListView =
CreateWindowW(WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
201, 25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
column.pszText = L"Group";
HWND header = ListView_GetHeader(m_groupListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_groupListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_groupListView, CLR_NONE);
ListView_SetTextColor(m_groupListView, RGB(255, 255, 255));
ListView_InsertColumn(m_groupListView, 0, &column);
ShowWindow(m_groupListView, SW_SHOW);
m_groupListView =
CreateWindowW(WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
201, 25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
column.pszText = L"Group";
HWND header = ListView_GetHeader(m_groupListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_groupListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_groupListView, CLR_NONE);
ListView_SetTextColor(m_groupListView, RGB(255, 255, 255));
ListView_InsertColumn(m_groupListView, 0, &column);
ShowWindow(m_groupListView, SW_SHOW);
m_pageListView =
CreateWindowW(WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
401, 25, 198, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
column.pszText = L"Page";
column.cx = 198 - GetSystemMetrics(SM_CXVSCROLL);
header = ListView_GetHeader(m_pageListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_pageListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_pageListView, CLR_NONE);
ListView_SetTextColor(m_pageListView, RGB(255, 255, 255));
ListView_InsertColumn(m_pageListView, 0, &column);
ShowWindow(m_pageListView, SW_SHOW);
m_pageListView =
CreateWindowW(WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
401, 25, 198, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
column.pszText = L"Page";
column.cx = 198 - GetSystemMetrics(SM_CXVSCROLL);
header = ListView_GetHeader(m_pageListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_pageListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_pageListView, CLR_NONE);
ListView_SetTextColor(m_pageListView, RGB(255, 255, 255));
ListView_InsertColumn(m_pageListView, 0, &column);
ShowWindow(m_pageListView, SW_SHOW);
update();
return true;
}
void VSTEditor::close() {
AEffEditor::close();
UnregisterClassW(L"VSTNotify", HINSTANCE(hInstance));
}
void VSTEditor::update() {
m_backend.getFilePresenter().populateCollectionColumn(*this);
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
m_backend.setGroup(m_selGroupIdx, true);
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
selectPage(m_selPageIdx);
_reselectColumns();
}
void VSTEditor::addAction() {
std::wstring path = openDB();
if (path.size()) {
amuse::ContainerRegistry::Type containerType;
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>> data =
amuse::ContainerRegistry::LoadContainer(path.c_str(), containerType);
if (data.empty()) {
std::wstring msg = fmt::format(L"Unable to load Audio Groups from {}", path);
MessageBoxW(nullptr, msg.c_str(), L"Invalid Data File", MB_OK | MB_ICONERROR);
return;
}
SystemString name(amuse::ContainerRegistry::TypeToName(containerType));
if (containerType == amuse::ContainerRegistry::Type::Raw4) {
size_t dotpos = path.rfind(L'.');
if (dotpos != std::string::npos)
name.assign(path.cbegin(), path.cbegin() + dotpos);
size_t slashpos = name.rfind(L'\\');
size_t fslashpos = name.rfind(L'/');
if (slashpos == std::string::npos)
slashpos = fslashpos;
else if (fslashpos != std::string::npos)
slashpos = std::max(slashpos, fslashpos);
if (slashpos != std::string::npos)
name.assign(name.cbegin() + slashpos + 1, name.cend());
}
m_backend.getFilePresenter().addCollection(name, std::move(data));
update();
return true;
}
}
void VSTEditor::close()
{
AEffEditor::close();
UnregisterClassW(L"VSTNotify", HINSTANCE(hInstance));
void VSTEditor::removeAction() {
if (m_selCollectionIdx == -1)
return;
m_backend.getFilePresenter().removeCollection(m_selCollectionIdx);
m_backend.getFilePresenter().populateCollectionColumn(*this);
m_selCollectionIdx = -1;
m_selFileIdx = -1;
m_selGroupIdx = -1;
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
Button_Enable(m_collectionRemove, FALSE);
}
void VSTEditor::update()
{
m_backend.getFilePresenter().populateCollectionColumn(*this);
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
m_backend.setGroup(m_selGroupIdx, true);
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
selectPage(m_selPageIdx);
_reselectColumns();
}
void VSTEditor::addAction()
{
std::wstring path = openDB();
if (path.size())
{
amuse::ContainerRegistry::Type containerType;
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>> data =
amuse::ContainerRegistry::LoadContainer(path.c_str(), containerType);
if (data.empty())
{
wchar_t msg[512];
SNPrintf(msg, 512, L"Unable to load Audio Groups from %s", path.c_str());
MessageBoxW(nullptr, msg, L"Invalid Data File", MB_OK | MB_ICONERROR);
return;
}
SystemString name(amuse::ContainerRegistry::TypeToName(containerType));
if (containerType == amuse::ContainerRegistry::Type::Raw4)
{
size_t dotpos = path.rfind(L'.');
if (dotpos != std::string::npos)
name.assign(path.cbegin(), path.cbegin() + dotpos);
size_t slashpos = name.rfind(L'\\');
size_t fslashpos = name.rfind(L'/');
if (slashpos == std::string::npos)
slashpos = fslashpos;
else if (fslashpos != std::string::npos)
slashpos = std::max(slashpos, fslashpos);
if (slashpos != std::string::npos)
name.assign(name.cbegin() + slashpos + 1, name.cend());
}
m_backend.getFilePresenter().addCollection(name, std::move(data));
update();
}
}
void VSTEditor::removeAction()
{
if (m_selCollectionIdx == -1)
return;
m_backend.getFilePresenter().removeCollection(m_selCollectionIdx);
m_backend.getFilePresenter().populateCollectionColumn(*this);
m_selCollectionIdx = -1;
m_selFileIdx = -1;
m_selGroupIdx = -1;
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
void VSTEditor::selectCollection(LPARAM idx) {
if (0x80000000 & idx) {
/* Sub-item */
int rootIdx = (idx >> 16) & 0x7fff;
int subIdx = idx & 0xffff;
Button_Enable(m_collectionRemove, FALSE);
m_selCollectionIdx = rootIdx;
m_selFileIdx = subIdx;
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
m_backend.getFilePresenter().populateGroupColumn(*this, rootIdx, subIdx);
} else {
/* Root-item */
int rootIdx = (idx >> 16) & 0x7fff;
m_selCollectionIdx = rootIdx;
Button_Enable(m_collectionRemove, TRUE);
}
}
void VSTEditor::selectCollection(LPARAM idx)
{
if (0x80000000 & idx)
{
/* Sub-item */
int rootIdx = (idx >> 16) & 0x7fff;
int subIdx = idx & 0xffff;
Button_Enable(m_collectionRemove, FALSE);
m_selCollectionIdx = rootIdx;
m_selFileIdx = subIdx;
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
m_backend.getFilePresenter().populateGroupColumn(*this, rootIdx, subIdx);
}
void VSTEditor::selectGroup(int idx) {
m_selGroupIdx = idx;
m_backend.setGroup(m_selGroupIdx, false);
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
m_lastLParam = -1;
}
void VSTEditor::selectPage(int idx) {
m_selPageIdx = idx;
LV_ITEM item = {};
item.mask = LVIF_PARAM;
item.iItem = idx;
ListView_GetItem(m_pageListView, &item);
m_lastLParam = item.lParam;
if (item.lParam & 0x80000000)
selectDrumPage(item.lParam & 0x7fffffff);
else
selectNormalPage(item.lParam & 0x7fffffff);
}
void VSTEditor::reselectPage() {
if (m_lastLParam != -1) {
if (m_lastLParam & 0x80000000)
m_backend._setDrumProgram(m_lastLParam & 0x7fffffff);
else
{
/* Root-item */
int rootIdx = (idx >> 16) & 0x7fff;
m_selCollectionIdx = rootIdx;
Button_Enable(m_collectionRemove, TRUE);
}
}
void VSTEditor::selectGroup(int idx)
{
m_selGroupIdx = idx;
m_backend.setGroup(m_selGroupIdx, false);
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
m_lastLParam = -1;
}
void VSTEditor::selectPage(int idx)
{
m_selPageIdx = idx;
LV_ITEM item = {};
item.mask = LVIF_PARAM;
item.iItem = idx;
ListView_GetItem(m_pageListView, &item);
m_lastLParam = item.lParam;
if (item.lParam & 0x80000000)
selectDrumPage(item.lParam & 0x7fffffff);
else
selectNormalPage(item.lParam & 0x7fffffff);
}
void VSTEditor::reselectPage()
{
if (m_lastLParam != -1)
{
if (m_lastLParam & 0x80000000)
m_backend._setDrumProgram(m_lastLParam & 0x7fffffff);
else
m_backend._setNormalProgram(m_lastLParam & 0x7fffffff);
}
m_backend._setNormalProgram(m_lastLParam & 0x7fffffff);
}
}
void VSTEditor::selectNormalPage(int idx) { m_backend.setNormalProgram(idx); }
void VSTEditor::selectDrumPage(int idx) { m_backend.setDrumProgram(idx); }
}
} // namespace amuse

View File

@@ -1,5 +1,4 @@
#ifndef __AMUSE_VSTEDITOR_HPP__
#define __AMUSE_VSTEDITOR_HPP__
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -7,58 +6,54 @@
#include "aeffeditor.h"
namespace amuse
{
namespace amuse {
class VSTBackend;
/** Editor UI class */
class VSTEditor : public AEffEditor
{
friend class VSTBackend;
friend class AudioGroupFilePresenter;
friend struct AudioGroupCollection;
class VSTEditor : public AEffEditor {
friend class VSTBackend;
friend class AudioGroupFilePresenter;
friend struct AudioGroupCollection;
VSTBackend& m_backend;
ERect m_windowRect = {0, 0, 420, 600};
VSTBackend& m_backend;
ERect m_windowRect = {0, 0, 420, 600};
HWND m_rootView = 0;
HWND m_collectionTree = 0;
HWND m_collectionAdd = 0;
HWND m_collectionRemove = 0;
HWND m_groupListView = 0;
HWND m_pageListView = 0;
HWND m_rootView = 0;
HWND m_collectionTree = 0;
HWND m_collectionAdd = 0;
HWND m_collectionRemove = 0;
HWND m_groupListView = 0;
HWND m_pageListView = 0;
int m_selCollectionIdx = -1;
int m_selFileIdx = -1;
int m_selGroupIdx = -1;
int m_selPageIdx = -1;
int m_lastLParam = -1;
int m_selCollectionIdx = -1;
int m_selFileIdx = -1;
int m_selGroupIdx = -1;
int m_selPageIdx = -1;
int m_lastLParam = -1;
HTREEITEM m_deferredCollectionSel = 0;
HTREEITEM m_deferredCollectionSel = 0;
static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
static LRESULT CALLBACK ColHeaderWindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
static LRESULT CALLBACK ColHeaderWindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
void _reselectColumns();
void _reselectColumns();
public:
VSTEditor(VSTBackend& backend);
VSTEditor(VSTBackend& backend);
bool getRect(ERect** rect);
bool open(void* ptr);
void close();
void update();
bool getRect(ERect** rect);
bool open(void* ptr);
void close();
void update();
void addAction();
void removeAction();
void addAction();
void removeAction();
void selectCollection(LPARAM idx);
void selectGroup(int idx);
void selectPage(int idx);
void reselectPage();
void selectNormalPage(int idx);
void selectDrumPage(int idx);
void selectCollection(LPARAM idx);
void selectGroup(int idx);
void selectPage(int idx);
void reselectPage();
void selectNormalPage(int idx);
void selectDrumPage(int idx);
};
}
#endif // __AMUSE_VSTEDITOR_HPP__
} // namespace amuse

View File

@@ -7,130 +7,111 @@
static logvisor::Module Log("amuseconv");
enum ConvType
{
ConvN64,
ConvGCN,
ConvPC
};
enum ConvType { ConvN64, ConvGCN, ConvPC };
static void ReportConvType(ConvType tp)
{
switch (tp)
{
case ConvN64:
Log.report(logvisor::Info, _S("using N64 format"));
break;
case ConvPC:
Log.report(logvisor::Info, _S("using PC format"));
break;
case ConvGCN:
default:
Log.report(logvisor::Info, _S("using GameCube format"));
break;
static void ReportConvType(ConvType tp) {
switch (tp) {
case ConvN64:
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("using N64 format")));
break;
case ConvPC:
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("using PC format")));
break;
case ConvGCN:
default:
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("using GameCube format")));
break;
}
}
static bool BuildAudioGroup(amuse::SystemStringView groupBase, amuse::SystemStringView targetPath) { return true; }
static bool ExtractAudioGroup(amuse::SystemStringView inPath, amuse::SystemStringView targetPath) {
amuse::ContainerRegistry::Type type;
auto groups = amuse::ContainerRegistry::LoadContainer(inPath.data(), type);
if (groups.size()) {
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Found '{}'")), amuse::ContainerRegistry::TypeToName(type));
amuse::Mkdir(targetPath.data(), 0755);
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Established directory at {}")), targetPath);
for (auto& group : groups) {
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Extracting {}")), group.first);
}
}
}
static bool BuildAudioGroup(amuse::SystemStringView groupBase, amuse::SystemStringView targetPath)
{
return true;
}
static bool ExtractAudioGroup(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
{
amuse::ContainerRegistry::Type type;
auto groups = amuse::ContainerRegistry::LoadContainer(inPath.data(), type);
if (groups.size())
{
Log.report(logvisor::Info, _S("Found '%s'"), amuse::ContainerRegistry::TypeToName(type));
amuse::Mkdir(targetPath.data(), 0755);
Log.report(logvisor::Info, _S("Established directory at %s"), targetPath.data());
for (auto& group : groups)
{
Log.report(logvisor::Info, _S("Extracting %s"), group.first.c_str());
}
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _SYS_STR("/midifiles");
bool madeDir = false;
for (auto& pair : songs) {
if (!madeDir) {
amuse::Mkdir(targetPath.data(), 0755);
amuse::Mkdir(songsDir.c_str(), 0755);
madeDir = true;
}
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _S("/midifiles");
bool madeDir = false;
for (auto& pair : songs)
{
if (!madeDir)
{
amuse::Mkdir(targetPath.data(), 0755);
amuse::Mkdir(songsDir.c_str(), 0755);
madeDir = true;
}
amuse::SystemString songPath = songsDir + _S('/') + pair.first + _S(".mid");
FILE* fp = amuse::FOpen(songPath.c_str(), _S("wb"));
if (fp)
{
Log.report(logvisor::Info, _S("Extracting %s"), pair.first.c_str());
int extractedVersion;
bool isBig;
std::vector<uint8_t> mid =
amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedVersion, isBig);
fwrite(mid.data(), 1, mid.size(), fp);
fclose(fp);
}
amuse::SystemString songPath = songsDir + _SYS_STR('/') + pair.first + _SYS_STR(".mid");
FILE* fp = amuse::FOpen(songPath.c_str(), _SYS_STR("wb"));
if (fp) {
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Extracting {}")), pair.first);
int extractedVersion;
bool isBig;
std::vector<uint8_t> mid = amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedVersion, isBig);
fwrite(mid.data(), 1, mid.size(), fp);
fclose(fp);
}
}
return true;
return true;
}
static bool BuildSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath, int version, bool big)
{
FILE* fp = amuse::FOpen(inPath.data(), _S("rb"));
if (!fp)
return false;
static bool BuildSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath, int version, bool big) {
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
if (!fp)
return false;
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
if (out.empty())
return false;
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
if (out.empty())
return false;
fp = amuse::FOpen(targetPath.data(), _S("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
return true;
return true;
}
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
{
FILE* fp = amuse::FOpen(inPath.data(), _S("rb"));
if (!fp)
return false;
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath) {
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
if (!fp)
return false;
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
int extractedVersion;
bool isBig;
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
if (out.empty())
return false;
int extractedVersion;
bool isBig;
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
if (out.empty())
return false;
fp = amuse::FOpen(targetPath.data(), _S("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
return true;
return true;
}
#if _WIN32
@@ -139,77 +120,64 @@ int wmain(int argc, const amuse::SystemChar** argv)
int main(int argc, const amuse::SystemChar** argv)
#endif
{
logvisor::RegisterConsoleLogger();
if (argc < 3)
{
printf("Usage: amuseconv <in-file> <out-file> [n64|pc|gcn]\n");
return 0;
}
ConvType type = ConvGCN;
if (argc >= 4)
{
if (!amuse::CompareCaseInsensitive(argv[3], _S("n64")))
type = ConvN64;
else if (!amuse::CompareCaseInsensitive(argv[3], _S("gcn")))
type = ConvGCN;
else if (!amuse::CompareCaseInsensitive(argv[3], _S("pc")))
type = ConvPC;
else
{
Log.report(logvisor::Error, _S("unrecognized format: %s"), argv[3]);
return 1;
}
}
bool good = false;
FILE* fin = amuse::FOpen(argv[1], _S("rb"));
if (fin)
{
fclose(fin);
amuse::SystemString barePath(argv[1]);
size_t dotPos = barePath.rfind(_S('.'));
const amuse::SystemChar* dot = barePath.c_str() + dotPos;
if (dotPos != amuse::SystemString::npos)
{
if (!amuse::CompareCaseInsensitive(dot, _S(".mid")) || !amuse::CompareCaseInsensitive(dot, _S(".midi")))
{
ReportConvType(type);
good = BuildSNG(barePath, argv[2], 1, true);
}
else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) || !amuse::CompareCaseInsensitive(dot, _S(".sng")))
{
good = ExtractSNG(argv[1], argv[2]);
}
else
{
good = ExtractAudioGroup(argv[1], argv[2]);
}
}
}
else
{
amuse::Sstat theStat;
if (!amuse::Stat(argv[1], &theStat) && S_ISDIR(theStat.st_mode))
{
amuse::SystemString projectPath(argv[1]);
projectPath += _S("/project.yaml");
fin = amuse::FOpen(projectPath.c_str(), _S("rb"));
if (fin)
{
fclose(fin);
ReportConvType(type);
good = BuildAudioGroup(argv[1], argv[2]);
}
}
}
if (!good)
{
Log.report(logvisor::Error, _S("unable to convert %s to %s"), argv[1], argv[2]);
return 1;
}
logvisor::RegisterConsoleLogger();
if (argc < 3) {
fmt::print(FMT_STRING("Usage: amuseconv <in-file> <out-file> [n64|pc|gcn]\n"));
return 0;
}
ConvType type = ConvGCN;
if (argc >= 4) {
if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("n64")))
type = ConvN64;
else if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("gcn")))
type = ConvGCN;
else if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("pc")))
type = ConvPC;
else {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unrecognized format: {}")), argv[3]);
return 1;
}
}
bool good = false;
FILE* fin = amuse::FOpen(argv[1], _SYS_STR("rb"));
if (fin) {
fclose(fin);
amuse::SystemString barePath(argv[1]);
size_t dotPos = barePath.rfind(_SYS_STR('.'));
const amuse::SystemChar* dot = barePath.c_str() + dotPos;
if (dotPos != amuse::SystemString::npos) {
if (!amuse::CompareCaseInsensitive(dot, _SYS_STR(".mid")) ||
!amuse::CompareCaseInsensitive(dot, _SYS_STR(".midi"))) {
ReportConvType(type);
good = BuildSNG(barePath, argv[2], 1, true);
} else if (!amuse::CompareCaseInsensitive(dot, _SYS_STR(".son")) ||
!amuse::CompareCaseInsensitive(dot, _SYS_STR(".sng"))) {
good = ExtractSNG(argv[1], argv[2]);
} else {
good = ExtractAudioGroup(argv[1], argv[2]);
}
}
} else {
amuse::Sstat theStat;
if (!amuse::Stat(argv[1], &theStat) && S_ISDIR(theStat.st_mode)) {
amuse::SystemString projectPath(argv[1]);
projectPath += _SYS_STR("/project.yaml");
fin = amuse::FOpen(projectPath.c_str(), _SYS_STR("rb"));
if (fin) {
fclose(fin);
ReportConvType(type);
good = BuildAudioGroup(argv[1], argv[2]);
}
}
}
if (!good) {
Log.report(logvisor::Error, FMT_STRING(_SYS_STR("unable to convert {} to {}")), argv[1], argv[2]);
return 1;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
#include "boo/boo.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "logvisor/logvisor.hpp"
#include "optional.hpp"
#include <optional>
#include <cstdio>
#include <cstring>
#include <signal.h>
@@ -17,71 +17,50 @@
static logvisor::Module Log("amuserender");
#if __GNUC__
__attribute__((__format__(__printf__, 3, 4)))
#endif
static inline void
SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...)
{
va_list va;
va_start(va, format);
#if _WIN32
_vsnwprintf(str, maxlen, format, va);
#else
vsnprintf(str, maxlen, format, va);
#endif
va_end(va);
}
#if _WIN32
#include <DbgHelp.h>
#pragma comment(lib, "Dbghelp.lib")
#include <signal.h>
static void abortHandler(int signum)
{
unsigned int i;
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
HANDLE process;
static void abortHandler(int signum) {
unsigned int i;
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for (i = 0; i < frames; i++)
{
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
for (i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
printf("%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
printf("%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line))
{
// SymGetLineFromAddr64 returned success
printf(" LINE %d\n", line.LineNumber);
}
else
{
printf("\n");
}
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
// SymGetLineFromAddr64 returned success
printf(" LINE %d\n", line.LineNumber);
} else {
printf("\n");
}
}
free(symbol);
free(symbol);
// If you caught one of the above signals, it is likely you just
// want to quit your program right now.
system("PAUSE");
exit(signum);
// If you caught one of the above signals, it is likely you just
// want to quit your program right now.
system("PAUSE");
exit(signum);
}
#endif
@@ -95,456 +74,376 @@ int wmain(int argc, const boo::SystemChar** argv)
int main(int argc, const boo::SystemChar** argv)
#endif
{
logvisor::RegisterConsoleLogger();
logvisor::RegisterConsoleLogger();
std::vector<boo::SystemString> m_args;
m_args.reserve(argc);
double rate = NativeSampleRate;
int chCount = 2;
double volume = 1.0;
for (int i = 1; i < argc; ++i)
{
std::vector<boo::SystemString> m_args;
m_args.reserve(argc);
double rate = NativeSampleRate;
int chCount = 2;
double volume = 1.0;
for (int i = 1; i < argc; ++i) {
#if _WIN32
if (!wcsncmp(argv[i], L"-r", 2))
{
if (argv[i][2])
rate = wcstod(&argv[i][2], nullptr);
else if (argc > (i + 1))
{
rate = wcstod(argv[i + 1], nullptr);
++i;
}
}
else if (!wcsncmp(argv[i], L"-c", 2))
{
if (argv[i][2])
chCount = wcstoul(&argv[i][2], nullptr, 0);
else if (argc > (i + 1))
{
chCount = wcstoul(argv[i + 1], nullptr, 0);
++i;
}
}
else if (!wcsncmp(argv[i], L"-v", 2))
{
if (argv[i][2])
volume = wcstod(&argv[i][2], nullptr);
else if (argc > (i + 1))
{
volume = wcstod(argv[i + 1], nullptr);
++i;
}
}
else
m_args.push_back(argv[i]);
if (!wcsncmp(argv[i], L"-r", 2)) {
if (argv[i][2])
rate = wcstod(&argv[i][2], nullptr);
else if (argc > (i + 1)) {
rate = wcstod(argv[i + 1], nullptr);
++i;
}
} else if (!wcsncmp(argv[i], L"-c", 2)) {
if (argv[i][2])
chCount = wcstoul(&argv[i][2], nullptr, 0);
else if (argc > (i + 1)) {
chCount = wcstoul(argv[i + 1], nullptr, 0);
++i;
}
} else if (!wcsncmp(argv[i], L"-v", 2)) {
if (argv[i][2])
volume = wcstod(&argv[i][2], nullptr);
else if (argc > (i + 1)) {
volume = wcstod(argv[i + 1], nullptr);
++i;
}
} else
m_args.push_back(argv[i]);
#else
if (!strncmp(argv[i], "-r", 2))
{
if (argv[i][2])
rate = strtod(&argv[i][2], nullptr);
else if (argc > (i + 1))
{
rate = strtod(argv[i + 1], nullptr);
++i;
}
}
else if (!strncmp(argv[i], "-c", 2))
{
if (argv[i][2])
chCount = strtoul(&argv[i][2], nullptr, 0);
else if (argc > (i + 1))
{
chCount = strtoul(argv[i + 1], nullptr, 0);
++i;
}
}
else if (!strncmp(argv[i], "-v", 2))
{
if (argv[i][2])
volume = strtod(&argv[i][2], nullptr);
else if (argc > (i + 1))
{
volume = strtod(argv[i + 1], nullptr);
++i;
}
}
else
m_args.push_back(argv[i]);
if (!strncmp(argv[i], "-r", 2)) {
if (argv[i][2])
rate = strtod(&argv[i][2], nullptr);
else if (argc > (i + 1)) {
rate = strtod(argv[i + 1], nullptr);
++i;
}
} else if (!strncmp(argv[i], "-c", 2)) {
if (argv[i][2])
chCount = strtoul(&argv[i][2], nullptr, 0);
else if (argc > (i + 1)) {
chCount = strtoul(argv[i + 1], nullptr, 0);
++i;
}
} else if (!strncmp(argv[i], "-v", 2)) {
if (argv[i][2])
volume = strtod(&argv[i][2], nullptr);
else if (argc > (i + 1)) {
volume = strtod(argv[i + 1], nullptr);
++i;
}
} else
m_args.push_back(argv[i]);
#endif
}
/* Load data */
if (m_args.size() < 1) {
Log.report(logvisor::Error,
FMT_STRING("Usage: amuserender <group-file> [<songs-file>] [-r <sample-rate>] [-c <channel-count>] [-v <volume "
"0.0-1.0>]"));
return 1;
}
amuse::ContainerRegistry::Type cType = amuse::ContainerRegistry::DetectContainerType(m_args[0].c_str());
if (cType == amuse::ContainerRegistry::Type::Invalid) {
Log.report(logvisor::Error, FMT_STRING("invalid/no data at path argument"));
return 1;
}
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Found '{}' Audio Group data")), amuse::ContainerRegistry::TypeToName(cType));
std::vector<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>> data =
amuse::ContainerRegistry::LoadContainer(m_args[0].c_str());
if (data.empty()) {
Log.report(logvisor::Error, FMT_STRING("invalid/no data at path argument"));
return 1;
}
int m_groupId = -1;
int m_setupId = -1;
const amuse::SystemString* m_groupName = nullptr;
const amuse::SystemString* m_songName = nullptr;
amuse::ContainerRegistry::SongData* m_arrData = nullptr;
bool m_sfxGroup = false;
std::list<amuse::AudioGroupProject> m_projs;
std::map<amuse::GroupId, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
amuse::ObjToken<amuse::SongGroupIndex>>>
allSongGroups;
std::map<amuse::GroupId, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
amuse::ObjToken<amuse::SFXGroupIndex>>>
allSFXGroups;
size_t totalGroups = 0;
for (auto& grp : data) {
/* Load project to assemble group list */
m_projs.push_back(amuse::AudioGroupProject::CreateAudioGroupProject(grp.second));
amuse::AudioGroupProject& proj = m_projs.back();
totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it)
allSongGroups[it->first] = std::make_pair(&grp, it->second);
for (auto it = proj.sfxGroups().begin(); it != proj.sfxGroups().end(); ++it)
allSFXGroups[it->first] = std::make_pair(&grp, it->second);
}
/* Attempt loading song */
std::vector<std::pair<amuse::SystemString, amuse::ContainerRegistry::SongData>> songs;
if (m_args.size() > 1)
songs = amuse::ContainerRegistry::LoadSongs(m_args[1].c_str());
else
songs = amuse::ContainerRegistry::LoadSongs(m_args[0].c_str());
if (songs.size()) {
bool play = true;
if (m_args.size() <= 1) {
bool prompt = true;
while (true) {
if (prompt) {
fmt::print(FMT_STRING("Render Song? (Y/N): "));
prompt = false;
}
char userSel;
if (scanf("%c", &userSel) <= 0 || userSel == '\n')
continue;
userSel = tolower(userSel);
if (userSel == 'n')
play = false;
else if (userSel != 'y') {
prompt = true;
continue;
}
break;
}
}
/* Load data */
if (m_args.size() < 1)
{
Log.report(logvisor::Error, "Usage: amuserender <group-file> [<songs-file>] [-r <sample-rate>] [-c <channel-count>] [-v <volume 0.0-1.0>]");
return 1;
}
amuse::ContainerRegistry::Type cType = amuse::ContainerRegistry::DetectContainerType(m_args[0].c_str());
if (cType == amuse::ContainerRegistry::Type::Invalid)
{
Log.report(logvisor::Error, "invalid/no data at path argument");
return 1;
}
Log.report(logvisor::Info, _S("Found '%s' Audio Group data"), amuse::ContainerRegistry::TypeToName(cType));
std::vector<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>> data =
amuse::ContainerRegistry::LoadContainer(m_args[0].c_str());
if (data.empty())
{
Log.report(logvisor::Error, "invalid/no data at path argument");
return 1;
}
int m_groupId = -1;
int m_setupId = -1;
const amuse::SystemString* m_groupName = nullptr;
const amuse::SystemString* m_songName = nullptr;
amuse::ContainerRegistry::SongData* m_arrData = nullptr;
bool m_sfxGroup = false;
std::list<amuse::AudioGroupProject> m_projs;
std::map<int,
std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, amuse::ObjToken<amuse::SongGroupIndex>>>
allSongGroups;
std::map<int,
std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, amuse::ObjToken<amuse::SFXGroupIndex>>>
allSFXGroups;
size_t totalGroups = 0;
for (auto& grp : data)
{
/* Load project to assemble group list */
m_projs.push_back(amuse::AudioGroupProject::CreateAudioGroupProject(grp.second));
amuse::AudioGroupProject& proj = m_projs.back();
totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it)
allSongGroups[it->first] = std::make_pair(&grp, it->second);
for (auto it = proj.sfxGroups().begin(); it != proj.sfxGroups().end(); ++it)
allSFXGroups[it->first] = std::make_pair(&grp, it->second);
}
/* Attempt loading song */
std::vector<std::pair<amuse::SystemString, amuse::ContainerRegistry::SongData>> songs;
if (m_args.size() > 1)
songs = amuse::ContainerRegistry::LoadSongs(m_args[1].c_str());
else
songs = amuse::ContainerRegistry::LoadSongs(m_args[0].c_str());
if (songs.size())
{
bool play = true;
if (m_args.size() <= 1)
{
bool prompt = true;
while (true)
{
if (prompt)
{
printf("Render Song? (Y/N): ");
prompt = false;
}
char userSel;
if (scanf("%c", &userSel) <= 0 || userSel == '\n')
continue;
userSel = tolower(userSel);
if (userSel == 'n')
play = false;
else if (userSel != 'y')
{
prompt = true;
continue;
if (play) {
/* Get song selection from user */
if (songs.size() > 1) {
/* Ask user to specify which song */
fmt::print(FMT_STRING("Multiple Songs discovered:\n"));
int idx = 0;
for (const auto& pair : songs) {
const amuse::ContainerRegistry::SongData& sngData = pair.second;
int16_t grpId = sngData.m_groupId;
int16_t setupId = sngData.m_setupId;
if (sngData.m_groupId == -1 && sngData.m_setupId != -1) {
for (const auto& pair : allSongGroups) {
for (const auto& setup : pair.second.second->m_midiSetups) {
if (setup.first == sngData.m_setupId) {
grpId = pair.first.id;
break;
}
}
if (grpId != -1)
break;
}
}
if (play)
{
/* Get song selection from user */
if (songs.size() > 1)
{
/* Ask user to specify which song */
printf("Multiple Songs discovered:\n");
int idx = 0;
for (const auto& pair : songs)
{
const amuse::ContainerRegistry::SongData& sngData = pair.second;
int16_t grpId = sngData.m_groupId;
int16_t setupId = sngData.m_setupId;
if (sngData.m_groupId == -1 && sngData.m_setupId != -1)
{
for (const auto& pair : allSongGroups)
{
for (const auto& setup : pair.second.second->m_midiSetups)
{
if (setup.first == sngData.m_setupId)
{
grpId = pair.first;
break;
}
}
if (grpId != -1)
break;
}
}
amuse::Printf(_S(" %d %s (Group %d, Setup %d)\n"), idx++, pair.first.c_str(), grpId, setupId);
}
int userSel = 0;
printf("Enter Song Number: ");
if (scanf("%d", &userSel) <= 0)
{
Log.report(logvisor::Error, "unable to parse prompt");
return 1;
}
if (userSel < songs.size())
{
m_arrData = &songs[userSel].second;
m_groupId = m_arrData->m_groupId;
m_setupId = m_arrData->m_setupId;
m_songName = &songs[userSel].first;
}
else
{
Log.report(logvisor::Error, "unable to find Song %d", userSel);
return 1;
}
}
else if (songs.size() == 1)
{
m_arrData = &songs[0].second;
m_groupId = m_arrData->m_groupId;
m_setupId = m_arrData->m_setupId;
m_songName = &songs[0].first;
}
}
}
/* Get group selection via setup search */
if (m_groupId == -1 && m_setupId != -1)
{
for (const auto& pair : allSongGroups)
{
for (const auto& setup : pair.second.second->m_midiSetups)
{
if (setup.first == m_setupId)
{
m_groupId = pair.first;
m_groupName = &pair.second.first->first;
break;
}
}
if (m_groupId != -1)
break;
}
}
/* Get group selection via user */
if (m_groupId != -1)
{
auto songSearch = allSongGroups.find(m_groupId);
auto sfxSearch = allSFXGroups.find(m_groupId);
if (songSearch != allSongGroups.end())
{
m_sfxGroup = false;
m_groupName = &songSearch->second.first->first;
}
else if (sfxSearch != allSFXGroups.end())
{
m_sfxGroup = true;
m_groupName = &sfxSearch->second.first->first;
}
else
{
Log.report(logvisor::Error, "unable to find Group %d", m_groupId);
return 1;
}
}
else if (totalGroups > 1)
{
/* Ask user to specify which group in project */
printf("Multiple Audio Groups discovered:\n");
for (const auto& pair : allSFXGroups)
{
amuse::Printf(_S(" %d %s (SFXGroup) %" PRISize " sfx-entries\n"), pair.first,
pair.second.first->first.c_str(), pair.second.second->m_sfxEntries.size());
}
for (const auto& pair : allSongGroups)
{
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize
" MIDI-setups\n"),
pair.first, pair.second.first->first.c_str(), pair.second.second->m_normPages.size(),
pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size());
}
fmt::print(FMT_STRING(_SYS_STR(" {} {} (Group {}, Setup {})\n")), idx++, pair.first, grpId, setupId);
}
int userSel = 0;
printf("Enter Group Number: ");
if (scanf("%d", &userSel) <= 0)
{
Log.report(logvisor::Error, "unable to parse prompt");
return 1;
fmt::print(FMT_STRING("Enter Song Number: "));
if (scanf("%d", &userSel) <= 0) {
Log.report(logvisor::Error, FMT_STRING("unable to parse prompt"));
return 1;
}
auto songSearch = allSongGroups.find(userSel);
auto sfxSearch = allSFXGroups.find(userSel);
if (songSearch != allSongGroups.end())
{
m_groupId = userSel;
m_groupName = &songSearch->second.first->first;
m_sfxGroup = false;
}
else if (sfxSearch != allSFXGroups.end())
{
m_groupId = userSel;
m_groupName = &sfxSearch->second.first->first;
m_sfxGroup = true;
}
else
{
Log.report(logvisor::Error, "unable to find Group %d", userSel);
return 1;
if (userSel < songs.size()) {
m_arrData = &songs[userSel].second;
m_groupId = m_arrData->m_groupId;
m_setupId = m_arrData->m_setupId;
m_songName = &songs[userSel].first;
} else {
Log.report(logvisor::Error, FMT_STRING("unable to find Song {}"), userSel);
return 1;
}
} else if (songs.size() == 1) {
m_arrData = &songs[0].second;
m_groupId = m_arrData->m_groupId;
m_setupId = m_arrData->m_setupId;
m_songName = &songs[0].first;
}
}
else if (totalGroups == 1)
{
/* Load one and only group */
if (allSongGroups.size())
{
const auto& pair = *allSongGroups.cbegin();
m_groupId = pair.first;
m_groupName = &pair.second.first->first;
m_sfxGroup = false;
}
else
{
const auto& pair = *allSFXGroups.cbegin();
m_groupId = pair.first;
m_groupName = &pair.second.first->first;
m_sfxGroup = true;
}
}
else
{
Log.report(logvisor::Error, "empty project");
return 1;
}
}
/* Make final group selection */
amuse::IntrusiveAudioGroupData* selData = nullptr;
amuse::ObjToken<amuse::SongGroupIndex> songIndex;
amuse::ObjToken<amuse::SFXGroupIndex> sfxIndex;
/* Get group selection via setup search */
if (m_groupId == -1 && m_setupId != -1) {
for (const auto& pair : allSongGroups) {
for (const auto& setup : pair.second.second->m_midiSetups) {
if (setup.first == m_setupId) {
m_groupId = pair.first.id;
m_groupName = &pair.second.first->first;
break;
}
}
if (m_groupId != -1)
break;
}
}
/* Get group selection via user */
if (m_groupId != -1) {
auto songSearch = allSongGroups.find(m_groupId);
if (songSearch != allSongGroups.end())
{
selData = &songSearch->second.first->second;
songIndex = songSearch->second.second;
std::set<int> sortSetups;
for (auto& pair : songIndex->m_midiSetups)
sortSetups.insert(pair.first);
if (m_setupId == -1)
{
/* Ask user to specify which group in project */
printf("Multiple MIDI Setups:\n");
for (int setup : sortSetups)
printf(" %d\n", setup);
int userSel = 0;
printf("Enter Setup Number: ");
if (scanf("%d", &userSel) <= 0)
{
Log.report(logvisor::Error, "unable to parse prompt");
return 1;
}
m_setupId = userSel;
}
if (sortSetups.find(m_setupId) == sortSetups.cend())
{
Log.report(logvisor::Error, "unable to find setup %d", m_setupId);
return 1;
}
auto sfxSearch = allSFXGroups.find(m_groupId);
if (songSearch != allSongGroups.end()) {
m_sfxGroup = false;
m_groupName = &songSearch->second.first->first;
} else if (sfxSearch != allSFXGroups.end()) {
m_sfxGroup = true;
m_groupName = &sfxSearch->second.first->first;
} else {
Log.report(logvisor::Error, FMT_STRING("unable to find Group {}"), m_groupId);
return 1;
}
else
{
auto sfxSearch = allSFXGroups.find(m_groupId);
if (sfxSearch != allSFXGroups.end())
{
selData = &sfxSearch->second.first->second;
sfxIndex = sfxSearch->second.second;
}
} else if (totalGroups > 1) {
/* Ask user to specify which group in project */
fmt::print(FMT_STRING("Multiple Audio Groups discovered:\n"));
for (const auto& pair : allSFXGroups) {
fmt::print(FMT_STRING(_SYS_STR(" {} {} (SFXGroup) {} sfx-entries\n")), pair.first,
pair.second.first->first, pair.second.second->m_sfxEntries.size());
}
for (const auto& pair : allSongGroups) {
fmt::print(FMT_STRING(_SYS_STR(" {} {} (SongGroup) {} normal-pages, {} drum-pages, {} MIDI-setups\n")),
pair.first, pair.second.first->first, pair.second.second->m_normPages.size(),
pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size());
}
if (!selData)
{
Log.report(logvisor::Error, "unable to select audio group data");
int userSel = 0;
fmt::print(FMT_STRING("Enter Group Number: "));
if (scanf("%d", &userSel) <= 0) {
Log.report(logvisor::Error, FMT_STRING("unable to parse prompt"));
return 1;
}
auto songSearch = allSongGroups.find(userSel);
auto sfxSearch = allSFXGroups.find(userSel);
if (songSearch != allSongGroups.end()) {
m_groupId = userSel;
m_groupName = &songSearch->second.first->first;
m_sfxGroup = false;
} else if (sfxSearch != allSFXGroups.end()) {
m_groupId = userSel;
m_groupName = &sfxSearch->second.first->first;
m_sfxGroup = true;
} else {
Log.report(logvisor::Error, FMT_STRING("unable to find Group {}"), userSel);
return 1;
}
} else if (totalGroups == 1) {
/* Load one and only group */
if (allSongGroups.size()) {
const auto& pair = *allSongGroups.cbegin();
m_groupId = pair.first.id;
m_groupName = &pair.second.first->first;
m_sfxGroup = false;
} else {
const auto& pair = *allSFXGroups.cbegin();
m_groupId = pair.first.id;
m_groupName = &pair.second.first->first;
m_sfxGroup = true;
}
} else {
Log.report(logvisor::Error, FMT_STRING("empty project"));
return 1;
}
/* Make final group selection */
amuse::IntrusiveAudioGroupData* selData = nullptr;
amuse::ObjToken<amuse::SongGroupIndex> songIndex;
amuse::ObjToken<amuse::SFXGroupIndex> sfxIndex;
auto songSearch = allSongGroups.find(m_groupId);
if (songSearch != allSongGroups.end()) {
selData = &songSearch->second.first->second;
songIndex = songSearch->second.second;
std::set<amuse::SongId> sortSetups;
for (auto& pair : songIndex->m_midiSetups)
sortSetups.insert(pair.first);
if (m_setupId == -1) {
/* Ask user to specify which group in project */
fmt::print(FMT_STRING("Multiple MIDI Setups:\n"));
for (auto setup : sortSetups)
fmt::print(FMT_STRING(" {}\n"), setup);
int userSel = 0;
fmt::print(FMT_STRING("Enter Setup Number: "));
if (scanf("%d", &userSel) <= 0) {
Log.report(logvisor::Error, FMT_STRING("unable to parse prompt"));
return 1;
}
m_setupId = userSel;
}
if (m_sfxGroup)
{
Log.report(logvisor::Error, "amuserender is currently only able to render SongGroups");
return 1;
if (sortSetups.find(m_setupId) == sortSetups.cend()) {
Log.report(logvisor::Error, FMT_STRING("unable to find setup {}"), m_setupId);
return 1;
}
/* WAV out path */
amuse::SystemChar pathOut[1024];
SNPrintf(pathOut, 1024, _S("%s-%s.wav"), m_groupName->c_str(), m_songName->c_str());
Log.report(logvisor::Info, _S("Writing to %s"), pathOut);
/* Build voice engine */
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewWAVAudioVoiceEngine(pathOut, rate, chCount);
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
amuse::Engine engine(booBackend, amuse::AmplitudeMode::PerSample);
engine.setVolume(float(amuse::clamp(0.0, volume, 1.0)));
/* Load group into engine */
const amuse::AudioGroup* group = engine.addAudioGroup(*selData);
if (!group)
{
Log.report(logvisor::Error, "unable to add audio group");
return 1;
} else {
auto sfxSearch = allSFXGroups.find(m_groupId);
if (sfxSearch != allSFXGroups.end()) {
selData = &sfxSearch->second.first->second;
sfxIndex = sfxSearch->second.second;
}
}
/* Enter playback loop */
amuse::ObjToken<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get(), false);
size_t wroteFrames = 0;
signal(SIGINT, SIGINTHandler);
do
{
voxEngine->pumpAndMixVoices();
wroteFrames += voxEngine->get5MsFrames();
printf("\rFrame %" PRISize, wroteFrames);
fflush(stdout);
} while (!g_BreakLoop && (seq->state() == amuse::SequencerState::Playing || seq->getVoiceCount() != 0));
if (!selData) {
Log.report(logvisor::Error, FMT_STRING("unable to select audio group data"));
return 1;
}
printf("\n");
return 0;
if (m_sfxGroup) {
Log.report(logvisor::Error, FMT_STRING("amuserender is currently only able to render SongGroups"));
return 1;
}
/* WAV out path */
amuse::SystemString pathOut = fmt::format(FMT_STRING(_SYS_STR("{}-{}.wav")), *m_groupName, *m_songName);
Log.report(logvisor::Info, FMT_STRING(_SYS_STR("Writing to {}")), pathOut);
/* Build voice engine */
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewWAVAudioVoiceEngine(pathOut.c_str(), rate, chCount);
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
amuse::Engine engine(booBackend, amuse::AmplitudeMode::PerSample);
engine.setVolume(float(std::clamp(0.0, volume, 1.0)));
/* Load group into engine */
const amuse::AudioGroup* group = engine.addAudioGroup(*selData);
if (!group) {
Log.report(logvisor::Error, FMT_STRING("unable to add audio group"));
return 1;
}
/* Enter playback loop */
amuse::ObjToken<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get(), false);
size_t wroteFrames = 0;
signal(SIGINT, SIGINTHandler);
do {
voxEngine->pumpAndMixVoices();
wroteFrames += voxEngine->get5MsFrames();
fmt::print(FMT_STRING("\rFrame {}"), wroteFrames);
fflush(stdout);
} while (!g_BreakLoop && (seq->state() == amuse::SequencerState::Playing || seq->getVoiceCount() != 0));
fmt::print(FMT_STRING("\n"));
return 0;
}
#if _WIN32
#include <shellapi.h>
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
{
signal(SIGABRT, abortHandler);
signal(SIGSEGV, abortHandler);
signal(SIGILL, abortHandler);
signal(SIGFPE, abortHandler);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) {
signal(SIGABRT, abortHandler);
signal(SIGSEGV, abortHandler);
signal(SIGILL, abortHandler);
signal(SIGFPE, abortHandler);
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i=0 ; i<argc ; ++i)
booArgv[i+1] = argv[i];
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i = 0; i < argc; ++i)
booArgv[i + 1] = argv[i];
logvisor::CreateWin32Console();
SetConsoleOutputCP(65001);
return wmain(argc + 1, booArgv);
logvisor::CreateWin32Console();
SetConsoleOutputCP(65001);
return wmain(argc + 1, booArgv);
}
#endif

View File

@@ -1,119 +1,101 @@
#ifndef __AMUSE_AUDIOGROUP_HPP__
#define __AMUSE_AUDIOGROUP_HPP__
#pragma once
#include "AudioGroupPool.hpp"
#include "AudioGroupProject.hpp"
#include "AudioGroupSampleDirectory.hpp"
#include <string>
#include <unordered_set>
namespace amuse
{
#include "amuse/AudioGroupPool.hpp"
#include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/Common.hpp"
namespace amuse {
class AudioGroupData;
class ProjectDatabase;
/** Runtime audio group index container */
class AudioGroup
{
friend class AudioGroupSampleDirectory;
class AudioGroup {
friend class AudioGroupSampleDirectory;
protected:
AudioGroupProject m_proj;
AudioGroupPool m_pool;
AudioGroupSampleDirectory m_sdir;
const unsigned char* m_samp = nullptr;
SystemString m_groupPath; /* Typically only set by editor */
bool m_valid;
AudioGroupProject m_proj;
AudioGroupPool m_pool;
AudioGroupSampleDirectory m_sdir;
const unsigned char* m_samp = nullptr;
SystemString m_groupPath; /* Typically only set by editor */
bool m_valid;
public:
SystemString getSampleBasePath(SampleId sfxId) const;
operator bool() const { return m_valid; }
AudioGroup() = default;
explicit AudioGroup(const AudioGroupData& data) { assign(data); }
explicit AudioGroup(SystemStringView groupPath) { assign(groupPath); }
explicit AudioGroup(const AudioGroup& data, SystemStringView groupPath) { assign(data, groupPath); }
SystemString getSampleBasePath(SampleId sfxId) const;
explicit operator bool() const { return m_valid; }
AudioGroup() = default;
explicit AudioGroup(const AudioGroupData& data) { assign(data); }
explicit AudioGroup(SystemStringView groupPath) { assign(groupPath); }
explicit AudioGroup(const AudioGroup& data, SystemStringView groupPath) { assign(data, groupPath); }
void assign(const AudioGroupData& data);
void assign(SystemStringView groupPath);
void assign(const AudioGroup& data, SystemStringView groupPath);
void setGroupPath(SystemStringView groupPath) { m_groupPath = groupPath; }
void assign(const AudioGroupData& data);
void assign(SystemStringView groupPath);
void assign(const AudioGroup& data, SystemStringView groupPath);
void setGroupPath(SystemStringView groupPath) { m_groupPath = groupPath; }
const SampleEntry* getSample(SampleId sfxId) const;
std::pair<ObjToken<SampleEntryData>, const unsigned char*>
getSampleData(SampleId sfxId, const SampleEntry* sample) const;
SampleFileState getSampleFileState(SampleId sfxId,
const SampleEntry* sample, SystemString* pathOut = nullptr) const;
void patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const;
void makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const;
void makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const;
const AudioGroupProject& getProj() const { return m_proj; }
const AudioGroupPool& getPool() const { return m_pool; }
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
AudioGroupProject& getProj() { return m_proj; }
AudioGroupPool& getPool() { return m_pool; }
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
virtual void setIdDatabases() const {}
const SampleEntry* getSample(SampleId sfxId) const;
std::pair<ObjToken<SampleEntryData>, const unsigned char*> getSampleData(SampleId sfxId,
const SampleEntry* sample) const;
SampleFileState getSampleFileState(SampleId sfxId, const SampleEntry* sample, SystemString* pathOut = nullptr) const;
void patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const;
void makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const;
void makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const;
const AudioGroupProject& getProj() const { return m_proj; }
const AudioGroupPool& getPool() const { return m_pool; }
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
AudioGroupProject& getProj() { return m_proj; }
AudioGroupPool& getPool() { return m_pool; }
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
};
class AudioGroupDatabase final : public AudioGroup
{
NameDB m_soundMacroDb;
NameDB m_sampleDb;
NameDB m_tableDb;
NameDB m_keymapDb;
NameDB m_layersDb;
void _recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
std::unordered_set<SoundMacroId>& renamedIds);
class AudioGroupDatabase final : public AudioGroup {
void _recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
std::unordered_set<SoundMacroId>& renamedIds);
public:
AudioGroupDatabase() = default;
explicit AudioGroupDatabase(const AudioGroupData& data)
{
setIdDatabases();
assign(data);
}
explicit AudioGroupDatabase(SystemStringView groupPath)
{
setIdDatabases();
assign(groupPath);
}
explicit AudioGroupDatabase(const AudioGroupDatabase& data, SystemStringView groupPath)
{
setIdDatabases();
assign(data, groupPath);
}
AudioGroupDatabase() = default;
explicit AudioGroupDatabase(const AudioGroupData& data) {
assign(data);
}
explicit AudioGroupDatabase(SystemStringView groupPath) {
assign(groupPath);
}
explicit AudioGroupDatabase(const AudioGroupDatabase& data, SystemStringView groupPath) {
assign(data, groupPath);
}
void setIdDatabases() const
{
SoundMacroId::CurNameDB = const_cast<NameDB*>(&m_soundMacroDb);
SampleId::CurNameDB = const_cast<NameDB*>(&m_sampleDb);
TableId::CurNameDB = const_cast<NameDB*>(&m_tableDb);
KeymapId::CurNameDB = const_cast<NameDB*>(&m_keymapDb);
LayersId::CurNameDB = const_cast<NameDB*>(&m_layersDb);
}
void renameSample(SampleId id, std::string_view str);
void deleteSample(SampleId id);
void copySampleInto(const SystemString& basePath, const SystemString& newBasePath);
void renameSample(SampleId id, std::string_view str);
void deleteSample(SampleId id);
void copySampleInto(const SystemString& basePath, const SystemString& newBasePath);
void importCHeader(std::string_view header);
std::string exportCHeader(std::string_view projectName, std::string_view groupName) const;
void importCHeader(std::string_view header);
std::string exportCHeader(std::string_view projectName, std::string_view groupName) const;
};
class ProjectDatabase
{
NameDB m_songDb;
NameDB m_sfxDb;
NameDB m_groupDb;
class ProjectDatabase {
NameDB m_songDb;
NameDB m_sfxDb;
NameDB m_groupDb;
NameDB m_soundMacroDb;
NameDB m_sampleDb;
NameDB m_tableDb;
NameDB m_keymapDb;
NameDB m_layersDb;
public:
void setIdDatabases() const
{
SongId::CurNameDB = const_cast<NameDB*>(&m_songDb);
SFXId::CurNameDB = const_cast<NameDB*>(&m_sfxDb);
GroupId::CurNameDB = const_cast<NameDB*>(&m_groupDb);
}
void setIdDatabases() const {
SongId::CurNameDB = const_cast<NameDB*>(&m_songDb);
SFXId::CurNameDB = const_cast<NameDB*>(&m_sfxDb);
GroupId::CurNameDB = const_cast<NameDB*>(&m_groupDb);
SoundMacroId::CurNameDB = const_cast<NameDB*>(&m_soundMacroDb);
SampleId::CurNameDB = const_cast<NameDB*>(&m_sampleDb);
TableId::CurNameDB = const_cast<NameDB*>(&m_tableDb);
KeymapId::CurNameDB = const_cast<NameDB*>(&m_keymapDb);
LayersId::CurNameDB = const_cast<NameDB*>(&m_layersDb);
}
};
}
#endif // __AMUSE_AUDIOGROUP_HPP__
} // namespace amuse

View File

@@ -1,126 +1,116 @@
#ifndef __AMUSE_AUDIOGROUPDATA_HPP__
#define __AMUSE_AUDIOGROUPDATA_HPP__
#pragma once
#include "Common.hpp"
#include <cstddef>
namespace amuse
{
#include "amuse/Common.hpp"
namespace amuse {
/** Simple pointer-container of the four Audio Group chunks */
class AudioGroupData
{
friend class Engine;
class AudioGroupData {
friend class Engine;
protected:
unsigned char* m_proj;
size_t m_projSz;
unsigned char* m_pool;
size_t m_poolSz;
unsigned char* m_sdir;
size_t m_sdirSz;
unsigned char* m_samp;
size_t m_sampSz;
unsigned char* m_proj;
size_t m_projSz;
unsigned char* m_pool;
size_t m_poolSz;
unsigned char* m_sdir;
size_t m_sdirSz;
unsigned char* m_samp;
size_t m_sampSz;
DataFormat m_fmt;
bool m_absOffs;
DataFormat m_fmt;
bool m_absOffs;
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, DataFormat fmt, bool absOffs)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(fmt)
, m_absOffs(absOffs)
{
}
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, DataFormat fmt, bool absOffs)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(fmt)
, m_absOffs(absOffs) {}
public:
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, GCNDataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::GCN)
, m_absOffs(true)
{
}
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, N64DataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::N64)
, m_absOffs(absOffs)
{
}
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, PCDataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::PC)
, m_absOffs(absOffs)
{
}
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, GCNDataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::GCN)
, m_absOffs(true) {}
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, N64DataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::N64)
, m_absOffs(absOffs) {}
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, PCDataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::PC)
, m_absOffs(absOffs) {}
const unsigned char* getProj() const { return m_proj; }
const unsigned char* getPool() const { return m_pool; }
const unsigned char* getSdir() const { return m_sdir; }
const unsigned char* getSamp() const { return m_samp; }
const unsigned char* getProj() const { return m_proj; }
const unsigned char* getPool() const { return m_pool; }
const unsigned char* getSdir() const { return m_sdir; }
const unsigned char* getSamp() const { return m_samp; }
unsigned char* getProj() { return m_proj; }
unsigned char* getPool() { return m_pool; }
unsigned char* getSdir() { return m_sdir; }
unsigned char* getSamp() { return m_samp; }
unsigned char* getProj() { return m_proj; }
unsigned char* getPool() { return m_pool; }
unsigned char* getSdir() { return m_sdir; }
unsigned char* getSamp() { return m_samp; }
size_t getProjSize() const { return m_projSz; }
size_t getPoolSize() const { return m_poolSz; }
size_t getSdirSize() const { return m_sdirSz; }
size_t getSampSize() const { return m_sampSz; }
size_t getProjSize() const { return m_projSz; }
size_t getPoolSize() const { return m_poolSz; }
size_t getSdirSize() const { return m_sdirSz; }
size_t getSampSize() const { return m_sampSz; }
operator bool() const { return m_proj != nullptr && m_pool != nullptr && m_sdir != nullptr && m_samp != nullptr; }
explicit operator bool() const {
return m_proj != nullptr && m_pool != nullptr && m_sdir != nullptr && m_samp != nullptr;
}
DataFormat getDataFormat() const { return m_fmt; }
bool getAbsoluteProjOffsets() const { return m_absOffs; }
DataFormat getDataFormat() const { return m_fmt; }
bool getAbsoluteProjOffsets() const { return m_absOffs; }
};
/** A buffer-owning version of AudioGroupData */
class IntrusiveAudioGroupData : public AudioGroupData
{
bool m_owns = true;
class IntrusiveAudioGroupData : public AudioGroupData {
bool m_owns = true;
public:
using AudioGroupData::AudioGroupData;
~IntrusiveAudioGroupData();
using AudioGroupData::AudioGroupData;
~IntrusiveAudioGroupData();
IntrusiveAudioGroupData(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData& operator=(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData& operator=(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept;
IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other) noexcept;
IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept;
IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other) noexcept;
void dangleOwnership() { m_owns = false; }
void dangleOwnership() { m_owns = false; }
};
}
#endif // __AMUSE_AUDIOGROUPDATA_HPP__
} // namespace amuse

File diff suppressed because it is too large Load Diff

View File

@@ -1,229 +1,213 @@
#ifndef __AMUSE_AUDIOGROUPPROJECT_HPP__
#define __AMUSE_AUDIOGROUPPROJECT_HPP__
#pragma once
#include "Entity.hpp"
#include "Common.hpp"
#include <vector>
#include <array>
#include <unordered_map>
#include <memory>
#include <unordered_map>
#include <vector>
namespace amuse
{
#include "amuse/Common.hpp"
#include <athena/DNA.hpp>
namespace amuse {
class AudioGroupData;
class AudioGroupPool;
class AudioGroupSampleDirectory;
enum class GroupType : atUint16
{
Song,
SFX
};
enum class GroupType : atUint16 { Song, SFX };
/** Header at top of project file */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
GroupHeader : BigDNA
{
AT_DECL_DNA
Value<atUint32, DNAEn> groupEndOff;
GroupIdDNA<DNAEn> groupId;
Value<GroupType, DNAEn> type;
Value<atUint32, DNAEn> soundMacroIdsOff;
Value<atUint32, DNAEn> samplIdsOff;
Value<atUint32, DNAEn> tableIdsOff;
Value<atUint32, DNAEn> keymapIdsOff;
Value<atUint32, DNAEn> layerIdsOff;
Value<atUint32, DNAEn> pageTableOff;
Value<atUint32, DNAEn> drumTableOff;
Value<atUint32, DNAEn> midiSetupsOff;
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) GroupHeader : BigDNA {
AT_DECL_DNA
Value<atUint32, DNAEn> groupEndOff;
GroupIdDNA<DNAEn> groupId;
Value<GroupType, DNAEn> type;
Value<atUint32, DNAEn> soundMacroIdsOff;
Value<atUint32, DNAEn> samplIdsOff;
Value<atUint32, DNAEn> tableIdsOff;
Value<atUint32, DNAEn> keymapIdsOff;
Value<atUint32, DNAEn> layerIdsOff;
Value<atUint32, DNAEn> pageTableOff;
Value<atUint32, DNAEn> drumTableOff;
Value<atUint32, DNAEn> midiSetupsOff;
};
/** Common index members of SongGroups and SFXGroups */
struct AudioGroupIndex {};
/** Root index of SongGroup */
struct SongGroupIndex : AudioGroupIndex
{
/** Maps GM program numbers to sound entities */
struct SongGroupIndex : AudioGroupIndex {
/** Maps GM program numbers to sound entities */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) PageEntryDNA : BigDNA {
AT_DECL_DNA_YAML
PageObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> programNo;
Seek<1, athena::SeekOrigin::Current> pad;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1PageEntryDNA : BigDNA {
AT_DECL_DNA
PageObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> unk;
Value<atUint8> programNo;
Seek<2, athena::SeekOrigin::Current> pad;
};
struct PageEntry : BigDNA {
AT_DECL_DNA_YAML
PageObjectIdDNA<athena::Endian::Big> objId;
Value<atUint8> priority = 0;
Value<atUint8> maxVoices = 255;
PageEntry() = default;
template <athena::Endian DNAE>
PageEntry(const PageEntryDNA<DNAE>& in) : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
template <athena::Endian DNAE>
PageEntry(const MusyX1PageEntryDNA<DNAE>& in)
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
PageEntryDNA : BigDNA
{
AT_DECL_DNA_YAML
PageObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> programNo;
Seek<1, athena::Current> pad;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
MusyX1PageEntryDNA : BigDNA
{
AT_DECL_DNA
PageObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> unk;
Value<atUint8> programNo;
Seek<2, athena::Current> pad;
};
struct PageEntry : BigDNA
{
AT_DECL_DNA_YAML
PageObjectIdDNA<athena::Big> objId;
Value<atUint8> priority = 0;
Value<atUint8> maxVoices = 255;
PageEntryDNA<DNAEn> toDNA(uint8_t programNo) const {
PageEntryDNA<DNAEn> ret;
ret.objId = objId;
ret.priority = priority;
ret.maxVoices = maxVoices;
ret.programNo = programNo;
return ret;
}
};
std::unordered_map<uint8_t, PageEntry> m_normPages;
std::unordered_map<uint8_t, PageEntry> m_drumPages;
PageEntry() = default;
/** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */
struct MusyX1MIDISetup : BigDNA {
AT_DECL_DNA_YAML
Value<atUint8> programNo;
Value<atUint8> volume;
Value<atUint8> panning;
Value<atUint8> reverb;
Value<atUint8> chorus;
Seek<3, athena::SeekOrigin::Current> pad;
};
struct MIDISetup : BigDNA {
AT_DECL_DNA_YAML
Value<atUint8> programNo = 0;
Value<atUint8> volume = 127;
Value<atUint8> panning = 64;
Value<atUint8> reverb = 0;
Value<atUint8> chorus = 0;
MIDISetup() = default;
MIDISetup(const MusyX1MIDISetup& setup)
: programNo(setup.programNo)
, volume(setup.volume)
, panning(setup.panning)
, reverb(setup.reverb)
, chorus(setup.chorus) {}
};
std::unordered_map<SongId, std::array<MIDISetup, 16>> m_midiSetups;
template <athena::Endian DNAE>
PageEntry(const PageEntryDNA<DNAE>& in)
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
template <athena::Endian DNAE>
PageEntry(const MusyX1PageEntryDNA<DNAE>& in)
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
template <athena::Endian DNAEn>
PageEntryDNA<DNAEn> toDNA(uint8_t programNo) const
{
PageEntryDNA<DNAEn> ret;
ret.objId = objId;
ret.priority = priority;
ret.maxVoices = maxVoices;
ret.programNo = programNo;
return ret;
}
};
std::unordered_map<uint8_t, PageEntry> m_normPages;
std::unordered_map<uint8_t, PageEntry> m_drumPages;
/** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */
struct MusyX1MIDISetup : BigDNA
{
AT_DECL_DNA_YAML
Value<atUint8> programNo;
Value<atUint8> volume;
Value<atUint8> panning;
Value<atUint8> reverb;
Value<atUint8> chorus;
Seek<3, athena::Current> pad;
};
struct MIDISetup : BigDNA
{
AT_DECL_DNA_YAML
Value<atUint8> programNo = 0;
Value<atUint8> volume = 127;
Value<atUint8> panning = 64;
Value<atUint8> reverb = 0;
Value<atUint8> chorus = 0;
MIDISetup() = default;
MIDISetup(const MusyX1MIDISetup& setup)
: programNo(setup.programNo), volume(setup.volume), panning(setup.panning),
reverb(setup.reverb), chorus(setup.chorus) {}
};
std::unordered_map<SongId, std::array<MIDISetup, 16>> m_midiSetups;
void toYAML(athena::io::YAMLDocWriter& w) const;
void fromYAML(athena::io::YAMLDocReader& r);
void toYAML(athena::io::YAMLDocWriter& w) const;
void fromYAML(athena::io::YAMLDocReader& r);
};
/** Root index of SFXGroup */
struct SFXGroupIndex : AudioGroupIndex
{
/** Maps game-side SFX define IDs to sound entities */
struct SFXGroupIndex : AudioGroupIndex {
/** Maps game-side SFX define IDs to sound entities */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) SFXEntryDNA : BigDNA {
AT_DECL_DNA
SFXIdDNA<DNAEn> sfxId;
PageObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> defVel;
Value<atUint8> panning;
Value<atUint8> defKey;
Seek<1, athena::SeekOrigin::Current> pad;
};
struct SFXEntry : BigDNA {
AT_DECL_DNA_YAML
PageObjectIdDNA<athena::Endian::Big> objId;
Value<atUint8> priority = 0;
Value<atUint8> maxVoices = 255;
Value<atUint8> defVel = 127;
Value<atUint8> panning = 64;
Value<atUint8> defKey = 60;
SFXEntry() = default;
template <athena::Endian DNAE>
SFXEntry(const SFXEntryDNA<DNAE>& in)
: objId(in.objId.id)
, priority(in.priority)
, maxVoices(in.maxVoices)
, defVel(in.defVel)
, panning(in.panning)
, defKey(in.defKey) {}
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
SFXEntryDNA : BigDNA
{
AT_DECL_DNA
SFXIdDNA<DNAEn> sfxId;
PageObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> defVel;
Value<atUint8> panning;
Value<atUint8> defKey;
Seek<1, athena::Current> pad;
};
struct SFXEntry : BigDNA
{
AT_DECL_DNA_YAML
PageObjectIdDNA<athena::Big> objId;
Value<atUint8> priority = 0;
Value<atUint8> maxVoices = 255;
Value<atUint8> defVel = 127;
Value<atUint8> panning = 64;
Value<atUint8> defKey = 60;
SFXEntryDNA<DNAEn> toDNA(SFXId id) const {
SFXEntryDNA<DNAEn> ret;
ret.sfxId.id = id;
ret.objId = objId;
ret.priority = priority;
ret.maxVoices = maxVoices;
ret.defVel = defVel;
ret.panning = panning;
ret.defKey = defKey;
return ret;
}
};
std::unordered_map<SFXId, SFXEntry> m_sfxEntries;
SFXEntry() = default;
SFXGroupIndex() = default;
SFXGroupIndex(const SFXGroupIndex& other);
template <athena::Endian DNAE>
SFXEntry(const SFXEntryDNA<DNAE>& in)
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices),
defVel(in.defVel), panning(in.panning), defKey(in.defKey) {}
template <athena::Endian DNAEn>
SFXEntryDNA<DNAEn> toDNA(SFXId id) const
{
SFXEntryDNA<DNAEn> ret;
ret.sfxId.id = id;
ret.objId = objId;
ret.priority = priority;
ret.maxVoices = maxVoices;
ret.defVel = defVel;
ret.panning = panning;
ret.defKey = defKey;
return ret;
}
};
std::unordered_map<SFXId, SFXEntry> m_sfxEntries;
SFXGroupIndex() = default;
SFXGroupIndex(const SFXGroupIndex& other);
void toYAML(athena::io::YAMLDocWriter& w) const;
void fromYAML(athena::io::YAMLDocReader& r);
void toYAML(athena::io::YAMLDocWriter& w) const;
void fromYAML(athena::io::YAMLDocReader& r);
};
/** Collection of SongGroup and SFXGroup indexes */
class AudioGroupProject
{
std::unordered_map<GroupId, ObjToken<SongGroupIndex>> m_songGroups;
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>> m_sfxGroups;
class AudioGroupProject {
std::unordered_map<GroupId, ObjToken<SongGroupIndex>> m_songGroups;
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>> m_sfxGroups;
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
static void BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs);
static void BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs);
public:
AudioGroupProject() = default;
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
static AudioGroupProject CreateAudioGroupProject(const AudioGroupProject& oldProj);
static void BootstrapObjectIDs(const AudioGroupData& data);
AudioGroupProject() = default;
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
static AudioGroupProject CreateAudioGroupProject(const AudioGroupProject& oldProj);
static void BootstrapObjectIDs(const AudioGroupData& data);
const SongGroupIndex* getSongGroupIndex(int groupId) const;
const SFXGroupIndex* getSFXGroupIndex(int groupId) const;
const SongGroupIndex* getSongGroupIndex(GroupId groupId) const;
const SFXGroupIndex* getSFXGroupIndex(GroupId groupId) const;
const std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() const { return m_songGroups; }
const std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() const { return m_sfxGroups; }
std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() { return m_songGroups; }
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
const std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() const { return m_songGroups; }
const std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() const { return m_sfxGroups; }
std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() { return m_songGroups; }
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
std::vector<uint8_t> toYAML() const;
std::vector<uint8_t> toGCNData(const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const;
std::vector<uint8_t> toYAML() const;
std::vector<uint8_t> toGCNData(const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const;
AudioGroupProject(const AudioGroupProject&) = delete;
AudioGroupProject& operator=(const AudioGroupProject&) = delete;
AudioGroupProject(AudioGroupProject&&) = default;
AudioGroupProject& operator=(AudioGroupProject&&) = default;
AudioGroupProject(const AudioGroupProject&) = delete;
AudioGroupProject& operator=(const AudioGroupProject&) = delete;
AudioGroupProject(AudioGroupProject&&) = default;
AudioGroupProject& operator=(AudioGroupProject&&) = default;
};
}
#endif // __AMUSE_AUDIOGROUPPROJECT_HPP__
} // namespace amuse

View File

@@ -1,386 +1,358 @@
#ifndef __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
#define __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
#pragma once
#include <unordered_map>
#include <cstdint>
#include "Common.hpp"
#include <unordered_map>
#include <vector>
namespace amuse
{
#include "amuse/Common.hpp"
#include <athena/DNA.hpp>
namespace amuse {
class AudioGroupData;
class AudioGroupDatabase;
struct DSPADPCMHeader : BigDNA
{
AT_DECL_DNA
Value<atUint32> x0_num_samples;
Value<atUint32> x4_num_nibbles;
Value<atUint32> x8_sample_rate;
Value<atUint16> xc_loop_flag = 0;
Value<atUint16> xe_format = 0; /* 0 for ADPCM */
Value<atUint32> x10_loop_start_nibble = 0;
Value<atUint32> x14_loop_end_nibble = 0;
Value<atUint32> x18_ca = 0;
Value<atInt16> x1c_coef[8][2];
Value<atInt16> x3c_gain = 0;
Value<atInt16> x3e_ps = 0;
Value<atInt16> x40_hist1 = 0;
Value<atInt16> x42_hist2 = 0;
Value<atInt16> x44_loop_ps = 0;
Value<atInt16> x46_loop_hist1 = 0;
Value<atInt16> x48_loop_hist2 = 0;
Value<atUint8> m_pitch = 0; // Stash this in the padding
Seek<21, athena::Current> pad;
struct DSPADPCMHeader : BigDNA {
AT_DECL_DNA
Value<atUint32> x0_num_samples;
Value<atUint32> x4_num_nibbles;
Value<atUint32> x8_sample_rate;
Value<atUint16> xc_loop_flag = 0;
Value<atUint16> xe_format = 0; /* 0 for ADPCM */
Value<atUint32> x10_loop_start_nibble = 0;
Value<atUint32> x14_loop_end_nibble = 0;
Value<atUint32> x18_ca = 0;
Value<atInt16> x1c_coef[8][2];
Value<atInt16> x3c_gain = 0;
Value<atInt16> x3e_ps = 0;
Value<atInt16> x40_hist1 = 0;
Value<atInt16> x42_hist2 = 0;
Value<atInt16> x44_loop_ps = 0;
Value<atInt16> x46_loop_hist1 = 0;
Value<atInt16> x48_loop_hist2 = 0;
Value<atUint8> m_pitch = 0; // Stash this in the padding
Seek<21, athena::SeekOrigin::Current> pad;
};
struct VADPCMHeader : BigDNA
{
AT_DECL_DNA
Value<uint32_t> m_pitchSampleRate;
Value<uint32_t> m_numSamples;
Value<uint32_t> m_loopStartSample;
Value<uint32_t> m_loopLengthSamples;
struct VADPCMHeader : BigDNA {
AT_DECL_DNA
Value<uint32_t> m_pitchSampleRate;
Value<uint32_t> m_numSamples;
Value<uint32_t> m_loopStartSample;
Value<uint32_t> m_loopLengthSamples;
};
struct WAVFormatChunk : LittleDNA
{
AT_DECL_DNA
Value<atUint16> sampleFmt = 1;
Value<atUint16> numChannels = 1;
Value<atUint32> sampleRate;
Value<atUint32> byteRate; // sampleRate * numChannels * bitsPerSample/8
Value<atUint16> blockAlign = 2; // numChannels * bitsPerSample/8
Value<atUint16> bitsPerSample = 16;
struct WAVFormatChunk : LittleDNA {
AT_DECL_DNA
Value<atUint16> sampleFmt = 1;
Value<atUint16> numChannels = 1;
Value<atUint32> sampleRate;
Value<atUint32> byteRate; // sampleRate * numChannels * bitsPerSample/8
Value<atUint16> blockAlign = 2; // numChannels * bitsPerSample/8
Value<atUint16> bitsPerSample = 16;
};
struct WAVSampleChunk : LittleDNA
{
AT_DECL_DNA
Value<atUint32> smplManufacturer = 0;
Value<atUint32> smplProduct = 0;
Value<atUint32> smplPeriod; // 1 / sampleRate in nanoseconds
Value<atUint32> midiNote; // native MIDI note of sample
Value<atUint32> midiPitchFrac = 0;
Value<atUint32> smpteFormat = 0;
Value<atUint32> smpteOffset = 0;
Value<atUint32> numSampleLoops = 0;
Value<atUint32> additionalDataSize = 0;
struct WAVSampleChunk : LittleDNA {
AT_DECL_DNA
Value<atUint32> smplManufacturer = 0;
Value<atUint32> smplProduct = 0;
Value<atUint32> smplPeriod; // 1 / sampleRate in nanoseconds
Value<atUint32> midiNote; // native MIDI note of sample
Value<atUint32> midiPitchFrac = 0;
Value<atUint32> smpteFormat = 0;
Value<atUint32> smpteOffset = 0;
Value<atUint32> numSampleLoops = 0;
Value<atUint32> additionalDataSize = 0;
};
struct WAVSampleLoop : LittleDNA
{
AT_DECL_DNA
Value<atUint32> cuePointId = 0;
Value<atUint32> loopType = 0; // 0: forward loop
Value<atUint32> start;
Value<atUint32> end;
Value<atUint32> fraction = 0;
Value<atUint32> playCount = 0;
struct WAVSampleLoop : LittleDNA {
AT_DECL_DNA
Value<atUint32> cuePointId = 0;
Value<atUint32> loopType = 0; // 0: forward loop
Value<atUint32> start;
Value<atUint32> end;
Value<atUint32> fraction = 0;
Value<atUint32> playCount = 0;
};
struct WAVHeader : LittleDNA
{
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
struct WAVHeader : LittleDNA {
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 36; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 36; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
};
struct WAVHeaderLoop : LittleDNA
{
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
struct WAVHeaderLoop : LittleDNA {
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 60; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
WAVSampleLoop sampleLoop;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 60; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
WAVSampleLoop sampleLoop;
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
};
enum class SampleFormat : uint8_t
{
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude appropriately) */
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
enum class SampleFormat : uint8_t {
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude appropriately)
*/
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
};
enum class SampleFileState
{
NoData,
MemoryOnlyWAV,
MemoryOnlyCompressed,
WAVRecent,
CompressedRecent,
CompressedNoWAV,
WAVNoCompressed
enum class SampleFileState {
NoData,
MemoryOnlyWAV,
MemoryOnlyCompressed,
WAVRecent,
CompressedRecent,
CompressedNoWAV,
WAVNoCompressed
};
/** Indexes individual samples in SAMP chunk */
class AudioGroupSampleDirectory
{
friend class AudioGroup;
class AudioGroupSampleDirectory {
friend class AudioGroup;
public:
union ADPCMParms {
struct DSPParms
{
uint16_t m_bytesPerFrame;
uint8_t m_ps;
uint8_t m_lps;
int16_t m_hist2;
int16_t m_hist1;
int16_t m_coefs[8][2];
} dsp;
struct VADPCMParms
{
int16_t m_coefs[8][2][8];
} vadpcm;
void swapBigDSP();
void swapBigVADPCM();
};
union ADPCMParms {
struct DSPParms {
uint16_t m_bytesPerFrame;
uint8_t m_ps;
uint8_t m_lps;
int16_t m_hist2;
int16_t m_hist1;
int16_t m_coefs[8][2];
} dsp;
struct VADPCMParms {
int16_t m_coefs[8][2][8];
} vadpcm;
void swapBigDSP();
void swapBigVADPCM();
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) EntryDNA : BigDNA {
AT_DECL_DNA
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::SeekOrigin::Current> pad;
Value<atUint32, DNAEn> m_sampleOff;
Value<atUint32, DNAEn> m_unk;
Value<atUint8, DNAEn> m_pitch;
Seek<1, athena::SeekOrigin::Current> pad2;
Value<atUint16, DNAEn> m_sampleRate;
Value<atUint32, DNAEn> m_numSamples; // Top 8 bits is SampleFormat
Value<atUint32, DNAEn> m_loopStartSample;
Value<atUint32, DNAEn> m_loopLengthSamples;
Value<atUint32, DNAEn> m_adpcmParmOffset;
void _setLoopStartSample(atUint32 sample) {
m_loopLengthSamples += m_loopStartSample - sample;
m_loopStartSample = sample;
}
void setLoopEndSample(atUint32 sample) { m_loopLengthSamples = sample + 1 - m_loopStartSample; }
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1SdirEntry : BigDNA {
AT_DECL_DNA
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::SeekOrigin::Current> pad;
Value<atUint32, DNAEn> m_sampleOff;
Value<atUint32, DNAEn> m_pitchSampleRate;
Value<atUint32, DNAEn> m_numSamples;
Value<atUint32, DNAEn> m_loopStartSample;
Value<atUint32, DNAEn> m_loopLengthSamples;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1AbsSdirEntry : BigDNA {
AT_DECL_DNA
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::SeekOrigin::Current> pad;
Value<uint32_t, DNAEn> m_sampleOff;
Value<uint32_t, DNAEn> m_unk;
Value<uint32_t, DNAEn> m_pitchSampleRate;
Value<uint32_t, DNAEn> m_numSamples;
Value<uint32_t, DNAEn> m_loopStartSample;
Value<uint32_t, DNAEn> m_loopLengthSamples;
};
struct EntryData {
atUint32 m_sampleOff = 0;
atUint32 m_unk = 0;
atUint8 m_pitch = 0;
atUint16 m_sampleRate = 0;
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
atUint32 m_loopStartSample = 0;
atUint32 m_loopLengthSamples = 0;
atUint32 m_adpcmParmOffset = 0;
/* Stored out-of-band in a platform-dependent way */
ADPCMParms m_ADPCMParms;
/* In-memory storage of an individual sample. Editors use this structure
* to override the loaded sample with a file-backed version without repacking
* the sample data into a SAMP block. */
time_t m_looseModTime = 0;
std::unique_ptr<uint8_t[]> m_looseData;
/* Use middle C when pitch is (impossibly low) default */
atUint8 getPitch() const { return m_pitch == 0 ? atUint8(60) : m_pitch; }
atUint32 getNumSamples() const { return m_numSamples & 0xffffff; }
SampleFormat getSampleFormat() const { return SampleFormat(m_numSamples >> 24); }
bool isFormatDSP() const {
SampleFormat fmt = getSampleFormat();
return fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM;
}
bool isLooped() const { return m_loopLengthSamples != 0 && m_loopStartSample != 0xffffffff; }
void _setLoopStartSample(atUint32 sample) {
m_loopLengthSamples += m_loopStartSample - sample;
m_loopStartSample = sample;
}
void setLoopStartSample(atUint32 sample);
atUint32 getLoopStartSample() const { return m_loopStartSample; }
void setLoopEndSample(atUint32 sample) { m_loopLengthSamples = sample + 1 - m_loopStartSample; }
atUint32 getLoopEndSample() const { return m_loopStartSample + m_loopLengthSamples - 1; }
EntryData() = default;
template <athena::Endian DNAE>
EntryData(const EntryDNA<DNAE>& in)
: m_sampleOff(in.m_sampleOff)
, m_unk(in.m_unk)
, m_pitch(in.m_pitch)
, m_sampleRate(in.m_sampleRate)
, m_numSamples(in.m_numSamples)
, m_loopStartSample(in.m_loopStartSample)
, m_loopLengthSamples(in.m_loopLengthSamples)
, m_adpcmParmOffset(in.m_adpcmParmOffset) {}
template <athena::Endian DNAE>
EntryData(const MusyX1SdirEntry<DNAE>& in)
: m_sampleOff(in.m_sampleOff)
, m_unk(0)
, m_pitch(in.m_pitchSampleRate >> 24)
, m_sampleRate(in.m_pitchSampleRate & 0xffff)
, m_numSamples(in.m_numSamples)
, m_loopStartSample(in.m_loopStartSample)
, m_loopLengthSamples(in.m_loopLengthSamples)
, m_adpcmParmOffset(0) {}
template <athena::Endian DNAE>
EntryData(const MusyX1AbsSdirEntry<DNAE>& in)
: m_sampleOff(in.m_sampleOff)
, m_unk(in.m_unk)
, m_pitch(in.m_pitchSampleRate >> 24)
, m_sampleRate(in.m_pitchSampleRate & 0xffff)
, m_numSamples(in.m_numSamples)
, m_loopStartSample(in.m_loopStartSample)
, m_loopLengthSamples(in.m_loopLengthSamples)
, m_adpcmParmOffset(0) {}
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
EntryDNA : BigDNA
{
AT_DECL_DNA
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::Current> pad;
Value<atUint32, DNAEn> m_sampleOff;
Value<atUint32, DNAEn> m_unk;
Value<atUint8, DNAEn> m_pitch;
Seek<1, athena::Current> pad2;
Value<atUint16, DNAEn> m_sampleRate;
Value<atUint32, DNAEn> m_numSamples; // Top 8 bits is SampleFormat
Value<atUint32, DNAEn> m_loopStartSample;
Value<atUint32, DNAEn> m_loopLengthSamples;
Value<atUint32, DNAEn> m_adpcmParmOffset;
EntryDNA<DNAEn> toDNA(SFXId id) const {
EntryDNA<DNAEn> ret;
ret.m_sfxId.id = id;
ret.m_sampleOff = m_sampleOff;
ret.m_unk = m_unk;
ret.m_pitch = m_pitch;
ret.m_sampleRate = m_sampleRate;
ret.m_numSamples = m_numSamples;
ret.m_loopStartSample = m_loopStartSample;
ret.m_loopLengthSamples = m_loopLengthSamples;
ret.m_adpcmParmOffset = m_adpcmParmOffset;
return ret;
}
void loadLooseDSP(SystemStringView dspPath);
void loadLooseVADPCM(SystemStringView vadpcmPath);
void loadLooseWAV(SystemStringView wavPath);
void patchMetadataDSP(SystemStringView dspPath);
void patchMetadataVADPCM(SystemStringView vadpcmPath);
void patchMetadataWAV(SystemStringView wavPath);
};
/* This double-wrapper allows Voices to keep a strong reference on
* a single instance of loaded loose data without being unexpectedly
* clobbered */
struct Entry {
ObjToken<EntryData> m_data;
Entry() : m_data(MakeObj<EntryData>()) {}
template <athena::Endian DNAE>
Entry(const EntryDNA<DNAE>& in) : m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAE>
Entry(const MusyX1SdirEntry<DNAE>& in) : m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAE>
Entry(const MusyX1AbsSdirEntry<DNAE>& in) : m_data(MakeObj<EntryData>(in)) {}
void _setLoopStartSample(atUint32 sample)
{
m_loopLengthSamples += m_loopStartSample - sample;
m_loopStartSample = sample;
}
void setLoopEndSample(atUint32 sample)
{
m_loopLengthSamples = sample + 1 - m_loopStartSample;
}
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
MusyX1SdirEntry : BigDNA
{
AT_DECL_DNA
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::Current> pad;
Value<atUint32, DNAEn> m_sampleOff;
Value<atUint32, DNAEn> m_pitchSampleRate;
Value<atUint32, DNAEn> m_numSamples;
Value<atUint32, DNAEn> m_loopStartSample;
Value<atUint32, DNAEn> m_loopLengthSamples;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
MusyX1AbsSdirEntry : BigDNA
{
AT_DECL_DNA
SampleIdDNA<DNAEn> m_sfxId;
Seek<2, athena::Current> pad;
Value<uint32_t, DNAEn> m_sampleOff;
Value<uint32_t, DNAEn> m_unk;
Value<uint32_t, DNAEn> m_pitchSampleRate;
Value<uint32_t, DNAEn> m_numSamples;
Value<uint32_t, DNAEn> m_loopStartSample;
Value<uint32_t, DNAEn> m_loopLengthSamples;
};
struct EntryData
{
atUint32 m_sampleOff = 0;
atUint32 m_unk = 0;
atUint8 m_pitch = 0;
atUint16 m_sampleRate = 0;
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
atUint32 m_loopStartSample = 0;
atUint32 m_loopLengthSamples = 0;
atUint32 m_adpcmParmOffset = 0;
EntryDNA<DNAEn> toDNA(SFXId id) const {
return m_data->toDNA<DNAEn>(id);
}
/* Stored out-of-band in a platform-dependent way */
ADPCMParms m_ADPCMParms;
/* In-memory storage of an individual sample. Editors use this structure
* to override the loaded sample with a file-backed version without repacking
* the sample data into a SAMP block. */
time_t m_looseModTime = 0;
std::unique_ptr<uint8_t[]> m_looseData;
/* Use middle C when pitch is (impossibly low) default */
atUint8 getPitch() const { return m_pitch == 0 ? atUint8(60) : m_pitch; }
atUint32 getNumSamples() const { return m_numSamples & 0xffffff; }
SampleFormat getSampleFormat() const { return SampleFormat(m_numSamples >> 24); }
bool isFormatDSP() const
{
SampleFormat fmt = getSampleFormat();
return fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM;
}
bool isLooped() const { return m_loopLengthSamples != 0 && m_loopStartSample != 0xffffffff; }
void _setLoopStartSample(atUint32 sample)
{
m_loopLengthSamples += m_loopStartSample - sample;
m_loopStartSample = sample;
}
void setLoopStartSample(atUint32 sample);
atUint32 getLoopStartSample() const
{
return m_loopStartSample;
}
void setLoopEndSample(atUint32 sample)
{
m_loopLengthSamples = sample + 1 - m_loopStartSample;
}
atUint32 getLoopEndSample() const
{
return m_loopStartSample + m_loopLengthSamples - 1;
}
EntryData() = default;
template <athena::Endian DNAE>
EntryData(const EntryDNA<DNAE>& in)
: m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitch),
m_sampleRate(in.m_sampleRate), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
m_adpcmParmOffset(in.m_adpcmParmOffset) {}
template <athena::Endian DNAE>
EntryData(const MusyX1SdirEntry<DNAE>& in)
: m_sampleOff(in.m_sampleOff), m_unk(0), m_pitch(in.m_pitchSampleRate >> 24),
m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
m_adpcmParmOffset(0) {}
template <athena::Endian DNAE>
EntryData(const MusyX1AbsSdirEntry<DNAE>& in)
: m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitchSampleRate >> 24),
m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
m_adpcmParmOffset(0) {}
template <athena::Endian DNAEn>
EntryDNA<DNAEn> toDNA(SFXId id) const
{
EntryDNA<DNAEn> ret;
ret.m_sfxId.id = id;
ret.m_sampleOff = m_sampleOff;
ret.m_unk = m_unk;
ret.m_pitch = m_pitch;
ret.m_sampleRate = m_sampleRate;
ret.m_numSamples = m_numSamples;
ret.m_loopStartSample = m_loopStartSample;
ret.m_loopLengthSamples = m_loopLengthSamples;
ret.m_adpcmParmOffset = m_adpcmParmOffset;
return ret;
}
void loadLooseDSP(SystemStringView dspPath);
void loadLooseVADPCM(SystemStringView vadpcmPath);
void loadLooseWAV(SystemStringView wavPath);
void patchMetadataDSP(SystemStringView dspPath);
void patchMetadataVADPCM(SystemStringView vadpcmPath);
void patchMetadataWAV(SystemStringView wavPath);
};
/* This double-wrapper allows Voices to keep a strong reference on
* a single instance of loaded loose data without being unexpectedly
* clobbered */
struct Entry
{
ObjToken<EntryData> m_data;
Entry()
: m_data(MakeObj<EntryData>()) {}
template <athena::Endian DNAE>
Entry(const EntryDNA<DNAE>& in)
: m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAE>
Entry(const MusyX1SdirEntry<DNAE>& in)
: m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAE>
Entry(const MusyX1AbsSdirEntry<DNAE>& in)
: m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAEn>
EntryDNA<DNAEn> toDNA(SFXId id) const
{
return m_data->toDNA<DNAEn>(id);
}
void loadLooseData(SystemStringView basePath);
SampleFileState getFileState(SystemStringView basePath, SystemString* pathOut = nullptr) const;
void patchSampleMetadata(SystemStringView basePath) const;
};
void loadLooseData(SystemStringView basePath);
SampleFileState getFileState(SystemStringView basePath, SystemString* pathOut = nullptr) const;
void patchSampleMetadata(SystemStringView basePath) const;
};
private:
std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp, bool compressWAV = false);
std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp, bool compressWAV = false);
public:
AudioGroupSampleDirectory() = default;
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
AudioGroupSampleDirectory() = default;
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
const std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() const { return m_entries; }
std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() { return m_entries; }
const std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() const { return m_entries; }
std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() { return m_entries; }
void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const;
void reloadSampleData(SystemStringView groupPath);
void reloadSampleData(SystemStringView groupPath);
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> toGCNData(const AudioGroupDatabase& group) const;
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> toGCNData(const AudioGroupDatabase& group) const;
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
};
using SampleEntry = AudioGroupSampleDirectory::Entry;
using SampleEntryData = AudioGroupSampleDirectory::EntryData;
}
#endif // __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
} // namespace amuse

View File

@@ -1,147 +1,144 @@
#ifndef __AMUSE_BOO_BACKEND_HPP__
#define __AMUSE_BOO_BACKEND_HPP__
#pragma once
#include <boo/audiodev/IAudioVoiceEngine.hpp>
#include <boo/audiodev/IAudioSubmix.hpp>
#include <boo/audiodev/IMIDIReader.hpp>
#include <boo/audiodev/MIDIDecoder.hpp>
#include "IBackendVoice.hpp"
#include "IBackendSubmix.hpp"
#include "IBackendVoiceAllocator.hpp"
#include <mutex>
#include <array>
#include <cstddef>
#include <cstdint>
#include <list>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
namespace amuse
{
#include "amuse/IBackendVoice.hpp"
#include "amuse/IBackendSubmix.hpp"
#include "amuse/IBackendVoiceAllocator.hpp"
#include "boo2/audiodev/IAudioSubmix.hpp"
#include "boo2/audiodev/IAudioVoiceEngine.hpp"
#include "boo2/audiodev/IMIDIReader.hpp"
#include "boo2/audiodev/MIDIDecoder.hpp"
namespace amuse {
/** Backend voice implementation for boo mixer */
class BooBackendVoice : public IBackendVoice
{
friend class BooBackendVoiceAllocator;
Voice& m_clientVox;
struct VoiceCallback : boo::IAudioVoiceCallback
{
BooBackendVoice& m_parent;
void preSupplyAudio(boo::IAudioVoice& voice, double dt);
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
void routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out);
void routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out);
void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out);
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
} m_cb;
boo::ObjToken<boo::IAudioVoice> m_booVoice;
class BooBackendVoice : public IBackendVoice {
friend class BooBackendVoiceAllocator;
Voice& m_clientVox;
struct VoiceCallback : boo2::IAudioVoiceCallback {
BooBackendVoice& m_parent;
void preSupplyAudio(boo2::IAudioVoice& voice, double dt) override;
size_t supplyAudio(boo2::IAudioVoice& voice, size_t frames, int16_t* data) override;
void routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out) override;
void routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out) override;
void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out) override;
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
} m_cb;
boo2::ObjToken<boo2::IAudioVoice> m_booVoice;
public:
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch);
void resetSampleRate(double sampleRate);
BooBackendVoice(boo2::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch);
void resetSampleRate(double sampleRate) override;
void resetChannelLevels();
void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew);
void setPitchRatio(double ratio, bool slew);
void start();
void stop();
void resetChannelLevels() override;
void setChannelLevels(IBackendSubmix* submix, const std::array<float, 8>& coefs, bool slew) override;
void setPitchRatio(double ratio, bool slew) override;
void start() override;
void stop() override;
};
/** Backend submix implementation for boo mixer */
class BooBackendSubmix : public IBackendSubmix
{
friend class BooBackendVoiceAllocator;
friend class BooBackendVoice;
Submix& m_clientSmx;
struct SubmixCallback : boo::IAudioSubmixCallback
{
BooBackendSubmix& m_parent;
bool canApplyEffect() const;
void applyEffect(int16_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
void applyEffect(int32_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
void applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
void resetOutputSampleRate(double sampleRate);
SubmixCallback(BooBackendSubmix& parent) : m_parent(parent) {}
} m_cb;
boo::ObjToken<boo::IAudioSubmix> m_booSubmix;
class BooBackendSubmix : public IBackendSubmix {
friend class BooBackendVoiceAllocator;
friend class BooBackendVoice;
Submix& m_clientSmx;
struct SubmixCallback : boo2::IAudioSubmixCallback {
BooBackendSubmix& m_parent;
bool canApplyEffect() const override;
void applyEffect(float* audio, size_t frameCount, const boo2::ChannelMap& chanMap, double sampleRate) const override;
void resetOutputSampleRate(double sampleRate) override;
SubmixCallback(BooBackendSubmix& parent) : m_parent(parent) {}
} m_cb;
boo2::ObjToken<boo2::IAudioSubmix> m_booSubmix;
public:
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId);
void setSendLevel(IBackendSubmix* submix, float level, bool slew);
double getSampleRate() const;
SubmixFormat getSampleFormat() const;
BooBackendSubmix(boo2::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId);
void setSendLevel(IBackendSubmix* submix, float level, bool slew) override;
double getSampleRate() const override;
};
/** Backend MIDI event reader for controlling sequencer with external hardware / software */
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
{
friend class BooBackendVoiceAllocator;
protected:
Engine& m_engine;
std::unordered_map<std::string, std::unique_ptr<boo::IMIDIIn>> m_midiIns;
std::unique_ptr<boo::IMIDIIn> m_virtualIn;
boo::MIDIDecoder m_decoder;
class BooBackendMIDIReader : public IMIDIReader, public boo2::IMIDIReader {
friend class BooBackendVoiceAllocator;
bool m_useLock;
std::list<std::pair<double, std::vector<uint8_t>>> m_queue;
std::mutex m_midiMutex;
void _MIDIReceive(std::vector<uint8_t>&& bytes, double time);
protected:
Engine& m_engine;
std::unordered_map<std::string, std::unique_ptr<boo2::IMIDIIn>> m_midiIns;
std::unique_ptr<boo2::IMIDIIn> m_virtualIn;
boo2::MIDIDecoder m_decoder;
bool m_useLock;
std::list<std::pair<double, std::vector<uint8_t>>> m_queue;
std::mutex m_midiMutex;
void _MIDIReceive(std::vector<uint8_t>&& bytes, double time);
public:
~BooBackendMIDIReader();
BooBackendMIDIReader(Engine& engine, bool useLock);
~BooBackendMIDIReader();
BooBackendMIDIReader(Engine& engine, bool useLock);
void addMIDIIn(const char* name);
void removeMIDIIn(const char* name);
bool hasMIDIIn(const char* name) const;
void setVirtualIn(bool v);
bool hasVirtualIn() const;
void addMIDIIn(const char* name);
void removeMIDIIn(const char* name);
bool hasMIDIIn(const char* name) const;
void setVirtualIn(bool v);
bool hasVirtualIn() const;
void pumpReader(double dt);
void pumpReader(double dt) override;
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
void programChange(uint8_t chan, uint8_t program);
void channelPressure(uint8_t chan, uint8_t pressure);
void pitchBend(uint8_t chan, int16_t pitch);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity) override;
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity) override;
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure) override;
void controlChange(uint8_t chan, uint8_t control, uint8_t value) override;
void programChange(uint8_t chan, uint8_t program) override;
void channelPressure(uint8_t chan, uint8_t pressure) override;
void pitchBend(uint8_t chan, int16_t pitch) override;
void allSoundOff(uint8_t chan);
void resetAllControllers(uint8_t chan);
void localControl(uint8_t chan, bool on);
void allNotesOff(uint8_t chan);
void omniMode(uint8_t chan, bool on);
void polyMode(uint8_t chan, bool on);
void allSoundOff(uint8_t chan) override;
void resetAllControllers(uint8_t chan) override;
void localControl(uint8_t chan, bool on) override;
void allNotesOff(uint8_t chan) override;
void omniMode(uint8_t chan, bool on) override;
void polyMode(uint8_t chan, bool on) override;
void sysex(const void* data, size_t len);
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
void songPositionPointer(uint16_t pointer);
void songSelect(uint8_t song);
void tuneRequest();
void sysex(const void* data, size_t len) override;
void timeCodeQuarterFrame(uint8_t message, uint8_t value) override;
void songPositionPointer(uint16_t pointer) override;
void songSelect(uint8_t song) override;
void tuneRequest() override;
void startSeq();
void continueSeq();
void stopSeq();
void startSeq() override;
void continueSeq() override;
void stopSeq() override;
void reset();
void reset() override;
};
/** Backend voice allocator implementation for boo mixer */
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback
{
friend class BooBackendMIDIReader;
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo2::IAudioVoiceEngineCallback {
friend class BooBackendMIDIReader;
protected:
boo::IAudioVoiceEngine& m_booEngine;
Engine* m_cbInterface = nullptr;
boo2::IAudioVoiceEngine& m_booEngine;
Engine* m_cbInterface = nullptr;
public:
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId);
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices();
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine);
void setCallbackInterface(Engine* engine);
AudioChannelSet getAvailableSet();
void setVolume(float vol);
void on5MsInterval(boo::IAudioVoiceEngine& engine, double dt);
void onPumpCycleComplete(boo::IAudioVoiceEngine& engine);
BooBackendVoiceAllocator(boo2::IAudioVoiceEngine& booEngine);
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) override;
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId) override;
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() override;
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) override;
void setCallbackInterface(Engine* engine) override;
AudioChannelSet getAvailableSet() override;
void setVolume(float vol) override;
void on5MsInterval(boo2::IAudioVoiceEngine& engine, double dt) override;
void onPumpCycleComplete(boo2::IAudioVoiceEngine& engine) override;
};
}
#endif // __AMUSE_BOO_BACKEND_HPP__
} // namespace amuse

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,41 @@
#ifndef __AMUSE_CONTAINERREGISTRY_HPP__
#define __AMUSE_CONTAINERREGISTRY_HPP__
#pragma once
#include "AudioGroupData.hpp"
#include "Common.hpp"
#include <string>
#include <vector>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>
namespace amuse
{
#include "amuse/AudioGroupData.hpp"
#include "amuse/Common.hpp"
class ContainerRegistry
{
namespace amuse {
class ContainerRegistry {
public:
enum class Type
{
Invalid = -1,
Raw4 = 0,
MetroidPrime,
MetroidPrime2,
RogueSquadronPC,
RogueSquadronN64,
Factor5N64Rev,
RogueSquadron2,
RogueSquadron3
};
struct SongData
{
std::unique_ptr<uint8_t[]> m_data;
size_t m_size;
int16_t m_groupId;
int16_t m_setupId;
SongData(std::unique_ptr<uint8_t[]>&& data, size_t size, int16_t groupId, int16_t setupId)
: m_data(std::move(data)), m_size(size), m_groupId(groupId), m_setupId(setupId)
{
}
};
static const SystemChar* TypeToName(Type tp);
static Type DetectContainerType(const SystemChar* path);
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path);
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path,
Type& typeOut);
static std::vector<std::pair<SystemString, SongData>> LoadSongs(const SystemChar* path);
enum class Type {
Invalid = -1,
Raw4 = 0,
MetroidPrime,
MetroidPrime2,
RogueSquadronPC,
RogueSquadronN64,
Factor5N64Rev,
RogueSquadron2,
RogueSquadron3
};
struct SongData {
std::unique_ptr<uint8_t[]> m_data;
size_t m_size;
int16_t m_groupId;
int16_t m_setupId;
SongData(std::unique_ptr<uint8_t[]>&& data, size_t size, int16_t groupId, int16_t setupId)
: m_data(std::move(data)), m_size(size), m_groupId(groupId), m_setupId(setupId) {}
};
static const SystemChar* TypeToName(Type tp);
static Type DetectContainerType(const SystemChar* path);
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path);
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path,
Type& typeOut);
static std::vector<std::pair<SystemString, SongData>> LoadSongs(const SystemChar* path);
};
}
#endif // __AMUSE_CONTAINERREGISTRY_HPP__
} // namespace amuse

View File

@@ -1,39 +1,31 @@
#ifndef _DSPCODEC_h
#define _DSPCODEC_h
#pragma once
#include <cstdint>
static inline int16_t DSPSampClamp(int32_t val)
{
if (val < -32768) val = -32768;
else if (val > 32767) val = 32767;
return val;
constexpr int16_t DSPSampClamp(int32_t val) {
if (val < -32768)
val = -32768;
else if (val > 32767)
val = 32767;
return val;
}
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
int16_t* prev2, unsigned lastSample);
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
int16_t* prev2, unsigned lastSample);
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned firstSample, unsigned lastSample);
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
int16_t* prev2, unsigned firstSample, unsigned lastSample);
unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned DSPDecompressFrameStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned firstSample, unsigned lastSample);
void DSPCorrelateCoefs(const short* source, int samples, short coefsOut[8][2]);
void DSPEncodeFrame(short pcmInOut[16], int sampleCount, unsigned char adpcmOut[8], const short coefsIn[8][2]);
#endif // _DSPCODEC_h

View File

@@ -1,68 +1,56 @@
#ifndef __AMUSE_DIRECTORY_ENUMERATOR__
#define __AMUSE_DIRECTORY_ENUMERATOR__
#pragma once
#include "Common.hpp"
#include <cstring>
#include <string>
#include <vector>
namespace amuse
{
#include "amuse/Common.hpp"
struct CaseInsensitiveCompare
{
bool operator()(std::string_view lhs, std::string_view rhs) const
{
namespace amuse {
struct CaseInsensitiveCompare {
bool operator()(std::string_view lhs, std::string_view rhs) const {
#if _WIN32
if (_stricmp(lhs.data(), rhs.data()) < 0)
if (_stricmp(lhs.data(), rhs.data()) < 0)
#else
if (strcasecmp(lhs.data(), rhs.data()) < 0)
if (strcasecmp(lhs.data(), rhs.data()) < 0)
#endif
return true;
return false;
}
return true;
return false;
}
#if _WIN32
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const
{
if (_wcsicmp(lhs.data(), rhs.data()) < 0)
return true;
return false;
}
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const {
if (_wcsicmp(lhs.data(), rhs.data()) < 0)
return true;
return false;
}
#endif
};
class DirectoryEnumerator
{
class DirectoryEnumerator {
public:
enum class Mode
{
Native,
DirsSorted,
FilesSorted,
DirsThenFilesSorted
};
struct Entry
{
SystemString m_path;
SystemString m_name;
size_t m_fileSz;
bool m_isDir;
enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted };
struct Entry {
SystemString m_path;
SystemString m_name;
size_t m_fileSz;
bool m_isDir;
Entry(const SystemString& path, const SystemChar* name, size_t sz, bool isDir)
: m_path(path), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
};
Entry(const SystemString& path, const SystemChar* name, size_t sz, bool isDir)
: m_path(path), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
};
private:
std::vector<Entry> m_entries;
std::vector<Entry> m_entries;
public:
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
bool reverse = false, bool noHidden = false);
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
bool reverse = false, bool noHidden = false);
operator bool() const { return m_entries.size() != 0; }
size_t size() const { return m_entries.size(); }
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
explicit operator bool() const { return m_entries.size() != 0; }
size_t size() const { return m_entries.size(); }
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
};
}
#endif // __AMUSE_DIRECTORY_ENUMERATOR__
} // namespace amuse

View File

@@ -1,37 +1,15 @@
#ifndef __AMUSE_EFFECTBASE_HPP__
#define __AMUSE_EFFECTBASE_HPP__
#pragma once
#include <cstdint>
#include <cstdlib>
namespace amuse
{
namespace amuse {
struct ChannelMap;
enum class EffectType
{
Invalid,
ReverbStd,
ReverbHi,
Delay,
Chorus,
EffectTypeMAX
};
enum class EffectType { Invalid, ReverbStd, ReverbHi, Delay, Chorus, EffectTypeMAX };
class EffectBaseTypeless
{
class EffectBase {
public:
virtual ~EffectBaseTypeless() = default;
virtual void resetOutputSampleRate(double sampleRate) = 0;
virtual EffectType Isa() const = 0;
virtual ~EffectBase() = default;
virtual void resetOutputSampleRate(double sampleRate) = 0;
virtual EffectType Isa() const = 0;
virtual void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) = 0;
};
template <typename T>
class EffectBase : public EffectBaseTypeless
{
public:
virtual void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) = 0;
};
}
#endif // __AMUSE_EFFECTBASE_HPP__
} // namespace amuse

View File

@@ -1,126 +1,115 @@
#ifndef __AMUSE_EFFECTCHORUS_HPP__
#define __AMUSE_EFFECTCHORUS_HPP__
#pragma once
#include "EffectBase.hpp"
#include "Common.hpp"
#include <array>
#include <cstdint>
namespace amuse
{
template <typename T>
#include "amuse/Common.hpp"
#include "amuse/EffectBase.hpp"
#include "amuse/IBackendVoice.hpp"
namespace amuse {
class EffectChorusImp;
#define AMUSE_CHORUS_NUM_BLOCKS 3
/** Parameters needed to create EffectChorus */
struct EffectChorusInfo
{
uint32_t baseDelay = 5; /**< [5, 15] minimum value (in ms) for computed delay */
uint32_t variation = 0; /**< [0, 5] time error (in ms) to set delay within */
uint32_t period = 500; /**< [500, 10000] time (in ms) of one delay-shift cycle */
struct EffectChorusInfo {
uint32_t baseDelay = 5; /**< [5, 15] minimum value (in ms) for computed delay */
uint32_t variation = 0; /**< [0, 5] time error (in ms) to set delay within */
uint32_t period = 500; /**< [500, 10000] time (in ms) of one delay-shift cycle */
EffectChorusInfo() = default;
EffectChorusInfo(uint32_t baseDelay, uint32_t variation, uint32_t period)
: baseDelay(baseDelay), variation(variation), period(period) {}
EffectChorusInfo() = default;
EffectChorusInfo(uint32_t baseDelay, uint32_t variation, uint32_t period)
: baseDelay(baseDelay), variation(variation), period(period) {}
};
/** Mixes the audio back into itself after continuously-varying delay */
class EffectChorus
{
uint32_t x90_baseDelay; /**< [5, 15] minimum value (in ms) for computed delay */
uint32_t x94_variation; /**< [0, 5] time error (in ms) to set delay within */
uint32_t x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
bool m_dirty = true; /**< needs update of internal parameter data */
class EffectChorus {
uint32_t x90_baseDelay; /**< [5, 15] minimum value (in ms) for computed delay */
uint32_t x94_variation; /**< [0, 5] time error (in ms) to set delay within */
uint32_t x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
bool m_dirty = true; /**< needs update of internal parameter data */
template <typename T>
friend class EffectChorusImp;
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
friend class EffectChorusImp;
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
public:
template <typename T>
using ImpType = EffectChorusImp<T>;
using ImpType = EffectChorusImp;
void setBaseDelay(uint32_t baseDelay)
{
baseDelay = clamp(5u, baseDelay, 15u);
x90_baseDelay = baseDelay;
m_dirty = true;
}
uint32_t getBaseDelay() const { return x90_baseDelay; }
void setBaseDelay(uint32_t baseDelay) {
baseDelay = std::clamp(baseDelay, 5u, 15u);
x90_baseDelay = baseDelay;
m_dirty = true;
}
uint32_t getBaseDelay() const { return x90_baseDelay; }
void setVariation(uint32_t variation)
{
variation = clamp(0u, variation, 5u);
x94_variation = variation;
m_dirty = true;
}
uint32_t getVariation() const { return x94_variation; }
void setVariation(uint32_t variation) {
variation = std::clamp(variation, 0u, 5u);
x94_variation = variation;
m_dirty = true;
}
uint32_t getVariation() const { return x94_variation; }
void setPeriod(uint32_t period)
{
period = clamp(500u, period, 10000u);
x98_period = period;
m_dirty = true;
}
uint32_t getPeriod() const { return x98_period; }
void setPeriod(uint32_t period) {
period = std::clamp(period, 500u, 10000u);
x98_period = period;
m_dirty = true;
}
uint32_t getPeriod() const { return x98_period; }
void updateParams(const EffectChorusInfo& info)
{
setBaseDelay(info.baseDelay);
setVariation(info.variation);
setPeriod(info.period);
}
void updateParams(const EffectChorusInfo& info) {
setBaseDelay(info.baseDelay);
setVariation(info.variation);
setPeriod(info.period);
}
};
/** Type-specific implementation of chorus effect */
template <typename T>
class EffectChorusImp : public EffectBase<T>, public EffectChorus
{
T* x0_lastChans[8][AMUSE_CHORUS_NUM_BLOCKS] = {}; /**< Evenly-allocated pointer-table for each channel's delay */
class EffectChorusImp : public EffectBase, public EffectChorus {
/** Evenly-allocated pointer-table for each channel's delay */
std::array<std::array<float*, AMUSE_CHORUS_NUM_BLOCKS>, NumChannels> x0_lastChans{};
uint8_t x24_currentLast = 1; /**< Last 5ms block-idx to be processed */
T x28_oldChans[8][4] = {}; /**< Unprocessed history of previous 4 samples */
uint8_t x24_currentLast = 1; /**< Last 5ms block-idx to be processed */
std::array<std::array<float, 4>, NumChannels> x28_oldChans{}; /**< Unprocessed history of previous 4 samples */
uint32_t x58_currentPosLo = 0; /**< 16.7 fixed-point low-part of sample index */
uint32_t x5c_currentPosHi = 0; /**< 16.7 fixed-point high-part of sample index */
uint32_t x58_currentPosLo = 0; /**< 16.7 fixed-point low-part of sample index */
uint32_t x5c_currentPosHi = 0; /**< 16.7 fixed-point high-part of sample index */
int32_t x60_pitchOffset; /**< packed 16.16 fixed-point value of pitchHi and pitchLo quantities */
uint32_t x64_pitchOffsetPeriodCount; /**< trigger value for flipping SRC state */
uint32_t x68_pitchOffsetPeriod; /**< intermediate block window quantity for calculating SRC state */
int32_t x60_pitchOffset; /**< packed 16.16 fixed-point value of pitchHi and pitchLo quantities */
uint32_t x64_pitchOffsetPeriodCount; /**< trigger value for flipping SRC state */
uint32_t x68_pitchOffsetPeriod; /**< intermediate block window quantity for calculating SRC state */
struct SrcInfo
{
T* x6c_dest; /**< selected channel's live buffer */
T* x70_smpBase; /**< selected channel's delay buffer */
T* x74_old; /**< selected channel's 4-sample history buffer */
uint32_t x78_posLo; /**< 16.7 fixed-point low-part of sample index */
uint32_t x7c_posHi; /**< 16.7 fixed-point high-part of sample index */
uint32_t x80_pitchLo; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
uint32_t x84_pitchHi; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
uint32_t x88_trigger; /**< total count of samples per channel across all blocks */
uint32_t x8c_target = 0; /**< value to reset to when trigger hit */
struct SrcInfo {
float* x6c_dest; /**< selected channel's live buffer */
float* x70_smpBase; /**< selected channel's delay buffer */
float* x74_old; /**< selected channel's 4-sample history buffer */
uint32_t x78_posLo; /**< 16.7 fixed-point low-part of sample index */
uint32_t x7c_posHi; /**< 16.7 fixed-point high-part of sample index */
uint32_t x80_pitchLo; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
uint32_t x84_pitchHi; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
uint32_t x88_trigger; /**< total count of samples per channel across all blocks */
uint32_t x8c_target = 0; /**< value to reset to when trigger hit */
void doSrc1(size_t blockSamples, size_t chanCount);
void doSrc2(size_t blockSamples, size_t chanCount);
} x6c_src;
void doSrc1(size_t blockSamples, size_t chanCount);
void doSrc2(size_t blockSamples, size_t chanCount);
};
SrcInfo x6c_src;
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
void _setup(double sampleRate);
void _update();
void _setup(double sampleRate);
void _update();
public:
~EffectChorusImp();
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
EffectChorusImp(const EffectChorusInfo& info, double sampleRate)
: EffectChorusImp(info.baseDelay, info.variation, info.period, sampleRate) {}
~EffectChorusImp() override;
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
EffectChorusImp(const EffectChorusInfo& info, double sampleRate)
: EffectChorusImp(info.baseDelay, info.variation, info.period, sampleRate) {}
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) override;
void resetOutputSampleRate(double sampleRate) override { _setup(sampleRate); }
EffectType Isa() const { return EffectType::Chorus; }
EffectType Isa() const override { return EffectType::Chorus; }
};
}
#endif // __AMUSE_EFFECTCHORUS_HPP__
} // namespace amuse

View File

@@ -1,145 +1,125 @@
#ifndef __AMUSE_EFFECTDELAY_HPP__
#define __AMUSE_EFFECTDELAY_HPP__
#pragma once
#include "EffectBase.hpp"
#include "IBackendVoice.hpp"
#include "Common.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <memory>
namespace amuse
{
template <typename T>
#include "amuse/Common.hpp"
#include "amuse/EffectBase.hpp"
#include "amuse/IBackendVoice.hpp"
namespace amuse {
class EffectDelayImp;
/** Parameters needed to create EffectDelay */
struct EffectDelayInfo
{
uint32_t delay[8]; /**< [10, 5000] time in ms of each channel's delay */
uint32_t feedback[8] = {}; /**< [0, 100] percent to mix delayed signal with input signal */
uint32_t output[8] = {}; /**< [0, 100] total output percent */
struct EffectDelayInfo {
std::array<uint32_t, NumChannels> delay; /**< [10, 5000] time in ms of each channel's delay */
std::array<uint32_t, NumChannels> feedback{}; /**< [0, 100] percent to mix delayed signal with input signal */
std::array<uint32_t, NumChannels> output{}; /**< [0, 100] total output percent */
static uint32_t lerp(uint32_t v0, uint32_t v1, float t) { return (1.f - t) * v0 + t * v1; }
static uint32_t lerp(uint32_t v0, uint32_t v1, float t) { return (1.f - t) * v0 + t * v1; }
static void Interp3To8(uint32_t arr[8], uint32_t L, uint32_t R, uint32_t S)
{
arr[int(AudioChannel::FrontLeft)] = L;
arr[int(AudioChannel::FrontRight)] = R;
arr[int(AudioChannel::RearLeft)] = lerp(L, S, 0.75f);
arr[int(AudioChannel::RearRight)] = lerp(R, S, 0.75f);
arr[int(AudioChannel::FrontCenter)] = lerp(L, R, 0.5f);
arr[int(AudioChannel::LFE)] = arr[int(AudioChannel::FrontCenter)];
arr[int(AudioChannel::SideLeft)] = lerp(L, S, 0.5f);
arr[int(AudioChannel::SideRight)] = lerp(R, S, 0.5f);
}
static void Interp3To8(std::array<uint32_t, 8>& arr, uint32_t L, uint32_t R, uint32_t S) {
arr[size_t(AudioChannel::FrontLeft)] = L;
arr[size_t(AudioChannel::FrontRight)] = R;
arr[size_t(AudioChannel::RearLeft)] = lerp(L, S, 0.75f);
arr[size_t(AudioChannel::RearRight)] = lerp(R, S, 0.75f);
arr[size_t(AudioChannel::FrontCenter)] = lerp(L, R, 0.5f);
arr[size_t(AudioChannel::LFE)] = arr[size_t(AudioChannel::FrontCenter)];
arr[size_t(AudioChannel::SideLeft)] = lerp(L, S, 0.5f);
arr[size_t(AudioChannel::SideRight)] = lerp(R, S, 0.5f);
}
EffectDelayInfo() { std::fill_n(delay, 8, 10); }
EffectDelayInfo(uint32_t delayL, uint32_t delayR, uint32_t delayS,
uint32_t feedbackL, uint32_t feedbackR, uint32_t feedbackS,
uint32_t outputL, uint32_t outputR, uint32_t outputS)
{
Interp3To8(delay, delayL, delayR, delayS);
Interp3To8(feedback, feedbackL, feedbackR, feedbackS);
Interp3To8(output, outputL, outputR, outputS);
}
EffectDelayInfo() { delay.fill(10); }
EffectDelayInfo(uint32_t delayL, uint32_t delayR, uint32_t delayS, uint32_t feedbackL, uint32_t feedbackR,
uint32_t feedbackS, uint32_t outputL, uint32_t outputR, uint32_t outputS) {
Interp3To8(delay, delayL, delayR, delayS);
Interp3To8(feedback, feedbackL, feedbackR, feedbackS);
Interp3To8(output, outputL, outputR, outputS);
}
};
/** Mixes the audio back into itself after specified delay */
class EffectDelay
{
class EffectDelay {
protected:
uint32_t x3c_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
uint32_t x48_feedback[8]; /**< [0, 100] percent to mix delayed signal with input signal */
uint32_t x54_output[8]; /**< [0, 100] total output percent */
bool m_dirty = true; /**< needs update of internal parameter data */
std::array<uint32_t, NumChannels> x3c_delay; /**< [10, 5000] time in ms of each channel's delay */
std::array<uint32_t, NumChannels> x48_feedback; /**< [0, 100] percent to mix delayed signal with input signal */
std::array<uint32_t, NumChannels> x54_output; /**< [0, 100] total output percent */
bool m_dirty = true; /**< needs update of internal parameter data */
public:
template <typename T>
using ImpType = EffectDelayImp<T>;
using ImpType = EffectDelayImp;
void setDelay(uint32_t delay)
{
delay = clamp(10u, delay, 5000u);
for (int i = 0; i < 8; ++i)
x3c_delay[i] = delay;
m_dirty = true;
}
void setChanDelay(int chanIdx, uint32_t delay)
{
delay = clamp(10u, delay, 5000u);
x3c_delay[chanIdx] = delay;
m_dirty = true;
}
uint32_t getChanDelay(int chanIdx) const { return x3c_delay[chanIdx]; }
void setDelay(uint32_t delay) {
delay = std::clamp(delay, 10u, 5000u);
x3c_delay.fill(delay);
m_dirty = true;
}
void setChanDelay(int chanIdx, uint32_t delay) {
delay = std::clamp(delay, 10u, 5000u);
x3c_delay[chanIdx] = delay;
m_dirty = true;
}
uint32_t getChanDelay(int chanIdx) const { return x3c_delay[chanIdx]; }
void setFeedback(uint32_t feedback)
{
feedback = clamp(0u, feedback, 100u);
for (int i = 0; i < 8; ++i)
x48_feedback[i] = feedback;
m_dirty = true;
}
void setFeedback(uint32_t feedback) {
feedback = std::clamp(feedback, 0u, 100u);
x48_feedback.fill(feedback);
m_dirty = true;
}
void setChanFeedback(int chanIdx, uint32_t feedback)
{
feedback = clamp(0u, feedback, 100u);
x48_feedback[chanIdx] = feedback;
m_dirty = true;
}
uint32_t getChanFeedback(int chanIdx) const { return x48_feedback[chanIdx]; }
void setChanFeedback(int chanIdx, uint32_t feedback) {
feedback = std::clamp(feedback, 0u, 100u);
x48_feedback[chanIdx] = feedback;
m_dirty = true;
}
uint32_t getChanFeedback(int chanIdx) const { return x48_feedback[chanIdx]; }
void setOutput(uint32_t output)
{
output = clamp(0u, output, 100u);
for (int i = 0; i < 8; ++i)
x54_output[i] = output;
m_dirty = true;
}
void setOutput(uint32_t output) {
output = std::clamp(output, 0u, 100u);
x54_output.fill(output);
m_dirty = true;
}
void setChanOutput(int chanIdx, uint32_t output)
{
output = clamp(0u, output, 100u);
x54_output[chanIdx] = output;
m_dirty = true;
}
uint32_t getChanOutput(int chanIdx) const { return x54_output[chanIdx]; }
void setChanOutput(int chanIdx, uint32_t output) {
output = std::clamp(output, 0u, 100u);
x54_output[chanIdx] = output;
m_dirty = true;
}
uint32_t getChanOutput(int chanIdx) const { return x54_output[chanIdx]; }
void setParams(const EffectDelayInfo& info)
{
for (int i = 0; i < 8; ++i)
{
x3c_delay[i] = clamp(10u, info.delay[i], 5000u);
x48_feedback[i] = clamp(0u, info.feedback[i], 100u);
x54_output[i] = clamp(0u, info.output[i], 100u);
}
m_dirty = true;
void setParams(const EffectDelayInfo& info) {
for (size_t i = 0; i < NumChannels; ++i) {
x3c_delay[i] = std::clamp(info.delay[i], 10u, 5000u);
x48_feedback[i] = std::clamp(info.feedback[i], 0u, 100u);
x54_output[i] = std::clamp(info.output[i], 0u, 100u);
}
m_dirty = true;
}
};
/** Type-specific implementation of delay effect */
template <typename T>
class EffectDelayImp : public EffectBase<T>, public EffectDelay
{
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
uint32_t xc_currentPos[8]; /**< per-channel block-index */
uint32_t x18_currentFeedback[8]; /**< [0, 128] feedback attenuator */
uint32_t x24_currentOutput[8]; /**< [0, 128] total attenuator */
class EffectDelayImp : public EffectBase, public EffectDelay {
std::array<uint32_t, NumChannels> x0_currentSize; /**< per-channel delay-line buffer sizes */
std::array<uint32_t, NumChannels> xc_currentPos; /**< per-channel block-index */
std::array<uint32_t, NumChannels> x18_currentFeedback; /**< [0, 128] feedback attenuator */
std::array<uint32_t, NumChannels> x24_currentOutput; /**< [0, 128] total attenuator */
std::unique_ptr<T[]> x30_chanLines[8]; /**< delay-line buffers for each channel */
std::array<std::unique_ptr<float[]>, NumChannels> x30_chanLines; /**< delay-line buffers for each channel */
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
void _setup(double sampleRate);
void _update();
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
void _setup(double sampleRate);
void _update();
public:
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
EffectDelayImp(const EffectDelayInfo& info, double sampleRate);
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
EffectDelayImp(const EffectDelayInfo& info, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) override;
void resetOutputSampleRate(double sampleRate) override { _setup(sampleRate); }
EffectType Isa() const { return EffectType::Delay; }
EffectType Isa() const override { return EffectType::Delay; }
};
}
#endif // __AMUSE_EFFECTDELAY_HPP__
} // namespace amuse

View File

@@ -1,223 +1,208 @@
#ifndef __AMUSE_EFFECTREVERB_HPP__
#define __AMUSE_EFFECTREVERB_HPP__
#pragma once
#include "EffectBase.hpp"
#include "amuse/Common.hpp"
#include <array>
#include <cstdint>
#include <memory>
namespace amuse
{
#include "amuse/Common.hpp"
#include "amuse/EffectBase.hpp"
#include "amuse/IBackendVoice.hpp"
namespace amuse {
/** Parameters needed to create EffectReverbStd */
struct EffectReverbStdInfo
{
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
struct EffectReverbStdInfo {
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
EffectReverbStdInfo() = default;
EffectReverbStdInfo(float coloration, float mix, float time, float damping, float preDelay)
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay) {}
EffectReverbStdInfo() = default;
EffectReverbStdInfo(float coloration, float mix, float time, float damping, float preDelay)
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay) {}
};
/** Parameters needed to create EffectReverbHi */
struct EffectReverbHiInfo
{
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
float crosstalk = 0.f; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
struct EffectReverbHiInfo {
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
float crosstalk = 0.f; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
EffectReverbHiInfo() = default;
EffectReverbHiInfo(float coloration, float mix, float time, float damping, float preDelay, float crosstalk)
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay), crosstalk(crosstalk) {}
EffectReverbHiInfo() = default;
EffectReverbHiInfo(float coloration, float mix, float time, float damping, float preDelay, float crosstalk)
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay), crosstalk(crosstalk) {}
};
/** Delay state for one 'tap' of the reverb effect */
struct ReverbDelayLine
{
int32_t x0_inPoint = 0;
int32_t x4_outPoint = 0;
int32_t x8_length = 0;
std::unique_ptr<float[]> xc_inputs;
float x10_lastInput = 0.f;
struct ReverbDelayLine {
int32_t x0_inPoint = 0;
int32_t x4_outPoint = 0;
int32_t x8_length = 0;
std::unique_ptr<float[]> xc_inputs;
float x10_lastInput = 0.f;
void allocate(int32_t delay);
void setdelay(int32_t delay);
void allocate(int32_t delay);
void setdelay(int32_t delay);
};
template <typename T>
class EffectReverbStdImp;
template <typename T>
class EffectReverbHiImp;
/** Reverb effect with configurable reflection filtering */
class EffectReverbStd
{
class EffectReverbStd {
protected:
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a
room */
float x144_x1cc_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float x148_x1d0_time; /**< [0.01, 10.0] time in seconds for reflection decay */
float x14c_x1d4_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float x150_x1d8_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
bool m_dirty = true; /**< needs update of internal parameter data */
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a
room */
float x144_x1cc_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float x148_x1d0_time; /**< [0.01, 10.0] time in seconds for reflection decay */
float x14c_x1d4_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float x150_x1d8_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
bool m_dirty = true; /**< needs update of internal parameter data */
template <typename T>
friend class EffectReverbStdImp;
template <typename T>
friend class EffectReverbHiImp;
EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay);
friend class EffectReverbStdImp;
friend class EffectReverbHiImp;
EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay);
public:
template <typename T>
using ImpType = EffectReverbStdImp<T>;
using ImpType = EffectReverbStdImp;
void setColoration(float coloration)
{
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
m_dirty = true;
}
float getColoration() const { return x140_x1c8_coloration; }
void setColoration(float coloration) {
x140_x1c8_coloration = std::clamp(coloration, 0.f, 1.f);
m_dirty = true;
}
float getColoration() const { return x140_x1c8_coloration; }
void setMix(float mix)
{
x144_x1cc_mix = clamp(0.f, mix, 1.f);
m_dirty = true;
}
float getMix() const { return x144_x1cc_mix; }
void setMix(float mix) {
x144_x1cc_mix = std::clamp(mix, 0.f, 1.f);
m_dirty = true;
}
float getMix() const { return x144_x1cc_mix; }
void setTime(float time)
{
x148_x1d0_time = clamp(0.01f, time, 10.f);
m_dirty = true;
}
float getTime() const { return x148_x1d0_time; }
void setTime(float time) {
x148_x1d0_time = std::clamp(time, 0.01f, 10.f);
m_dirty = true;
}
float getTime() const { return x148_x1d0_time; }
void setDamping(float damping)
{
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
m_dirty = true;
}
float getDamping() const { return x14c_x1d4_damping; }
void setDamping(float damping) {
x14c_x1d4_damping = std::clamp(damping, 0.f, 1.f);
m_dirty = true;
}
float getDamping() const { return x14c_x1d4_damping; }
void setPreDelay(float preDelay)
{
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
m_dirty = true;
}
float getPreDelay() const { return x150_x1d8_preDelay; }
void setPreDelay(float preDelay) {
x150_x1d8_preDelay = std::clamp(preDelay, 0.f, 0.1f);
m_dirty = true;
}
float getPreDelay() const { return x150_x1d8_preDelay; }
void setParams(const EffectReverbStdInfo& info)
{
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
}
void setParams(const EffectReverbStdInfo& info) {
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
}
};
/** Reverb effect with configurable reflection filtering, adds per-channel low-pass and crosstalk */
class EffectReverbHi : public EffectReverbStd
{
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
class EffectReverbHi : public EffectReverbStd {
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
template <typename T>
friend class EffectReverbHiImp;
EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
friend class EffectReverbHiImp;
EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
public:
template <typename T>
using ImpType = EffectReverbHiImp<T>;
using ImpType = EffectReverbHiImp;
void setCrosstalk(float crosstalk)
{
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
m_dirty = true;
}
float getCrosstalk() const { return x1dc_crosstalk; }
void setCrosstalk(float crosstalk) {
x1dc_crosstalk = std::clamp(crosstalk, 0.f, 1.f);
m_dirty = true;
}
float getCrosstalk() const { return x1dc_crosstalk; }
void setParams(const EffectReverbHiInfo& info)
{
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
setCrosstalk(info.crosstalk);
}
void setParams(const EffectReverbHiInfo& info) {
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
setCrosstalk(info.crosstalk);
}
};
/** Standard-quality 2-stage reverb */
template <typename T>
class EffectReverbStdImp : public EffectBase<T>, public EffectReverbStd
{
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_C[8][2] = {}; /**< Comb delay lines */
float xf0_allPassCoef = 0.f; /**< All-pass mix coefficient */
float xf4_combCoef[8][2] = {}; /**< Comb mix coefficients */
float x10c_lpLastout[8] = {}; /**< Last low-pass results */
float x118_level = 0.f; /**< Internal wet/dry mix factor */
float x11c_damping = 0.f; /**< Low-pass damping */
int32_t x120_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x124_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x130_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
class EffectReverbStdImp : public EffectBase, public EffectReverbStd {
using CombCoeffArray = std::array<std::array<float, 2>, NumChannels>;
using ReverbDelayArray = std::array<std::array<ReverbDelayLine, 2>, NumChannels>;
using PreDelayArray = std::array<std::unique_ptr<float[]>, NumChannels>;
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
ReverbDelayArray x0_AP{}; /**< All-pass delay lines */
ReverbDelayArray x78_C{}; /**< Comb delay lines */
float xf0_allPassCoef = 0.f; /**< All-pass mix coefficient */
CombCoeffArray xf4_combCoef{}; /**< Comb mix coefficients */
std::array<float, NumChannels> x10c_lpLastout{}; /**< Last low-pass results */
float x118_level = 0.f; /**< Internal wet/dry mix factor */
float x11c_damping = 0.f; /**< Low-pass damping */
int32_t x120_preDelayTime = 0; /**< Sample count of pre-delay */
PreDelayArray x124_preDelayLine; /**< Dedicated pre-delay buffers */
std::array<float*, NumChannels> x130_preDelayPtr{}; /**< Current pre-delay pointers */
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
public:
EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay, double sampleRate);
EffectReverbStdImp(const EffectReverbStdInfo& info, double sampleRate)
: EffectReverbStdImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, sampleRate) {}
EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay, double sampleRate);
EffectReverbStdImp(const EffectReverbStdInfo& info, double sampleRate)
: EffectReverbStdImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, sampleRate) {}
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) override;
void resetOutputSampleRate(double sampleRate) override { _setup(sampleRate); }
EffectType Isa() const { return EffectType::ReverbStd; }
EffectType Isa() const override { return EffectType::ReverbStd; }
};
/** High-quality 3-stage reverb with per-channel low-pass and crosstalk */
template <typename T>
class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi
{
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
ReverbDelayLine xb4_C[8][3] = {}; /**< Comb delay lines */
float x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
float x16c_combCoef[8][3] = {}; /**< Comb mix coefficients */
float x190_lpLastout[8] = {}; /**< Last low-pass results */
float x19c_level = 0.f; /**< Internal wet/dry mix factor */
float x1a0_damping = 0.f; /**< Low-pass damping */
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
float x1a8_internalCrosstalk = 0.f;
class EffectReverbHiImp : public EffectBase, public EffectReverbHi {
using AllPassDelayLines = std::array<std::array<ReverbDelayLine, 2>, NumChannels>;
using CombCoefficients = std::array<std::array<float, 3>, NumChannels>;
using CombDelayLines = std::array<std::array<ReverbDelayLine, 3>, NumChannels>;
using LowPassDelayLines = std::array<ReverbDelayLine, 8>;
using PreDelayLines = std::array<std::unique_ptr<float[]>, 8>;
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
AllPassDelayLines x0_AP{}; /**< All-pass delay lines */
LowPassDelayLines x78_LP{}; /**< Per-channel low-pass delay-lines */
CombDelayLines xb4_C{}; /**< Comb delay lines */
float x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
CombCoefficients x16c_combCoef{}; /**< Comb mix coefficients */
std::array<float, 8> x190_lpLastout{}; /**< Last low-pass results */
float x19c_level = 0.f; /**< Internal wet/dry mix factor */
float x1a0_damping = 0.f; /**< Low-pass damping */
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
PreDelayLines x1ac_preDelayLine; /**< Dedicated pre-delay buffers */
std::array<float*, NumChannels> x1b8_preDelayPtr{}; /**< Current pre-delay pointers */
float x1a8_internalCrosstalk = 0.f;
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
void _handleReverb(float* audio, int chanIdx, int chanCount, int sampleCount);
void _doCrosstalk(float* audio, float wet, float dry, int chanCount, int sampleCount);
public:
EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay, float crosstalk,
double sampleRate);
EffectReverbHiImp(const EffectReverbHiInfo& info, double sampleRate)
: EffectReverbHiImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, info.crosstalk, sampleRate) {}
EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay, float crosstalk,
double sampleRate);
EffectReverbHiImp(const EffectReverbHiInfo& info, double sampleRate)
: EffectReverbHiImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, info.crosstalk, sampleRate) {}
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) override;
void resetOutputSampleRate(double sampleRate) override { _setup(sampleRate); }
EffectType Isa() const { return EffectType::ReverbHi; }
EffectType Isa() const override { return EffectType::ReverbHi; }
};
}
#endif // __AMUSE_EFFECTREVERB_HPP__
} // namespace amuse

View File

@@ -1,67 +1,66 @@
#ifndef __AMUSE_EMITTER_HPP__
#define __AMUSE_EMITTER_HPP__
#pragma once
#include "Entity.hpp"
#include "Common.hpp"
#include "Voice.hpp"
#include <memory>
#include <cmath>
#include <array>
#include <cfloat>
#include <cmath>
namespace amuse
{
#include "amuse/Common.hpp"
#include "amuse/Entity.hpp"
#include "amuse/Voice.hpp"
namespace amuse {
class Listener;
using Vector3f = float[3];
using Vector3f = std::array<float, 3>;
static inline float Dot(const Vector3f& a, const Vector3f& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
constexpr float Dot(const Vector3f& a, const Vector3f& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
static inline float Length(const Vector3f& a)
{
if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON)
return 0.f;
return std::sqrt(Dot(a, a));
inline float Length(const Vector3f& a) {
if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON)
return 0.f;
return std::sqrt(Dot(a, a));
}
static inline float Normalize(Vector3f& out)
{
float dist = Length(out);
if (dist == 0.f)
return 0.f;
out[0] /= dist;
out[1] /= dist;
out[2] /= dist;
return dist;
inline Vector3f Normalize(const Vector3f& in) {
const float dist = Length(in);
if (dist == 0.f) {
return {};
}
return {in[0] / dist, in[1] / dist, in[2] / dist};
}
/** Voice wrapper with positional-3D level control */
class Emitter : public Entity
{
ObjToken<Voice> m_vox;
Vector3f m_pos = {};
Vector3f m_dir = {};
float m_maxDist;
float m_maxVol = 1.f;
float m_minVol;
float m_falloff;
bool m_doppler;
bool m_dirty = true;
class Emitter : public Entity {
friend class Engine;
friend class Engine;
void _destroy();
float _attenuationCurve(float dist) const;
void _update();
ObjToken<Voice> m_vox;
Vector3f m_pos = {};
Vector3f m_dir = {};
float m_maxDist;
float m_maxVol = 1.f;
float m_minVol;
float m_falloff;
bool m_doppler;
bool m_dirty = true;
Voice::VolumeCache m_attCache;
void _destroy();
float _attenuationCurve(float dist) const;
void _update();
public:
~Emitter();
Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox,
float maxDist, float minVol, float falloff, bool doppler);
~Emitter() override;
Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox, float maxDist, float minVol, float falloff,
bool doppler);
void setVectors(const float* pos, const float* dir);
void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); m_dirty = true; }
void setVectors(const float* pos, const float* dir);
void setMaxVol(float maxVol) {
m_maxVol = std::clamp(maxVol, 0.f, 1.f);
m_dirty = true;
}
ObjToken<Voice> getVoice() const { return m_vox; }
ObjToken<Voice> getVoice() const { return m_vox; }
};
}
#endif // __AMUSE_EMITTER_HPP__
} // namespace amuse

View File

@@ -1,198 +1,184 @@
#ifndef __AMUSE_ENGINE_HPP__
#define __AMUSE_ENGINE_HPP__
#pragma once
#include <memory>
#include <cstddef>
#include <cstdint>
#include <list>
#include <memory>
#include <random>
#include <unordered_map>
#include <unordered_set>
#include "Emitter.hpp"
#include "Listener.hpp"
#include "AudioGroupSampleDirectory.hpp"
#include "Sequencer.hpp"
#include "Studio.hpp"
#include "IBackendVoiceAllocator.hpp"
namespace amuse
{
class IBackendVoiceAllocator;
class Voice;
class Submix;
class Emitter;
#include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/Emitter.hpp"
#include "amuse/IBackendVoiceAllocator.hpp"
#include "amuse/Listener.hpp"
#include "amuse/Sequencer.hpp"
#include "amuse/Studio.hpp"
namespace amuse {
class AudioGroup;
class AudioGroupData;
class Emitter;
class IBackendVoiceAllocator;
class IMIDIReader;
class Submix;
class Voice;
enum class AmplitudeMode
{
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
enum class AmplitudeMode {
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
};
/** Main audio playback system for a single audio output */
class Engine
{
friend class Voice;
friend class Emitter;
friend class Sequencer;
friend class Studio;
friend struct Sequencer::ChannelState;
class Engine {
friend class Emitter;
friend class Sequencer;
friend class Studio;
friend class Voice;
friend struct Sequencer::ChannelState;
IBackendVoiceAllocator& m_backend;
AmplitudeMode m_ampMode;
std::unique_ptr<IMIDIReader> m_midiReader;
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
std::list<ObjToken<Voice>> m_activeVoices;
std::list<ObjToken<Emitter>> m_activeEmitters;
std::list<ObjToken<Listener>> m_activeListeners;
std::list<ObjToken<Sequencer>> m_activeSequencers;
bool m_defaultStudioReady = false;
ObjToken<Studio> m_defaultStudio;
std::unordered_map<SFXId, std::tuple<AudioGroup*, GroupId, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
int m_nextVid = 0;
float m_masterVolume = 1.f;
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
IBackendVoiceAllocator& m_backend;
AmplitudeMode m_ampMode;
std::unique_ptr<IMIDIReader> m_midiReader;
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
std::list<ObjToken<Voice>> m_activeVoices;
std::list<ObjToken<Emitter>> m_activeEmitters;
std::list<ObjToken<Listener>> m_activeListeners;
std::list<ObjToken<Sequencer>> m_activeSequencers;
bool m_defaultStudioReady = false;
ObjToken<Studio> m_defaultStudio;
std::unordered_map<SFXId, std::tuple<AudioGroup*, GroupId, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
int m_nextVid = 0;
float m_masterVolume = 1.f;
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(GroupId groupId) const;
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(GroupId groupId) const;
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(GroupId groupId) const;
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(GroupId groupId) const;
std::list<ObjToken<Voice>>::iterator _allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, GroupId groupId,
SongId setupId, ObjToken<Studio> studio);
ObjToken<Studio> _allocateStudio(bool mainOut);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
std::list<ObjToken<Sequencer>>::iterator
_destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
void _bringOutYourDead();
std::list<ObjToken<Voice>>::iterator _allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, GroupId groupId, SongId setupId,
ObjToken<Studio> studio);
ObjToken<Studio> _allocateStudio(bool mainOut);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
std::list<ObjToken<Sequencer>>::iterator _destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
void _bringOutYourDead();
public:
~Engine();
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
~Engine();
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
/** Access voice backend of engine */
IBackendVoiceAllocator& getBackend() { return m_backend; }
/** Access voice backend of engine */
IBackendVoiceAllocator& getBackend() { return m_backend; }
/** Access MIDI reader */
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
/** Access MIDI reader */
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
/** Add audio group data pointers to engine; must remain resident! */
const AudioGroup* addAudioGroup(const AudioGroupData& data);
/** Add audio group data pointers to engine; must remain resident! */
const AudioGroup* addAudioGroup(const AudioGroupData& data);
/** Remove audio group from engine */
void removeAudioGroup(const AudioGroupData& data);
/** Remove audio group from engine */
void removeAudioGroup(const AudioGroupData& data);
/** Access engine's default studio */
ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
/** Access engine's default studio */
ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
/** Create new Studio within engine */
ObjToken<Studio> addStudio(bool mainOut);
/** Create new Studio within engine */
ObjToken<Studio> addStudio(bool mainOut);
/** Start soundFX playing from loaded audio groups */
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx);
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan)
{
return fxStart(sfxId, vol, pan, m_defaultStudio);
}
/** Start soundFX playing from loaded audio groups */
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx);
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan) { return fxStart(sfxId, vol, pan, m_defaultStudio); }
/** Start soundFX playing from explicit group data (for editor use) */
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan, ObjToken<Studio> smx);
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan)
{
return fxStart(group, groupId, sfxId, vol, pan, m_defaultStudio);
}
/** Start soundFX playing from explicit group data (for editor use) */
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan,
ObjToken<Studio> smx);
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan) {
return fxStart(group, groupId, sfxId, vol, pan, m_defaultStudio);
}
/** Start SoundMacro node playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
uint8_t vel, uint8_t mod)
{
return macroStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start SoundMacro node playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod) {
return macroStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start SoundMacro object playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
uint8_t vel, uint8_t mod)
{
return macroStart(group, macro, key, vel, mod, m_defaultStudio);
}
/** Start SoundMacro object playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod) {
return macroStart(group, macro, key, vel, mod, m_defaultStudio);
}
/** Start PageObject node playing directly (for editor use) */
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
uint8_t vel, uint8_t mod)
{
return pageObjectStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start PageObject node playing directly (for editor use) */
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx);
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod) {
return pageObjectStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
SFXId sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
SFXId sfxId, float minVol, float maxVol, bool doppler)
{
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
}
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
float minVol, float maxVol, bool doppler) {
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
}
/** Build listener and add to engine's listener list */
ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume);
/** Build listener and add to engine's listener list */
ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume);
/** Remove listener from engine's listener list */
void removeListener(Listener* listener);
/** Remove listener from engine's listener list */
void removeListener(Listener* listener);
/** Start song playing from loaded audio groups */
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop, ObjToken<Studio> smx);
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop = true)
{
return seqPlay(groupId, songId, arrData, loop, m_defaultStudio);
}
/** Start song playing from loaded audio groups */
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop,
ObjToken<Studio> smx);
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop = true) {
return seqPlay(groupId, songId, arrData, loop, m_defaultStudio);
}
/** Start song playing from explicit group data (for editor use) */
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData, bool loop, ObjToken<Studio> smx);
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData, bool loop = true)
{
return seqPlay(group, groupId, songId, arrData, loop, m_defaultStudio);
}
/** Start song playing from explicit group data (for editor use) */
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData,
bool loop, ObjToken<Studio> smx);
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData,
bool loop = true) {
return seqPlay(group, groupId, songId, arrData, loop, m_defaultStudio);
}
/** Set total volume of engine */
void setVolume(float vol);
/** Set total volume of engine */
void setVolume(float vol);
/** Find voice from VoiceId */
ObjToken<Voice> findVoice(int vid);
/** Find voice from VoiceId */
ObjToken<Voice> findVoice(int vid);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Obtain next random number from engine's PRNG */
uint32_t nextRandom() { return m_random(); }
/** Obtain next random number from engine's PRNG */
uint32_t nextRandom() { return m_random(); }
/** Obtain list of active voices */
std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
/** Obtain list of active voices */
std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
/** Obtain total active voice count (including child voices) */
size_t getNumTotalActiveVoices() const;
/** Obtain total active voice count (including child voices) */
size_t getNumTotalActiveVoices() const;
/** Obtain list of active sequencers */
std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
/** Obtain list of active sequencers */
std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
/** All mixing occurs in virtual 5ms intervals;
* this is called at the start of each interval for all mixable entities */
void _on5MsInterval(IBackendVoiceAllocator& engine, double dt);
/** All mixing occurs in virtual 5ms intervals;
* this is called at the start of each interval for all mixable entities */
void _on5MsInterval(IBackendVoiceAllocator& engine, double dt);
/** When a pumping cycle is complete this is called to allow the client to
* perform periodic cleanup tasks */
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
/** When a pumping cycle is complete this is called to allow the client to
* perform periodic cleanup tasks */
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
};
}
#endif // __AMUSE_ENGINE_HPP__
} // namespace amuse

View File

@@ -1,53 +1,42 @@
#ifndef __AMUSE_ENTITY_HPP__
#define __AMUSE_ENTITY_HPP__
#pragma once
#include <cstdint>
#include <functional>
#include <cassert>
#include "Common.hpp"
#include "amuse/Common.hpp"
namespace amuse
{
class Engine;
namespace amuse {
class AudioGroup;
class Engine;
/** Common 'engine child' class */
class Entity : public IObj
{
/* Only the Engine will manage Entity lifetimes,
* but shared_ptrs are issued to the client so it can safely track state */
friend class Engine;
friend struct SoundMacroState;
class Entity : public IObj {
/* Only the Engine will manage Entity lifetimes,
* but shared_ptrs are issued to the client so it can safely track state */
friend class Engine;
friend struct SoundMacroState;
protected:
bool m_destroyed = false;
void _destroy()
{
assert(!m_destroyed);
m_destroyed = true;
}
Engine& m_engine;
const AudioGroup& m_audioGroup;
GroupId m_groupId;
ObjectId m_objectId; /* if applicable */
bool m_destroyed = false;
void _destroy() {
assert(!m_destroyed);
m_destroyed = true;
}
Engine& m_engine;
const AudioGroup& m_audioGroup;
GroupId m_groupId;
ObjectId m_objectId; /* if applicable */
public:
Entity(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid = ObjectId())
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid)
{
}
~Entity()
{
/* Ensure proper destruction procedure followed */
assert(m_destroyed);
}
Entity(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid = ObjectId())
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid) {}
~Entity() override {
/* Ensure proper destruction procedure followed */
assert(m_destroyed);
}
Engine& getEngine() { return m_engine; }
const AudioGroup& getAudioGroup() const { return m_audioGroup; }
GroupId getGroupId() const { return m_groupId; }
ObjectId getObjectId() const { return m_objectId; }
bool isDestroyed() const { return m_destroyed; }
Engine& getEngine() { return m_engine; }
const AudioGroup& getAudioGroup() const { return m_audioGroup; }
GroupId getGroupId() const { return m_groupId; }
ObjectId getObjectId() const { return m_objectId; }
bool isDestroyed() const { return m_destroyed; }
};
}
#endif // __AMUSE_ENTITY_HPP__
} // namespace amuse

View File

@@ -1,45 +1,36 @@
#ifndef __AMUSE_ENVELOPE_HPP__
#define __AMUSE_ENVELOPE_HPP__
#pragma once
#include "AudioGroupPool.hpp"
#include <cstdint>
namespace amuse
{
namespace amuse {
class Voice;
struct ADSR;
struct ADSRDLS;
/** Per-sample state tracker for ADSR envelope data */
class Envelope
{
class Envelope {
public:
enum class State
{
Attack,
Decay,
Sustain,
Release,
Complete
};
enum class State { Attack, Decay, Sustain, Release, Complete };
private:
State m_phase = State::Attack; /**< Current envelope state */
double m_attackTime = 0.01; /**< Time of attack in seconds */
double m_decayTime = 0.0; /**< Time of decay in seconds */
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
double m_releaseTime = 0.01; /**< Time of release in seconds */
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
bool m_adsrSet = false;
State m_phase = State::Attack; /**< Current envelope state */
double m_attackTime = 0.01; /**< Time of attack in seconds */
double m_decayTime = 0.0; /**< Time of decay in seconds */
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
double m_releaseTime = 0.01; /**< Time of release in seconds */
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
bool m_adsrSet = false;
public:
void reset(const ADSR* adsr);
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
void keyOff(const Voice& vox);
void keyOff();
float advance(double dt, const Voice& vox);
float advance(double dt);
bool isComplete() const { return m_phase == State::Complete; }
bool isAdsrSet() const { return m_adsrSet; }
void reset(const ADSR* adsr);
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
void keyOff(const Voice& vox);
void keyOff();
float advance(double dt, const Voice& vox);
float advance(double dt);
bool isComplete(const Voice& vox) const;
bool isAdsrSet() const { return m_adsrSet; }
};
}
#endif // __AMUSE_ENVELOPE_HPP__
} // namespace amuse

View File

@@ -1,35 +1,16 @@
#ifndef __AMUSE_IBACKENDSUBMIX_HPP__
#define __AMUSE_IBACKENDSUBMIX_HPP__
#pragma once
#include <memory>
namespace amuse
{
class IBackendVoice;
class Voice;
enum class SubmixFormat
{
Int16,
Int32,
Float
};
namespace amuse {
/** Client-implemented submix instance */
class IBackendSubmix
{
class IBackendSubmix {
public:
virtual ~IBackendSubmix() = default;
virtual ~IBackendSubmix() = default;
/** Set send level for submix (AudioChannel enum for array index) */
virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew) = 0;
/** Set send level for submix (AudioChannel enum for array index) */
virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew) = 0;
/** Amuse gets fixed sample rate of submix this way */
virtual double getSampleRate() const = 0;
/** Amuse gets fixed sample format of submix this way */
virtual SubmixFormat getSampleFormat() const = 0;
/** Amuse gets fixed sample rate of submix this way */
virtual double getSampleRate() const = 0;
};
}
#endif // __AMUSE_IBACKENDSUBMIX_HPP__
} // namespace amuse

View File

@@ -1,55 +1,53 @@
#ifndef __AMUSE_IBACKENDVOICE_HPP__
#define __AMUSE_IBACKENDVOICE_HPP__
#pragma once
namespace amuse
{
#include <array>
#include <cstddef>
namespace amuse {
class IBackendSubmix;
/** Same channel enums from boo, used for matrix coefficient table index */
enum class AudioChannel
{
FrontLeft,
FrontRight,
RearLeft,
RearRight,
FrontCenter,
LFE,
SideLeft,
SideRight,
Unknown = 0xff
enum class AudioChannel {
FrontLeft,
FrontRight,
RearLeft,
RearRight,
FrontCenter,
LFE,
SideLeft,
SideRight,
Unknown = 0xff
};
constexpr size_t NumChannels = 8;
/** Same structure from boo, used to represent interleaved speaker layout */
struct ChannelMap
{
unsigned m_channelCount = 0;
AudioChannel m_channels[8] = {};
struct ChannelMap {
unsigned m_channelCount = 0;
AudioChannel m_channels[NumChannels] = {};
};
/** Client-implemented voice instance */
class IBackendVoice
{
class IBackendVoice {
public:
virtual ~IBackendVoice() = default;
virtual ~IBackendVoice() = default;
/** Set new sample rate into platform voice (may result in artifacts while playing) */
virtual void resetSampleRate(double sampleRate) = 0;
/** Set new sample rate into platform voice (may result in artifacts while playing) */
virtual void resetSampleRate(double sampleRate) = 0;
/** Reset channel-gains to silence and unbind all submixes */
virtual void resetChannelLevels() = 0;
/** Reset channel-gains to silence and unbind all submixes */
virtual void resetChannelLevels() = 0;
/** Set channel-gains for audio source (AudioChannel enum for array index) */
virtual void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew) = 0;
/** Set channel-gains for audio source (AudioChannel enum for array index) */
virtual void setChannelLevels(IBackendSubmix* submix, const std::array<float, 8>& coefs, bool slew) = 0;
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
virtual void setPitchRatio(double ratio, bool slew) = 0;
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
virtual void setPitchRatio(double ratio, bool slew) = 0;
/** Instructs platform to begin consuming sample data; invoking callback as needed */
virtual void start() = 0;
/** Instructs platform to begin consuming sample data; invoking callback as needed */
virtual void start() = 0;
/** Instructs platform to stop consuming sample data */
virtual void stop() = 0;
/** Instructs platform to stop consuming sample data */
virtual void stop() = 0;
};
}
#endif // __AMUSE_IBACKENDVOICE_HPP__
} // namespace amuse

View File

@@ -1,63 +1,49 @@
#ifndef __AMUSE_IBACKENDVOICEALLOCATOR_HPP__
#define __AMUSE_IBACKENDVOICEALLOCATOR_HPP__
#pragma once
#include <memory>
#include <functional>
#include <vector>
namespace amuse
{
class IBackendVoice;
class IBackendSubmix;
class Voice;
class Submix;
namespace amuse {
class Engine;
class IBackendSubmix;
class IBackendVoice;
class Submix;
class Voice;
/** Same enum as boo for describing speaker-configuration */
enum class AudioChannelSet
{
Stereo,
Quad,
Surround51,
Surround71,
Unknown = 0xff
};
enum class AudioChannelSet { Stereo, Quad, Surround51, Surround71, Unknown = 0xff };
/** Handle to MIDI-reader, implementation opaque */
class IMIDIReader
{
class IMIDIReader {
public:
virtual ~IMIDIReader() = default;
virtual void pumpReader(double dt) = 0;
virtual ~IMIDIReader() = default;
virtual void pumpReader(double dt) = 0;
};
/** Client-implemented voice allocator */
class IBackendVoiceAllocator
{
class IBackendVoiceAllocator {
public:
virtual ~IBackendVoiceAllocator() = default;
virtual ~IBackendVoiceAllocator() = default;
/** Amuse obtains a new voice from the platform this way */
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) = 0;
/** Amuse obtains a new voice from the platform this way */
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) = 0;
/** Amuse obtains a new submix from the platform this way */
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId) = 0;
/** Amuse obtains a new submix from the platform this way */
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId) = 0;
/** Amuse obtains a list of all MIDI devices this way */
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
/** Amuse obtains a list of all MIDI devices this way */
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
/** Amuse obtains an interactive MIDI-in connection from the OS this way */
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) = 0;
/** Amuse obtains an interactive MIDI-in connection from the OS this way */
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) = 0;
/** Amuse obtains speaker-configuration from the platform this way */
virtual AudioChannelSet getAvailableSet() = 0;
/** Amuse obtains speaker-configuration from the platform this way */
virtual AudioChannelSet getAvailableSet() = 0;
/** Set volume of main mix out */
virtual void setVolume(float vol) = 0;
/** Set volume of main mix out */
virtual void setVolume(float vol) = 0;
/** Amuse registers for key callback events from the mixing engine this way */
virtual void setCallbackInterface(Engine* engine) = 0;
/** Amuse registers for key callback events from the mixing engine this way */
virtual void setCallbackInterface(Engine* engine) = 0;
};
}
#endif // __AMUSE_IBACKENDVOICEALLOCATOR_HPP__
} // namespace amuse

View File

@@ -1,30 +1,29 @@
#ifndef __AMUSE_LISTENER_HPP__
#define __AMUSE_LISTENER_HPP__
#pragma once
#include "amuse/Emitter.hpp"
namespace amuse
{
class Listener
{
friend class Emitter;
friend class Engine;
Vector3f m_pos = {};
Vector3f m_dir = {};
Vector3f m_heading = {};
Vector3f m_up = {};
Vector3f m_right = {};
float m_volume;
float m_frontDiff;
float m_backDiff;
float m_soundSpeed;
bool m_dirty = true;
public:
Listener(float volume, float frontDiff, float backDiff, float soundSpeed)
: m_volume(clamp(0.f, volume, 1.f)), m_frontDiff(frontDiff), m_backDiff(backDiff), m_soundSpeed(soundSpeed) {}
void setVectors(const float* pos, const float* dir, const float* heading, const float* up);
void setVolume(float vol) { m_volume = clamp(0.f, vol, 1.f); m_dirty = true; }
};
}
namespace amuse {
class Listener {
friend class Emitter;
friend class Engine;
Vector3f m_pos = {};
Vector3f m_dir = {};
Vector3f m_heading = {};
Vector3f m_up = {};
Vector3f m_right = {};
float m_volume;
float m_frontDiff;
float m_backDiff;
float m_soundSpeed;
bool m_dirty = true;
#endif // __AMUSE_LISTENER_HPP__
public:
Listener(float volume, float frontDiff, float backDiff, float soundSpeed)
: m_volume(std::clamp(volume, 0.f, 1.f)), m_frontDiff(frontDiff), m_backDiff(backDiff), m_soundSpeed(soundSpeed) {}
void setVectors(const float* pos, const float* dir, const float* heading, const float* up);
void setVolume(float vol) {
m_volume = std::clamp(vol, 0.f, 1.f);
m_dirty = true;
}
};
} // namespace amuse

View File

@@ -1,21 +1,16 @@
#ifndef _N64MUSYXCODEC_h
#define _N64MUSYXCODEC_h
#pragma once
#include <cstdint>
static inline int16_t N64MusyXSampClamp(int32_t val)
{
if (val < -32768) val = -32768;
else if (val > 32767) val = 32767;
return val;
constexpr int16_t N64MusyXSampClamp(int32_t val) {
if (val < -32768)
val = -32768;
else if (val > 32767)
val = 32767;
return val;
}
unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2][8],
unsigned lastSample);
unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], unsigned lastSample);
unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2][8],
unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8],
unsigned firstSample, unsigned lastSample);
#endif // _N64MUSYXCODEC_h

View File

@@ -1,169 +1,164 @@
#ifndef __AMUSE_SEQUENCER_HPP__
#define __AMUSE_SEQUENCER_HPP__
#pragma once
#include "Entity.hpp"
#include "AudioGroupProject.hpp"
#include "SongState.hpp"
#include "Studio.hpp"
#include "Voice.hpp"
#include <cstddef>
#include <cstdint>
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include <list>
namespace amuse
{
#include "amuse/AudioGroupProject.hpp"
#include "amuse/Common.hpp"
#include "amuse/Entity.hpp"
#include "amuse/SongState.hpp"
#include "amuse/Studio.hpp"
#include "amuse/Voice.hpp"
namespace amuse {
/** State of sequencer over lifetime */
enum class SequencerState
{
Playing, /**< Sequencer actively playing arrangement */
Interactive, /**< Interactive sequencer for live MIDI message processing, will not automatically die */
Dead /**< Set when arrangement complete and `dieOnEnd` was set, or manually with die() */
enum class SequencerState {
Playing, /**< Sequencer actively playing arrangement */
Interactive, /**< Interactive sequencer for live MIDI message processing, will not automatically die */
Dead /**< Set when arrangement complete and `dieOnEnd` was set, or manually with die() */
};
/** Multi-voice lifetime manager and polyphonic parameter tracking */
class Sequencer : public Entity
{
friend class Engine;
const SongGroupIndex* m_songGroup = nullptr; /**< Quick access to song group project index */
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */
const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */
std::vector<const SFXGroupIndex::SFXEntry*> m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */
ObjToken<Studio> m_studio; /**< Studio this sequencer outputs to */
class Sequencer : public Entity {
friend class Engine;
const SongGroupIndex* m_songGroup = nullptr; /**< Quick access to song group project index */
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */
const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */
std::vector<const SFXGroupIndex::SFXEntry*> m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */
ObjToken<Studio> m_studio; /**< Studio this sequencer outputs to */
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
SongState m_songState; /**< State of current arrangement playback */
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
SongState m_songState; /**< State of current arrangement playback */
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
float m_curVol = 1.f; /**< Current volume of sequencer */
float m_volFadeTime = 0.f;
float m_volFadeTarget = 0.f;
float m_volFadeStart = 0.f;
float m_stopFadeTime = 0.f;
float m_stopFadeBeginVol = 0.f;
float m_curVol = 1.f; /**< Current volume of sequencer */
float m_volFadeTime = 0.f;
float m_volFadeTarget = 0.f;
float m_volFadeStart = 0.f;
float m_stopFadeTime = 0.f;
float m_stopFadeBeginVol = 0.f;
/** State of a single MIDI channel */
struct ChannelState
{
Sequencer* m_parent = nullptr;
uint8_t m_chanId;
const SongGroupIndex::MIDISetup* m_setup = nullptr; /* Channel defaults to program 0 if null */
const SongGroupIndex::PageEntry* m_page = nullptr;
~ChannelState();
ChannelState() = default;
ChannelState(Sequencer& parent, uint8_t chanId);
operator bool() const { return m_parent != nullptr; }
/** State of a single MIDI channel */
struct ChannelState {
Sequencer* m_parent = nullptr;
uint8_t m_chanId = 0;
const SongGroupIndex::MIDISetup* m_setup = nullptr; /* Channel defaults to program 0 if null */
const SongGroupIndex::PageEntry* m_page = nullptr;
~ChannelState();
ChannelState() = default;
ChannelState(Sequencer& parent, uint8_t chanId);
explicit operator bool() const { return m_parent != nullptr; }
/** Voices corresponding to currently-pressed keys in channel */
std::unordered_map<uint8_t, ObjToken<Voice>> m_chanVoxs;
std::unordered_set<ObjToken<Voice>> m_keyoffVoxs;
ObjToken<Voice> m_lastVoice;
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
int8_t m_pitchWheelRange = -1; /**< Pitch wheel range settable by RPN 0 */
int8_t m_curProgram = 0; /**< MIDI program number */
float m_curVol = 1.f; /**< Current volume of channel */
float m_curPan = 0.f; /**< Current panning of channel */
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for channel */
void _bringOutYourDead();
size_t getVoiceCount() const;
ObjToken<Voice> keyOn(uint8_t note, uint8_t velocity);
void keyOff(uint8_t note, uint8_t velocity);
void setCtrlValue(uint8_t ctrl, int8_t val);
bool programChange(int8_t prog);
void nextProgram();
void prevProgram();
void setPitchWheel(float pitchWheel);
void setVolume(float vol);
void setPan(float pan);
void allOff();
void killKeygroup(uint8_t kg, bool now);
ObjToken<Voice> findVoice(int vid);
void sendMacroMessage(ObjectId macroId, int32_t val);
};
std::array<ChannelState, 16> m_chanStates; /**< Lazily-allocated channel states */
/** Voices corresponding to currently-pressed keys in channel */
std::unordered_map<uint8_t, ObjToken<Voice>> m_chanVoxs;
std::unordered_set<ObjToken<Voice>> m_keyoffVoxs;
ObjToken<Voice> m_lastVoice;
std::array<int8_t, 128> m_ctrlVals{}; /**< MIDI controller values */
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
int8_t m_pitchWheelRange = -1; /**< Pitch wheel range settable by RPN 0 */
int8_t m_curProgram = 0; /**< MIDI program number */
float m_curVol = 1.f; /**< Current volume of channel */
float m_curPan = 0.f; /**< Current panning of channel */
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for channel */
void _bringOutYourDead();
void _destroy();
size_t getVoiceCount() const;
ObjToken<Voice> keyOn(uint8_t note, uint8_t velocity);
void keyOff(uint8_t note, uint8_t velocity);
void setCtrlValue(uint8_t ctrl, int8_t val);
bool programChange(int8_t prog);
void nextProgram();
void prevProgram();
void setPitchWheel(float pitchWheel);
void setVolume(float vol);
void setPan(float pan);
void allOff();
void killKeygroup(uint8_t kg, bool now);
ObjToken<Voice> findVoice(int vid);
void sendMacroMessage(ObjectId macroId, int32_t val);
};
std::array<ChannelState, 16> m_chanStates; /**< Lazily-allocated channel states */
void _bringOutYourDead();
void _destroy();
public:
~Sequencer();
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup, SongId setupId,
ObjToken<Studio> studio);
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup,
ObjToken<Studio> studio);
~Sequencer() override;
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup, SongId setupId,
ObjToken<Studio> studio);
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup,
ObjToken<Studio> studio);
/** Advance current song data (if any) */
void advance(double dt);
/** Advance current song data (if any) */
void advance(double dt);
/** Obtain pointer to Sequencer's Submix */
ObjToken<Studio> getStudio() { return m_studio; }
/** Obtain pointer to Sequencer's Submix */
ObjToken<Studio> getStudio() { return m_studio; }
/** Get current state of sequencer */
SequencerState state() const { return m_state; }
/** Get current state of sequencer */
SequencerState state() const { return m_state; }
/** Get number of active voices */
size_t getVoiceCount() const;
/** Get number of active voices */
size_t getVoiceCount() const;
/** Register key press with voice set */
ObjToken<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
/** Register key press with voice set */
ObjToken<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
/** Register key release with voice set */
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
/** Register key release with voice set */
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
/** Set MIDI control value [0,127] for all voices */
void setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val);
/** Set MIDI control value [0,127] for all voices */
void setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val);
/** Set pitchwheel value for use with voice controllers */
void setPitchWheel(uint8_t chan, float pitchWheel);
/** Set pitchwheel value for use with voice controllers */
void setPitchWheel(uint8_t chan, float pitchWheel);
/** Send keyoffs to all active notes, silence immediately if `now` set */
void allOff(bool now = false);
/** Send keyoffs to all active notes, silence immediately if `now` set */
void allOff(bool now = false);
/** Send keyoffs to all active notes on specified channel, silence immediately if `now` set */
void allOff(uint8_t chan, bool now = false);
/** Send keyoffs to all active notes on specified channel, silence immediately if `now` set */
void allOff(uint8_t chan, bool now = false);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Find voice instance contained within Sequencer */
ObjToken<Voice> findVoice(int vid);
/** Find voice instance contained within Sequencer */
ObjToken<Voice> findVoice(int vid);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Set tempo of sequencer and all voices in ticks per second */
void setTempo(uint8_t chan, double ticksPerSec);
void setTempo(double ticksPerSec);
/** Set tempo of sequencer and all voices in ticks per second */
void setTempo(uint8_t chan, double ticksPerSec);
void setTempo(double ticksPerSec);
/** Play MIDI arrangement */
void playSong(const unsigned char* arrData, bool loop = true, bool dieOnEnd = true);
/** Play MIDI arrangement */
void playSong(const unsigned char* arrData, bool loop = true, bool dieOnEnd = true);
/** Stop current MIDI arrangement */
void stopSong(float fadeTime = 0.f, bool now = false);
/** Stop current MIDI arrangement */
void stopSong(float fadeTime = 0.f, bool now = false);
/** Set total volume of sequencer */
void setVolume(float vol, float fadeTime = 0.f);
/** Set total volume of sequencer */
void setVolume(float vol, float fadeTime = 0.f);
/** Get current program number of channel */
int8_t getChanProgram(int8_t chanId) const;
/** Get current program number of channel */
int8_t getChanProgram(int8_t chanId) const;
/** Set current program number of channel */
bool setChanProgram(int8_t chanId, int8_t prog);
/** Set current program number of channel */
bool setChanProgram(int8_t chanId, int8_t prog);
/** Advance to next program in channel */
void nextChanProgram(int8_t chanId);
/** Advance to next program in channel */
void nextChanProgram(int8_t chanId);
/** Advance to prev program in channel */
void prevChanProgram(int8_t chanId);
/** Advance to prev program in channel */
void prevChanProgram(int8_t chanId);
/** Manually kill sequencer for deferred release from engine */
void kill() { m_state = SequencerState::Dead; }
/** Manually kill sequencer for deferred release from engine */
void kill() { m_state = SequencerState::Dead; }
};
}
#endif // __AMUSE_SEQUENCER_HPP__
} // namespace amuse

View File

@@ -1,18 +1,13 @@
#ifndef __AMUSE_SONGCONVERTER_HPP__
#define __AMUSE_SONGCONVERTER_HPP__
#pragma once
#include <vector>
#include <cstdint>
#include <vector>
namespace amuse
{
namespace amuse {
class SongConverter
{
class SongConverter {
public:
static std::vector<uint8_t> SongToMIDI(const unsigned char* data, int& versionOut, bool& isBig);
static std::vector<uint8_t> MIDIToSong(const std::vector<uint8_t>& data, int version, bool big);
static std::vector<uint8_t> SongToMIDI(const unsigned char* data, int& versionOut, bool& isBig);
static std::vector<uint8_t> MIDIToSong(const std::vector<uint8_t>& data, int version, bool big);
};
}
#endif // __AMUSE_SONGCONVERTER_HPP__
} // namespace amuse

View File

@@ -1,140 +1,125 @@
#ifndef __AMUSE_SONGSTATE_HPP__
#define __AMUSE_SONGSTATE_HPP__
#pragma once
#include <cstdint>
#include <vector>
#include <array>
#include <list>
#include "Entity.hpp"
#include <cstdint>
namespace amuse
{
#include "amuse/Entity.hpp"
namespace amuse {
class Sequencer;
enum class SongPlayState
{
Stopped,
Playing
};
enum class SongPlayState { Stopped, Playing };
/** Real-time state of Song execution */
class SongState
{
friend class Voice;
friend class SongConverter;
class SongState {
friend class SongConverter;
friend class Voice;
/** Song header */
struct Header
{
uint32_t m_trackIdxOff;
uint32_t m_regionIdxOff;
uint32_t m_chanMapOff;
uint32_t m_tempoTableOff;
uint32_t m_initialTempo; /* Top bit indicates per-channel looping */
uint32_t m_loopStartTicks[16];
uint32_t m_chanMapOff2;
void swapToBig();
void swapFromBig();
Header& operator=(const Header& other);
Header(const Header& other) { *this = other; }
Header() = default;
} m_header;
/** Song header */
struct Header {
uint32_t m_trackIdxOff;
uint32_t m_regionIdxOff;
uint32_t m_chanMapOff;
uint32_t m_tempoTableOff;
uint32_t m_initialTempo; /* Top bit indicates per-channel looping */
uint32_t m_loopStartTicks[16];
uint32_t m_chanMapOff2;
void swapToBig();
void swapFromBig();
Header& operator=(const Header& other);
Header(const Header& other) { *this = other; }
Header() = default;
} m_header;
/** Track region ('clip' in an NLA representation) */
struct TrackRegion
{
uint32_t m_startTick;
uint8_t m_progNum;
uint8_t m_unk1;
uint16_t m_unk2;
int16_t m_regionIndex; /* -1 to terminate song, -2 to loop to previous region */
int16_t m_loopToRegion;
bool indexDone(bool bigEndian, bool loop) const;
bool indexValid(bool bigEndian) const;
int indexLoop(bool bigEndian) const;
/** Track region ('clip' in an NLA representation) */
struct TrackRegion {
uint32_t m_startTick;
uint8_t m_progNum;
uint8_t m_unk1;
uint16_t m_unk2;
int16_t m_regionIndex; /* -1 to terminate song, -2 to loop to previous region */
int16_t m_loopToRegion;
bool indexDone(bool bigEndian, bool loop) const;
bool indexValid(bool bigEndian) const;
int indexLoop(bool bigEndian) const;
};
/** Tempo change entry */
struct TempoChange {
uint32_t m_tick; /**< Relative song ticks from previous tempo change */
uint32_t m_tempo; /**< Tempo value in beats-per-minute (at 384 ticks per quarter-note) */
void swapBig();
};
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
int m_sngVersion; /**< Detected song revision, 1 has RLE-compressed delta-times */
bool m_bigEndian; /**< True if loaded song is big-endian data */
/** State of a single track within arrangement */
struct Track {
struct Header {
uint32_t m_type;
uint32_t m_pitchOff;
uint32_t m_modOff;
void swapBig();
};
/** Tempo change entry */
struct TempoChange
{
uint32_t m_tick; /**< Relative song ticks from previous tempo change */
uint32_t m_tempo; /**< Tempo value in beats-per-minute (at 384 ticks per quarter-note) */
void swapBig();
};
SongState* m_parent = nullptr;
uint8_t m_midiChan = 0xff; /**< MIDI channel number of song channel */
const TrackRegion* m_initRegion = nullptr; /**< Pointer to first track region */
const TrackRegion* m_curRegion = nullptr; /**< Pointer to currently-playing track region */
const TrackRegion* m_nextRegion = nullptr; /**< Pointer to next-queued track region */
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
int m_sngVersion; /**< Detected song revision, 1 has RLE-compressed delta-times */
bool m_bigEndian; /**< True if loaded song is big-endian data */
double m_remDt = 0.0; /**< Remaining dt for keeping remainder between cycles */
uint32_t m_curTick = 0; /**< Current playback position for this track */
uint32_t m_loopStartTick = 0; /**< Tick to loop back to */
/** Current pointer to tempo control, iterated over playback */
const TempoChange* m_tempoPtr;
uint32_t m_tempo; /**< Current tempo (beats per minute) */
/** State of a single track within arrangement */
struct Track
{
struct Header
{
uint32_t m_type;
uint32_t m_pitchOff;
uint32_t m_modOff;
void swapBig();
};
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
int32_t m_pitchVal = 0; /**< Accumulated value of pitch */
uint32_t m_nextPitchTick = 0; /**< Upcoming position of pitch wheel change */
int32_t m_nextPitchDelta = 0; /**< Upcoming delta value of pitch */
int32_t m_modVal = 0; /**< Accumulated value of mod */
uint32_t m_nextModTick = 0; /**< Upcoming position of mod wheel change */
int32_t m_nextModDelta = 0; /**< Upcoming delta value of mod */
std::array<int, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
SongState* m_parent = nullptr;
uint8_t m_midiChan = 0xff; /**< MIDI channel number of song channel */
const TrackRegion* m_initRegion = nullptr; /**< Pointer to first track region */
const TrackRegion* m_curRegion = nullptr; /**< Pointer to currently-playing track region */
const TrackRegion* m_nextRegion = nullptr; /**< Pointer to next-queued track region */
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
int32_t m_lastN64EventTick =
0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */
double m_remDt = 0.0; /**< Remaining dt for keeping remainder between cycles */
uint32_t m_curTick = 0; /**< Current playback position for this track */
uint32_t m_loopStartTick = 0; /**< Tick to loop back to */
/** Current pointer to tempo control, iterated over playback */
const TempoChange* m_tempoPtr;
uint32_t m_tempo; /**< Current tempo (beats per minute) */
Track() = default;
Track(SongState& parent, uint8_t midiChan, uint32_t loopStart, const TrackRegion* regions, uint32_t tempo);
explicit operator bool() const { return m_parent != nullptr; }
void setRegion(const TrackRegion* region);
void advanceRegion();
bool advance(Sequencer& seq, double dt);
void resetTempo();
};
std::array<Track, 64> m_tracks;
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
int32_t m_pitchVal = 0; /**< Accumulated value of pitch */
uint32_t m_nextPitchTick = 0; /**< Upcoming position of pitch wheel change */
int32_t m_nextPitchDelta = 0; /**< Upcoming delta value of pitch */
int32_t m_modVal = 0; /**< Accumulated value of mod */
uint32_t m_nextModTick = 0; /**< Upcoming position of mod wheel change */
int32_t m_nextModDelta = 0; /**< Upcoming delta value of mod */
std::array<int, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
int32_t m_lastN64EventTick =
0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */
Track() = default;
Track(SongState& parent, uint8_t midiChan, uint32_t loopStart, const TrackRegion* regions, uint32_t tempo);
operator bool() const { return m_parent != nullptr; }
void setRegion(const TrackRegion* region);
void advanceRegion();
bool advance(Sequencer& seq, double dt);
void resetTempo();
};
std::array<Track, 64> m_tracks;
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
SongPlayState m_songState = SongPlayState::Playing; /**< High-level state of Song playback */
bool m_loop = true; /**< Enable looping */
SongPlayState m_songState = SongPlayState::Playing; /**< High-level state of Song playback */
bool m_loop = true; /**< Enable looping */
public:
/** Determine SNG version
* @param isBig returns true if big-endian SNG
* @return 0 for initial version, 1 for delta-time revision, -1 for non-SNG */
static int DetectVersion(const unsigned char* ptr, bool& isBig);
/** Determine SNG version
* @param isBig returns true if big-endian SNG
* @return 0 for initial version, 1 for delta-time revision, -1 for non-SNG */
static int DetectVersion(const unsigned char* ptr, bool& isBig);
/** initialize state for Song data at `ptr` */
bool initialize(const unsigned char* ptr, bool loop);
/** initialize state for Song data at `ptr` */
bool initialize(const unsigned char* ptr, bool loop);
uint32_t getInitialTempo() const { return m_header.m_initialTempo & 0x7fffffff; }
uint32_t getInitialTempo() const { return m_header.m_initialTempo & 0x7fffffff; }
/** advances `dt` seconds worth of commands in the Song
* @return `true` if END reached
*/
bool advance(Sequencer& seq, double dt);
/** advances `dt` seconds worth of commands in the Song
* @return `true` if END reached
*/
bool advance(Sequencer& seq, double dt);
};
}
#endif // __AMUSE_SONGSTATE_HPP__
} // namespace amuse

View File

@@ -1,146 +1,125 @@
#ifndef __AMUSE_SOUNDMACROSTATE_HPP__
#define __AMUSE_SOUNDMACROSTATE_HPP__
#pragma once
#include <cstdint>
#include <tuple>
#include <vector>
#include <list>
#include "Entity.hpp"
#include "Common.hpp"
#include "AudioGroupPool.hpp"
#include "amuse/AudioGroupPool.hpp"
#include "amuse/Common.hpp"
#include "amuse/Entity.hpp"
/* Squelch Win32 macro pollution >.< */
#undef SendMessage
#undef GetMessage
namespace amuse
{
namespace amuse {
class Voice;
/** Real-time state of SoundMacro execution */
struct SoundMacroState
{
/** 'program counter' stack for the active SoundMacro */
std::vector<std::tuple<ObjectId, const SoundMacro*, int>> m_pc;
void _setPC(int pc)
{
std::get<2>(m_pc.back()) = std::get<1>(m_pc.back())->assertPC(pc);
}
struct SoundMacroState {
/** 'program counter' stack for the active SoundMacro */
std::vector<std::tuple<ObjectId, const SoundMacro*, int>> m_pc;
void _setPC(int pc) { std::get<2>(m_pc.back()) = std::get<1>(m_pc.back())->assertPC(pc); }
double m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
uint8_t m_initVel; /**< Velocity played for this macro invocation */
uint8_t m_initMod; /**< Modulation played for this macro invocation */
uint8_t m_initKey; /**< Key played for this macro invocation */
uint8_t m_curVel; /**< Current velocity played for this macro invocation */
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
uint32_t m_curPitch; /**< Current key played for this macro invocation (in cents) */
double m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
uint8_t m_initVel; /**< Velocity played for this macro invocation */
uint8_t m_initMod; /**< Modulation played for this macro invocation */
uint8_t m_initKey; /**< Key played for this macro invocation */
uint8_t m_curVel; /**< Current velocity played for this macro invocation */
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
uint32_t m_curPitch; /**< Current key played for this macro invocation (in cents) */
double m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
bool m_keyoff; /**< keyoff message has been received */
bool m_sampleEnd; /**< sample has finished playback */
double m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
bool m_keyoff; /**< keyoff message has been received */
bool m_sampleEnd; /**< sample has finished playback */
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
bool m_indefiniteWait = false; /**< set when timer wait is indefinite (keyoff/sampleend only) */
bool m_keyoffWait = false; /**< set when active wait is a keyoff wait */
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
double m_waitCountdown; /**< countdown timer for active wait */
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
bool m_indefiniteWait = false; /**< set when timer wait is indefinite (keyoff/sampleend only) */
bool m_keyoffWait = false; /**< set when active wait is a keyoff wait */
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
double m_waitCountdown; /**< countdown timer for active wait */
int m_loopCountdown = -1; /**< countdown for current loop */
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
int m_loopCountdown = -1; /**< countdown for current loop */
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
bool m_useAdsrControllers; /**< when set, use the following controllers for envelope times */
uint8_t m_midiAttack; /**< Attack MIDI controller */
uint8_t m_midiDecay; /**< Decay MIDI controller */
uint8_t m_midiSustain; /**< Sustain MIDI controller */
uint8_t m_midiRelease; /**< Release MIDI controller */
bool m_useAdsrControllers; /**< when set, use the following controllers for envelope times */
uint8_t m_midiAttack; /**< Attack MIDI controller */
uint8_t m_midiDecay; /**< Decay MIDI controller */
uint8_t m_midiSustain; /**< Sustain MIDI controller */
uint8_t m_midiRelease; /**< Release MIDI controller */
SoundMacro::CmdPortamento::PortState m_portamentoMode =
SoundMacro::CmdPortamento::PortState::MIDIControlled; /**< (0: Off, 1: On, 2: MIDI specified) */
SoundMacro::CmdPortamento::PortType m_portamentoType =
SoundMacro::CmdPortamento::PortType::LastPressed; /**< (0: New key pressed while old key pressed, 1: Always) */
float m_portamentoTime = 0.5f; /**< portamento transition time, 0.f will perform legato */
SoundMacro::CmdPortamento::PortState m_portamentoMode =
SoundMacro::CmdPortamento::PortState::MIDIControlled; /**< (0: Off, 1: On, 2: MIDI specified) */
SoundMacro::CmdPortamento::PortType m_portamentoType =
SoundMacro::CmdPortamento::PortType::LastPressed; /**< (0: New key pressed while old key pressed, 1: Always) */
float m_portamentoTime = 0.5f; /**< portamento transition time, 0.f will perform legato */
/** Used to build a multi-component formula for overriding controllers */
struct Evaluator
{
enum class Combine : uint8_t
{
Set,
Add,
Mult
};
enum class VarType : uint8_t
{
Ctrl,
Var
};
/** Used to build a multi-component formula for overriding controllers */
struct Evaluator {
enum class Combine : uint8_t { Set, Add, Mult };
enum class VarType : uint8_t { Ctrl, Var };
/** Represents one term of the formula assembled via *_SELECT commands */
struct Component
{
uint8_t m_midiCtrl;
float m_scale;
Combine m_combine;
VarType m_varType;
/** Represents one term of the formula assembled via *_SELECT commands */
struct Component {
uint8_t m_midiCtrl;
float m_scale;
Combine m_combine;
VarType m_varType;
Component(uint8_t midiCtrl, float scale, Combine combine, VarType varType)
: m_midiCtrl(midiCtrl), m_scale(scale), m_combine(combine), m_varType(varType)
{
}
};
std::vector<Component> m_comps; /**< Components built up by the macro */
/** Combine additional component(s) to formula */
void addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType);
/** Calculate value */
float evaluate(double time, const Voice& vox, const SoundMacroState& st) const;
/** Determine if able to use */
operator bool() const { return m_comps.size() != 0; }
Component(uint8_t midiCtrl, float scale, Combine combine, VarType varType)
: m_midiCtrl(midiCtrl), m_scale(scale), m_combine(combine), m_varType(varType) {}
};
std::vector<Component> m_comps; /**< Components built up by the macro */
Evaluator m_volumeSel;
Evaluator m_panSel;
Evaluator m_pitchWheelSel;
Evaluator m_modWheelSel;
Evaluator m_pedalSel;
Evaluator m_portamentoSel;
Evaluator m_reverbSel;
Evaluator m_preAuxASel;
Evaluator m_preAuxBSel;
Evaluator m_auxAFxSel[3];
Evaluator m_auxBFxSel[3];
Evaluator m_postAuxB;
Evaluator m_spanSel;
Evaluator m_dopplerSel;
Evaluator m_tremoloSel;
/** Combine additional component(s) to formula */
void addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType);
int32_t m_variables[32]; /**< 32-bit variables set with relevant commands */
/** Calculate value */
float evaluate(double time, const Voice& vox, const SoundMacroState& st) const;
/** Event registration data for TRAP_EVENT */
struct EventTrap
{
ObjectId macroId = 0xffff;
uint16_t macroStep;
};
/** Determine if able to use */
explicit operator bool() const { return m_comps.size() != 0; }
};
Evaluator m_volumeSel;
Evaluator m_panSel;
Evaluator m_pitchWheelSel;
Evaluator m_modWheelSel;
Evaluator m_pedalSel;
Evaluator m_portamentoSel;
Evaluator m_reverbSel;
Evaluator m_preAuxASel;
Evaluator m_preAuxBSel;
Evaluator m_auxAFxSel[3];
Evaluator m_auxBFxSel[3];
Evaluator m_postAuxB;
Evaluator m_spanSel;
Evaluator m_dopplerSel;
Evaluator m_tremoloSel;
int32_t m_variables[32]; /**< 32-bit variables set with relevant commands */
/** Event registration data for TRAP_EVENT */
struct EventTrap {
ObjectId macroId = 0xffff;
uint16_t macroStep;
};
public:
/** initialize state for SoundMacro data at `ptr` */
void initialize(ObjectId id, const SoundMacro* macro, int step);
void initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
/** initialize state for SoundMacro data at `ptr` */
void initialize(ObjectId id, const SoundMacro* macro, int step);
void initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod);
/** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached
*/
bool advance(Voice& vox, double dt);
/** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached
*/
bool advance(Voice& vox, double dt);
/** keyoff event */
void keyoffNotify(Voice& vox);
/** keyoff event */
void keyoffNotify(Voice& vox);
/** sample end event */
void sampleEndNotify(Voice& vox);
/** sample end event */
void sampleEndNotify(Voice& vox);
};
}
#endif // __AMUSE_SOUNDMACROSTATE_HPP__
} // namespace amuse

View File

@@ -1,57 +1,49 @@
#ifndef __AMUSE_STUDIO_HPP__
#define __AMUSE_STUDIO_HPP__
#pragma once
#include <memory>
#include <list>
#include "Entity.hpp"
#include "Submix.hpp"
#include <type_traits>
namespace amuse
{
#include "amuse/Common.hpp"
#include "amuse/Entity.hpp"
#include "amuse/Submix.hpp"
namespace amuse {
struct StudioSend;
class Studio
{
friend class Engine;
Engine& m_engine;
Submix m_master;
Submix m_auxA;
Submix m_auxB;
class Studio {
friend class Engine;
Engine& m_engine;
Submix m_master;
Submix m_auxA;
Submix m_auxB;
std::list<StudioSend> m_studiosOut;
std::list<StudioSend> m_studiosOut;
#ifndef NDEBUG
bool _cyclicCheck(Studio* leaf);
bool _cyclicCheck(const Studio* leaf) const;
#endif
public:
Studio(Engine& engine, bool mainOut);
Studio(Engine& engine, bool mainOut);
/** Register a target Studio to send this Studio's mixing busses */
void addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB);
/** Register a target Studio to send this Studio's mixing busses */
void addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB);
/** Advise submixes of changing sample rate */
void resetOutputSampleRate(double sampleRate);
/** Advise submixes of changing sample rate */
void resetOutputSampleRate(double sampleRate);
Submix& getMaster() { return m_master; }
Submix& getAuxA() { return m_auxA; }
Submix& getAuxB() { return m_auxB; }
Submix& getMaster() { return m_master; }
Submix& getAuxA() { return m_auxA; }
Submix& getAuxB() { return m_auxB; }
Engine& getEngine() { return m_engine; }
Engine& getEngine() { return m_engine; }
};
struct StudioSend
{
ObjToken<Studio> m_targetStudio;
float m_dryLevel;
float m_auxALevel;
float m_auxBLevel;
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB)
{
}
struct StudioSend {
ObjToken<Studio> m_targetStudio;
float m_dryLevel;
float m_auxALevel;
float m_auxBLevel;
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB) {}
};
}
#endif // __AMUSE_STUDIO_HPP__
} // namespace amuse

View File

@@ -1,114 +1,85 @@
#ifndef __AMUSE_SUBMIX_HPP__
#define __AMUSE_SUBMIX_HPP__
#pragma once
#include <cstdint>
#include <memory>
#include "SoundMacroState.hpp"
#include "IBackendSubmix.hpp"
#include "IBackendVoice.hpp"
#include "EffectBase.hpp"
#include "EffectChorus.hpp"
#include "EffectDelay.hpp"
#include "EffectReverb.hpp"
#include <unordered_set>
#include <vector>
namespace amuse
{
#include "amuse/EffectBase.hpp"
#include "amuse/EffectChorus.hpp"
#include "amuse/EffectDelay.hpp"
#include "amuse/EffectReverb.hpp"
#include "amuse/IBackendSubmix.hpp"
#include "amuse/SoundMacroState.hpp"
namespace amuse {
class IBackendSubmix;
class Sequencer;
/** Intermediate mix of voices for applying auxiliary effects */
class Submix
{
friend class Engine;
friend class Voice;
friend class Sequencer;
Engine& m_root;
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
class Submix {
friend class Engine;
friend class Voice;
friend class Sequencer;
Engine& m_root;
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
std::vector<std::unique_ptr<EffectBase>> m_effectStack; /**< Ordered list of effects to apply to submix */
public:
Submix(Engine& engine);
Submix(Engine& engine);
/** Construct new effect */
template <class T, class... Args>
std::unique_ptr<EffectBaseTypeless> _makeEffect(Args... args)
{
switch (m_backendSubmix->getSampleFormat())
{
case SubmixFormat::Int16:
{
using ImpType = typename T::template ImpType<int16_t>;
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
}
case SubmixFormat::Int32:
default:
{
using ImpType = typename T::template ImpType<int32_t>;
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
}
case SubmixFormat::Float:
{
using ImpType = typename T::template ImpType<float>;
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
}
}
}
/** Construct new effect */
template <class T, class... Args>
std::unique_ptr<EffectBase> _makeEffect(Args... args) {
using ImpType = typename T::ImpType;
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
}
/** Add new effect to effect stack and assume ownership */
template <class T, class... Args>
T& makeEffect(Args... args)
{
m_effectStack.push_back(_makeEffect<T>(args...));
return static_cast<typename T::template ImpType<float>&>(*m_effectStack.back());
}
/** Add new effect to effect stack and assume ownership */
template <class T, class... Args>
T& makeEffect(Args... args) {
m_effectStack.push_back(_makeEffect<T>(args...));
return static_cast<typename T::ImpType&>(*m_effectStack.back());
}
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(const EffectChorusInfo& info);
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(const EffectChorusInfo& info);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(const EffectDelayInfo& info);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(const EffectDelayInfo& info);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(const EffectReverbStdInfo& info);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(const EffectReverbStdInfo& info);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay,
float crosstalk);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info);
/** Remove and deallocate all effects from effect stack */
void clearEffects() { m_effectStack.clear(); }
/** Remove and deallocate all effects from effect stack */
void clearEffects() { m_effectStack.clear(); }
/** Returns true when an effect callback is bound */
bool canApplyEffect() const { return m_effectStack.size() != 0; }
/** Returns true when an effect callback is bound */
bool canApplyEffect() const { return m_effectStack.size() != 0; }
/** in/out transformation entry for audio effect */
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** in/out transformation entry for audio effect */
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** in/out transformation entry for audio effect */
void applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** advice effects of changing sample rate */
void resetOutputSampleRate(double sampleRate);
/** in/out transformation entry for audio effect */
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const;
Engine& getEngine() { return m_root; }
/** advice effects of changing sample rate */
void resetOutputSampleRate(double sampleRate);
Engine& getEngine() { return m_root; }
std::vector<std::unique_ptr<EffectBaseTypeless>>& getEffectStack() { return m_effectStack; }
std::vector<std::unique_ptr<EffectBase>>& getEffectStack() { return m_effectStack; }
};
}
#endif // __AMUSE_SUBMIX_HPP__
} // namespace amuse

View File

@@ -1,396 +1,392 @@
#ifndef __AMUSE_VOICE_HPP__
#define __AMUSE_VOICE_HPP__
#pragma once
#include <array>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <list>
#include <memory>
#include <queue>
#include "SoundMacroState.hpp"
#include "Entity.hpp"
#include "AudioGroupSampleDirectory.hpp"
#include "AudioGroup.hpp"
#include "Envelope.hpp"
#include "Studio.hpp"
namespace amuse
{
#include "amuse/AudioGroup.hpp"
#include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/Entity.hpp"
#include "amuse/Envelope.hpp"
#include "amuse/SoundMacroState.hpp"
#include "amuse/Studio.hpp"
namespace amuse {
constexpr float convertMusyXPanToAmusePan(uint32_t pan) {
// Amuse expects pan values to be between [-1,1] while MusyX expects pan to be between [0,127], we need to make sure
// to map the values properly, we do this by subtracting the center value and then dividing, thus forcing [0, 127] into
// [-1,1], we must also make sure clamp the result so we don't over or underflow.
return std::clamp(static_cast<float>(pan - 64) / 63.f, -1.f, 1.f);
}
class IBackendVoice;
struct Keymap;
struct LayerMapping;
/** State of voice over lifetime */
enum class VoiceState
{
Playing, /**< SoundMacro actively executing, not in KeyOff */
KeyOff, /**< KeyOff event issued, macro beginning fade-out */
Dead /**< Default state, causes Engine to remove voice at end of pump cycle */
enum class VoiceState {
Playing, /**< SoundMacro actively executing, not in KeyOff */
KeyOff, /**< KeyOff event issued, macro beginning fade-out */
Dead /**< Default state, causes Engine to remove voice at end of pump cycle */
};
/** Individual source of audio */
class Voice : public Entity
{
friend class Engine;
friend class Sequencer;
friend struct SoundMacroState;
friend class Envelope;
friend class Emitter;
friend struct SoundMacro::CmdScaleVolume;
friend struct SoundMacro::CmdKeyOff;
friend struct SoundMacro::CmdScaleVolumeDLS;
friend struct SoundMacro::CmdReturn;
friend struct SoundMacro::CmdGoSub;
friend struct SoundMacro::CmdTrapEvent;
friend struct SoundMacro::CmdUntrapEvent;
friend struct SoundMacro::CmdGetMessage;
friend struct SoundMacro::CmdModeSelect;
class Voice : public Entity {
friend class Emitter;
friend class Engine;
friend class Envelope;
friend class Sequencer;
friend struct SoundMacro::CmdGetMessage;
friend struct SoundMacro::CmdGoSub;
friend struct SoundMacro::CmdKeyOff;
friend struct SoundMacro::CmdModeSelect;
friend struct SoundMacro::CmdReturn;
friend struct SoundMacro::CmdScaleVolume;
friend struct SoundMacro::CmdScaleVolumeDLS;
friend struct SoundMacro::CmdTrapEvent;
friend struct SoundMacro::CmdUntrapEvent;
friend struct SoundMacroState;
struct VolumeCache
{
bool m_curDLS = false; /**< Current DLS status of cached lookup */
float m_curVolLUTKey = -1.f; /**< Current input level cached out of LUT */
float m_curVolLUTVal = 0.f; /**< Current output level cached out of LUT */
float getVolume(float vol, bool dls);
float getCachedVolume() const { return m_curVolLUTVal; }
};
struct VolumeCache {
bool m_curDLS = false; /**< Current DLS status of cached lookup */
float m_curVolLUTKey = -1.f; /**< Current input level cached out of LUT */
float m_curVolLUTVal = 0.f; /**< Current output level cached out of LUT */
float getVolume(float vol, bool dls);
float getCachedVolume() const { return m_curVolLUTVal; }
};
struct Panning
{
double m_time; /**< time of PANNING command */
double m_dur; /**< requested duration of PANNING command */
uint8_t m_pos; /**< initial pan value of PANNING command */
int8_t m_width; /**< delta pan value to target of PANNING command */
};
struct Panning {
double m_time; /**< time of PANNING command */
double m_dur; /**< requested duration of PANNING command */
uint8_t m_pos; /**< initial pan value of PANNING command */
int8_t m_width; /**< delta pan value to target of PANNING command */
};
void _setObjectId(ObjectId id) { m_objectId = id; }
void _setObjectId(ObjectId id) { m_objectId = id; }
int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */
ObjToken<Studio> m_studio; /**< Studio this voice outputs to */
IObjToken<Sequencer> m_sequencer; /**< Strong reference to parent sequencer to retain ctrl vals */
int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */
ObjToken<Studio> m_studio; /**< Studio this voice outputs to */
IObjToken<Sequencer> m_sequencer; /**< Strong reference to parent sequencer to retain ctrl vals */
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */
SoundMacroState::EventTrap m_keyoffTrap; /**< Trap for keyoff (SoundMacro overrides default envelope behavior) */
SoundMacroState::EventTrap m_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
int32_t m_latestMessage = 0; /**< Latest message received on voice */
std::list<ObjToken<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */
SoundMacroState::EventTrap m_keyoffTrap; /**< Trap for keyoff (SoundMacro overrides default envelope behavior) */
SoundMacroState::EventTrap m_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
int32_t m_latestMessage = 0; /**< Latest message received on voice */
std::list<ObjToken<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
ObjToken<SampleEntryData> m_curSample; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
double m_dopplerRatio = 1.0; /**< Current ratio to mix with chromatic pitch for doppler effects */
double m_sampleRate = NativeSampleRate; /**< Current sample rate computed from relative sample key or SETPITCH */
double m_voiceTime = 0.0; /**< Current seconds of voice playback (per-sample resolution) */
uint64_t m_voiceSamples = 0; /**< Count of samples processed over voice's lifetime */
float m_lastLevel = 0.f; /**< Last computed level ([0,1] mapped to [-10,0] clamped decibels) */
float m_nextLevel = 0.f; /**< Next computed level used for lerp-mode amplitude */
VolumeCache m_nextLevelCache;
VolumeCache m_lerpedCache;
ObjToken<SampleEntryData> m_curSample; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
double m_dopplerRatio = 1.0; /**< Current ratio to mix with chromatic pitch for doppler effects */
double m_sampleRate = NativeSampleRate; /**< Current sample rate computed from relative sample key or SETPITCH */
double m_voiceTime = 0.0; /**< Current seconds of voice playback (per-sample resolution) */
uint64_t m_voiceSamples = 0; /**< Count of samples processed over voice's lifetime */
float m_lastLevel = 0.f; /**< Last computed level ([0,1] mapped to [-10,0] clamped decibels) */
float m_nextLevel = 0.f; /**< Next computed level used for lerp-mode amplitude */
VolumeCache m_nextLevelCache;
VolumeCache m_lerpedCache;
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
float m_targetUserVol = 1.f; /**< Target user volume of voice (slewed to prevent audible aliasing) */
float m_curUserVol = 1.f; /**< Current user volume of voice */
float m_curVol = 1.f; /**< Current volume of voice */
float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
float m_curAuxBVol = 0.f; /**< Current AuxB volume of voice */
float m_curPan = 0.f; /**< Current pan of voice */
float m_curSpan = -1.f; /**< Current surround pan of voice */
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
int32_t m_pitchWheelUp = 200; /**< Up range for pitchwheel control in cents */
int32_t m_pitchWheelDown = 200; /**< Down range for pitchwheel control in cents */
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
int32_t m_curPitch; /**< Current base pitch in cents */
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
bool m_needsSlew = false; /**< next _setTotalPitch will be slewed */
bool m_dlsVol = false; /**< Use DLS LUT for resolving voice volume */
float m_targetUserVol = 1.f; /**< Target user volume of voice (slewed to prevent audible aliasing) */
float m_curUserVol = 1.f; /**< Current user volume of voice */
float m_curVol = 1.f; /**< Current volume of voice */
float m_envelopeVol = 1.f; /**< Current envelope volume of voice */
float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
float m_curAuxBVol = 0.f; /**< Current AuxB volume of voice */
float m_curPan = 0.f; /**< Current pan of voice */
float m_curSpan = -1.f; /**< Current surround pan of voice */
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
int32_t m_pitchWheelUp = 200; /**< Up range for pitchwheel control in cents */
int32_t m_pitchWheelDown = 200; /**< Down range for pitchwheel control in cents */
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
int32_t m_curPitch; /**< Current base pitch in cents */
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
bool m_needsSlew = false; /**< next _setTotalPitch will be slewed */
bool m_dlsVol = false; /**< Use DLS LUT for resolving voice volume */
Envelope m_volAdsr; /**< Volume envelope */
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
double m_envelopeDur; /**< requested duration of last ENVELOPE command */
float m_envelopeStart; /**< initial value for last ENVELOPE command */
float m_envelopeEnd; /**< final value for last ENVELOPE command */
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
Envelope m_volAdsr; /**< Volume envelope */
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
double m_envelopeDur; /**< requested duration of last ENVELOPE command */
float m_envelopeStart; /**< initial value for last ENVELOPE command */
float m_envelopeEnd; /**< final value for last ENVELOPE command */
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
bool m_pitchEnv = false; /**< Pitch envelope activated */
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */
bool m_pitchEnv = false; /**< Pitch envelope activated */
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */
float m_portamentoTime = -1.f; /**< time since last portamento invocation, -1 for no active portamento-glide */
int32_t m_portamentoTarget; /**< destination pitch for latest portamento invocation */
float m_portamentoTime = -1.f; /**< time since last portamento invocation, -1 for no active portamento-glide */
int32_t m_portamentoTarget; /**< destination pitch for latest portamento invocation */
int32_t m_pitchSweep1 = 0; /**< Current value of PITCHSWEEP1 controller (in cents) */
int32_t m_pitchSweep2 = 0; /**< Current value of PITCHSWEEP2 controller (in cents) */
int16_t m_pitchSweep1Add = 0; /**< Value to add to PITCHSWEEP1 controller each cycle */
int16_t m_pitchSweep2Add = 0; /**< Value to add to PITCHSWEEP2 controller each cycle */
uint8_t m_pitchSweep1Times = 0; /**< Remaining times to advance PITCHSWEEP1 controller */
uint8_t m_pitchSweep2Times = 0; /**< Remaining times to advance PITCHSWEEP2 controller */
uint8_t m_pitchSweep1It = 0; /**< Current iteration of PITCHSWEEP1 controller */
uint8_t m_pitchSweep2It = 0; /**< Current iteration of PITCHSWEEP2 controller */
int32_t m_pitchSweep1 = 0; /**< Current value of PITCHSWEEP1 controller (in cents) */
int32_t m_pitchSweep2 = 0; /**< Current value of PITCHSWEEP2 controller (in cents) */
int16_t m_pitchSweep1Add = 0; /**< Value to add to PITCHSWEEP1 controller each cycle */
int16_t m_pitchSweep2Add = 0; /**< Value to add to PITCHSWEEP2 controller each cycle */
uint8_t m_pitchSweep1Times = 0; /**< Remaining times to advance PITCHSWEEP1 controller */
uint8_t m_pitchSweep2Times = 0; /**< Remaining times to advance PITCHSWEEP2 controller */
uint8_t m_pitchSweep1It = 0; /**< Current iteration of PITCHSWEEP1 controller */
uint8_t m_pitchSweep2It = 0; /**< Current iteration of PITCHSWEEP2 controller */
std::queue<Panning> m_panningQueue; /**< Queue of PANNING commands */
std::queue<Panning> m_spanningQueue; /**< Queue of SPANNING commands */
std::queue<Panning> m_panningQueue; /**< Queue of PANNING commands */
std::queue<Panning> m_spanningQueue; /**< Queue of SPANNING commands */
float m_vibratoTime = -1.f; /**< time since last VIBRATO command, -1 for no active vibrato */
int32_t m_vibratoLevel = 0; /**< scale of vibrato effect (in cents) */
int32_t m_vibratoModLevel = 0; /**< scale of vibrato mod-wheel influence (in cents) */
float m_vibratoPeriod = 0.f; /**< vibrato wave period-time, 0.f will disable vibrato */
bool m_vibratoModWheel = false; /**< vibrato scaled with mod-wheel if set */
float m_vibratoTime = -1.f; /**< time since last VIBRATO command, -1 for no active vibrato */
int32_t m_vibratoLevel = 0; /**< scale of vibrato effect (in cents) */
int32_t m_vibratoModLevel = 0; /**< scale of vibrato mod-wheel influence (in cents) */
float m_vibratoPeriod = 0.f; /**< vibrato wave period-time, 0.f will disable vibrato */
bool m_vibratoModWheel = false; /**< vibrato scaled with mod-wheel if set */
float m_tremoloScale = 0.f; /**< minimum volume factor produced via LFO */
float m_tremoloModScale = 0.f; /**< minimum volume factor produced via LFO, scaled via mod wheel */
float m_tremoloScale = 0.f; /**< minimum volume factor produced via LFO */
float m_tremoloModScale = 0.f; /**< minimum volume factor produced via LFO, scaled via mod wheel */
float m_lfoPeriods[2] = {}; /**< time-periods for LFO1 and LFO2 */
std::unique_ptr<int8_t[]> m_ctrlValsSelf; /**< Self-owned MIDI Controller values */
int8_t* m_extCtrlVals = nullptr; /**< MIDI Controller values (external storage) */
std::array<float, 2> m_lfoPeriods{}; /**< time-periods for LFO1 and LFO2 */
std::unique_ptr<int8_t[]> m_ctrlValsSelf; /**< Self-owned MIDI Controller values */
int8_t* m_extCtrlVals = nullptr; /**< MIDI Controller values (external storage) */
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
void _destroy();
bool _checkSamplePos(bool& looped);
void _doKeyOff();
void _macroKeyOff();
void _macroSampleEnd();
void _procSamplePre(int16_t& samp);
VolumeCache m_masterCache;
template <typename T>
T _procSampleMaster(double time, T samp);
VolumeCache m_auxACache;
template <typename T>
T _procSampleAuxA(double time, T samp);
VolumeCache m_auxBCache;
template <typename T>
T _procSampleAuxB(double time, T samp);
void _setTotalPitch(int32_t cents, bool slew);
bool _isRecursivelyDead();
void _bringOutYourDead();
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
void _destroy();
bool _checkSamplePos(bool& looped);
void _doKeyOff();
void _macroKeyOff();
void _macroSampleEnd();
void _procSamplePre(int16_t& samp);
VolumeCache m_masterCache;
template <typename T>
T _procSampleMaster(double time, T samp);
VolumeCache m_auxACache;
template <typename T>
T _procSampleAuxA(double time, T samp);
VolumeCache m_auxBCache;
template <typename T>
T _procSampleAuxB(double time, T samp);
void _setTotalPitch(int32_t cents, bool slew);
bool _isRecursivelyDead();
void _bringOutYourDead();
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
std::list<ObjToken<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
std::list<ObjToken<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
bool pushPc = false);
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
void _setPan(float pan);
void _setSurroundPan(float span);
void _setChannelCoefs(const float coefs[8]);
void _setPitchWheel(float pitchWheel);
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
std::array<float, 8> _panLaw(float frontPan, float backPan, float totalSpan) const;
void _setPan(float pan);
void _setSurroundPan(float span);
void _setChannelCoefs(const std::array<float, 8>& coefs);
void _setPitchWheel(float pitchWheel);
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
public:
~Voice();
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken<Studio> studio);
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter,
ObjToken<Studio> studio);
~Voice() override;
Voice(Engine& engine, const AudioGroup& group, GroupId groupId, int vid, bool emitter, ObjToken<Studio> studio);
Voice(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid, int vid, bool emitter,
ObjToken<Studio> studio);
/** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */
void preSupplyAudio(double dt);
/** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */
void preSupplyAudio(double dt);
/** Request specified count of audio frames (samples) from voice,
* internally advancing the voice stream */
size_t supplyAudio(size_t frames, int16_t* data);
/** Request specified count of audio frames (samples) from voice,
* internally advancing the voice stream */
size_t supplyAudio(size_t frames, int16_t* data);
/** Called three times after resampling supplyAudio output, voice should
* perform volume processing / send routing for each aux bus and master */
void routeAudio(size_t frames, double dt, int busId, int16_t* in, int16_t* out);
void routeAudio(size_t frames, double dt, int busId, int32_t* in, int32_t* out);
void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
/** Called three times after resampling supplyAudio output, voice should
* perform volume processing / send routing for each aux bus and master */
void routeAudio(size_t frames, double dt, int busId, int16_t* in, int16_t* out);
void routeAudio(size_t frames, double dt, int busId, int32_t* in, int32_t* out);
void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
/** Obtain pointer to Voice's Studio */
ObjToken<Studio> getStudio() { return m_studio; }
/** Obtain pointer to Voice's Studio */
ObjToken<Studio> getStudio() { return m_studio; }
/** Get current state of voice */
VoiceState state() const { return m_voxState; }
/** Get current state of voice */
VoiceState state() const { return m_voxState; }
/** Get VoiceId of this voice (unique to all currently-playing voices) */
int vid() const { return m_vid; }
/** Get VoiceId of this voice (unique to all currently-playing voices) */
int vid() const { return m_vid; }
/** Get max VoiceId of this voice and any contained children */
int maxVid() const;
/** Get max VoiceId of this voice and any contained children */
int maxVid() const;
/** Allocate parallel macro and tie to voice for possible emitter influence */
ObjToken<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
/** Allocate parallel macro and tie to voice for possible emitter influence */
ObjToken<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
/** Load specified SoundMacro Object from within group into voice */
bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** Load specified SoundMacro Object from within group into voice */
bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** Load specified SoundMacro Object into voice */
bool loadMacroObject(const SoundMacro* macro, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** Load specified SoundMacro Object into voice */
bool loadMacroObject(const SoundMacro* macro, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** Load specified song page object (Keymap/Layer) from within group into voice */
bool loadPageObject(ObjectId objectId, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
/** Load specified song page object (Keymap/Layer) from within group into voice */
bool loadPageObject(ObjectId objectId, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
void keyOff();
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
void keyOff();
/** Sends numeric message to voice and all siblings */
void message(int32_t val);
/** Sends numeric message to voice and all siblings */
void message(int32_t val);
/** Start playing specified sample from within group, optionally by sample offset */
void startSample(SampleId sampId, int32_t offset);
/** Start playing specified sample from within group, optionally by sample offset */
void startSample(SampleId sampId, int32_t offset);
/** Stop playing current sample */
void stopSample();
/** Stop playing current sample */
void stopSample();
/** Set current voice volume immediately */
void setVolume(float vol);
/** Set current voice volume immediately */
void setVolume(float vol);
/** Set current voice panning immediately */
void setPan(float pan);
/** Set current voice panning immediately */
void setPan(float pan);
/** Set current voice surround-panning immediately */
void setSurroundPan(float span);
/** Set current voice surround-panning immediately */
void setSurroundPan(float span);
/** Set current voice channel coefficients immediately */
void setChannelCoefs(const float coefs[8]);
/** Set current voice channel coefficients immediately */
void setChannelCoefs(const std::array<float, 8>& coefs);
/** Start volume envelope to specified level */
void startEnvelope(double dur, float vol, const Curve* envCurve);
/** Start volume envelope to specified level */
void startEnvelope(double dur, float vol, const Curve* envCurve);
/** Start volume envelope from zero to current level */
void startFadeIn(double dur, float vol, const Curve* envCurve);
/** Start volume envelope from zero to current level */
void startFadeIn(double dur, float vol, const Curve* envCurve);
/** Start pan envelope to specified position */
void startPanning(double dur, uint8_t panPos, int8_t panWidth);
/** Start pan envelope to specified position */
void startPanning(double dur, uint8_t panPos, int8_t panWidth);
/** Start span envelope to specified position */
void startSpanning(double dur, uint8_t spanPos, int8_t spanWidth);
/** Start span envelope to specified position */
void startSpanning(double dur, uint8_t spanPos, int8_t spanWidth);
/** Set voice relative-pitch in cents */
void setPitchKey(int32_t cents);
/** Set voice relative-pitch in cents */
void setPitchKey(int32_t cents);
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
void setPedal(bool pedal);
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
void setPedal(bool pedal);
/** Set doppler factor for voice */
void setDoppler(float doppler);
/** Set doppler factor for voice */
void setDoppler(float doppler);
/** Set vibrato parameters for voice */
void setVibrato(int32_t level, bool modScale, float period);
/** Set vibrato parameters for voice */
void setVibrato(int32_t level, bool modScale, float period);
/** Configure modwheel influence range over vibrato */
void setMod2VibratoRange(int32_t modLevel);
/** Configure modwheel influence range over vibrato */
void setMod2VibratoRange(int32_t modLevel);
/** Setup tremolo parameters for voice */
void setTremolo(float tremoloScale, float tremoloModScale);
/** Setup tremolo parameters for voice */
void setTremolo(float tremoloScale, float tremoloModScale);
/** Setup LFO1 for voice */
void setLFO1Period(float period) { m_lfoPeriods[0] = period; }
/** Setup LFO1 for voice */
void setLFO1Period(float period) { m_lfoPeriods[0] = period; }
/** Setup LFO2 for voice */
void setLFO2Period(float period) { m_lfoPeriods[1] = period; }
/** Setup LFO2 for voice */
void setLFO2Period(float period) { m_lfoPeriods[1] = period; }
/** Setup pitch sweep controller 1 */
void setPitchSweep1(uint8_t times, int16_t add);
/** Setup pitch sweep controller 1 */
void setPitchSweep1(uint8_t times, int16_t add);
/** Setup pitch sweep controller 2 */
void setPitchSweep2(uint8_t times, int16_t add);
/** Setup pitch sweep controller 2 */
void setPitchSweep2(uint8_t times, int16_t add);
/** Set reverb mix for voice */
void setReverbVol(float rvol);
/** Set reverb mix for voice */
void setReverbVol(float rvol);
/** Set AuxB volume for voice */
void setAuxBVol(float bvol);
/** Set AuxB volume for voice */
void setAuxBVol(float bvol);
/** Set envelope for voice */
void setAdsr(ObjectId adsrId, bool dls);
/** Set envelope for voice */
void setAdsr(ObjectId adsrId, bool dls);
/** Set pitch in absolute hertz */
void setPitchFrequency(uint32_t hz, uint16_t fine);
/** Set pitch in absolute hertz */
void setPitchFrequency(uint32_t hz, uint16_t fine);
/** Set pitch envelope */
void setPitchAdsr(ObjectId adsrId, int32_t cents);
/** Set pitch envelope */
void setPitchAdsr(ObjectId adsrId, int32_t cents);
/** Set pitchwheel value for use with controller */
void setPitchWheel(float pitchWheel);
/** Set pitchwheel value for use with controller */
void setPitchWheel(float pitchWheel);
/** Set effective pitch range via the pitchwheel controller */
void setPitchWheelRange(int8_t up, int8_t down);
/** Set effective pitch range via the pitchwheel controller */
void setPitchWheelRange(int8_t up, int8_t down);
/** Set aftertouch */
void setAftertouch(uint8_t aftertouch);
/** Set aftertouch */
void setAftertouch(uint8_t aftertouch);
/** Assign voice to keygroup for coordinated mass-silencing */
void setKeygroup(uint8_t kg) { m_keygroup = kg; }
/** Assign voice to keygroup for coordinated mass-silencing */
void setKeygroup(uint8_t kg) { m_keygroup = kg; }
/** Get note played on voice */
uint8_t getLastNote() const { return m_state.m_initKey; }
/** Get note played on voice */
uint8_t getLastNote() const { return m_state.m_initKey; }
/** Do portamento glide; returns `false` if portamento disabled */
bool doPortamento(uint8_t newNote);
/** Do portamento glide; returns `false` if portamento disabled */
bool doPortamento(uint8_t newNote);
/** Get MIDI Controller value on voice */
int8_t getCtrlValue(uint8_t ctrl) const
{
if (!m_extCtrlVals)
{
if (m_ctrlValsSelf)
return m_ctrlValsSelf[ctrl];
return 0;
}
return m_extCtrlVals[ctrl];
/** Get MIDI Controller value on voice */
int8_t getCtrlValue(uint8_t ctrl) const {
if (!m_extCtrlVals) {
if (m_ctrlValsSelf)
return m_ctrlValsSelf[ctrl];
return 0;
}
return m_extCtrlVals[ctrl];
}
/** Set MIDI Controller value on voice */
void setCtrlValue(uint8_t ctrl, int8_t val)
{
if (!m_extCtrlVals)
{
std::unique_ptr<int8_t[]>& vals = _ensureCtrlVals();
vals[ctrl] = val;
}
else
m_extCtrlVals[ctrl] = val;
_notifyCtrlChange(ctrl, val);
}
/** Set MIDI Controller value on voice */
void setCtrlValue(uint8_t ctrl, int8_t val) {
if (!m_extCtrlVals) {
std::unique_ptr<int8_t[]>& vals = _ensureCtrlVals();
vals[ctrl] = val;
} else
m_extCtrlVals[ctrl] = val;
_notifyCtrlChange(ctrl, val);
}
/** 'install' external MIDI controller storage */
void installCtrlValues(int8_t* cvs)
{
m_ctrlValsSelf.reset();
m_extCtrlVals = cvs;
for (ObjToken<Voice>& vox : m_childVoices)
vox->installCtrlValues(cvs);
}
/** 'install' external MIDI controller storage */
void installCtrlValues(int8_t* cvs) {
m_ctrlValsSelf.reset();
m_extCtrlVals = cvs;
for (ObjToken<Voice>& vox : m_childVoices)
vox->installCtrlValues(cvs);
}
/** Get MIDI pitch wheel value on voice */
float getPitchWheel() const { return m_curPitchWheel; }
/** Get MIDI pitch wheel value on voice */
float getPitchWheel() const { return m_curPitchWheel; }
/** Get MIDI aftertouch value on voice */
int8_t getAftertouch() const { return m_curAftertouch; }
/** Get MIDI aftertouch value on voice */
int8_t getAftertouch() const { return m_curAftertouch; }
/** Get count of all voices in hierarchy, including this one */
size_t getTotalVoices() const;
/** Get count of all voices in hierarchy, including this one */
size_t getTotalVoices() const;
/** Get latest decoded sample index */
uint32_t getSamplePos() const { return m_curSamplePos; }
/** Get latest decoded sample index */
uint32_t getSamplePos() const { return m_curSamplePos; }
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
void kill();
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
void kill();
};
}
#endif // __AMUSE_VOICE_HPP__
} // namespace amuse

View File

@@ -1,10 +1,6 @@
#ifndef __AMUSE_VOLUME_TABLE_HPP__
#define __AMUSE_VOLUME_TABLE_HPP__
#pragma once
namespace amuse
{
namespace amuse {
float LookupVolume(float vol);
float LookupDLSVolume(float vol);
}
#endif // __AMUSE_VOLUME_TABLE_HPP__
} // namespace amuse

Some files were not shown because too many files have changed in this diff Show More