Compare commits

...

284 Commits

Author SHA1 Message Date
Phillip Stephens ad9bc96af4 Minor fixes 2023-09-20 10:59:52 -07:00
Phillip Stephens bc1e04b116 Add option to enable/disable amuse-gui 2023-01-11 12:49:43 -08:00
Luke Street 101746f268 Common.hpp: Refactor to remove <Windows.h> include 2022-02-22 00:46:15 -05:00
Luke Street 89986bdd65 Stop trying to make <ranges> happen, it's not going to happen 2021-10-25 22:34:11 -04:00
Luke Street 7865694d75 Merge remote-tracking branch 'origin/utf8' 2021-10-25 19:05:48 -04:00
Phillip Stephens f37a067174
Minor code cleanup 2021-07-10 19:38:41 -07:00
Luke Street 6b73240364 Fixes for macOS/Linux 2021-06-30 16:21:20 -04:00
Luke Street 603e066eed Use UTF-8 exclusively internally 2021-06-30 14:15:40 -04:00
Phillip Stephens 6e896fa1d3
Fix crashes while moving tiles 2021-06-20 17:42:38 -07:00
Luke Street 3290b706fc EffectChorus: Fix large std::array compile error 2021-06-11 21:54:56 -04:00
Phillip Stephens d96be61e29
Fix GC compatiblity by always exporting id tables (if empty just write 0xFFFF) 2021-03-18 22:50:28 -07:00
Luke Street 217e791b88 Fix AppleClang -Wrange-loop-analysis warnings 2021-03-05 09:26:26 -05:00
Luke Street df7136d265 Voice: Fix -Wmaybe-uninitialized with GCC9 2021-03-01 18:47:03 -05:00
Luke Street 454424b8b3 AudioGroupSampleDirectory: Fix -Wmaybe-uninitialized with GCC9 2021-03-01 18:36:37 -05:00
Phillip Stephens 2ec749e6c7
Upgrade to qt 6 2021-01-23 16:27:34 -08:00
Luke Street d3c4f568bb Fix target_atdna ordering 2021-01-06 20:27:57 -05:00
Phillip Stephens 1275293327
Add utility function to convert panning values from [0,127] to [-1,1] 2020-10-24 15:58:29 -07:00
Phillip Stephens fa3188e569
Harden Listener against NaN as well 2020-09-27 22:07:01 -07:00
Phillip Stephens 69bc5dd69f
Harden setVectors against NaN values 2020-09-27 14:36:18 -07:00
Phillip Stephens 63a58858e8
Fix deprecation warnings in amuse-gui 2020-09-08 16:23:55 -07:00
Luke Street f172427991 amuse-mkqticon: Use target_include_directories 2020-06-13 17:17:14 -04:00
Phillip Stephens aa9ef6e866
Merge commit 'cae2e9c' 2020-06-12 05:42:32 -07:00
Phillip Stephens cae2e9c897
Fix libPNG 2020-06-12 05:42:07 -07:00
Luke Street 281af5d997 Correct LIBPNG -> PNG 2020-05-27 21:40:04 -04:00
Luke Street 70380d8fbc Add Homebrew Qt5 path hint 2020-05-24 12:02:39 -04:00
Phillip Stephens 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
Phillip Stephens 9c75aeccbe
Fix C header export 2020-03-12 00:54:22 -07:00
Luke Street 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
Phillip Stephens ae26ed2fda
Compile fixes 2019-09-14 10:07:52 -07:00
Phillip Stephens 757defee7a
Merge pull request #43 from lioncash/array2
AudioGroupPool: Use std::array where applicable
2019-09-10 23:53:38 -07:00
Phillip Stephens 02da440523
Merge pull request #41 from lioncash/include
VolumeTable: Remove unnecessary Common include
2019-09-10 23:53:29 -07:00
Phillip Stephens 0dca047352
Merge pull request #40 from lioncash/const
Studio: Make _cyclicCheck a const member function
2019-09-10 23:53:19 -07:00
Phillip Stephens 95ffcc2d16
Merge pull request #39 from lioncash/magic
Sequencer: Minor cleanup
2019-09-10 23:53:08 -07:00
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens d88b14c38a
Merge pull request #37 from lioncash/emitter
Emitter: Use std::array where applicable
2019-09-08 00:10:40 -07:00
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 9b482ba013
Merge pull request #30 from lioncash/include
Editor: Amend includes of ProjectModel.hpp
2019-08-28 22:30:35 -07:00
Phillip Stephens 5ee32c105b
Merge pull request #32 from lioncash/status-bar
StatusBarWidget: Use std::array where applicable
2019-08-28 22:29:44 -07:00
Phillip Stephens af3f2a8d0c
Merge pull request #33 from lioncash/studio
StudioSetupWidget: Use std::array where applicable
2019-08-28 22:29:36 -07:00
Phillip Stephens 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
Phillip Stephens 1604cbeb70
Linux compile fixes 2019-08-27 18:02:30 -07:00
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 8569cab4d7
Merge pull request #27 from AxioDL/revert-23-rotate
Revert "Editor/LayersEditor: Simplify moveRows implementation"
2019-08-26 18:31:36 -07:00
Phillip Stephens 4fdb2bde8c
Revert "Editor/LayersEditor: Simplify moveRows implementation" 2019-08-26 18:31:18 -07:00
Phillip Stephens 80da83eb16
Merge pull request #25 from lioncash/array
Envelope: Make lookup table const
2019-08-26 15:54:04 -07:00
Phillip Stephens 8f810e571c
Merge pull request #24 from lioncash/emplace
General: Use emplace_back where applicable
2019-08-26 15:53:55 -07:00
Phillip Stephens 8f8add1eb1
Merge pull request #23 from lioncash/rotate
Editor/LayersEditor: Simplify moveRows implementation
2019-08-26 15:53:46 -07:00
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 6850f0b2c9
Add missing include 2019-08-25 09:25:27 -07:00
Phillip Stephens c08dd97cbf
Merge pull request #19 from lioncash/key-array
Editor/KeymapEditor: Use std::array where applicable
2019-08-25 09:00:31 -07:00
Phillip Stephens 8da26720d1
Merge pull request #18 from lioncash/fwd
General: Clean up inclusions
2019-08-25 09:00:23 -07:00
Phillip Stephens db9e911a23
Merge pull request #17 from lioncash/curve
CurveEditor: Use std::array where applicable
2019-08-25 09:00:15 -07:00
Phillip Stephens 2b09d86df4
Merge pull request #16 from lioncash/const
ADSREditor: Make colors const
2019-08-25 09:00:07 -07:00
Phillip Stephens 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
Phillip Stephens bc8218e183
Merge pull request #14 from lioncash/cmake
CMakeLists: Add source files directly to amuse target
2019-08-25 01:39:02 -07:00
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 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
Phillip Stephens 9e6d97564b Switch build fixes 2018-09-25 15:39:22 -07:00
Jack Andersen 81f0a91569 Looping SNG support; bug fixes 2018-09-08 11:34:01 -10:00
Jack Andersen 25aacc9511 Multichannel rendering for amuserender 2018-09-03 14:14:12 -10:00
Jack Andersen 8fee7a282b Avoid narrowing conversion for panning 2018-09-02 20:33:41 -10:00
Jack Andersen 1312a5fa86 Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/amuse 2018-09-02 14:43:26 -10:00
Jack Andersen 6a7f32a29d Bug fixes for MP2 extraction 2018-09-02 14:43:05 -10:00
Phillip Stephens a16fcf3342 Merge commit 'd9018e2' 2018-09-01 20:46:26 -07:00
Phillip Stephens d9018e2d2e Fix NameDB and Voice related crashes 2018-09-01 20:45:46 -07:00
Jack Andersen 9cc4bdefd0 Various model editing bug fixes 2018-08-30 10:34:10 -10:00
Jack Andersen 2d4fd3493e Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/amuse 2018-08-29 22:23:00 -10:00
Jack Andersen 59f99d0b99 Better unsaved project warning 2018-08-29 22:19:53 -10:00
Jack Andersen 3d29005567 Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/amuse 2018-08-29 22:16:53 -10:00
Jack Andersen 68f0f2e769 Use asynchronous file dialogs 2018-08-29 22:16:37 -10:00
Jack Andersen 1884e0ca7a waitForRetrace API change 2018-08-27 19:42:47 -10:00
Jack Andersen 4fc5dfdc76 macOS fixes 2018-08-27 17:48:53 -10:00
Jack Andersen 33d2cc9ef1 Windows build fixes 2018-08-25 21:58:04 -10:00
Jack Andersen 19c5443e9e Bug fixes, xref search, more context menus 2018-08-25 18:57:02 -10:00
Jack Andersen 27cdee0c14 Clipboard support and various bug fixes 2018-08-24 22:34:04 -10:00
Jack Andersen cefa0ac18c Ensure all post-show widgets are constructed with a parent 2018-08-19 10:42:11 -10:00
Jack Andersen 303877655f Fixes for Windows 2018-08-19 10:05:39 -10:00
Jack Andersen 08988fe3ec Group export and various bug fixes 2018-08-18 14:28:52 -10:00
Jack Andersen fec074ad30 Studio setup window and volume LUT 2018-08-15 20:26:44 -10:00
Jack Andersen 5e89954094 More undo commands and pitch/mod coding fix 2018-08-13 22:36:02 -10:00
Jack Andersen 277e78c14b Vastly improved node insertion/deletion 2018-08-10 20:31:10 -10:00
Jack Andersen 45cb6be3c5 Implement songs import function 2018-08-09 22:11:08 -10:00
Jack Andersen d24e06f101 All editors implemented 2018-08-09 20:19:23 -10:00
Jack Andersen eff832bb8c Implement SongGroupEditor 2018-08-08 21:42:17 -10:00
Jack Andersen 32deea8341 Implement LayersEditor 2018-08-06 21:12:30 -10:00
Jack Andersen 2b45f69ff4 Add support for Keymap playback 2018-08-05 18:48:03 -10:00
Jack Andersen 409d52c120 Initial KeymapEditor implementation 2018-08-05 18:20:42 -10:00
Jack Andersen 2abed18784 Finish CurveEditor implementation 2018-08-04 12:05:01 -10:00
Jack Andersen c2a242022a Use manual Qt macros in CMake 2018-08-03 16:31:47 -10:00
Jack Andersen 57cbbf24b1 Implement ADSREditor 2018-08-03 16:07:34 -10:00
Jack Andersen 721dd361fa Finish SampleEditor implementation 2018-08-02 17:45:48 -10:00
Jack Andersen d062a087c5 SampleEditor rendering fixes 2018-07-31 14:49:05 -10:00
Jack Andersen 6f0a26a86c Work on SampleEditor 2018-07-30 22:04:43 -10:00
Jack Andersen f00904dd76 Subclass delete button rather than use QSS 2018-07-29 20:34:01 -10:00
Jack Andersen 708ed599ae Merge branch 'athena-refactor' of ssh://git.axiodl.com:6431/AxioDL/amuse into athena-refactor 2018-07-29 20:22:07 -10:00
Jack Andersen 8d24e19989 Initial SampleEditor 2018-07-29 20:20:03 -10:00
Phillip Stephens e0f4fdd959 Add hover state to Macro Delete button 2018-07-28 21:09:30 -07:00
Jack Andersen 16745c9bf8 ObjToken refactor and Sample nodes 2018-07-28 17:37:06 -10:00
Jack Andersen f5984141fd Implement amuse playback 2018-07-27 18:34:29 -10:00
Jack Andersen cb24322fc1 Integrate undo infrastructure with SoundMacroEditor 2018-07-25 17:41:48 -10:00
Jack Andersen ca81c07600 Add translation infrastructure 2018-07-24 20:01:01 -10:00
Jack Andersen b3958e9d52 Merge branch 'athena-refactor' of ssh://git.axiodl.com:6431/AxioDL/amuse into athena-refactor 2018-07-21 17:46:18 -10:00
Jack Andersen 441a3dbfd9 Work on SoundMacroEditor 2018-07-21 17:45:47 -10:00
Phillip Stephens 095e73a2b4 Fix amuseplay compiling 2018-07-20 09:49:24 -07:00
Jack Andersen 321c2d9a3c Finish SoundMacro command introspection. Initial work on SoundMacro
editor
2018-07-19 20:38:09 -10:00
Jack Andersen f50ee6e8f1 More work on Amuse Editor 2018-07-17 21:39:26 -10:00
Jack Andersen 3f265cdb46 Initial ProjectModel implementation 2018-07-16 18:48:38 -10:00
Jack Andersen 7a38fd0676 Work on project file reading 2018-07-15 21:41:15 -10:00
Jack Andersen 26cfa07f77 More athena refactoring 2018-07-14 20:10:50 -10:00
Jack Andersen 4c884d019d Lots of foundational work for Amuse editor 2018-07-13 20:06:33 -10:00
Jack Andersen 1e8b6e599c Add FaceGrey image 2018-07-09 16:06:45 -10:00
Jack Andersen cdc720b8a7 Work on amuse GUI, use C++ linking for audio decoders 2018-07-09 11:05:31 -07:00
Jack Andersen 1e8ce19b16 Ensure mod wheel value is used in respective vibrato mode 2018-06-21 11:03:35 -10:00
Jack Andersen d7011f0e77 Windows command-line argument fix 2018-05-28 10:26:55 -10:00
Jack Andersen 77c7daa67c Windows build fixes and warning avoidance 2018-05-24 20:37:27 -10:00
Phillip Stephens e3c936648b Fix compiling of amuserender/play 2018-05-20 08:29:27 -07:00
Jack Andersen 1fefba66e7 Implement VIBRATO 2018-05-18 19:15:12 -10:00
Jack Andersen 594fb346e1 Add sanitizers to executable targets 2018-05-06 13:06:49 -10:00
Jack Andersen 3833c137f6 Linux amuseplay crash fixes 2018-05-06 12:47:03 -10:00
Jack Andersen 912eb7950c Add amuse-gui .rc in CMake 2018-03-23 11:54:52 -10:00
Phillip Stephens 4146cdd049 Rename mainwindow.ui to MainWindow.ui 2018-03-15 16:17:00 -07:00
Jack Andersen f5a0a46453 Stub editor application 2018-03-14 14:25:29 -10:00
Jack Andersen 8f94bdd488 Minor syntax adjustment 2018-01-11 16:32:13 -10:00
Jack Andersen f37296d560 Updated boo API 2018-01-09 20:18:15 -10:00
Phillip Stephens fd292491c7 Build fix 2018-01-07 01:27:58 -08:00
Jack Andersen 380381c7d1 Update for boo API change 2018-01-06 19:18:31 -10:00
Jack Andersen 3f9a91b0ab Disable cotire unity build 2017-12-31 16:52:40 -10:00
Jack Andersen 985994a291 Huge compile performance refactor 2017-12-28 21:57:22 -10:00
Jack Andersen e37128d657 Use com.axiodl.* rather than io.github.axiodl.* for Apple identifiers 2017-12-27 17:45:59 -10:00
Jack Andersen 4c2a262478 Removed redundant NDEBUGs 2017-12-22 19:35:55 -10:00
Jack Andersen 3eba4cc1ac Minor Amuse-AU changes 2017-12-15 14:18:57 -10:00
Jack Andersen 91b88c0568 Add ASan build capability 2017-12-11 16:05:54 -10:00
Jack Andersen 6f7d09ce45 Fix amuseplay build 2017-12-06 18:34:45 -10:00
Jack Andersen 5caf6bf017 Remove optional.hpp dependency 2017-12-06 18:06:35 -10:00
Jack Andersen d399f1d302 UWP support 2017-12-05 17:23:07 -10:00
Jack Andersen 77215fc996 boo::ObjToken for referencing voices and submixes 2017-12-03 16:51:23 -10:00
Jack Andersen 2f4d0e7541 Minor code formatting adjustments 2017-12-02 20:09:34 -10:00
Jack Andersen 89233e98b5 Use sqrt() in pan law 2017-11-28 00:05:59 -10:00
Jack Andersen b890a76e20 Adjust 7.1 pan law 2017-11-27 17:38:58 -10:00
Jack Andersen 3c7cf5515f Voice lifetime and emitter bug fixes 2017-11-27 16:44:35 -10:00
Jack Andersen ebe6f18898 MIDIToSong bug fix 2017-11-26 19:04:13 -10:00
Jack Andersen cab7402104 Don't keyoff on non-trapped, non-looping macros 2017-11-21 00:35:38 -10:00
Jack Andersen 862c618b7e Require CMake 3.10 2017-11-13 18:52:03 -10:00
Jack Andersen 448b212ad9 Windows fixes 2017-11-13 17:35:02 -10:00
Jack Andersen 03d597b0ac Linux build fixes 2017-11-12 21:21:07 -10:00
Jack Andersen 75830dc6dd string_view refactor 2017-11-12 20:15:33 -10:00
Jack Andersen e18c0a62de Update amuseplay for boo changes 2017-11-04 20:16:06 -10:00
Jack Andersen 047a91452e Win32 macro undef 2017-09-30 19:30:34 -10:00
Jack Andersen fdf07d6c33 Update amuseplay for new texture API 2017-09-30 18:30:12 -10:00
Jack Andersen 4b2b86f420 Emitter bug fixes and test macro for amuseplay 2017-09-19 23:22:46 -10:00
Jack Andersen c6781df90a Implement listener, emitter, and -3dB pan law 2017-09-18 17:59:20 -10:00
Jack Andersen aef2b2a707 Update amuseplay for boo changes 2017-07-16 16:55:14 -10:00
Phillip Stephens 0bace131e8 Compile fixes 2017-07-02 03:14:33 -07:00
Tom M 4e7c31849d Minor fixes (#6)
* cmake: require c++14

* correctly clip samples

by using numeric_limits rather than C macros

* oops, implicit type conversion of template causes overflow

* store default sample rate in global constexpr var
2017-06-17 16:48:52 -10:00
Jack Andersen 3d56d5f0cc MIDI encoder fix 2017-03-23 19:28:05 -10:00
Phillip Stephens 54bbc7399a Use std::fabs instead of std::fabsf 2017-02-28 21:36:58 -08:00
Jack Andersen a23af16349 Add sequencer fade times 2017-02-26 19:24:58 -10:00
Jack Andersen 5c8fa2e8ab New engine event interface 2017-02-14 20:01:39 -10:00
Jack Andersen aff8880595 Better volume handling 2017-02-05 17:21:38 -10:00
Jack Andersen 2e7345f11d Add info structures for holding effect parameters 2017-01-22 21:21:50 -10:00
Jack Andersen ecd990e94e Remove VolumeLUT; SoundMacro mixing appears to be linear 2017-01-10 20:54:40 -10:00
Jack Andersen 2836e73812 Add some reserve() calls 2016-12-29 20:36:17 -10:00
Jack Andersen df167556fb Emitter API tweak 2016-12-28 19:52:28 -10:00
Phillip Stephens 72d0df7d46 Silence double->float conversion warning 2016-12-21 10:42:32 -08:00
Jack Andersen 1dfdf4c392 Update amuse-boo interface 2016-12-13 15:09:48 -10:00
Jack Andersen 9cf96ad6f9 Windows fixes 2016-12-10 15:52:42 -10:00
Jack Andersen eb948dfd63 Add bounds checking to SoundMacro execution loop 2016-12-07 18:54:45 -10:00
Phillip Stephens c886bfd7d2 FreeBSD fixes 2016-10-27 15:48:12 -08:00
Jack Andersen 3fd3f3edc5 Construct sequencer for playing back SFX groups 2016-10-08 15:39:04 -10:00
Jack Andersen 7cb7ed73ea Remove unnecessary and RVO-detrimental moves 2016-10-02 18:38:08 -10:00
Jack Andersen 2521f37408 Ensure LZO linked on OS X 2016-09-10 19:49:51 -10:00
Jack Andersen bfe6668d0c Windows fixes 2016-09-10 15:25:32 -10:00
Jack Andersen 38f24ce3e4 Minor literal value adjustment 2016-09-05 19:50:42 -10:00
Jack Andersen 218fad7541 less redundant way of handling little-endian shorts 2016-09-04 20:56:01 -10:00
Phillip Stephens af68ee61e1 Remove debug print 2016-09-02 13:05:58 -07:00
Phillip Stephens b40b2c031a Better temp file handling 2016-09-02 13:00:56 -07:00
Phillip Stephens aa32ff7e84 Add LZO support for MP2 audio groups 2016-09-02 12:52:17 -07:00
Jack Andersen 28cac7ff83 Update .clang-format 2016-08-21 14:09:24 -10:00
Jack Andersen 055e73183a Add standalone bootstrap script 2016-07-18 13:18:15 -10:00
Jack Andersen a048605011 Support for loading revised Factor5 N64 ROMs 2016-07-18 12:38:28 -10:00
Jack Andersen 260ec5bb93 Add standalone bootstrap script 2016-07-18 07:52:17 -10:00
Jack Andersen 2a2a16fd17 Typing refinements for song events 2016-07-17 11:23:29 -10:00
Jack Andersen b421412cac Add lookup table for Rogue Squadron SNG Setup mappings 2016-07-16 11:55:13 -10:00
Jack Andersen 5bae40d3c3 Handle additional corner cases for getChanProgram 2016-07-14 09:42:54 -10:00
Jack Andersen d06a5ebcfa Fix amuserender crash 2016-07-13 20:51:59 -10:00
Jack Andersen e2581cea8b Better master volume lookup 2016-07-13 20:36:12 -10:00
Jack Andersen d602fbacd3 Added master volume API for entire engine 2016-07-13 20:16:00 -10:00
Jack Andersen 2dcb9dd1c7 Working Studio implementation 2016-07-13 18:54:46 -10:00
Jack Andersen d3d5595422 Initial multiple-referencing submix refactor 2016-07-12 17:04:55 -10:00
197 changed files with 44296 additions and 81621 deletions

28
.clang-format Normal file
View File

@ -0,0 +1,28 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
UseTab: Never
---
Language: Cpp
DerivePointerAlignment: false
PointerAlignment: Left
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
IndentCaseLabels: false
AllowShortBlocksOnASingleLine: true
AlignOperands: true
AlignTrailingComments: true
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BreakConstructorInitializersBeforeComma: true
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
AllowShortFunctionsOnASingleLine: All
Cpp11BracedListStyle: true
NamespaceIndentation: None
BinPackArguments: true
BinPackParameters: true
SortIncludes: false
AccessModifierOffset: -2
ConstructorInitializerIndentWidth: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true

View File

@ -12,7 +12,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.io.github.axiodl.Amuse.AudioGroups</string>
<string>group.com.axiodl.Amuse.AudioGroups</string>
</array>
</dict>
</plist>

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;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* removeDataMenu;
AudioGroupFilePresenter* groupFilePresenter;
SamplesTableController* samplesController;
SFXTableController* sfxController;
@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;
AudioGroupFilePresenter* groupFilePresenter;
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

@ -218,7 +218,7 @@
- (void)pumpTimer:(NSTimer*)timer
{
amuseEngine->pumpEngine();
booEngine->pumpAndMixVoices();
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
@ -251,7 +251,7 @@
{
__block NSOpenPanel* panel = [NSOpenPanel openPanel];
[panel beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) {
if (result == NSFileHandlingPanelOKButton)
if (result == NSModalResponseOK)
{
[self importURL:panel.URL];
}

View File

@ -10,7 +10,7 @@
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.io.github.axiodl.Amuse.AudioGroups</string>
<string>group.com.axiodl.Amuse.AudioGroups</string>
</array>
</dict>
</plist>

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,140 +19,133 @@
- (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;
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;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
std::vector<AudioGroupToken*> m_groupTokens;
AudioGroupDataToken* m_token;
void moveURL(NSURL* oldUrl, NSURL* newUrl);
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
bool loadProj(AudioGroupFilePresenter* presenter);
bool loadPool(AudioGroupFilePresenter* presenter);
bool loadSdir(AudioGroupFilePresenter* presenter);
bool loadSamp(AudioGroupFilePresenter* presenter);
bool loadMeta(AudioGroupFilePresenter* presenter);
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;
AudioGroupDataCollection(const std::string& 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);
std::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
std::vector<AudioGroupToken*> m_groupTokens;
void enable(AudioGroupFilePresenter* presenter);
void disable(AudioGroupFilePresenter* presenter);
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);
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);
};
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;
AudioGroupCollection(NSURL* url);
void addCollection(AudioGroupFilePresenter* presenter,
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter* presenter);
bool doSearch(const std::string& str);
bool doActiveFilter();
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
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;
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;
std::vector<AudioGroupSFXToken*> m_sfxTableData;
std::vector<AudioGroupSampleToken*> m_sampleTableData;
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;
}
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
- (BOOL)addCollectionName:(std::string&&)name items:(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&&)collection;
- (BOOL)addCollectionName:(std::string&&)name
items:(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&&)collection;
- (void)update;
- (void)resetIterators;
- (void)setSearchFilter:(NSString*)str;
- (void)removeSelectedItem;
@end
#endif // __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__

View File

@ -161,13 +161,14 @@ void AudioGroupCollection::update(AudioGroupFilePresenter* presenter)
}
}
bool AudioGroupCollection::doSearch(const std::string& str)
bool AudioGroupCollection::doSearch(std::string_view str)
{
bool ret = false;
m_filterGroups.clear();
m_filterGroups.reserve(m_groups.size());
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
if (str.empty() || StrToLower(it->first).find(str) != std::string::npos)
// TODO: Heterogeneous lookup when C++20 available
if (str.empty() || StrToLower(it->first).find(str.data()) != std::string::npos)
{
m_filterGroups.push_back(it);
ret = true;
@ -239,7 +240,7 @@ void AudioGroupCollection::addSamples(std::vector<AudioGroupSampleToken*>& vecOu
}
}
AudioGroupDataCollection::AudioGroupDataCollection(const std::string& name, NSURL* proj, NSURL* pool,
AudioGroupDataCollection::AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool,
NSURL* sdir, NSURL* samp, NSURL* meta)
: m_name(name), m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_meta(meta),
m_token([[AudioGroupDataToken alloc] initWithDataCollection:this]) {}
@ -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)
@ -849,7 +850,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter)
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client
{
m_audioGroupClient = client;
m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.io.github.axiodl.Amuse.AudioGroups"];
m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.axiodl.Amuse.AudioGroups"];
if (!m_groupURL)
return nil;
[NSFileCoordinator addFilePresenter:self];

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,38 +19,32 @@
@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
error:(NSError* __nullable* __nonnull)outError
viewController:(AudioUnitViewController* __nonnull)vc;
- (void)requestAudioGroup:(AudioGroupToken* _Nonnull)group;
@end
#endif
#endif
#endif // __AMUSE_AUDIOUNIT_BACKEND_HPP__

View File

@ -112,11 +112,13 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine
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();
if (m_voiceHead)
for (boo::AudioVoice& vox : *m_voiceHead)
vox._resetSampleRate(vox.m_sampleRateIn);
if (m_submixHead)
for (boo::AudioSubmix& smx : *m_submixHead)
smx._resetOutputSampleRate();
}
void pumpAndMixVoices()
@ -281,7 +283,7 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine
/* Output buffers */
voxEngine.m_renderFrames = frameCount;
voxEngine.m_outputData = outputData;
amuseEngine.pumpEngine();
voxEngine.pumpAndMixVoices();
return noErr;
};
}

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

@ -1,109 +1,113 @@
if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER 10.10))
set(APPLE_DEV_ID "" CACHE STRING "Mac Developer ID string 'Mac Developer: John Smith (XXXXXXXXXX)'")
set(APPLE_TEAM_ID "" CACHE STRING "Team ID string provisioned within Xcode / Apple's portal")
find_library(AVFOUNDATION_LIBRARY AVFoundation)
find_library(AUDIOUNIT_LIBRARY AudioUnit)
find_library(COREAUDIOKIT_LIBRARY CoreAudioKit)
if (NOT (AUDIOUNIT_LIBRARY STREQUAL AUDIOUNIT_LIBRARY-NOTFOUND))
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}")
if (APPLE_DEV_ID AND APPLE_TEAM_ID)
find_library(AVFOUNDATION_LIBRARY AVFoundation)
find_library(AUDIOUNIT_LIBRARY AudioUnit)
find_library(COREAUDIOKIT_LIBRARY CoreAudioKit)
if (NOT (AUDIOUNIT_LIBRARY STREQUAL AUDIOUNIT_LIBRARY-NOTFOUND))
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}")
# Search for provision profile to make AudioUnit extension on OS X
unset(PROV_PROFILE)
file(GLOB PROV_FILES "$ENV{HOME}/Library/MobileDevice/Provisioning Profiles/*.provisionprofile")
foreach(FILE ${PROV_FILES})
file(STRINGS "${FILE}" NAME REGEX ${APPLE_TEAM_ID})
if(NAME)
set(PROV_PROFILE "${FILE}")
break()
# Search for provision profile to make AudioUnit extension on OS X
unset(PROV_PROFILE)
file(GLOB PROV_FILES "$ENV{HOME}/Library/MobileDevice/Provisioning Profiles/*.provisionprofile")
foreach(FILE ${PROV_FILES})
file(STRINGS "${FILE}" NAME REGEX ${APPLE_TEAM_ID})
if(NAME)
set(PROV_PROFILE "${FILE}")
break()
endif()
endforeach()
if(EXISTS "${PROV_PROFILE}")
# Extension App
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
COMMAND ibtool --errors --warnings --notices --module amuse_au --auto-activate-custom-fonts
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
)
add_executable(amuse-au MACOSX_BUNDLE AudioUnitBackend.hpp AudioUnitBackend.mm
AudioUnitViewController.hpp AudioUnitViewController.mm
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
AudioUnitViewController.nib)
set(APPLE_BUNDLE_ID "com.axiodl.Amuse.AudioUnit")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseExtension.entitlements.in
${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements)
target_link_libraries(amuse-au amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY}
${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core)
set_target_properties(amuse-au PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ExtensionInfo.plist"
BUNDLE_EXTENSION "appex" BUNDLE TRUE
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}"
LINK_FLAGS "-e _NSExtensionMain -fobjc-arc -fobjc-link-runtime -fapplication-extension")
# Containing App
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
COMMAND ibtool --errors --warnings --notices --module amuse_au_container --auto-activate-custom-fonts
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
)
add_executable(amuse-au-container MACOSX_BUNDLE AmuseContainingApp.hpp AmuseContainingApp.mm
AudioUnitBackend.hpp AudioUnitBackend.mm
AudioUnitViewController.hpp AudioUnitViewController.mm
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
AmuseContainerMainMenu.nib)
set_source_files_properties(AudioUnitBackend.mm AudioUnitViewController.mm
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} lzokay ${BOO_SYS_LIBS} logvisor athena-core)
set(APPLE_BUNDLE_ID "com.axiodl.Amuse")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainer.entitlements.in
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements)
set_target_properties(amuse-au-container PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ContainerInfo.plist"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}")
add_custom_command(TARGET amuse-au POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib"
"$<TARGET_FILE_DIR:amuse-au>/../Resources/AudioUnitViewController.nib"
COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$<TARGET_FILE_DIR:amuse-au>/../embedded.provisionprofile"
COMMAND ${CMAKE_COMMAND} -E remove_directory "$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
COMMAND ${CMAKE_COMMAND} -E copy_directory "$<TARGET_FILE_DIR:amuse-au>/../.."
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
COMMAND codesign --force --sign
${APPLE_DEV_ID} --entitlements "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
COMMAND codesign --force --sign ${APPLE_DEV_ID}
"$<TARGET_FILE_DIR:amuse-au-container>/../.."
VERBATIM
)
add_custom_command(TARGET amuse-au-container POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib"
"$<TARGET_FILE_DIR:amuse-au-container>/../Resources/AmuseContainerMainMenu.nib"
COMMAND codesign --force --sign
${APPLE_DEV_ID} "$<TARGET_FILE_DIR:amuse-au-container>/../.."
VERBATIM
)
else()
message(WARNING "Unable to find developer provisioning profile; skipping Amuse-AU")
endif()
endforeach()
if(EXISTS "${PROV_PROFILE}")
# Extension App
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
COMMAND ibtool --errors --warnings --notices --module amuse_au --auto-activate-custom-fonts
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
)
add_executable(amuse-au MACOSX_BUNDLE AudioUnitBackend.hpp AudioUnitBackend.mm
AudioUnitViewController.hpp AudioUnitViewController.mm
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
AudioUnitViewController.nib)
set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse.AudioUnit")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseExtension.entitlements.in
${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements)
target_link_libraries(amuse-au amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY}
${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core)
set_target_properties(amuse-au PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ExtensionInfo.plist"
BUNDLE_EXTENSION "appex" BUNDLE TRUE
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}"
LINK_FLAGS "-e _NSExtensionMain -fobjc-arc -fobjc-link-runtime -fapplication-extension")
# Containing App
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
COMMAND ibtool --errors --warnings --notices --module amuse_au_container --auto-activate-custom-fonts
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
)
add_executable(amuse-au-container MACOSX_BUNDLE AmuseContainingApp.hpp AmuseContainingApp.mm
AudioUnitBackend.hpp AudioUnitBackend.mm
AudioUnitViewController.hpp AudioUnitViewController.mm
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
AmuseContainerMainMenu.nib)
set_source_files_properties(AudioUnitBackend.mm AudioUnitViewController.mm
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} ${BOO_SYS_LIBS} logvisor athena-core)
set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainer.entitlements.in
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements)
set_target_properties(amuse-au-container PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ContainerInfo.plist"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}")
add_custom_command(TARGET amuse-au POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib"
"$<TARGET_FILE_DIR:amuse-au>/../Resources/AudioUnitViewController.nib"
COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$<TARGET_FILE_DIR:amuse-au>/../embedded.provisionprofile"
COMMAND ${CMAKE_COMMAND} -E remove_directory "$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
COMMAND ${CMAKE_COMMAND} -E copy_directory "$<TARGET_FILE_DIR:amuse-au>/../.."
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
COMMAND codesign --force --sign
${APPLE_DEV_ID} --entitlements "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
COMMAND codesign --force --sign ${APPLE_DEV_ID}
"$<TARGET_FILE_DIR:amuse-au-container>/../.."
VERBATIM
)
add_custom_command(TARGET amuse-au-container POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib"
"$<TARGET_FILE_DIR:amuse-au-container>/../Resources/AmuseContainerMainMenu.nib"
COMMAND codesign --force --sign
${APPLE_DEV_ID} "$<TARGET_FILE_DIR:amuse-au-container>/../.."
VERBATIM
)
else()
message(WARNING "Unable to find developer provision profile; skipping Amuse-AU")
endif()
else()
message(WARNING "APPLE_DEV_ID and/or APPLE_TEAM_ID not set; skipping Amuse-AU")
endif()
endif()

View File

@ -20,7 +20,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>io.github.axiodl.Amuse</string>
<string>com.axiodl.Amuse</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>amuse-au</string>
<key>CFBundleIdentifier</key>
<string>io.github.axiodl.Amuse.AudioUnit</string>
<string>com.axiodl.Amuse.AudioUnit</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -1,84 +1,143 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(amuse)
if(EXISTS boo)
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)
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)
endif()
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/VolumeLUT.cpp
lib/Submix.cpp
lib/EffectBase.cpp
lib/EffectReverb.cpp
lib/EffectChorus.cpp
lib/EffectDelay.cpp
lib/SurroundProfiles.cpp
lib/ContainerRegistry.cpp
lib/DSPCodec.c
lib/N64MusyXCodec.c)
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/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/SurroundProfiles.hpp
include/amuse/ContainerRegistry.hpp
include/amuse/Common.hpp
include/amuse/amuse.hpp
include/amuse/DSPCodec.h
include/amuse/N64MusyXCodec.h)
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})
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)
option(AMUSE_BUILD_EDITOR "Build Amuse with editor enabled (includes VST)" ON)
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
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 boo)
target_sources(amuse PRIVATE lib/BooBackend.cpp include/amuse/BooBackend.hpp)
target_link_libraries(amuse boo)
endif()
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(TARGET boo AND NOT WINDOWS_STORE AND NOT NX)
# AudioUnit Target (OS X only)
add_subdirectory(AudioUnit)
@ -89,13 +148,32 @@ if(TARGET boo)
# Player
add_executable(amuseplay WIN32 driver/amuseplay.cpp)
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
target_link_libraries(amuseplay amuse logvisor)
# Converter
add_executable(amuseconv driver/amuseconv.cpp)
target_link_libraries(amuseconv amuse ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
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 ${ZLIB_LIBRARIES})
target_link_libraries(amuserender amuse logvisor)
if(COMMAND add_sanitizers)
add_sanitizers(amuseplay)
add_sanitizers(amuseconv)
add_sanitizers(amuserender)
endif()
# Editor
find_package(Qt6 COMPONENTS Widgets PATHS /usr/local/opt/qt)
if (Qt6Widgets_FOUND AND AMUSE_BUILD_EDITOR)
message(STATUS "Qt6 found, amuse-gui will be built")
add_subdirectory(Editor)
else()
if (NOT AMUSE_BUILD_EDITOR)
message(STATUS "amuse-gui has been disabled and will not be built, to enable pass `-DAMUSE_BUILD_EDITOR=ON` to cmake")
else()
message(STATUS "Qt6 not found, amuse-gui will not be built")
endif()
endif()
endif()

977
Editor/ADSREditor.cpp Normal file
View File

@ -0,0 +1,977 @@
#include "ADSREditor.hpp"
#include "MainWindow.hpp"
#include <QCheckBox>
#include <QDoubleSpinBox>
#include <QMouseEvent>
#include <QPainter>
#include <QVBoxLayout>
const QColor Red = QColor(255, 127, 127);
const QColor Green = QColor(127, 255, 127);
const QColor Blue = QColor(150, 150, 255);
ADSREditor* ADSRView::getEditor() const { return qobject_cast<ADSREditor*>(parentWidget()); }
void ADSRView::loadData(ProjectModel::ADSRNode* node) { m_node = node; }
void ADSRView::unloadData() { m_node.reset(); }
ProjectModel::INode* ADSRView::currentNode() const { return m_node.get(); }
void ADSRView::paintEvent(QPaintEvent* ev) {
if (!m_node) {
return;
}
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
const amuse::ITable& table = **m_node->m_obj;
qreal adTime = 0.0;
qreal aTime = 0.0;
qreal relTime = 0.0;
qreal sustain = 0.0;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
sustain = adsr.getSustain();
relTime = adsr.getRelease();
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
const auto& adsr = static_cast<const amuse::ADSR&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
sustain = adsr.getSustain();
relTime = adsr.getRelease();
}
const qreal totalTime = adTime + 1.0 + relTime;
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 = qreal(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));
}
const size_t maxTime = size_t(std::max(adTime, relTime)) + 1;
if (m_timeTexts.size() < maxTime) {
m_timeTexts.reserve(maxTime);
for (size_t i = m_timeTexts.size(); i < maxTime; ++i) {
m_timeTexts.emplace_back();
QStaticText& text = m_timeTexts.back();
text.setText(QStringLiteral("%1.0s").arg(i));
text.setTextWidth(32.0);
text.setTextOption(QTextOption(Qt::AlignHCenter | Qt::AlignBaseline));
}
}
qreal xBase = 30.0;
const qreal xIncrement = (width() - xBase) / totalTime;
for (size_t i = 0; i <= size_t(adTime); ++i) {
const qreal thisX = i * xIncrement + xBase;
const qreal textX = thisX - 15.0;
painter.drawStaticText(QPointF(textX, height() - 16.0), m_timeTexts[i]);
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
}
xBase = (width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0;
for (size_t i = 0; i <= size_t(relTime); ++i) {
const qreal thisX = i * xIncrement + xBase;
const qreal textX = thisX - 15.0;
painter.drawStaticText(QPointF(textX, height() - 16.0), m_timeTexts[i]);
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
}
const qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain;
const auto pt0 = QPointF(30.0, height() - 16.0);
const auto pt1 = QPointF((width() - 30.0) * (aTime / totalTime) + 30.0, 0.0);
const auto pt2 = QPointF((width() - 30.0) * (adTime / totalTime) + 30.0, sustainY);
const auto pt3 = QPointF((width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0, sustainY);
const auto pt4 = QPointF(width(), height() - 16.0);
painter.setPen(QPen(Red, penWidth * 3.0));
painter.drawLine(pt0, pt1);
painter.setPen(QPen(Green, penWidth * 3.0));
painter.drawLine(pt1, pt2);
painter.setPen(QPen(Qt::white, penWidth * 3.0));
painter.drawLine(pt2, pt3);
painter.setPen(QPen(Blue, penWidth * 3.0));
painter.drawLine(pt3, pt4);
painter.setPen(Qt::NoPen);
painter.setBrush(Green);
painter.drawEllipse(pt1, 8.0, 8.0);
painter.setBrush(Qt::white);
painter.drawEllipse(pt2, 8.0, 8.0);
painter.setBrush(Blue);
painter.drawEllipse(pt3, 8.0, 8.0);
}
static qreal PointDistanceSq(const QPointF& p1, const QPointF& p2) {
const QPointF delta = p1 - p2;
return QPointF::dotProduct(delta, delta);
}
static qreal PointDistance(const QPointF& p1, const QPointF& p2) { return std::sqrt(PointDistanceSq(p1, p2)); }
void ADSRView::mousePressEvent(QMouseEvent* ev) {
if (!m_node)
return;
const amuse::ITable& table = **m_node->m_obj;
qreal adTime = 0.0;
qreal aTime = 0.0;
qreal relTime = 0.0;
qreal sustain = 0.0;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
sustain = adsr.getSustain();
relTime = adsr.getRelease();
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
const auto& adsr = static_cast<const amuse::ADSR&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
sustain = adsr.getSustain();
relTime = adsr.getRelease();
}
const qreal totalTime = adTime + 1.0 + relTime;
const qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain;
const QPointF points[] = {
QPointF((width() - 30.0) * (aTime / totalTime) + 30.0, 0.0),
QPointF((width() - 30.0) * (adTime / totalTime) + 30.0, sustainY),
QPointF((width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0, sustainY),
};
const qreal dists[] = {
PointDistance(ev->position(), points[0]),
PointDistance(ev->position(), points[1]),
PointDistance(ev->position(), points[2]),
};
int minDist = 0;
if (dists[1] < dists[minDist] + 8.0) /* pt1 overlaps pt0, so include radius in test */
minDist = 1;
if (dists[2] < dists[minDist])
minDist = 2;
if (dists[minDist] < 10.0) {
++m_cycleIdx;
m_dragPoint = minDist;
mouseMoveEvent(ev);
}
}
void ADSRView::mouseReleaseEvent(QMouseEvent* ev) { m_dragPoint = -1; }
void ADSRView::mouseMoveEvent(QMouseEvent* ev) {
const amuse::ITable& table = **m_node->m_obj;
ADSRControls* ctrls = getEditor()->m_controls;
qreal adTime = 0.0;
qreal aTime = 0.0;
qreal relTime = 0.0;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
relTime = adsr.getRelease();
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
const auto& adsr = static_cast<const amuse::ADSR&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
relTime = adsr.getRelease();
}
qreal totalTime = adTime + 1.0 + relTime;
if (m_dragPoint == 0) {
const qreal newAttack = std::max(0.0, (ev->position().x() - 30.0) / (width() - 30.0) * totalTime);
const qreal delta = newAttack - aTime;
ctrls->setAttackAndDecay(newAttack, std::max(0.0, ctrls->m_decay->value() - delta), m_cycleIdx);
} else if (m_dragPoint == 1) {
const qreal newDecay = std::max(0.0, (ev->position().x() - 30.0) * totalTime / (width() - 30.0) - aTime);
const qreal newSustain = (-ev->position().y() + (height() - 16.0)) / (height() - 16.0);
ctrls->setDecayAndSustain(newDecay, newSustain * 100.0, m_cycleIdx);
} else if (m_dragPoint == 2) {
const qreal newRelease =
std::max(0.0, (width() - 30.0) * (adTime + 1.0) / (ev->position().x() - 30.0) - (adTime + 1.0));
ctrls->setRelease(newRelease, m_cycleIdx);
ctrls->m_release->setValue(newRelease);
}
}
ADSRView::ADSRView(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_gridFont.setPointSize(8);
}
ADSRView::~ADSRView() = default;
ADSREditor* ADSRControls::getEditor() const { return qobject_cast<ADSREditor*>(parentWidget()); }
void ADSRControls::loadData() {
m_enableUpdate = false;
const amuse::ITable& table = **getEditor()->m_adsrView->m_node->m_obj;
m_attack->setDisabled(false);
m_decay->setDisabled(false);
m_sustain->setDisabled(false);
m_release->setDisabled(false);
m_dls->setDisabled(false);
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
m_attack->setValue(adsr.getAttack());
m_decay->setValue(adsr.getDecay());
m_sustain->setValue(adsr.getSustain() * 100.0);
m_release->setValue(adsr.getRelease());
m_dls->setChecked(true);
m_velToAttackLab->setVisible(true);
m_velToAttack->setVisible(true);
m_velToAttack->setValue(adsr._getVelToAttack());
m_keyToDecayLab->setVisible(true);
m_keyToDecay->setVisible(true);
m_keyToDecay->setValue(adsr._getKeyToDecay());
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
const auto& adsr = static_cast<const amuse::ADSR&>(table);
m_attack->setValue(adsr.getAttack());
m_decay->setValue(adsr.getDecay());
m_sustain->setValue(adsr.getSustain() * 100.0);
m_release->setValue(adsr.getRelease());
m_dls->setChecked(false);
m_velToAttackLab->setVisible(false);
m_velToAttack->setVisible(false);
m_keyToDecayLab->setVisible(false);
m_keyToDecay->setVisible(false);
} else {
unloadData();
}
m_enableUpdate = true;
}
void ADSRControls::unloadData() {
m_attack->setDisabled(true);
m_decay->setDisabled(true);
m_sustain->setDisabled(true);
m_release->setDisabled(true);
m_dls->setDisabled(true);
m_velToAttackLab->setVisible(false);
m_velToAttack->setVisible(false);
m_keyToDecayLab->setVisible(false);
m_keyToDecay->setVisible(false);
}
class ADSRAttackUndoCommand : public EditorUndoCommand {
double m_redoVal, m_undoVal;
bool m_undid = false;
public:
ADSRAttackUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack")), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setAttack(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
adsr.setAttack(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getAttack();
adsr.setAttack(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getAttack();
adsr.setAttack(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRAttackUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRAttack); }
};
void ADSRControls::attackChanged(double val) {
if (m_enableUpdate) {
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRAttackUndoCommand(val, view->m_node));
view->update();
}
}
class ADSRDecayUndoCommand : public EditorUndoCommand {
double m_redoVal, m_undoVal;
bool m_undid = false;
public:
ADSRDecayUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay")), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setDecay(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
adsr.setDecay(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getDecay();
adsr.setDecay(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getDecay();
adsr.setDecay(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRDecayUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRDecay); }
};
void ADSRControls::decayChanged(double val) {
if (m_enableUpdate) {
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRDecayUndoCommand(val, view->m_node));
view->update();
}
}
class ADSRSustainUndoCommand : public EditorUndoCommand {
double m_redoVal, m_undoVal;
bool m_undid = false;
public:
ADSRSustainUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Sustain")), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setSustain(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
adsr.setSustain(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getSustain();
adsr.setSustain(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getSustain();
adsr.setSustain(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRSustainUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRSustain); }
};
void ADSRControls::sustainChanged(double val) {
if (m_enableUpdate) {
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRSustainUndoCommand(val / 100.0, view->m_node));
view->update();
}
}
class ADSRAttackAndDecayUndoCommand : public EditorUndoCommand {
double m_redoAttack, m_redoDecay;
double m_undoAttack, m_undoDecay;
uint64_t m_cycleCount;
bool m_undid = false;
public:
ADSRAttackAndDecayUndoCommand(double redoAttack, double redoDecay, uint64_t cycleCount,
amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack/Decay"))
, m_redoAttack(redoAttack)
, m_redoDecay(redoDecay)
, m_cycleCount(cycleCount) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setAttack(m_undoAttack);
adsr.setDecay(m_undoDecay);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
adsr.setAttack(m_undoAttack);
adsr.setDecay(m_undoDecay);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoAttack = adsr.getAttack();
m_undoDecay = adsr.getDecay();
adsr.setAttack(m_redoAttack);
adsr.setDecay(m_redoDecay);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
m_undoAttack = adsr.getAttack();
m_undoDecay = adsr.getDecay();
adsr.setAttack(m_redoAttack);
adsr.setDecay(m_redoDecay);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
const auto* const command = static_cast<const ADSRAttackAndDecayUndoCommand*>(other);
if (other->id() == id() && m_cycleCount == command->m_cycleCount) {
m_redoAttack = command->m_redoAttack;
m_redoDecay = command->m_redoDecay;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRAttackAndDecay); }
};
void ADSRControls::setAttackAndDecay(double attack, double decay, uint64_t cycleCount) {
m_enableUpdate = false;
m_attack->setValue(attack);
m_decay->setValue(decay);
m_enableUpdate = true;
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(
new ADSRAttackAndDecayUndoCommand(m_attack->value(), m_decay->value(), cycleCount, view->m_node));
view->update();
}
class ADSRDecayAndSustainUndoCommand : public EditorUndoCommand {
double m_redoDecay, m_redoSustain;
double m_undoDecay, m_undoSustain;
uint64_t m_cycleCount;
bool m_undid = false;
public:
ADSRDecayAndSustainUndoCommand(double redoDecay, double redoSustain, uint64_t cycleCount,
amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay/Sustain"))
, m_redoDecay(redoDecay)
, m_redoSustain(redoSustain)
, m_cycleCount(cycleCount) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setDecay(m_undoDecay);
adsr.setSustain(m_undoSustain);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
adsr.setDecay(m_undoDecay);
adsr.setSustain(m_undoSustain);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoDecay = adsr.getDecay();
m_undoSustain = adsr.getSustain();
adsr.setDecay(m_redoDecay);
adsr.setSustain(m_redoSustain);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
m_undoDecay = adsr.getDecay();
m_undoSustain = adsr.getSustain();
adsr.setDecay(m_redoDecay);
adsr.setSustain(m_redoSustain);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
const auto* const command = static_cast<const ADSRDecayAndSustainUndoCommand*>(other);
if (other->id() == id() && m_cycleCount == command->m_cycleCount) {
m_redoDecay = command->m_redoDecay;
m_redoSustain = command->m_redoSustain;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRDecayAndSustain); }
};
void ADSRControls::setDecayAndSustain(double decay, double sustain, uint64_t cycleCount) {
m_enableUpdate = false;
m_decay->setValue(decay);
m_sustain->setValue(sustain);
m_enableUpdate = true;
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(
new ADSRDecayAndSustainUndoCommand(m_decay->value(), m_sustain->value() / 100.0, cycleCount, view->m_node));
view->update();
}
class ADSRReleaseUndoCommand : public EditorUndoCommand {
double m_redoVal, m_undoVal;
uint64_t m_cycleCount;
bool m_undid = false;
public:
ADSRReleaseUndoCommand(double redoVal, uint64_t cycleCount, amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Release")), m_redoVal(redoVal), m_cycleCount(cycleCount) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setRelease(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
adsr.setRelease(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getRelease();
adsr.setRelease(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getRelease();
adsr.setRelease(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
const auto* const command = static_cast<const ADSRReleaseUndoCommand*>(other);
if (other->id() == id() && m_cycleCount == command->m_cycleCount) {
m_redoVal = command->m_redoVal;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRRelease); }
};
void ADSRControls::setRelease(double release, uint64_t cycleCount) {
m_enableUpdate = false;
m_release->setValue(release);
m_enableUpdate = true;
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRReleaseUndoCommand(m_release->value(), cycleCount, view->m_node));
view->update();
}
void ADSRControls::releaseChanged(double val) {
if (m_enableUpdate) {
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRReleaseUndoCommand(val, ~0ull, view->m_node));
view->update();
}
}
template <class T>
static std::unique_ptr<T> MakeAlternateVersion(amuse::ITable& table) {
std::unique_ptr<T> ret = std::make_unique<T>();
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
ret->setAttack(adsr.getAttack());
ret->setDecay(adsr.getDecay());
ret->setSustain(adsr.getSustain());
ret->setRelease(adsr.getRelease());
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
auto& adsr = static_cast<amuse::ADSR&>(table);
ret->setAttack(adsr.getAttack());
ret->setDecay(adsr.getDecay());
ret->setSustain(adsr.getSustain());
ret->setRelease(adsr.getRelease());
}
return ret;
}
class ADSRDLSUndoCommand : public EditorUndoCommand {
bool m_redoVal;
double m_redoVelToAttack, m_redoKeyToDecay;
double m_undoVelToAttack, m_undoKeyToDecay;
bool m_undid = false;
public:
ADSRDLSUndoCommand(bool redoVal, double redoVelToAttack, double redoKeyToDecay,
amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change DLS"))
, m_redoVal(redoVal)
, m_redoVelToAttack(redoVelToAttack)
, m_redoKeyToDecay(redoKeyToDecay) {}
void undo() override {
m_undid = true;
std::unique_ptr<amuse::ITable>& table = *m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if ((table->Isa() == amuse::ITable::Type::ADSRDLS && !m_redoVal) ||
(table->Isa() == amuse::ITable::Type::ADSR && m_redoVal)) {
return;
}
std::unique_ptr<amuse::ITable> oldTable = std::move(table);
if (!m_redoVal) {
table = MakeAlternateVersion<amuse::ADSRDLS>(*oldTable);
static_cast<amuse::ADSRDLS&>(*table)._setVelToAttack(m_undoVelToAttack);
static_cast<amuse::ADSRDLS&>(*table)._setKeyToDecay(m_undoKeyToDecay);
} else {
table = MakeAlternateVersion<amuse::ADSR>(*oldTable);
}
EditorUndoCommand::undo();
}
void redo() override {
std::unique_ptr<amuse::ITable>& table = *m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if ((table->Isa() == amuse::ITable::Type::ADSRDLS && m_redoVal) ||
(table->Isa() == amuse::ITable::Type::ADSR && !m_redoVal)) {
return;
}
std::unique_ptr<amuse::ITable> oldTable = std::move(table);
if (m_redoVal) {
table = MakeAlternateVersion<amuse::ADSRDLS>(*oldTable);
static_cast<amuse::ADSRDLS&>(*table)._setVelToAttack(m_redoVelToAttack);
static_cast<amuse::ADSRDLS&>(*table)._setKeyToDecay(m_redoKeyToDecay);
} else {
if (oldTable->Isa() == amuse::ITable::Type::ADSRDLS) {
m_undoVelToAttack = static_cast<amuse::ADSRDLS&>(*oldTable)._getVelToAttack();
m_undoKeyToDecay = static_cast<amuse::ADSRDLS&>(*oldTable)._getKeyToDecay();
}
table = MakeAlternateVersion<amuse::ADSR>(*oldTable);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id()) {
const auto* const command = static_cast<const ADSRDLSUndoCommand*>(other);
m_redoVal = command->m_redoVal;
m_redoVelToAttack = command->m_redoVelToAttack;
m_redoKeyToDecay = command->m_redoKeyToDecay;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRDLS); }
};
void ADSRControls::dlsStateChanged(int state) {
if (m_enableUpdate) {
m_velToAttackLab->setVisible(state == Qt::Checked);
m_velToAttack->setVisible(state == Qt::Checked);
m_keyToDecayLab->setVisible(state == Qt::Checked);
m_keyToDecay->setVisible(state == Qt::Checked);
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(
new ADSRDLSUndoCommand(state == Qt::Checked, m_velToAttack->value(), m_keyToDecay->value(), view->m_node));
view->update();
}
}
class ADSRVelToAttackUndoCommand : public EditorUndoCommand {
double m_redoVal, m_undoVal;
bool m_undid = false;
public:
ADSRVelToAttackUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Vel To Attack")), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr._setVelToAttack(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr._getVelToAttack();
adsr._setVelToAttack(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRVelToAttackUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRVelToAttack); }
};
void ADSRControls::velToAttackChanged(double val) {
if (m_enableUpdate) {
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRVelToAttackUndoCommand(val, view->m_node));
view->update();
}
}
class ADSRKeyToDecayUndoCommand : public EditorUndoCommand {
double m_redoVal, m_undoVal;
bool m_undid = false;
public:
ADSRKeyToDecayUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Key To Decay")), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr._setKeyToDecay(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr._getKeyToDecay();
adsr._setKeyToDecay(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRKeyToDecayUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const override { return int(Id::ADSRKeyToDecay); }
};
void ADSRControls::keyToDecayChanged(double val) {
if (m_enableUpdate) {
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRKeyToDecayUndoCommand(val, view->m_node));
view->update();
}
}
ADSRControls::ADSRControls(QWidget* parent) : QFrame(parent) {
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(100);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
auto* const mainLayout = new QHBoxLayout;
auto* const leftLayout = new QGridLayout;
QPalette palette = QWidget::palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
palette.setColor(QPalette::Text, Red);
QLabel* lab = new QLabel(tr("Attack"));
lab->setPalette(palette);
leftLayout->addWidget(lab, 0, 0);
m_attack = new QDoubleSpinBox;
m_attack->setDisabled(true);
m_attack->setRange(0, 65.535);
m_attack->setDecimals(3);
m_attack->setSingleStep(0.1);
m_attack->setSuffix(tr(" sec"));
m_attack->setPalette(palette);
connect(m_attack, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::attackChanged);
leftLayout->addWidget(m_attack, 1, 0);
palette.setColor(QPalette::Text, Green);
lab = new QLabel(tr("Decay"));
lab->setPalette(palette);
leftLayout->addWidget(lab, 0, 1);
m_decay = new QDoubleSpinBox;
m_decay->setDisabled(true);
m_decay->setRange(0, 65.535);
m_decay->setDecimals(3);
m_decay->setSingleStep(0.1);
m_decay->setSuffix(tr(" sec"));
m_decay->setPalette(palette);
connect(m_decay, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::decayChanged);
leftLayout->addWidget(m_decay, 1, 1);
palette.setColor(QPalette::Text, Qt::white);
leftLayout->addWidget(new QLabel(tr("Sustain")), 0, 2);
m_sustain = new QDoubleSpinBox;
m_sustain->setDisabled(true);
m_sustain->setRange(0.0, 100.0);
m_sustain->setDecimals(3);
m_sustain->setSuffix(tr(" %"));
m_sustain->setPalette(palette);
connect(m_sustain, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::sustainChanged);
leftLayout->addWidget(m_sustain, 1, 2);
palette.setColor(QPalette::Text, Blue);
lab = new QLabel(tr("Release"));
lab->setPalette(palette);
leftLayout->addWidget(lab, 0, 3);
m_release = new QDoubleSpinBox;
m_release->setDisabled(true);
m_release->setRange(0.0, 65.535);
m_release->setDecimals(3);
m_release->setSingleStep(0.1);
m_release->setSuffix(tr(" sec"));
m_release->setPalette(palette);
connect(m_release, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::releaseChanged);
leftLayout->addWidget(m_release, 1, 3);
palette.setColor(QPalette::Text, Qt::white);
leftLayout->addWidget(new QLabel(tr("DLS")), 0, 4);
m_dls = new QCheckBox;
m_dls->setPalette(palette);
m_dls->setDisabled(true);
m_dls->setChecked(false);
connect(m_dls, qOverload<int>(&QCheckBox::stateChanged), this, &ADSRControls::dlsStateChanged);
leftLayout->addWidget(m_dls, 1, 4);
m_velToAttackLab = new QLabel(tr("Vel To Attack"));
m_velToAttackLab->setVisible(false);
leftLayout->addWidget(m_velToAttackLab, 0, 5);
m_velToAttack = new QDoubleSpinBox;
m_velToAttack->setVisible(false);
m_velToAttack->setRange(0.0, 9999.999);
m_velToAttack->setDecimals(3);
m_velToAttack->setSingleStep(1.0);
m_velToAttack->setPalette(palette);
connect(m_velToAttack, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::velToAttackChanged);
leftLayout->addWidget(m_velToAttack, 1, 5);
m_keyToDecayLab = new QLabel(tr("Key To Decay"));
m_keyToDecayLab->setVisible(false);
leftLayout->addWidget(m_keyToDecayLab, 0, 6);
m_keyToDecay = new QDoubleSpinBox;
m_keyToDecay->setVisible(false);
m_keyToDecay->setRange(0.0, 9999.999);
m_keyToDecay->setDecimals(3);
m_keyToDecay->setSingleStep(1.0);
m_keyToDecay->setPalette(palette);
connect(m_keyToDecay, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::keyToDecayChanged);
leftLayout->addWidget(m_keyToDecay, 1, 6);
leftLayout->setColumnMinimumWidth(0, 75);
leftLayout->setColumnStretch(0, 1);
leftLayout->setColumnMinimumWidth(1, 75);
leftLayout->setColumnStretch(1, 1);
leftLayout->setColumnMinimumWidth(2, 75);
leftLayout->setColumnStretch(2, 1);
leftLayout->setColumnMinimumWidth(3, 75);
leftLayout->setColumnStretch(3, 1);
leftLayout->setColumnMinimumWidth(4, 50);
leftLayout->setColumnStretch(4, 0);
leftLayout->setColumnMinimumWidth(5, 75);
leftLayout->setColumnStretch(5, 1);
leftLayout->setColumnMinimumWidth(6, 75);
leftLayout->setColumnStretch(6, 1);
leftLayout->setRowMinimumHeight(0, 22);
leftLayout->setRowMinimumHeight(1, 37);
leftLayout->setContentsMargins(10, 6, 0, 14);
mainLayout->addLayout(leftLayout);
mainLayout->addStretch();
setLayout(mainLayout);
}
ADSRControls::~ADSRControls() = default;
bool ADSREditor::loadData(ProjectModel::ADSRNode* node) {
m_adsrView->loadData(node);
m_controls->loadData();
return true;
}
void ADSREditor::unloadData() {
m_adsrView->unloadData();
m_controls->unloadData();
}
ProjectModel::INode* ADSREditor::currentNode() const { return m_adsrView->currentNode(); }
ADSREditor::ADSREditor(QWidget* parent) : EditorWidget(parent), m_adsrView(new ADSRView), m_controls(new ADSRControls) {
auto* const layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_adsrView);
layout->addWidget(m_controls);
setLayout(layout);
}
ADSREditor::~ADSREditor() = default;

95
Editor/ADSREditor.hpp Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include <array>
#include <cstdint>
#include <vector>
#include <QFrame>
#include <QStaticText>
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <amuse/Common.hpp>
class ADSREditor;
class QCheckBox;
class QDoubleSpinBox;
class QLabel;
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);
public:
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);
};
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);
~ADSREditor() override;
bool loadData(ProjectModel::ADSRNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
};

154
Editor/CMakeLists.txt Normal file
View File

@ -0,0 +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(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)
target_sources(amuse-gui PRIVATE
platforms/win/amuse-gui.rc
platforms/win/amuse-gui.manifest
)
elseif(APPLE)
target_sources(amuse-gui PRIVATE
MacOSExtras.mm
platforms/mac/mainicon.icns
)
set_source_files_properties(platforms/mac/mainicon.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources
)
endif()
add_subdirectory(platforms/freedesktop)
declare_qticon_target()
target_sources(amuse-gui PRIVATE mainicon_qt.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"
)
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}
)

91
Editor/Common.cpp Normal file
View File

@ -0,0 +1,91 @@
#include "Common.hpp"
#include "MainWindow.hpp"
#include <QDir>
#include <QMessageBox>
#include <QObject>
#include <QProcess>
#include <QTransform>
std::string QStringToUTF8(const QString& str) {
return str.toUtf8().toStdString();
}
QString UTF8ToQString(const std::string& str) {
return QString::fromStdString(str);
}
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;
}
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;
}
}
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);
#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 QStringList browserArgs = QStringList() << QStringLiteral("%1").arg(QFileInfo(folder).path());
browserProc.startDetached(QStringLiteral("xdg-open"), browserArgs);
#endif
}
QString ShowInGraphicalShellString() {
#if defined(Q_OS_WIN)
return MainWindow::tr("Show in Explorer");
#elif defined(Q_OS_MAC)
return MainWindow::tr("Show in Finder");
#else
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;
}

50
Editor/Common.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include <functional>
#include <QMessageBox>
#include <QString>
#include <boo/System.hpp>
class MainWindow;
extern MainWindow* g_MainWindow;
class QDir;
class QRectF;
class QTransform;
class UIMessenger : public QObject {
Q_OBJECT
public:
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);
};
std::string QStringToUTF8(const QString& str);
QString UTF8ToQString(const std::string& str);
bool MkPath(const QString& path, UIMessenger& messenger);
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger);
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn);
QString ShowInGraphicalShellString();
/* Used for generating transform matrices to map SVG coordinate space */
QTransform RectToRect(const QRectF& from, const QRectF& to);

303
Editor/CurveEditor.cpp Normal file
View File

@ -0,0 +1,303 @@
#include "CurveEditor.hpp"
#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:
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());
}
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());
}
if (m_undid) {
EditorUndoCommand::redo();
}
}
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;
}
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()); }
void CurveView::loadData(ProjectModel::CurveNode* node) { m_node = node; }
void CurveView::unloadData() { m_node.reset(); }
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;
}
auto& curve = static_cast<amuse::Curve&>(table);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
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);
}
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;
}
}
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 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);
}
CurveControls::~CurveControls() = default;
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(); }
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);
}
CurveEditor::~CurveEditor() = default;

77
Editor/CurveEditor.hpp Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include <array>
#include <QFrame>
#include <QJSEngine>
#include <QStaticText>
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <amuse/Common.hpp>
class CurveEditor;
class QLabel;
class QLineEdit;
class QPushButton;
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;
public:
explicit CurveControls(QWidget* parent = Q_NULLPTR);
~CurveControls() override;
void loadData();
void unloadData();
void resizeEvent(QResizeEvent* ev) override;
public slots:
void exprCommit();
};
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);
~CurveEditor() override;
bool loadData(ProjectModel::CurveNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
};

283
Editor/EditorWidget.cpp Normal file
View File

@ -0,0 +1,283 @@
#include "EditorWidget.hpp"
#include "MainWindow.hpp"
#include <QStandardItemModel>
#include <QHBoxLayout>
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 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, qOverload<int>(&QSlider::valueChanged), this, &FieldDoubleSlider::doValueChanged);
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);
}
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
: 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);
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);
}
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);
}
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) {
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);
return;
}
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getPageObjectProxy());
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
}
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();
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);
}
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);
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);
}
static QIcon ListingDeleteIcon;
static QIcon 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);
}
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);
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, &QAction::triggered, this, &BaseObjectDelegate::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, &QAction::triggered, this, &BaseObjectDelegate::doFindUsages);
menu->addAction(findUsagesAction);
menu->popup(ev->globalPosition().toPoint());
}
}
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::doFindUsages() {
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->findUsages(node);
}

256
Editor/EditorWidget.hpp Normal file
View File

@ -0,0 +1,256 @@
#pragma once
#include <QAction>
#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"
#include <amuse/Common.hpp>
class EditorWidget : public QWidget {
Q_OBJECT
public:
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() {}
};
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
};
public:
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
public:
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR) : QSpinBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
};
class FieldSlider : public QWidget {
Q_OBJECT
QSlider m_slider;
QLabel m_value;
public:
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
/* 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); }
private slots:
void doValueChanged(int value);
signals:
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;
public:
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
/* Don't scroll */
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
double value() const;
void setValue(double value);
void setRange(double min, double max);
private slots:
void doValueChanged(int value);
signals:
void valueChanged(double value);
};
class FieldComboBox : public QComboBox {
Q_OBJECT
public:
explicit FieldComboBox(QWidget* parent = Q_NULLPTR) : QComboBox(parent) {}
/* 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;
public:
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);
public slots:
void openCurrent();
signals:
void currentIndexChanged(int);
};
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);
~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);
public slots:
void openCurrent();
signals:
void currentIndexChanged(int);
};
template <class T>
class EditorFieldNode : public T {
bool m_deferPopupOpen = true;
public:
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 {
public:
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;
public:
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
QAction* addAction() { return &m_addAction; }
QAction* removeAction() { return &m_removeAction; }
};
class ListingDeleteButton : public QPushButton {
Q_OBJECT
public:
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
};
class ContextMenu : public QMenu {
public:
void hideEvent(QHideEvent* ev) override {
QMenu::hideEvent(ev);
deleteLater();
}
};
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) override;
private slots:
void doOpenEditor();
void doFindUsages();
};

223
Editor/KeyboardWidget.cpp Normal file
View File

@ -0,0 +1,223 @@
#include "KeyboardWidget.hpp"
#include <QApplication>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QScrollArea>
#include <QScrollBar>
#include <QSvgRenderer>
#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 std::array<QString, 5> SharpKeyNames{
QStringLiteral("Cs"), QStringLiteral("Ds"), QStringLiteral("Fs"), QStringLiteral("Gs"), QStringLiteral("As"),
};
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 std::array<int, 7> NaturalKeyNumbers{
0, 2, 4, 5, 7, 9, 11,
};
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 (size_t i = 0; i < NaturalKeyNames.size(); ++i) {
const auto& naturalKeyName = NaturalKeyNames[i];
if (renderer()->elementExists(naturalKeyName)) {
m_natural[i] = renderer()->transformForElement(naturalKeyName).mapRect(renderer()->boundsOnElement(naturalKeyName));
}
}
for (size_t i = 0; i < SharpKeyNames.size(); ++i) {
const auto& sharpKeyName = SharpKeyNames[i];
if (renderer()->elementExists(sharpKeyName)) {
m_sharp[i] = renderer()->transformForElement(sharpKeyName).mapRect(renderer()->boundsOnElement(sharpKeyName));
}
}
/* The parent keyboard manages all mouse events */
setAttribute(Qt::WA_TransparentForMouseEvents);
}
int KeyboardOctave::getKey(const QPoint& localPos) const {
const QPointF localPoint = m_widgetToSvg.map(localPos);
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 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::_pressOnKey(int octave, int key) {
_moveOnKey(octave, key);
m_holding = true;
_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::mousePressEvent(QMouseEvent* event) {
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_pressOnKey(ok.first, ok.second);
}
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event) {
_stopKey();
m_holding = false;
}
void KeyboardWidget::enterEvent(QEnterEvent* event) {
if (m_statusFocus)
m_statusFocus->enter();
}
void KeyboardWidget::leaveEvent(QEvent* event) {
if (m_statusFocus)
m_statusFocus->exit();
}
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::showEvent(QShowEvent* event) {
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
/* Scroll to C1 */
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
}
}
KeyboardSlider::KeyboardSlider(QWidget* parent) : QSlider(parent) {}
KeyboardSlider::~KeyboardSlider() = default;
void KeyboardSlider::enterEvent(QEnterEvent* event) {
if (m_statusFocus)
m_statusFocus->enter();
}
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);
}
}
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);
}

107
Editor/KeyboardWidget.hpp Normal file
View File

@ -0,0 +1,107 @@
#pragma once
#include <array>
#include <QSlider>
#include <QString>
#include <QSvgWidget>
#include <QWheelEvent>
#include <QWidget>
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;
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) override;
};
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);
public:
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
~KeyboardWidget() override;
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();
};
class KeyboardSlider : public QSlider {
Q_OBJECT
protected:
StatusBarFocus* m_statusFocus = nullptr;
virtual QString stringOfValue(int value) const = 0;
public:
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 override;
public:
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
};
class ModulationSlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const override;
public:
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
};
class PitchSlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const override;
public:
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* ev) override;
void wheelEvent(QWheelEvent* ev) override { ev->ignore(); }
};

530
Editor/KeymapEditor.cpp Normal file
View File

@ -0,0 +1,530 @@
#include "KeymapEditor.hpp"
#include <array>
#include <QCheckBox>
#include <QPainter>
#include <QScrollArea>
#include <QScrollBar>
#include <QSpinBox>
#include <QVBoxLayout>
#include "Common.hpp"
#include "KeyboardWidget.hpp"
#include "MainWindow.hpp"
constexpr std::array<int, 10> HueTable{
0, 30, 60, 80, 120, 170, 200, 240, 280, 320,
};
constexpr std::array<int, 13> SaturationTable{
255, 255, 255, 255, 255, 127, 127, 127, 127, 127, 63, 63, 63,
};
constexpr std::array<int, 13> ValueTable{
240, 200, 160, 120, 80, 240, 200, 160, 120, 80, 240, 200, 160,
};
constexpr std::array<QPoint, 12> PointTable{{
{21, 180},
{41, 104},
{61, 180},
{86, 104},
{101, 180},
{141, 180},
{156, 104},
{181, 180},
{201, 104},
{221, 180},
{246, 104},
{261, 180},
}};
constexpr std::array<int, 12> RadiusTable{
14, 10, 14, 10, 14, 14, 10, 14, 10, 14, 10, 14,
};
constexpr std::array<Qt::GlobalColor, 12> PenColorTable{
Qt::black, Qt::white, Qt::black, Qt::white, Qt::black, Qt::black,
Qt::white, Qt::black, Qt::white, Qt::black, Qt::white, Qt::black,
};
constexpr std::array<Qt::GlobalColor, 12> NeutralColorTable{
Qt::white, Qt::black, Qt::white, Qt::black, Qt::white, Qt::white,
Qt::black, Qt::white, Qt::black, Qt::white, Qt::black, Qt::white,
};
PaintButton::PaintButton(QWidget* parent) : QPushButton(parent) {
setIcon(QIcon(QStringLiteral(":/icons/IconPaintbrush.svg")));
setIconSize(QSize(26, 26));
setFixedSize(46, 46);
setToolTip(tr("Activate brush to apply values to keys"));
}
KeymapEditor* KeymapView::getEditor() const {
return qobject_cast<KeymapEditor*>(parentWidget()->parentWidget()->parentWidget());
}
void KeymapView::loadData(ProjectModel::KeymapNode* node) { m_node = node; }
void KeymapView::unloadData() {
m_node.reset();
m_keyPalettes.fill(-1);
}
ProjectModel::INode* KeymapView::currentNode() const { return m_node.get(); }
void KeymapView::drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const PaintPalette& keyPalette,
int o, int k) const {
const int keyIdx = o * 12 + k;
const int keyPalIdx = m_keyPalettes[keyIdx];
painter.setPen(QPen(PenColorTable[k], penWidth));
painter.setBrush(keyPalIdx < 0 ? NeutralColorTable[k] : keyPalette[keyPalIdx]);
painter.drawEllipse(PointTable[k] + octaveRect.topLeft(), RadiusTable[k], RadiusTable[k]);
painter.setTransform(
QTransform()
.translate(PointTable[k].x() + octaveRect.left() - 13, PointTable[k].y() + octaveRect.top() - 20)
.rotate(-90.0));
painter.drawStaticText(QPointF{}, m_keyTexts[keyIdx]);
painter.setTransform(QTransform());
}
void KeymapView::paintEvent(QPaintEvent* ev) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
const auto& keyPalette = getEditor()->m_paintPalette;
const qreal deviceRatio = devicePixelRatioF();
const qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
painter.setFont(m_keyFont);
const int kbY = height() / 2 - 100;
for (int o = 0; o < 10; ++o) {
const QRect thisRect(o * 280, kbY, 280, 200);
if (ev->rect().intersects(thisRect)) {
m_octaveRenderer.render(&painter, thisRect);
for (int k = 0; k < 12; ++k) {
drawKey(painter, thisRect, penWidth, keyPalette, o, k);
}
}
}
const QRect thisRect(2800, kbY, 202, 200);
if (ev->rect().intersects(thisRect)) {
m_lastOctaveRenderer.render(&painter, thisRect);
for (int k = 0; k < 8; ++k) {
drawKey(painter, thisRect, penWidth, keyPalette, 10, k);
}
}
}
void KeymapView::mousePressEvent(QMouseEvent* ev) { mouseMoveEvent(ev); }
int KeymapView::getKey(const QPoint& localPos) const {
const QPointF localPoint = m_widgetToSvg.map(localPos);
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 KeymapView::mouseMoveEvent(QMouseEvent* ev) {
const int octave = ev->position().x() / 280;
const int key = getKey(ev->pos() - QPoint(octave * 280, height() / 2 - 100));
if (octave >= 0 && key >= 0) {
getEditor()->touchKey(octave * 12 + key, ev->modifiers() & Qt::ShiftModifier);
}
}
void KeymapView::wheelEvent(QWheelEvent* event) {
if (const QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
/* Send wheel event directly to the scroll bar */
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
}
}
KeymapView::KeymapView(QWidget* parent)
: QWidget(parent)
, m_octaveRenderer(QStringLiteral(":/bg/keyboard.svg"))
, m_lastOctaveRenderer(QStringLiteral(":/bg/keyboard_last.svg")) {
setMinimumWidth(3002);
size_t k = 0;
for (size_t i = 0; i < 11; ++i) {
for (size_t j = 0; j < KeyStrings.size() && k < m_keyTexts.size(); ++j) {
m_keyTexts[k++].setText(QStringLiteral("%1%2").arg(KeyStrings[j]).arg(i - 1));
}
}
m_keyFont.setPointSize(12);
m_keyPalettes.fill(-1);
for (size_t i = 0; i < m_natural.size(); ++i) {
const auto& naturalKeyName = NaturalKeyNames[i];
if (m_octaveRenderer.elementExists(naturalKeyName)) {
m_natural[i] =
m_octaveRenderer.transformForElement(naturalKeyName).mapRect(m_octaveRenderer.boundsOnElement(naturalKeyName));
}
}
for (size_t i = 0; i < m_sharp.size(); ++i) {
const auto& sharpKeyName = SharpKeyNames[i];
if (m_octaveRenderer.elementExists(sharpKeyName)) {
m_sharp[i] =
m_octaveRenderer.transformForElement(sharpKeyName).mapRect(m_octaveRenderer.boundsOnElement(sharpKeyName));
}
}
m_widgetToSvg = RectToRect(QRect(0, 0, 280, 200), m_octaveRenderer.viewBoxF());
}
KeymapView::~KeymapView() = default;
KeymapEditor* KeymapControls::getEditor() const { return qobject_cast<KeymapEditor*>(parentWidget()); }
void KeymapControls::setPaintIdx(int idx) {
QPalette palette = m_paintButton->palette();
if (idx < 0) {
palette.setColor(QPalette::Window, QWidget::palette().color(QPalette::Window));
palette.setColor(QPalette::Button, QWidget::palette().color(QPalette::Button));
} else {
const auto& keyPalette = getEditor()->m_paintPalette;
palette.setColor(QPalette::Window, keyPalette[idx]);
palette.setColor(QPalette::Button, keyPalette[idx].darker(300));
}
m_paintButton->setPalette(palette);
}
void KeymapControls::setKeymap(const amuse::Keymap& km) {
m_enableUpdate = false;
const int idx = m_macro->collection()->indexOfId(km.macro.id);
m_macro->setCurrentIndex(idx + 1);
m_transpose->setValue(km.transpose);
if (km.pan == -128) {
m_pan->setDisabled(true);
m_pan->setValue(true);
m_surround->setChecked(true);
} else {
m_pan->setEnabled(true);
m_pan->setValue(km.pan);
m_surround->setChecked(false);
}
m_prioOffset->setValue(km.prioOffset);
m_enableUpdate = true;
}
void KeymapControls::loadData(ProjectModel::KeymapNode* node) {
m_enableUpdate = false;
m_macro->setCollection(
g_MainWindow->projectModel()->getGroupNode(node)->getCollectionOfType(ProjectModel::INode::Type::SoundMacro));
m_macro->setDisabled(false);
m_transpose->setDisabled(false);
m_transpose->setValue(0);
m_pan->setDisabled(false);
m_pan->setValue(64);
m_surround->setDisabled(false);
m_surround->setChecked(false);
m_prioOffset->setDisabled(false);
m_prioOffset->setValue(0);
m_paintButton->setDisabled(false);
m_paintButton->setPalette(palette());
m_enableUpdate = true;
}
void KeymapControls::unloadData() {
m_enableUpdate = false;
m_macro->setCollection(nullptr);
m_macro->setDisabled(true);
m_transpose->setDisabled(true);
m_pan->setDisabled(true);
m_surround->setDisabled(true);
m_prioOffset->setDisabled(true);
m_paintButton->setDisabled(true);
m_paintButton->setPalette(palette());
m_enableUpdate = true;
}
void KeymapControls::controlChanged() {
if (m_enableUpdate) {
amuse::Keymap km;
km.macro.id = m_macro->currentIndex() == 0 ? amuse::SoundMacroId{}
: m_macro->collection()->idOfIndex(m_macro->currentIndex() - 1);
km.transpose = int8_t(m_transpose->value());
km.pan = int8_t(m_pan->value());
if (m_surround->isChecked()) {
km.pan = -128;
m_pan->setDisabled(true);
} else {
m_pan->setEnabled(true);
}
km.prioOffset = int8_t(m_prioOffset->value());
getEditor()->touchControl(km);
}
}
void KeymapControls::paintButtonPressed() {
KeymapEditor* editor = getEditor();
if (!editor->m_inPaint) {
editor->setCursor(QCursor(QPixmap(QStringLiteral(":/icons/IconPaintbrush.svg")), 2, 30));
editor->m_inPaint = true;
m_paintButton->setDown(true);
} else {
editor->unsetCursor();
editor->m_inPaint = false;
m_paintButton->setDown(false);
}
}
KeymapControls::KeymapControls(QWidget* parent) : QFrame(parent) {
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(100);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
QPalette palette = QWidget::palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
QHBoxLayout* mainLayout = new QHBoxLayout;
QGridLayout* leftLayout = new QGridLayout;
leftLayout->addWidget(new QLabel(tr("SoundMacro")), 0, 0);
m_macro = new FieldProjectNode;
m_macro->setDisabled(true);
connect(m_macro, qOverload<int>(&FieldProjectNode::currentIndexChanged), this, &KeymapControls::controlChanged);
leftLayout->addWidget(m_macro, 1, 0);
leftLayout->addWidget(new QLabel(tr("Transpose")), 0, 1);
m_transpose = new QSpinBox;
m_transpose->setPalette(palette);
m_transpose->setDisabled(true);
m_transpose->setRange(-128, 127);
m_transpose->setToolTip(tr("Offset resulting MIDI note"));
connect(m_transpose, qOverload<int>(&QSpinBox::valueChanged), this, &KeymapControls::controlChanged);
leftLayout->addWidget(m_transpose, 1, 1);
leftLayout->addWidget(new QLabel(tr("Pan")), 0, 2);
m_pan = new QSpinBox;
m_pan->setPalette(palette);
m_pan->setDisabled(true);
m_pan->setRange(-127, 127);
m_pan->setToolTip(tr("Set initial pan"));
connect(m_pan, qOverload<int>(&QSpinBox::valueChanged), this, &KeymapControls::controlChanged);
leftLayout->addWidget(m_pan, 1, 2);
leftLayout->addWidget(new QLabel(tr("Surround")), 0, 3);
m_surround = new QCheckBox;
m_surround->setPalette(palette);
m_surround->setDisabled(true);
m_surround->setToolTip(tr("Initially play through surround channels"));
connect(m_surround, qOverload<int>(&QCheckBox::stateChanged), this, &KeymapControls::controlChanged);
leftLayout->addWidget(m_surround, 1, 3);
leftLayout->addWidget(new QLabel(tr("Prio Offset")), 0, 4);
m_prioOffset = new QSpinBox;
m_prioOffset->setPalette(palette);
m_prioOffset->setDisabled(true);
m_prioOffset->setRange(-128, 127);
m_prioOffset->setToolTip(tr("Offset resulting priority"));
connect(m_prioOffset, qOverload<int>(&QSpinBox::valueChanged), this, &KeymapControls::controlChanged);
leftLayout->addWidget(m_prioOffset, 1, 4);
leftLayout->setColumnMinimumWidth(0, 200);
leftLayout->setColumnMinimumWidth(1, 75);
leftLayout->setColumnMinimumWidth(2, 75);
leftLayout->setColumnMinimumWidth(3, 50);
leftLayout->setColumnMinimumWidth(4, 75);
leftLayout->setRowMinimumHeight(0, 22);
leftLayout->setRowMinimumHeight(1, 37);
leftLayout->setContentsMargins(10, 6, 0, 14);
QVBoxLayout* rightLayout = new QVBoxLayout;
m_paintButton = new PaintButton;
m_paintButton->setDisabled(true);
connect(m_paintButton, &PaintButton::pressed, this, &KeymapControls::paintButtonPressed);
rightLayout->addWidget(m_paintButton);
rightLayout->setContentsMargins(0, 0, 10, 0);
mainLayout->addLayout(leftLayout);
mainLayout->addStretch(1);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
}
KeymapControls::~KeymapControls() = default;
void KeymapEditor::_touch() {
if (m_controlKeymap.macro.id == 0xffff)
m_controls->setPaintIdx(-1);
else
m_controls->setPaintIdx(getConfigIdx(m_controlKeymap.configKey()));
}
void KeymapEditor::touchKey(int key, bool bulk) {
if (m_inPaint) {
if (bulk) {
uint64_t refKey = (*m_kmView->m_node->m_obj)[key].configKey();
for (int i = 0; i < 128; ++i) {
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[i];
if (km.configKey() != refKey)
continue;
if (km.macro.id != 0xffff)
deallocateConfigIdx(km.configKey());
km = m_controlKeymap;
if (km.macro.id == 0xffff)
m_kmView->m_keyPalettes[i] = -1;
else
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
}
} else {
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
if (km.macro.id != 0xffff)
deallocateConfigIdx(km.configKey());
km = m_controlKeymap;
if (km.macro.id == 0xffff)
m_kmView->m_keyPalettes[key] = -1;
else
m_kmView->m_keyPalettes[key] = allocateConfigIdx(km.configKey());
}
m_kmView->update();
} else {
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
m_controlKeymap = km;
m_controls->setKeymap(km);
_touch();
}
}
void KeymapEditor::touchControl(const amuse::Keymap& km) {
m_controlKeymap = km;
_touch();
}
int KeymapEditor::allocateConfigIdx(uint64_t key) {
auto search = m_configToIdx.find(key);
if (search != m_configToIdx.end()) {
++search->second.second;
return search->second.first;
}
for (size_t i = 0; i < m_idxBitmap.size(); ++i)
if (!m_idxBitmap[i]) {
m_configToIdx.insert_or_assign(key, std::make_pair(int(i), 1));
m_idxBitmap.set(i);
return int(i);
}
Q_UNREACHABLE();
}
void KeymapEditor::deallocateConfigIdx(uint64_t key) {
auto search = m_configToIdx.find(key);
if (search != m_configToIdx.end()) {
--search->second.second;
if (search->second.second == 0) {
m_idxBitmap.reset(search->second.first);
m_configToIdx.erase(search);
}
return;
}
Q_UNREACHABLE();
}
int KeymapEditor::getConfigIdx(uint64_t key) const {
const auto search = m_configToIdx.find(key);
if (search != m_configToIdx.end()) {
return search->second.first;
}
for (size_t i = 0; i < m_idxBitmap.size(); ++i) {
if (!m_idxBitmap[i]) {
return int(i);
}
}
Q_UNREACHABLE();
}
bool KeymapEditor::loadData(ProjectModel::KeymapNode* node) {
if (m_kmView->m_node.get() != node) {
m_configToIdx.clear();
m_idxBitmap.reset();
for (size_t i = 0; i < m_kmView->m_keyPalettes.size(); ++i) {
const amuse::Keymap& km = (*node->m_obj)[i];
if (km.macro.id == 0xffff) {
m_kmView->m_keyPalettes[i] = -1;
} else {
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
}
}
m_controlKeymap = amuse::Keymap();
m_kmView->loadData(node);
m_controls->loadData(node);
}
m_inPaint = false;
m_controls->m_paintButton->setDown(false);
unsetCursor();
return true;
}
void KeymapEditor::unloadData() {
m_configToIdx.clear();
m_idxBitmap.reset();
m_kmView->unloadData();
m_controls->unloadData();
m_inPaint = false;
m_controls->m_paintButton->setDown(false);
unsetCursor();
}
ProjectModel::INode* KeymapEditor::currentNode() const { return m_kmView->currentNode(); }
void KeymapEditor::keyPressEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Escape) {
if (m_inPaint) {
m_inPaint = false;
m_controls->m_paintButton->setDown(false);
unsetCursor();
}
}
}
KeymapEditor::KeymapEditor(QWidget* parent)
: EditorWidget(parent), m_scrollArea(new QScrollArea), m_kmView(new KeymapView), m_controls(new KeymapControls) {
size_t k = 0;
for (size_t i = 0; i < ValueTable.size(); ++i) {
for (size_t j = 0; j < HueTable.size() && k < m_paintPalette.size(); ++j) {
m_paintPalette[k++].setHsv(HueTable[j], SaturationTable[i], ValueTable[i]);
}
}
m_scrollArea->setWidget(m_kmView);
m_scrollArea->setWidgetResizable(true);
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_scrollArea);
layout->addWidget(m_controls);
setLayout(layout);
}
KeymapEditor::~KeymapEditor() = default;

122
Editor/KeymapEditor.hpp Normal file
View File

@ -0,0 +1,122 @@
#pragma once
#include <array>
#include <bitset>
#include <cstdint>
#include <unordered_map>
#include <QFrame>
#include <QPushButton>
#include <QStaticText>
#include <QSvgRenderer>
#include "EditorWidget.hpp"
#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
public:
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(); }
};
using PaintPalette = std::array<QColor, 129>;
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);
public:
explicit KeymapControls(QWidget* parent = Q_NULLPTR);
~KeymapControls() override;
void loadData(ProjectModel::KeymapNode* node);
void unloadData();
public slots:
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;
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);
~KeymapEditor() override;
bool loadData(ProjectModel::KeymapNode* node);
void unloadData() override;
ProjectModel::INode* currentNode() const override;
void keyPressEvent(QKeyEvent* event) override;
};

636
Editor/LayersEditor.cpp Normal file
View File

@ -0,0 +1,636 @@
#include "LayersEditor.hpp"
#include <algorithm>
#include <QMimeData>
#include <QScrollBar>
#include <QVBoxLayout>
#include "MainWindow.hpp"
class LayerDataChangeUndoCommand : public EditorUndoCommand {
QModelIndex m_index;
int m_undoVal, m_redoVal;
bool m_undid = false;
public:
explicit LayerDataChangeUndoCommand(ProjectModel::LayersNode* node, const QString& text, QModelIndex index,
int redoVal)
: EditorUndoCommand(node, text), m_index(index), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
amuse::LayerMapping& layer = (*static_cast<ProjectModel::LayersNode*>(m_node.get())->m_obj)[m_index.row()];
switch (m_index.column()) {
case 0:
layer.macro.id = m_undoVal;
break;
case 1:
layer.keyLo = m_undoVal;
break;
case 2:
layer.keyHi = m_undoVal;
break;
case 3:
layer.transpose = m_undoVal;
break;
case 4:
layer.volume = m_undoVal;
break;
case 5:
layer.prioOffset = m_undoVal;
break;
case 6:
layer.span = m_undoVal;
break;
case 7:
layer.pan = m_undoVal;
break;
default:
break;
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::LayerMapping& layer = (*static_cast<ProjectModel::LayersNode*>(m_node.get())->m_obj)[m_index.row()];
switch (m_index.column()) {
case 0:
m_undoVal = layer.macro.id.id;
layer.macro.id = m_redoVal;
break;
case 1:
m_undoVal = layer.keyLo;
layer.keyLo = m_redoVal;
break;
case 2:
m_undoVal = layer.keyHi;
layer.keyHi = m_redoVal;
break;
case 3:
m_undoVal = layer.transpose;
layer.transpose = m_redoVal;
break;
case 4:
m_undoVal = layer.volume;
layer.volume = m_redoVal;
break;
case 5:
m_undoVal = layer.prioOffset;
layer.prioOffset = m_redoVal;
break;
case 6:
m_undoVal = layer.span;
layer.span = m_redoVal;
break;
case 7:
m_undoVal = layer.pan;
layer.pan = m_redoVal;
break;
default:
break;
}
if (m_undid)
EditorUndoCommand::redo();
}
};
SoundMacroDelegate::SoundMacroDelegate(QObject* parent) : BaseObjectDelegate(parent) {}
SoundMacroDelegate::~SoundMacroDelegate() = default;
ProjectModel::INode* SoundMacroDelegate::getNode(const QAbstractItemModel* __model, const QModelIndex& index) const {
const LayersModel* model = static_cast<const LayersModel*>(__model);
const amuse::LayerMapping& layer = (*model->m_node->m_obj)[index.row()];
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
return smColl->nodeOfId(layer.macro.id);
}
QWidget* SoundMacroDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option,
const QModelIndex& index) const {
const LayersModel* model = static_cast<const LayersModel*>(index.model());
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
EditorFieldProjectNode* cb =
new EditorFieldProjectNode(group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro), parent);
connect(cb, qOverload<int>(&EditorFieldProjectNode::currentIndexChanged), this, &SoundMacroDelegate::smIndexChanged);
return cb;
}
void SoundMacroDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
const LayersModel* model = static_cast<const LayersModel*>(index.model());
const amuse::LayerMapping& layer = (*model->m_node->m_obj)[index.row()];
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
static_cast<EditorFieldProjectNode*>(editor)->setCurrentIndex(smColl->indexOfId(layer.macro.id) + 1);
if (static_cast<EditorFieldProjectNode*>(editor)->shouldPopupOpen())
QApplication::postEvent(editor, new QEvent(QEvent::User));
}
void SoundMacroDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, const QModelIndex& index) const {
const LayersModel* model = static_cast<const LayersModel*>(m);
amuse::LayerMapping& layer = (*model->m_node->m_obj)[index.row()];
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
int idx = static_cast<EditorFieldProjectNode*>(editor)->currentIndex();
amuse::SoundMacroId id;
if (idx != 0)
id = smColl->idOfIndex(idx - 1);
if (layer.macro.id == id)
return;
g_MainWindow->pushUndoCommand(new LayerDataChangeUndoCommand(
model->m_node.get(), tr("Change %1").arg(m->headerData(0, Qt::Horizontal).toString()), index, id.id));
emit m->dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
}
void SoundMacroDelegate::smIndexChanged() { emit commitData(static_cast<QWidget*>(sender())); }
void LayersModel::loadData(ProjectModel::LayersNode* node) {
beginResetModel();
m_node = node;
endResetModel();
}
void LayersModel::unloadData() {
beginResetModel();
m_node.reset();
endResetModel();
}
int LayersModel::rowCount(const QModelIndex& parent) const {
if (parent.isValid())
return 0;
if (!m_node)
return 0;
return int(m_node->m_obj->size()) + 1;
}
int LayersModel::columnCount(const QModelIndex& parent) const {
if (parent.isValid())
return 0;
return 8;
}
QVariant LayersModel::data(const QModelIndex& index, int role) const {
if (!m_node)
return QVariant();
if (index.row() == m_node->m_obj->size())
return QVariant();
const amuse::LayerMapping& layer = (*m_node->m_obj)[index.row()];
if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch (index.column()) {
case 0: {
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(m_node.get());
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
if (ProjectModel::BasePoolObjectNode* node = smColl->nodeOfId(layer.macro.id))
return node->text();
return QVariant();
}
case 1:
return layer.keyLo;
case 2:
return layer.keyHi;
case 3:
return layer.transpose;
case 4:
return layer.volume;
case 5:
return layer.prioOffset;
case 6:
return layer.span;
case 7:
return layer.pan;
default:
break;
}
}
return QVariant();
}
bool LayersModel::setData(const QModelIndex& index, const QVariant& value, int role) {
if (!m_node || role != Qt::EditRole)
return false;
const amuse::LayerMapping& layer = (*m_node->m_obj)[index.row()];
switch (index.column()) {
case 0:
if (layer.macro.id == value.toInt())
return false;
break;
case 1:
if (layer.keyLo == value.toInt())
return false;
break;
case 2:
if (layer.keyHi == value.toInt())
return false;
break;
case 3:
if (layer.transpose == value.toInt())
return false;
break;
case 4:
if (layer.volume == value.toInt())
return false;
break;
case 5:
if (layer.prioOffset == value.toInt())
return false;
break;
case 6:
if (layer.span == value.toInt())
return false;
break;
case 7:
if (layer.pan == value.toInt())
return false;
break;
default:
return false;
}
g_MainWindow->pushUndoCommand(new LayerDataChangeUndoCommand(
m_node.get(), tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()), index, value.toInt()));
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
return false;
}
QVariant LayersModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("SoundMacro");
case 1:
return tr("Key Lo");
case 2:
return tr("Key Hi");
case 3:
return tr("Transpose");
case 4:
return tr("Volume");
case 5:
return tr("Prio Off");
case 6:
return tr("Span");
case 7:
return tr("Pan");
default:
break;
}
}
return QVariant();
}
Qt::ItemFlags LayersModel::flags(const QModelIndex& index) const {
if (!index.isValid())
return Qt::NoItemFlags;
if (index.row() == m_node->m_obj->size())
return Qt::NoItemFlags;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
Qt::DropActions LayersModel::supportedDropActions() const { return Qt::MoveAction; }
Qt::DropActions LayersModel::supportedDragActions() const { return Qt::MoveAction; }
class LayerRowMoveCommand : public EditorUndoCommand {
LayersTableView* m_view;
int m_undoPos, m_redoPos, m_count;
bool m_undid = false;
public:
explicit LayerRowMoveCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view, int undoPos,
int redoPos, int count)
: EditorUndoCommand(node, text), m_view(view), m_undoPos(undoPos), m_redoPos(redoPos), m_count(count) {}
void undo() override {
m_undid = true;
EditorUndoCommand::undo();
if (m_redoPos > m_undoPos)
m_view->model()->moveRows(QModelIndex(), m_redoPos - 1, m_count, QModelIndex(), m_undoPos);
else
m_view->model()->moveRows(QModelIndex(), m_redoPos, m_count, QModelIndex(), m_undoPos + 1);
}
void redo() override {
if (m_undid)
EditorUndoCommand::redo();
m_view->model()->moveRows(QModelIndex(), m_undoPos, m_count, QModelIndex(), m_redoPos);
}
};
bool LayersModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
const QModelIndex& parent) {
// check if the action is supported
if (!data || action != Qt::MoveAction)
return false;
// check if the format is supported
QStringList types = mimeTypes();
if (types.isEmpty())
return false;
QString format = types.at(0);
if (!data->hasFormat(format))
return false;
// decode and insert
QByteArray encoded = data->data(format);
QDataStream stream(&encoded, QIODevice::ReadOnly);
std::unordered_set<int> rows;
int lastRow = -1;
while (!stream.atEnd()) {
int r, c;
QMap<int, QVariant> v;
stream >> r >> c >> v;
rows.insert(r);
lastRow = r;
}
if (lastRow == -1)
return false;
int start = lastRow;
while (rows.find(start - 1) != rows.cend())
start -= 1;
int count = lastRow - start + 1;
while (rows.find(start + count) != rows.cend())
count += 1;
int dest = parent.row();
if (dest >= start) {
if (dest - start < count)
return false;
dest += 1;
}
g_MainWindow->pushUndoCommand(new LayerRowMoveCommand(m_node.get(), count > 1 ? tr("Move Layers") : tr("Move Layer"),
&static_cast<LayersEditor*>(QObject::parent())->m_tableView,
start, dest, count));
return true;
}
class LayerRowUndoCommand : public EditorUndoCommand {
protected:
LayersTableView* m_view;
std::vector<std::pair<amuse::LayerMapping, int>> m_data;
bool m_undid = false;
void add() {
m_view->selectionModel()->clearSelection();
for (const auto& p : m_data) {
static_cast<LayersModel*>(m_view->model())->_insertRow(p.second, p.first);
m_view->setCurrentIndex(m_view->model()->index(p.second, 0));
}
}
void del() {
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it) {
it->first = static_cast<LayersModel*>(m_view->model())->_removeRow(it->second);
}
}
void undo() override {
m_undid = true;
EditorUndoCommand::undo();
}
void redo() override {
if (m_undid)
EditorUndoCommand::redo();
}
public:
explicit LayerRowUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
};
class LayerRowAddUndoCommand : public LayerRowUndoCommand {
using base = LayerRowUndoCommand;
public:
explicit LayerRowAddUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
: LayerRowUndoCommand(node, text, view, std::move(data)) {}
void undo() override {
base::undo();
base::del();
}
void redo() override {
base::redo();
base::add();
}
};
class LayerRowDelUndoCommand : public LayerRowUndoCommand {
using base = LayerRowUndoCommand;
public:
explicit LayerRowDelUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
: LayerRowUndoCommand(node, text, view, std::move(data)) {}
void undo() override {
base::undo();
base::add();
}
void redo() override {
base::redo();
base::del();
}
};
bool LayersModel::insertRows(int row, int count, const QModelIndex& parent) {
if (!m_node)
return false;
beginInsertRows(parent, row, row + count - 1);
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
layers.insert(layers.begin() + row, count, amuse::LayerMapping());
endInsertRows();
return true;
}
bool LayersModel::moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
const QModelIndex& destinationParent, int destinationChild) {
if (!m_node) {
return false;
}
const bool moving =
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
const auto pivot = layers.begin() + sourceRow;
const auto begin = layers.begin() + destinationChild;
const auto end = pivot + count;
if (destinationChild < sourceRow) {
std::rotate(begin, pivot, end);
} else if (destinationChild > sourceRow) {
std::rotate(pivot, end, begin);
}
if (moving) {
endMoveRows();
}
return true;
}
bool LayersModel::removeRows(int row, int count, const QModelIndex& parent) {
if (!m_node)
return false;
beginRemoveRows(parent, row, row + count - 1);
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
layers.erase(layers.begin() + row, layers.begin() + row + count);
endRemoveRows();
return true;
}
void LayersModel::_insertRow(int row, const amuse::LayerMapping& data) {
if (!m_node)
return;
beginInsertRows(QModelIndex(), row, row);
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
layers.insert(layers.begin() + row, data);
endInsertRows();
}
amuse::LayerMapping LayersModel::_removeRow(int row) {
if (!m_node)
return {};
beginRemoveRows(QModelIndex(), row, row);
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
amuse::LayerMapping ret = layers[row];
layers.erase(layers.begin() + row);
endRemoveRows();
return ret;
}
LayersModel::LayersModel(QObject* parent) : QAbstractTableModel(parent) {}
LayersModel::~LayersModel() = default;
void LayersTableView::deleteSelection() {
const QModelIndexList list = selectionModel()->selectedRows();
if (list.isEmpty()) {
return;
}
std::vector<std::pair<amuse::LayerMapping, int>> data;
data.reserve(list.size());
for (const QModelIndex& idx : list) {
data.emplace_back(amuse::LayerMapping{}, idx.row());
}
std::sort(data.begin(), data.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
g_MainWindow->pushUndoCommand(new LayerRowDelUndoCommand(static_cast<LayersModel*>(model())->m_node.get(),
data.size() > 1 ? tr("Delete Layers") : tr("Delete Layer"),
this, std::move(data)));
}
void LayersTableView::setModel(QAbstractItemModel* model) {
QTableView::setModel(model);
horizontalHeader()->setMinimumSectionSize(75);
horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
horizontalHeader()->resizeSection(1, 75);
horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
horizontalHeader()->resizeSection(2, 75);
horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
horizontalHeader()->resizeSection(3, 75);
horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed);
horizontalHeader()->resizeSection(4, 75);
horizontalHeader()->setSectionResizeMode(5, QHeaderView::Fixed);
horizontalHeader()->resizeSection(5, 75);
horizontalHeader()->setSectionResizeMode(6, QHeaderView::Fixed);
horizontalHeader()->resizeSection(6, 75);
horizontalHeader()->setSectionResizeMode(7, QHeaderView::Fixed);
horizontalHeader()->resizeSection(7, 75);
}
LayersTableView::LayersTableView(QWidget* parent) : QTableView(parent) {
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setDragDropMode(QAbstractItemView::InternalMove);
setDefaultDropAction(Qt::MoveAction);
setDragEnabled(true);
setGridStyle(Qt::NoPen);
m_signedDelegate.setItemEditorFactory(&m_signedFactory);
m_unsignedDelegate.setItemEditorFactory(&m_unsignedFactory);
setItemDelegateForColumn(0, &m_smDelegate);
setItemDelegateForColumn(1, &m_unsignedDelegate);
setItemDelegateForColumn(2, &m_unsignedDelegate);
setItemDelegateForColumn(3, &m_signedDelegate);
setItemDelegateForColumn(4, &m_unsignedDelegate);
setItemDelegateForColumn(5, &m_signedDelegate);
setItemDelegateForColumn(6, &m_unsignedDelegate);
setItemDelegateForColumn(7, &m_unsignedDelegate);
}
LayersTableView::~LayersTableView() = default;
bool LayersEditor::loadData(ProjectModel::LayersNode* node) {
m_model.loadData(node);
return true;
}
void LayersEditor::unloadData() { m_model.unloadData(); }
ProjectModel::INode* LayersEditor::currentNode() const { return m_model.m_node.get(); }
void LayersEditor::resizeEvent(QResizeEvent* ev) {
m_tableView.setGeometry(QRect({}, ev->size()));
m_addRemoveButtons.move(0, ev->size().height() - 32);
}
void LayersEditor::rowsInserted(const QModelIndex& parent, int first, int last) {
m_tableView.scrollTo(m_tableView.model()->index(first, 0));
}
void LayersEditor::rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row) {
m_tableView.scrollTo(m_tableView.model()->index(row, 0));
}
void LayersEditor::doAdd() {
const QModelIndex idx = m_tableView.selectionModel()->currentIndex();
std::vector<std::pair<amuse::LayerMapping, int>> data;
if (idx.isValid()) {
data.emplace_back(amuse::LayerMapping{}, idx.row());
} else {
data.emplace_back(amuse::LayerMapping{}, m_model.rowCount() - 1);
}
g_MainWindow->pushUndoCommand(
new LayerRowAddUndoCommand(m_model.m_node.get(), tr("Add Layer"), &m_tableView, std::move(data)));
}
void LayersEditor::doSelectionChanged() {
m_addRemoveButtons.removeAction()->setDisabled(m_tableView.selectionModel()->selectedRows().isEmpty());
g_MainWindow->updateFocus();
}
AmuseItemEditFlags LayersEditor::itemEditFlags() const {
return m_tableView.selectionModel()->selectedRows().isEmpty() ? AmuseItemNone : AmuseItemDelete;
}
void LayersEditor::itemDeleteAction() { m_tableView.deleteSelection(); }
LayersEditor::LayersEditor(QWidget* parent)
: EditorWidget(parent), m_model(this), m_tableView(this), m_addRemoveButtons(this) {
m_tableView.setModel(&m_model);
connect(m_tableView.selectionModel(), &QItemSelectionModel::selectionChanged, this,
&LayersEditor::doSelectionChanged);
connect(&m_model, &LayersModel::rowsInserted, this, &LayersEditor::rowsInserted);
connect(&m_model, &LayersModel::rowsMoved, this, &LayersEditor::rowsMoved);
m_addRemoveButtons.addAction()->setToolTip(tr("Add new layer mapping"));
connect(m_addRemoveButtons.addAction(), &QAction::triggered, this, &LayersEditor::doAdd);
m_addRemoveButtons.removeAction()->setToolTip(tr("Remove selected layer mappings"));
connect(m_addRemoveButtons.removeAction(), &QAction::triggered, this, &LayersEditor::itemDeleteAction);
}
LayersEditor::~LayersEditor() = default;

103
Editor/LayersEditor.hpp Normal file
View File

@ -0,0 +1,103 @@
#pragma once
#include <QAbstractTableModel>
#include <QAction>
#include <QStyledItemDelegate>
#include <QTableView>
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#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 override;
public:
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();
};
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);
~LayersModel() override;
void loadData(ProjectModel::LayersNode* node);
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;
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;
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;
public:
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;
public:
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() override;
};

147
Editor/MIDIReader.cpp Normal file
View File

@ -0,0 +1,147 @@
#include "MIDIReader.hpp"
#include "MainWindow.hpp"
#include <amuse/Engine.hpp>
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock) : amuse::BooBackendMIDIReader(engine, useLock) {}
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;
}
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;
}
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;
}
}
/* 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;
}
}
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;
}
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::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::allSoundOff(uint8_t chan) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->kill();
return;
}
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;
}
for (auto& v : m_engine.getActiveVoices())
v->kill();
}
void MIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
void MIDIReader::polyMode(uint8_t /*chan*/, bool /*on*/) {}
void MIDIReader::sysex(const void* /*data*/, size_t /*len*/) {}
void MIDIReader::timeCodeQuarterFrame(uint8_t /*message*/, uint8_t /*value*/) {}
void MIDIReader::songPositionPointer(uint16_t /*pointer*/) {}
void MIDIReader::songSelect(uint8_t /*song*/) {}
void MIDIReader::tuneRequest() {}
void MIDIReader::startSeq() {}
void MIDIReader::continueSeq() {}
void MIDIReader::stopSeq() {}
void MIDIReader::reset() {}
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());
}

56
Editor/MIDIReader.hpp Normal file
View File

@ -0,0 +1,56 @@
#pragma once
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#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);
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) 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) 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() override;
void continueSeq() override;
void stopSeq() override;
void reset() override;
};
class VoiceAllocator : public amuse::BooBackendVoiceAllocator {
public:
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine) override;
};

9
Editor/MacOSExtras.mm Normal file
View File

@ -0,0 +1,9 @@
#import <AppKit/AppKit.h>
void MacOSSetDarkAppearance()
{
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
if ([NSApp respondsToSelector:@selector(setAppearance:)])
[NSApp setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]];
#endif
}

1900
Editor/MainWindow.cpp Normal file

File diff suppressed because it is too large Load Diff

313
Editor/MainWindow.hpp Normal file
View File

@ -0,0 +1,313 @@
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <QFileDialog>
#include <QMainWindow>
#include <QMessageBox>
#include <QStyledItemDelegate>
#include <QThread>
#include "Common.hpp"
#include "EditorWidget.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 boo {
struct IAudioVoiceEngine;
}
namespace Ui {
class MainWindow;
}
class ADSREditor;
class CurveEditor;
class KeymapEditor;
class LayersEditor;
class MainWindow;
class SampleEditor;
class SongGroupEditor;
class SoundGroupEditor;
class SoundMacroEditor;
class StudioSetupWidget;
class VoiceAllocator;
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;
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; }
signals:
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; }
};
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) override;
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;
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;
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);
};

730
Editor/MainWindow.ui Normal file
View File

@ -0,0 +1,730 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1360</width>
<height>768</height>
</rect>
</property>
<property name="windowTitle">
<string>Amuse</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>950</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="projectOutlineContainer">
<layout class="QVBoxLayout" name="outlineLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="outlineLayout2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="projectOutlineFilter">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="placeholderText">
<string>Filter</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="backButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconBack.svg</normaloff>
<disabledoff>:/icons/IconBackDisabled.svg</disabledoff>
<disabledon>:/icons/IconBackDisabled.svg</disabledon>:/icons/IconBack.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="forwardButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconForward.svg</normaloff>
<disabledoff>:/icons/IconForwardDisabled.svg</disabledoff>
<disabledon>:/icons/IconForwardDisabled.svg</disabledon>:/icons/IconForward.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="projectOutline">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
</property>
<property name="animated">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QSplitter" name="rightSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QStackedWidget" name="editorContents"/>
<widget class="QWidget" name="keyboard" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="PitchSlider" name="pitchSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="minimum">
<number>-2048</number>
</property>
<property name="maximum">
<number>2048</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ModulationSlider" name="modulationSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="VelocitySlider" name="velocitySlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="value">
<number>90</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="keyboardScrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="KeyboardWidget" name="keyboardContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1501</width>
<height>85</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1501</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1360</width>
<height>27</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>&amp;File</string>
</property>
<widget class="QMenu" name="menuRecent_Projects">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string>Recent &amp;Projects</string>
</property>
<property name="icon">
<iconset theme="document-open-recent"/>
</property>
</widget>
<addaction name="actionNew_Project"/>
<addaction name="actionOpen_Project"/>
<addaction name="menuRecent_Projects"/>
<addaction name="actionSave_Project"/>
<addaction name="actionRevert_Project"/>
<addaction name="actionReload_Sample_Data"/>
<addaction name="separator"/>
<addaction name="actionImport_Groups"/>
<addaction name="actionImport_Songs"/>
<addaction name="actionExport_GameCube_Groups"/>
<addaction name="actionImport_C_Headers"/>
<addaction name="actionExport_C_Headers"/>
<addaction name="separator"/>
<addaction name="actionQuit"/>
</widget>
<widget class="QMenu" name="menuProject">
<property name="title">
<string>Pro&amp;ject</string>
</property>
<addaction name="actionNew_Subproject"/>
<addaction name="separator"/>
<addaction name="actionNew_SFX_Group"/>
<addaction name="actionNew_Song_Group"/>
<addaction name="separator"/>
<addaction name="actionNew_Sound_Macro"/>
<addaction name="actionNew_ADSR"/>
<addaction name="actionNew_Curve"/>
<addaction name="actionNew_Keymap"/>
<addaction name="actionNew_Layers"/>
</widget>
<widget class="QMenu" name="menuAudio">
<property name="title">
<string>&amp;Audio</string>
</property>
<addaction name="separator"/>
<addaction name="actionSelect_Output_Device"/>
</widget>
<widget class="QMenu" name="menuMIDI">
<property name="title">
<string>&amp;MIDI</string>
</property>
<addaction name="separator"/>
<addaction name="actionInput_Device"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>&amp;Edit</string>
</property>
<addaction name="actionCut"/>
<addaction name="actionCopy"/>
<addaction name="actionPaste"/>
<addaction name="actionDelete"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="actionAbout_Amuse"/>
<addaction name="actionAbout_Qt"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuProject"/>
<addaction name="menuAudio"/>
<addaction name="menuMIDI"/>
<addaction name="menuHelp"/>
</widget>
<widget class="StatusBarWidget" name="statusbar"/>
<action name="actionNew_Project">
<property name="icon">
<iconset theme="document-new"/>
</property>
<property name="text">
<string>&amp;New Project</string>
</property>
</action>
<action name="actionOpen_Project">
<property name="icon">
<iconset theme="document-open"/>
</property>
<property name="text">
<string>&amp;Open Project</string>
</property>
</action>
<action name="actionCut">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="edit-cut"/>
</property>
<property name="text">
<string>&amp;Cut</string>
</property>
</action>
<action name="actionCopy">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="edit-copy"/>
</property>
<property name="text">
<string>C&amp;opy</string>
</property>
</action>
<action name="actionPaste">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="edit-paste"/>
</property>
<property name="text">
<string>&amp;Paste</string>
</property>
</action>
<action name="actionDelete">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="edit-delete"/>
</property>
<property name="text">
<string>&amp;Delete</string>
</property>
</action>
<action name="actionImport_Groups">
<property name="text">
<string>&amp;Import Groups</string>
</property>
<property name="shortcut">
<string>Ctrl+I</string>
</property>
</action>
<action name="actionNew_SFX_Group">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewSoundGroup.svg</normaloff>:/icons/IconNewSoundGroup.svg</iconset>
</property>
<property name="text">
<string>New SF&amp;X Group</string>
</property>
</action>
<action name="actionNew_Song_Group">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewSongGroup.svg</normaloff>:/icons/IconNewSongGroup.svg</iconset>
</property>
<property name="text">
<string>New Son&amp;g Group</string>
</property>
</action>
<action name="actionNew_Sound_Macro">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewSoundMacro.svg</normaloff>:/icons/IconNewSoundMacro.svg</iconset>
</property>
<property name="text">
<string>New Sound &amp;Macro</string>
</property>
</action>
<action name="actionNew_Keymap">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewKeymap.svg</normaloff>:/icons/IconNewKeymap.svg</iconset>
</property>
<property name="text">
<string>New &amp;Keymap</string>
</property>
</action>
<action name="actionNew_Layers">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewLayers.svg</normaloff>:/icons/IconNewLayers.svg</iconset>
</property>
<property name="text">
<string>New &amp;Layers</string>
</property>
</action>
<action name="actionSelect_Output_Device">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Output Device:</string>
</property>
</action>
<action name="actionInput_Device">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Input Device:</string>
</property>
</action>
<action name="actionExport_GameCube_Groups">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Export GameCube Groups</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property>
</action>
<action name="actionNew_Subproject">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewGroup.svg</normaloff>:/icons/IconNewGroup.svg</iconset>
</property>
<property name="text">
<string>&amp;New Subproject</string>
</property>
</action>
<action name="actionNew_ADSR">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewADSR.svg</normaloff>:/icons/IconNewADSR.svg</iconset>
</property>
<property name="text">
<string>New &amp;ADSR</string>
</property>
</action>
<action name="actionNew_Curve">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/IconNewCurve.svg</normaloff>:/icons/IconNewCurve.svg</iconset>
</property>
<property name="text">
<string>New &amp;Curve</string>
</property>
</action>
<action name="actionSave_Project">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="document-save"/>
</property>
<property name="text">
<string>&amp;Save Project</string>
</property>
</action>
<action name="actionRevert_Project">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="document-revert"/>
</property>
<property name="text">
<string>&amp;Revert Project</string>
</property>
</action>
<action name="actionReload_Sample_Data">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Reload Sample &amp;Data</string>
</property>
</action>
<action name="actionImport_Songs">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>I&amp;mport Songs</string>
</property>
</action>
<action name="actionAbout_Amuse">
<property name="icon">
<iconset theme="help-about"/>
</property>
<property name="text">
<string>&amp;About Amuse</string>
</property>
<property name="menuRole">
<enum>QAction::AboutRole</enum>
</property>
</action>
<action name="actionAbout_Qt">
<property name="icon">
<iconset theme="help-about"/>
</property>
<property name="text">
<string>About &amp;Qt</string>
</property>
<property name="menuRole">
<enum>QAction::AboutQtRole</enum>
</property>
</action>
<action name="actionExport_C_Headers">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Export &amp;C Headers</string>
</property>
</action>
<action name="actionQuit">
<property name="icon">
<iconset theme="application-exit"/>
</property>
<property name="text">
<string>&amp;Quit</string>
</property>
<property name="menuRole">
<enum>QAction::QuitRole</enum>
</property>
</action>
<action name="actionImport_C_Headers">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Import C &amp;Headers</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>KeyboardWidget</class>
<extends>QWidget</extends>
<header>KeyboardWidget.hpp</header>
<container>1</container>
</customwidget>
<customwidget>
<class>StatusBarWidget</class>
<extends>QStatusBar</extends>
<header>StatusBarWidget.hpp</header>
</customwidget>
<customwidget>
<class>ModulationSlider</class>
<extends>QSlider</extends>
<header>KeyboardWidget.hpp</header>
</customwidget>
<customwidget>
<class>PitchSlider</class>
<extends>QSlider</extends>
<header>KeyboardWidget.hpp</header>
</customwidget>
<customwidget>
<class>VelocitySlider</class>
<extends>QSlider</extends>
<header>KeyboardWidget.hpp</header>
</customwidget>
</customwidgets>
<resources>
<include location="resources/resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,159 @@
#include "NewSoundMacroDialog.hpp"
#include <QVBoxLayout>
#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 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 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 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 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 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 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 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 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 SimplePlaySample[] = {
0x10000000,
0x00000000,
0x00000000,
0x00000000,
};
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 ADSR"), sizeof(LoopADSR), LoopADSR},
{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", "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}};
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"));
int idx = 0;
for (const auto& ent : Entries)
m_combo.addItem(tr(ent.m_name), idx++);
m_combo.setCurrentIndex(0);
connect(&m_buttonBox, &QDialogButtonBox::accepted, this, &NewSoundMacroDialog::accept);
connect(&m_buttonBox, &QDialogButtonBox::rejected, this, &NewSoundMacroDialog::reject);
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);
setLayout(layout);
}
NewSoundMacroDialog::~NewSoundMacroDialog() = default;

View File

@ -0,0 +1,29 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
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;
public:
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
~NewSoundMacroDialog() override;
QString getName() const { return m_le.text(); }
const SoundMacroTemplateEntry* getSelectedTemplate() const;
};

2132
Editor/ProjectModel.cpp Normal file

File diff suppressed because it is too large Load Diff

486
Editor/ProjectModel.hpp Normal file
View File

@ -0,0 +1,486 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <QAbstractItemModel>
#include <QDir>
#include <QIcon>
#include <QIdentityProxyModel>
#include <QSortFilterProxyModel>
#include "Common.hpp"
#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;
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
QRegularExpression m_usageKey;
public:
explicit OutlineFilterProxyModel(ProjectModel* source);
public slots:
void setFilterRegExp(const QString& pattern);
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
};
class NullItemProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
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
public:
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
public:
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();
}
};
private:
QDir m_dir;
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;
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:
~INode() override = default;
explicit INode(QString name);
// ONLY USED BY NULL NODE!
explicit INode(INode* parent) : m_parent(parent), m_row(0) {}
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 {}
};
struct NullNode final : INode {
explicit NullNode(INode* parent) : INode(parent) {}
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>")) {}
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) {}
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);
~ProjectModel() override;
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();
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(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);
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);
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, 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;
};

784
Editor/SampleEditor.cpp Normal file
View File

@ -0,0 +1,784 @@
#include "SampleEditor.hpp"
#include <cfloat>
#include <QCheckBox>
#include <QPaintEvent>
#include <QPainter>
#include <QScrollArea>
#include <QScrollBar>
#include <QSpinBox>
#include "MainWindow.hpp"
#include <amuse/DSPCodec.hpp>
SampleEditor* SampleView::getEditor() const {
return qobject_cast<SampleEditor*>(parentWidget()->parentWidget()->parentWidget());
}
void SampleView::seekToSample(qreal sample) {
sample = std::min(sample, qreal(m_sample->getNumSamples()));
if (m_sample->isFormatDSP()) {
if (sample < m_curSamplePos) {
m_prev1 = m_prev2 = 0;
m_curSamplePos = 0.0;
}
uint32_t startBlock = uint32_t(m_curSamplePos) / 14;
uint32_t startRem = uint32_t(m_curSamplePos) % 14;
uint32_t endBlock = uint32_t(sample) / 14;
uint32_t endRem = uint32_t(sample) % 14;
if (startRem) {
uint32_t end = 14;
if (startBlock == endBlock) {
end = endRem;
endRem = 0;
}
DSPDecompressFrameRangedStateOnly(m_sampleData + 8 * startBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
&m_prev2, startRem, end);
if (end == 14)
++startBlock;
}
for (uint32_t b = startBlock; b < endBlock; ++b) {
DSPDecompressFrameStateOnly(m_sampleData + 8 * b, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, 14);
}
if (endRem) {
DSPDecompressFrameStateOnly(m_sampleData + 8 * endBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2,
endRem);
}
}
m_curSamplePos = sample;
}
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> SampleView::iterateSampleInterval(qreal interval) {
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> ret = {{0.0, 1.0}, {0.0, -1.0}}; // min,max avg,peak
qreal avg = 0.0;
qreal div = 0.0;
qreal endSample = std::min(m_curSamplePos + interval, qreal(m_sample->getNumSamples()));
auto accumulate = [&ret, &avg, &div](int16_t sample) {
qreal sampleF = sample / 32768.0;
avg += sampleF;
div += 1.0;
ret.first.second = std::min(ret.first.second, sampleF);
ret.second.second = std::max(ret.second.second, sampleF);
};
if (m_sample->isFormatDSP()) {
uint32_t startBlock = uint32_t(m_curSamplePos) / 14;
uint32_t startRem = uint32_t(m_curSamplePos) % 14;
uint32_t endBlock = uint32_t(endSample) / 14;
uint32_t endRem = uint32_t(endSample) % 14;
int16_t sampleBlock[14];
if (startRem) {
uint32_t end = 14;
if (startBlock == endBlock) {
end = endRem;
endRem = 0;
}
DSPDecompressFrameRanged(sampleBlock, m_sampleData + 8 * startBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
&m_prev2, startRem, end);
for (uint32_t s = 0; s < end - startRem; ++s)
accumulate(sampleBlock[s]);
if (end == 14)
++startBlock;
}
for (uint32_t b = startBlock; b < endBlock; ++b) {
DSPDecompressFrame(sampleBlock, m_sampleData + 8 * b, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, 14);
for (uint32_t s = 0; s < 14; ++s)
accumulate(sampleBlock[s]);
}
if (endRem) {
DSPDecompressFrame(sampleBlock, m_sampleData + 8 * endBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
&m_prev2, endRem);
for (uint32_t s = 0; s < endRem; ++s)
accumulate(sampleBlock[s]);
}
} else if (m_sample->getSampleFormat() == amuse::SampleFormat::PCM_PC) {
for (uint32_t s = uint32_t(m_curSamplePos); s < uint32_t(endSample); ++s)
accumulate(reinterpret_cast<const int16_t*>(m_sampleData)[s]);
}
m_curSamplePos = endSample;
if (div == 0.0)
return {};
avg /= div;
if (avg > 0.0) {
ret.first.first = ret.first.second;
ret.second.first = avg;
} else {
ret.first.first = avg;
ret.second.first = ret.second.second;
}
return ret;
}
void SampleView::paintEvent(QPaintEvent* ev) {
QPainter painter(this);
constexpr int rulerHeight = 28;
int sampleHeight = height() - rulerHeight;
qreal deviceRatio = devicePixelRatioF();
qreal rectStart = ev->rect().x();
qreal deviceWidth = ev->rect().width() * deviceRatio;
qreal increment = 1.0 / deviceRatio;
qreal deviceSamplesPerPx = m_samplesPerPx / deviceRatio;
qreal startSample = rectStart * m_samplesPerPx;
qreal startSampleRem = std::fmod(startSample, deviceSamplesPerPx);
if (startSampleRem > DBL_EPSILON) {
startSample -= startSampleRem;
deviceWidth += startSampleRem;
rectStart = startSample / m_samplesPerPx;
}
if (m_sample) {
QPen peakPen(QColor(255, 147, 41), increment);
QPen avgPen(QColor(254, 177, 68), increment);
qreal scale = -sampleHeight / 2.0;
qreal trans = sampleHeight / 2.0;
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> lastAvgPeak;
if (startSample >= deviceSamplesPerPx) {
seekToSample(startSample - deviceSamplesPerPx);
lastAvgPeak = iterateSampleInterval(deviceSamplesPerPx);
} else {
seekToSample(startSample);
lastAvgPeak = std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>>{};
}
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> avgPeak;
for (qreal i = 0.0; i < deviceWidth; i += increment) {
if (m_curSamplePos + deviceSamplesPerPx > m_sample->getNumSamples())
break;
if (i == 0.0 || std::floor(m_curSamplePos) != std::floor(m_curSamplePos + deviceSamplesPerPx))
avgPeak = iterateSampleInterval(deviceSamplesPerPx);
else
m_curSamplePos += deviceSamplesPerPx;
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> drawAvgPeak = avgPeak;
if (lastAvgPeak.first.first > avgPeak.second.first)
drawAvgPeak.second.first = lastAvgPeak.first.first;
else if (lastAvgPeak.second.first < avgPeak.first.first)
drawAvgPeak.first.first = lastAvgPeak.second.first;
painter.setPen(peakPen);
painter.drawLine(QPointF(rectStart + i, drawAvgPeak.first.second * scale + trans),
QPointF(rectStart + i, drawAvgPeak.second.second * scale + trans));
painter.setPen(avgPen);
painter.drawLine(QPointF(rectStart + i, drawAvgPeak.first.first * scale + trans),
QPointF(rectStart + i, drawAvgPeak.second.first * scale + trans));
lastAvgPeak = avgPeak;
}
}
painter.setBrush(palette().brush(QPalette::Base));
painter.setPen(Qt::NoPen);
painter.drawRect(QRect(ev->rect().x(), sampleHeight, ev->rect().width(), rulerHeight));
constexpr qreal minNumSpacing = 40.0;
qreal binaryDiv = 1.0;
while (m_samplesPerPx * minNumSpacing > binaryDiv)
binaryDiv *= 2.0;
qreal numSpacing = binaryDiv / m_samplesPerPx;
qreal tickSpacing = numSpacing / 5;
int startX = std::max(0, int(ev->rect().x() - numSpacing));
int lastNumDiv = int(startX / numSpacing);
int lastTickDiv = int(startX / tickSpacing);
qreal samplePos = startX * m_samplesPerPx;
painter.setFont(m_rulerFont);
painter.setPen(QPen(QColor(127, 127, 127), increment));
for (int i = startX; i < ev->rect().x() + ev->rect().width() + numSpacing; ++i) {
int thisNumDiv = int(i / numSpacing);
int thisTickDiv = int(i / tickSpacing);
if (thisNumDiv != lastNumDiv) {
painter.drawLine(i, sampleHeight + 1, i, sampleHeight + 8);
lastNumDiv = thisNumDiv;
lastTickDiv = thisTickDiv;
painter.drawText(QRectF(i - numSpacing / 2.0, sampleHeight + 11.0, numSpacing, 12.0),
QString::number(int(samplePos)), QTextOption(Qt::AlignCenter));
} else if (thisTickDiv != lastTickDiv) {
painter.drawLine(i, sampleHeight + 1, i, sampleHeight + 5);
lastTickDiv = thisTickDiv;
}
samplePos += m_samplesPerPx;
}
if (m_sample) {
if (m_sample->isLooped()) {
int loopStart = m_sample->m_loopStartSample;
int loopEnd = loopStart + m_sample->m_loopLengthSamples - 1;
QPointF points[4];
painter.setPen(QPen(Qt::green, increment));
painter.setBrush(Qt::green);
qreal startPos = loopStart / m_samplesPerPx;
painter.drawLine(QPointF(startPos, 0.0), QPointF(startPos, height()));
points[0] = QPointF(startPos, height() - 6.0);
points[1] = QPointF(startPos - 6.0, height());
points[2] = QPointF(startPos + 6.0, height());
points[3] = QPointF(startPos, height() - 6.0);
painter.drawPolygon(points, 4);
painter.setPen(QPen(Qt::red, increment));
painter.setBrush(Qt::red);
qreal endPos = loopEnd / m_samplesPerPx;
painter.drawLine(QPointF(endPos, 0.0), QPointF(endPos, height()));
points[0] = QPointF(endPos, height() - 6.0);
points[1] = QPointF(endPos - 6.0, height());
points[2] = QPointF(endPos + 6.0, height());
points[3] = QPointF(endPos, height() - 6.0);
painter.drawPolygon(points, 4);
}
if (m_displaySamplePos >= 0) {
painter.setPen(QPen(Qt::white, increment));
qreal pos = m_displaySamplePos / m_samplesPerPx;
painter.drawLine(QPointF(pos, 0.0), QPointF(pos, height()));
}
}
}
void SampleView::calculateSamplesPerPx() {
m_samplesPerPx = (1.0 - m_zoomFactor) * m_baseSamplesPerPx + m_zoomFactor * 1.0;
}
void SampleView::resetZoom() {
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
m_baseSamplesPerPx = m_sample->getNumSamples() / qreal(scroll->width() - 4);
calculateSamplesPerPx();
setMinimumWidth(int(m_sample->getNumSamples() / m_samplesPerPx) + 1);
update();
}
}
void SampleView::setZoom(int zVal) {
m_zoomFactor = zVal / 99.0;
calculateSamplesPerPx();
setMinimumWidth(int(m_sample->getNumSamples() / m_samplesPerPx) + 1);
update();
}
void SampleView::showEvent(QShowEvent* ev) { setZoom(0); }
void SampleView::mousePressEvent(QMouseEvent* ev) {
if (m_sample && m_sample->isLooped()) {
int loopStart = m_sample->m_loopStartSample;
int startPos = int(loopStart / m_samplesPerPx);
int startDelta = std::abs(startPos - ev->pos().x());
bool startPass = startDelta < 20;
int loopEnd = loopStart + m_sample->m_loopLengthSamples - 1;
int endPos = int(loopEnd / m_samplesPerPx);
int endDelta = std::abs(endPos - ev->pos().x());
bool endPass = endDelta < 20;
if (startPass && endPass) {
if (startDelta == endDelta) {
if (ev->pos().x() < startPos)
m_dragState = DragState::Start;
else
m_dragState = DragState::End;
} else if (startDelta < endDelta)
m_dragState = DragState::Start;
else
m_dragState = DragState::End;
} else if (startPass)
m_dragState = DragState::Start;
else if (endPass)
m_dragState = DragState::End;
getEditor()->m_controls->setFileWrite(m_dragState == DragState::None);
mouseMoveEvent(ev);
}
}
void SampleView::mouseReleaseEvent(QMouseEvent* ev) {
m_dragState = DragState::None;
getEditor()->m_controls->setFileWrite(true);
}
void SampleView::mouseMoveEvent(QMouseEvent* ev) {
if (m_dragState == DragState::Start)
getEditor()->m_controls->setLoopStartSample(int(ev->pos().x() * m_samplesPerPx));
else if (m_dragState == DragState::End)
getEditor()->m_controls->setLoopEndSample(int(ev->pos().x() * m_samplesPerPx));
}
void SampleView::wheelEvent(QWheelEvent* ev) {
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
/* Send wheel event directly to the scroll bar */
QApplication::sendEvent(scroll->horizontalScrollBar(), ev);
}
}
bool SampleView::loadData(ProjectModel::SampleNode* node) {
bool reset = m_node.get() != node;
m_node = node;
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(m_node.get());
std::tie(m_sample, m_sampleData) = group->getAudioGroup()->getSampleData(m_node->id(), m_node->m_obj.get());
if (reset)
resetZoom();
m_playbackMacro = amuse::MakeObj<amuse::SoundMacro>();
amuse::SoundMacro::CmdStartSample* startSample = static_cast<amuse::SoundMacro::CmdStartSample*>(
m_playbackMacro->insertNewCmd(0, amuse::SoundMacro::CmdOp::StartSample));
startSample->sample.id = m_node->id();
amuse::SoundMacro::CmdWaitMs* waitMillisec = static_cast<amuse::SoundMacro::CmdWaitMs*>(
m_playbackMacro->insertNewCmd(1, amuse::SoundMacro::CmdOp::WaitMs));
waitMillisec->keyOff = true;
waitMillisec->ms = 65535;
m_playbackMacro->insertNewCmd(2, amuse::SoundMacro::CmdOp::StopSample);
m_playbackMacro->insertNewCmd(3, amuse::SoundMacro::CmdOp::End);
update();
return reset;
}
void SampleView::unloadData() {
m_node.reset();
m_sample.reset();
m_playbackMacro.reset();
update();
}
ProjectModel::INode* SampleView::currentNode() const { return m_node.get(); }
amuse::SampleEntryData* SampleView::entryData() const { return m_sample.get(); }
const amuse::SoundMacro* SampleView::soundMacro() const { return m_playbackMacro.get(); }
void SampleView::setSamplePos(int pos) {
if (pos != m_displaySamplePos) {
if (pos >= 0) {
int lastPos = m_displaySamplePos;
m_displaySamplePos = pos;
updateSampleRange(lastPos, pos);
} else {
m_displaySamplePos = pos;
update();
}
}
}
void SampleView::updateSampleRange(int oldSamp, int newSamp) {
qreal lastPos = oldSamp / m_samplesPerPx;
qreal newPos = newSamp / m_samplesPerPx;
update(int(std::min(lastPos, newPos)) - 10, 0, int(std::fabs(newPos - lastPos)) + 20, height());
}
SampleView::SampleView(QWidget* parent) : QWidget(parent) {
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
m_rulerFont.setPointSize(8);
}
SampleView::~SampleView() = default;
void SampleControls::zoomSliderChanged(int val) {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
editor->m_sampleView->setZoom(val);
}
class SampLoopUndoCommand : public EditorUndoCommand {
uint32_t m_redoStartVal, m_redoEndVal, m_undoStartVal, m_undoEndVal;
int m_fieldIdx;
bool m_undid = false;
public:
SampLoopUndoCommand(uint32_t redoStart, uint32_t redoEnd, const QString& fieldName, int fieldIdx,
amuse::ObjToken<ProjectModel::SampleNode> node)
: EditorUndoCommand(node.get(), SampleControls::tr("Change %1").arg(fieldName))
, m_redoStartVal(redoStart)
, m_redoEndVal(redoEnd)
, m_fieldIdx(fieldIdx) {}
void undo() override {
m_undid = true;
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
data->setLoopStartSample(m_undoStartVal);
data->setLoopEndSample(m_undoEndVal);
EditorUndoCommand::undo();
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
e->m_controls->doFileWrite();
}
void redo() override {
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
m_undoStartVal = data->getLoopStartSample();
m_undoEndVal = data->getLoopEndSample();
data->setLoopStartSample(m_redoStartVal);
data->setLoopEndSample(m_redoEndVal);
if (m_undid) {
EditorUndoCommand::redo();
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
e->m_controls->doFileWrite();
}
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id() && static_cast<const SampLoopUndoCommand*>(other)->m_fieldIdx == m_fieldIdx) {
m_redoStartVal = static_cast<const SampLoopUndoCommand*>(other)->m_redoStartVal;
m_redoEndVal = static_cast<const SampLoopUndoCommand*>(other)->m_redoEndVal;
return true;
}
return false;
}
int id() const override { return int(Id::SampLoop); }
};
void SampleControls::loopStateChanged(int state) {
if (m_enableUpdate) {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
if (state == Qt::Checked) {
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(
atUint32(m_loopStart->value()), uint32_t(m_loopEnd->value()), tr("Loop"), 0, editor->m_sampleView->m_node));
m_loopStart->setEnabled(true);
m_loopEnd->setEnabled(true);
if (m_loopStart->value() == 0 && m_loopEnd->value() == 0) {
m_enableUpdate = false;
m_loopEnd->setValue(data->getNumSamples() - 1);
m_loopStart->setMaximum(m_loopEnd->value());
m_enableUpdate = true;
}
data->m_loopStartSample = atUint32(m_loopStart->value());
data->m_loopLengthSamples = m_loopEnd->value() + 1 - data->m_loopStartSample;
} else {
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(0, 0, tr("Loop"), 0, editor->m_sampleView->m_node));
m_loopStart->setEnabled(false);
m_loopEnd->setEnabled(false);
data->m_loopStartSample = 0;
data->m_loopLengthSamples = 0;
}
editor->m_sampleView->update();
doFileWrite();
}
}
void SampleControls::startValueChanged(int val) {
if (m_enableUpdate) {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
int oldPos = data->getLoopStartSample();
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(atUint32(val), data->getLoopEndSample(), tr("Loop Start"), 1,
editor->m_sampleView->m_node));
data->setLoopStartSample(atUint32(val));
m_loopEnd->setMinimum(val);
editor->m_sampleView->updateSampleRange(oldPos, val);
doFileWrite();
}
}
void SampleControls::endValueChanged(int val) {
if (m_enableUpdate) {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
int oldPos = data->getLoopEndSample();
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(data->getLoopStartSample(), atUint32(val), tr("Loop End"), 2,
editor->m_sampleView->m_node));
data->setLoopEndSample(atUint32(val));
m_loopStart->setMaximum(val);
editor->m_sampleView->updateSampleRange(oldPos, val);
doFileWrite();
}
}
class SampPitchUndoCommand : public EditorUndoCommand {
uint8_t m_redoPitchVal, m_undoPitchVal;
bool m_undid = false;
public:
SampPitchUndoCommand(atUint8 redoPitch, amuse::ObjToken<ProjectModel::SampleNode> node)
: EditorUndoCommand(node.get(), SampleControls::tr("Change Base Pitch")), m_redoPitchVal(redoPitch) {}
void undo() override {
m_undid = true;
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
data->m_pitch = m_undoPitchVal;
EditorUndoCommand::undo();
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
e->m_controls->doFileWrite();
}
void redo() override {
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
m_undoPitchVal = data->m_pitch;
data->m_pitch = m_redoPitchVal;
if (m_undid) {
EditorUndoCommand::redo();
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
e->m_controls->doFileWrite();
}
}
bool mergeWith(const QUndoCommand* other) override {
if (other->id() == id()) {
m_redoPitchVal = static_cast<const SampPitchUndoCommand*>(other)->m_redoPitchVal;
return true;
}
return false;
}
int id() const override { return int(Id::SampPitch); }
};
void SampleControls::pitchValueChanged(int val) {
if (m_enableUpdate) {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
g_MainWindow->pushUndoCommand(new SampPitchUndoCommand(atUint8(val), editor->m_sampleView->m_node));
data->m_pitch = atUint8(val);
doFileWrite();
}
}
void SampleControls::makeWAVVersion() {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->makeWAVVersion(node->id(), node->m_obj.get());
updateFileState();
}
void SampleControls::makeCompressedVersion() {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->makeCompressedVersion(node->id(),
node->m_obj.get());
updateFileState();
}
void SampleControls::showInBrowser() { ShowInGraphicalShell(this, m_path); }
void SampleControls::updateFileState() {
m_enableUpdate = false;
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
std::string path;
amuse::SampleFileState state = g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->getSampleFileState(
node->id(), node->m_obj.get(), &path);
disconnect(m_makeOtherConn);
switch (state) {
case amuse::SampleFileState::MemoryOnlyWAV:
case amuse::SampleFileState::CompressedNoWAV:
m_makeOtherVersion->setText(tr("Make WAV Version"));
m_makeOtherVersion->setDisabled(false);
m_makeOtherConn = connect(m_makeOtherVersion, &QPushButton::clicked, this, &SampleControls::makeWAVVersion);
break;
case amuse::SampleFileState::MemoryOnlyCompressed:
case amuse::SampleFileState::WAVRecent:
case amuse::SampleFileState::WAVNoCompressed:
m_makeOtherVersion->setText(tr("Make Compressed Version"));
m_makeOtherVersion->setDisabled(false);
m_makeOtherConn = connect(m_makeOtherVersion, &QPushButton::clicked, this, &SampleControls::makeCompressedVersion);
break;
default:
m_makeOtherVersion->setText(tr("Up To Date"));
m_makeOtherVersion->setDisabled(true);
break;
}
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
m_loopCheck->setChecked(data->isLooped());
int loopStart = 0;
int loopEnd = 0;
int loopMax = data->getNumSamples() - 1;
if (data->isLooped()) {
loopStart = data->m_loopStartSample;
loopEnd = loopStart + data->m_loopLengthSamples - 1;
}
m_loopStart->setMaximum(loopEnd);
m_loopEnd->setMinimum(loopStart);
m_loopEnd->setMaximum(loopMax);
m_loopStart->setValue(loopStart);
m_loopEnd->setValue(loopEnd);
m_basePitch->setValue(data->m_pitch);
m_loopStart->setEnabled(data->isLooped());
m_loopEnd->setEnabled(data->isLooped());
if (!path.empty()) {
m_path = UTF8ToQString(path);
m_showInBrowser->setDisabled(false);
} else {
m_path = QString();
m_showInBrowser->setDisabled(true);
}
m_enableUpdate = true;
}
void SampleControls::doFileWrite() {
if (m_enableFileWrite) {
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->patchSampleMetadata(node->id(),
node->m_obj.get());
}
}
void SampleControls::setFileWrite(bool w) {
m_enableFileWrite = w;
doFileWrite();
}
void SampleControls::loadData(bool reset) {
m_zoomSlider->setDisabled(false);
m_loopCheck->setDisabled(false);
m_loopStart->setDisabled(false);
m_loopEnd->setDisabled(false);
m_basePitch->setDisabled(false);
if (reset)
m_zoomSlider->setValue(0);
updateFileState();
}
void SampleControls::unloadData() {
m_zoomSlider->setDisabled(true);
m_loopCheck->setDisabled(true);
m_loopStart->setDisabled(true);
m_loopEnd->setDisabled(true);
m_basePitch->setDisabled(true);
m_makeOtherVersion->setText(tr("Nothing Loaded"));
m_makeOtherVersion->setDisabled(true);
m_showInBrowser->setDisabled(true);
}
SampleControls::SampleControls(QWidget* parent) : QFrame(parent) {
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(100);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
QPalette palette = QWidget::palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
QHBoxLayout* mainLayout = new QHBoxLayout;
QGridLayout* leftLayout = new QGridLayout;
leftLayout->addWidget(new QLabel(tr("Zoom")), 0, 0);
m_zoomSlider = new QSlider(Qt::Horizontal);
m_zoomSlider->setDisabled(true);
m_zoomSlider->setRange(0, 99);
connect(m_zoomSlider, qOverload<int>(&QSlider::valueChanged), this, &SampleControls::zoomSliderChanged);
leftLayout->addWidget(m_zoomSlider, 1, 0);
leftLayout->addWidget(new QLabel(tr("Loop")), 0, 1);
m_loopCheck = new QCheckBox;
m_loopCheck->setPalette(palette);
m_loopCheck->setDisabled(true);
connect(m_loopCheck, qOverload<int>(&QCheckBox::stateChanged), this, &SampleControls::loopStateChanged);
leftLayout->addWidget(m_loopCheck, 1, 1);
leftLayout->addWidget(new QLabel(tr("Start")), 0, 2);
m_loopStart = new QSpinBox;
m_loopStart->setPalette(palette);
m_loopStart->setDisabled(true);
connect(m_loopStart, qOverload<int>(&QSpinBox::valueChanged), this, &SampleControls::startValueChanged);
leftLayout->addWidget(m_loopStart, 1, 2);
leftLayout->addWidget(new QLabel(tr("End")), 0, 3);
m_loopEnd = new QSpinBox;
m_loopEnd->setPalette(palette);
m_loopEnd->setDisabled(true);
connect(m_loopEnd, qOverload<int>(&QSpinBox::valueChanged), this, &SampleControls::endValueChanged);
leftLayout->addWidget(m_loopEnd, 1, 3);
leftLayout->addWidget(new QLabel(tr("Base Pitch")), 0, 4);
m_basePitch = new QSpinBox;
m_basePitch->setPalette(palette);
m_basePitch->setDisabled(true);
m_basePitch->setMinimum(0);
m_basePitch->setMaximum(127);
connect(m_basePitch, qOverload<int>(&QSpinBox::valueChanged), this, &SampleControls::pitchValueChanged);
leftLayout->addWidget(m_basePitch, 1, 4);
leftLayout->setColumnMinimumWidth(0, 100);
leftLayout->setColumnMinimumWidth(1, 50);
leftLayout->setColumnMinimumWidth(2, 75);
leftLayout->setColumnMinimumWidth(3, 75);
leftLayout->setColumnMinimumWidth(4, 75);
leftLayout->setRowMinimumHeight(0, 22);
leftLayout->setRowMinimumHeight(1, 37);
leftLayout->setContentsMargins(10, 6, 0, 14);
QVBoxLayout* rightLayout = new QVBoxLayout;
m_makeOtherVersion = new QPushButton(tr("Nothing Loaded"));
m_makeOtherVersion->setMinimumWidth(250);
m_makeOtherVersion->setDisabled(true);
rightLayout->addWidget(m_makeOtherVersion);
m_showInBrowser = new QPushButton(ShowInGraphicalShellString());
m_showInBrowser->setMinimumWidth(250);
m_showInBrowser->setDisabled(true);
connect(m_showInBrowser, &QPushButton::clicked, this, &SampleControls::showInBrowser);
rightLayout->addWidget(m_showInBrowser);
mainLayout->addLayout(leftLayout);
mainLayout->addStretch(1);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
}
SampleControls::~SampleControls() = default;
bool SampleEditor::loadData(ProjectModel::SampleNode* node) {
m_controls->loadData(m_sampleView->loadData(node));
return true;
}
void SampleEditor::unloadData() {
m_sampleView->unloadData();
m_controls->unloadData();
}
ProjectModel::INode* SampleEditor::currentNode() const { return m_sampleView->currentNode(); }
const amuse::SoundMacro* SampleEditor::soundMacro() const { return m_sampleView->soundMacro(); }
void SampleEditor::setSamplePos(int pos) { m_sampleView->setSamplePos(pos); }
void SampleEditor::resizeEvent(QResizeEvent* ev) { m_sampleView->resetZoom(); }
SampleEditor::SampleEditor(QWidget* parent)
: EditorWidget(parent), m_scrollArea(new QScrollArea), m_sampleView(new SampleView), m_controls(new SampleControls) {
m_scrollArea->setWidget(m_sampleView);
m_scrollArea->setWidgetResizable(true);
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_scrollArea);
layout->addWidget(m_controls);
setLayout(layout);
}
SampleEditor::~SampleEditor() = default;

125
Editor/SampleEditor.hpp Normal file
View File

@ -0,0 +1,125 @@
#pragma once
#include <cstdint>
#include <utility>
#include <QFont>
#include <QWidget>
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <amuse/AudioGroupPool.hpp>
#include <amuse/AudioGroupSampleDirectory.hpp>
#include <amuse/Common.hpp>
class SampleEditor;
class QCheckBox;
class QPushButton;
class QScrollArea;
class QSlider;
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;
public:
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();
};
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);
~SampleEditor() override;
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;
};

1612
Editor/SongGroupEditor.cpp Normal file

File diff suppressed because it is too large Load Diff

324
Editor/SongGroupEditor.hpp Normal file
View File

@ -0,0 +1,324 @@
#pragma once
#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 <QTableView>
#include <QToolButton>
#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
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override;
public:
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();
};
class MIDIFileFieldWidget : public QWidget {
Q_OBJECT
QLineEdit m_le;
QPushButton m_button;
QFileDialog m_dialog;
public:
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);
signals:
void pathChanged();
};
class MIDIFileDelegate : public QStyledItemDelegate {
Q_OBJECT
QFileDialog m_fileDialogMid, m_fileDialogSng;
std::vector<uint8_t> m_exportData;
public:
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);
public slots:
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;
public:
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
~PageModel() override;
void loadData(ProjectModel::SongGroupNode* node);
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;
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;
public:
explicit SetupListModel(QObject* parent = Q_NULLPTR);
~SetupListModel() override;
void loadData(ProjectModel::SongGroupNode* node);
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;
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);
~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;
public:
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;
public:
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 {
public:
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
public:
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
};
class ColoredTabWidget : public QTabWidget {
Q_OBJECT
ColoredTabBar m_tabBar;
public:
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;
public:
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();
};
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);
~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() override;
};

645
Editor/SoundGroupEditor.cpp Normal file
View File

@ -0,0 +1,645 @@
#include "SoundGroupEditor.hpp"
#include "MainWindow.hpp"
class SFXDataChangeUndoCommand : public EditorUndoCommand {
amuse::SFXId m_sfx;
int m_column;
int m_undoVal, m_redoVal;
bool m_undid = false;
public:
explicit SFXDataChangeUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, amuse::SFXId sfx,
int column, int redoVal)
: EditorUndoCommand(node, text), m_sfx(sfx), m_column(column), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
auto& map = index.m_sfxEntries;
amuse::SFXGroupIndex::SFXEntry& entry = map[m_sfx];
switch (m_column) {
case 1:
entry.objId.id = m_undoVal;
break;
case 2:
entry.priority = m_undoVal;
break;
case 3:
entry.maxVoices = m_undoVal;
break;
case 4:
entry.defVel = m_undoVal;
break;
case 5:
entry.panning = m_undoVal;
break;
case 6:
entry.defKey = m_undoVal;
break;
default:
break;
}
EditorUndoCommand::undo();
}
void redo() override {
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
auto& map = index.m_sfxEntries;
amuse::SFXGroupIndex::SFXEntry& entry = map[m_sfx];
switch (m_column) {
case 1:
m_undoVal = entry.objId.id.id;
entry.objId.id = m_redoVal;
break;
case 2:
m_undoVal = entry.priority;
entry.priority = m_redoVal;
break;
case 3:
m_undoVal = entry.maxVoices;
entry.maxVoices = m_redoVal;
break;
case 4:
m_undoVal = entry.defVel;
entry.defVel = m_redoVal;
break;
case 5:
m_undoVal = entry.panning;
entry.panning = m_redoVal;
break;
case 6:
m_undoVal = entry.defKey;
entry.defKey = m_redoVal;
break;
default:
break;
}
if (m_undid)
EditorUndoCommand::redo();
}
};
class SFXNameChangeUndoCommand : public EditorUndoCommand {
amuse::SFXId m_sfx;
std::string m_undoVal, m_redoVal;
bool m_undid = false;
public:
explicit SFXNameChangeUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, amuse::SFXId sfx,
std::string_view redoVal)
: EditorUndoCommand(node, text), m_sfx(sfx), m_redoVal(redoVal) {}
void undo() override {
m_undid = true;
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
auto& map = index.m_sfxEntries;
amuse::SFXId::CurNameDB->rename(m_sfx, m_undoVal);
EditorUndoCommand::undo();
}
void redo() override {
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
auto& map = index.m_sfxEntries;
m_undoVal = amuse::SFXId::CurNameDB->resolveNameFromId(m_sfx);
amuse::SFXId::CurNameDB->rename(m_sfx, m_redoVal);
if (m_undid)
EditorUndoCommand::redo();
}
};
SFXObjectDelegate::SFXObjectDelegate(QObject* parent) : BaseObjectDelegate(parent) {}
SFXObjectDelegate::~SFXObjectDelegate() = default;
ProjectModel::INode* SFXObjectDelegate::getNode(const QAbstractItemModel* __model, const QModelIndex& index) const {
const SFXModel* model = static_cast<const SFXModel*>(__model);
auto entry = model->m_sorted[index.row()];
if (entry->second.objId.id.id == 0xffff)
return nullptr;
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
return group->pageObjectNodeOfId(entry->second.objId.id);
}
QWidget* SFXObjectDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option,
const QModelIndex& index) const {
const SFXModel* model = static_cast<const SFXModel*>(index.model());
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
EditorFieldPageObjectNode* cb = new EditorFieldPageObjectNode(group, parent);
connect(cb, &EditorFieldPageObjectNode::currentIndexChanged, this, &SFXObjectDelegate::objIndexChanged);
return cb;
}
void SFXObjectDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
int idx = 0;
if (ProjectModel::BasePoolObjectNode* node =
static_cast<ProjectModel::BasePoolObjectNode*>(getNode(index.model(), index)))
idx = g_MainWindow->projectModel()
->getPageObjectProxy()
->mapFromSource(g_MainWindow->projectModel()->index(node))
.row();
static_cast<EditorFieldPageObjectNode*>(editor)->setCurrentIndex(idx);
if (static_cast<EditorFieldPageObjectNode*>(editor)->shouldPopupOpen())
QApplication::postEvent(editor, new QEvent(QEvent::User));
}
void SFXObjectDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, const QModelIndex& index) const {
const SFXModel* model = static_cast<const SFXModel*>(m);
auto entry = model->m_sorted[index.row()];
ProjectModel::BasePoolObjectNode* node = static_cast<EditorFieldPageObjectNode*>(editor)->currentNode();
amuse::ObjectId id;
if (node)
id = node->id();
if (id == entry->second.objId.id) {
emit m->dataChanged(index, index);
return;
}
g_MainWindow->pushUndoCommand(new SFXDataChangeUndoCommand(
model->m_node.get(), tr("Change %1").arg(m->headerData(1, Qt::Horizontal).toString()), entry->first, 1, id.id));
emit m->dataChanged(index, index);
}
void SFXObjectDelegate::objIndexChanged() { emit commitData(static_cast<QWidget*>(sender())); }
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& SFXModel::_getMap() const {
return m_node->m_index->m_sfxEntries;
}
void SFXModel::_buildSortedList() {
m_sorted.clear();
if (!m_node)
return;
auto& map = _getMap();
m_sorted.reserve(map.size());
for (auto it = map.begin(); it != map.end(); ++it)
m_sorted.emplace_back(it);
std::sort(m_sorted.begin(), m_sorted.end());
}
QModelIndex SFXModel::_indexOfSFX(amuse::SFXId sfx) const {
auto search = std::lower_bound(m_sorted.cbegin(), m_sorted.cend(), sfx);
if (search == m_sorted.cend() || search->m_it->first != sfx)
return QModelIndex();
else
return createIndex(search - m_sorted.begin(), 0);
}
int SFXModel::_hypotheticalIndexOfSFX(const std::string& sfxName) const {
auto search = std::lower_bound(m_sorted.cbegin(), m_sorted.cend(), sfxName);
return search - m_sorted.begin();
}
void SFXModel::loadData(ProjectModel::SoundGroupNode* node) {
beginResetModel();
m_node = node;
_buildSortedList();
endResetModel();
}
void SFXModel::unloadData() {
beginResetModel();
m_node.reset();
m_sorted.clear();
endResetModel();
}
int SFXModel::rowCount(const QModelIndex& parent) const {
if (parent.isValid())
return 0;
if (!m_node)
return 0;
return int(m_sorted.size()) + 1;
}
int SFXModel::columnCount(const QModelIndex& parent) const {
if (parent.isValid())
return 0;
return 7;
}
QVariant SFXModel::data(const QModelIndex& index, int role) const {
if (!m_node)
return QVariant();
if (index.row() == m_sorted.size())
return QVariant();
auto entry = m_sorted[index.row()];
if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch (index.column()) {
case 0: {
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
return QString::fromUtf8(amuse::SFXId::CurNameDB->resolveNameFromId(entry->first.id).data());
}
case 1: {
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(m_node.get());
if (ProjectModel::BasePoolObjectNode* node = group->pageObjectNodeOfId(entry->second.objId.id))
return node->text();
return QVariant();
}
case 2:
return entry->second.priority;
case 3:
return entry->second.maxVoices;
case 4:
return entry->second.defVel;
case 5:
return entry->second.panning;
case 6:
return entry->second.defKey;
default:
break;
}
}
return QVariant();
}
bool SFXModel::setData(const QModelIndex& index, const QVariant& value, int role) {
if (!m_node || role != Qt::EditRole)
return false;
auto& map = _getMap();
auto entry = m_sorted[index.row()];
switch (index.column()) {
case 0: {
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
auto utf8key = value.toString().toUtf8();
std::unordered_map<std::string, amuse::ObjectId>::iterator idIt;
// TODO: Heterogeneous lookup when C++20 available
if ((idIt = amuse::SFXId::CurNameDB->m_stringToId.find(utf8key.data())) !=
amuse::SFXId::CurNameDB->m_stringToId.cend()) {
if (idIt->second == entry->first)
return false;
g_MainWindow->uiMessenger().critical(tr("SFX Conflict"),
tr("SFX %1 is already defined in project").arg(value.toString()));
return false;
}
int newIdx = _hypotheticalIndexOfSFX(utf8key.data());
bool moving = beginMoveRows(index.parent(), index.row(), index.row(), index.parent(), newIdx);
g_MainWindow->pushUndoCommand(
new SFXNameChangeUndoCommand(m_node.get(), tr("Change SFX Name"), entry->first, utf8key.data()));
_buildSortedList();
if (moving)
endMoveRows();
QModelIndex newIndex = _indexOfSFX(entry->first);
emit dataChanged(newIndex, newIndex, {Qt::DisplayRole, Qt::EditRole});
return true;
}
case 2:
if (entry->second.priority == value.toInt())
return false;
break;
case 3:
if (entry->second.maxVoices == value.toInt())
return false;
break;
case 4:
if (entry->second.defVel == value.toInt())
return false;
break;
case 5:
if (entry->second.panning == value.toInt())
return false;
break;
case 6:
if (entry->second.defKey == value.toInt())
return false;
break;
default:
return false;
}
g_MainWindow->pushUndoCommand(new SFXDataChangeUndoCommand(
m_node.get(), tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()), entry->first,
index.column(), value.toInt()));
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
return true;
}
QVariant SFXModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("SFX");
case 1:
return tr("Object");
case 2:
return tr("Priority");
case 3:
return tr("Max Voices");
case 4:
return tr("Velocity");
case 5:
return tr("Panning");
case 6:
return tr("Key");
default:
break;
}
}
return QVariant();
}
Qt::ItemFlags SFXModel::flags(const QModelIndex& index) const {
if (!index.isValid())
return Qt::NoItemFlags;
if (index.row() == m_sorted.size())
return Qt::NoItemFlags;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
class SFXRowUndoCommand : public EditorUndoCommand {
protected:
SFXTableView* m_view;
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> m_data;
bool m_undid = false;
void add() {
m_view->selectionModel()->clearSelection();
for (auto& p : m_data) {
int row = static_cast<SFXModel*>(m_view->model())->_insertRow(p);
m_view->setCurrentIndex(m_view->model()->index(row, 0));
}
}
void del() {
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it) {
*it = static_cast<SFXModel*>(m_view->model())->_removeRow(std::get<0>(*it));
}
}
void undo() override {
m_undid = true;
EditorUndoCommand::undo();
}
void redo() override {
if (m_undid)
EditorUndoCommand::redo();
}
public:
explicit SFXRowUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
};
class SFXRowAddUndoCommand : public SFXRowUndoCommand {
using base = SFXRowUndoCommand;
public:
explicit SFXRowAddUndoCommand(
ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
: SFXRowUndoCommand(node, text, view, std::move(data)) {}
void undo() override {
base::undo();
base::del();
}
void redo() override {
base::redo();
base::add();
}
};
class SFXRowDelUndoCommand : public SFXRowUndoCommand {
using base = SFXRowUndoCommand;
public:
explicit SFXRowDelUndoCommand(
ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
: SFXRowUndoCommand(node, text, view, std::move(data)) {}
void undo() override {
base::undo();
base::add();
}
void redo() override {
base::redo();
base::del();
}
};
int SFXModel::_insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data) {
if (!m_node)
return 0;
auto& map = _getMap();
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
amuse::SFXId::CurNameDB->registerPair(std::get<1>(data), std::get<0>(data));
int idx = _hypotheticalIndexOfSFX(std::get<1>(data));
beginInsertRows(QModelIndex(), idx, idx);
map.emplace(std::make_pair(std::get<0>(data), std::get<2>(data)));
_buildSortedList();
endInsertRows();
return idx;
}
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> SFXModel::_removeRow(amuse::SFXId sfx) {
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> ret;
if (!m_node)
return ret;
auto& map = _getMap();
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
int idx = _indexOfSFX(sfx).row();
beginRemoveRows(QModelIndex(), idx, idx);
std::get<0>(ret) = sfx;
std::get<1>(ret) = amuse::SFXId::CurNameDB->resolveNameFromId(sfx);
auto search = map.find(sfx);
if (search != map.cend()) {
std::get<2>(ret) = search->second;
amuse::SFXId::CurNameDB->remove(sfx);
map.erase(search);
}
_buildSortedList();
endRemoveRows();
return ret;
}
SFXModel::SFXModel(QObject* parent) : QAbstractTableModel(parent) {}
SFXModel::~SFXModel() = default;
void SFXTableView::deleteSelection() {
QModelIndexList list = selectionModel()->selectedRows();
if (list.isEmpty())
return;
std::sort(list.begin(), list.end(), [](const auto& a, const auto& b) { return a.row() < b.row(); });
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> data;
data.reserve(list.size());
for (QModelIndex idx : list) {
auto& entry = *static_cast<SFXModel*>(model())->m_sorted[idx.row()].m_it;
data.push_back({entry.first, {}, {}});
}
g_MainWindow->pushUndoCommand(new SFXRowDelUndoCommand(
static_cast<SFXModel*>(model())->m_node.get(),
data.size() > 1 ? tr("Delete SFX Entries") : tr("Delete SFX Entry"), this, std::move(data)));
}
void SFXTableView::setModel(QAbstractItemModel* model) {
QTableView::setModel(model);
horizontalHeader()->setMinimumSectionSize(75);
horizontalHeader()->resizeSection(0, 200);
horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
horizontalHeader()->resizeSection(2, 75);
horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
horizontalHeader()->resizeSection(3, 100);
horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed);
horizontalHeader()->resizeSection(4, 75);
horizontalHeader()->setSectionResizeMode(5, QHeaderView::Fixed);
horizontalHeader()->resizeSection(5, 75);
horizontalHeader()->setSectionResizeMode(6, QHeaderView::Fixed);
horizontalHeader()->resizeSection(6, 75);
}
SFXTableView::SFXTableView(QWidget* parent) : QTableView(parent) {
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setGridStyle(Qt::NoPen);
m_127Delegate.setItemEditorFactory(&m_127Factory);
m_255Delegate.setItemEditorFactory(&m_255Factory);
setItemDelegateForColumn(1, &m_sfxDelegate);
setItemDelegateForColumn(2, &m_255Delegate);
setItemDelegateForColumn(3, &m_255Delegate);
setItemDelegateForColumn(4, &m_127Delegate);
setItemDelegateForColumn(5, &m_127Delegate);
setItemDelegateForColumn(6, &m_127Delegate);
}
SFXTableView::~SFXTableView() = default;
void SFXPlayerWidget::clicked() {
if (!m_vox) {
m_vox = g_MainWindow->startSFX(m_groupId, m_sfxId);
if (m_vox) {
m_playAction.setText(tr("Stop"));
m_playAction.setIcon(QIcon(QStringLiteral(":/icons/IconStop.svg")));
}
} else {
stopped();
}
}
void SFXPlayerWidget::stopped() {
m_vox->keyOff();
m_vox.reset();
m_playAction.setText(tr("Play"));
m_playAction.setIcon(QIcon(QStringLiteral(":/icons/IconSoundMacro.svg")));
}
void SFXPlayerWidget::resizeEvent(QResizeEvent* event) {
m_button.setGeometry(event->size().width() - event->size().height(), 0, event->size().height(),
event->size().height());
}
void SFXPlayerWidget::mouseDoubleClickEvent(QMouseEvent* event) {
qobject_cast<QTableView*>(parentWidget()->parentWidget())->setIndexWidget(m_index, nullptr);
event->ignore();
}
void SFXPlayerWidget::mousePressEvent(QMouseEvent* event) {
if (event->button() == Qt::RightButton) {
QTableView* view = qobject_cast<QTableView*>(parentWidget()->parentWidget());
QAbstractItemDelegate* delegate = view->itemDelegateForColumn(1);
delegate->editorEvent(event, view->model(), {}, m_index);
}
event->ignore();
}
SFXPlayerWidget::~SFXPlayerWidget() {
if (m_vox)
m_vox->keyOff();
}
SFXPlayerWidget::SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id, QWidget* parent)
: QWidget(parent), m_playAction(tr("Play")), m_button(this), m_index(index), m_groupId(gid), m_sfxId(id) {
m_playAction.setIcon(QIcon(QStringLiteral(":/icons/IconSoundMacro.svg")));
m_button.setDefaultAction(&m_playAction);
connect(&m_playAction, &QAction::triggered, this, &SFXPlayerWidget::clicked);
}
bool SoundGroupEditor::loadData(ProjectModel::SoundGroupNode* node) {
m_sfxs.loadData(node);
return true;
}
void SoundGroupEditor::unloadData() { m_sfxs.unloadData(); }
ProjectModel::INode* SoundGroupEditor::currentNode() const { return m_sfxs.m_node.get(); }
void SoundGroupEditor::resizeEvent(QResizeEvent* ev) {
m_sfxTable->setGeometry(QRect({}, ev->size()));
m_addRemoveButtons.move(0, ev->size().height() - 32);
}
AmuseItemEditFlags SoundGroupEditor::itemEditFlags() const {
return (m_sfxTable->hasFocus() && !m_sfxTable->selectionModel()->selectedRows().isEmpty()) ? AmuseItemDelete
: AmuseItemNone;
}
void SoundGroupEditor::doAdd() {
g_MainWindow->projectModel()->setIdDatabases(m_sfxs.m_node.get());
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> data;
amuse::SFXId sfxId = amuse::SFXId::CurNameDB->generateId(amuse::NameDB::Type::SFX);
std::string sfxName = amuse::SFXId::CurNameDB->generateName(sfxId, amuse::NameDB::Type::SFX);
data.emplace_back(sfxId, sfxName, amuse::SFXGroupIndex::SFXEntry{});
g_MainWindow->pushUndoCommand(
new SFXRowAddUndoCommand(m_sfxs.m_node.get(), tr("Add SFX Entry"), m_sfxTable, std::move(data)));
}
void SoundGroupEditor::doSelectionChanged() {
m_addRemoveButtons.removeAction()->setDisabled(m_sfxTable->selectionModel()->selectedRows().isEmpty());
g_MainWindow->updateFocus();
}
void SoundGroupEditor::rowsInserted(const QModelIndex& parent, int first, int last) {
m_sfxTable->scrollTo(m_sfxTable->model()->index(first, 0));
sfxDataChanged();
}
void SoundGroupEditor::rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination,
int row) {
m_sfxTable->scrollTo(m_sfxTable->model()->index(row, 0));
sfxDataChanged();
}
void SoundGroupEditor::sfxDataChanged() {
int idx = 0;
for (const auto& p : m_sfxs.m_sorted) {
QModelIndex index = m_sfxs.index(idx, 1);
SFXPlayerWidget* w = qobject_cast<SFXPlayerWidget*>(m_sfxTable->indexWidget(index));
if (!w || w->sfxId() != p->first) {
SFXPlayerWidget* newW = new SFXPlayerWidget(index, m_sfxs.m_node->m_id, p->first, m_sfxTable->viewport());
m_sfxTable->setIndexWidget(index, newW);
}
++idx;
}
}
void SoundGroupEditor::itemDeleteAction() { m_sfxTable->deleteSelection(); }
SoundGroupEditor::SoundGroupEditor(QWidget* parent)
: EditorWidget(parent), m_sfxs(this), m_sfxTable(new SFXTableView(this)), m_addRemoveButtons(this) {
m_sfxTable->setModel(&m_sfxs);
connect(m_sfxTable->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&SoundGroupEditor::doSelectionChanged);
connect(&m_sfxs, &SFXModel::rowsInserted, this, &SoundGroupEditor::rowsInserted);
connect(&m_sfxs, &SFXModel::rowsRemoved, this, &SoundGroupEditor::sfxDataChanged);
connect(&m_sfxs, &SFXModel::dataChanged, this, &SoundGroupEditor::sfxDataChanged);
connect(&m_sfxs, &SFXModel::rowsMoved, this, &SoundGroupEditor::rowsMoved);
connect(&m_sfxs, &SFXModel::modelReset, this, &SoundGroupEditor::sfxDataChanged);
m_addRemoveButtons.addAction()->setToolTip(tr("Add new SFX entry"));
connect(m_addRemoveButtons.addAction(), &QAction::triggered, this, &SoundGroupEditor::doAdd);
m_addRemoveButtons.removeAction()->setToolTip(tr("Remove selected SFX entries"));
connect(m_addRemoveButtons.removeAction(), &QAction::triggered, this, &SoundGroupEditor::itemDeleteAction);
}
SoundGroupEditor::~SoundGroupEditor() = default;

146
Editor/SoundGroupEditor.hpp Normal file
View File

@ -0,0 +1,146 @@
#pragma once
#include <tuple>
#include <unordered_map>
#include <vector>
#include <QAction>
#include <QStyledItemDelegate>
#include <QTableView>
#include <QToolButton>
#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 override;
public:
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();
};
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);
~SFXModel() override;
void loadData(ProjectModel::SoundGroupNode* node);
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;
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;
public:
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;
public:
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();
};
class SoundGroupEditor : public EditorWidget {
Q_OBJECT
SFXModel m_sfxs;
SFXTableView* m_sfxTable;
AddRemoveButtons m_addRemoveButtons;
public:
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
~SoundGroupEditor() override;
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;
};

1070
Editor/SoundMacroEditor.cpp Normal file

File diff suppressed because it is too large Load Diff

200
Editor/SoundMacroEditor.hpp Normal file
View File

@ -0,0 +1,200 @@
#pragma once
#include <QLabel>
#include <QMouseEvent>
#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 QLayoutItem;
class QPropertyAnimation;
class QSplitter;
class QVBoxLayout;
class TargetButton : public QPushButton {
Q_OBJECT
public:
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;
signals:
void valueChanged(int);
public slots:
void targetPressed();
void updateMacroField();
public:
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;
private slots:
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);
public:
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();
private slots:
void animationDestroyed();
public:
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 = 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);
~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;
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);
~CatalogueItem() override;
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
QString getText() const { return m_label.text(); }
};
class SoundMacroCatalogue : public QTreeWidget {
Q_OBJECT
public:
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);
~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);
};

103
Editor/StatusBarWidget.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "StatusBarWidget.hpp"
#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);
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);
}
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) {
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::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;
}
}
}

View File

@ -0,0 +1,90 @@
#pragma once
#include <array>
#include <QLabel>
#include <QMouseEvent>
#include <QPushButton>
#include <QSlider>
#include <QStatusBar>
class StatusBarFocus;
class FXButton : public QPushButton {
Q_OBJECT
public:
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;
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);
~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);
};
class StatusBarFocus : public QObject {
Q_OBJECT
QString m_message;
public:
explicit StatusBarFocus(StatusBarWidget* statusWidget) : QObject(statusWidget) {}
~StatusBarFocus() override { exit(); }
void setMessage(const QString& message);
void enter();
void exit();
};

View File

@ -0,0 +1,976 @@
#include "StudioSetupWidget.hpp"
#include <QApplication>
#include <QPainter>
#include <QScrollBar>
#include <QStylePainter>
#include <amuse/EffectBase.hpp>
#include <amuse/EffectChorus.hpp>
#include <amuse/EffectDelay.hpp>
#include <amuse/EffectReverb.hpp>
#include <amuse/Studio.hpp>
#include <amuse/Submix.hpp>
using namespace std::literals;
struct EffectIntrospection {
struct Field {
enum class Type {
Invalid,
UInt32,
UInt32x8,
Float,
};
Type m_tp{};
std::string_view m_name;
float m_min{};
float m_max{};
float m_default{};
};
amuse::EffectType m_tp;
std::string_view m_name;
std::string_view m_description;
std::array<Field, 7> m_fields;
};
namespace {
constexpr EffectIntrospection ReverbStdIntrospective = {
amuse::EffectType::ReverbStd,
"Reverb Std"sv,
"Standard Reverb"sv,
{
{{EffectIntrospection::Field::Type::Float, "Coloration"sv, 0.f, 1.f, 0.f},
{EffectIntrospection::Field::Type::Float, "Mix"sv, 0.f, 1.f, 0.f},
{EffectIntrospection::Field::Type::Float, "Time"sv, 0.01f, 10.f, 0.01f},
{EffectIntrospection::Field::Type::Float, "Damping"sv, 0.f, 1.f, 0.f},
{EffectIntrospection::Field::Type::Float, "Pre Delay"sv, 0.f, 0.1f, 0.f}},
},
};
using ReverbStdGetFunc = float (amuse::EffectReverbStd::*)() const;
using ReverbStdSetFunc = void (amuse::EffectReverbStd::*)(float);
constexpr std::array<ReverbStdGetFunc, 5> ReverbStdGetters{
&amuse::EffectReverbStd::getColoration, &amuse::EffectReverbStd::getMix, &amuse::EffectReverbStd::getTime,
&amuse::EffectReverbStd::getDamping, &amuse::EffectReverbStd::getPreDelay,
};
constexpr std::array<ReverbStdSetFunc, 5> ReverbStdSetters{
&amuse::EffectReverbStd::setColoration, &amuse::EffectReverbStd::setMix, &amuse::EffectReverbStd::setTime,
&amuse::EffectReverbStd::setDamping, &amuse::EffectReverbStd::setPreDelay,
};
constexpr EffectIntrospection ReverbHiIntrospective = {
amuse::EffectType::ReverbHi,
"Reverb Hi"sv,
"High Reverb"sv,
{
{{EffectIntrospection::Field::Type::Float, "Coloration"sv, 0.f, 1.f, 0.f},
{EffectIntrospection::Field::Type::Float, "Mix"sv, 0.f, 1.f, 0.f},
{EffectIntrospection::Field::Type::Float, "Time"sv, 0.01f, 10.f, 0.01f},
{EffectIntrospection::Field::Type::Float, "Damping"sv, 0.f, 1.f, 0.f},
{EffectIntrospection::Field::Type::Float, "Pre Delay"sv, 0.f, 0.1f, 0.f},
{EffectIntrospection::Field::Type::Float, "Crosstalk"sv, 0.f, 1.f, 0.f}},
},
};
using ReverbHiGetFunc = float (amuse::EffectReverbHi::*)() const;
using ReverbHiSetFunc = void (amuse::EffectReverbHi::*)(float);
constexpr std::array<ReverbHiGetFunc, 6> ReverbHiGetters{
&amuse::EffectReverbHi::getColoration, &amuse::EffectReverbHi::getMix, &amuse::EffectReverbHi::getTime,
&amuse::EffectReverbHi::getDamping, &amuse::EffectReverbHi::getPreDelay, &amuse::EffectReverbHi::getCrosstalk,
};
constexpr std::array<ReverbHiSetFunc, 6> ReverbHiSetters{
&amuse::EffectReverbHi::setColoration, &amuse::EffectReverbHi::setMix, &amuse::EffectReverbHi::setTime,
&amuse::EffectReverbHi::setDamping, &amuse::EffectReverbHi::setPreDelay, &amuse::EffectReverbHi::setCrosstalk,
};
constexpr EffectIntrospection DelayIntrospective = {
amuse::EffectType::Delay,
"Delay"sv,
"Delay"sv,
{{
{EffectIntrospection::Field::Type::UInt32x8, "Delay"sv, 10, 5000, 10},
{EffectIntrospection::Field::Type::UInt32x8, "Feedback"sv, 0, 100, 0},
{EffectIntrospection::Field::Type::UInt32x8, "Output"sv, 0, 100, 0},
}},
};
using DelayGetFunc = uint32_t (amuse::EffectDelay::*)(int) const;
using DelaySetFunc = void (amuse::EffectDelay::*)(int, uint32_t);
constexpr std::array<DelayGetFunc, 3> DelayGetters{
&amuse::EffectDelay::getChanDelay,
&amuse::EffectDelay::getChanFeedback,
&amuse::EffectDelay::getChanOutput,
};
constexpr std::array<DelaySetFunc, 3> DelaySetters{
&amuse::EffectDelay::setChanDelay,
&amuse::EffectDelay::setChanFeedback,
&amuse::EffectDelay::setChanOutput,
};
constexpr EffectIntrospection ChorusIntrospective = {
amuse::EffectType::Chorus,
"Chorus"sv,
"Chorus"sv,
{{
{EffectIntrospection::Field::Type::UInt32, "Base Delay"sv, 5, 15, 5},
{EffectIntrospection::Field::Type::UInt32, "Variation"sv, 0, 5, 0},
{EffectIntrospection::Field::Type::UInt32, "Period"sv, 500, 10000, 500},
}},
};
using ChorusGetFunc = uint32_t (amuse::EffectChorus::*)() const;
using ChorusSetFunc = void (amuse::EffectChorus::*)(uint32_t);
constexpr std::array<ChorusGetFunc, 3> ChorusGetters{
&amuse::EffectChorus::getBaseDelay,
&amuse::EffectChorus::getVariation,
&amuse::EffectChorus::getPeriod,
};
constexpr std::array<ChorusSetFunc, 3> ChorusSetters{
&amuse::EffectChorus::setBaseDelay,
&amuse::EffectChorus::setVariation,
&amuse::EffectChorus::setPeriod,
};
constexpr const EffectIntrospection* GetEffectIntrospection(amuse::EffectType type) {
switch (type) {
case amuse::EffectType::ReverbStd:
return &ReverbStdIntrospective;
case amuse::EffectType::ReverbHi:
return &ReverbHiIntrospective;
case amuse::EffectType::Delay:
return &DelayIntrospective;
case amuse::EffectType::Chorus:
return &ChorusIntrospective;
default:
return nullptr;
}
}
template <typename T>
T GetEffectParm(const amuse::EffectBaseTypeless* effect, int idx, int chanIdx) {
switch (effect->Isa()) {
case amuse::EffectType::ReverbStd:
return (static_cast<const amuse::EffectReverbStdImp<float>*>(effect)->*ReverbStdGetters[idx])();
case amuse::EffectType::ReverbHi:
return (static_cast<const amuse::EffectReverbHiImp<float>*>(effect)->*ReverbHiGetters[idx])();
case amuse::EffectType::Delay:
return (static_cast<const amuse::EffectDelayImp<float>*>(effect)->*DelayGetters[idx])(chanIdx);
case amuse::EffectType::Chorus:
return (static_cast<const amuse::EffectChorusImp<float>*>(effect)->*ChorusGetters[idx])();
default:
return 0.f;
}
}
template <typename T>
void SetEffectParm(amuse::EffectBaseTypeless* effect, int idx, int chanIdx, T val) {
switch (effect->Isa()) {
case amuse::EffectType::ReverbStd:
(static_cast<amuse::EffectReverbStdImp<float>*>(effect)->*ReverbStdSetters[idx])(val);
break;
case amuse::EffectType::ReverbHi:
(static_cast<amuse::EffectReverbHiImp<float>*>(effect)->*ReverbHiSetters[idx])(val);
break;
case amuse::EffectType::Delay:
(static_cast<amuse::EffectDelayImp<float>*>(effect)->*DelaySetters[idx])(chanIdx, val);
break;
case amuse::EffectType::Chorus:
(static_cast<amuse::EffectChorusImp<float>*>(effect)->*ChorusSetters[idx])(val);
break;
default:
break;
}
}
constexpr int NumChanNames = 8;
constexpr std::array<const char*, NumChanNames> ChanNames{
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Left"), QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Right"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Left"), QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Right"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Center"), QT_TRANSLATE_NOOP("Uint32X8Popup", "LFE"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Left"), QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Right"),
};
constexpr std::array<const char*, 4> EffectStrings{
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus"),
};
constexpr std::array<const char*, 4> EffectDocStrings{
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus"),
};
} // Anonymous namespace
Uint32X8Popup::Uint32X8Popup(int min, int max, QWidget* parent) : QFrame(parent, Qt::Popup) {
setAttribute(Qt::WA_WindowPropagation);
setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
Uint32X8Button* combo = static_cast<Uint32X8Button*>(parent);
QStyleOptionComboBox opt = combo->comboStyleOption();
setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
QGridLayout* layout = new QGridLayout;
for (int i = 0; i < NumChanNames; ++i) {
layout->addWidget(new QLabel(tr(ChanNames[i])), i, 0);
FieldSlider* slider = new FieldSlider(this);
m_sliders[i] = slider;
slider->setToolTip(QStringLiteral("[%1,%2]").arg(min).arg(max));
slider->setProperty("chanIdx", i);
slider->setRange(min, max);
connect(slider, qOverload<int>(&FieldSlider::valueChanged), this, &Uint32X8Popup::doValueChanged);
layout->addWidget(slider, i, 1);
}
setLayout(layout);
}
Uint32X8Popup::~Uint32X8Popup() = default;
void Uint32X8Popup::setValue(int chanIdx, int val) { m_sliders[chanIdx]->setValue(val); }
void Uint32X8Popup::doValueChanged(int val) {
FieldSlider* slider = static_cast<FieldSlider*>(sender());
int chanIdx = slider->property("chanIdx").toInt();
emit valueChanged(chanIdx, val);
}
Uint32X8Button::Uint32X8Button(int min, int max, QWidget* parent)
: QPushButton(parent), m_popup(new Uint32X8Popup(min, max, this)) {
connect(this, &Uint32X8Button::pressed, this, &Uint32X8Button::onPressed);
}
Uint32X8Button::~Uint32X8Button() = default;
void Uint32X8Button::paintEvent(QPaintEvent*) {
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt = comboStyleOption();
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
QStyleOptionComboBox Uint32X8Button::comboStyleOption() const {
QStyleOptionComboBox opt;
opt.initFrom(this);
opt.editable = false;
opt.frame = true;
opt.currentText = tr("Channels");
return opt;
}
void Uint32X8Button::onPressed() {
QPoint pt = parentWidget()->mapToGlobal(pos());
m_popup->move(pt.x(), pt.y());
m_popup->show();
}
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type)
: QWidget(parent)
, m_titleLabel(this)
, m_deleteButton(this)
, m_effect(effect)
, m_introspection(GetEffectIntrospection(type)) {
QFont titleFont = m_titleLabel.font();
titleFont.setWeight(QFont::Bold);
m_titleLabel.setFont(titleFont);
m_titleLabel.setForegroundRole(QPalette::Window);
m_titleLabel.setContentsMargins(46, 0, 0, 0);
m_titleLabel.setFixedHeight(20);
m_numberText.setTextOption(QTextOption(Qt::AlignRight));
m_numberText.setTextWidth(25);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_numberFont.setWeight(QFont::Bold);
m_numberFont.setStyleHint(QFont::Monospace);
m_numberFont.setPointSize(m_numberFont.pointSize() * 2);
setContentsMargins(QMargins());
setFixedHeight(100);
QVBoxLayout* mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(QMargins());
mainLayout->setSpacing(0);
QHBoxLayout* headLayout = new QHBoxLayout;
headLayout->setContentsMargins(QMargins());
headLayout->setSpacing(0);
headLayout->addWidget(&m_titleLabel);
m_deleteButton.setVisible(true);
connect(&m_deleteButton, &ListingDeleteButton::clicked, this, &EffectWidget::deleteClicked);
headLayout->addWidget(&m_deleteButton);
mainLayout->addLayout(headLayout);
mainLayout->addSpacing(8);
QGridLayout* layout = new QGridLayout;
layout->setSpacing(6);
layout->setContentsMargins(64, 0, 12, 12);
if (m_introspection) {
m_titleLabel.setText(tr(m_introspection->m_name.data()));
m_titleLabel.setToolTip(tr(m_introspection->m_description.data()));
for (int f = 0; f < int(m_introspection->m_fields.size()); ++f) {
const EffectIntrospection::Field& field = m_introspection->m_fields[f];
if (!field.m_name.empty()) {
QString fieldName = tr(field.m_name.data());
layout->addWidget(new QLabel(fieldName, this), 0, f);
switch (field.m_tp) {
case EffectIntrospection::Field::Type::UInt32: {
FieldSlider* sb = new FieldSlider(this);
sb->setProperty("fieldIndex", f);
sb->setRange(int(field.m_min), int(field.m_max));
sb->setToolTip(QStringLiteral("[%1,%2]").arg(int(field.m_min)).arg(int(field.m_max)));
sb->setValue(GetEffectParm<uint32_t>(m_effect, f, 0));
connect(sb, qOverload<int>(&FieldSlider::valueChanged), this, qOverload<int>(&EffectWidget::numChanged));
layout->addWidget(sb, 1, f);
break;
}
case EffectIntrospection::Field::Type::UInt32x8: {
Uint32X8Button* sb = new Uint32X8Button(int(field.m_min), int(field.m_max), this);
sb->popup()->setProperty("fieldIndex", f);
for (int i = 0; i < Uint32X8Popup::NumSliders; ++i) {
sb->popup()->setValue(i, GetEffectParm<uint32_t>(m_effect, f, i));
}
connect(sb->popup(), &Uint32X8Popup::valueChanged, this, &EffectWidget::chanNumChanged);
layout->addWidget(sb, 1, f);
break;
}
case EffectIntrospection::Field::Type::Float: {
FieldDoubleSlider* sb = new FieldDoubleSlider(this);
sb->setProperty("fieldIndex", f);
sb->setRange(field.m_min, field.m_max);
sb->setToolTip(QStringLiteral("[%1,%2]").arg(field.m_min).arg(field.m_max));
sb->setValue(GetEffectParm<float>(m_effect, f, 0));
connect(sb, qOverload<double>(&FieldDoubleSlider::valueChanged), this,
qOverload<double>(&EffectWidget::numChanged));
layout->addWidget(sb, 1, f);
break;
}
default:
break;
}
}
}
}
mainLayout->addLayout(layout);
layout->setRowMinimumHeight(0, 22);
layout->setRowMinimumHeight(1, 22);
setLayout(mainLayout);
}
EffectWidget::~EffectWidget() = default;
void EffectWidget::paintEvent(QPaintEvent* event) {
/* Rounded frame */
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
const std::array<QPoint, 6> points{{
{1, 20},
{1, 99},
{width() - 1, 99},
{width() - 1, 1},
{20, 1},
{1, 20},
}};
painter.setBrush(palette().brush(QPalette::Window));
painter.drawPolygon(points.data(), int(points.size()));
painter.setPen(QPen(QColor(127, 127, 127), 2.0));
painter.drawPolyline(points.data(), int(points.size()));
const std::array<QPoint, 7> headPoints{{
{1, 20},
{1, 55},
{35, 20},
{width() - 1, 20},
{width() - 1, 1},
{20, 1},
{1, 20},
}};
painter.setBrush(QColor(127, 127, 127));
painter.drawPolygon(headPoints.data(), int(headPoints.size()));
painter.drawRect(17, 51, 32, 32);
QTransform rotate;
rotate.rotate(-45.0).translate(-15, 8);
painter.setTransform(rotate);
painter.setFont(m_numberFont);
painter.setPen(palette().color(QPalette::Window));
painter.drawStaticText(0, 0, m_numberText);
}
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* cmd) : EffectWidget(parent, cmd, cmd->Isa()) {}
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectType type) : EffectWidget(parent, nullptr, type) {}
void EffectWidget::numChanged(int value) {
SetEffectParm<uint32_t>(m_effect, sender()->property("fieldIndex").toInt(), 0, value);
}
void EffectWidget::numChanged(double value) {
SetEffectParm<float>(m_effect, sender()->property("fieldIndex").toInt(), 0, value);
}
void EffectWidget::chanNumChanged(int chanIdx, int value) {
SetEffectParm<uint32_t>(m_effect, sender()->property("fieldIndex").toInt(), chanIdx, value);
}
void EffectWidget::deleteClicked() {
if (m_index != -1)
if (EffectListing* listing = getParent())
listing->deleteEffect(m_index);
}
EffectListing* EffectWidget::getParent() const { return qobject_cast<EffectListing*>(parentWidget()->parentWidget()); }
void EffectWidget::setIndex(int index) {
m_index = index;
m_numberText.setText(QString::number(index));
update();
}
void EffectWidgetContainer::animateOpen() {
int newHeight = 200 + parentWidget()->layout()->spacing();
m_animation = new QPropertyAnimation(this, "minimumHeight");
m_animation->setDuration(abs(minimumHeight() - newHeight) * 4);
m_animation->setStartValue(minimumHeight());
m_animation->setEndValue(newHeight);
m_animation->setEasingCurve(QEasingCurve::InOutExpo);
connect(m_animation, &QPropertyAnimation::valueChanged, parentWidget(), qOverload<>(&QWidget::update));
connect(m_animation, &QPropertyAnimation::destroyed, this, &EffectWidgetContainer::animationDestroyed);
m_animation->start(QAbstractAnimation::DeleteWhenStopped);
}
void EffectWidgetContainer::animateClosed() {
m_animation = new QPropertyAnimation(this, "minimumHeight");
m_animation->setDuration(abs(minimumHeight() - 100) * 4);
m_animation->setStartValue(minimumHeight());
m_animation->setEndValue(100);
m_animation->setEasingCurve(QEasingCurve::InOutExpo);
connect(m_animation, &QPropertyAnimation::valueChanged, parentWidget(), qOverload<>(&QWidget::update));
connect(m_animation, &QPropertyAnimation::destroyed, this, &EffectWidgetContainer::animationDestroyed);
m_animation->start(QAbstractAnimation::DeleteWhenStopped);
}
void EffectWidgetContainer::snapOpen() {
if (m_animation)
m_animation->stop();
setMinimumHeight(200 + parentWidget()->layout()->spacing());
}
void EffectWidgetContainer::snapClosed() {
if (m_animation)
m_animation->stop();
setMinimumHeight(100);
}
void EffectWidgetContainer::animationDestroyed() { m_animation = nullptr; }
template <class... _Args>
EffectWidgetContainer::EffectWidgetContainer(QWidget* parent, _Args&&... args)
: QWidget(parent), m_effectWidget(new EffectWidget(this, std::forward<_Args>(args)...)) {
setMinimumHeight(100);
setContentsMargins(QMargins());
QVBoxLayout* outerLayout = new QVBoxLayout;
outerLayout->setAlignment(Qt::AlignBottom);
outerLayout->setContentsMargins(QMargins());
outerLayout->setSpacing(0);
outerLayout->addWidget(m_effectWidget);
setLayout(outerLayout);
}
EffectWidgetContainer::~EffectWidgetContainer() = default;
void EffectListing::startAutoscroll(QWidget* source, QMouseEvent* event, int delta) {
if (m_autoscrollTimer == -1)
m_autoscrollTimer = startTimer(50);
m_autoscrollDelta = delta;
m_autoscrollSource = source;
if (m_autoscrollEvent != nullptr) {
delete m_autoscrollEvent;
m_autoscrollEvent = nullptr;
}
m_autoscrollEvent = event->clone();
}
void EffectListing::stopAutoscroll() {
if (m_autoscrollTimer != -1) {
killTimer(m_autoscrollTimer);
m_autoscrollTimer = -1;
}
m_autoscrollDelta = 0;
if (m_autoscrollEvent != nullptr) {
delete m_autoscrollEvent;
m_autoscrollEvent = nullptr;
}
m_autoscrollSource = nullptr;
}
void EffectListing::timerEvent(QTimerEvent* event) {
if (QScrollArea* scrollArea = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
QScrollBar* bar = scrollArea->verticalScrollBar();
int oldValue = bar->value();
bar->setValue(oldValue + m_autoscrollDelta);
int valueDelta = bar->value() - oldValue;
if (valueDelta != 0) {
if (m_autoscrollSource)
QApplication::sendEvent(m_autoscrollSource, m_autoscrollEvent);
update();
}
}
}
bool EffectListing::beginDrag(EffectWidget* widget) {
int origIdx = m_layout->indexOf(widget->parentWidget());
if (origIdx < 0 || origIdx >= m_layout->count() - 1)
return false;
if (origIdx < m_layout->count() - 1) {
// Animate next item open
m_dragOpenIdx = origIdx;
if (EffectWidgetContainer* nextItem = qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(origIdx + 1)->widget()))
nextItem->snapOpen();
} else {
m_dragOpenIdx = -1;
}
m_origIdx = origIdx;
m_dragItem = m_layout->takeAt(origIdx);
m_dragItem->widget()->raise();
return true;
}
void EffectListing::endDrag() {
int insertIdx;
if (m_dragOpenIdx != -1) {
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
prevItem->snapClosed();
insertIdx = m_dragOpenIdx;
m_dragOpenIdx = -1;
} else {
insertIdx = m_layout->count() - 1;
}
if (m_prevDragOpen) {
m_prevDragOpen->snapClosed();
m_prevDragOpen = nullptr;
}
if (m_origIdx != insertIdx)
std::swap(m_submix->getEffectStack()[m_origIdx], m_submix->getEffectStack()[insertIdx]);
m_layout->insertItem(insertIdx, m_dragItem);
m_dragItem = nullptr;
stopAutoscroll();
reindex();
}
void EffectListing::cancelDrag() {
if (m_dragOpenIdx != -1) {
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
prevItem->snapClosed();
m_dragOpenIdx = -1;
}
m_layout->insertItem(m_origIdx, m_dragItem);
if (m_prevDragOpen) {
m_prevDragOpen->snapClosed();
m_prevDragOpen = nullptr;
}
m_dragItem = nullptr;
stopAutoscroll();
}
void EffectListing::_moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event) {
QRect scrollVpRect = parentWidget()->parentWidget()->rect();
QPoint scrollVpPoint = mapTo(parentWidget()->parentWidget(), pt);
if (scrollVpRect.bottom() - scrollVpPoint.y() < 50)
startAutoscroll(source, event, 10); // Scroll Down
else if (scrollVpRect.top() - scrollVpPoint.y() > -50)
startAutoscroll(source, event, -10);
else
stopAutoscroll();
hoverIdx = std::max(0, std::min(hoverIdx, m_layout->count() - 1));
if (hoverIdx != m_dragOpenIdx) {
if (m_dragOpenIdx != -1)
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget())) {
m_prevDragOpen = prevItem;
prevItem->animateClosed();
}
if (EffectWidgetContainer* nextItem = qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(hoverIdx)->widget()))
nextItem->animateOpen();
m_dragOpenIdx = hoverIdx;
}
update();
}
void EffectListing::moveDrag(EffectWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event) {
EffectWidgetContainer* container = static_cast<EffectWidgetContainer*>(widget->parentWidget());
int pitch = 100 + m_layout->spacing();
_moveDrag((container->pos().y() - m_layout->contentsMargins().top() + pitch / 2) / pitch, pt, source, event);
}
int EffectListing::moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event) {
int pitch = 100 + m_layout->spacing();
_moveDrag(pt.y() / pitch, pt, source, event);
return m_dragOpenIdx;
}
void EffectListing::insertDragout() {
if (m_dragOpenIdx != -1) {
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget())) {
m_prevDragOpen = prevItem;
prevItem->animateClosed();
}
m_dragOpenIdx = -1;
}
stopAutoscroll();
}
void EffectListing::insert(amuse::EffectType type, const QString& text) {
int insertIdx;
if (m_dragOpenIdx != -1) {
if (EffectWidgetContainer* prevItem =
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
prevItem->snapClosed();
insertIdx = m_dragOpenIdx;
m_dragOpenIdx = -1;
} else {
insertIdx = m_layout->count() - 1;
}
if (m_prevDragOpen) {
m_prevDragOpen->snapClosed();
m_prevDragOpen = nullptr;
}
std::unique_ptr<amuse::EffectBaseTypeless> newEffect;
switch (type) {
case amuse::EffectType::ReverbStd:
newEffect = m_submix->_makeEffect<amuse::EffectReverbStd>(amuse::EffectReverbStdInfo{});
break;
case amuse::EffectType::ReverbHi:
newEffect = m_submix->_makeEffect<amuse::EffectReverbHi>(amuse::EffectReverbHiInfo{});
break;
case amuse::EffectType::Delay:
newEffect = m_submix->_makeEffect<amuse::EffectDelay>(amuse::EffectDelayInfo{});
break;
case amuse::EffectType::Chorus:
newEffect = m_submix->_makeEffect<amuse::EffectChorus>(amuse::EffectChorusInfo{});
break;
default:
break;
}
auto it = m_submix->getEffectStack().insert(m_submix->getEffectStack().begin() + insertIdx, std::move(newEffect));
m_layout->insertWidget(insertIdx, new EffectWidgetContainer(this, it->get()));
stopAutoscroll();
reindex();
}
void EffectListing::deleteEffect(int index) {
QLayoutItem* item = m_layout->takeAt(index);
m_submix->getEffectStack().erase(m_submix->getEffectStack().begin() + index);
item->widget()->deleteLater();
delete item;
reindex();
}
void EffectListing::reindex() {
for (int i = 0; i < m_layout->count() - 1; ++i)
if (EffectWidgetContainer* item = qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(i)->widget()))
item->m_effectWidget->setIndex(i);
}
void EffectListing::clear() {
while (m_layout->count() > 2) {
QLayoutItem* item = m_layout->takeAt(0);
item->widget()->deleteLater();
delete item;
}
}
bool EffectListing::loadData(amuse::Submix* submix) {
m_submix = submix;
clear();
int i = 0;
for (auto& effect : submix->getEffectStack())
m_layout->insertWidget(i++, new EffectWidgetContainer(this, effect.get()));
reindex();
update();
return true;
}
void EffectListing::unloadData() {
m_submix = nullptr;
clear();
reindex();
update();
}
EffectListing::EffectListing(QWidget* parent) : QWidget(parent), m_layout(new QVBoxLayout) {
m_layout->addStretch();
setLayout(m_layout);
reindex();
}
EffectListing::~EffectListing() = default;
EffectCatalogueItem::EffectCatalogueItem(amuse::EffectType type, const QString& name, const QString& doc,
QWidget* parent)
: QWidget(parent), m_type(type), m_iconLab(this), m_label(name, this) {
QHBoxLayout* layout = new QHBoxLayout;
QString iconPath = QStringLiteral(":/commands/%1.svg").arg(name);
if (QFile(iconPath).exists())
m_iconLab.setPixmap(QIcon(iconPath).pixmap(32, 32));
else
m_iconLab.setPixmap(QIcon(QStringLiteral(":/icons/IconOpen.svg")).pixmap(32, 32));
layout->addWidget(&m_iconLab);
layout->addWidget(&m_label);
layout->addStretch();
layout->setContentsMargins(QMargins());
setLayout(layout);
setToolTip(doc);
}
EffectCatalogueItem::EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent)
: QWidget(parent), m_type(other.getType()) {
QHBoxLayout* layout = new QHBoxLayout;
QHBoxLayout* oldLayout = static_cast<QHBoxLayout*>(other.layout());
m_iconLab.setPixmap(static_cast<QLabel*>(oldLayout->itemAt(0)->widget())->pixmap(Qt::ReturnByValue));
layout->addWidget(&m_iconLab);
m_label.setText(static_cast<QLabel*>(oldLayout->itemAt(1)->widget())->text());
layout->addWidget(&m_label);
layout->addStretch();
layout->setContentsMargins(QMargins());
setLayout(layout);
}
EffectCatalogueItem::~EffectCatalogueItem() = default;
EffectCatalogue::EffectCatalogue(QWidget* parent) : QTreeWidget(parent) {
setSelectionMode(QAbstractItemView::NoSelection);
setColumnCount(1);
setHeaderHidden(true);
for (int i = 1; i < int(amuse::EffectType::EffectTypeMAX); ++i) {
QTreeWidgetItem* item = new QTreeWidgetItem(this);
setItemWidget(
item, 0,
new EffectCatalogueItem(amuse::EffectType(i), tr(EffectStrings[i - 1]), tr(EffectDocStrings[i - 1]), this));
}
}
void EffectCatalogue::mousePressEvent(QMouseEvent* event) {
QTreeWidget::mousePressEvent(event);
event->ignore();
}
void EffectCatalogue::mouseReleaseEvent(QMouseEvent* event) {
QTreeWidget::mouseReleaseEvent(event);
event->ignore();
}
void EffectCatalogue::mouseMoveEvent(QMouseEvent* event) {
StudioSetupWidget* editor = qobject_cast<StudioSetupWidget*>(parentWidget()->parentWidget()->parentWidget());
if (!editor || !editor->m_draggedItem)
QTreeWidget::mouseMoveEvent(event);
event->ignore();
}
EffectListing* StudioSetupWidget::getCurrentListing() const {
return static_cast<EffectListing*>(static_cast<QScrollArea*>(m_tabs->currentWidget())->widget());
}
void StudioSetupWidget::beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt) {
if (widget->getParent()->beginDrag(widget)) {
m_draggedPt = pt;
m_draggedCmd = widget;
}
}
void StudioSetupWidget::beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt) {
m_draggedPt = pt;
m_draggedItem = new EffectCatalogueItem(*item, this);
m_draggedItem->setGeometry(item->geometry());
m_draggedItem->move(eventPt - m_draggedPt);
m_draggedItem->raise();
m_draggedItem->show();
}
void StudioSetupWidget::mousePressEvent(QMouseEvent* event) {
if (m_catalogue->geometry().contains(event->pos())) {
QPoint fromParent1 = m_catalogue->mapFrom(this, event->pos());
QWidget* ch = m_catalogue->childAt(fromParent1);
if (ch) {
EffectCatalogueItem* child = nullptr;
while (ch && !(child = qobject_cast<EffectCatalogueItem*>(ch)))
ch = ch->parentWidget();
if (child) {
QPoint fromParent2 = child->mapFrom(m_catalogue, fromParent1);
beginCatalogueDrag(child, event->pos(), fromParent2);
}
}
} else {
EffectListing* listing = getCurrentListing();
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos())) {
QPoint fromParent1 = listing->mapFrom(this, event->pos());
QWidget* ch = listing->childAt(fromParent1);
if (ch) {
EffectWidget* child = nullptr;
while (ch && !(child = qobject_cast<EffectWidget*>(ch)))
ch = ch->parentWidget();
if (child) {
QPoint fromParent2 = child->mapFrom(listing, fromParent1);
beginCommandDrag(child, event->pos(), fromParent2);
}
}
}
}
}
void StudioSetupWidget::mouseReleaseEvent(QMouseEvent* event) {
EffectListing* listing = getCurrentListing();
if (m_draggedItem) {
amuse::EffectType type = m_draggedItem->getType();
QString text = m_draggedItem->getText();
m_draggedItem->deleteLater();
m_draggedItem = nullptr;
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos())) {
if (m_dragInsertIdx != -1)
listing->insert(type, text);
else
listing->insertDragout();
} else {
listing->insertDragout();
}
m_dragInsertIdx = -1;
} else if (m_draggedCmd) {
listing->endDrag();
m_draggedCmd = nullptr;
}
}
void StudioSetupWidget::mouseMoveEvent(QMouseEvent* event) {
EffectListing* listing = getCurrentListing();
if (m_draggedItem) {
m_draggedItem->move(event->pos() - m_draggedPt);
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos())) {
m_dragInsertIdx = listing->moveInsertDrag(listing->mapFrom(this, event->pos()), this, event);
} else if (m_dragInsertIdx != -1) {
listing->insertDragout();
m_dragInsertIdx = -1;
}
m_catalogue->update();
update();
} else if (m_draggedCmd) {
QPoint listingPt = listing->mapFrom(this, event->pos());
EffectWidgetContainer* container = static_cast<EffectWidgetContainer*>(m_draggedCmd->parentWidget());
container->move(container->x(), listingPt.y() - m_draggedPt.y());
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos()))
listing->moveDrag(m_draggedCmd, listingPt, this, event);
listing->update();
update();
}
}
void StudioSetupWidget::keyPressEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Escape) {
EffectListing* listing = getCurrentListing();
if (m_draggedItem) {
m_draggedItem->deleteLater();
m_draggedItem = nullptr;
listing->insertDragout();
} else if (m_draggedCmd) {
listing->cancelDrag();
m_draggedCmd = nullptr;
}
}
}
void StudioSetupWidget::hideEvent(QHideEvent* event) { emit hidden(); }
void StudioSetupWidget::showEvent(QShowEvent* event) { emit shown(); }
void StudioSetupWidget::updateWindowPosition() {
QWidget* parent = parentWidget();
move(parent->width() / 2 - width() / 2 + parent->x(), parent->height() / 2 - height() / 2 + parent->y());
}
void StudioSetupWidget::catalogueDoubleClicked(QTreeWidgetItem* item, int column) {
if (EffectCatalogueItem* cItem = qobject_cast<EffectCatalogueItem*>(m_catalogue->itemWidget(item, column))) {
amuse::EffectType type = cItem->getType();
if (type != amuse::EffectType::Invalid) {
EffectListing* listing = getCurrentListing();
listing->insert(type, cItem->getText());
}
}
}
bool StudioSetupWidget::loadData(amuse::Studio* studio) {
m_listing[0]->loadData(&studio->getAuxA());
m_listing[1]->loadData(&studio->getAuxB());
return true;
}
void StudioSetupWidget::unloadData() {
m_listing[0]->unloadData();
m_listing[1]->unloadData();
}
StudioSetupWidget::StudioSetupWidget(QWidget* parent)
: QWidget(parent, Qt::Tool), m_splitter(new QSplitter), m_tabs(new QTabWidget), m_catalogue(new EffectCatalogue) {
setWindowTitle(tr("Studio Setup"));
setGeometry(0, 0, 900, 450);
std::array<QScrollArea*, 2> scrollAreas{};
for (size_t i = 0; i < m_listing.size(); ++i) {
m_listing[i] = new EffectListing;
QScrollArea* listingScroll = new QScrollArea;
scrollAreas[i] = listingScroll;
listingScroll->setWidget(m_listing[i]);
listingScroll->setWidgetResizable(true);
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(1);
sizePolicy.setVerticalStretch(0);
listingScroll->setSizePolicy(sizePolicy);
listingScroll->setMinimumWidth(350);
m_splitter->addWidget(listingScroll);
}
m_tabs->addTab(scrollAreas[0], tr("Aux A"));
m_tabs->addTab(scrollAreas[1], tr("Aux B"));
m_splitter->addWidget(m_tabs);
{
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
m_catalogue->setSizePolicy(sizePolicy);
m_catalogue->setMinimumWidth(150);
m_catalogue->setGeometry(0, 0, 215, 0);
m_catalogue->setMaximumWidth(300);
m_splitter->addWidget(m_catalogue);
}
connect(m_catalogue, &EffectCatalogue::itemDoubleClicked, this, &StudioSetupWidget::catalogueDoubleClicked);
m_splitter->setCollapsible(0, false);
QGridLayout* layout = new QGridLayout;
layout->setContentsMargins(QMargins());
layout->addWidget(m_splitter);
setLayout(layout);
}
StudioSetupWidget::~StudioSetupWidget() = default;

View File

@ -0,0 +1,215 @@
#pragma once
#include <array>
#include <string>
#include <QLabel>
#include <QLayoutItem>
#include <QMouseEvent>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QScrollArea>
#include <QSplitter>
#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;
class Uint32X8Popup : public QFrame {
Q_OBJECT
public:
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);
signals:
void valueChanged(int chanIdx, int val);
private:
std::array<FieldSlider*, NumSliders> m_sliders {};
};
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 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);
~EffectWidget() override;
public:
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 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:
template <class... _Args>
EffectWidgetContainer(QWidget* parent, _Args&&... args);
~EffectWidgetContainer() override;
};
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 EffectListing(QWidget* parent = Q_NULLPTR);
~EffectListing() override;
bool loadData(amuse::Submix* submix);
void unloadData();
void timerEvent(QTimerEvent* event) override;
};
class EffectCatalogueItem : public QWidget {
Q_OBJECT
amuse::EffectType m_type;
QLabel m_iconLab;
QLabel m_label;
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);
~EffectCatalogueItem() override;
amuse::EffectType getType() const { return m_type; }
QString getText() const { return m_label.text(); }
};
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);
signals:
void hidden();
void shown();
};

98
Editor/main.cpp Normal file
View File

@ -0,0 +1,98 @@
#include <cstdint>
#include <QApplication>
#include <QStyleFactory>
#include <QTranslator>
#include "MainWindow.hpp"
#include "SongGroupEditor.hpp"
#include <QResource>
#include <QCommandLineParser>
#include <logvisor/logvisor.hpp>
using namespace std::literals;
#ifdef __APPLE__
void MacOSSetDarkAppearance();
#endif
extern "C" const uint8_t MAINICON_QT[];
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;
QPixmap pm;
pm.loadFromData(ptr, size);
ret.addPixmap(pm);
ptr += size;
}
return ret;
}
MainWindow* g_MainWindow = nullptr;
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(QStringLiteral("Fusion"))));
QApplication a(argc, argv);
QApplication::setWindowIcon(MakeAppIcon());
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);
#ifdef __APPLE__
MacOSSetDarkAppearance();
#endif
logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions();
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();
QCommandLineParser parser;
parser.process(a);
QStringList args = parser.positionalArguments();
if (!args.empty())
w.openProject(args.back());
return a.exec();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -0,0 +1,17 @@
add_executable(amuse-mkqticon mkqticon.c)
target_link_libraries(amuse-mkqticon ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
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-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)
endmacro()

View File

@ -0,0 +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-gui
Terminal=false
Type=Application
Categories=Audio

View File

@ -0,0 +1,71 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
static const int DIMS[] =
{
16,
32,
48,
64,
128,
256,
0
};
int main(int argc, char* argv[])
{
if (argc < 2)
{
fprintf(stderr, "Usage: makeqticon <out.bin>\n");
return 1;
}
FILE* ofp = fopen(argv[1], "wb");
if (!ofp)
{
fprintf(stderr, "'%s' is not able to be opened for writing as a regular file\n", argv[1]);
return 1;
}
char command[2048];
for (const int* d = DIMS ; *d != 0 ; ++d)
{
printf("Rendering main icon @%dx%d\n", *d, *d);
fflush(stdout);
snprintf(command, 2048, "%dx%d/apps/amuse-gui.png", *d, *d);
FILE* fp = fopen(command, "rb");
if (!fp)
{
fprintf(stderr, "unable to open '%s' for reading\n", command);
fclose(ofp);
return 1;
}
fseek(fp, 0, SEEK_END);
uint32_t size = (uint32_t)ftell(fp);
fseek(fp, 0, SEEK_SET);
fwrite(&size, 1, 4, ofp);
uint8_t buf[1024];
while (size > 0)
{
long thisSize = size;
if (thisSize > 1024)
thisSize = 1024;
fread(buf, 1, (size_t)thisSize, fp);
fwrite(buf, 1, (size_t)thisSize, ofp);
size -= thisSize;
}
fclose(fp);
}
fclose(ofp);
return 0;
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>amuse-gui</string>
<key>CFBundleGetInfoString</key>
<string>Version BETA</string>
<key>CFBundleShortVersionString</key>
<string>BETA</string>
<key>NSHumanReadableCopyright</key>
<string>2015-2018 Antidote / Jackoalan</string>
<key>CFBundleIconFile</key>
<string>mainicon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.axiodl.Amuse</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Amuse</string>
<key>CFBundleVersion</key>
<string>BETA</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

Binary file not shown.

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<description>Amuse</description>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View File

@ -0,0 +1,33 @@
#include "winver.h"
#define IDI_ICON1 101
IDI_ICON1 ICON DISCARDABLE "mainicon.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGS 0x0L
FILEFLAGSMASK 0x3fL
FILEOS 0x00040004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "CompanyName", "Antidote / Jackoalan"
VALUE "FileDescription", "Amuse"
VALUE "FileVersion", "BETA"
VALUE "LegalCopyright", "Copyright (C) 2015-2018 Antidote / Jackoalan"
VALUE "InternalName", "amuse"
VALUE "OriginalFilename", "amuse-gui.exe"
VALUE "ProductName", "Amuse"
VALUE "ProductVersion", "BETA"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0, 1200
END
END

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -0,0 +1,579 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="247.54231"
height="247.54233"
viewBox="0 0 261.98227 261.98229"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="FaceGrey.svg"
inkscape:export-filename="/Users/jacko/Desktop/amuse-logo/face1024.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008">
<defs
id="defs4">
<linearGradient
id="linearGradient4345">
<stop
id="stop4353"
offset="0"
style="stop-color:#858585;stop-opacity:1;" />
<stop
style="stop-color:#4f4f4f;stop-opacity:1;"
offset="0.27903292"
id="stop4351" />
<stop
id="stop4349"
offset="0.56446373"
style="stop-color:#606060;stop-opacity:1;" />
<stop
id="stop4347"
offset="1"
style="stop-color:#484848;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4315">
<stop
id="stop4323"
offset="0"
style="stop-color:#414141;stop-opacity:1;" />
<stop
id="stop4321"
offset="0.43553627"
style="stop-color:#4e4e4e;stop-opacity:1;" />
<stop
style="stop-color:#7c7c7c;stop-opacity:1;"
offset="0.72096705"
id="stop4319" />
<stop
id="stop4317"
offset="1"
style="stop-color:#b1b1b1;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4346">
<stop
style="stop-color:#cdcdcd;stop-opacity:1;"
offset="0"
id="stop4348" />
<stop
id="stop4350"
offset="0.5"
style="stop-color:#8f8f8f;stop-opacity:1;" />
<stop
style="stop-color:#4e4e4e;stop-opacity:1;"
offset="0.50999999"
id="stop4352" />
<stop
style="stop-color:#414141;stop-opacity:1;"
offset="1"
id="stop4354" />
</linearGradient>
<linearGradient
id="linearGradient4287">
<stop
id="stop4336"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
style="stop-color:#b9b9b9;stop-opacity:1;"
offset="0.5"
id="stop4338" />
<stop
id="stop4340"
offset="0.50999999"
style="stop-color:#7b7b7b;stop-opacity:1;" />
<stop
id="stop4342"
offset="1"
style="stop-color:#888888;stop-opacity:1;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4369">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4371" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop4373" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4354"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4356"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4358"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4375"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4369"
id="linearGradient4377"
gradientUnits="userSpaceOnUse"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668"
gradientTransform="matrix(0,3.7795276,-3.7795276,0,0.00629291,0)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4380"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,3.7795276,-3.7795276,0,0.00629291,0)"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4369"
id="linearGradient4186"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4345"
id="linearGradient4332"
x1="61.876694"
y1="33.663357"
x2="20.055458"
y2="244.05054"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4345"
id="linearGradient4184"
gradientUnits="userSpaceOnUse"
x1="5.9291153"
y1="-7.2909441"
x2="5.9291153"
y2="1029.2085" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4315"
id="linearGradient4187"
gradientUnits="userSpaceOnUse"
x1="-2.135401"
y1="373.35422"
x2="-2.135401"
y2="640.49878" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4359"
gradientUnits="userSpaceOnUse"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4380-5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,1.0065636,-1.0065636,0,-0.88913176,-0.88914365)"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4526"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,3.7795276,-3.7795276,0,0.00629291,0)"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4528"
gradientUnits="userSpaceOnUse"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4530"
gradientUnits="userSpaceOnUse"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4532"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4534"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4536"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4552"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,3.7795276,-3.7795276,0,0.00629291,0)"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4554"
gradientUnits="userSpaceOnUse"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4556"
gradientUnits="userSpaceOnUse"
x1="5.4296875"
y1="135.4668"
x2="265.5"
y2="135.4668" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4558"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4560"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4287"
id="linearGradient4562"
gradientUnits="userSpaceOnUse"
x1="66.356667"
y1="-135.465"
x2="204.57664"
y2="-135.465" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#3a3a3a"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.00392157"
inkscape:pageshadow="2"
inkscape:zoom="2.5293854"
inkscape:cx="57.159723"
inkscape:cy="115.45029"
inkscape:document-units="mm"
inkscape:current-layer="layer3"
showgrid="false"
units="px"
inkscape:window-width="1555"
inkscape:window-height="1280"
inkscape:window-x="1890"
inkscape:window-y="286"
inkscape:window-maximized="0"
scale-x="4"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="source"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-4.3711946,-30.440026)"
style="display:none"
sodipodi:insensitive="true">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="53.794159"
y="-74.699028"
id="text4141"
transform="rotate(90)"><tspan
sodipodi:role="line"
id="tspan4143"
x="53.794159"
y="-74.699028"
style="font-size:176.3888855px;line-height:1.25;letter-spacing:-26.45833206px;stroke-width:0.26458332px">:D</tspan></text>
<circle
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:8.24636078;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4147"
cx="135.465"
cy="161.53331"
r="92.200752" />
<circle
r="107.47308"
cy="161.53331"
cx="135.465"
id="circle4173"
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.88776207;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<circle
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.47896814;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle4175"
cx="135.465"
cy="161.53331"
r="119.55315" />
<circle
r="129.68999"
cy="161.53331"
cx="135.465"
id="circle4177"
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.68900001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="path"
style="display:inline;opacity:0.76700003"
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g
id="g4360"
style="fill:url(#linearGradient4375);fill-opacity:1"
transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)">
<g
id="text4141-2"
aria-label=":D"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4359);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="rotate(90)">
<path
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4380);fill-opacity:1;stroke-width:0.99999994px"
d="M 512,147.94141 C 311.12038,147.94141 147.93945,311.12036 147.93945,512 147.93945,712.87962 311.12038,876.06055 512,876.06055 712.87966,876.06055 876.05859,712.87962 876.05859,512 876.05859,311.12036 712.87966,147.94141 512,147.94141 Z m 0,31.16601 c 184.03556,0 332.89258,148.85702 332.89258,332.89258 0,184.03552 -148.85702,332.89453 -332.89258,332.89453 -184.03553,0 -332.89453,-148.85901 -332.89453,-332.89453 0,-184.03556 148.859,-332.89258 332.89453,-332.89258 z m -129.66602,71.68946 c -15.55555,0 -28.44574,5.11176 -38.66796,15.33398 -10.22223,10.22222 -15.33204,22.88889 -15.33204,38 0,15.55555 5.10981,28.44379 15.33204,38.66602 10.22222,10.22222 23.11241,15.33398 38.66796,15.33398 15.11111,0 27.77777,-5.11176 38,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 -10.66668,-10.22222 -23.33334,-15.33398 -38,-15.33398 z m 240.66602,0 c -15.11111,0 -27.99933,5.11176 -38.66602,15.33398 -10.66665,10.22222 -16,22.88889 -16,38 0,15.55555 5.33335,28.44379 16,38.66602 10.66669,10.22222 23.55491,15.33398 38.66602,15.33398 14.66666,0 27.11175,-5.11176 37.33398,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 C 650.11175,255.90864 637.66666,250.79688 623,250.79688 Z M 282.33398,459.86914 v 105.33398 c 0,58.66666 16.88824,108 50.66602,148 33.77778,40.00001 94.22288,60 181.33398,60 86.66666,0 146.22157,-19.77843 178.66602,-59.33398 32.44445,-39.55555 48.66602,-91.55555 48.66602,-156 v -98 z m 46.66602,58 h 365.33398 v 54 c 0,25.77779 -4.44509,48.66797 -13.33398,68.66797 -8.44445,20 -25.99936,36.66668 -52.66602,50 -26.22221,13.77779 -64.22221,20.66601 -114,20.66601 -68.88889,0 -117.11242,-13.33331 -144.66796,-40 C 342.5549,644.98091 329,612.09266 329,572.53711 Z"
transform="matrix(0,-0.26458333,0.26458333,0,0,0.001665)"
id="path4247"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssssssssssssssscsssssssscscscsssssccccsccscsc" />
</g>
</g>
<g
id="g4348"
aria-label=":D"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;display:none;fill:url(#linearGradient4358);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="rotate(90,-110.10081,-119.91599)">
<path
id="path4350"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4354);fill-opacity:1;stroke-width:0.26458332px"
d="m 66.356665,-164.83375 q 0,-5.82083 4.056944,-9.87778 4.056945,-4.23333 10.054167,-4.23333 6.173611,0 10.230555,4.23333 4.056944,4.05695 4.056944,9.87778 0,5.99722 -4.056944,10.23056 -4.056944,4.23333 -10.230555,4.23333 -5.997222,0 -10.054167,-4.23333 -4.056944,-4.23334 -4.056944,-10.23056 z m 0,63.67639 q 0,-5.82083 4.056944,-10.05417 4.056945,-4.23333 10.054167,-4.23333 6.173611,0 10.230555,4.23333 4.056944,4.05695 4.056944,10.05417 0,6.17361 -4.056944,10.230555 -4.056944,4.056944 -10.230555,4.056944 -5.997222,0 -10.054167,-4.056944 -4.056944,-4.056945 -4.056944,-10.230555 z"
inkscape:connector-curvature="0" />
<path
id="path4352"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4356);fill-opacity:1;stroke-width:0.26458332px"
d="m 204.57664,-136.08236 q 0,34.57222 -15.875,47.977777 -15.875,13.405555 -39.15833,13.405555 H 121.67387 V -196.23097 h 25.92916 q 25.57639,0 41.275,12.87639 15.69861,12.87639 15.69861,47.27222 z m -16.40416,0 q 0,-19.75556 -5.46806,-30.1625 -5.29166,-10.58333 -13.22916,-13.93472 -7.9375,-3.52778 -18.16806,-3.52778 h -14.2875 v 96.66111 h 14.46389 q 15.69861,0 26.10555,-10.759722 10.58334,-10.936108 10.58334,-38.276388 z"
inkscape:connector-curvature="0" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="path 1"
style="display:inline;opacity:0.24734982"
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<path
inkscape:connector-curvature="0"
id="path4439"
d="m 135.46501,4.5782177 c -72.283329,0 -130.8889765,58.6051203 -130.8889765,130.8884523 0,72.28333 58.6056475,130.88899 130.8889765,130.88899 72.28333,0 130.88845,-58.60566 130.88845,-130.88899 0,-72.283332 -58.60512,-130.8884523 -130.88845,-130.8884523 z m 0,0.6938883 c 71.90852,0 130.19455,58.286041 130.19455,130.194564 0,71.90852 -58.28603,130.19456 -130.19455,130.19456 C 63.55649,265.66123 5.2699219,207.37519 5.2699216,135.46667 5.2699216,63.558147 63.55649,5.272106 135.46501,5.272106 Z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:176.3888855px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:-26.45833206px;word-spacing:0px;display:inline;opacity:0.75199998;fill:url(#linearGradient4380-5);fill-opacity:1;stroke:none;stroke-width:0.26631993px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="ssssssssss" />
</g>
<g
style="display:inline;opacity:0.36749118"
inkscape:label="path 2"
id="g4512"
inkscape:groupmode="layer"
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g
transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)"
style="fill:url(#linearGradient4530);fill-opacity:1"
id="g4514">
<g
transform="rotate(90)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4528);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
aria-label=":D"
id="g4516">
<path
sodipodi:nodetypes="ssssssssss"
inkscape:connector-curvature="0"
id="path4518"
transform="matrix(0,-0.26458333,0.26458333,0,0,0.001665)"
d="M 512,55.460938 C 259.91593,55.460938 55.458984,259.9159 55.458984,512 55.458984,764.08406 259.91593,968.54102 512,968.54102 764.0841,968.54102 968.53906,764.08406 968.53906,512 968.53906,259.9159 764.0841,55.460938 512,55.460938 Z m 0,9.367187 C 759.02055,64.828125 959.17188,264.97946 959.17188,512 959.17188,759.02051 759.02055,959.17383 512,959.17383 264.97949,959.17383 64.826172,759.02051 64.826172,512 64.826172,264.97946 264.97949,64.828125 512,64.828125 Z"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4526);fill-opacity:1;stroke-width:0.99999994px" />
</g>
</g>
<g
transform="rotate(90,-110.10081,-119.91599)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;display:none;fill:url(#linearGradient4536);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
aria-label=":D"
id="g4520">
<path
inkscape:connector-curvature="0"
d="m 66.356665,-164.83375 q 0,-5.82083 4.056944,-9.87778 4.056945,-4.23333 10.054167,-4.23333 6.173611,0 10.230555,4.23333 4.056944,4.05695 4.056944,9.87778 0,5.99722 -4.056944,10.23056 -4.056944,4.23333 -10.230555,4.23333 -5.997222,0 -10.054167,-4.23333 -4.056944,-4.23334 -4.056944,-10.23056 z m 0,63.67639 q 0,-5.82083 4.056944,-10.05417 4.056945,-4.23333 10.054167,-4.23333 6.173611,0 10.230555,4.23333 4.056944,4.05695 4.056944,10.05417 0,6.17361 -4.056944,10.230555 -4.056944,4.056944 -10.230555,4.056944 -5.997222,0 -10.054167,-4.056944 -4.056944,-4.056945 -4.056944,-10.230555 z"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4532);fill-opacity:1;stroke-width:0.26458332px"
id="path4522" />
<path
inkscape:connector-curvature="0"
d="m 204.57664,-136.08236 q 0,34.57222 -15.875,47.977777 -15.875,13.405555 -39.15833,13.405555 H 121.67387 V -196.23097 h 25.92916 q 25.57639,0 41.275,12.87639 15.69861,12.87639 15.69861,47.27222 z m -16.40416,0 q 0,-19.75556 -5.46806,-30.1625 -5.29166,-10.58333 -13.22916,-13.93472 -7.9375,-3.52778 -18.16806,-3.52778 h -14.2875 v 96.66111 h 14.46389 q 15.69861,0 26.10555,-10.759722 10.58334,-10.936108 10.58334,-38.276388 z"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4534);fill-opacity:1;stroke-width:0.26458332px"
id="path4524" />
</g>
</g>
<g
style="display:inline;opacity:0.6501767"
inkscape:label="path 3"
id="g4538"
inkscape:groupmode="layer"
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g
transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)"
style="fill:url(#linearGradient4556);fill-opacity:1"
id="g4540">
<g
transform="rotate(90)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4554);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
aria-label=":D"
id="g4542">
<path
sodipodi:nodetypes="ssssssssss"
inkscape:connector-curvature="0"
id="path4544"
transform="matrix(0,-0.26458333,0.26458333,0,0,0.001665)"
d="M 512,96.5625 C 282.67144,96.5625 96.568359,282.67142 96.568359,512 96.568359,741.32854 282.67144,927.43164 512,927.43164 741.32858,927.43164 927.4375,741.32854 927.4375,512 927.4375,282.67142 741.32858,96.5625 512,96.5625 Z m 0,18.47656 C 731.34481,115.03906 908.96094,292.6552 908.96094,512 908.96094,731.34477 731.34481,908.96094 512,908.96094 292.65523,908.96094 115.03906,731.34477 115.03906,512 115.03906,292.6552 292.65523,115.03906 512,115.03906 Z"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4552);fill-opacity:1;stroke-width:0.99999994px" />
</g>
</g>
<g
transform="rotate(90,-110.10081,-119.91599)"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;display:none;fill:url(#linearGradient4562);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
aria-label=":D"
id="g4546">
<path
inkscape:connector-curvature="0"
d="m 66.356665,-164.83375 q 0,-5.82083 4.056944,-9.87778 4.056945,-4.23333 10.054167,-4.23333 6.173611,0 10.230555,4.23333 4.056944,4.05695 4.056944,9.87778 0,5.99722 -4.056944,10.23056 -4.056944,4.23333 -10.230555,4.23333 -5.997222,0 -10.054167,-4.23333 -4.056944,-4.23334 -4.056944,-10.23056 z m 0,63.67639 q 0,-5.82083 4.056944,-10.05417 4.056945,-4.23333 10.054167,-4.23333 6.173611,0 10.230555,4.23333 4.056944,4.05695 4.056944,10.05417 0,6.17361 -4.056944,10.230555 -4.056944,4.056944 -10.230555,4.056944 -5.997222,0 -10.054167,-4.056944 -4.056944,-4.056945 -4.056944,-10.230555 z"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4558);fill-opacity:1;stroke-width:0.26458332px"
id="path4548" />
<path
inkscape:connector-curvature="0"
d="m 204.57664,-136.08236 q 0,34.57222 -15.875,47.977777 -15.875,13.405555 -39.15833,13.405555 H 121.67387 V -196.23097 h 25.92916 q 25.57639,0 41.275,12.87639 15.69861,12.87639 15.69861,47.27222 z m -16.40416,0 q 0,-19.75556 -5.46806,-30.1625 -5.29166,-10.58333 -13.22916,-13.93472 -7.9375,-3.52778 -18.16806,-3.52778 h -14.2875 v 96.66111 h 14.46389 q 15.69861,0 26.10555,-10.759722 10.58334,-10.936108 10.58334,-38.276388 z"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4560);fill-opacity:1;stroke-width:0.26458332px"
id="path4550" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="light"
style="display:inline;opacity:1"
transform="translate(-4.3711946,-4.3733763)">
<g
id="g4360-5"
style="display:inline;fill:url(#linearGradient4332);fill-opacity:1"
transform="matrix(1.002648,0,0,1.002648,-1.0727108,-1.0727166)">
<g
id="text4141-2-9"
aria-label=":D"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4187);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="rotate(90)">
<path
inkscape:connector-curvature="0"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4184);fill-opacity:1;stroke-width:0.99999994px"
d="M 512,20.529297 C 240.58464,20.529297 20.527344,240.58461 20.527344,512 c 0,271.41536 220.057296,491.4727 491.472656,491.4727 271.4154,0 491.4707,-220.05734 491.4707,-491.4727 C 1003.4707,240.58461 783.4154,20.529297 512,20.529297 Z m 0,2.605469 c 270.00802,0 488.8652,218.857204 488.8652,488.865234 0,270.00802 -218.85718,488.8652 -488.8652,488.8652 C 241.992,1000.8652 23.132813,782.00802 23.132812,512 23.132812,241.99197 241.992,23.134766 512,23.134766 Z m 0,32.326172 C 259.91593,55.460938 55.458984,259.9159 55.458984,512 55.458984,764.08406 259.91593,968.54102 512,968.54102 764.0841,968.54102 968.53906,764.08406 968.53906,512 968.53906,259.9159 764.0841,55.460938 512,55.460938 Z m 0,9.367187 C 759.02055,64.828125 959.17188,264.97946 959.17188,512 959.17188,759.02051 759.02055,959.17383 512,959.17383 264.97949,959.17383 64.826172,759.02051 64.826172,512 64.826172,264.97946 264.97949,64.828125 512,64.828125 Z M 512,96.5625 C 282.67144,96.5625 96.568359,282.67142 96.568359,512 96.568359,741.32854 282.67144,927.43164 512,927.43164 741.32858,927.43164 927.4375,741.32854 927.4375,512 927.4375,282.67142 741.32858,96.5625 512,96.5625 Z m 0,18.47656 C 731.34481,115.03906 908.96094,292.6552 908.96094,512 908.96094,731.34477 731.34481,908.96094 512,908.96094 292.65523,908.96094 115.03906,731.34477 115.03906,512 115.03906,292.6552 292.65523,115.03906 512,115.03906 Z m 0,32.90235 C 311.12038,147.94141 147.93945,311.12036 147.93945,512 147.93945,712.87962 311.12038,876.06055 512,876.06055 712.87966,876.06055 876.05859,712.87962 876.05859,512 876.05859,311.12036 712.87966,147.94141 512,147.94141 Z m 0,31.16601 c 184.03556,0 332.89258,148.85702 332.89258,332.89258 0,184.03552 -148.85702,332.89453 -332.89258,332.89453 -184.03553,0 -332.89453,-148.85901 -332.89453,-332.89453 0,-184.03556 148.859,-332.89258 332.89453,-332.89258 z m -129.66602,71.68946 c -15.55555,0 -28.44574,5.11176 -38.66796,15.33398 -10.22223,10.22222 -15.33204,22.88889 -15.33204,38 0,15.55555 5.10981,28.44379 15.33204,38.66602 10.22222,10.22222 23.11241,15.33398 38.66796,15.33398 15.11111,0 27.77777,-5.11176 38,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 -10.66668,-10.22222 -23.33334,-15.33398 -38,-15.33398 z m 240.66602,0 c -15.11111,0 -27.99933,5.11176 -38.66602,15.33398 -10.66665,10.22222 -16,22.88889 -16,38 0,15.55555 5.33335,28.44379 16,38.66602 10.66669,10.22222 23.55491,15.33398 38.66602,15.33398 14.66666,0 27.11175,-5.11176 37.33398,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 C 650.11175,255.90864 637.66666,250.79688 623,250.79688 Z M 282.33398,459.86914 v 105.33398 c 0,58.66666 16.88824,108 50.66602,148 33.77778,40.00001 94.22288,60 181.33398,60 86.66666,0 146.22157,-19.77843 178.66602,-59.33398 32.44445,-39.55555 48.66602,-91.55555 48.66602,-156 v -98 z m 46.66602,58 h 365.33398 v 54 c 0,25.77779 -4.44509,48.66797 -13.33398,68.66797 -8.44445,20 -25.99936,36.66668 -52.66602,50 -26.22221,13.77779 -64.22221,20.66601 -114,20.66601 -68.88889,0 -117.11242,-13.33331 -144.66796,-40 C 342.5549,644.98091 329,612.09266 329,572.53711 Z"
transform="matrix(0,-0.26458333,0.26458333,0,0,0.001665)"
id="path4247-2" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916668"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconA.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="42.15"
inkscape:cx="10.765381"
inkscape:cy="12.799167"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="10"
showguides="false"
objecttolerance="17">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<g
aria-label="A"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.33670712px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.37563241"
id="text1395">
<path
d="m 0.04443603,296.98746 0.05869366,-0.30814 h 0.13939743 q 0.13939744,0 0.24211134,-0.044 0.1027139,-0.044 0.19809109,-0.16875 0.1027139,-0.12472 0.24211133,-0.3595 l 2.55317412,-4.358 h 0.5722631 l 0.6969872,4.54142 q 0.036684,0.22744 0.124724,0.30814 0.095377,0.0807 0.3081417,0.0807 h 0.095377 l -0.058694,0.30814 H 3.2212302 l 0.058694,-0.30814 h 0.1687442 q 0.2347747,0 0.3815088,-0.0954 0.1467341,-0.10272 0.1467341,-0.2788 0,-0.0587 -0.00734,-0.12472 0,-0.0734 -0.00734,-0.12472 l -0.117381,-0.82171 H 1.9226331 l -0.4475392,0.77036 q -0.1393974,0.25678 -0.1393974,0.41085 0,0.26413 0.4108556,0.26413 h 0.1687442 l -0.058694,0.30814 z m 2.87598917,-3.47027 -0.7850276,1.34996 h 1.6580958 l -0.1834177,-1.28392 q -0.04402,-0.32282 -0.080704,-0.60895 -0.029347,-0.28613 -0.036684,-0.55025 -0.1173873,0.26412 -0.2494481,0.50623 -0.124724,0.24211 -0.3228144,0.58693 z"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif Italic';fill:#ffffff;fill-opacity:1;stroke-width:1.37563241"
id="path1397"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconADSR.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="7.2902772"
inkscape:cy="8.0995259"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="267"
inkscape:window-y="1022"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.26458333,296.7354 1.0583334,293.47246 1.5875,294.35415 H 2.9104167 L 3.96875,296.7354"
id="path1834"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 6.3499998 6.3500004"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconAdd.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="111.23937"
inkscape:cx="14.81887"
inkscape:cy="16.006269"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="4"
showguides="false"
showborder="true"
objecttolerance="13">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-290.64999)">
<path
style="fill:#66ff00;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0,292.76666 h 2.1166667 v -2.11667 h 2.1166667 v 2.11667 h 2.1166665 v 2.11666 H 4.2333334 v 2.11667 H 2.1166667 v -2.11667 H 0 Z"
id="path846"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916668"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconB.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="42.15"
inkscape:cx="10.765381"
inkscape:cy="12.799167"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="10"
showguides="false"
objecttolerance="17">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<g
aria-label="B"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.34962893px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.37805545"
id="text1421">
<path
d="m 0.31920594,296.97952 0.058797,-0.30868 h 0.0955452 q 0.24988739,0 0.44832737,-0.0882 0.19843999,-0.0955 0.27193629,-0.44097 l 0.7643614,-3.60132 q 0.029398,-0.1176 0.029398,-0.20579 0,-0.19109 -0.1616918,-0.24254 -0.1543422,-0.0514 -0.3674815,-0.0514 h -0.095545 l 0.066147,-0.30868 h 1.9035539 q 0.8452073,0 1.2788354,0.27928 0.4409778,0.27929 0.4409778,0.82316 0,0.40423 -0.1616919,0.68352 -0.1543422,0.27193 -0.4262784,0.44098 -0.2719363,0.16904 -0.6100193,0.24253 l -0.00735,0.0294 q 0.39688,0.0735 0.6467674,0.34543 0.2498873,0.26459 0.2498873,0.72762 0,0.80846 -0.5365229,1.24208 -0.5365229,0.43363 -1.5801702,0.43363 z m 2.05789606,-2.9325 h 0.5659215 q 0.6908651,0 0.9995495,-0.28663 0.3086844,-0.29399 0.3086844,-0.83051 0,-0.83051 -1.0142488,-0.83051 H 2.7960309 Z m -0.5438725,2.56502 h 0.712914 q 0.7570118,0 1.0803955,-0.34543 0.3233836,-0.35278 0.3233836,-1.01425 0,-0.83786 -1.028948,-0.83786 H 2.3036057 Z"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif Italic';fill:#ffffff;fill-opacity:1;stroke-width:1.37805545"
id="path1423"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconBack.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="39.383707"
inkscape:cx="6.3383887"
inkscape:cy="7.7203454"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0,294.35415 2.6458333,-2.64583 v 1.5875 h 2.6458334 v 1.85208 H 2.6458333 l 0,1.85209 z"
id="path841"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconBackDisabled.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="39.383707"
inkscape:cx="1.7552752"
inkscape:cy="7.7203454"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<path
style="fill:#ffffff;fill-opacity:0.39215687;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0,294.35415 2.6458333,-2.64583 v 1.5875 h 2.6458334 v 1.85208 H 2.6458333 v 1.85209 z"
id="path841"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconCurve.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="-2.7199818"
inkscape:cy="9.4290379"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="267"
inkscape:window-y="1022"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#00ffcc;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#808080;stroke-width:0.52916667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.52916667, 0.52916666999999995;stroke-opacity:1;stroke-dashoffset:0"
d="m 0.26458333,292.76665 0,3.96875 H 4.2333333"
id="path1231"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0,296.7354 c 2.0787582,0 3.96875,-2.11667 3.96875,-3.96875"
id="path1233"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916668"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconFX.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="42.15"
inkscape:cx="31.643198"
inkscape:cy="20.391101"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="10"
showguides="false"
objecttolerance="17">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<g
aria-label="fx"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.17202616px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';letter-spacing:0px;word-spacing:0px;fill:#fffffa;fill-opacity:1;stroke:none;stroke-width:0.96975476"
id="text5280">
<path
d="m 0.31713132,296.97813 q -0.0775804,0 -0.16033281,-0.0155 -0.08792445,-0.0104 -0.13964471,-0.0259 l 0.04654823,-0.23792 q 0.04137621,0.0103 0.11378458,0.0207 0.0672363,0.0155 0.12412863,0.0155 0.25342928,0 0.39824601,-0.21723 0.13964471,-0.21722 0.25342928,-0.73442 L 1.4963533,293.25945 H 1.0256989 l 0.031032,-0.1862 0.4965145,-0.10861 0.056892,-0.28446 q 0.082752,-0.43962 0.3206656,-0.68271 0.2379132,-0.24308 0.6620193,-0.24308 0.3310097,0 0.4965145,0.10861 0.1706769,0.10344 0.1706769,0.27412 0,0.17067 -0.1137846,0.25343 -0.1086125,0.0827 -0.3154936,0.0827 0,-0.19654 -0.05172,-0.33618 -0.05172,-0.14482 -0.2120531,-0.14482 -0.191365,0 -0.2948055,0.16033 -0.1034405,0.15516 -0.1655048,0.481 l -0.067236,0.33101 H 2.613511 L 2.556619,293.2594 H 1.9773517 l -0.5378907,2.51877 q -0.098269,0.46031 -0.2534293,0.71891 -0.1551608,0.26378 -0.37238587,0.37239 -0.2172251,0.10861 -0.49651451,0.10861 z"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif Italic';fill:#fffffa;fill-opacity:1;stroke-width:0.96975476"
id="path5282"
inkscape:connector-curvature="0" />
<path
d="m 2.28436,295.73685 0.056892,-0.21723 h 0.067236 q 0.098269,0 0.1706769,-0.0259 0.07758,-0.0259 0.1603328,-0.10344 0.082752,-0.0776 0.206881,-0.22757 l 0.6620194,-0.80166 -0.3361817,-0.80684 q -0.093097,-0.2224 -0.1603328,-0.2948 -0.062064,-0.0776 -0.2120531,-0.0776 h -0.067236 l 0.041376,-0.21723 H 3.56185 l 0.3930739,1.04475 0.6878795,-1.04475 h 0.6361592 l -0.056892,0.21723 h -0.067236 q -0.098268,0 -0.1758489,0.031 -0.07758,0.031 -0.1603328,0.11378 -0.082752,0.0776 -0.201709,0.22757 l -0.5430628,0.69823 0.3568698,0.89476 q 0.082752,0.22757 0.1499888,0.29997 0.072408,0.0724 0.2223971,0.0724 h 0.067236 l -0.041376,0.21723 H 4.1359454 L 3.7170112,294.599 2.9412073,295.73685 Z"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif Italic';fill:#fffffa;fill-opacity:1;stroke-width:0.96975476"
id="path5284"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconForward.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="39.383707"
inkscape:cx="6.3383887"
inkscape:cy="7.7203454"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5.2916667,294.35415 -2.6458333,-2.64583 v 1.5875 H 0 v 1.85208 h 2.6458334 v 1.85209 z"
id="path841"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconForwardDisabled.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="39.383707"
inkscape:cx="6.3383887"
inkscape:cy="7.7203454"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<path
style="fill:#ffffff;fill-opacity:0.39170507;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5.2916667,294.35415 -2.6458333,-2.64583 v 1.5875 H 0 v 1.85208 h 2.6458334 v 1.85209 z"
id="path841"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="IconSoundGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="19.66"
inkscape:cx="6.046968"
inkscape:cy="6.9530957"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="231"
inkscape:window-y="82"
inkscape:window-maximized="0"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#fbd365;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="IconImport.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#2a2a2a"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="2"
inkscape:cx="9.0918787"
inkscape:cy="8.1216613"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="863"
inkscape:window-x="365"
inkscape:window-y="83"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#ff2ad4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.984375,292.7713 a 2.1166665,2.1166665 0 0 0 -1.62935791,0.94258 l 1.62935791,0.94103 0,-1.88361 z"
id="path4824"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:#d40000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.2479248,292.77285 0,1.88309 1.6303914,-0.94154 a 2.1166665,2.1166665 0 0 0 -1.6303914,-0.94155 z"
id="path4822"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:#6600ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.22375895,293.94281 A 2.1166665,2.1166665 0 0 0 0,294.88332 a 2.1166665,2.1166665 0 0 0 0.22479248,0.94102 l 1.62832432,-0.94051 -1.62935785,-0.94102 z"
id="path4820"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:#ff6600;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.0095744,293.94332 -1.6288412,0.94051 1.6288412,0.94051 a 2.1166665,2.1166665 0 0 0 0.2237589,-0.94102 2.1166665,2.1166665 0 0 0 -0.2237589,-0.94 z"
id="path4818"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:#ffcc00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.2479248,295.11121 0,1.88412 a 2.1166665,2.1166665 0 0 0 1.6303914,-0.94258 l -1.6303914,-0.94154 z"
id="path4816"
inkscape:connector-curvature="0" />
<path
style="opacity:1;fill:#2ad4ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.984375,295.11276 -1.62884115,0.93999 a 2.1166665,2.1166665 0 0 0 1.62884115,0.94052 l 0,-1.88051 z"
id="path4782"
inkscape:connector-curvature="0" />
<circle
style="opacity:1;fill:#66ff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4826"
cx="2.1166668"
cy="294.88333"
r="1.2296628" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="IconKeymap.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="22.528678"
inkscape:cx="6.3570002"
inkscape:cy="7.1823475"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect6472"
width="4.2333331"
height="3.1750097"
x="0"
y="293.29581" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 0.26458333,293.5604 0,2.64583 0.79374997,0 0,-1.32291 -0.26458331,0 0,-1.32292 -0.52916666,0"
id="path6474"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 1.3229166,294.88332 0,1.32291 0.79375,0 0,-1.32291 -0.2645833,0 0,-1.32292 -0.2645833,0 0,1.32292 z"
id="path6476"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 2.38125,294.88332 0,1.32291 0.79375,0 0,-1.32291 0,-1.32292 -0.5291667,0 0,1.32292 z"
id="path6478"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 3.4395833,293.5604 0,2.64583 0.5291667,0 0,-2.64583 z"
id="path6480"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916668"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconKill.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="39.226909"
inkscape:cx="9.3355483"
inkscape:cy="10.583863"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="10"
showguides="false">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<path
style="fill:#fffff8;fill-opacity:1;stroke:#000000;stroke-width:0.30217573px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 1.4046674,296.70833 v -1.17708 c 0,0 -1.24116588,-0.58856 -1.24116588,-1.1771 0,-0.58853 0,-2.35417 2.48233178,-2.35417 2.4823318,0 2.4823318,1.76564 2.4823318,2.35417 0,0.58854 -1.2411659,1.1771 -1.2411659,1.1771 v 1.17708 z"
id="path842"
inkscape:connector-curvature="0" />
<path
style="fill:#000006;fill-opacity:1;stroke:none;stroke-width:0.79053128;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.49803922"
id="path844"
sodipodi:type="arc"
sodipodi:cx="1.6969496"
sodipodi:cy="294.08957"
sodipodi:rx="0.71268904"
sodipodi:ry="0.65353703"
sodipodi:start="0"
sodipodi:end="5.866374"
d="m 2.4096386,294.08957 a 0.71268904,0.65353703 0 0 1 -0.6385591,0.64999 0.71268904,0.65353703 0 0 1 -0.7713978,-0.51477 0.71268904,0.65353703 0 0 1 0.4780863,-0.75708 0.71268904,0.65353703 0 0 1 0.8708534,0.35728 l -0.6516718,0.26458 z" />
<path
d="m 4.2943505,294.08957 a 0.71268904,0.65353703 0 0 1 -0.6385591,0.64999 0.71268904,0.65353703 0 0 1 -0.7713978,-0.51477 0.71268904,0.65353703 0 0 1 0.4780862,-0.75708 0.71268904,0.65353703 0 0 1 0.8708535,0.35728 l -0.6516718,0.26458 z"
sodipodi:end="5.866374"
sodipodi:start="0"
sodipodi:ry="0.65353703"
sodipodi:rx="0.71268904"
sodipodi:cy="294.08957"
sodipodi:cx="3.5816615"
sodipodi:type="arc"
id="path846"
style="fill:#000006;fill-opacity:1;stroke:none;stroke-width:0.79053128;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.49803922" />
<path
style="fill:none;stroke:#000000;stroke-width:0.29107958px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2.6458333,295.41249 v 1.28091"
id="path848"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.29184496px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2.0289823,295.41249 v 1.28766"
id="path850"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.29107958px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.2694292,295.41249 v 1.28091"
id="path852"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconLayers.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="4.92"
inkscape:cx="-13.575223"
inkscape:cy="7.1823475"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="1749"
inkscape:window-y="677"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#ff0066;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0.26458333,293.29581 0,0.79375 3.70416667,0 0,-0.79375 z"
id="path7056"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path7058"
d="m 0.26458333,294.35415 0,0.79375 3.70416667,0 0,-0.79375 z"
style="fill:#ffcc00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#00aad4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0.26458333,295.41248 0,0.79375 3.70416667,0 0,-0.79375 z"
id="path7060"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="IconNew.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="4.3015958"
inkscape:cx="-1.383522"
inkscape:cy="20.267081"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="172"
inkscape:window-y="33"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.4395833,292.76665 0,4.23333 -2.6458333,0 c 0,-1.14653 0,-2.02847 0,-3.175 l 1.0583333,-1.05833 z"
id="path4171"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#c7c7c7;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0.79375,293.9564 1.1898162,0 0,-1.18975"
id="path4182"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewADSR.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="7.2902772"
inkscape:cy="8.0995259"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="341"
inkscape:window-y="1113"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.26458333,296.7354 1.0583334,293.47246 1.5875,294.35415 H 2.9104167 L 3.96875,296.7354"
id="path1834"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.34071 0.9362114,0.93622 m -0.9362104,-10e-6 0.9362094,-0.9362 m -1.1301062,0.4681 H 3.96875 m -0.6620015,0.662 v -1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewCurve.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="8.2484922"
inkscape:cy="9.4290379"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="459"
inkscape:window-y="172"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#00ffcc;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#808080;stroke-width:0.52916667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.52916667, 0.52916666999999995;stroke-opacity:1;stroke-dashoffset:0"
d="m 0.26458333,292.76665 0,3.96875 H 4.2333333"
id="path1231"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0,296.7354 c 2.0787582,0 3.96875,-2.11667 3.96875,-3.96875"
id="path1233"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.34071 0.9362114,0.93622 m -0.9362104,-10e-6 0.9362094,-0.9362 m -1.1301062,0.4681 H 3.96875 m -0.6620015,0.662 v -1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="NewSoundGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="19.66"
inkscape:cx="6.046968"
inkscape:cy="6.9530957"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="231"
inkscape:window-y="82"
inkscape:window-maximized="0"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#fbd365;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<path
id="path5876"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.07809 0.9362114,0.93622 m -0.9362104,-1e-5 0.9362094,-0.9362 m -1.1301062,0.4681 1.324003,0 m -0.6620015,0.662 0,-1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="NewKeymap.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="6.052834"
inkscape:cx="36.266061"
inkscape:cy="7.1823475"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect6472"
width="4.2333331"
height="3.1750097"
x="0"
y="293.29581" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 0.26458333,293.5604 0,2.64583 0.79374997,0 0,-1.32291 -0.26458331,0 0,-1.32292 -0.52916666,0"
id="path6474"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 1.3229166,294.88332 0,1.32291 0.79375,0 0,-1.32291 -0.2645833,0 0,-1.32292 -0.2645833,0 0,1.32292 z"
id="path6476"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 2.38125,294.88332 0,1.32291 0.79375,0 0,-1.32291 0,-1.32292 -0.5291667,0 0,1.32292 z"
id="path6478"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="m 3.4395833,293.5604 0,2.64583 0.5291667,0 0,-2.64583 z"
id="path6480"
inkscape:connector-curvature="0" />
<path
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.07721 0.9362114,0.93622 m -0.9362104,-1e-5 0.9362094,-0.9362 m -1.1301062,0.4681 1.324003,0 m -0.6620015,0.662 0,-1.324"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="NewLayers.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="2"
inkscape:cx="10.530468"
inkscape:cy="7.1823475"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="131"
inkscape:window-y="170"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#ff0066;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0.26458333,293.29581 0,0.79375 3.70416667,0 0,-0.79375 z"
id="path7056"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path7058"
d="m 0.26458333,294.35415 0,0.79375 3.70416667,0 0,-0.79375 z"
style="fill:#ffcc00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#00aad4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0.26458333,295.41248 0,0.79375 3.70416667,0 0,-0.79375 z"
id="path7060"
inkscape:connector-curvature="0" />
<path
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.34072 0.9362114,0.93622 m -0.9362104,-1e-5 0.9362094,-0.9362 m -1.1301062,0.4681 1.324003,0 m -0.6620015,0.662 0,-1.324"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewSongGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="29.730996"
inkscape:cx="3.0052979"
inkscape:cy="9.0045012"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#ff5599;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<path
inkscape:connector-curvature="0"
id="path868"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none"
d="m 1.8675862,295.87272 a 0.57456181,0.36088177 0 0 1 -0.5745618,0.36089 0.57456181,0.36088177 0 0 1 -0.57456172,-0.36089 0.57456181,0.36088177 0 0 1 0.57456172,-0.36088 0.57456181,0.36088177 0 0 1 0.5745618,0.36088 z m 1.3298265,-0.001 a 0.57456181,0.36088177 0 0 1 -0.5745619,0.36089 0.57456181,0.36088177 0 0 1 -0.5745618,-0.36089 0.57456181,0.36088177 0 0 1 0.5745618,-0.36088 0.57456181,0.36088177 0 0 1 0.5745619,0.36088 z m -0.253445,-1.78193 0.253445,0.006 v 1.77617 c -0.2177221,0.0701 -0.286996,-0.0173 -0.3755473,-0.1676 l 0.00387,-0.79478 H 1.868454 v 0.95424 c -0.2323614,-0.008 -0.2369279,-0.14254 -0.3755412,-0.22458 v -1.54879 z" />
<path
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.07809 0.9362114,0.93622 m -0.9362104,-1e-5 0.9362094,-0.9362 m -1.1301062,0.4681 1.324003,0 m -0.6620015,0.662 0,-1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewSoundGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="31.94"
inkscape:cx="9.7896878"
inkscape:cy="6.9530957"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="777"
inkscape:window-x="2038"
inkscape:window-y="1136"
inkscape:window-maximized="0"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#04e2ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<path
id="path1184"
title="sin(x*4*pi)*sin(x*pi)"
d="m 0.52916699,295.1479 c 0.11759258,-1.5e-4 0.23518516,-0.18652 0.35277774,-0.31191 0.11759258,-0.1254 0.23518517,-0.1236 0.35277777,0.10832 0.1175926,0.23193 0.2351851,0.66463 0.3527777,0.89812 0.1175926,0.23349 0.2351852,0.20457 0.3527778,-0.10832 0.1175925,-0.3129 0.2351851,-0.85907 0.3527777,-1.17242 0.1175926,-0.31334 0.2351852,-0.3414 0.3527777,-0.10832 0.1175926,0.23308 0.2351852,0.66584 0.3527778,0.89812 0.1175926,0.23228 0.2351851,0.23355 0.3527778,0.10832 0.1175925,-0.12522 0.235185,-0.31176 0.3527777,-0.31191"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.396875;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
inkscape:connector-curvature="0" />
<path
id="path5876"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.07809 0.9362114,0.93622 m -0.9362104,-1e-5 0.9362094,-0.9362 m -1.1301062,0.4681 1.324003,0 m -0.6620015,0.662 0,-1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewSoundMacro.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.33"
inkscape:cx="2.9941402"
inkscape:cy="4.6784097"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="606"
inkscape:window-y="435"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#62ff04;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.30031049;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.96793039,292.83382 2.60268711,1.68348 c 0.392506,0.24382 0.3952841,0.49652 0,0.73535 l -2.60268711,1.69163 c -0.2789936,0.18133 -0.6006367,-0.0548 -0.6006367,-0.39294 v -3.35322 c 0,-0.33276 0.3212394,-0.54502 0.6006367,-0.3643 z"
id="rect5899-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sccssss" />
<path
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.6133 0.9362114,0.93622 m -0.9362104,-10e-6 0.9362094,-0.9362 m -1.1301062,0.4681 H 3.96875 m -0.6620015,0.662 v -1.324"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="IconOpen.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="2"
inkscape:cx="8.3840992"
inkscape:cy="8.1216613"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="231"
inkscape:window-y="60"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#fbd365;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<path
style="fill:#00eaff;fill-rule:evenodd;stroke:#2a2a2a;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
d="m 1.1851666,295.02021 1.8652063,-0.005 c 0,0.50687 -0.4045396,1.05375 -0.9337062,1.05512 -0.5291667,0 -0.9315001,-0.53641 -0.9315001,-1.04967 z"
id="path4753"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<circle
style="opacity:1;fill:#2a2a2a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.3379305;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4755"
cx="1.723109"
cy="294.216"
r="0.19592465" />
<circle
r="0.19592465"
cy="294.2269"
cx="2.4938412"
id="circle4757"
style="opacity:1;fill:#2a2a2a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.3379305;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 8.4666664 8.4666672"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconPaintbrush.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="119.2182"
inkscape:cx="10.254924"
inkscape:cy="16.654642"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="10"
showguides="false"
showborder="true">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-288.53332)">
<path
sodipodi:type="inkscape:offset"
inkscape:radius="0.24255499"
inkscape:original="M 7.9375 289.0625 C 7.6711732 288.79616 2.6464844 293.29492 2.6464844 293.29492 L 3.7050781 294.35352 C 3.7050781 294.35352 8.2038267 289.32882 7.9375 289.0625 z "
style="fill:none;fill-opacity:1;stroke:#000019;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5257"
d="m 7.8886719,288.80859 c -0.056389,0.002 -0.097096,0.0152 -0.1367188,0.0293 -0.079245,0.0282 -0.1534463,0.068 -0.2421875,0.12109 -0.1774822,0.10627 -0.4004054,0.26416 -0.6582031,0.45899 -0.5155954,0.38965 -1.1699607,0.92941 -1.8164063,1.47656 -1.2928911,1.0943 -2.5507812,2.21875 -2.5507812,2.21875 a 0.24257925,0.24257925 0 0 0 -0.00977,0.35352 l 1.0585937,1.05859 a 0.24257925,0.24257925 0 0 0 0.3535157,-0.01 c 0,0 1.1244523,-1.25788 2.21875,-2.55078 0.5471488,-0.64644 1.0869076,-1.30081 1.4765624,-1.8164 0.1948275,-0.2578 0.3527187,-0.48072 0.4589844,-0.65821 0.053133,-0.0887 0.092867,-0.16294 0.1210938,-0.24218 0.014113,-0.0396 0.027013,-0.0803 0.029297,-0.13672 0.00228,-0.0564 -0.00982,-0.1485 -0.082031,-0.22071 -0.072205,-0.0722 -0.1643139,-0.0843 -0.2207031,-0.082 z" />
<path
style="fill:#333333;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 7.9375067,289.06249 c 0.2663267,0.26632 -4.2333372,5.29165 -4.2333372,5.29165 l -1.0583343,-1.05832 c 0,0 5.0253447,-4.49967 5.2916715,-4.23333 z"
id="path846"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zccz" />
<path
style="fill:#333333;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 3.7041695,294.35414 c 0.7934571,0.79347 -2.1166685,2.11668 -3.17500284,2.11668 0,-1.05833 1.32960554,-3.96208 2.11666854,-3.175 z"
id="path848"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.7321623,293.41165 c 0,0 -1.1446613,0.94249 -2.20299564,3.05917"
id="path850"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 3.6041925,294.27028 c 0,0 -0.9583573,1.14221 -3.07502584,2.20054"
id="path852"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 3.1750023,293.82499 -2.64583564,2.64583"
id="path854"
inkscape:connector-curvature="0" />
<path
sodipodi:type="inkscape:offset"
inkscape:radius="0.25359377"
inkscape:original="M 2.6464844 293.29492 C 1.8594214 292.50784 0.52929688 295.41237 0.52929688 296.4707 C 1.5876312 296.4707 4.4985352 295.14699 3.7050781 294.35352 L 2.6464844 293.29492 z "
style="fill:none;fill-opacity:1;stroke:#00001b;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5261"
d="m 2.3164062,292.9082 c -0.181907,0.01 -0.3430295,0.0908 -0.4863281,0.19922 -0.2865971,0.21692 -0.5328425,0.56219 -0.7578125,0.95899 -0.44993989,0.79359 -0.79687498,1.76889 -0.79687498,2.40429 a 0.25361913,0.25361913 0 0 0 0.25390626,0.25391 c 0.63489452,0 1.61169302,-0.34575 2.40625002,-0.79492 0.3972785,-0.22459 0.7421604,-0.47049 0.9589843,-0.75781 0.108412,-0.14367 0.1899049,-0.30393 0.1992188,-0.48633 0.00931,-0.1824 -0.069928,-0.37266 -0.2089844,-0.51172 l -1.0585937,-1.0586 C 2.6875905,292.97665 2.4983133,292.89817 2.3164062,292.9082 Z" />
<path
style="fill:#fffffd;fill-opacity:1;stroke:none;stroke-width:0.26458356px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.3534735,292.49828 1.1682763,1.12584 -0.6609111,0.75797 -1.2383055,-1.2582 z"
id="path1960"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 6.3499998 6.3500004"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconRemove.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="39.327341"
inkscape:cx="14.81887"
inkscape:cy="14.927514"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="4"
showguides="false"
showborder="true"
objecttolerance="13">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-290.64999)">
<path
style="fill:#ff0000;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0,292.76666 v 2.11666 h 6.3499999 v -2.11666 z"
id="path867"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconSample.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="34.25"
inkscape:cx="8.0516224"
inkscape:cy="8.452632"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
id="path1184"
title="sin(x*4*pi)*sin(x*pi)"
d="m 0.36489826,294.88332 c 0.12976062,-1.7e-4 0.25952124,-0.20582 0.38928188,-0.34418 0.12976062,-0.13838 0.25952116,-0.13639 0.38928186,0.11952 0.1297606,0.25593 0.2595211,0.73341 0.3892818,0.99106 0.1297606,0.25765 0.2595213,0.22574 0.3892819,-0.11953 0.1297606,-0.34528 0.2595212,-0.94796 0.3892819,-1.29374 0.1297606,-0.34576 0.2595213,-0.37673 0.3892818,-0.11953 0.1297606,0.2572 0.2595213,0.73474 0.3892819,0.99106 0.1297607,0.25631 0.2595213,0.25771 0.3892819,0.11953 0.1297606,-0.13818 0.2595212,-0.34402 0.3892819,-0.34419"
style="fill:none;fill-opacity:1;stroke:#04e2ff;stroke-width:0.43794215;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconSongGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="34.350339"
inkscape:cx="8.8858389"
inkscape:cy="8.8343343"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="1944"
inkscape:window-y="288"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#ff5599;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<g
aria-label="♬"
style="font-style:normal;font-weight:normal;font-size:1.66674709px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.31251505"
id="text820"
transform="matrix(1.1529095,0,0,1.1529095,-0.32840767,-44.985003)" />
<path
id="path868"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none"
d="m 1.8675862,295.87272 a 0.57456183,0.36088178 0 0 1 -0.5745618,0.36089 0.57456183,0.36088178 0 0 1 -0.57456171,-0.36089 0.57456183,0.36088178 0 0 1 0.57456171,-0.36088 0.57456183,0.36088178 0 0 1 0.5745618,0.36088 z m 1.3298265,-0.001 a 0.57456183,0.36088178 0 0 1 -0.5745619,0.36089 0.57456183,0.36088178 0 0 1 -0.5745618,-0.36089 0.57456183,0.36088178 0 0 1 0.5745618,-0.36088 0.57456183,0.36088178 0 0 1 0.5745619,0.36088 z m -0.253445,-1.78193 0.253445,0.006 v 1.77617 c -0.2177221,0.0701 -0.286996,-0.0173 -0.3755473,-0.1676 l 0.00387,-0.79478 H 1.868454 v 0.95424 c -0.2323614,-0.008 -0.2369279,-0.14254 -0.3755412,-0.22458 v -1.54879 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconSoundGroup.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="34.25"
inkscape:cx="8.0808195"
inkscape:cy="8.452632"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1258"
inkscape:window-height="787"
inkscape:window-x="1970"
inkscape:window-y="1150"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
sodipodi:nodetypes="cccssssscccc"
inkscape:connector-curvature="0"
id="rect4141"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
style="opacity:1;fill:#04e2ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1184"
title="sin(x*4*pi)*sin(x*pi)"
d="m 0.52916698,295.1479 c 0.11759258,-1.5e-4 0.23518516,-0.18652 0.35277775,-0.31191 0.11759258,-0.1254 0.23518517,-0.1236 0.35277777,0.10832 0.1175926,0.23193 0.2351851,0.66463 0.3527777,0.89812 0.1175926,0.23349 0.2351852,0.20457 0.3527778,-0.10832 0.1175925,-0.3129 0.2351851,-0.85907 0.3527777,-1.17242 0.1175926,-0.31334 0.2351852,-0.3414 0.3527777,-0.10832 0.1175926,0.23308 0.2351852,0.66584 0.3527778,0.89812 0.1175926,0.23228 0.2351852,0.23355 0.3527777,0.10832 0.1175926,-0.12522 0.2351852,-0.31176 0.3527778,-0.31191"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.396875;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stroke-linecap:round;paint-order:normal"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconSoundMacro.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="49.27"
inkscape:cx="6.2572034"
inkscape:cy="5.0825963"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#62ff04;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.30031049;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.96793039,292.83382 2.60268711,1.68348 c 0.392506,0.24382 0.3952842,0.49652 0,0.73535 l -2.60268711,1.69163 c -0.27899363,0.18133 -0.60063679,-0.0548 -0.60063679,-0.39294 l 0,-3.35322 c 0,-0.33276 0.32123948,-0.54502 0.60063679,-0.3643 z"
id="rect5899"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sccssss" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

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