135 Commits

Author SHA1 Message Date
Jack Andersen
d80f1346bb Fix unwanted cutoff bug 2019-01-22 21:52:00 -10:00
Jack Andersen
7719459ac7 Switch to lzokay library 2018-12-19 17:45:26 -10:00
Jack Andersen
c1c47c51eb Update translation file 2018-12-07 20:45:52 -10:00
Jack Andersen
a7a408cc66 New code style refactor 2018-12-07 19:20:09 -10:00
Jack Andersen
b4c073c373 More accurate attenuation curve 2018-10-25 21:44:56 -10:00
Jack Andersen
885a2d2154 Windows build fixes 2018-10-14 10:10:33 -10:00
Jack Andersen
dbd48a39cc Convert to pragma once 2018-10-06 17:40:25 -10:00
Jack Andersen
4c7971f53a Merge branch 'master' of ssh://git.axiodl.com:6431/AxioDL/amuse 2018-10-06 16:55:07 -10:00
Jack Andersen
38b9f57cd0 NX build fixes 2018-10-06 16:54:56 -10:00
Jack Andersen
92cd385f47 Update README.md 2018-09-30 23:13:59 -07:00
9e6d97564b Switch build fixes 2018-09-25 15:39:22 -07:00
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
a16fcf3342 Merge commit 'd9018e2' 2018-09-01 20:46:26 -07:00
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
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
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
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
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
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
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
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
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
af68ee61e1 Remove debug print 2016-09-02 13:05:58 -07:00
b40b2c031a Better temp file handling 2016-09-02 13:00:56 -07:00
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
196 changed files with 42736 additions and 80888 deletions

View File

@@ -1,5 +1,5 @@
---
IndentWidth: 4
BasedOnStyle: LLVM
ColumnLimit: 120
UseTab: Never
---
@@ -8,7 +8,6 @@ DerivePointerAlignment: false
PointerAlignment: Left
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
BreakBeforeBraces: Allman
IndentCaseLabels: false
AllowShortBlocksOnASingleLine: true
AlignOperands: true
@@ -16,16 +15,14 @@ AlignTrailingComments: true
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BreakConstructorInitializersBeforeComma: true
BreakStringLiterals: true
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
AllowShortFunctionsOnASingleLine: All
Cpp11BracedListStyle: true
NamespaceIndentation: None
ReflowComments: true
BinPackArguments: true
BinPackParameters: true
SortIncludes: false
AccessModifierOffset: -4
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;
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient> {
IBOutlet NSWindow* mainWindow;
IBOutlet NSOutlineView* dataOutline;
IBOutlet NSSearchField* dataSearchField;
IBOutlet NSTableView* sfxTable;
IBOutlet NSTableView* samplesTable;
IBOutlet NSTextView* creditsView;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* removeDataMenu;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* removeDataMenu;
AudioGroupFilePresenter* groupFilePresenter;
AudioGroupFilePresenter* groupFilePresenter;
SamplesTableController* samplesController;
SFXTableController* sfxController;
SamplesTableController* samplesController;
SFXTableController* sfxController;
@public
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
std::experimental::optional<amuse::Engine> amuseEngine;
std::shared_ptr<amuse::Voice> activeSFXVox;
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
std::experimental::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,5 +1,4 @@
#ifndef __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__
#define __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__
#pragma once
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
@@ -20,142 +19,127 @@
- (amuse::Engine&)getAmuseEngine;
@end
struct AudioGroupDataCollection
{
std::string m_name;
NSURL* m_proj;
NSURL* m_pool;
NSURL* m_sdir;
NSURL* m_samp;
NSURL* m_meta;
struct AudioGroupDataCollection {
std::string m_name;
NSURL* m_proj;
NSURL* m_pool;
NSURL* m_sdir;
NSURL* m_samp;
NSURL* m_meta;
AudioGroupDataToken* m_token;
AudioGroupDataToken* m_token;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
struct MetaData
{
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
{
}
MetaData(athena::io::FileReader& r)
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
{
}
};
std::experimental::optional<MetaData> m_metaData;
struct MetaData {
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(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;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
std::vector<AudioGroupToken*> m_groupTokens;
void moveURL(NSURL* oldUrl, NSURL* newUrl);
void moveURL(NSURL* oldUrl, NSURL* newUrl);
bool loadProj(AudioGroupFilePresenter* presenter);
bool loadPool(AudioGroupFilePresenter* presenter);
bool loadSdir(AudioGroupFilePresenter* presenter);
bool loadSamp(AudioGroupFilePresenter* presenter);
bool loadMeta(AudioGroupFilePresenter* presenter);
bool loadProj(AudioGroupFilePresenter* presenter);
bool loadPool(AudioGroupFilePresenter* presenter);
bool loadSdir(AudioGroupFilePresenter* presenter);
bool loadSamp(AudioGroupFilePresenter* presenter);
bool loadMeta(AudioGroupFilePresenter* presenter);
AudioGroupDataCollection(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);
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
bool isDataComplete() const {
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad(AudioGroupFilePresenter* presenter);
bool _indexData(AudioGroupFilePresenter* presenter);
void enable(AudioGroupFilePresenter* presenter);
void disable(AudioGroupFilePresenter* presenter);
void enable(AudioGroupFilePresenter* presenter);
void disable(AudioGroupFilePresenter* presenter);
};
struct AudioGroupCollection
{
NSURL* m_url;
struct AudioGroupCollection {
NSURL* m_url;
AudioGroupCollectionToken* m_token;
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
AudioGroupCollectionToken* m_token;
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
AudioGroupCollection(NSURL* url);
void addCollection(AudioGroupFilePresenter* presenter,
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter* presenter);
bool doSearch(const std::string& str);
bool doActiveFilter();
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
AudioGroupCollection(NSURL* url);
void addCollection(AudioGroupFilePresenter* presenter,
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter* presenter);
bool doSearch(std::string_view str);
bool doActiveFilter();
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
};
@interface AudioGroupDataToken : NSObject
{
@interface AudioGroupDataToken : NSObject {
@public
AudioGroupDataCollection* m_collection;
AudioGroupDataCollection* m_collection;
}
- (id)initWithDataCollection:(AudioGroupDataCollection*)collection;
@end
@interface AudioGroupCollectionToken : NSObject
{
@interface AudioGroupCollectionToken : NSObject {
@public
AudioGroupCollection* m_collection;
AudioGroupCollection* m_collection;
}
- (id)initWithCollection:(AudioGroupCollection*)collection;
@end
@interface AudioGroupSFXToken : NSObject
{
@interface AudioGroupSFXToken : NSObject {
@public
NSAttributedString* m_name;
int m_loadId;
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
NSAttributedString* m_name;
int m_loadId;
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
}
- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx;
@end
@interface AudioGroupSampleToken : NSObject
{
@interface AudioGroupSampleToken : NSObject {
@public
NSAttributedString* m_name;
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
NSAttributedString* m_name;
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
}
- (id)
initWithName:(NSAttributedString*)name
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>*)
sample;
- (id)initWithName:(NSAttributedString*)name
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry,
amuse::AudioGroupSampleDirectory::ADPCMParms>*)sample;
@end
@interface AudioGroupToken : NSObject
{
@interface AudioGroupToken : NSObject {
@public
NSString* m_name;
int m_id;
const amuse::SongGroupIndex* m_song;
const amuse::SFXGroupIndex* m_sfx;
NSString* m_name;
int m_id;
const amuse::SongGroupIndex* m_song;
const amuse::SFXGroupIndex* m_sfx;
}
- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group;
- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group;
@end
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate>
{
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate> {
@public
id<AudioGroupClient> m_audioGroupClient;
NSURL* m_groupURL;
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
NSOutlineView* m_lastOutlineView;
NSString* m_searchStr;
id<AudioGroupClient> m_audioGroupClient;
NSURL* m_groupURL;
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
NSOutlineView* m_lastOutlineView;
NSString* m_searchStr;
std::vector<AudioGroupSFXToken*> m_sfxTableData;
std::vector<AudioGroupSampleToken*> m_sampleTableData;
std::vector<AudioGroupSFXToken*> m_sfxTableData;
std::vector<AudioGroupSampleToken*> m_sampleTableData;
}
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
- (BOOL)addCollectionName:(std::string&&)name
@@ -165,5 +149,3 @@ initWithName:(NSAttributedString*)name
- (void)setSearchFilter:(NSString*)str;
- (void)removeSelectedItem;
@end
#endif // __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__

View File

@@ -161,13 +161,13 @@ 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)
if (str.empty() || StrToLower(it->first).find(str.data()) != std::string::npos)
{
m_filterGroups.push_back(it);
ret = true;
@@ -239,7 +239,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]) {}
@@ -849,7 +849,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>
@@ -20,29 +19,26 @@
@class AudioUnitViewController;
namespace amuse
{
namespace amuse {
/** Backend voice allocator implementation for AudioUnit mixer */
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator
{
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator {
public:
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
};
void RegisterAudioUnit();
}
} // namespace amuse
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient>
{
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient> {
@public
AudioUnitViewController* m_viewController;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
AudioGroupFilePresenter* m_filePresenter;
AUAudioUnitBus* m_outBus;
AUAudioUnitBusArray* m_outs;
AudioUnitViewController* m_viewController;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
AudioGroupFilePresenter* m_filePresenter;
AUAudioUnitBus* m_outBus;
AUAudioUnitBusArray* m_outs;
}
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
error:(NSError* __nullable* __nonnull)outError
@@ -52,4 +48,3 @@ void RegisterAudioUnit();
#endif
#endif
#endif // __AMUSE_AUDIOUNIT_BACKEND_HPP__

View File

@@ -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,8 +1,24 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(amuse)
if(EXISTS boo)
add_subdirectory(boo)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing")
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/boo AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/athena)
message(STATUS "Preparing standalone build")
add_subdirectory(boo)
add_subdirectory(athena)
include_directories(athena/include)
endif()
atdna(atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp)
atdna(atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp)
atdna(atdna_AudioGroupSampleDirectory.cpp include/amuse/AudioGroupSampleDirectory.hpp)
set(SOURCES
lib/AudioGroup.cpp
@@ -20,16 +36,19 @@ set(SOURCES
lib/SongConverter.cpp
lib/SongState.cpp
lib/Voice.cpp
lib/VolumeLUT.c
lib/Submix.cpp
lib/Studio.cpp
lib/EffectReverb.cpp
lib/EffectChorus.cpp
lib/EffectDelay.cpp
lib/SurroundProfiles.cpp
lib/ContainerRegistry.cpp
lib/DSPCodec.c
lib/N64MusyXCodec.c)
lib/Common.cpp
lib/DSPCodec.cpp
lib/N64MusyXCodec.cpp
lib/VolumeTable.cpp
atdna_AudioGroupPool.cpp
atdna_AudioGroupProject.cpp
atdna_AudioGroupSampleDirectory.cpp)
set(HEADERS
include/amuse/AudioGroup.hpp
@@ -57,17 +76,20 @@ set(HEADERS
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)
include/amuse/DSPCodec.hpp
include/amuse/N64MusyXCodec.hpp
include/amuse/VolumeTable.hpp)
if(NX)
list(APPEND HEADERS include/switch_math.hpp)
endif()
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})
${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${LZOKAY_INCLUDE_DIR})
list(APPEND EXTRAS lib/BooBackend.cpp include/amuse/BooBackend.hpp)
endif()
@@ -78,8 +100,15 @@ add_library(amuse
${SOURCES}
${HEADERS}
${EXTRAS})
if(COMMAND add_sanitizers)
add_sanitizers(amuse)
endif()
if(COMMAND cotire)
set_target_properties(amuse PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
cotire(amuse)
endif()
if(TARGET boo)
if(TARGET boo AND NOT WINDOWS_STORE AND NOT NX)
# AudioUnit Target (OS X only)
add_subdirectory(AudioUnit)
@@ -90,13 +119,28 @@ 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 boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} lzokay)
# 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 boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} lzokay)
# 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 boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} lzokay)
if(COMMAND add_sanitizers)
add_sanitizers(amuseplay)
add_sanitizers(amuseconv)
add_sanitizers(amuserender)
endif()
# Editor
find_package(Qt5Widgets)
if (Qt5Widgets_FOUND)
message(STATUS "Qt5 found, amuse-gui will be built")
add_subdirectory(Editor)
else()
message(STATUS "Qt5 not found, amuse-gui will not be built")
endif()
endif()

949
Editor/ADSREditor.cpp Normal file
View File

@@ -0,0 +1,949 @@
#include "ADSREditor.hpp"
#include "MainWindow.hpp"
#include <QVBoxLayout>
#include <QPainter>
#include <QMouseEvent>
static QColor Red = QColor(255, 127, 127);
static QColor Green = QColor(127, 255, 127);
static 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);
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) {
amuse::ADSRDLS& adsr = static_cast<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) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
sustain = adsr.getSustain();
relTime = adsr.getRelease();
}
qreal totalTime = adTime + 1.0 + relTime;
qreal deviceRatio = devicePixelRatioF();
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
painter.setFont(m_gridFont);
qreal yIncrement = (height() - 16.0) / 10.0;
for (int i = 0; i < 11; ++i) {
qreal thisY = i * yIncrement;
qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0));
painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]);
painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY));
}
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;
qreal xIncrement = (width() - xBase) / totalTime;
for (size_t i = 0; i <= size_t(adTime); ++i) {
qreal thisX = i * xIncrement + xBase;
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) {
qreal thisX = i * xIncrement + xBase;
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));
}
qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain;
QPointF pt0 = QPointF(30.0, height() - 16.0);
QPointF pt1 = QPointF((width() - 30.0) * (aTime / totalTime) + 30.0, 0.0);
QPointF pt2 = QPointF((width() - 30.0) * (adTime / totalTime) + 30.0, sustainY);
QPointF pt3 = QPointF((width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0, sustainY);
QPointF 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) {
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;
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) {
amuse::ADSRDLS& adsr = static_cast<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) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
sustain = adsr.getSustain();
relTime = adsr.getRelease();
}
qreal totalTime = adTime + 1.0 + relTime;
qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain;
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)};
qreal dists[] = {PointDistance(ev->localPos(), points[0]), PointDistance(ev->localPos(), points[1]),
PointDistance(ev->localPos(), 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) {
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) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adTime += adsr.getAttack();
aTime = adsr.getAttack();
adTime += adsr.getDecay();
relTime = adsr.getRelease();
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<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) {
qreal newAttack = std::max(0.0, (ev->localPos().x() - 30.0) / (width() - 30.0) * totalTime);
qreal delta = newAttack - aTime;
ctrls->setAttackAndDecay(newAttack, std::max(0.0, ctrls->m_decay->value() - delta), m_cycleIdx);
} else if (m_dragPoint == 1) {
qreal newDecay = std::max(0.0, (ev->localPos().x() - 30.0) * totalTime / (width() - 30.0) - aTime);
qreal newSustain = (-ev->localPos().y() + (height() - 16.0)) / (height() - 16.0);
ctrls->setDecayAndSustain(newDecay, newSustain * 100.0, m_cycleIdx);
} else if (m_dragPoint == 2) {
qreal newRelease = std::max(0.0, (width() - 30.0) * (adTime + 1.0) / (ev->localPos().x() - 30.0) - (adTime + 1.0));
ctrls->setRelease(newRelease, m_cycleIdx);
ctrls->m_release->setValue(newRelease);
}
}
ADSRView::ADSRView(QWidget* parent) : QWidget(parent) {
for (int i = 0; i < 11; ++i) {
m_percentTexts[i].setText(QStringLiteral("%1%").arg(100 - i * 10));
m_percentTexts[i].setTextOption(QTextOption(Qt::AlignVCenter | Qt::AlignRight));
m_percentTexts[i].setTextWidth(28.0);
}
m_gridFont.setPointSize(8);
}
ADSREditor* ADSRControls::getEditor() const { return qobject_cast<ADSREditor*>(parentWidget()); }
void ADSRControls::loadData() {
m_enableUpdate = false;
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) {
amuse::ADSRDLS& adsr = static_cast<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) {
amuse::ADSR& adsr = static_cast<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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setAttack(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adsr.setAttack(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getAttack();
adsr.setAttack(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getAttack();
adsr.setAttack(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRAttackUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const { 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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setDecay(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adsr.setDecay(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getDecay();
adsr.setDecay(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getDecay();
adsr.setDecay(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRDecayUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const { 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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setSustain(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adsr.setSustain(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getSustain();
adsr.setSustain(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getSustain();
adsr.setSustain(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRSustainUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const { 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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setAttack(m_undoAttack);
adsr.setDecay(m_undoDecay);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adsr.setAttack(m_undoAttack);
adsr.setDecay(m_undoDecay);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& 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) {
amuse::ADSR& 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) {
if (other->id() == id() && m_cycleCount == static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_cycleCount) {
m_redoAttack = static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_redoAttack;
m_redoDecay = static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_redoDecay;
return true;
}
return false;
}
int id() const { 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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setDecay(m_undoDecay);
adsr.setSustain(m_undoSustain);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adsr.setDecay(m_undoDecay);
adsr.setSustain(m_undoSustain);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& 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) {
amuse::ADSR& 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) {
if (other->id() == id() &&
m_cycleCount == static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_cycleCount) {
m_redoDecay = static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_redoDecay;
m_redoSustain = static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_redoSustain;
return true;
}
return false;
}
int id() const { 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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr.setRelease(m_undoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
adsr.setRelease(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr.getRelease();
adsr.setRelease(m_redoVal);
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
amuse::ADSR& adsr = static_cast<amuse::ADSR&>(table);
m_undoVal = adsr.getRelease();
adsr.setRelease(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) {
if (other->id() == id() && m_cycleCount == static_cast<const ADSRReleaseUndoCommand*>(other)->m_cycleCount) {
m_redoVal = static_cast<const ADSRReleaseUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const { 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) {
amuse::ADSRDLS& 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) {
amuse::ADSR& 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() {
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() {
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) {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRDLSUndoCommand*>(other)->m_redoVal;
m_redoVelToAttack = static_cast<const ADSRDLSUndoCommand*>(other)->m_redoVelToAttack;
m_redoKeyToDecay = static_cast<const ADSRDLSUndoCommand*>(other)->m_redoKeyToDecay;
return true;
}
return false;
}
int id() const { 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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr._setVelToAttack(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr._getVelToAttack();
adsr._setVelToAttack(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRVelToAttackUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const { 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() {
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
adsr._setKeyToDecay(m_undoVal);
}
EditorUndoCommand::undo();
}
void redo() {
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
amuse::ADSRDLS& adsr = static_cast<amuse::ADSRDLS&>(table);
m_undoVal = adsr._getKeyToDecay();
adsr._setKeyToDecay(m_redoVal);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other) {
if (other->id() == id()) {
m_redoVal = static_cast<const ADSRKeyToDecayUndoCommand*>(other)->m_redoVal;
return true;
}
return false;
}
int id() const { 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);
QHBoxLayout* mainLayout = new QHBoxLayout;
QGridLayout* leftLayout = new QGridLayout;
QPalette palette = QWidget::palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
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, SIGNAL(valueChanged(double)), this, SLOT(attackChanged(double)));
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, SIGNAL(valueChanged(double)), this, SLOT(decayChanged(double)));
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, SIGNAL(valueChanged(double)), this, SLOT(sustainChanged(double)));
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, SIGNAL(valueChanged(double)), this, SLOT(releaseChanged(double)));
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, SIGNAL(stateChanged(int)), this, SLOT(dlsStateChanged(int)));
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, SIGNAL(valueChanged(double)), this, SLOT(velToAttackChanged(double)));
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, SIGNAL(valueChanged(double)), this, SLOT(keyToDecayChanged(double)));
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);
}
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) {
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_adsrView);
layout->addWidget(m_controls);
setLayout(layout);
}

80
Editor/ADSREditor.hpp Normal file
View File

@@ -0,0 +1,80 @@
#pragma once
#include "EditorWidget.hpp"
#include <QFrame>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QLabel>
#include <QStaticText>
class ADSREditor;
class ADSRView : public QWidget {
Q_OBJECT
friend class ADSRControls;
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
std::vector<QStaticText> m_timeTexts;
int m_dragPoint = -1;
uint64_t m_cycleIdx = 0;
ADSREditor* getEditor() const;
public:
explicit ADSRView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseReleaseEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
};
class ADSRControls : public QFrame {
Q_OBJECT
friend class ADSRView;
QDoubleSpinBox* m_attack;
QDoubleSpinBox* m_decay;
QDoubleSpinBox* m_sustain;
QDoubleSpinBox* m_release;
QCheckBox* m_dls;
QLabel* m_velToAttackLab;
QDoubleSpinBox* m_velToAttack;
QLabel* m_keyToDecayLab;
QDoubleSpinBox* m_keyToDecay;
bool m_enableUpdate = true;
ADSREditor* getEditor() const;
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
void setRelease(double release, uint64_t cycleCount);
public:
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
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);
bool loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
};

87
Editor/CMakeLists.txt Normal file
View File

@@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.10)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Xml REQUIRED)
find_package(Qt5Svg REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5LinguistTools REQUIRED)
if(WIN32)
list(APPEND PLAT_SRCS platforms/win/amuse-gui.rc platforms/win/amuse-gui.manifest)
elseif(APPLE)
list(APPEND PLAT_SRCS platforms/mac/mainicon.icns MacOSExtras.mm)
set_source_files_properties(platforms/mac/mainicon.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
endif()
add_subdirectory(platforms/freedesktop)
declare_qticon_target()
list(APPEND PLAT_SRCS mainicon_qt.cpp)
configure_file(resources/translation_res.qrc translation_res.qrc @ONLY)
set(TRANSLATIONS
resources/lang_de.ts)
QT5_CREATE_TRANSLATION(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../lib ${TRANSLATIONS})
QT5_ADD_RESOURCES(qrc_resources.cpp resources/resources.qrc)
QT5_ADD_RESOURCES(qrc_translation_res.cpp ${CMAKE_CURRENT_BINARY_DIR}/translation_res.qrc OPTIONS -no-compress)
QT5_WRAP_UI(MAIN_WINDOW_UI MainWindow.ui)
QT5_WRAP_CPP(AMUSE_MOC
Common.hpp
MainWindow.hpp
KeyboardWidget.hpp
StatusBarWidget.hpp
ProjectModel.hpp
EditorWidget.hpp
SoundMacroEditor.hpp
ADSREditor.hpp
CurveEditor.hpp
KeymapEditor.hpp
LayersEditor.hpp
SampleEditor.hpp
SoundGroupEditor.hpp
SongGroupEditor.hpp
NewSoundMacroDialog.hpp
StudioSetupWidget.hpp)
add_executable(amuse-gui WIN32 MACOSX_BUNDLE
Common.hpp Common.cpp
MainWindow.ui ${MAIN_WINDOW_UI} MainWindow.hpp MainWindow.cpp
KeyboardWidget.hpp KeyboardWidget.cpp
StatusBarWidget.hpp StatusBarWidget.cpp
ProjectModel.hpp ProjectModel.cpp
EditorWidget.hpp EditorWidget.cpp
SoundMacroEditor.hpp SoundMacroEditor.cpp
ADSREditor.hpp ADSREditor.cpp
CurveEditor.hpp CurveEditor.cpp
KeymapEditor.hpp KeymapEditor.cpp
LayersEditor.hpp LayersEditor.cpp
SampleEditor.hpp SampleEditor.cpp
SoundGroupEditor.hpp SoundGroupEditor.cpp
SongGroupEditor.hpp SongGroupEditor.cpp
NewSoundMacroDialog.hpp NewSoundMacroDialog.cpp
StudioSetupWidget.hpp StudioSetupWidget.cpp
MIDIReader.hpp MIDIReader.cpp
resources/resources.qrc qrc_resources.cpp
${QM_FILES} qrc_translation_res.cpp
${AMUSE_MOC} ${PLAT_SRCS}
main.cpp)
if(COMMAND add_sanitizers)
add_sanitizers(amuse-gui)
endif()
set_target_properties(amuse-gui PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist")
target_link_libraries(amuse-gui ${PLAT_LIBS}
${Qt5Widgets_LIBRARIES}
${Qt5Network_LIBRARIES}
${Qt5Xml_LIBRARIES}
${Qt5Svg_LIBRARIES}
${Qt5Qml_LIBRARIES}
amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml xxhash ${ZLIB_LIBRARIES} lzokay)

96
Editor/Common.cpp Normal file
View File

@@ -0,0 +1,96 @@
#include "Common.hpp"
#include "MainWindow.hpp"
#include <QMessageBox>
#include <QObject>
#include <QProcess>
boo::SystemString QStringToSysString(const QString& str) {
#ifdef _WIN32
return (wchar_t*)str.utf16();
#else
return str.toUtf8().toStdString();
#endif
}
QString SysStringToQString(const boo::SystemString& str) {
#ifdef _WIN32
return QString::fromStdWString(str);
#else
return QString::fromStdString(str);
#endif
}
bool MkPath(const QString& path, UIMessenger& messenger) {
QFileInfo fInfo(path);
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
}
bool MkPath(const 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 QString browserArgs = QStringLiteral("xdg-open \"%1\"").arg(QFileInfo(folder).path());
browserProc.startDetached(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;
}

53
Editor/Common.hpp Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include "boo/System.hpp"
#include <QString>
#include <QDir>
#include <QMessageBox>
class MainWindow;
extern MainWindow* g_MainWindow;
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);
};
boo::SystemString QStringToSysString(const QString& str);
QString SysStringToQString(const boo::SystemString& 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();
static QLatin1String StringViewToQString(std::string_view sv) { return QLatin1String(sv.data(), int(sv.size())); }
/* Used for generating transform matrices to map SVG coordinate space */
QTransform RectToRect(const QRectF& from, const QRectF& to);
namespace std {
template <>
struct hash<QString> {
std::size_t operator()(const QString& s) const noexcept { return qHash(s); }
};
} // namespace std

271
Editor/CurveEditor.cpp Normal file
View File

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

63
Editor/CurveEditor.hpp Normal file
View File

@@ -0,0 +1,63 @@
#pragma once
#include "EditorWidget.hpp"
#include <QFrame>
#include <QLabel>
#include <QStaticText>
#include <QLineEdit>
#include <QPushButton>
#include <QJSEngine>
class CurveEditor;
class CurveView : public QWidget {
Q_OBJECT
friend class CurveControls;
amuse::ObjToken<ProjectModel::CurveNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
QStaticText m_percentTextsCenter[11];
CurveEditor* getEditor() const;
public:
explicit CurveView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
};
class CurveControls : public QFrame {
Q_OBJECT
friend class CurveView;
QLineEdit* m_lineEdit;
QLabel* m_errLabel;
QPushButton* m_setExpr;
QJSEngine m_engine;
CurveEditor* getEditor() const;
public:
explicit CurveControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
void resizeEvent(QResizeEvent* ev);
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);
bool loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
};

275
Editor/EditorWidget.cpp Normal file
View File

@@ -0,0 +1,275 @@
#include "EditorWidget.hpp"
#include "MainWindow.hpp"
#include <QStandardItemModel>
#include <QHBoxLayout>
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) {}
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, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
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, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
m_slider.setRange(0, 1000);
m_slider.setValue(0);
}
double FieldDoubleSlider::value() const {
double t = m_slider.value() / 1000.0;
return t * (m_max - m_min) + m_min;
}
void FieldDoubleSlider::setValue(double value) {
if (value < m_min)
value = m_min;
else if (value > m_max)
value = m_max;
double t = (value - m_min) / (m_max - m_min);
m_slider.setValue(int(t * 1000.0));
doValueChanged(int(t * 1000.0));
}
void FieldDoubleSlider::setRange(double min, double max) {
m_min = min;
m_max = max;
double curValue = value();
if (curValue < min)
setValue(min);
else if (curValue > max)
setValue(max);
}
void FieldDoubleSlider::doValueChanged(int value) {
double t = value / 1000.0;
t = t * (m_max - m_min) + m_min;
m_value.setText(QString::number(t, 'g', 2));
emit valueChanged(t);
}
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, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
m_button.setIcon(icon);
QHBoxLayout* layout = new QHBoxLayout;
layout->setContentsMargins({});
layout->setSpacing(0);
layout->addWidget(&m_comboBox);
layout->addWidget(&m_button);
setLayout(layout);
setCollection(collection);
}
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, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
m_button.setIcon(icon);
QHBoxLayout* layout = new QHBoxLayout;
layout->setContentsMargins({});
layout->setSpacing(0);
layout->addWidget(&m_comboBox);
layout->addWidget(&m_button);
setLayout(layout);
setGroup(group);
}
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(QEvent* 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, SIGNAL(triggered()), this, SLOT(doOpenEditor()));
menu->addAction(openEditorAction);
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
findUsagesAction->setData(QVariant::fromValue((void*)node));
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(doFindUsages()));
menu->addAction(findUsagesAction);
menu->popup(ev->globalPos());
}
}
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);
}

241
Editor/EditorWidget.hpp Normal file
View File

@@ -0,0 +1,241 @@
#pragma once
#include <QWidget>
#include <QUndoCommand>
#include <QApplication>
#include <QSpinBox>
#include <QComboBox>
#include <QWheelEvent>
#include <QItemEditorFactory>
#include <QToolButton>
#include <QAction>
#include <QPushButton>
#include <QLabel>
#include <QMenu>
#include <QStyledItemDelegate>
#include "ProjectModel.hpp"
class EditorWidget : public QWidget {
Q_OBJECT
public:
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
virtual bool valid() const { return true; }
virtual void unloadData() {}
virtual ProjectModel::INode* currentNode() const { return nullptr; }
virtual void setEditorEnabled(bool en) { setEnabled(en); }
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
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();
void redo();
};
class FieldSpinBox : public QSpinBox {
Q_OBJECT
public:
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR) : QSpinBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { 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) { 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) { 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) { 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);
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);
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);
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);
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 {
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(QEvent* event);
void leaveEvent(QEvent* event);
};
class ContextMenu : public QMenu {
public:
void hideEvent(QHideEvent* ev) {
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);
private slots:
void doOpenEditor();
void doFindUsages();
};

188
Editor/KeyboardWidget.cpp Normal file
View File

@@ -0,0 +1,188 @@
#include "KeyboardWidget.hpp"
#include <QHBoxLayout>
#include <QSvgRenderer>
#include <QMouseEvent>
#include <QScrollArea>
#include <QApplication>
#include <QScrollBar>
const QString NaturalKeyNames[] = {QStringLiteral("C"), QStringLiteral("D"), QStringLiteral("E"), QStringLiteral("F"),
QStringLiteral("G"), QStringLiteral("A"), QStringLiteral("B")};
const QString SharpKeyNames[] = {QStringLiteral("Cs"), QStringLiteral("Ds"), QStringLiteral("Fs"), QStringLiteral("Gs"),
QStringLiteral("As")};
const QString KeyStrings[] = {QStringLiteral("C"), QStringLiteral("C#"), QStringLiteral("D"), QStringLiteral("D#"),
QStringLiteral("E"), QStringLiteral("F"), QStringLiteral("F#"), QStringLiteral("G"),
QStringLiteral("G#"), QStringLiteral("A"), QStringLiteral("A#"), QStringLiteral("B")};
const int NaturalKeyNumbers[] = {0, 2, 4, 5, 7, 9, 11};
const int SharpKeyNumbers[] = {1, 3, 6, 8, 10};
KeyboardOctave::KeyboardOctave(int octave, const QString& svgPath, QWidget* parent)
: QSvgWidget(svgPath, parent), m_octave(octave) {
for (int i = 0; i < 7; ++i)
if (renderer()->elementExists(NaturalKeyNames[i]))
m_natural[i] =
renderer()->matrixForElement(NaturalKeyNames[i]).mapRect(renderer()->boundsOnElement(NaturalKeyNames[i]));
for (int i = 0; i < 5; ++i)
if (renderer()->elementExists(SharpKeyNames[i]))
m_sharp[i] =
renderer()->matrixForElement(SharpKeyNames[i]).mapRect(renderer()->boundsOnElement(SharpKeyNames[i]));
/* The parent keyboard manages all mouse events */
setAttribute(Qt::WA_TransparentForMouseEvents);
}
int KeyboardOctave::getKey(const QPoint& localPos) const {
QPointF localPoint = m_widgetToSvg.map(localPos);
for (int i = 0; i < 5; ++i)
if (m_sharp[i].contains(localPoint))
return SharpKeyNumbers[i];
for (int i = 0; i < 7; ++i)
if (m_natural[i].contains(localPoint))
return NaturalKeyNumbers[i];
return -1;
}
void KeyboardOctave::resizeEvent(QResizeEvent* event) { m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF()); }
KeyboardWidget::KeyboardWidget(QWidget* parent) : QWidget(parent) {
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
for (int i = 0; i < 10; ++i) {
m_widgets[i] = new KeyboardOctave(i, QStringLiteral(":/bg/keyboard.svg"), this);
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[i]);
}
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);
}
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) {
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(QEvent* 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) {}
void KeyboardSlider::enterEvent(QEvent* 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);
}

101
Editor/KeyboardWidget.hpp Normal file
View File

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

465
Editor/KeymapEditor.cpp Normal file
View File

@@ -0,0 +1,465 @@
#include "KeymapEditor.hpp"
#include "MainWindow.hpp"
#include "KeyboardWidget.hpp"
#include <QVBoxLayout>
#include <QPainter>
#include <QScrollBar>
static const int HueTable[] = {0, 30, 60, 80, 120, 170, 200, 240, 280, 320};
static const int SaturationTable[] = {255, 255, 255, 255, 255, 127, 127, 127, 127, 127, 63, 63, 63};
static const int ValueTable[] = {240, 200, 160, 120, 80, 240, 200, 160, 120, 80, 240, 200, 160};
static const QPoint 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}};
static const int RadiusTable[] = {14, 10, 14, 10, 14, 14, 10, 14, 10, 14, 10, 14};
static const QColor 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};
static const QColor 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();
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
}
ProjectModel::INode* KeymapView::currentNode() const { return m_node.get(); }
void KeymapView::drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const QColor* keyPalette, int o,
int k) const {
int keyIdx = o * 12 + k;
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 QColor* keyPalette = getEditor()->m_paintPalette;
qreal deviceRatio = devicePixelRatioF();
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
painter.setFont(m_keyFont);
int kbY = height() / 2 - 100;
for (int o = 0; o < 10; ++o) {
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);
}
}
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 {
QPointF localPoint = m_widgetToSvg.map(localPos);
for (int i = 0; i < 5; ++i)
if (m_sharp[i].contains(localPoint))
return SharpKeyNumbers[i];
for (int i = 0; i < 7; ++i)
if (m_natural[i].contains(localPoint))
return NaturalKeyNumbers[i];
return -1;
}
void KeymapView::mouseMoveEvent(QMouseEvent* ev) {
int octave = ev->x() / 280;
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 (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);
int k = 0;
for (int i = 0; i < 11; ++i)
for (int j = 0; j < 12 && k < 128; ++j)
m_keyTexts[k++].setText(QStringLiteral("%1%2").arg(KeyStrings[j]).arg(i - 1));
m_keyFont.setPointSize(12);
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
for (int i = 0; i < 7; ++i)
if (m_octaveRenderer.elementExists(NaturalKeyNames[i]))
m_natural[i] = m_octaveRenderer.matrixForElement(NaturalKeyNames[i])
.mapRect(m_octaveRenderer.boundsOnElement(NaturalKeyNames[i]));
for (int i = 0; i < 5; ++i)
if (m_octaveRenderer.elementExists(SharpKeyNames[i]))
m_sharp[i] = m_octaveRenderer.matrixForElement(SharpKeyNames[i])
.mapRect(m_octaveRenderer.boundsOnElement(SharpKeyNames[i]));
m_widgetToSvg = RectToRect(QRect(0, 0, 280, 200), m_octaveRenderer.viewBoxF());
}
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::Background, QWidget::palette().color(QPalette::Background));
palette.setColor(QPalette::Button, QWidget::palette().color(QPalette::Button));
} else {
const QColor* keyPalette = getEditor()->m_paintPalette;
palette.setColor(QPalette::Background, keyPalette[idx]);
palette.setColor(QPalette::Button, keyPalette[idx].darker(300));
}
m_paintButton->setPalette(palette);
}
void KeymapControls::setKeymap(const amuse::Keymap& km) {
m_enableUpdate = false;
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::Background));
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, SIGNAL(currentIndexChanged(int)), this, SLOT(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, SIGNAL(valueChanged(int)), this, SLOT(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, SIGNAL(valueChanged(int)), this, SLOT(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, SIGNAL(stateChanged(int)), this, SLOT(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, SIGNAL(valueChanged(int)), this, SLOT(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, SIGNAL(pressed()), this, SLOT(paintButtonPressed()));
rightLayout->addWidget(m_paintButton);
rightLayout->setContentsMargins(0, 0, 10, 0);
mainLayout->addLayout(leftLayout);
mainLayout->addStretch(1);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
}
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 (int i = 0; i < 129; ++i)
if (!m_idxBitmap[i]) {
m_configToIdx[key] = std::make_pair(i, 1);
m_idxBitmap.set(i);
return 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 {
auto search = m_configToIdx.find(key);
if (search != m_configToIdx.end())
return search->second.first;
for (int i = 0; i < 129; ++i)
if (!m_idxBitmap[i])
return i;
Q_UNREACHABLE();
}
bool KeymapEditor::loadData(ProjectModel::KeymapNode* node) {
if (m_kmView->m_node.get() != node) {
m_configToIdx.clear();
m_idxBitmap.reset();
for (int i = 0; i < 128; ++i) {
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) {
int k = 0;
for (int i = 0; i < 13; ++i)
for (int j = 0; j < 10 && k < 129; ++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);
}

105
Editor/KeymapEditor.hpp Normal file
View File

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

620
Editor/LayersEditor.cpp Normal file
View File

@@ -0,0 +1,620 @@
#include "LayersEditor.hpp"
#include "MainWindow.hpp"
#include <QVBoxLayout>
#include <QScrollBar>
#include <QMimeData>
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() {
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() {
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;
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) {}
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, SIGNAL(currentIndexChanged(int)), this, SLOT(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() {
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() {
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() {
m_undid = true;
EditorUndoCommand::undo();
}
void redo() {
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() {
base::undo();
base::del();
}
void redo() {
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() {
base::undo();
base::add();
}
void redo() {
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;
bool moving = beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
if (destinationChild < sourceRow) {
for (int i = 0; i < count; ++i) {
amuse::LayerMapping tmp = std::move(layers[sourceRow]);
for (int j = sourceRow; j != destinationChild; --j)
layers[j] = std::move(layers[j - 1]);
layers[destinationChild] = std::move(tmp);
++sourceRow;
++destinationChild;
}
} else if (destinationChild > sourceRow) {
for (int i = 0; i < count; ++i) {
amuse::LayerMapping tmp = std::move(layers[sourceRow]);
for (int j = sourceRow; j != destinationChild - 1; ++j)
layers[j] = std::move(layers[j + 1]);
layers[destinationChild - 1] = std::move(tmp);
}
}
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) {}
void LayersTableView::deleteSelection() {
QModelIndexList list = selectionModel()->selectedRows();
if (list.isEmpty())
return;
std::vector<std::pair<amuse::LayerMapping, int>> data;
data.reserve(list.size());
for (QModelIndex idx : list)
data.push_back(std::make_pair(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);
}
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() {
QModelIndex idx = m_tableView.selectionModel()->currentIndex();
std::vector<std::pair<amuse::LayerMapping, int>> data;
if (!idx.isValid())
data.push_back(std::make_pair(amuse::LayerMapping{}, m_model.rowCount() - 1));
else
data.push_back(std::make_pair(amuse::LayerMapping{}, idx.row()));
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(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this,
SLOT(doSelectionChanged()));
connect(&m_model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this,
SLOT(rowsInserted(const QModelIndex&, int, int)));
connect(&m_model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this,
SLOT(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
m_addRemoveButtons.addAction()->setToolTip(tr("Add new layer mapping"));
connect(m_addRemoveButtons.addAction(), SIGNAL(triggered(bool)), this, SLOT(doAdd()));
m_addRemoveButtons.removeAction()->setToolTip(tr("Remove selected layer mappings"));
connect(m_addRemoveButtons.removeAction(), SIGNAL(triggered(bool)), this, SLOT(itemDeleteAction()));
}

88
Editor/LayersEditor.hpp Normal file
View File

@@ -0,0 +1,88 @@
#pragma once
#include "EditorWidget.hpp"
#include <QAbstractTableModel>
#include <QTableView>
#include <QAction>
#include <QToolButton>
#include <QStyledItemDelegate>
class SoundMacroDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
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);
void loadData(ProjectModel::LayersNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
Qt::DropActions supportedDropActions() const;
Qt::DropActions supportedDragActions() const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent,
int destinationChild);
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
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);
void setModel(QAbstractItemModel* model);
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);
bool loadData(ProjectModel::LayersNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void resizeEvent(QResizeEvent* ev);
AmuseItemEditFlags itemEditFlags() const;
private slots:
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void itemDeleteAction();
};

144
Editor/MIDIReader.cpp Normal file
View File

@@ -0,0 +1,144 @@
#include "MIDIReader.hpp"
#include "MainWindow.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());
}

48
Editor/MIDIReader.hpp Normal file
View File

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

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
}

1833
Editor/MainWindow.cpp Normal file

File diff suppressed because it is too large Load Diff

297
Editor/MainWindow.hpp Normal file
View File

@@ -0,0 +1,297 @@
#pragma once
#include <QMainWindow>
#include <QUndoStack>
#include <QProgressDialog>
#include <QThread>
#include <QStyledItemDelegate>
#include <QSortFilterProxyModel>
#include <QLinkedList>
#include <QFileDialog>
#include "ui_MainWindow.h"
#include "amuse/Engine.hpp"
#include "amuse/BooBackend.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "ProjectModel.hpp"
#include "EditorWidget.hpp"
#include "MIDIReader.hpp"
#include "StudioSetupWidget.hpp"
#define MaxRecentFiles 4
namespace Ui {
class MainWindow;
}
class MainWindow;
class SongGroupEditor;
class SoundGroupEditor;
class SoundMacroEditor;
class ADSREditor;
class CurveEditor;
class KeymapEditor;
class LayersEditor;
class SampleEditor;
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);
public slots:
void doExportGroup();
void doFindUsages();
void doCut();
void doCopy();
void doPaste();
void doDuplicate();
void doDelete();
void doRename();
};
class MainWindow : public QMainWindow {
friend class MIDIReader;
friend class ProjectModel;
friend class GroupNodeUndoCommand;
friend class TreeDelegate;
Q_OBJECT
Ui::MainWindow m_ui;
QAction* m_goBack;
QAction* m_goForward;
QLinkedList<ProjectModel::INode*> m_navList;
QLinkedList<ProjectModel::INode*>::iterator m_navIt;
QAction* m_clearRecentFileAct;
QAction* m_recentFileActs[MaxRecentFiles];
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
QWidget* m_faceSvg;
SongGroupEditor* m_songGroupEditor = nullptr;
SoundGroupEditor* m_soundGroupEditor = nullptr;
SoundMacroEditor* m_soundMacroEditor = nullptr;
ADSREditor* m_adsrEditor = nullptr;
CurveEditor* m_curveEditor = nullptr;
KeymapEditor* m_keymapEditor = nullptr;
LayersEditor* m_layersEditor = nullptr;
SampleEditor* m_sampleEditor = nullptr;
StudioSetupWidget* m_studioSetup = nullptr;
QFileDialog m_openDirectoryDialog;
QFileDialog m_openFileDialog;
QFileDialog m_newFileDialog;
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<VoiceAllocator> m_voxAllocator;
std::unique_ptr<amuse::Engine> m_engine;
amuse::ObjToken<amuse::Voice> m_lastSound;
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
int m_velocity = 90;
float m_pitch = 0.f;
int8_t m_ctrlVals[128] = {};
float m_auxAVol = 0.f;
float m_auxBVol = 0.f;
bool m_uiDisabled = false;
bool m_clipboardAmuseData = false;
QUndoStack* m_undoStack;
QMetaObject::Connection m_cutConn;
QMetaObject::Connection m_copyConn;
QMetaObject::Connection m_pasteConn;
QMetaObject::Connection m_deleteConn;
QMetaObject::Connection m_canEditConn;
BackgroundTask* m_backgroundTask = nullptr;
QProgressDialog* m_backgroundDialog = nullptr;
QThread m_backgroundThread;
uint64_t m_timerFireCount = 0;
int m_peakVoices = 0;
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
void updateWindowTitle();
void updateRecentFileActions();
void updateNavigationButtons();
bool setProjectPath(const QString& path);
void refreshAudioIO();
void refreshMIDIIO();
void timerEvent(QTimerEvent* ev);
void setSustain(bool sustain);
void keyPressEvent(QKeyEvent* ev);
void keyReleaseEvent(QKeyEvent* ev);
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task);
bool _setEditor(EditorWidget* widget, bool appendNav = true);
public:
explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow();
bool openProject(const QString& path);
bool openEditor(ProjectModel::SongGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundMacroNode* node, bool appendNav = true);
bool openEditor(ProjectModel::ADSRNode* node, bool appendNav = true);
bool openEditor(ProjectModel::CurveNode* node, bool appendNav = true);
bool openEditor(ProjectModel::KeymapNode* node, bool appendNav = true);
bool openEditor(ProjectModel::LayersNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SampleNode* node, bool appendNav = true);
bool openEditor(ProjectModel::INode* node, bool appendNav = true);
void closeEditor();
ProjectModel::INode* getEditorNode() const;
EditorWidget* getEditorWidget() const;
amuse::ObjToken<amuse::Voice> startEditorVoice(uint8_t key, uint8_t vel);
amuse::ObjToken<amuse::Voice> startSFX(amuse::GroupId groupId, amuse::SFXId sfxId);
amuse::ObjToken<amuse::Sequencer> startSong(amuse::GroupId groupId, amuse::SongId songId,
const unsigned char* arrData);
void pushUndoCommand(EditorUndoCommand* cmd);
void updateFocus();
void aboutToDeleteNode(ProjectModel::INode* node);
bool askAboutSave();
void closeEvent(QCloseEvent* ev);
void showEvent(QShowEvent* ev);
QString getGroupName(ProjectModel::GroupNode* group) const;
ProjectModel::GroupNode* getSelectedGroupNode() const;
QString getSelectedGroupName() const;
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
ProjectModel* projectModel() const { return m_projectModel; }
UIMessenger& uiMessenger() { return m_mainMessenger; }
void setItemEditFlags(AmuseItemEditFlags flags);
void setItemNewEnabled(bool enabled);
AmuseItemEditFlags outlineEditFlags();
bool isUiDisabled() const { return m_uiDisabled; }
void findUsages(ProjectModel::INode* node);
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,157 @@
#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);
QObject::connect(&m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
QObject::connect(&m_buttonBox, SIGNAL(rejected()), this, SLOT(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);
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <QDialog>
#include <QLineEdit>
#include <QComboBox>
#include <QDialogButtonBox>
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);
QString getName() const { return m_le.text(); }
const SoundMacroTemplateEntry* getSelectedTemplate() const;
};

2093
Editor/ProjectModel.cpp Normal file

File diff suppressed because it is too large Load Diff

467
Editor/ProjectModel.hpp Normal file
View File

@@ -0,0 +1,467 @@
#pragma once
#include <QAbstractItemModel>
#include <QIdentityProxyModel>
#include <QSortFilterProxyModel>
#include <QDir>
#include <QIcon>
#include <map>
#include "Common.hpp"
#include "NewSoundMacroDialog.hpp"
#include "amuse/AudioGroup.hpp"
#include "amuse/AudioGroupData.hpp"
#include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupPool.hpp"
#include "amuse/AudioGroupSampleDirectory.hpp"
class ProjectModel;
class EditorUndoCommand;
enum AmuseItemEditFlags {
AmuseItemNone = 0,
AmuseItemCut = 1,
AmuseItemCopy = 2,
AmuseItemPaste = 4,
AmuseItemDelete = 8,
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
};
class OutlineFilterProxyModel : public QSortFilterProxyModel {
Q_OBJECT
QRegExp m_usageKey;
public:
explicit OutlineFilterProxyModel(ProjectModel* source);
public slots:
void setFilterRegExp(const QString& pattern);
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
};
class NullItemProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit NullItemProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
};
class PageObjectProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit PageObjectProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const;
};
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;
OutlineFilterProxyModel m_outlineProxy;
NullItemProxyModel m_nullProxy;
PageObjectProxyModel m_pageObjectProxy;
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:
virtual ~INode() = default;
INode(const QString& name);
INode(INode* parent) : m_parent(parent), m_row(0) { /* ONLY USED BY NULL NODE! */
}
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 {
NullNode(INode* parent) : INode(parent) {}
Type type() const { return Type::Null; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
};
struct RootNode final : INode {
RootNode() : INode(QStringLiteral("<root>")) {}
Type type() const { return Type::Root; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
};
struct CollectionNode;
struct BasePoolObjectNode;
struct GroupNode final : INode {
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
GroupNode(const QString& name) : INode(name) {}
GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
: INode(it->first), m_it(it) {}
int hypotheticalIndex(const QString& name) const;
void _sortChildren();
static QIcon Icon;
Type type() const { return Type::Group; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemNoCut; }
CollectionNode* getCollectionOfType(Type tp) const;
amuse::AudioGroupDatabase* getAudioGroup() const { return m_it->second.get(); }
BasePoolObjectNode* pageObjectNodeOfId(amuse::ObjectId id) const;
};
struct SongGroupNode final : INode {
amuse::GroupId m_id;
amuse::ObjToken<amuse::SongGroupIndex> m_index;
SongGroupNode(const QString& name, amuse::ObjToken<amuse::SongGroupIndex> index) : INode(name), m_index(index) {}
SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SongGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
void registerNames(const NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_midiSetups)
registry.registerSongName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_midiSetups)
registry.unregisterSongName(p.first);
}
};
struct SoundGroupNode final : INode {
amuse::GroupId m_id;
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
SoundGroupNode(const QString& name, amuse::ObjToken<amuse::SFXGroupIndex> index) : INode(name), m_index(index) {}
SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SoundGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
void registerNames(const NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_sfxEntries)
registry.registerSFXName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_sfxEntries)
registry.unregisterSFXName(p.first);
}
};
struct CollectionNode final : INode {
QIcon m_icon;
Type m_collectionType;
CollectionNode(const QString& name, const QIcon& icon, Type collectionType)
: INode(name), m_icon(icon), m_collectionType(collectionType) {}
Type type() const { return Type::Collection; }
QString text() const { return m_name; }
QIcon icon() const { return m_icon; }
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
Type collectionType() const { return m_collectionType; }
int indexOfId(amuse::ObjectId id) const;
amuse::ObjectId idOfIndex(int idx) const;
BasePoolObjectNode* nodeOfIndex(int idx) const;
BasePoolObjectNode* nodeOfId(amuse::ObjectId id) const;
};
struct BasePoolObjectNode : INode {
amuse::ObjectId m_id;
BasePoolObjectNode(const QString& name) : INode(name) {}
BasePoolObjectNode(amuse::ObjectId id, const QString& name) : INode(name), m_id(id) {}
amuse::ObjectId id() const { return m_id; }
QString text() const { return m_name; }
QIcon icon() const { return {}; }
};
template <class ID, class T, INode::Type TP>
struct PoolObjectNode final : BasePoolObjectNode {
amuse::ObjToken<T> m_obj;
PoolObjectNode(const QString& name, amuse::ObjToken<T> obj) : BasePoolObjectNode(name), m_obj(obj) {}
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
: BasePoolObjectNode(id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
Type type() const { return TP; }
AmuseItemEditFlags editFlags() const { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
void registerNames(const NameUndoRegistry& registry) const {
ID::CurNameDB->registerPair(text().toUtf8().data(), m_id);
}
void unregisterNames(NameUndoRegistry& registry) const { ID::CurNameDB->remove(m_id); }
amuse::NameDB* getNameDb() const { return ID::CurNameDB; }
};
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
using KeymapNode = PoolObjectNode<amuse::KeymapId, std::array<amuse::Keymap, 128>, INode::Type::Keymap>;
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
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);
public:
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
bool clearProjectData();
bool openGroupData(const QString& groupName, UIMessenger& messenger);
void openSongsData();
void importSongsData(const QString& path);
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data, ImportMode mode,
UIMessenger& messenger);
void saveSongsIndex();
bool saveToFile(UIMessenger& messenger);
QStringList getGroupList() const;
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
void updateNodeNames();
bool ensureModelData();
QModelIndex proxyCreateIndex(int arow, int acolumn, void* adata) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex index(INode* node) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex& index) const;
INode* node(const QModelIndex& index) const;
GroupNode* getGroupNode(INode* node) const;
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
RootNode* rootNode() const { return m_root.get(); }
void _postAddNode(INode* n, const NameUndoRegistry& registry);
void _preDelNode(INode* n, NameUndoRegistry& registry);
void _addNode(GroupNode* node, std::unique_ptr<amuse::AudioGroupDatabase>&& data, const NameUndoRegistry& registry);
std::unique_ptr<amuse::AudioGroupDatabase> _delNode(GroupNode* node, NameUndoRegistry& registry);
GroupNode* newSubproject(const QString& name);
template <class NT, class T>
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name);
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SongGroupNode* newSongGroup(GroupNode* group, const QString& name);
template <class NT, class T>
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name, const SoundMacroTemplateEntry* templ = nullptr);
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
ADSRNode* newADSR(GroupNode* group, const QString& name);
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
CurveNode* newCurve(GroupNode* group, const QString& name);
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
KeymapNode* newKeymap(GroupNode* group, const QString& name);
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
LayersNode* newLayers(GroupNode* group, const QString& name);
void _renameNode(INode* node, const QString& name);
template <class NT>
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
template <class NT>
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList& indexes) const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
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, const QString& path);
std::pair<amuse::SongId, std::string> bootstrapSongId();
void allocateSongId(amuse::SongId id, std::string_view name);
void deallocateSongId(amuse::SongId oldId);
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
void setIdDatabases(INode* context) const;
};

767
Editor/SampleEditor.cpp Normal file
View File

@@ -0,0 +1,767 @@
#include "SampleEditor.hpp"
#include "MainWindow.hpp"
#include "amuse/DSPCodec.hpp"
#include <QPainter>
#include <QPaintEvent>
#include <QSpinBox>
#include <QScrollBar>
#include <QCheckBox>
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 (int 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 (int 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 (int 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();
m_playbackMacro->insertNewCmd(1, 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);
}
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() {
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() {
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) {
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 { 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() {
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() {
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) {
if (other->id() == id()) {
m_redoPitchVal = static_cast<const SampPitchUndoCommand*>(other)->m_redoPitchVal;
return true;
}
return false;
}
int id() const { 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());
amuse::SystemString 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, SIGNAL(clicked(bool)), this, SLOT(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, SIGNAL(clicked(bool)), this, SLOT(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 = SysStringToQString(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::Background));
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, SIGNAL(valueChanged(int)), this, SLOT(zoomSliderChanged(int)));
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, SIGNAL(stateChanged(int)), this, SLOT(loopStateChanged(int)));
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, SIGNAL(valueChanged(int)), this, SLOT(startValueChanged(int)));
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, SIGNAL(valueChanged(int)), this, SLOT(endValueChanged(int)));
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, SIGNAL(valueChanged(int)), this, SLOT(pitchValueChanged(int)));
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, SIGNAL(clicked(bool)), this, SLOT(showInBrowser()));
rightLayout->addWidget(m_showInBrowser);
mainLayout->addLayout(leftLayout);
mainLayout->addStretch(1);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
}
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);
}

107
Editor/SampleEditor.hpp Normal file
View File

@@ -0,0 +1,107 @@
#pragma once
#include "EditorWidget.hpp"
#include "ProjectModel.hpp"
#include <QScrollArea>
#include <QSlider>
#include <QCheckBox>
#include <QSpinBox>
class SampleEditor;
class SampleView : public QWidget {
Q_OBJECT
friend class SampleControls;
qreal m_baseSamplesPerPx = 100.0;
qreal m_samplesPerPx = 100.0;
qreal m_zoomFactor = 1.0;
amuse::ObjToken<ProjectModel::SampleNode> m_node;
amuse::ObjToken<amuse::SampleEntryData> m_sample;
amuse::ObjToken<amuse::SoundMacro> m_playbackMacro;
const unsigned char* m_sampleData = nullptr;
qreal m_curSamplePos = 0.0;
int16_t m_prev1 = 0;
int16_t m_prev2 = 0;
QFont m_rulerFont;
int m_displaySamplePos = -1;
enum class DragState { None, Start, End };
DragState m_dragState = DragState::None;
void seekToSample(qreal sample);
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> iterateSampleInterval(qreal interval);
void calculateSamplesPerPx();
SampleEditor* getEditor() const;
public:
explicit SampleView(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SampleNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
amuse::SampleEntryData* entryData() const;
const amuse::SoundMacro* soundMacro() const;
void setSamplePos(int pos);
void updateSampleRange(int oldSamp, int newSamp);
void paintEvent(QPaintEvent* ev);
void resetZoom();
void setZoom(int zVal);
void showEvent(QShowEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseReleaseEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
void wheelEvent(QWheelEvent* ev);
};
class SampleControls : public QFrame {
Q_OBJECT
QString m_path;
QSlider* m_zoomSlider;
QCheckBox* m_loopCheck;
QSpinBox* m_loopStart;
QSpinBox* m_loopEnd;
QSpinBox* m_basePitch;
QPushButton* m_makeOtherVersion;
QMetaObject::Connection m_makeOtherConn;
QPushButton* m_showInBrowser;
bool m_enableUpdate = true;
bool m_enableFileWrite = true;
public:
explicit SampleControls(QWidget* parent = Q_NULLPTR);
void doFileWrite();
void setFileWrite(bool w);
void updateFileState();
void setLoopStartSample(int sample) { m_loopStart->setValue(sample); }
void setLoopEndSample(int sample) { m_loopEnd->setValue(sample); }
void loadData(bool reset);
void unloadData();
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);
bool loadData(ProjectModel::SampleNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
const amuse::SoundMacro* soundMacro() const;
void setSamplePos(int pos);
void resizeEvent(QResizeEvent* ev);
};

1600
Editor/SongGroupEditor.cpp Normal file

File diff suppressed because it is too large Load Diff

289
Editor/SongGroupEditor.hpp Normal file
View File

@@ -0,0 +1,289 @@
#pragma once
#include "EditorWidget.hpp"
#include <QTabWidget>
#include <QAbstractTableModel>
#include <QTableView>
#include <QToolButton>
#include <QAction>
#include <QSplitter>
#include <QListView>
#include <QLineEdit>
#include <QPushButton>
#include <QFileDialog>
#include <QProxyStyle>
#include "amuse/Sequencer.hpp"
class SetupTableView;
class PageObjectDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
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);
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);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void destroyEditor(QWidget* editor, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index);
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);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
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);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
int _insertRow(std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data);
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>> _removeRow(amuse::SongId id);
};
class SetupModel : public QAbstractTableModel {
Q_OBJECT
friend class SongGroupEditor;
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
public:
explicit SetupModel(QObject* parent = Q_NULLPTR);
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
};
class PageTableView : public QTableView {
Q_OBJECT
PageObjectDelegate m_poDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
public:
explicit PageTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
};
class SetupTableView : public QSplitter {
Q_OBJECT
friend class SongGroupEditor;
friend class SetupRowUndoCommand;
QTableView* m_listView;
QTableView* m_tableView;
MIDIFileDelegate m_midiDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate;
public:
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
void deleteSelection();
void showEvent(QShowEvent* event);
};
class ColoredTabBarStyle : public QProxyStyle {
public:
using QProxyStyle::QProxyStyle;
void drawControl(QStyle::ControlElement element, const QStyleOption* option, QPainter* painter,
const QWidget* widget = nullptr) const;
};
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();
amuse::SongId songId() const { return m_songId; }
amuse::Sequencer* sequencer() const { return m_seq.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
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);
bool loadData(ProjectModel::SongGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
AmuseItemEditFlags itemEditFlags() const;
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();
};

639
Editor/SoundGroupEditor.cpp Normal file
View File

@@ -0,0 +1,639 @@
#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() {
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() {
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;
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() {
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() {
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) {}
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, SIGNAL(currentIndexChanged(int)), this, SLOT(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 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;
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() {
m_undid = true;
EditorUndoCommand::undo();
}
void redo() {
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() {
base::undo();
base::del();
}
void redo() {
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() {
base::undo();
base::add();
}
void redo() {
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) {}
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);
}
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_button(this), m_playAction(tr("Play")), 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, SIGNAL(triggered()), this, SLOT(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.push_back(std::make_tuple(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(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this,
SLOT(doSelectionChanged()));
connect(&m_sfxs, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this,
SLOT(rowsInserted(const QModelIndex&, int, int)));
connect(&m_sfxs, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(sfxDataChanged()));
connect(&m_sfxs, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), this,
SLOT(sfxDataChanged()));
connect(&m_sfxs, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this,
SLOT(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
connect(&m_sfxs, SIGNAL(modelReset()), this, SLOT(sfxDataChanged()));
m_addRemoveButtons.addAction()->setToolTip(tr("Add new SFX entry"));
connect(m_addRemoveButtons.addAction(), SIGNAL(triggered(bool)), this, SLOT(doAdd()));
m_addRemoveButtons.removeAction()->setToolTip(tr("Remove selected SFX entries"));
connect(m_addRemoveButtons.removeAction(), SIGNAL(triggered(bool)), this, SLOT(itemDeleteAction()));
}

125
Editor/SoundGroupEditor.hpp Normal file
View File

@@ -0,0 +1,125 @@
#pragma once
#include "EditorWidget.hpp"
#include <QStyledItemDelegate>
#include <QTableView>
#include "amuse/Voice.hpp"
class SFXObjectDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
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);
void loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
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);
void setModel(QAbstractItemModel* model);
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();
amuse::SongId sfxId() const { return m_sfxId; }
amuse::Voice* voice() const { return m_vox.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
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);
bool loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSFXListView() const { return m_sfxTable; }
AmuseItemEditFlags itemEditFlags() const;
private slots:
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void sfxDataChanged();
void itemDeleteAction();
};

1047
Editor/SoundMacroEditor.cpp Normal file

File diff suppressed because it is too large Load Diff

185
Editor/SoundMacroEditor.hpp Normal file
View File

@@ -0,0 +1,185 @@
#pragma once
#include "EditorWidget.hpp"
#include <QStaticText>
#include <QVBoxLayout>
#include <QPropertyAnimation>
#include <QSplitter>
#include <QLabel>
#include <QMouseEvent>
#include <QSpinBox>
#include <QComboBox>
#include <QTreeWidget>
#include <QPushButton>
class SoundMacroEditor;
class SoundMacroListing;
class CatalogueItem;
class TargetButton : public QPushButton {
Q_OBJECT
public:
explicit TargetButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
};
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();
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);
void paintEvent(QPaintEvent* event);
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);
};
class SoundMacroListing : public QWidget {
Q_OBJECT
friend class CommandWidget;
friend class SoundMacroEditor;
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
QVBoxLayout* m_layout;
QLayoutItem* m_dragItem = nullptr;
int m_origIdx = -1;
int m_dragOpenIdx = -1;
CommandWidgetContainer* m_prevDragOpen = nullptr;
int m_autoscrollTimer = -1;
int m_autoscrollDelta = 0;
QWidget* m_autoscrollSource = nullptr;
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
void stopAutoscroll();
bool beginDrag(CommandWidget* widget);
void endDrag();
void cancelDrag();
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
void moveDrag(CommandWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
void insertDragout();
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
void deleteCommand(int index);
void reindex();
void clear();
public:
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void timerEvent(QTimerEvent* event);
};
class CatalogueItem : public QWidget {
Q_OBJECT
amuse::SoundMacro::CmdOp m_op;
QLabel m_iconLab;
QLabel m_label;
public:
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name, const QString& doc,
QWidget* parent = Q_NULLPTR);
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
QString getText() const { return m_label.text(); }
};
class SoundMacroCatalogue : public QTreeWidget {
Q_OBJECT
public:
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
};
class SoundMacroEditor : public EditorWidget {
Q_OBJECT
friend class SoundMacroCatalogue;
friend class FieldSoundMacroStep;
QSplitter* m_splitter;
SoundMacroListing* m_listing;
SoundMacroCatalogue* m_catalogue;
CommandWidget* m_draggedCmd = nullptr;
CatalogueItem* m_draggedItem = nullptr;
FieldSoundMacroStep* m_targetField = nullptr;
QPoint m_draggedPt;
int m_dragInsertIdx = -1;
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
void beginStepTarget(FieldSoundMacroStep* stepField);
void endStepTarget();
public:
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event);
public slots:
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
};

View File

@@ -0,0 +1,97 @@
#include "StatusBarWidget.hpp"
FXButton::FXButton(QWidget* parent) : QPushButton(parent) {
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
}
StatusBarWidget::StatusBarWidget(QWidget* parent)
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal), m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal) {
addWidget(&m_normalMessage);
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
m_killButton.setVisible(false);
m_killButton.setToolTip(tr("Immediately kill active voices"));
m_voiceCount.setVisible(false);
m_volumeIcons[0] = QIcon(QStringLiteral(":/icons/IconVolume0.svg"));
m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1.svg"));
m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2.svg"));
m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3.svg"));
m_aIcon.setFixedSize(16, 16);
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
QString aTip = tr("Aux A send level for all voices");
m_aIcon.setToolTip(aTip);
m_aSlider.setRange(0, 100);
m_aSlider.setFixedWidth(100);
m_aSlider.setToolTip(aTip);
m_bIcon.setFixedSize(16, 16);
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
QString bTip = tr("Aux B send level for all voices");
m_bIcon.setToolTip(bTip);
m_bSlider.setRange(0, 100);
m_bSlider.setFixedWidth(100);
m_bSlider.setToolTip(bTip);
m_volumeIcon.setFixedSize(16, 16);
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
QString volTip = tr("Master volume level");
m_volumeIcon.setToolTip(volTip);
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
m_volumeSlider.setRange(0, 100);
m_volumeSlider.setFixedWidth(100);
m_volumeSlider.setToolTip(volTip);
addPermanentWidget(&m_voiceCount);
addPermanentWidget(&m_killButton);
addPermanentWidget(&m_fxButton);
addPermanentWidget(&m_aIcon);
addPermanentWidget(&m_aSlider);
addPermanentWidget(&m_bIcon);
addPermanentWidget(&m_bSlider);
addPermanentWidget(&m_volumeIcon);
addPermanentWidget(&m_volumeSlider);
}
void StatusBarWidget::setVoiceCount(int voices) {
if (voices != m_cachedVoiceCount) {
m_voiceCount.setText(QString::number(voices));
m_cachedVoiceCount = voices;
setKillVisible(voices != 0);
}
}
void StatusBarWidget::volumeChanged(int vol) {
int idx = int(std::round(vol * (3.f / 100.f)));
if (idx != m_lastVolIdx) {
m_lastVolIdx = idx;
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
}
}
void 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,80 @@
#pragma once
#include <QStatusBar>
#include <QLabel>
#include <QPushButton>
#include <QSlider>
#include <QMouseEvent>
#include <cmath>
class StatusBarFocus;
class FXButton : public QPushButton {
Q_OBJECT
public:
explicit FXButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
};
class StatusBarWidget : public QStatusBar {
friend class StatusBarFocus;
Q_OBJECT
QLabel m_normalMessage;
QPushButton m_killButton;
FXButton m_fxButton;
QIcon m_volumeIcons[4];
QLabel m_volumeIcon;
QSlider m_volumeSlider;
QLabel m_aIcon;
QSlider m_aSlider;
QLabel m_bIcon;
QSlider m_bSlider;
int m_lastVolIdx = 0;
QLabel m_voiceCount;
int m_cachedVoiceCount = -1;
StatusBarFocus* m_curFocus = nullptr;
void setKillVisible(bool vis) {
m_killButton.setVisible(vis);
m_voiceCount.setVisible(vis);
}
public:
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
void setVoiceCount(int voices);
void connectKillClicked(const QObject* receiver, const char* method) {
connect(&m_killButton, SIGNAL(clicked(bool)), receiver, method);
}
void connectFXPressed(const QObject* receiver, const char* method) {
connect(&m_fxButton, SIGNAL(pressed()), receiver, method);
}
void setFXDown(bool down) { m_fxButton.setDown(down); }
void connectVolumeSlider(const QObject* receiver, const char* method) {
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method);
}
void connectASlider(const QObject* receiver, const char* method) {
connect(&m_aSlider, SIGNAL(valueChanged(int)), receiver, method);
}
void connectBSlider(const QObject* receiver, const char* method) {
connect(&m_bSlider, SIGNAL(valueChanged(int)), receiver, method);
}
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
private slots:
void volumeChanged(int vol);
};
class StatusBarFocus : public QObject {
Q_OBJECT
QString m_message;
public:
explicit StatusBarFocus(StatusBarWidget* statusWidget) : QObject(statusWidget) {}
~StatusBarFocus() { exit(); }
void setMessage(const QString& message);
void enter();
void exit();
};

View File

@@ -0,0 +1,877 @@
#include "StudioSetupWidget.hpp"
#include "amuse/EffectChorus.hpp"
#include "amuse/EffectDelay.hpp"
#include "amuse/EffectReverb.hpp"
#include <QPainter>
#include <QScrollBar>
#include <QStylePainter>
using namespace std::literals;
static const 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}}};
typedef float (amuse::EffectReverbStd::*ReverbStdGetFunc)() const;
typedef void (amuse::EffectReverbStd::*ReverbStdSetFunc)(float);
static const ReverbStdGetFunc ReverbStdGetters[] = {
&amuse::EffectReverbStd::getColoration, &amuse::EffectReverbStd::getMix, &amuse::EffectReverbStd::getTime,
&amuse::EffectReverbStd::getDamping, &amuse::EffectReverbStd::getPreDelay};
static const ReverbStdSetFunc ReverbStdSetters[] = {
&amuse::EffectReverbStd::setColoration, &amuse::EffectReverbStd::setMix, &amuse::EffectReverbStd::setTime,
&amuse::EffectReverbStd::setDamping, &amuse::EffectReverbStd::setPreDelay};
static const 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}}};
typedef float (amuse::EffectReverbHi::*ReverbHiGetFunc)() const;
typedef void (amuse::EffectReverbHi::*ReverbHiSetFunc)(float);
static const ReverbHiGetFunc ReverbHiGetters[] = {
&amuse::EffectReverbHi::getColoration, &amuse::EffectReverbHi::getMix, &amuse::EffectReverbHi::getTime,
&amuse::EffectReverbHi::getDamping, &amuse::EffectReverbHi::getPreDelay, &amuse::EffectReverbHi::getCrosstalk};
static const ReverbHiSetFunc ReverbHiSetters[] = {
&amuse::EffectReverbHi::setColoration, &amuse::EffectReverbHi::setMix, &amuse::EffectReverbHi::setTime,
&amuse::EffectReverbHi::setDamping, &amuse::EffectReverbHi::setPreDelay, &amuse::EffectReverbHi::setCrosstalk};
static const 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},
}};
typedef uint32_t (amuse::EffectDelay::*DelayGetFunc)(int) const;
typedef void (amuse::EffectDelay::*DelaySetFunc)(int, uint32_t);
static const DelayGetFunc DelayGetters[] = {&amuse::EffectDelay::getChanDelay, &amuse::EffectDelay::getChanFeedback,
&amuse::EffectDelay::getChanOutput};
static const DelaySetFunc DelaySetters[] = {&amuse::EffectDelay::setChanDelay, &amuse::EffectDelay::setChanFeedback,
&amuse::EffectDelay::setChanOutput};
static const 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},
}};
typedef uint32_t (amuse::EffectChorus::*ChorusGetFunc)() const;
typedef void (amuse::EffectChorus::*ChorusSetFunc)(uint32_t);
static const ChorusGetFunc ChorusGetters[] = {&amuse::EffectChorus::getBaseDelay, &amuse::EffectChorus::getVariation,
&amuse::EffectChorus::getPeriod};
static const ChorusSetFunc ChorusSetters[] = {&amuse::EffectChorus::setBaseDelay, &amuse::EffectChorus::setVariation,
&amuse::EffectChorus::setPeriod};
static 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>
static 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>
static 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;
}
}
static const char* 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")};
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 < 8; ++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, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
layout->addWidget(slider, i, 1);
}
setLayout(layout);
}
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, SIGNAL(pressed()), this, SLOT(onPressed()));
}
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::Background);
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, SIGNAL(clicked(bool)), this, SLOT(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 < 7; ++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, SIGNAL(valueChanged(int)), this, SLOT(numChanged(int)));
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 < 8; ++i)
sb->popup()->setValue(i, GetEffectParm<uint32_t>(m_effect, f, i));
connect(sb->popup(), SIGNAL(valueChanged(int, int)), this, SLOT(chanNumChanged(int, int)));
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, SIGNAL(valueChanged(double)), this, SLOT(numChanged(double)));
layout->addWidget(sb, 1, f);
break;
}
default:
break;
}
}
}
}
mainLayout->addLayout(layout);
layout->setRowMinimumHeight(0, 22);
layout->setRowMinimumHeight(1, 22);
setLayout(mainLayout);
}
void EffectWidget::paintEvent(QPaintEvent* event) {
/* Rounded frame */
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QPoint points[] = {
{1, 20}, {1, 99}, {width() - 1, 99}, {width() - 1, 1}, {20, 1}, {1, 20},
};
painter.setBrush(palette().brush(QPalette::Background));
painter.drawPolygon(points, 6);
painter.setPen(QPen(QColor(127, 127, 127), 2.0));
painter.drawPolyline(points, 6);
QPoint 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, 7);
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::Background));
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, SIGNAL(valueChanged(const QVariant&)), parentWidget(), SLOT(update()));
connect(m_animation, SIGNAL(destroyed(QObject*)), this, SLOT(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, SIGNAL(valueChanged(const QVariant&)), parentWidget(), SLOT(update()));
connect(m_animation, SIGNAL(destroyed(QObject*)), this, SLOT(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);
}
void EffectListing::startAutoscroll(QWidget* source, QMouseEvent* event, int delta) {
if (m_autoscrollTimer == -1)
m_autoscrollTimer = startTimer(50);
m_autoscrollDelta = delta;
m_autoscrollSource = source;
m_autoscrollEvent = *event;
}
void EffectListing::stopAutoscroll() {
if (m_autoscrollTimer != -1) {
killTimer(m_autoscrollTimer);
m_autoscrollTimer = -1;
}
m_autoscrollDelta = 0;
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();
}
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());
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);
}
static const char* EffectStrings[] = {
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"), QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"), QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus")};
static const char* EffectDocStrings[] = {
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"), QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"), QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus")};
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);
QScrollArea* scrollAreas[2];
for (int i = 0; i < 2; ++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, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this,
SLOT(catalogueDoubleClicked(QTreeWidgetItem*, int)));
m_splitter->setCollapsible(0, false);
QGridLayout* layout = new QGridLayout;
layout->setContentsMargins(QMargins());
layout->addWidget(m_splitter);
setLayout(layout);
}

View File

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

119
Editor/main.cpp Normal file
View File

@@ -0,0 +1,119 @@
#include <cstdint>
#include <QApplication>
#include <QStyleFactory>
#include <QTranslator>
#include "MainWindow.hpp"
#include "SongGroupEditor.hpp"
#include "boo/IApplication.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;
}
/* This is for adapting the get*Name methods */
class BooInterface : public boo::IApplication {
std::vector<boo::SystemString> m_args;
void _deletedWindow(boo::IWindow* window) {}
public:
EPlatformType getPlatformType() const { return EPlatformType::Qt; }
int run() { return 0; }
boo::SystemStringView getUniqueName() const { return _SYS_STR("amuse-gui"sv); }
boo::SystemStringView getFriendlyName() const { return _SYS_STR("Amuse"sv); }
boo::SystemStringView getProcessName() const { return _SYS_STR("amuse-gui"sv); }
const std::vector<boo::SystemString>& getArgs() const { return m_args; }
/* Constructors/initializers for sub-objects */
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) { return {}; }
};
MainWindow* g_MainWindow = nullptr;
int main(int argc, char* argv[]) {
QApplication::setAttribute(Qt::AA_Use96Dpi);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
QApplication a(argc, argv);
QApplication::setWindowIcon(MakeAppIcon());
a.setOrganizationName("AxioDL");
a.setApplicationName("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();
BooInterface booApp;
boo::APP = &booApp;
Q_INIT_RESOURCE(translation_res);
QTranslator translator;
if (translator.load(QLocale(), QLatin1String("lang"), QLatin1String("_"), QLatin1String(":/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,18 @@
include_directories(${LIBPNG_INCLUDE_DIR})
add_executable(amuse-mkqticon mkqticon.c)
target_link_libraries(amuse-mkqticon ${PNG_LIB} ${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.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/64x64/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/48x48/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/32x32/apps/amuse.png
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/16x16/apps/amuse.png
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,9 @@
[Desktop Entry]
Name=Amuse
GenericName=MusyX Game Audio Editor
Comment=Edit Audio Data of MusyX Sound Groups
Exec=amuse-gui
Icon=amuse
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.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