Compare commits
294 Commits
Author | SHA1 | Date |
---|---|---|
Phillip Stephens | ad9bc96af4 | |
Phillip Stephens | bc1e04b116 | |
Luke Street | 101746f268 | |
Luke Street | 89986bdd65 | |
Luke Street | 7865694d75 | |
Phillip Stephens | f37a067174 | |
Luke Street | 6b73240364 | |
Luke Street | 603e066eed | |
Phillip Stephens | 6e896fa1d3 | |
Luke Street | 3290b706fc | |
Phillip Stephens | d96be61e29 | |
Luke Street | 217e791b88 | |
Luke Street | df7136d265 | |
Luke Street | 454424b8b3 | |
Phillip Stephens | 2ec749e6c7 | |
Luke Street | d3c4f568bb | |
Phillip Stephens | 1275293327 | |
Phillip Stephens | fa3188e569 | |
Phillip Stephens | 69bc5dd69f | |
Phillip Stephens | 63a58858e8 | |
Luke Street | f172427991 | |
Phillip Stephens | aa9ef6e866 | |
Phillip Stephens | cae2e9c897 | |
Luke Street | 281af5d997 | |
Luke Street | 70380d8fbc | |
Phillip Stephens | 6c7094f6fb | |
Jack Andersen | 5112228abd | |
Jack Andersen | 40efdcc38c | |
Jack Andersen | 92f44407c6 | |
Lioncash | 99f00a7cba | |
Lioncash | 5b6d736cfb | |
Lioncash | be754a44a4 | |
Lioncash | 5de0035adb | |
Phillip Stephens | 9c75aeccbe | |
Luke Street | b14b091f70 | |
Jack Andersen | c27bb5c435 | |
Jack Andersen | 5d4abb5fb1 | |
Phillip Stephens | ae26ed2fda | |
Phillip Stephens | 757defee7a | |
Phillip Stephens | 02da440523 | |
Phillip Stephens | 0dca047352 | |
Phillip Stephens | 95ffcc2d16 | |
Phillip Stephens | 035f407be7 | |
Lioncash | 321a229dfd | |
Lioncash | 60dad60448 | |
Lioncash | a8c9d555fe | |
Lioncash | 7bc23b7190 | |
Lioncash | b92674e127 | |
Lioncash | 04fbc328e9 | |
Lioncash | 1b37122b12 | |
Lioncash | 2c75668e0a | |
Lioncash | 9ec78d6b9f | |
Lioncash | 5f76f0ecbf | |
Lioncash | f50d67eff0 | |
Phillip Stephens | 6c07ec907a | |
Lioncash | b6f0201fbb | |
Lioncash | d10a0ac7f1 | |
Phillip Stephens | d88b14c38a | |
Phillip Stephens | 8b411b35a1 | |
Lioncash | 29e7d8bc1e | |
Lioncash | 7600f8ad27 | |
Phillip Stephens | 0be0ca2911 | |
Lioncash | 50c4b5cdab | |
Phillip Stephens | 5da58eb1da | |
Lioncash | 344dbea31a | |
Phillip Stephens | 9b482ba013 | |
Phillip Stephens | 5ee32c105b | |
Phillip Stephens | af3f2a8d0c | |
Phillip Stephens | 8d944ac5b0 | |
Lioncash | 792199897c | |
Lioncash | bfdb504457 | |
Lioncash | 9c2c2be8eb | |
Lioncash | e6af69f882 | |
Lioncash | ce70df2e8f | |
Phillip Stephens | 1604cbeb70 | |
Phillip Stephens | 3b7d577f73 | |
Lioncash | ef86e69421 | |
Phillip Stephens | 107ddeb5a9 | |
Lioncash | bf872e9037 | |
Lioncash | a178d55007 | |
Phillip Stephens | 8569cab4d7 | |
Phillip Stephens | 4fdb2bde8c | |
Phillip Stephens | 80da83eb16 | |
Phillip Stephens | 8f810e571c | |
Phillip Stephens | 8f8add1eb1 | |
Phillip Stephens | 401c977378 | |
Lioncash | eaf62288a2 | |
Lioncash | e31dfc6f98 | |
Lioncash | 176493c539 | |
Lioncash | 7ef7562b06 | |
Phillip Stephens | 88c017926b | |
Lioncash | a380d4da21 | |
Phillip Stephens | 9cc47aa803 | |
Lioncash | 8b4a096b50 | |
Lioncash | f269b66f85 | |
Lioncash | 694ecdd9c3 | |
Lioncash | e3dc475d7d | |
Lioncash | da54ade80b | |
Lioncash | 658c33eb0d | |
Phillip Stephens | ea84a7e35b | |
Lioncash | 56a7d842b5 | |
Lioncash | f452516f12 | |
Lioncash | f140972920 | |
Lioncash | 07b4f02d1d | |
Lioncash | 771abb3390 | |
Phillip Stephens | 6850f0b2c9 | |
Phillip Stephens | c08dd97cbf | |
Phillip Stephens | 8da26720d1 | |
Phillip Stephens | db9e911a23 | |
Phillip Stephens | 2b09d86df4 | |
Phillip Stephens | 3310bf5b6e | |
Lioncash | db805075d9 | |
Lioncash | d7aaff25d0 | |
Lioncash | b1ab67f8c2 | |
Lioncash | 532f3a5265 | |
Lioncash | c736fdd00d | |
Lioncash | a016b72ea6 | |
Phillip Stephens | bc8218e183 | |
Phillip Stephens | cec062797e | |
Lioncash | ddc5661a38 | |
Lioncash | 6d7665ecd6 | |
Lioncash | 43eff31412 | |
Phillip Stephens | cbf331823f | |
Lioncash | 67c64a2d4e | |
Phillip Stephens | 82d1a8d214 | |
Lioncash | 051e4b1704 | |
Phillip Stephens | 520061a3bb | |
Lioncash | a8f4c1d34a | |
Lioncash | c46bb3f72b | |
Phillip Stephens | 529efa72b4 | |
Jack Andersen | 1e35db90aa | |
Jack Andersen | b0e4973c64 | |
Jack Andersen | a74caa5fb0 | |
Jack Andersen | 2a3444400e | |
Jack Andersen | 1b10016369 | |
Jack Andersen | aeaba9366d | |
Jack Andersen | e04603bb7d | |
Jack Andersen | aa5abd5ff5 | |
Jack Andersen | a4b8946ee2 | |
Jack Andersen | 9f2bb1b371 | |
Jack Andersen | 54ef2dd73b | |
Jack Andersen | fc2f8542c1 | |
Jack Andersen | d80f1346bb | |
Jack Andersen | 7719459ac7 | |
Jack Andersen | c1c47c51eb | |
Jack Andersen | a7a408cc66 | |
Jack Andersen | b4c073c373 | |
Jack Andersen | 885a2d2154 | |
Jack Andersen | dbd48a39cc | |
Jack Andersen | 4c7971f53a | |
Jack Andersen | 38b9f57cd0 | |
Jack Andersen | 92cd385f47 | |
Phillip Stephens | 9e6d97564b | |
Jack Andersen | 81f0a91569 | |
Jack Andersen | 25aacc9511 | |
Jack Andersen | 8fee7a282b | |
Jack Andersen | 1312a5fa86 | |
Jack Andersen | 6a7f32a29d | |
Phillip Stephens | a16fcf3342 | |
Phillip Stephens | d9018e2d2e | |
Jack Andersen | 9cc4bdefd0 | |
Jack Andersen | 2d4fd3493e | |
Jack Andersen | 59f99d0b99 | |
Jack Andersen | 3d29005567 | |
Jack Andersen | 68f0f2e769 | |
Jack Andersen | 1884e0ca7a | |
Jack Andersen | 4fc5dfdc76 | |
Jack Andersen | 33d2cc9ef1 | |
Jack Andersen | 19c5443e9e | |
Jack Andersen | 27cdee0c14 | |
Jack Andersen | cefa0ac18c | |
Jack Andersen | 303877655f | |
Jack Andersen | 08988fe3ec | |
Jack Andersen | fec074ad30 | |
Jack Andersen | 5e89954094 | |
Jack Andersen | 277e78c14b | |
Jack Andersen | 45cb6be3c5 | |
Jack Andersen | d24e06f101 | |
Jack Andersen | eff832bb8c | |
Jack Andersen | 32deea8341 | |
Jack Andersen | 2b45f69ff4 | |
Jack Andersen | 409d52c120 | |
Jack Andersen | 2abed18784 | |
Jack Andersen | c2a242022a | |
Jack Andersen | 57cbbf24b1 | |
Jack Andersen | 721dd361fa | |
Jack Andersen | d062a087c5 | |
Jack Andersen | 6f0a26a86c | |
Jack Andersen | f00904dd76 | |
Jack Andersen | 708ed599ae | |
Jack Andersen | 8d24e19989 | |
Phillip Stephens | e0f4fdd959 | |
Jack Andersen | 16745c9bf8 | |
Jack Andersen | f5984141fd | |
Jack Andersen | cb24322fc1 | |
Jack Andersen | ca81c07600 | |
Jack Andersen | b3958e9d52 | |
Jack Andersen | 441a3dbfd9 | |
Phillip Stephens | 095e73a2b4 | |
Jack Andersen | 321c2d9a3c | |
Jack Andersen | f50ee6e8f1 | |
Jack Andersen | 3f265cdb46 | |
Jack Andersen | 7a38fd0676 | |
Jack Andersen | 26cfa07f77 | |
Jack Andersen | 4c884d019d | |
Jack Andersen | 1e8b6e599c | |
Jack Andersen | cdc720b8a7 | |
Jack Andersen | 1e8ce19b16 | |
Jack Andersen | d7011f0e77 | |
Jack Andersen | 77c7daa67c | |
Phillip Stephens | e3c936648b | |
Jack Andersen | 1fefba66e7 | |
Jack Andersen | 594fb346e1 | |
Jack Andersen | 3833c137f6 | |
Jack Andersen | 912eb7950c | |
Phillip Stephens | 4146cdd049 | |
Jack Andersen | f5a0a46453 | |
Jack Andersen | 8f94bdd488 | |
Jack Andersen | f37296d560 | |
Phillip Stephens | fd292491c7 | |
Jack Andersen | 380381c7d1 | |
Jack Andersen | 3f9a91b0ab | |
Jack Andersen | 985994a291 | |
Jack Andersen | e37128d657 | |
Jack Andersen | 4c2a262478 | |
Jack Andersen | 3eba4cc1ac | |
Jack Andersen | 91b88c0568 | |
Jack Andersen | 6f7d09ce45 | |
Jack Andersen | 5caf6bf017 | |
Jack Andersen | d399f1d302 | |
Jack Andersen | 77215fc996 | |
Jack Andersen | 2f4d0e7541 | |
Jack Andersen | 89233e98b5 | |
Jack Andersen | b890a76e20 | |
Jack Andersen | 3c7cf5515f | |
Jack Andersen | ebe6f18898 | |
Jack Andersen | cab7402104 | |
Jack Andersen | 862c618b7e | |
Jack Andersen | 448b212ad9 | |
Jack Andersen | 03d597b0ac | |
Jack Andersen | 75830dc6dd | |
Jack Andersen | e18c0a62de | |
Jack Andersen | 047a91452e | |
Jack Andersen | fdf07d6c33 | |
Jack Andersen | 4b2b86f420 | |
Jack Andersen | c6781df90a | |
Jack Andersen | aef2b2a707 | |
Phillip Stephens | 0bace131e8 | |
Tom M | 4e7c31849d | |
Jack Andersen | 3d56d5f0cc | |
Phillip Stephens | 54bbc7399a | |
Jack Andersen | a23af16349 | |
Jack Andersen | 5c8fa2e8ab | |
Jack Andersen | aff8880595 | |
Jack Andersen | 2e7345f11d | |
Jack Andersen | ecd990e94e | |
Jack Andersen | 2836e73812 | |
Jack Andersen | df167556fb | |
Phillip Stephens | 72d0df7d46 | |
Jack Andersen | 1dfdf4c392 | |
Jack Andersen | 9cf96ad6f9 | |
Jack Andersen | eb948dfd63 | |
Phillip Stephens | c886bfd7d2 | |
Jack Andersen | 3fd3f3edc5 | |
Jack Andersen | 7cb7ed73ea | |
Jack Andersen | 2521f37408 | |
Jack Andersen | bfe6668d0c | |
Jack Andersen | 38f24ce3e4 | |
Jack Andersen | 218fad7541 | |
Phillip Stephens | af68ee61e1 | |
Phillip Stephens | b40b2c031a | |
Phillip Stephens | aa32ff7e84 | |
Jack Andersen | 28cac7ff83 | |
Jack Andersen | 055e73183a | |
Jack Andersen | a048605011 | |
Jack Andersen | 260ec5bb93 | |
Jack Andersen | 2a2a16fd17 | |
Jack Andersen | b421412cac | |
Jack Andersen | 5bae40d3c3 | |
Jack Andersen | d06a5ebcfa | |
Jack Andersen | e2581cea8b | |
Jack Andersen | d602fbacd3 | |
Jack Andersen | 2dcb9dd1c7 | |
Jack Andersen | d3d5595422 | |
Jack Andersen | 596bc66ce6 | |
Jack Andersen | 3a7b43a63a | |
Jack Andersen | 83a2bf0b4e | |
Jack Andersen | 695fc10b8f | |
Phillip Stephens | feea7c2ecc | |
Jack Andersen | 1be5d6e821 | |
Jack Andersen | 2d31313594 | |
Jack Andersen | 52cba61f76 | |
Jack Andersen | fe78a675d7 | |
Jack Andersen | 3427515960 |
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
BasedOnStyle: LLVM
|
||||
ColumnLimit: 120
|
||||
UseTab: Never
|
||||
---
|
||||
Language: Cpp
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
IndentCaseLabels: false
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
Cpp11BracedListStyle: true
|
||||
NamespaceIndentation: None
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
SortIncludes: false
|
||||
AccessModifierOffset: -2
|
||||
ConstructorInitializerIndentWidth: 0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
|
@ -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>
|
||||
|
|
|
@ -1,55 +1,50 @@
|
|||
#ifndef __AMUSE_AUDIOUNIT_CONTAININGAPP_HPP__
|
||||
#define __AMUSE_AUDIOUNIT_CONTAININGAPP_HPP__
|
||||
#pragma once
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "AudioGroupFilePresenter.hpp"
|
||||
#include <amuse/BooBackend.hpp>
|
||||
#include <boo/audiodev/IAudioVoiceEngine.hpp>
|
||||
|
||||
@interface DataOutlineView : NSOutlineView
|
||||
{
|
||||
@interface DataOutlineView : NSOutlineView {
|
||||
@public
|
||||
IBOutlet NSButton* removeDataButton;
|
||||
IBOutlet NSMenuItem* deleteMenuItem;
|
||||
IBOutlet NSButton* removeDataButton;
|
||||
IBOutlet NSMenuItem* deleteMenuItem;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
|
||||
{
|
||||
AudioGroupFilePresenter* presenter;
|
||||
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
|
||||
AudioGroupFilePresenter* presenter;
|
||||
}
|
||||
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
|
||||
@end
|
||||
|
||||
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
|
||||
{
|
||||
AudioGroupFilePresenter* presenter;
|
||||
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
|
||||
AudioGroupFilePresenter* presenter;
|
||||
}
|
||||
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
|
||||
@end
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient>
|
||||
{
|
||||
IBOutlet NSWindow* mainWindow;
|
||||
IBOutlet NSOutlineView* dataOutline;
|
||||
IBOutlet NSSearchField* dataSearchField;
|
||||
IBOutlet NSTableView* sfxTable;
|
||||
IBOutlet NSTableView* samplesTable;
|
||||
IBOutlet NSTextView* creditsView;
|
||||
|
||||
IBOutlet NSButton* removeDataButton;
|
||||
IBOutlet NSMenuItem* removeDataMenu;
|
||||
|
||||
AudioGroupFilePresenter* groupFilePresenter;
|
||||
|
||||
SamplesTableController* samplesController;
|
||||
SFXTableController* sfxController;
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient> {
|
||||
IBOutlet NSWindow* mainWindow;
|
||||
IBOutlet NSOutlineView* dataOutline;
|
||||
IBOutlet NSSearchField* dataSearchField;
|
||||
IBOutlet NSTableView* sfxTable;
|
||||
IBOutlet NSTableView* samplesTable;
|
||||
IBOutlet NSTextView* creditsView;
|
||||
|
||||
IBOutlet NSButton* removeDataButton;
|
||||
IBOutlet NSMenuItem* removeDataMenu;
|
||||
|
||||
AudioGroupFilePresenter* groupFilePresenter;
|
||||
|
||||
SamplesTableController* samplesController;
|
||||
SFXTableController* sfxController;
|
||||
|
||||
@public
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
|
||||
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
|
||||
std::experimental::optional<amuse::Engine> amuseEngine;
|
||||
std::shared_ptr<amuse::Voice> activeSFXVox;
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
|
||||
std::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
|
||||
std::optional<amuse::Engine> amuseEngine;
|
||||
std::shared_ptr<amuse::Voice> activeSFXVox;
|
||||
}
|
||||
- (BOOL)importURL:(NSURL*)url;
|
||||
- (void)outlineView:(DataOutlineView*)ov selectionChanged:(id)item;
|
||||
|
@ -57,5 +52,3 @@
|
|||
- (void)startSFX:(int)sfxId;
|
||||
- (void)startSample:(int)sampId;
|
||||
@end
|
||||
|
||||
#endif // __AMUSE_AUDIOUNIT_CONTAININGAPP_HPP__
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
#ifndef __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__
|
||||
#define __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__
|
||||
#pragma once
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "optional.hpp"
|
||||
#include <optional>
|
||||
#include <amuse/amuse.hpp>
|
||||
#include <athena/FileReader.hpp>
|
||||
|
||||
|
@ -20,140 +19,133 @@
|
|||
- (amuse::Engine&)getAmuseEngine;
|
||||
@end
|
||||
|
||||
struct AudioGroupDataCollection
|
||||
{
|
||||
std::string m_name;
|
||||
NSURL* m_proj;
|
||||
NSURL* m_pool;
|
||||
NSURL* m_sdir;
|
||||
NSURL* m_samp;
|
||||
NSURL* m_meta;
|
||||
struct AudioGroupDataCollection {
|
||||
std::string m_name;
|
||||
NSURL* m_proj;
|
||||
NSURL* m_pool;
|
||||
NSURL* m_sdir;
|
||||
NSURL* m_samp;
|
||||
NSURL* m_meta;
|
||||
|
||||
AudioGroupDataToken* m_token;
|
||||
|
||||
std::vector<uint8_t> m_projData;
|
||||
std::vector<uint8_t> m_poolData;
|
||||
std::vector<uint8_t> m_sdirData;
|
||||
std::vector<uint8_t> m_sampData;
|
||||
|
||||
struct MetaData
|
||||
{
|
||||
amuse::DataFormat fmt;
|
||||
uint32_t absOffs;
|
||||
uint32_t active;
|
||||
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
||||
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {}
|
||||
MetaData(athena::io::FileReader& r)
|
||||
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
|
||||
};
|
||||
std::experimental::optional<MetaData> m_metaData;
|
||||
|
||||
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
|
||||
const amuse::AudioGroup* m_loadedGroup;
|
||||
std::vector<AudioGroupToken*> m_groupTokens;
|
||||
AudioGroupDataToken* m_token;
|
||||
|
||||
void moveURL(NSURL* oldUrl, NSURL* newUrl);
|
||||
std::vector<uint8_t> m_projData;
|
||||
std::vector<uint8_t> m_poolData;
|
||||
std::vector<uint8_t> m_sdirData;
|
||||
std::vector<uint8_t> m_sampData;
|
||||
|
||||
bool loadProj(AudioGroupFilePresenter* presenter);
|
||||
bool loadPool(AudioGroupFilePresenter* presenter);
|
||||
bool loadSdir(AudioGroupFilePresenter* presenter);
|
||||
bool loadSamp(AudioGroupFilePresenter* presenter);
|
||||
bool loadMeta(AudioGroupFilePresenter* presenter);
|
||||
struct MetaData {
|
||||
amuse::DataFormat fmt;
|
||||
uint32_t absOffs;
|
||||
uint32_t active;
|
||||
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
||||
: FMT_STRING(fmtIn), absOffs(absOffsIn), active(activeIn) {}
|
||||
MetaData(athena::io::FileReader& r)
|
||||
: FMT_STRING(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
|
||||
};
|
||||
std::optional<MetaData> m_metaData;
|
||||
|
||||
AudioGroupDataCollection(const std::string& name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
|
||||
bool isDataComplete() const {return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;}
|
||||
bool _attemptLoad(AudioGroupFilePresenter* presenter);
|
||||
bool _indexData(AudioGroupFilePresenter* presenter);
|
||||
std::optional<amuse::AudioGroupData> m_loadedData;
|
||||
const amuse::AudioGroup* m_loadedGroup;
|
||||
std::vector<AudioGroupToken*> m_groupTokens;
|
||||
|
||||
void enable(AudioGroupFilePresenter* presenter);
|
||||
void disable(AudioGroupFilePresenter* presenter);
|
||||
void moveURL(NSURL* oldUrl, NSURL* newUrl);
|
||||
|
||||
bool loadProj(AudioGroupFilePresenter* presenter);
|
||||
bool loadPool(AudioGroupFilePresenter* presenter);
|
||||
bool loadSdir(AudioGroupFilePresenter* presenter);
|
||||
bool loadSamp(AudioGroupFilePresenter* presenter);
|
||||
bool loadMeta(AudioGroupFilePresenter* presenter);
|
||||
|
||||
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
|
||||
bool isDataComplete() const {
|
||||
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
|
||||
}
|
||||
bool _attemptLoad(AudioGroupFilePresenter* presenter);
|
||||
bool _indexData(AudioGroupFilePresenter* presenter);
|
||||
|
||||
void enable(AudioGroupFilePresenter* presenter);
|
||||
void disable(AudioGroupFilePresenter* presenter);
|
||||
};
|
||||
|
||||
struct AudioGroupCollection
|
||||
{
|
||||
NSURL* m_url;
|
||||
|
||||
AudioGroupCollectionToken* m_token;
|
||||
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
|
||||
|
||||
AudioGroupCollection(NSURL* url);
|
||||
void addCollection(AudioGroupFilePresenter* presenter,
|
||||
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||
void update(AudioGroupFilePresenter* presenter);
|
||||
bool doSearch(const std::string& str);
|
||||
bool doActiveFilter();
|
||||
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
|
||||
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
|
||||
struct AudioGroupCollection {
|
||||
NSURL* m_url;
|
||||
|
||||
AudioGroupCollectionToken* m_token;
|
||||
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
|
||||
|
||||
AudioGroupCollection(NSURL* url);
|
||||
void addCollection(AudioGroupFilePresenter* presenter,
|
||||
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||
void update(AudioGroupFilePresenter* presenter);
|
||||
bool doSearch(std::string_view str);
|
||||
bool doActiveFilter();
|
||||
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
|
||||
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
|
||||
};
|
||||
|
||||
@interface AudioGroupDataToken : NSObject
|
||||
{
|
||||
@interface AudioGroupDataToken : NSObject {
|
||||
@public
|
||||
AudioGroupDataCollection* m_collection;
|
||||
AudioGroupDataCollection* m_collection;
|
||||
}
|
||||
- (id)initWithDataCollection:(AudioGroupDataCollection*)collection;
|
||||
@end
|
||||
|
||||
@interface AudioGroupCollectionToken : NSObject
|
||||
{
|
||||
@interface AudioGroupCollectionToken : NSObject {
|
||||
@public
|
||||
AudioGroupCollection* m_collection;
|
||||
AudioGroupCollection* m_collection;
|
||||
}
|
||||
- (id)initWithCollection:(AudioGroupCollection*)collection;
|
||||
@end
|
||||
|
||||
@interface AudioGroupSFXToken : NSObject
|
||||
{
|
||||
@interface AudioGroupSFXToken : NSObject {
|
||||
@public
|
||||
NSAttributedString* m_name;
|
||||
int m_loadId;
|
||||
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
|
||||
NSAttributedString* m_name;
|
||||
int m_loadId;
|
||||
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
|
||||
}
|
||||
- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx;
|
||||
@end
|
||||
|
||||
@interface AudioGroupSampleToken : NSObject
|
||||
{
|
||||
@interface AudioGroupSampleToken : NSObject {
|
||||
@public
|
||||
NSAttributedString* m_name;
|
||||
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
|
||||
NSAttributedString* m_name;
|
||||
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
|
||||
}
|
||||
- (id)initWithName:(NSAttributedString*)name samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry,
|
||||
amuse::AudioGroupSampleDirectory::ADPCMParms>*)sample;
|
||||
- (id)initWithName:(NSAttributedString*)name
|
||||
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry,
|
||||
amuse::AudioGroupSampleDirectory::ADPCMParms>*)sample;
|
||||
@end
|
||||
|
||||
@interface AudioGroupToken : NSObject
|
||||
{
|
||||
@interface AudioGroupToken : NSObject {
|
||||
@public
|
||||
NSString* m_name;
|
||||
int m_id;
|
||||
const amuse::SongGroupIndex* m_song;
|
||||
const amuse::SFXGroupIndex* m_sfx;
|
||||
NSString* m_name;
|
||||
int m_id;
|
||||
const amuse::SongGroupIndex* m_song;
|
||||
const amuse::SFXGroupIndex* m_sfx;
|
||||
}
|
||||
- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group;
|
||||
- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group;
|
||||
@end
|
||||
|
||||
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate>
|
||||
{
|
||||
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate> {
|
||||
@public
|
||||
id<AudioGroupClient> m_audioGroupClient;
|
||||
NSURL* m_groupURL;
|
||||
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
|
||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
|
||||
NSOutlineView* m_lastOutlineView;
|
||||
NSString* m_searchStr;
|
||||
|
||||
std::vector<AudioGroupSFXToken*> m_sfxTableData;
|
||||
std::vector<AudioGroupSampleToken*> m_sampleTableData;
|
||||
id<AudioGroupClient> m_audioGroupClient;
|
||||
NSURL* m_groupURL;
|
||||
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
|
||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
|
||||
NSOutlineView* m_lastOutlineView;
|
||||
NSString* m_searchStr;
|
||||
|
||||
std::vector<AudioGroupSFXToken*> m_sfxTableData;
|
||||
std::vector<AudioGroupSampleToken*> m_sampleTableData;
|
||||
}
|
||||
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
|
||||
- (BOOL)addCollectionName:(std::string&&)name items:(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&&)collection;
|
||||
- (BOOL)addCollectionName:(std::string&&)name
|
||||
items:(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&&)collection;
|
||||
- (void)update;
|
||||
- (void)resetIterators;
|
||||
- (void)setSearchFilter:(NSString*)str;
|
||||
- (void)removeSelectedItem;
|
||||
@end
|
||||
|
||||
#endif // __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__
|
||||
|
|
|
@ -161,13 +161,14 @@ void AudioGroupCollection::update(AudioGroupFilePresenter* presenter)
|
|||
}
|
||||
}
|
||||
|
||||
bool AudioGroupCollection::doSearch(const std::string& str)
|
||||
bool AudioGroupCollection::doSearch(std::string_view str)
|
||||
{
|
||||
bool ret = false;
|
||||
m_filterGroups.clear();
|
||||
m_filterGroups.reserve(m_groups.size());
|
||||
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
|
||||
if (str.empty() || StrToLower(it->first).find(str) != std::string::npos)
|
||||
// TODO: Heterogeneous lookup when C++20 available
|
||||
if (str.empty() || StrToLower(it->first).find(str.data()) != std::string::npos)
|
||||
{
|
||||
m_filterGroups.push_back(it);
|
||||
ret = true;
|
||||
|
@ -239,7 +240,7 @@ void AudioGroupCollection::addSamples(std::vector<AudioGroupSampleToken*>& vecOu
|
|||
}
|
||||
}
|
||||
|
||||
AudioGroupDataCollection::AudioGroupDataCollection(const std::string& name, NSURL* proj, NSURL* pool,
|
||||
AudioGroupDataCollection::AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool,
|
||||
NSURL* sdir, NSURL* samp, NSURL* meta)
|
||||
: m_name(name), m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_meta(meta),
|
||||
m_token([[AudioGroupDataToken alloc] initWithDataCollection:this]) {}
|
||||
|
@ -486,7 +487,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter)
|
|||
if (!coord)
|
||||
return false;
|
||||
NSError* err;
|
||||
__block std::experimental::optional<MetaData>& ret = m_metaData;
|
||||
__block std::optional<MetaData>& ret = m_metaData;
|
||||
[coord coordinateReadingItemAtURL:m_meta
|
||||
options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err
|
||||
byAccessor:^(NSURL* newUrl)
|
||||
|
@ -849,7 +850,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter)
|
|||
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client
|
||||
{
|
||||
m_audioGroupClient = client;
|
||||
m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.io.github.axiodl.Amuse.AudioGroups"];
|
||||
m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.axiodl.Amuse.AudioGroups"];
|
||||
if (!m_groupURL)
|
||||
return nil;
|
||||
[NSFileCoordinator addFilePresenter:self];
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#ifndef __AMUSE_AUDIOUNIT_BACKEND_HPP__
|
||||
#define __AMUSE_AUDIOUNIT_BACKEND_HPP__
|
||||
#pragma once
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include <Availability.h>
|
||||
|
@ -9,7 +8,7 @@
|
|||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
|
||||
#include "optional.hpp"
|
||||
#include <optional>
|
||||
|
||||
#include "amuse/BooBackend.hpp"
|
||||
#include "amuse/Engine.hpp"
|
||||
|
@ -20,38 +19,32 @@
|
|||
|
||||
@class AudioUnitViewController;
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
namespace amuse {
|
||||
|
||||
/** Backend voice allocator implementation for AudioUnit mixer */
|
||||
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator
|
||||
{
|
||||
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator {
|
||||
public:
|
||||
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
||||
: BooBackendVoiceAllocator(booEngine) {}
|
||||
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
|
||||
};
|
||||
|
||||
void RegisterAudioUnit();
|
||||
} // namespace amuse
|
||||
|
||||
}
|
||||
|
||||
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient>
|
||||
{
|
||||
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient> {
|
||||
@public
|
||||
AudioUnitViewController* m_viewController;
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
||||
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
|
||||
std::experimental::optional<amuse::Engine> m_engine;
|
||||
AudioGroupFilePresenter* m_filePresenter;
|
||||
AUAudioUnitBus* m_outBus;
|
||||
AUAudioUnitBusArray* m_outs;
|
||||
AudioUnitViewController* m_viewController;
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
||||
std::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
|
||||
std::optional<amuse::Engine> m_engine;
|
||||
AudioGroupFilePresenter* m_filePresenter;
|
||||
AUAudioUnitBus* m_outBus;
|
||||
AUAudioUnitBusArray* m_outs;
|
||||
}
|
||||
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
|
||||
error:(NSError * __nullable * __nonnull)outError
|
||||
error:(NSError* __nullable* __nonnull)outError
|
||||
viewController:(AudioUnitViewController* __nonnull)vc;
|
||||
- (void)requestAudioGroup:(AudioGroupToken* _Nonnull)group;
|
||||
@end
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif // __AMUSE_AUDIOUNIT_BACKEND_HPP__
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
230
CMakeLists.txt
|
@ -1,84 +1,143 @@
|
|||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
|
||||
|
||||
project(amuse)
|
||||
|
||||
if(EXISTS boo)
|
||||
if(POLICY CMP0072)
|
||||
cmake_policy(SET CMP0072 NEW)
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
if(NOT MSVC)
|
||||
add_compile_options(-Wno-narrowing)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/boo AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/athena)
|
||||
message(STATUS "Preparing standalone build")
|
||||
add_subdirectory(boo)
|
||||
add_subdirectory(athena)
|
||||
endif()
|
||||
|
||||
set(SOURCES
|
||||
lib/AudioGroup.cpp
|
||||
lib/AudioGroupData.cpp
|
||||
lib/AudioGroupPool.cpp
|
||||
lib/AudioGroupProject.cpp
|
||||
lib/AudioGroupSampleDirectory.cpp
|
||||
lib/DirectoryEnumerator.cpp
|
||||
lib/Emitter.cpp
|
||||
lib/Engine.cpp
|
||||
lib/Envelope.cpp
|
||||
lib/Listener.cpp
|
||||
lib/Sequencer.cpp
|
||||
lib/SoundMacroState.cpp
|
||||
lib/SongConverter.cpp
|
||||
lib/SongState.cpp
|
||||
lib/Voice.cpp
|
||||
lib/VolumeLUT.cpp
|
||||
lib/Submix.cpp
|
||||
lib/EffectBase.cpp
|
||||
lib/EffectReverb.cpp
|
||||
lib/EffectChorus.cpp
|
||||
lib/EffectDelay.cpp
|
||||
lib/SurroundProfiles.cpp
|
||||
lib/ContainerRegistry.cpp
|
||||
lib/DSPCodec.c
|
||||
lib/N64MusyXCodec.c)
|
||||
|
||||
set(HEADERS
|
||||
include/amuse/AudioGroup.hpp
|
||||
include/amuse/AudioGroupData.hpp
|
||||
include/amuse/AudioGroupPool.hpp
|
||||
include/amuse/AudioGroupProject.hpp
|
||||
include/amuse/AudioGroupSampleDirectory.hpp
|
||||
include/amuse/DirectoryEnumerator.hpp
|
||||
include/amuse/Emitter.hpp
|
||||
include/amuse/Engine.hpp
|
||||
include/amuse/Entity.hpp
|
||||
include/amuse/Envelope.hpp
|
||||
include/amuse/Listener.hpp
|
||||
include/amuse/Sequencer.hpp
|
||||
include/amuse/SoundMacroState.hpp
|
||||
include/amuse/SongConverter.hpp
|
||||
include/amuse/SongState.hpp
|
||||
include/amuse/Voice.hpp
|
||||
include/amuse/Submix.hpp
|
||||
include/amuse/IBackendSubmix.hpp
|
||||
include/amuse/IBackendVoice.hpp
|
||||
include/amuse/IBackendVoiceAllocator.hpp
|
||||
include/amuse/EffectBase.hpp
|
||||
include/amuse/EffectReverb.hpp
|
||||
include/amuse/EffectChorus.hpp
|
||||
include/amuse/EffectDelay.hpp
|
||||
include/amuse/SurroundProfiles.hpp
|
||||
include/amuse/ContainerRegistry.hpp
|
||||
include/amuse/Common.hpp
|
||||
include/amuse/amuse.hpp
|
||||
include/amuse/DSPCodec.h
|
||||
include/amuse/N64MusyXCodec.h)
|
||||
|
||||
unset(EXTRAS)
|
||||
if(TARGET boo)
|
||||
include_directories(${BOO_INCLUDE_DIR} ${BOO_INCLUDE_DIR}/../lib ${BOO_INCLUDE_DIR}/../soxr/src
|
||||
${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
||||
list(APPEND EXTRAS lib/BooBackend.cpp include/amuse/BooBackend.hpp)
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
set(AMUSE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE PATH "amuse include path" FORCE)
|
||||
option(AMUSE_BUILD_EDITOR "Build Amuse with editor enabled (includes VST)" ON)
|
||||
|
||||
add_library(amuse
|
||||
${SOURCES}
|
||||
${HEADERS}
|
||||
${EXTRAS})
|
||||
lib/AudioGroup.cpp
|
||||
lib/AudioGroupData.cpp
|
||||
lib/AudioGroupPool.cpp
|
||||
lib/AudioGroupProject.cpp
|
||||
lib/AudioGroupSampleDirectory.cpp
|
||||
lib/Common.cpp
|
||||
lib/ContainerRegistry.cpp
|
||||
lib/DirectoryEnumerator.cpp
|
||||
lib/DSPCodec.cpp
|
||||
lib/EffectChorus.cpp
|
||||
lib/EffectDelay.cpp
|
||||
lib/EffectReverb.cpp
|
||||
lib/Emitter.cpp
|
||||
lib/Engine.cpp
|
||||
lib/Envelope.cpp
|
||||
lib/Listener.cpp
|
||||
lib/N64MusyXCodec.cpp
|
||||
lib/Sequencer.cpp
|
||||
lib/SongConverter.cpp
|
||||
lib/SongState.cpp
|
||||
lib/SoundMacroState.cpp
|
||||
lib/Studio.cpp
|
||||
lib/Submix.cpp
|
||||
lib/Voice.cpp
|
||||
lib/VolumeTable.cpp
|
||||
|
||||
include/amuse/amuse.hpp
|
||||
include/amuse/AudioGroup.hpp
|
||||
include/amuse/AudioGroupData.hpp
|
||||
include/amuse/AudioGroupPool.hpp
|
||||
include/amuse/AudioGroupProject.hpp
|
||||
include/amuse/AudioGroupSampleDirectory.hpp
|
||||
include/amuse/Common.hpp
|
||||
include/amuse/ContainerRegistry.hpp
|
||||
include/amuse/DirectoryEnumerator.hpp
|
||||
include/amuse/DSPCodec.hpp
|
||||
include/amuse/EffectBase.hpp
|
||||
include/amuse/EffectChorus.hpp
|
||||
include/amuse/EffectDelay.hpp
|
||||
include/amuse/EffectReverb.hpp
|
||||
include/amuse/Emitter.hpp
|
||||
include/amuse/Engine.hpp
|
||||
include/amuse/Entity.hpp
|
||||
include/amuse/Envelope.hpp
|
||||
include/amuse/IBackendSubmix.hpp
|
||||
include/amuse/IBackendVoice.hpp
|
||||
include/amuse/IBackendVoiceAllocator.hpp
|
||||
include/amuse/Listener.hpp
|
||||
include/amuse/N64MusyXCodec.hpp
|
||||
include/amuse/Sequencer.hpp
|
||||
include/amuse/SongConverter.hpp
|
||||
include/amuse/SoundMacroState.hpp
|
||||
include/amuse/SongState.hpp
|
||||
include/amuse/Submix.hpp
|
||||
include/amuse/Studio.hpp
|
||||
include/amuse/Voice.hpp
|
||||
include/amuse/VolumeTable.hpp
|
||||
)
|
||||
|
||||
target_include_directories(amuse PUBLIC include)
|
||||
target_link_libraries(amuse
|
||||
athena-core
|
||||
lzokay
|
||||
logvisor
|
||||
fmt
|
||||
${ZLIB_LIBRARIES}
|
||||
)
|
||||
|
||||
target_atdna(amuse atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp)
|
||||
target_atdna(amuse atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp)
|
||||
target_atdna(amuse atdna_AudioGroupSampleDirectory.cpp include/amuse/AudioGroupSampleDirectory.hpp)
|
||||
|
||||
if(NX)
|
||||
target_sources(amuse PRIVATE include/switch_math.hpp)
|
||||
endif()
|
||||
|
||||
if(TARGET boo)
|
||||
target_sources(amuse PRIVATE lib/BooBackend.cpp include/amuse/BooBackend.hpp)
|
||||
target_link_libraries(amuse boo)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(amuse PRIVATE
|
||||
# Enforce various standards compliant behavior.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
|
||||
|
||||
# Enable standard volatile semantics.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
|
||||
|
||||
# Reports the proper value for the __cplusplus preprocessor macro.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
|
||||
|
||||
# Use latest C++ standard.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>
|
||||
)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# Flags for MSVC (not clang-cl)
|
||||
target_compile_options(amuse PRIVATE
|
||||
# Allow constexpr variables to have explicit external linkage.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>
|
||||
|
||||
# Assume that new throws exceptions, allowing better code generation.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:throwingNew>
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
target_compile_options(amuse PRIVATE -Wno-unknown-pragmas)
|
||||
endif()
|
||||
|
||||
if(COMMAND add_sanitizers)
|
||||
add_sanitizers(amuse)
|
||||
endif()
|
||||
|
||||
if(TARGET boo AND NOT WINDOWS_STORE AND NOT NX)
|
||||
# AudioUnit Target (OS X only)
|
||||
add_subdirectory(AudioUnit)
|
||||
|
||||
|
@ -89,9 +148,32 @@ if(TARGET boo)
|
|||
|
||||
# Player
|
||||
add_executable(amuseplay WIN32 driver/amuseplay.cpp)
|
||||
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
|
||||
target_link_libraries(amuseplay amuse logvisor)
|
||||
|
||||
# Converter
|
||||
add_executable(amuseconv driver/amuseconv.cpp)
|
||||
target_link_libraries(amuseconv amuse ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
|
||||
target_link_libraries(amuseconv amuse logvisor)
|
||||
|
||||
# Renderer
|
||||
add_executable(amuserender driver/amuserender.cpp)
|
||||
target_link_libraries(amuserender amuse logvisor)
|
||||
|
||||
if(COMMAND add_sanitizers)
|
||||
add_sanitizers(amuseplay)
|
||||
add_sanitizers(amuseconv)
|
||||
add_sanitizers(amuserender)
|
||||
endif()
|
||||
|
||||
# Editor
|
||||
find_package(Qt6 COMPONENTS Widgets PATHS /usr/local/opt/qt)
|
||||
if (Qt6Widgets_FOUND AND AMUSE_BUILD_EDITOR)
|
||||
message(STATUS "Qt6 found, amuse-gui will be built")
|
||||
add_subdirectory(Editor)
|
||||
else()
|
||||
if (NOT AMUSE_BUILD_EDITOR)
|
||||
message(STATUS "amuse-gui has been disabled and will not be built, to enable pass `-DAMUSE_BUILD_EDITOR=ON` to cmake")
|
||||
else()
|
||||
message(STATUS "Qt6 not found, amuse-gui will not be built")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1,977 @@
|
|||
#include "ADSREditor.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
const QColor Red = QColor(255, 127, 127);
|
||||
const QColor Green = QColor(127, 255, 127);
|
||||
const QColor Blue = QColor(150, 150, 255);
|
||||
|
||||
ADSREditor* ADSRView::getEditor() const { return qobject_cast<ADSREditor*>(parentWidget()); }
|
||||
|
||||
void ADSRView::loadData(ProjectModel::ADSRNode* node) { m_node = node; }
|
||||
|
||||
void ADSRView::unloadData() { m_node.reset(); }
|
||||
|
||||
ProjectModel::INode* ADSRView::currentNode() const { return m_node.get(); }
|
||||
|
||||
void ADSRView::paintEvent(QPaintEvent* ev) {
|
||||
if (!m_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const amuse::ITable& table = **m_node->m_obj;
|
||||
qreal adTime = 0.0;
|
||||
qreal aTime = 0.0;
|
||||
qreal relTime = 0.0;
|
||||
qreal sustain = 0.0;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
|
||||
adTime += adsr.getAttack();
|
||||
aTime = adsr.getAttack();
|
||||
adTime += adsr.getDecay();
|
||||
sustain = adsr.getSustain();
|
||||
relTime = adsr.getRelease();
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
const auto& adsr = static_cast<const amuse::ADSR&>(table);
|
||||
adTime += adsr.getAttack();
|
||||
aTime = adsr.getAttack();
|
||||
adTime += adsr.getDecay();
|
||||
sustain = adsr.getSustain();
|
||||
relTime = adsr.getRelease();
|
||||
}
|
||||
const qreal totalTime = adTime + 1.0 + relTime;
|
||||
|
||||
const qreal deviceRatio = devicePixelRatioF();
|
||||
const qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
|
||||
|
||||
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
|
||||
painter.setFont(m_gridFont);
|
||||
const qreal yIncrement = (height() - 16.0) / 10.0;
|
||||
for (size_t i = 0; i < m_percentTexts.size(); ++i) {
|
||||
const qreal thisY = qreal(i) * yIncrement;
|
||||
const qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0));
|
||||
painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]);
|
||||
painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY));
|
||||
}
|
||||
|
||||
const size_t maxTime = size_t(std::max(adTime, relTime)) + 1;
|
||||
if (m_timeTexts.size() < maxTime) {
|
||||
m_timeTexts.reserve(maxTime);
|
||||
for (size_t i = m_timeTexts.size(); i < maxTime; ++i) {
|
||||
m_timeTexts.emplace_back();
|
||||
QStaticText& text = m_timeTexts.back();
|
||||
text.setText(QStringLiteral("%1.0s").arg(i));
|
||||
text.setTextWidth(32.0);
|
||||
text.setTextOption(QTextOption(Qt::AlignHCenter | Qt::AlignBaseline));
|
||||
}
|
||||
}
|
||||
qreal xBase = 30.0;
|
||||
const qreal xIncrement = (width() - xBase) / totalTime;
|
||||
for (size_t i = 0; i <= size_t(adTime); ++i) {
|
||||
const qreal thisX = i * xIncrement + xBase;
|
||||
const qreal textX = thisX - 15.0;
|
||||
painter.drawStaticText(QPointF(textX, height() - 16.0), m_timeTexts[i]);
|
||||
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
|
||||
}
|
||||
xBase = (width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0;
|
||||
for (size_t i = 0; i <= size_t(relTime); ++i) {
|
||||
const qreal thisX = i * xIncrement + xBase;
|
||||
const qreal textX = thisX - 15.0;
|
||||
painter.drawStaticText(QPointF(textX, height() - 16.0), m_timeTexts[i]);
|
||||
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
|
||||
}
|
||||
|
||||
const qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain;
|
||||
const auto pt0 = QPointF(30.0, height() - 16.0);
|
||||
const auto pt1 = QPointF((width() - 30.0) * (aTime / totalTime) + 30.0, 0.0);
|
||||
const auto pt2 = QPointF((width() - 30.0) * (adTime / totalTime) + 30.0, sustainY);
|
||||
const auto pt3 = QPointF((width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0, sustainY);
|
||||
const auto pt4 = QPointF(width(), height() - 16.0);
|
||||
|
||||
painter.setPen(QPen(Red, penWidth * 3.0));
|
||||
painter.drawLine(pt0, pt1);
|
||||
painter.setPen(QPen(Green, penWidth * 3.0));
|
||||
painter.drawLine(pt1, pt2);
|
||||
painter.setPen(QPen(Qt::white, penWidth * 3.0));
|
||||
painter.drawLine(pt2, pt3);
|
||||
painter.setPen(QPen(Blue, penWidth * 3.0));
|
||||
painter.drawLine(pt3, pt4);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(Green);
|
||||
painter.drawEllipse(pt1, 8.0, 8.0);
|
||||
painter.setBrush(Qt::white);
|
||||
painter.drawEllipse(pt2, 8.0, 8.0);
|
||||
painter.setBrush(Blue);
|
||||
painter.drawEllipse(pt3, 8.0, 8.0);
|
||||
}
|
||||
|
||||
static qreal PointDistanceSq(const QPointF& p1, const QPointF& p2) {
|
||||
const QPointF delta = p1 - p2;
|
||||
return QPointF::dotProduct(delta, delta);
|
||||
}
|
||||
|
||||
static qreal PointDistance(const QPointF& p1, const QPointF& p2) { return std::sqrt(PointDistanceSq(p1, p2)); }
|
||||
|
||||
void ADSRView::mousePressEvent(QMouseEvent* ev) {
|
||||
if (!m_node)
|
||||
return;
|
||||
|
||||
const amuse::ITable& table = **m_node->m_obj;
|
||||
qreal adTime = 0.0;
|
||||
qreal aTime = 0.0;
|
||||
qreal relTime = 0.0;
|
||||
qreal sustain = 0.0;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
|
||||
adTime += adsr.getAttack();
|
||||
aTime = adsr.getAttack();
|
||||
adTime += adsr.getDecay();
|
||||
sustain = adsr.getSustain();
|
||||
relTime = adsr.getRelease();
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
const auto& adsr = static_cast<const amuse::ADSR&>(table);
|
||||
adTime += adsr.getAttack();
|
||||
aTime = adsr.getAttack();
|
||||
adTime += adsr.getDecay();
|
||||
sustain = adsr.getSustain();
|
||||
relTime = adsr.getRelease();
|
||||
}
|
||||
const qreal totalTime = adTime + 1.0 + relTime;
|
||||
|
||||
const qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain;
|
||||
const QPointF points[] = {
|
||||
QPointF((width() - 30.0) * (aTime / totalTime) + 30.0, 0.0),
|
||||
QPointF((width() - 30.0) * (adTime / totalTime) + 30.0, sustainY),
|
||||
QPointF((width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0, sustainY),
|
||||
};
|
||||
const qreal dists[] = {
|
||||
PointDistance(ev->position(), points[0]),
|
||||
PointDistance(ev->position(), points[1]),
|
||||
PointDistance(ev->position(), points[2]),
|
||||
};
|
||||
|
||||
int minDist = 0;
|
||||
if (dists[1] < dists[minDist] + 8.0) /* pt1 overlaps pt0, so include radius in test */
|
||||
minDist = 1;
|
||||
if (dists[2] < dists[minDist])
|
||||
minDist = 2;
|
||||
|
||||
if (dists[minDist] < 10.0) {
|
||||
++m_cycleIdx;
|
||||
m_dragPoint = minDist;
|
||||
mouseMoveEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
void ADSRView::mouseReleaseEvent(QMouseEvent* ev) { m_dragPoint = -1; }
|
||||
|
||||
void ADSRView::mouseMoveEvent(QMouseEvent* ev) {
|
||||
const amuse::ITable& table = **m_node->m_obj;
|
||||
ADSRControls* ctrls = getEditor()->m_controls;
|
||||
|
||||
qreal adTime = 0.0;
|
||||
qreal aTime = 0.0;
|
||||
qreal relTime = 0.0;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
|
||||
adTime += adsr.getAttack();
|
||||
aTime = adsr.getAttack();
|
||||
adTime += adsr.getDecay();
|
||||
relTime = adsr.getRelease();
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
const auto& adsr = static_cast<const amuse::ADSR&>(table);
|
||||
adTime += adsr.getAttack();
|
||||
aTime = adsr.getAttack();
|
||||
adTime += adsr.getDecay();
|
||||
relTime = adsr.getRelease();
|
||||
}
|
||||
qreal totalTime = adTime + 1.0 + relTime;
|
||||
|
||||
if (m_dragPoint == 0) {
|
||||
const qreal newAttack = std::max(0.0, (ev->position().x() - 30.0) / (width() - 30.0) * totalTime);
|
||||
const qreal delta = newAttack - aTime;
|
||||
ctrls->setAttackAndDecay(newAttack, std::max(0.0, ctrls->m_decay->value() - delta), m_cycleIdx);
|
||||
} else if (m_dragPoint == 1) {
|
||||
const qreal newDecay = std::max(0.0, (ev->position().x() - 30.0) * totalTime / (width() - 30.0) - aTime);
|
||||
const qreal newSustain = (-ev->position().y() + (height() - 16.0)) / (height() - 16.0);
|
||||
ctrls->setDecayAndSustain(newDecay, newSustain * 100.0, m_cycleIdx);
|
||||
} else if (m_dragPoint == 2) {
|
||||
const qreal newRelease =
|
||||
std::max(0.0, (width() - 30.0) * (adTime + 1.0) / (ev->position().x() - 30.0) - (adTime + 1.0));
|
||||
ctrls->setRelease(newRelease, m_cycleIdx);
|
||||
ctrls->m_release->setValue(newRelease);
|
||||
}
|
||||
}
|
||||
|
||||
ADSRView::ADSRView(QWidget* parent) : QWidget(parent) {
|
||||
for (size_t i = 0; i < m_percentTexts.size(); ++i) {
|
||||
m_percentTexts[i].setText(QStringLiteral("%1%").arg(100 - i * 10));
|
||||
m_percentTexts[i].setTextOption(QTextOption(Qt::AlignVCenter | Qt::AlignRight));
|
||||
m_percentTexts[i].setTextWidth(28.0);
|
||||
}
|
||||
m_gridFont.setPointSize(8);
|
||||
}
|
||||
|
||||
ADSRView::~ADSRView() = default;
|
||||
|
||||
ADSREditor* ADSRControls::getEditor() const { return qobject_cast<ADSREditor*>(parentWidget()); }
|
||||
|
||||
void ADSRControls::loadData() {
|
||||
m_enableUpdate = false;
|
||||
|
||||
const amuse::ITable& table = **getEditor()->m_adsrView->m_node->m_obj;
|
||||
m_attack->setDisabled(false);
|
||||
m_decay->setDisabled(false);
|
||||
m_sustain->setDisabled(false);
|
||||
m_release->setDisabled(false);
|
||||
m_dls->setDisabled(false);
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
const auto& adsr = static_cast<const amuse::ADSRDLS&>(table);
|
||||
m_attack->setValue(adsr.getAttack());
|
||||
m_decay->setValue(adsr.getDecay());
|
||||
m_sustain->setValue(adsr.getSustain() * 100.0);
|
||||
m_release->setValue(adsr.getRelease());
|
||||
m_dls->setChecked(true);
|
||||
m_velToAttackLab->setVisible(true);
|
||||
m_velToAttack->setVisible(true);
|
||||
m_velToAttack->setValue(adsr._getVelToAttack());
|
||||
m_keyToDecayLab->setVisible(true);
|
||||
m_keyToDecay->setVisible(true);
|
||||
m_keyToDecay->setValue(adsr._getKeyToDecay());
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
const auto& adsr = static_cast<const amuse::ADSR&>(table);
|
||||
m_attack->setValue(adsr.getAttack());
|
||||
m_decay->setValue(adsr.getDecay());
|
||||
m_sustain->setValue(adsr.getSustain() * 100.0);
|
||||
m_release->setValue(adsr.getRelease());
|
||||
m_dls->setChecked(false);
|
||||
m_velToAttackLab->setVisible(false);
|
||||
m_velToAttack->setVisible(false);
|
||||
m_keyToDecayLab->setVisible(false);
|
||||
m_keyToDecay->setVisible(false);
|
||||
} else {
|
||||
unloadData();
|
||||
}
|
||||
|
||||
m_enableUpdate = true;
|
||||
}
|
||||
|
||||
void ADSRControls::unloadData() {
|
||||
m_attack->setDisabled(true);
|
||||
m_decay->setDisabled(true);
|
||||
m_sustain->setDisabled(true);
|
||||
m_release->setDisabled(true);
|
||||
m_dls->setDisabled(true);
|
||||
m_velToAttackLab->setVisible(false);
|
||||
m_velToAttack->setVisible(false);
|
||||
m_keyToDecayLab->setVisible(false);
|
||||
m_keyToDecay->setVisible(false);
|
||||
}
|
||||
|
||||
class ADSRAttackUndoCommand : public EditorUndoCommand {
|
||||
double m_redoVal, m_undoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRAttackUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack")), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr.setAttack(m_undoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
adsr.setAttack(m_undoVal);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoVal = adsr.getAttack();
|
||||
adsr.setAttack(m_redoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
m_undoVal = adsr.getAttack();
|
||||
adsr.setAttack(m_redoVal);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id()) {
|
||||
m_redoVal = static_cast<const ADSRAttackUndoCommand*>(other)->m_redoVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRAttack); }
|
||||
};
|
||||
|
||||
void ADSRControls::attackChanged(double val) {
|
||||
if (m_enableUpdate) {
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(new ADSRAttackUndoCommand(val, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
class ADSRDecayUndoCommand : public EditorUndoCommand {
|
||||
double m_redoVal, m_undoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRDecayUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay")), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr.setDecay(m_undoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
adsr.setDecay(m_undoVal);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoVal = adsr.getDecay();
|
||||
adsr.setDecay(m_redoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
m_undoVal = adsr.getDecay();
|
||||
adsr.setDecay(m_redoVal);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id()) {
|
||||
m_redoVal = static_cast<const ADSRDecayUndoCommand*>(other)->m_redoVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRDecay); }
|
||||
};
|
||||
|
||||
void ADSRControls::decayChanged(double val) {
|
||||
if (m_enableUpdate) {
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(new ADSRDecayUndoCommand(val, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
class ADSRSustainUndoCommand : public EditorUndoCommand {
|
||||
double m_redoVal, m_undoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRSustainUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Sustain")), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr.setSustain(m_undoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
adsr.setSustain(m_undoVal);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoVal = adsr.getSustain();
|
||||
adsr.setSustain(m_redoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
m_undoVal = adsr.getSustain();
|
||||
adsr.setSustain(m_redoVal);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id()) {
|
||||
m_redoVal = static_cast<const ADSRSustainUndoCommand*>(other)->m_redoVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRSustain); }
|
||||
};
|
||||
|
||||
void ADSRControls::sustainChanged(double val) {
|
||||
if (m_enableUpdate) {
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(new ADSRSustainUndoCommand(val / 100.0, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
class ADSRAttackAndDecayUndoCommand : public EditorUndoCommand {
|
||||
double m_redoAttack, m_redoDecay;
|
||||
double m_undoAttack, m_undoDecay;
|
||||
uint64_t m_cycleCount;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRAttackAndDecayUndoCommand(double redoAttack, double redoDecay, uint64_t cycleCount,
|
||||
amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack/Decay"))
|
||||
, m_redoAttack(redoAttack)
|
||||
, m_redoDecay(redoDecay)
|
||||
, m_cycleCount(cycleCount) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr.setAttack(m_undoAttack);
|
||||
adsr.setDecay(m_undoDecay);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
adsr.setAttack(m_undoAttack);
|
||||
adsr.setDecay(m_undoDecay);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoAttack = adsr.getAttack();
|
||||
m_undoDecay = adsr.getDecay();
|
||||
adsr.setAttack(m_redoAttack);
|
||||
adsr.setDecay(m_redoDecay);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
m_undoAttack = adsr.getAttack();
|
||||
m_undoDecay = adsr.getDecay();
|
||||
adsr.setAttack(m_redoAttack);
|
||||
adsr.setDecay(m_redoDecay);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
const auto* const command = static_cast<const ADSRAttackAndDecayUndoCommand*>(other);
|
||||
|
||||
if (other->id() == id() && m_cycleCount == command->m_cycleCount) {
|
||||
m_redoAttack = command->m_redoAttack;
|
||||
m_redoDecay = command->m_redoDecay;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRAttackAndDecay); }
|
||||
};
|
||||
|
||||
void ADSRControls::setAttackAndDecay(double attack, double decay, uint64_t cycleCount) {
|
||||
m_enableUpdate = false;
|
||||
m_attack->setValue(attack);
|
||||
m_decay->setValue(decay);
|
||||
m_enableUpdate = true;
|
||||
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new ADSRAttackAndDecayUndoCommand(m_attack->value(), m_decay->value(), cycleCount, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
|
||||
class ADSRDecayAndSustainUndoCommand : public EditorUndoCommand {
|
||||
double m_redoDecay, m_redoSustain;
|
||||
double m_undoDecay, m_undoSustain;
|
||||
uint64_t m_cycleCount;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRDecayAndSustainUndoCommand(double redoDecay, double redoSustain, uint64_t cycleCount,
|
||||
amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay/Sustain"))
|
||||
, m_redoDecay(redoDecay)
|
||||
, m_redoSustain(redoSustain)
|
||||
, m_cycleCount(cycleCount) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr.setDecay(m_undoDecay);
|
||||
adsr.setSustain(m_undoSustain);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
adsr.setDecay(m_undoDecay);
|
||||
adsr.setSustain(m_undoSustain);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoDecay = adsr.getDecay();
|
||||
m_undoSustain = adsr.getSustain();
|
||||
adsr.setDecay(m_redoDecay);
|
||||
adsr.setSustain(m_redoSustain);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
m_undoDecay = adsr.getDecay();
|
||||
m_undoSustain = adsr.getSustain();
|
||||
adsr.setDecay(m_redoDecay);
|
||||
adsr.setSustain(m_redoSustain);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
const auto* const command = static_cast<const ADSRDecayAndSustainUndoCommand*>(other);
|
||||
|
||||
if (other->id() == id() && m_cycleCount == command->m_cycleCount) {
|
||||
m_redoDecay = command->m_redoDecay;
|
||||
m_redoSustain = command->m_redoSustain;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRDecayAndSustain); }
|
||||
};
|
||||
|
||||
void ADSRControls::setDecayAndSustain(double decay, double sustain, uint64_t cycleCount) {
|
||||
m_enableUpdate = false;
|
||||
m_decay->setValue(decay);
|
||||
m_sustain->setValue(sustain);
|
||||
m_enableUpdate = true;
|
||||
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new ADSRDecayAndSustainUndoCommand(m_decay->value(), m_sustain->value() / 100.0, cycleCount, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
|
||||
class ADSRReleaseUndoCommand : public EditorUndoCommand {
|
||||
double m_redoVal, m_undoVal;
|
||||
uint64_t m_cycleCount;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRReleaseUndoCommand(double redoVal, uint64_t cycleCount, amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Release")), m_redoVal(redoVal), m_cycleCount(cycleCount) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr.setRelease(m_undoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
adsr.setRelease(m_undoVal);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoVal = adsr.getRelease();
|
||||
adsr.setRelease(m_redoVal);
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
m_undoVal = adsr.getRelease();
|
||||
adsr.setRelease(m_redoVal);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
const auto* const command = static_cast<const ADSRReleaseUndoCommand*>(other);
|
||||
|
||||
if (other->id() == id() && m_cycleCount == command->m_cycleCount) {
|
||||
m_redoVal = command->m_redoVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRRelease); }
|
||||
};
|
||||
|
||||
void ADSRControls::setRelease(double release, uint64_t cycleCount) {
|
||||
m_enableUpdate = false;
|
||||
m_release->setValue(release);
|
||||
m_enableUpdate = true;
|
||||
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(new ADSRReleaseUndoCommand(m_release->value(), cycleCount, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
|
||||
void ADSRControls::releaseChanged(double val) {
|
||||
if (m_enableUpdate) {
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(new ADSRReleaseUndoCommand(val, ~0ull, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static std::unique_ptr<T> MakeAlternateVersion(amuse::ITable& table) {
|
||||
std::unique_ptr<T> ret = std::make_unique<T>();
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
ret->setAttack(adsr.getAttack());
|
||||
ret->setDecay(adsr.getDecay());
|
||||
ret->setSustain(adsr.getSustain());
|
||||
ret->setRelease(adsr.getRelease());
|
||||
} else if (table.Isa() == amuse::ITable::Type::ADSR) {
|
||||
auto& adsr = static_cast<amuse::ADSR&>(table);
|
||||
ret->setAttack(adsr.getAttack());
|
||||
ret->setDecay(adsr.getDecay());
|
||||
ret->setSustain(adsr.getSustain());
|
||||
ret->setRelease(adsr.getRelease());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
class ADSRDLSUndoCommand : public EditorUndoCommand {
|
||||
bool m_redoVal;
|
||||
double m_redoVelToAttack, m_redoKeyToDecay;
|
||||
double m_undoVelToAttack, m_undoKeyToDecay;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRDLSUndoCommand(bool redoVal, double redoVelToAttack, double redoKeyToDecay,
|
||||
amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change DLS"))
|
||||
, m_redoVal(redoVal)
|
||||
, m_redoVelToAttack(redoVelToAttack)
|
||||
, m_redoKeyToDecay(redoKeyToDecay) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
std::unique_ptr<amuse::ITable>& table = *m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if ((table->Isa() == amuse::ITable::Type::ADSRDLS && !m_redoVal) ||
|
||||
(table->Isa() == amuse::ITable::Type::ADSR && m_redoVal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<amuse::ITable> oldTable = std::move(table);
|
||||
if (!m_redoVal) {
|
||||
table = MakeAlternateVersion<amuse::ADSRDLS>(*oldTable);
|
||||
static_cast<amuse::ADSRDLS&>(*table)._setVelToAttack(m_undoVelToAttack);
|
||||
static_cast<amuse::ADSRDLS&>(*table)._setKeyToDecay(m_undoKeyToDecay);
|
||||
} else {
|
||||
table = MakeAlternateVersion<amuse::ADSR>(*oldTable);
|
||||
}
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
std::unique_ptr<amuse::ITable>& table = *m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if ((table->Isa() == amuse::ITable::Type::ADSRDLS && m_redoVal) ||
|
||||
(table->Isa() == amuse::ITable::Type::ADSR && !m_redoVal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<amuse::ITable> oldTable = std::move(table);
|
||||
if (m_redoVal) {
|
||||
table = MakeAlternateVersion<amuse::ADSRDLS>(*oldTable);
|
||||
static_cast<amuse::ADSRDLS&>(*table)._setVelToAttack(m_redoVelToAttack);
|
||||
static_cast<amuse::ADSRDLS&>(*table)._setKeyToDecay(m_redoKeyToDecay);
|
||||
} else {
|
||||
if (oldTable->Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
m_undoVelToAttack = static_cast<amuse::ADSRDLS&>(*oldTable)._getVelToAttack();
|
||||
m_undoKeyToDecay = static_cast<amuse::ADSRDLS&>(*oldTable)._getKeyToDecay();
|
||||
}
|
||||
table = MakeAlternateVersion<amuse::ADSR>(*oldTable);
|
||||
}
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id()) {
|
||||
const auto* const command = static_cast<const ADSRDLSUndoCommand*>(other);
|
||||
|
||||
m_redoVal = command->m_redoVal;
|
||||
m_redoVelToAttack = command->m_redoVelToAttack;
|
||||
m_redoKeyToDecay = command->m_redoKeyToDecay;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRDLS); }
|
||||
};
|
||||
|
||||
void ADSRControls::dlsStateChanged(int state) {
|
||||
if (m_enableUpdate) {
|
||||
m_velToAttackLab->setVisible(state == Qt::Checked);
|
||||
m_velToAttack->setVisible(state == Qt::Checked);
|
||||
m_keyToDecayLab->setVisible(state == Qt::Checked);
|
||||
m_keyToDecay->setVisible(state == Qt::Checked);
|
||||
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new ADSRDLSUndoCommand(state == Qt::Checked, m_velToAttack->value(), m_keyToDecay->value(), view->m_node));
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
class ADSRVelToAttackUndoCommand : public EditorUndoCommand {
|
||||
double m_redoVal, m_undoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRVelToAttackUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Vel To Attack")), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr._setVelToAttack(m_undoVal);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoVal = adsr._getVelToAttack();
|
||||
adsr._setVelToAttack(m_redoVal);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id()) {
|
||||
m_redoVal = static_cast<const ADSRVelToAttackUndoCommand*>(other)->m_redoVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRVelToAttack); }
|
||||
};
|
||||
|
||||
void ADSRControls::velToAttackChanged(double val) {
|
||||
if (m_enableUpdate) {
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(new ADSRVelToAttackUndoCommand(val, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
class ADSRKeyToDecayUndoCommand : public EditorUndoCommand {
|
||||
double m_redoVal, m_undoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
ADSRKeyToDecayUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node)
|
||||
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Key To Decay")), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
adsr._setKeyToDecay(m_undoVal);
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::ADSRNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::ADSRDLS) {
|
||||
auto& adsr = static_cast<amuse::ADSRDLS&>(table);
|
||||
m_undoVal = adsr._getKeyToDecay();
|
||||
adsr._setKeyToDecay(m_redoVal);
|
||||
}
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id()) {
|
||||
m_redoVal = static_cast<const ADSRKeyToDecayUndoCommand*>(other)->m_redoVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::ADSRKeyToDecay); }
|
||||
};
|
||||
|
||||
void ADSRControls::keyToDecayChanged(double val) {
|
||||
if (m_enableUpdate) {
|
||||
ADSRView* view = getEditor()->m_adsrView;
|
||||
g_MainWindow->pushUndoCommand(new ADSRKeyToDecayUndoCommand(val, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
ADSRControls::ADSRControls(QWidget* parent) : QFrame(parent) {
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
setFixedHeight(100);
|
||||
setFrameShape(QFrame::StyledPanel);
|
||||
setFrameShadow(QFrame::Sunken);
|
||||
setBackgroundRole(QPalette::Base);
|
||||
setAutoFillBackground(true);
|
||||
|
||||
auto* const mainLayout = new QHBoxLayout;
|
||||
auto* const leftLayout = new QGridLayout;
|
||||
|
||||
QPalette palette = QWidget::palette();
|
||||
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
|
||||
|
||||
palette.setColor(QPalette::Text, Red);
|
||||
QLabel* lab = new QLabel(tr("Attack"));
|
||||
lab->setPalette(palette);
|
||||
leftLayout->addWidget(lab, 0, 0);
|
||||
m_attack = new QDoubleSpinBox;
|
||||
m_attack->setDisabled(true);
|
||||
m_attack->setRange(0, 65.535);
|
||||
m_attack->setDecimals(3);
|
||||
m_attack->setSingleStep(0.1);
|
||||
m_attack->setSuffix(tr(" sec"));
|
||||
m_attack->setPalette(palette);
|
||||
connect(m_attack, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::attackChanged);
|
||||
leftLayout->addWidget(m_attack, 1, 0);
|
||||
|
||||
palette.setColor(QPalette::Text, Green);
|
||||
lab = new QLabel(tr("Decay"));
|
||||
lab->setPalette(palette);
|
||||
leftLayout->addWidget(lab, 0, 1);
|
||||
m_decay = new QDoubleSpinBox;
|
||||
m_decay->setDisabled(true);
|
||||
m_decay->setRange(0, 65.535);
|
||||
m_decay->setDecimals(3);
|
||||
m_decay->setSingleStep(0.1);
|
||||
m_decay->setSuffix(tr(" sec"));
|
||||
m_decay->setPalette(palette);
|
||||
connect(m_decay, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::decayChanged);
|
||||
leftLayout->addWidget(m_decay, 1, 1);
|
||||
|
||||
palette.setColor(QPalette::Text, Qt::white);
|
||||
leftLayout->addWidget(new QLabel(tr("Sustain")), 0, 2);
|
||||
m_sustain = new QDoubleSpinBox;
|
||||
m_sustain->setDisabled(true);
|
||||
m_sustain->setRange(0.0, 100.0);
|
||||
m_sustain->setDecimals(3);
|
||||
m_sustain->setSuffix(tr(" %"));
|
||||
m_sustain->setPalette(palette);
|
||||
connect(m_sustain, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::sustainChanged);
|
||||
leftLayout->addWidget(m_sustain, 1, 2);
|
||||
|
||||
palette.setColor(QPalette::Text, Blue);
|
||||
lab = new QLabel(tr("Release"));
|
||||
lab->setPalette(palette);
|
||||
leftLayout->addWidget(lab, 0, 3);
|
||||
m_release = new QDoubleSpinBox;
|
||||
m_release->setDisabled(true);
|
||||
m_release->setRange(0.0, 65.535);
|
||||
m_release->setDecimals(3);
|
||||
m_release->setSingleStep(0.1);
|
||||
m_release->setSuffix(tr(" sec"));
|
||||
m_release->setPalette(palette);
|
||||
connect(m_release, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::releaseChanged);
|
||||
leftLayout->addWidget(m_release, 1, 3);
|
||||
|
||||
palette.setColor(QPalette::Text, Qt::white);
|
||||
leftLayout->addWidget(new QLabel(tr("DLS")), 0, 4);
|
||||
m_dls = new QCheckBox;
|
||||
m_dls->setPalette(palette);
|
||||
m_dls->setDisabled(true);
|
||||
m_dls->setChecked(false);
|
||||
connect(m_dls, qOverload<int>(&QCheckBox::stateChanged), this, &ADSRControls::dlsStateChanged);
|
||||
leftLayout->addWidget(m_dls, 1, 4);
|
||||
|
||||
m_velToAttackLab = new QLabel(tr("Vel To Attack"));
|
||||
m_velToAttackLab->setVisible(false);
|
||||
leftLayout->addWidget(m_velToAttackLab, 0, 5);
|
||||
m_velToAttack = new QDoubleSpinBox;
|
||||
m_velToAttack->setVisible(false);
|
||||
m_velToAttack->setRange(0.0, 9999.999);
|
||||
m_velToAttack->setDecimals(3);
|
||||
m_velToAttack->setSingleStep(1.0);
|
||||
m_velToAttack->setPalette(palette);
|
||||
connect(m_velToAttack, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::velToAttackChanged);
|
||||
leftLayout->addWidget(m_velToAttack, 1, 5);
|
||||
|
||||
m_keyToDecayLab = new QLabel(tr("Key To Decay"));
|
||||
m_keyToDecayLab->setVisible(false);
|
||||
leftLayout->addWidget(m_keyToDecayLab, 0, 6);
|
||||
m_keyToDecay = new QDoubleSpinBox;
|
||||
m_keyToDecay->setVisible(false);
|
||||
m_keyToDecay->setRange(0.0, 9999.999);
|
||||
m_keyToDecay->setDecimals(3);
|
||||
m_keyToDecay->setSingleStep(1.0);
|
||||
m_keyToDecay->setPalette(palette);
|
||||
connect(m_keyToDecay, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ADSRControls::keyToDecayChanged);
|
||||
leftLayout->addWidget(m_keyToDecay, 1, 6);
|
||||
|
||||
leftLayout->setColumnMinimumWidth(0, 75);
|
||||
leftLayout->setColumnStretch(0, 1);
|
||||
leftLayout->setColumnMinimumWidth(1, 75);
|
||||
leftLayout->setColumnStretch(1, 1);
|
||||
leftLayout->setColumnMinimumWidth(2, 75);
|
||||
leftLayout->setColumnStretch(2, 1);
|
||||
leftLayout->setColumnMinimumWidth(3, 75);
|
||||
leftLayout->setColumnStretch(3, 1);
|
||||
leftLayout->setColumnMinimumWidth(4, 50);
|
||||
leftLayout->setColumnStretch(4, 0);
|
||||
leftLayout->setColumnMinimumWidth(5, 75);
|
||||
leftLayout->setColumnStretch(5, 1);
|
||||
leftLayout->setColumnMinimumWidth(6, 75);
|
||||
leftLayout->setColumnStretch(6, 1);
|
||||
leftLayout->setRowMinimumHeight(0, 22);
|
||||
leftLayout->setRowMinimumHeight(1, 37);
|
||||
leftLayout->setContentsMargins(10, 6, 0, 14);
|
||||
|
||||
mainLayout->addLayout(leftLayout);
|
||||
mainLayout->addStretch();
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
ADSRControls::~ADSRControls() = default;
|
||||
|
||||
bool ADSREditor::loadData(ProjectModel::ADSRNode* node) {
|
||||
m_adsrView->loadData(node);
|
||||
m_controls->loadData();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ADSREditor::unloadData() {
|
||||
m_adsrView->unloadData();
|
||||
m_controls->unloadData();
|
||||
}
|
||||
|
||||
ProjectModel::INode* ADSREditor::currentNode() const { return m_adsrView->currentNode(); }
|
||||
|
||||
ADSREditor::ADSREditor(QWidget* parent) : EditorWidget(parent), m_adsrView(new ADSRView), m_controls(new ADSRControls) {
|
||||
auto* const layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(QMargins());
|
||||
layout->setSpacing(1);
|
||||
layout->addWidget(m_adsrView);
|
||||
layout->addWidget(m_controls);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
ADSREditor::~ADSREditor() = default;
|
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <QFrame>
|
||||
#include <QStaticText>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class ADSREditor;
|
||||
|
||||
class QCheckBox;
|
||||
class QDoubleSpinBox;
|
||||
class QLabel;
|
||||
|
||||
class ADSRView : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class ADSRControls;
|
||||
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
|
||||
QFont m_gridFont;
|
||||
std::array<QStaticText, 11> m_percentTexts;
|
||||
std::vector<QStaticText> m_timeTexts;
|
||||
int m_dragPoint = -1;
|
||||
uint64_t m_cycleIdx = 0;
|
||||
ADSREditor* getEditor() const;
|
||||
|
||||
public:
|
||||
explicit ADSRView(QWidget* parent = Q_NULLPTR);
|
||||
~ADSRView() override;
|
||||
|
||||
void loadData(ProjectModel::ADSRNode* node);
|
||||
void unloadData();
|
||||
ProjectModel::INode* currentNode() const;
|
||||
|
||||
void paintEvent(QPaintEvent* ev) override;
|
||||
void mousePressEvent(QMouseEvent* ev) override;
|
||||
void mouseReleaseEvent(QMouseEvent* ev) override;
|
||||
void mouseMoveEvent(QMouseEvent* ev) override;
|
||||
};
|
||||
|
||||
class ADSRControls : public QFrame {
|
||||
Q_OBJECT
|
||||
friend class ADSRView;
|
||||
QDoubleSpinBox* m_attack;
|
||||
QDoubleSpinBox* m_decay;
|
||||
QDoubleSpinBox* m_sustain;
|
||||
QDoubleSpinBox* m_release;
|
||||
QCheckBox* m_dls;
|
||||
QLabel* m_velToAttackLab;
|
||||
QDoubleSpinBox* m_velToAttack;
|
||||
QLabel* m_keyToDecayLab;
|
||||
QDoubleSpinBox* m_keyToDecay;
|
||||
bool m_enableUpdate = true;
|
||||
ADSREditor* getEditor() const;
|
||||
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
|
||||
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
|
||||
void setRelease(double release, uint64_t cycleCount);
|
||||
|
||||
public:
|
||||
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
|
||||
~ADSRControls() override;
|
||||
|
||||
void loadData();
|
||||
void unloadData();
|
||||
|
||||
public slots:
|
||||
void attackChanged(double val);
|
||||
void decayChanged(double val);
|
||||
void sustainChanged(double val);
|
||||
void releaseChanged(double val);
|
||||
void dlsStateChanged(int state);
|
||||
void velToAttackChanged(double val);
|
||||
void keyToDecayChanged(double val);
|
||||
};
|
||||
|
||||
class ADSREditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
friend class ADSRView;
|
||||
friend class ADSRControls;
|
||||
ADSRView* m_adsrView;
|
||||
ADSRControls* m_controls;
|
||||
|
||||
public:
|
||||
explicit ADSREditor(QWidget* parent = Q_NULLPTR);
|
||||
~ADSREditor() override;
|
||||
|
||||
bool loadData(ProjectModel::ADSRNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
};
|
|
@ -0,0 +1,154 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# Automatically handle invoking autorcc, moc, and uic.
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
find_package(Qt6 COMPONENTS LinguistTools Network Qml SvgWidgets Widgets Xml REQUIRED)
|
||||
|
||||
configure_file(resources/translation_res.qrc translation_res.qrc @ONLY)
|
||||
set(TRANSLATIONS
|
||||
resources/lang_de.ts
|
||||
)
|
||||
QT6_CREATE_TRANSLATION(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../lib ${TRANSLATIONS})
|
||||
|
||||
add_executable(amuse-gui WIN32 MACOSX_BUNDLE
|
||||
ADSREditor.cpp
|
||||
ADSREditor.hpp
|
||||
Common.cpp
|
||||
Common.hpp
|
||||
CurveEditor.cpp
|
||||
CurveEditor.hpp
|
||||
EditorWidget.cpp
|
||||
EditorWidget.hpp
|
||||
KeyboardWidget.cpp
|
||||
KeyboardWidget.hpp
|
||||
KeymapEditor.cpp
|
||||
KeymapEditor.hpp
|
||||
LayersEditor.cpp
|
||||
LayersEditor.hpp
|
||||
MainWindow.cpp
|
||||
MainWindow.hpp
|
||||
MainWindow.ui
|
||||
MIDIReader.cpp
|
||||
MIDIReader.hpp
|
||||
NewSoundMacroDialog.cpp
|
||||
NewSoundMacroDialog.hpp
|
||||
ProjectModel.cpp
|
||||
ProjectModel.hpp
|
||||
SampleEditor.cpp
|
||||
SampleEditor.hpp
|
||||
SongGroupEditor.cpp
|
||||
SongGroupEditor.hpp
|
||||
SoundGroupEditor.cpp
|
||||
SoundGroupEditor.hpp
|
||||
SoundMacroEditor.cpp
|
||||
SoundMacroEditor.hpp
|
||||
StatusBarWidget.cpp
|
||||
StatusBarWidget.hpp
|
||||
StudioSetupWidget.cpp
|
||||
StudioSetupWidget.hpp
|
||||
|
||||
main.cpp
|
||||
|
||||
resources/resources.qrc
|
||||
${CMAKE_CURRENT_BINARY_DIR}/translation_res.qrc
|
||||
|
||||
${QM_FILES}
|
||||
)
|
||||
|
||||
target_compile_definitions(amuse-gui PRIVATE
|
||||
# Disable implicit conversions from ASCII to QString.
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_CAST_TO_ASCII
|
||||
|
||||
# Disable implicit conversions of QByteArray to const char* or const void*
|
||||
-DQT_NO_CAST_FROM_BYTEARRAY
|
||||
|
||||
# Disable narrowing conversions in signal/slot connect() calls.
|
||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT
|
||||
|
||||
# Disable unsafe overloads of QProcess' start() function.
|
||||
-DQT_NO_PROCESS_COMBINED_ARGUMENT_START
|
||||
|
||||
# Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
|
||||
-DQT_NO_URL_CAST_FROM_STRING
|
||||
|
||||
# Allows for more efficient string concatenation, resulting in less temporaries.
|
||||
-DQT_USE_QSTRINGBUILDER
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(amuse-gui PRIVATE
|
||||
# Enforce various standards compliant behavior.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
|
||||
|
||||
# Enable standard volatile semantics.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
|
||||
|
||||
# Reports the proper value for the __cplusplus preprocessor macro.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
|
||||
|
||||
# Use latest C++ standard.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>
|
||||
)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# Flags for MSVC (not clang-cl)
|
||||
target_compile_options(amuse-gui PRIVATE
|
||||
# Allow constexpr variables to have explicit external linkage.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>
|
||||
|
||||
# Assume that new throws exceptions, allowing better code generation.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:throwingNew>
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(amuse-gui PRIVATE
|
||||
platforms/win/amuse-gui.rc
|
||||
platforms/win/amuse-gui.manifest
|
||||
)
|
||||
elseif(APPLE)
|
||||
target_sources(amuse-gui PRIVATE
|
||||
MacOSExtras.mm
|
||||
platforms/mac/mainicon.icns
|
||||
)
|
||||
set_source_files_properties(platforms/mac/mainicon.icns PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION Resources
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(platforms/freedesktop)
|
||||
declare_qticon_target()
|
||||
target_sources(amuse-gui PRIVATE mainicon_qt.cpp)
|
||||
|
||||
if(COMMAND add_sanitizers)
|
||||
add_sanitizers(amuse-gui)
|
||||
endif()
|
||||
|
||||
set_target_properties(amuse-gui PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist"
|
||||
)
|
||||
|
||||
target_link_libraries(amuse-gui
|
||||
${PLAT_LIBS}
|
||||
|
||||
Qt6::Network
|
||||
Qt6::Qml
|
||||
Qt6::SvgWidgets
|
||||
Qt6::Widgets
|
||||
Qt6::Xml
|
||||
|
||||
amuse
|
||||
athena-core
|
||||
boo
|
||||
${BOO_SYS_LIBS}
|
||||
logvisor
|
||||
lzokay
|
||||
xxhash
|
||||
${ZLIB_LIBRARIES}
|
||||
)
|
|
@ -0,0 +1,91 @@
|
|||
#include "Common.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QTransform>
|
||||
|
||||
std::string QStringToUTF8(const QString& str) {
|
||||
return str.toUtf8().toStdString();
|
||||
}
|
||||
|
||||
QString UTF8ToQString(const std::string& str) {
|
||||
return QString::fromStdString(str);
|
||||
}
|
||||
|
||||
bool MkPath(const QString& path, UIMessenger& messenger) {
|
||||
QFileInfo fInfo(path);
|
||||
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
|
||||
}
|
||||
|
||||
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger) {
|
||||
if (!dir.mkpath(file)) {
|
||||
QString msg = QString(MainWindow::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
|
||||
messenger.critical(MainWindow::tr("Unable to create directory"), msg);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn) {
|
||||
const QFileInfo fileInfo(pathIn);
|
||||
// Mac, Windows support folder or file.
|
||||
#if defined(Q_OS_WIN)
|
||||
QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path"));
|
||||
QString explorer;
|
||||
for (QString path : paths.split(QStringLiteral(";"))) {
|
||||
QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe"));
|
||||
if (finfo.exists()) {
|
||||
explorer = finfo.filePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (explorer.isEmpty()) {
|
||||
QMessageBox::warning(parent, MainWindow::tr("Launching Windows Explorer Failed"),
|
||||
MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer."));
|
||||
return;
|
||||
}
|
||||
QStringList param;
|
||||
if (!fileInfo.isDir())
|
||||
param += QLatin1String("/select,");
|
||||
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
|
||||
QProcess::startDetached(explorer, param);
|
||||
#elif defined(Q_OS_MAC)
|
||||
QStringList scriptArgs;
|
||||
scriptArgs << QLatin1String("-e")
|
||||
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
|
||||
.arg(fileInfo.canonicalFilePath());
|
||||
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
||||
scriptArgs.clear();
|
||||
scriptArgs << QLatin1String("-e") << QLatin1String("tell application \"Finder\" to activate");
|
||||
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
||||
#else
|
||||
// we cannot select a file here, because no file browser really supports it...
|
||||
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
|
||||
QProcess browserProc;
|
||||
const QStringList browserArgs = QStringList() << QStringLiteral("%1").arg(QFileInfo(folder).path());
|
||||
browserProc.startDetached(QStringLiteral("xdg-open"), browserArgs);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString ShowInGraphicalShellString() {
|
||||
#if defined(Q_OS_WIN)
|
||||
return MainWindow::tr("Show in Explorer");
|
||||
#elif defined(Q_OS_MAC)
|
||||
return MainWindow::tr("Show in Finder");
|
||||
#else
|
||||
return MainWindow::tr("Show in Browser");
|
||||
#endif
|
||||
}
|
||||
|
||||
QTransform RectToRect(const QRectF& from, const QRectF& to) {
|
||||
QPolygonF orig(from);
|
||||
orig.pop_back();
|
||||
QPolygonF resize(to);
|
||||
resize.pop_back();
|
||||
QTransform ret;
|
||||
QTransform::quadToQuad(orig, resize, ret);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QString>
|
||||
|
||||
#include <boo/System.hpp>
|
||||
|
||||
class MainWindow;
|
||||
extern MainWindow* g_MainWindow;
|
||||
|
||||
class QDir;
|
||||
class QRectF;
|
||||
class QTransform;
|
||||
|
||||
class UIMessenger : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QObject::QObject;
|
||||
signals:
|
||||
QMessageBox::StandardButton information(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
int question(const QString& title, const QString& text, const QString& button0Text,
|
||||
const QString& button1Text = QString(), const QString& button2Text = QString(),
|
||||
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
|
||||
QMessageBox::StandardButton
|
||||
question(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
QMessageBox::StandardButton warning(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
QMessageBox::StandardButton critical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
};
|
||||
|
||||
std::string QStringToUTF8(const QString& str);
|
||||
QString UTF8ToQString(const std::string& str);
|
||||
|
||||
bool MkPath(const QString& path, UIMessenger& messenger);
|
||||
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger);
|
||||
|
||||
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn);
|
||||
QString ShowInGraphicalShellString();
|
||||
|
||||
/* Used for generating transform matrices to map SVG coordinate space */
|
||||
QTransform RectToRect(const QRectF& from, const QRectF& to);
|
|
@ -0,0 +1,303 @@
|
|||
#include "CurveEditor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QJSValueIterator>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
|
||||
class CurveEditUndoCommand : public EditorUndoCommand {
|
||||
public:
|
||||
using RedoData = std::array<uint8_t, 128>;
|
||||
using UndoData = std::array<uint8_t, 128>;
|
||||
|
||||
CurveEditUndoCommand(const RedoData& redoData, bool usedExpr, amuse::ObjToken<ProjectModel::CurveNode> node)
|
||||
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")), m_redoData{redoData}, m_usedExpr(usedExpr) {}
|
||||
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::Curve) {
|
||||
auto& curve = static_cast<amuse::Curve&>(table);
|
||||
curve.data.assign(m_undoData.cbegin(), m_undoData.cend());
|
||||
}
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
|
||||
void redo() override {
|
||||
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
|
||||
if (table.Isa() == amuse::ITable::Type::Curve) {
|
||||
auto& curve = static_cast<amuse::Curve&>(table);
|
||||
curve.data.resize(128);
|
||||
std::copy(curve.data.cbegin(), curve.data.cend(), m_undoData.begin());
|
||||
curve.data.assign(m_redoData.cbegin(), m_redoData.cend());
|
||||
}
|
||||
if (m_undid) {
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
}
|
||||
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
const auto* const command = static_cast<const CurveEditUndoCommand*>(other);
|
||||
|
||||
if (other->id() == id() && !m_usedExpr && !command->m_usedExpr) {
|
||||
m_redoData = command->m_redoData;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::CurveEdit); }
|
||||
|
||||
private:
|
||||
RedoData m_redoData;
|
||||
UndoData m_undoData;
|
||||
bool m_usedExpr;
|
||||
bool m_undid = false;
|
||||
};
|
||||
|
||||
CurveEditor* CurveView::getEditor() const { return qobject_cast<CurveEditor*>(parentWidget()); }
|
||||
|
||||
void CurveView::loadData(ProjectModel::CurveNode* node) { m_node = node; }
|
||||
|
||||
void CurveView::unloadData() { m_node.reset(); }
|
||||
|
||||
ProjectModel::INode* CurveView::currentNode() const { return m_node.get(); }
|
||||
|
||||
void CurveView::paintEvent(QPaintEvent* ev) {
|
||||
if (!m_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
amuse::ITable& table = **m_node->m_obj;
|
||||
if (table.Isa() != amuse::ITable::Type::Curve) {
|
||||
return;
|
||||
}
|
||||
auto& curve = static_cast<amuse::Curve&>(table);
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const qreal deviceRatio = devicePixelRatioF();
|
||||
const qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
|
||||
|
||||
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
|
||||
painter.setFont(m_gridFont);
|
||||
const qreal yIncrement = (height() - 16.0) / 10.0;
|
||||
for (size_t i = 0; i < m_percentTexts.size(); ++i) {
|
||||
const qreal thisY = i * yIncrement;
|
||||
const qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0));
|
||||
painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]);
|
||||
painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY));
|
||||
}
|
||||
|
||||
qreal xIncrement = (width() - 30.0) / 10.0;
|
||||
for (size_t i = 0; i < m_percentTextsCenter.size(); ++i) {
|
||||
const qreal thisX = i * xIncrement + 30.0;
|
||||
const qreal textX = thisX - (i == 10 ? 30.0 : 15.0);
|
||||
painter.drawStaticText(QPointF(textX, height() - 16.0), m_percentTextsCenter[i]);
|
||||
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
|
||||
}
|
||||
|
||||
size_t i;
|
||||
xIncrement = (width() - 30.0) / 127.0;
|
||||
QPointF lastPt;
|
||||
painter.setPen(QPen(Qt::white, penWidth * 3.0));
|
||||
for (i = 0; i < curve.data.size(); ++i) {
|
||||
const QPointF thisPt(i * xIncrement + 30.0, (height() - 16.0) - (height() - 16.0) * (curve.data[i] / 127.0));
|
||||
if (i != 0) {
|
||||
painter.drawLine(lastPt, thisPt);
|
||||
}
|
||||
lastPt = thisPt;
|
||||
}
|
||||
for (; i < 128; ++i) {
|
||||
const QPointF thisPt(i * xIncrement + 30.0, height() - 16.0);
|
||||
if (i != 0) {
|
||||
painter.drawLine(lastPt, thisPt);
|
||||
}
|
||||
lastPt = thisPt;
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::mousePressEvent(QMouseEvent* ev) { mouseMoveEvent(ev); }
|
||||
|
||||
void CurveView::mouseMoveEvent(QMouseEvent* ev) {
|
||||
CurveView* view = getEditor()->m_curveView;
|
||||
amuse::ITable& table = **view->m_node->m_obj;
|
||||
if (table.Isa() != amuse::ITable::Type::Curve) {
|
||||
return;
|
||||
}
|
||||
|
||||
const qreal xIncrement = (width() - 30.0) / 127.0;
|
||||
const int idx = int(std::round((ev->position().x() - 30.0) / xIncrement));
|
||||
if (idx < 0 || idx > 127) {
|
||||
return;
|
||||
}
|
||||
const int val = 127 - std::clamp(0, int(std::round(ev->position().y() / (height() - 16.0) * 127.0)), 127);
|
||||
|
||||
CurveEditUndoCommand::RedoData newData;
|
||||
auto& curve = static_cast<amuse::Curve&>(table);
|
||||
curve.data.resize(newData.size());
|
||||
|
||||
std::copy(curve.data.cbegin(), curve.data.cend(), newData.begin());
|
||||
newData[idx] = uint8_t(val);
|
||||
|
||||
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, false, m_node));
|
||||
update();
|
||||
}
|
||||
|
||||
CurveView::CurveView(QWidget* parent) : QWidget(parent) {
|
||||
for (size_t i = 0; i < m_percentTexts.size(); ++i) {
|
||||
m_percentTexts[i].setText(QStringLiteral("%1%").arg(100 - i * 10));
|
||||
m_percentTexts[i].setTextOption(QTextOption(Qt::AlignVCenter | Qt::AlignRight));
|
||||
m_percentTexts[i].setTextWidth(28.0);
|
||||
m_percentTextsCenter[i].setText(QStringLiteral("%1%").arg(i * 10));
|
||||
m_percentTextsCenter[i].setTextOption(QTextOption(Qt::AlignBaseline | Qt::AlignCenter));
|
||||
m_percentTextsCenter[i].setTextWidth(28.0);
|
||||
}
|
||||
m_gridFont.setPointSize(8);
|
||||
}
|
||||
|
||||
CurveView::~CurveView() = default;
|
||||
|
||||
CurveEditor* CurveControls::getEditor() const { return qobject_cast<CurveEditor*>(parentWidget()); }
|
||||
|
||||
void CurveControls::loadData() {
|
||||
m_lineEdit->setDisabled(false);
|
||||
m_setExpr->setDisabled(false);
|
||||
}
|
||||
|
||||
void CurveControls::unloadData() {
|
||||
m_lineEdit->setDisabled(true);
|
||||
m_setExpr->setDisabled(true);
|
||||
}
|
||||
|
||||
void CurveControls::exprCommit() {
|
||||
CurveView* view = getEditor()->m_curveView;
|
||||
amuse::ITable& table = **view->m_node->m_obj;
|
||||
if (table.Isa() != amuse::ITable::Type::Curve) {
|
||||
return;
|
||||
}
|
||||
const QString progText = m_lineEdit->text();
|
||||
|
||||
CurveEditUndoCommand::RedoData newData;
|
||||
auto& curve = static_cast<amuse::Curve&>(table);
|
||||
curve.data.resize(newData.size());
|
||||
std::copy(curve.data.cbegin(), curve.data.cend(), newData.begin());
|
||||
|
||||
bool notANumber = false;
|
||||
for (size_t i = 0; i < newData.size(); ++i) {
|
||||
m_engine.globalObject().setProperty(QStringLiteral("x"), i / 127.0);
|
||||
QJSValue val = m_engine.evaluate(progText);
|
||||
if (val.isError()) {
|
||||
m_errLabel->setText(val.toString());
|
||||
return;
|
||||
} else if (val.isNumber()) {
|
||||
newData[i] = uint8_t(std::clamp(0, int(std::round(val.toNumber() * 127.0)), 127));
|
||||
} else {
|
||||
notANumber = true;
|
||||
newData[i] = 0;
|
||||
}
|
||||
}
|
||||
if (notANumber)
|
||||
m_errLabel->setText(tr("Did not evaluate as a number"));
|
||||
|
||||
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, true, view->m_node));
|
||||
view->update();
|
||||
}
|
||||
|
||||
void CurveControls::resizeEvent(QResizeEvent* ev) { m_errLabel->setGeometry(22, 78, width() - 44, 14); }
|
||||
|
||||
CurveControls::CurveControls(QWidget* parent) : QFrame(parent) {
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
setFixedHeight(100);
|
||||
setFrameShape(QFrame::StyledPanel);
|
||||
setFrameShadow(QFrame::Sunken);
|
||||
setBackgroundRole(QPalette::Base);
|
||||
setAutoFillBackground(true);
|
||||
|
||||
QHBoxLayout* mainLayout = new QHBoxLayout;
|
||||
|
||||
QGridLayout* leftLayout = new QGridLayout;
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Expression")), 0, 0);
|
||||
m_lineEdit = new QLineEdit;
|
||||
m_lineEdit->setDisabled(true);
|
||||
QPalette palette = m_lineEdit->palette();
|
||||
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
|
||||
m_lineEdit->setPalette(palette);
|
||||
connect(m_lineEdit, &QLineEdit::returnPressed, this, &CurveControls::exprCommit);
|
||||
leftLayout->addWidget(m_lineEdit, 1, 0);
|
||||
|
||||
m_setExpr = new QPushButton(tr("Apply"));
|
||||
m_setExpr->setDisabled(true);
|
||||
connect(m_setExpr, &QPushButton::clicked, this, &CurveControls::exprCommit);
|
||||
leftLayout->addWidget(m_setExpr, 1, 1);
|
||||
|
||||
m_errLabel = new QLabel(this);
|
||||
QFont font = m_errLabel->font();
|
||||
font.setPointSize(8);
|
||||
m_errLabel->setFont(font);
|
||||
palette.setColor(QPalette::Text, Qt::red);
|
||||
m_errLabel->setPalette(palette);
|
||||
|
||||
leftLayout->setColumnMinimumWidth(0, 500);
|
||||
leftLayout->setColumnStretch(0, 1);
|
||||
leftLayout->setColumnMinimumWidth(1, 75);
|
||||
leftLayout->setColumnStretch(1, 0);
|
||||
leftLayout->setRowMinimumHeight(0, 22);
|
||||
leftLayout->setRowMinimumHeight(1, 37);
|
||||
leftLayout->setContentsMargins(10, 6, 10, 14);
|
||||
|
||||
mainLayout->addLayout(leftLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
QJSValueIterator it(m_engine.globalObject().property(QStringLiteral("Math")));
|
||||
QString docStr =
|
||||
tr("Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available:\n");
|
||||
bool needsComma = false;
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
m_engine.globalObject().setProperty(it.name(), it.value());
|
||||
if (needsComma)
|
||||
docStr += QStringLiteral(", ");
|
||||
needsComma = true;
|
||||
docStr += it.name();
|
||||
}
|
||||
m_lineEdit->setToolTip(docStr);
|
||||
}
|
||||
|
||||
CurveControls::~CurveControls() = default;
|
||||
|
||||
bool CurveEditor::loadData(ProjectModel::CurveNode* node) {
|
||||
m_curveView->loadData(node);
|
||||
m_controls->loadData();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CurveEditor::unloadData() {
|
||||
m_curveView->unloadData();
|
||||
m_controls->unloadData();
|
||||
}
|
||||
|
||||
ProjectModel::INode* CurveEditor::currentNode() const { return m_curveView->currentNode(); }
|
||||
|
||||
CurveEditor::CurveEditor(QWidget* parent)
|
||||
: EditorWidget(parent), m_curveView(new CurveView), m_controls(new CurveControls) {
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(QMargins());
|
||||
layout->setSpacing(1);
|
||||
layout->addWidget(m_curveView);
|
||||
layout->addWidget(m_controls);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
CurveEditor::~CurveEditor() = default;
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QFrame>
|
||||
#include <QJSEngine>
|
||||
#include <QStaticText>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class CurveEditor;
|
||||
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
|
||||
class CurveView : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class CurveControls;
|
||||
amuse::ObjToken<ProjectModel::CurveNode> m_node;
|
||||
QFont m_gridFont;
|
||||
std::array<QStaticText, 11> m_percentTexts;
|
||||
std::array<QStaticText, 11> m_percentTextsCenter;
|
||||
CurveEditor* getEditor() const;
|
||||
|
||||
public:
|
||||
explicit CurveView(QWidget* parent = Q_NULLPTR);
|
||||
~CurveView() override;
|
||||
|
||||
void loadData(ProjectModel::CurveNode* node);
|
||||
void unloadData();
|
||||
ProjectModel::INode* currentNode() const;
|
||||
|
||||
void paintEvent(QPaintEvent* ev) override;
|
||||
void mousePressEvent(QMouseEvent* ev) override;
|
||||
void mouseMoveEvent(QMouseEvent* ev) override;
|
||||
};
|
||||
|
||||
class CurveControls : public QFrame {
|
||||
Q_OBJECT
|
||||
friend class CurveView;
|
||||
QLineEdit* m_lineEdit;
|
||||
QLabel* m_errLabel;
|
||||
QPushButton* m_setExpr;
|
||||
QJSEngine m_engine;
|
||||
CurveEditor* getEditor() const;
|
||||
|
||||
public:
|
||||
explicit CurveControls(QWidget* parent = Q_NULLPTR);
|
||||
~CurveControls() override;
|
||||
|
||||
void loadData();
|
||||
void unloadData();
|
||||
void resizeEvent(QResizeEvent* ev) override;
|
||||
|
||||
public slots:
|
||||
void exprCommit();
|
||||
};
|
||||
|
||||
class CurveEditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
friend class CurveView;
|
||||
friend class CurveControls;
|
||||
CurveView* m_curveView;
|
||||
CurveControls* m_controls;
|
||||
|
||||
public:
|
||||
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
|
||||
~CurveEditor() override;
|
||||
|
||||
bool loadData(ProjectModel::CurveNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
};
|
|
@ -0,0 +1,283 @@
|
|||
#include "EditorWidget.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
#include <QStandardItemModel>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) {}
|
||||
|
||||
EditorWidget::~EditorWidget() = default;
|
||||
|
||||
void EditorUndoCommand::undo() { g_MainWindow->openEditor(m_node.get()); }
|
||||
|
||||
void EditorUndoCommand::redo() { g_MainWindow->openEditor(m_node.get()); }
|
||||
|
||||
FieldSlider::FieldSlider(QWidget* parent) : QWidget(parent), m_slider(Qt::Horizontal) {
|
||||
setFixedHeight(22);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
layout->addWidget(&m_slider);
|
||||
layout->addWidget(&m_value);
|
||||
layout->setContentsMargins(QMargins());
|
||||
setLayout(layout);
|
||||
m_value.setFixedWidth(42);
|
||||
connect(&m_slider, qOverload<int>(&QSlider::valueChanged), this, &FieldSlider::doValueChanged);
|
||||
m_slider.setValue(0);
|
||||
}
|
||||
|
||||
void FieldSlider::doValueChanged(int value) {
|
||||
m_value.setText(QString::number(value));
|
||||
emit valueChanged(value);
|
||||
}
|
||||
|
||||
FieldDoubleSlider::FieldDoubleSlider(QWidget* parent) : QWidget(parent), m_slider(Qt::Horizontal) {
|
||||
setFixedHeight(22);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
layout->addWidget(&m_slider);
|
||||
layout->addWidget(&m_value);
|
||||
layout->setContentsMargins(QMargins());
|
||||
setLayout(layout);
|
||||
m_value.setFixedWidth(42);
|
||||
connect(&m_slider, qOverload<int>(&QSlider::valueChanged), this, &FieldDoubleSlider::doValueChanged);
|
||||
m_slider.setRange(0, 1000);
|
||||
m_slider.setValue(0);
|
||||
}
|
||||
|
||||
double FieldDoubleSlider::value() const {
|
||||
double t = m_slider.value() / 1000.0;
|
||||
return t * (m_max - m_min) + m_min;
|
||||
}
|
||||
void FieldDoubleSlider::setValue(double value) {
|
||||
if (value < m_min)
|
||||
value = m_min;
|
||||
else if (value > m_max)
|
||||
value = m_max;
|
||||
|
||||
double t = (value - m_min) / (m_max - m_min);
|
||||
m_slider.setValue(int(t * 1000.0));
|
||||
doValueChanged(int(t * 1000.0));
|
||||
}
|
||||
void FieldDoubleSlider::setRange(double min, double max) {
|
||||
m_min = min;
|
||||
m_max = max;
|
||||
double curValue = value();
|
||||
if (curValue < min)
|
||||
setValue(min);
|
||||
else if (curValue > max)
|
||||
setValue(max);
|
||||
}
|
||||
|
||||
void FieldDoubleSlider::doValueChanged(int value) {
|
||||
double t = value / 1000.0;
|
||||
t = t * (m_max - m_min) + m_min;
|
||||
m_value.setText(QString::number(t, 'g', 2));
|
||||
emit valueChanged(t);
|
||||
}
|
||||
|
||||
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
|
||||
: QWidget(parent), m_comboBox(this), m_button(this) {
|
||||
m_button.setDisabled(true);
|
||||
m_button.setFixedSize(30, 30);
|
||||
m_comboBox.setFixedHeight(30);
|
||||
connect(&m_comboBox, qOverload<int>(&FieldComboBox::currentIndexChanged), this,
|
||||
&FieldProjectNode::_currentIndexChanged);
|
||||
connect(&m_button, &QPushButton::clicked, this, &FieldProjectNode::openCurrent);
|
||||
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
|
||||
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
|
||||
m_button.setIcon(icon);
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
layout->setContentsMargins({});
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(&m_comboBox);
|
||||
layout->addWidget(&m_button);
|
||||
setLayout(layout);
|
||||
setCollection(collection);
|
||||
}
|
||||
|
||||
FieldProjectNode::~FieldProjectNode() = default;
|
||||
|
||||
void FieldProjectNode::setCollection(ProjectModel::CollectionNode* collection) {
|
||||
m_collection = collection;
|
||||
|
||||
if (!collection) {
|
||||
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
|
||||
m_button.setDisabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectModel* model = g_MainWindow->projectModel();
|
||||
m_comboBox.setModel(model->getNullProxy());
|
||||
m_comboBox.setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
|
||||
}
|
||||
|
||||
void FieldProjectNode::_currentIndexChanged(int index) {
|
||||
m_button.setEnabled(index != 0);
|
||||
emit currentIndexChanged(index);
|
||||
}
|
||||
|
||||
ProjectModel::BasePoolObjectNode* FieldProjectNode::currentNode() const {
|
||||
int index = m_comboBox.currentIndex();
|
||||
if (index == 0)
|
||||
return nullptr;
|
||||
else
|
||||
return m_collection->nodeOfIndex(index - 1);
|
||||
}
|
||||
|
||||
bool FieldProjectNode::event(QEvent* ev) {
|
||||
if (ev->type() == QEvent::User) {
|
||||
showPopup();
|
||||
return true;
|
||||
}
|
||||
return QWidget::event(ev);
|
||||
}
|
||||
|
||||
void FieldProjectNode::openCurrent() {
|
||||
if (ProjectModel::BasePoolObjectNode* node = currentNode())
|
||||
if (!g_MainWindow->isUiDisabled())
|
||||
g_MainWindow->openEditor(node);
|
||||
}
|
||||
|
||||
FieldPageObjectNode::FieldPageObjectNode(ProjectModel::GroupNode* group, QWidget* parent)
|
||||
: QWidget(parent), m_comboBox(this), m_button(this) {
|
||||
m_button.setDisabled(true);
|
||||
m_button.setFixedSize(30, 30);
|
||||
m_comboBox.setFixedHeight(30);
|
||||
connect(&m_comboBox, qOverload<int>(&FieldComboBox::currentIndexChanged), this,
|
||||
&FieldPageObjectNode::_currentIndexChanged);
|
||||
connect(&m_button, &QPushButton::clicked, this, &FieldPageObjectNode::openCurrent);
|
||||
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
|
||||
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
|
||||
m_button.setIcon(icon);
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
layout->setContentsMargins({});
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(&m_comboBox);
|
||||
layout->addWidget(&m_button);
|
||||
setLayout(layout);
|
||||
setGroup(group);
|
||||
}
|
||||
|
||||
FieldPageObjectNode::~FieldPageObjectNode() = default;
|
||||
|
||||
void FieldPageObjectNode::setGroup(ProjectModel::GroupNode* group) {
|
||||
m_group = group;
|
||||
|
||||
if (!group) {
|
||||
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
|
||||
m_button.setDisabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectModel* model = g_MainWindow->projectModel();
|
||||
m_comboBox.setModel(model->getPageObjectProxy());
|
||||
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
|
||||
}
|
||||
|
||||
void FieldPageObjectNode::_currentIndexChanged(int index) {
|
||||
m_button.setEnabled(index != 0);
|
||||
emit currentIndexChanged(index);
|
||||
}
|
||||
|
||||
ProjectModel::BasePoolObjectNode* FieldPageObjectNode::currentNode() const {
|
||||
int index = m_comboBox.currentIndex();
|
||||
if (index == 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
ProjectModel* model = g_MainWindow->projectModel();
|
||||
return static_cast<ProjectModel::BasePoolObjectNode*>(model->node(model->getPageObjectProxy()->mapToSource(
|
||||
model->getPageObjectProxy()->index(index, 0, m_comboBox.rootModelIndex()))));
|
||||
}
|
||||
}
|
||||
|
||||
bool FieldPageObjectNode::event(QEvent* ev) {
|
||||
if (ev->type() == QEvent::User) {
|
||||
showPopup();
|
||||
return true;
|
||||
}
|
||||
return QWidget::event(ev);
|
||||
}
|
||||
|
||||
void FieldPageObjectNode::openCurrent() {
|
||||
if (ProjectModel::BasePoolObjectNode* node = currentNode())
|
||||
if (!g_MainWindow->isUiDisabled())
|
||||
g_MainWindow->openEditor(node);
|
||||
}
|
||||
|
||||
AddRemoveButtons::AddRemoveButtons(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_addAction(tr("Add Row"))
|
||||
, m_addButton(this)
|
||||
, m_removeAction(tr("Remove Row"))
|
||||
, m_removeButton(this) {
|
||||
setFixedSize(64, 32);
|
||||
|
||||
m_addAction.setIcon(QIcon(QStringLiteral(":/icons/IconAdd.svg")));
|
||||
m_addButton.setDefaultAction(&m_addAction);
|
||||
m_addButton.setFixedSize(32, 32);
|
||||
m_addButton.move(0, 0);
|
||||
|
||||
m_removeAction.setIcon(QIcon(QStringLiteral(":/icons/IconRemove.svg")));
|
||||
m_removeButton.setDefaultAction(&m_removeAction);
|
||||
m_removeButton.setFixedSize(32, 32);
|
||||
m_removeButton.move(32, 0);
|
||||
m_removeAction.setEnabled(false);
|
||||
}
|
||||
|
||||
static QIcon ListingDeleteIcon;
|
||||
static QIcon ListingDeleteHoveredIcon;
|
||||
|
||||
void ListingDeleteButton::enterEvent(QEnterEvent* event) { setIcon(ListingDeleteHoveredIcon); }
|
||||
|
||||
void ListingDeleteButton::leaveEvent(QEvent* event) { setIcon(ListingDeleteIcon); }
|
||||
|
||||
ListingDeleteButton::ListingDeleteButton(QWidget* parent) : QPushButton(parent) {
|
||||
if (ListingDeleteIcon.isNull())
|
||||
ListingDeleteIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg"));
|
||||
if (ListingDeleteHoveredIcon.isNull())
|
||||
ListingDeleteHoveredIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDeleteHovered.svg"));
|
||||
|
||||
setVisible(false);
|
||||
setFixedSize(21, 21);
|
||||
setFlat(true);
|
||||
setToolTip(tr("Delete this SoundMacro"));
|
||||
setIcon(ListingDeleteIcon);
|
||||
}
|
||||
|
||||
bool BaseObjectDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) {
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
|
||||
if (ev->button() == Qt::RightButton) {
|
||||
ProjectModel::INode* node = getNode(model, index);
|
||||
|
||||
ContextMenu* menu = new ContextMenu;
|
||||
|
||||
QAction* openEditorAction = new QAction(tr("Open in Editor"), menu);
|
||||
openEditorAction->setData(QVariant::fromValue((void*)node));
|
||||
openEditorAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
|
||||
connect(openEditorAction, &QAction::triggered, this, &BaseObjectDelegate::doOpenEditor);
|
||||
menu->addAction(openEditorAction);
|
||||
|
||||
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
|
||||
findUsagesAction->setData(QVariant::fromValue((void*)node));
|
||||
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
|
||||
connect(findUsagesAction, &QAction::triggered, this, &BaseObjectDelegate::doFindUsages);
|
||||
menu->addAction(findUsagesAction);
|
||||
|
||||
menu->popup(ev->globalPosition().toPoint());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseObjectDelegate::doOpenEditor() {
|
||||
QAction* act = static_cast<QAction*>(sender());
|
||||
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
|
||||
g_MainWindow->openEditor(node);
|
||||
}
|
||||
|
||||
void BaseObjectDelegate::doFindUsages() {
|
||||
QAction* act = static_cast<QAction*>(sender());
|
||||
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
|
||||
g_MainWindow->findUsages(node);
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
#pragma once
|
||||
|
||||
#include <QAction>
|
||||
#include <QComboBox>
|
||||
#include <QItemEditorFactory>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QToolButton>
|
||||
#include <QUndoCommand>
|
||||
#include <QWheelEvent>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class EditorWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
|
||||
~EditorWidget() override;
|
||||
|
||||
virtual bool valid() const { return true; }
|
||||
virtual void unloadData() {}
|
||||
virtual ProjectModel::INode* currentNode() const { return nullptr; }
|
||||
virtual void setEditorEnabled(bool en) { setEnabled(en); }
|
||||
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
|
||||
|
||||
public slots:
|
||||
virtual void itemCutAction() {}
|
||||
virtual void itemCopyAction() {}
|
||||
virtual void itemPasteAction() {}
|
||||
virtual void itemDeleteAction() {}
|
||||
};
|
||||
|
||||
class EditorUndoCommand : public QUndoCommand {
|
||||
protected:
|
||||
amuse::ObjToken<ProjectModel::INode> m_node;
|
||||
enum class Id {
|
||||
SMChangeVal,
|
||||
SampLoop,
|
||||
SampPitch,
|
||||
ADSRAttack,
|
||||
ADSRDecay,
|
||||
ADSRSustain,
|
||||
ADSRAttackAndDecay,
|
||||
ADSRDecayAndSustain,
|
||||
ADSRRelease,
|
||||
ADSRDLS,
|
||||
ADSRVelToAttack,
|
||||
ADSRKeyToDecay,
|
||||
CurveEdit
|
||||
};
|
||||
|
||||
public:
|
||||
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node, const QString& text, QUndoCommand* parent = nullptr)
|
||||
: QUndoCommand(text, parent), m_node(node) {}
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
};
|
||||
|
||||
class FieldSpinBox : public QSpinBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR) : QSpinBox(parent) {}
|
||||
|
||||
/* Don't scroll */
|
||||
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
|
||||
};
|
||||
|
||||
class FieldSlider : public QWidget {
|
||||
Q_OBJECT
|
||||
QSlider m_slider;
|
||||
QLabel m_value;
|
||||
|
||||
public:
|
||||
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
|
||||
|
||||
/* Don't scroll */
|
||||
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
|
||||
|
||||
int value() const { return m_slider.value(); }
|
||||
void setValue(int value) {
|
||||
m_slider.setValue(value);
|
||||
doValueChanged(value);
|
||||
}
|
||||
void setRange(int min, int max) { m_slider.setRange(min, max); }
|
||||
|
||||
private slots:
|
||||
void doValueChanged(int value);
|
||||
signals:
|
||||
void valueChanged(int value);
|
||||
};
|
||||
|
||||
class FieldDoubleSlider : public QWidget {
|
||||
Q_OBJECT
|
||||
QSlider m_slider;
|
||||
QLabel m_value;
|
||||
double m_min = 0.0;
|
||||
double m_max = 1.0;
|
||||
|
||||
public:
|
||||
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
|
||||
|
||||
/* Don't scroll */
|
||||
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
|
||||
|
||||
double value() const;
|
||||
void setValue(double value);
|
||||
void setRange(double min, double max);
|
||||
|
||||
private slots:
|
||||
void doValueChanged(int value);
|
||||
signals:
|
||||
void valueChanged(double value);
|
||||
};
|
||||
|
||||
class FieldComboBox : public QComboBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FieldComboBox(QWidget* parent = Q_NULLPTR) : QComboBox(parent) {}
|
||||
|
||||
/* Don't scroll */
|
||||
void wheelEvent(QWheelEvent* event) override { event->ignore(); }
|
||||
};
|
||||
|
||||
class FieldProjectNode : public QWidget {
|
||||
Q_OBJECT
|
||||
ProjectModel::CollectionNode* m_collection;
|
||||
FieldComboBox m_comboBox;
|
||||
QPushButton m_button;
|
||||
|
||||
public:
|
||||
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
||||
~FieldProjectNode() override;
|
||||
|
||||
void setCollection(ProjectModel::CollectionNode* collection);
|
||||
ProjectModel::CollectionNode* collection() const { return m_collection; }
|
||||
int currentIndex() const { return m_comboBox.currentIndex(); }
|
||||
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
|
||||
void showPopup() { m_comboBox.showPopup(); }
|
||||
ProjectModel::BasePoolObjectNode* currentNode() const;
|
||||
bool event(QEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void _currentIndexChanged(int);
|
||||
|
||||
public slots:
|
||||
void openCurrent();
|
||||
|
||||
signals:
|
||||
void currentIndexChanged(int);
|
||||
};
|
||||
|
||||
class FieldPageObjectNode : public QWidget {
|
||||
Q_OBJECT
|
||||
ProjectModel::GroupNode* m_group;
|
||||
FieldComboBox m_comboBox;
|
||||
QPushButton m_button;
|
||||
|
||||
public:
|
||||
explicit FieldPageObjectNode(ProjectModel::GroupNode* group = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
||||
~FieldPageObjectNode() override;
|
||||
|
||||
void setGroup(ProjectModel::GroupNode* group);
|
||||
ProjectModel::GroupNode* group() const { return m_group; }
|
||||
int currentIndex() const { return m_comboBox.currentIndex(); }
|
||||
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
|
||||
QModelIndex rootModelIndex() const { return m_comboBox.rootModelIndex(); }
|
||||
void showPopup() { m_comboBox.showPopup(); }
|
||||
ProjectModel::BasePoolObjectNode* currentNode() const;
|
||||
bool event(QEvent* ev) override;
|
||||
|
||||
private slots:
|
||||
void _currentIndexChanged(int);
|
||||
|
||||
public slots:
|
||||
void openCurrent();
|
||||
|
||||
signals:
|
||||
void currentIndexChanged(int);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class EditorFieldNode : public T {
|
||||
bool m_deferPopupOpen = true;
|
||||
|
||||
public:
|
||||
using T::T;
|
||||
bool shouldPopupOpen() {
|
||||
bool ret = m_deferPopupOpen;
|
||||
m_deferPopupOpen = false;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
using EditorFieldProjectNode = EditorFieldNode<FieldProjectNode>;
|
||||
using EditorFieldPageObjectNode = EditorFieldNode<FieldPageObjectNode>;
|
||||
|
||||
template <int MIN, int MAX>
|
||||
class RangedValueFactory : public QItemEditorFactory {
|
||||
public:
|
||||
QWidget* createEditor(int userType, QWidget* parent) const override {
|
||||
QSpinBox* sb = new QSpinBox(parent);
|
||||
sb->setFrame(false);
|
||||
sb->setMinimum(MIN);
|
||||
sb->setMaximum(MAX);
|
||||
return sb;
|
||||
}
|
||||
};
|
||||
|
||||
class AddRemoveButtons : public QWidget {
|
||||
Q_OBJECT
|
||||
QAction m_addAction;
|
||||
QToolButton m_addButton;
|
||||
QAction m_removeAction;
|
||||
QToolButton m_removeButton;
|
||||
|
||||
public:
|
||||
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
|
||||
QAction* addAction() { return &m_addAction; }
|
||||
QAction* removeAction() { return &m_removeAction; }
|
||||
};
|
||||
|
||||
class ListingDeleteButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
|
||||
void enterEvent(QEnterEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
};
|
||||
|
||||
class ContextMenu : public QMenu {
|
||||
public:
|
||||
void hideEvent(QHideEvent* ev) override {
|
||||
QMenu::hideEvent(ev);
|
||||
deleteLater();
|
||||
}
|
||||
};
|
||||
|
||||
class BaseObjectDelegate : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
virtual ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const = 0;
|
||||
|
||||
public:
|
||||
explicit BaseObjectDelegate(QObject* parent = Q_NULLPTR) : QStyledItemDelegate(parent) {}
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) override;
|
||||
private slots:
|
||||
void doOpenEditor();
|
||||
void doFindUsages();
|
||||
};
|
|
@ -0,0 +1,223 @@
|
|||
#include "KeyboardWidget.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSvgRenderer>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "StatusBarWidget.hpp"
|
||||
|
||||
const std::array<QString, 7> NaturalKeyNames{
|
||||
QStringLiteral("C"), QStringLiteral("D"), QStringLiteral("E"), QStringLiteral("F"),
|
||||
QStringLiteral("G"), QStringLiteral("A"), QStringLiteral("B"),
|
||||
};
|
||||
|
||||
const std::array<QString, 5> SharpKeyNames{
|
||||
QStringLiteral("Cs"), QStringLiteral("Ds"), QStringLiteral("Fs"), QStringLiteral("Gs"), QStringLiteral("As"),
|
||||
};
|
||||
|
||||
const std::array<QString, 12> KeyStrings{
|
||||
QStringLiteral("C"), QStringLiteral("C#"), QStringLiteral("D"), QStringLiteral("D#"),
|
||||
QStringLiteral("E"), QStringLiteral("F"), QStringLiteral("F#"), QStringLiteral("G"),
|
||||
QStringLiteral("G#"), QStringLiteral("A"), QStringLiteral("A#"), QStringLiteral("B"),
|
||||
};
|
||||
|
||||
const std::array<int, 7> NaturalKeyNumbers{
|
||||
0, 2, 4, 5, 7, 9, 11,
|
||||
};
|
||||
|
||||
const std::array<int, 5> SharpKeyNumbers{
|
||||
1, 3, 6, 8, 10,
|
||||
};
|
||||
|
||||
KeyboardOctave::KeyboardOctave(int octave, const QString& svgPath, QWidget* parent)
|
||||
: QSvgWidget(svgPath, parent), m_octave(octave) {
|
||||
for (size_t i = 0; i < NaturalKeyNames.size(); ++i) {
|
||||
const auto& naturalKeyName = NaturalKeyNames[i];
|
||||
|
||||
if (renderer()->elementExists(naturalKeyName)) {
|
||||
m_natural[i] = renderer()->transformForElement(naturalKeyName).mapRect(renderer()->boundsOnElement(naturalKeyName));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < SharpKeyNames.size(); ++i) {
|
||||
const auto& sharpKeyName = SharpKeyNames[i];
|
||||
|
||||
if (renderer()->elementExists(sharpKeyName)) {
|
||||
m_sharp[i] = renderer()->transformForElement(sharpKeyName).mapRect(renderer()->boundsOnElement(sharpKeyName));
|
||||
}
|
||||
}
|
||||
|
||||
/* The parent keyboard manages all mouse events */
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
|
||||
int KeyboardOctave::getKey(const QPoint& localPos) const {
|
||||
const QPointF localPoint = m_widgetToSvg.map(localPos);
|
||||
|
||||
for (size_t i = 0; i < m_sharp.size(); ++i) {
|
||||
if (m_sharp[i].contains(localPoint)) {
|
||||
return SharpKeyNumbers[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_natural.size(); ++i) {
|
||||
if (m_natural[i].contains(localPoint)) {
|
||||
return NaturalKeyNumbers[i];
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void KeyboardOctave::resizeEvent(QResizeEvent* event) { m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF()); }
|
||||
|
||||
KeyboardWidget::KeyboardWidget(QWidget* parent) : QWidget(parent) {
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(QMargins());
|
||||
layout->setSpacing(0);
|
||||
|
||||
for (size_t i = 0; i < m_widgets.size() - 1; ++i) {
|
||||
m_widgets[i] = new KeyboardOctave(int(i), QStringLiteral(":/bg/keyboard.svg"), this);
|
||||
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
|
||||
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
||||
layout->addWidget(m_widgets[i]);
|
||||
}
|
||||
|
||||
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
|
||||
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
|
||||
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
||||
layout->addWidget(m_widgets[10]);
|
||||
|
||||
setLayout(layout);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
KeyboardWidget::~KeyboardWidget() = default;
|
||||
|
||||
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const {
|
||||
for (KeyboardOctave* oct : m_widgets) {
|
||||
QPoint localPos = oct->mapFromParent(event->pos());
|
||||
if (oct->rect().contains(localPos))
|
||||
return {oct->getOctave(), oct->getKey(localPos)};
|
||||
}
|
||||
return {-1, -1};
|
||||
}
|
||||
|
||||
void KeyboardWidget::_startKey(int octave, int key) { emit notePressed(octave * 12 + key); }
|
||||
|
||||
void KeyboardWidget::_stopKey() { emit noteReleased(); }
|
||||
|
||||
void KeyboardWidget::_moveOnKey(int octave, int key) {
|
||||
if (m_lastOctave == octave && m_lastKey == key) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastOctave = octave;
|
||||
m_lastKey = key;
|
||||
|
||||
if (m_statusFocus) {
|
||||
m_statusFocus->setMessage(QStringLiteral("%1%2 (%3)").arg(KeyStrings[key]).arg(octave - 1).arg(octave * 12 + key));
|
||||
}
|
||||
|
||||
if (m_holding) {
|
||||
_startKey(octave, key);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardWidget::_pressOnKey(int octave, int key) {
|
||||
_moveOnKey(octave, key);
|
||||
m_holding = true;
|
||||
_startKey(octave, key);
|
||||
}
|
||||
|
||||
void KeyboardWidget::mouseMoveEvent(QMouseEvent* event) {
|
||||
std::pair<int, int> ok = _getOctaveAndKey(event);
|
||||
if (ok.first != -1 && ok.second != -1)
|
||||
_moveOnKey(ok.first, ok.second);
|
||||
}
|
||||
|
||||
void KeyboardWidget::mousePressEvent(QMouseEvent* event) {
|
||||
std::pair<int, int> ok = _getOctaveAndKey(event);
|
||||
if (ok.first != -1 && ok.second != -1)
|
||||
_pressOnKey(ok.first, ok.second);
|
||||
}
|
||||
|
||||
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event) {
|
||||
_stopKey();
|
||||
m_holding = false;
|
||||
}
|
||||
|
||||
void KeyboardWidget::enterEvent(QEnterEvent* event) {
|
||||
if (m_statusFocus)
|
||||
m_statusFocus->enter();
|
||||
}
|
||||
|
||||
void KeyboardWidget::leaveEvent(QEvent* event) {
|
||||
if (m_statusFocus)
|
||||
m_statusFocus->exit();
|
||||
}
|
||||
|
||||
void KeyboardWidget::wheelEvent(QWheelEvent* event) {
|
||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||
/* Send wheel event directly to the scroll bar */
|
||||
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardWidget::showEvent(QShowEvent* event) {
|
||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||
/* Scroll to C1 */
|
||||
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardSlider::KeyboardSlider(QWidget* parent) : QSlider(parent) {}
|
||||
|
||||
KeyboardSlider::~KeyboardSlider() = default;
|
||||
|
||||
void KeyboardSlider::enterEvent(QEnterEvent* event) {
|
||||
if (m_statusFocus)
|
||||
m_statusFocus->enter();
|
||||
}
|
||||
|
||||
void KeyboardSlider::leaveEvent(QEvent* event) {
|
||||
if (m_statusFocus)
|
||||
m_statusFocus->exit();
|
||||
}
|
||||
|
||||
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus) {
|
||||
m_statusFocus = statusFocus;
|
||||
QString str = stringOfValue(value());
|
||||
m_statusFocus->setMessage(str);
|
||||
setToolTip(str);
|
||||
}
|
||||
|
||||
void KeyboardSlider::sliderChange(SliderChange change) {
|
||||
QSlider::sliderChange(change);
|
||||
if (m_statusFocus && change == QAbstractSlider::SliderValueChange) {
|
||||
QString str = stringOfValue(value());
|
||||
m_statusFocus->setMessage(str);
|
||||
setToolTip(str);
|
||||
}
|
||||
}
|
||||
|
||||
VelocitySlider::VelocitySlider(QWidget* parent) : KeyboardSlider(parent) {}
|
||||
|
||||
QString VelocitySlider::stringOfValue(int value) const { return tr("Velocity: %1").arg(value); }
|
||||
|
||||
ModulationSlider::ModulationSlider(QWidget* parent) : KeyboardSlider(parent) {}
|
||||
|
||||
QString ModulationSlider::stringOfValue(int value) const { return tr("Modulation: %1").arg(value); }
|
||||
|
||||
PitchSlider::PitchSlider(QWidget* parent) : KeyboardSlider(parent) {}
|
||||
|
||||
QString PitchSlider::stringOfValue(int value) const { return tr("Pitch: %1").arg(value / 2048.0, 0, 'g', 2); }
|
||||
|
||||
void PitchSlider::mouseReleaseEvent(QMouseEvent* ev) {
|
||||
KeyboardSlider::mouseReleaseEvent(ev);
|
||||
setValue(0);
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QSlider>
|
||||
#include <QString>
|
||||
#include <QSvgWidget>
|
||||
#include <QWheelEvent>
|
||||
#include <QWidget>
|
||||
|
||||
extern const std::array<QString, 7> NaturalKeyNames;
|
||||
extern const std::array<QString, 5> SharpKeyNames;
|
||||
extern const std::array<QString, 12> KeyStrings;
|
||||
extern const std::array<int, 7> NaturalKeyNumbers;
|
||||
extern const std::array<int, 5> SharpKeyNumbers;
|
||||
|
||||
class KeyboardWidget;
|
||||
class StatusBarFocus;
|
||||
|
||||
class KeyboardOctave : public QSvgWidget {
|
||||
Q_OBJECT
|
||||
int m_octave;
|
||||
std::array<QRectF, 7> m_natural;
|
||||
std::array<QRectF, 5> m_sharp;
|
||||
QTransform m_widgetToSvg;
|
||||
|
||||
public:
|
||||
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
|
||||
int getOctave() const { return m_octave; }
|
||||
int getKey(const QPoint& localPos) const;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
};
|
||||
|
||||
class KeyboardWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
std::array<KeyboardOctave*, 11> m_widgets{};
|
||||
StatusBarFocus* m_statusFocus = nullptr;
|
||||
int m_lastOctave = -1;
|
||||
int m_lastKey = -1;
|
||||
bool m_holding = false;
|
||||
|
||||
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
|
||||
void _startKey(int octave, int key);
|
||||
void _stopKey();
|
||||
void _moveOnKey(int octave, int key);
|
||||
void _pressOnKey(int octave, int key);
|
||||
|
||||
public:
|
||||
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
|
||||
~KeyboardWidget() override;
|
||||
|
||||
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void enterEvent(QEnterEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
signals:
|
||||
void notePressed(int key);
|
||||
void noteReleased();
|
||||
};
|
||||
|
||||
class KeyboardSlider : public QSlider {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
StatusBarFocus* m_statusFocus = nullptr;
|
||||
virtual QString stringOfValue(int value) const = 0;
|
||||
|
||||
public:
|
||||
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
|
||||
~KeyboardSlider() override;
|
||||
|
||||
void enterEvent(QEnterEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
void setStatusFocus(StatusBarFocus* statusFocus);
|
||||
void sliderChange(SliderChange change) override;
|
||||
};
|
||||
|
||||
class VelocitySlider : public KeyboardSlider {
|
||||
Q_OBJECT
|
||||
QString stringOfValue(int value) const override;
|
||||
|
||||
public:
|
||||
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
|
||||
};
|
||||
|
||||
class ModulationSlider : public KeyboardSlider {
|
||||
Q_OBJECT
|
||||
QString stringOfValue(int value) const override;
|
||||
|
||||
public:
|
||||
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
|
||||
};
|
||||
|
||||
class PitchSlider : public KeyboardSlider {
|
||||
Q_OBJECT
|
||||
QString stringOfValue(int value) const override;
|
||||
|
||||
public:
|
||||
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
|
||||
void mouseReleaseEvent(QMouseEvent* ev) override;
|
||||
void wheelEvent(QWheelEvent* ev) override { ev->ignore(); }
|
||||
};
|
|
@ -0,0 +1,530 @@
|
|||
#include "KeymapEditor.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QPainter>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "KeyboardWidget.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
constexpr std::array<int, 10> HueTable{
|
||||
0, 30, 60, 80, 120, 170, 200, 240, 280, 320,
|
||||
};
|
||||
|
||||
constexpr std::array<int, 13> SaturationTable{
|
||||
255, 255, 255, 255, 255, 127, 127, 127, 127, 127, 63, 63, 63,
|
||||
};
|
||||
|
||||
constexpr std::array<int, 13> ValueTable{
|
||||
240, 200, 160, 120, 80, 240, 200, 160, 120, 80, 240, 200, 160,
|
||||
};
|
||||
|
||||
constexpr std::array<QPoint, 12> PointTable{{
|
||||
{21, 180},
|
||||
{41, 104},
|
||||
{61, 180},
|
||||
{86, 104},
|
||||
{101, 180},
|
||||
{141, 180},
|
||||
{156, 104},
|
||||
{181, 180},
|
||||
{201, 104},
|
||||
{221, 180},
|
||||
{246, 104},
|
||||
{261, 180},
|
||||
}};
|
||||
|
||||
constexpr std::array<int, 12> RadiusTable{
|
||||
14, 10, 14, 10, 14, 14, 10, 14, 10, 14, 10, 14,
|
||||
};
|
||||
|
||||
constexpr std::array<Qt::GlobalColor, 12> PenColorTable{
|
||||
Qt::black, Qt::white, Qt::black, Qt::white, Qt::black, Qt::black,
|
||||
Qt::white, Qt::black, Qt::white, Qt::black, Qt::white, Qt::black,
|
||||
};
|
||||
|
||||
constexpr std::array<Qt::GlobalColor, 12> NeutralColorTable{
|
||||
Qt::white, Qt::black, Qt::white, Qt::black, Qt::white, Qt::white,
|
||||
Qt::black, Qt::white, Qt::black, Qt::white, Qt::black, Qt::white,
|
||||
};
|
||||
|
||||
PaintButton::PaintButton(QWidget* parent) : QPushButton(parent) {
|
||||
setIcon(QIcon(QStringLiteral(":/icons/IconPaintbrush.svg")));
|
||||
setIconSize(QSize(26, 26));
|
||||
setFixedSize(46, 46);
|
||||
setToolTip(tr("Activate brush to apply values to keys"));
|
||||
}
|
||||
|
||||
KeymapEditor* KeymapView::getEditor() const {
|
||||
return qobject_cast<KeymapEditor*>(parentWidget()->parentWidget()->parentWidget());
|
||||
}
|
||||
|
||||
void KeymapView::loadData(ProjectModel::KeymapNode* node) { m_node = node; }
|
||||
|
||||
void KeymapView::unloadData() {
|
||||
m_node.reset();
|
||||
m_keyPalettes.fill(-1);
|
||||
}
|
||||
|
||||
ProjectModel::INode* KeymapView::currentNode() const { return m_node.get(); }
|
||||
|
||||
void KeymapView::drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const PaintPalette& keyPalette,
|
||||
int o, int k) const {
|
||||
const int keyIdx = o * 12 + k;
|
||||
const int keyPalIdx = m_keyPalettes[keyIdx];
|
||||
|
||||
painter.setPen(QPen(PenColorTable[k], penWidth));
|
||||
painter.setBrush(keyPalIdx < 0 ? NeutralColorTable[k] : keyPalette[keyPalIdx]);
|
||||
painter.drawEllipse(PointTable[k] + octaveRect.topLeft(), RadiusTable[k], RadiusTable[k]);
|
||||
painter.setTransform(
|
||||
QTransform()
|
||||
.translate(PointTable[k].x() + octaveRect.left() - 13, PointTable[k].y() + octaveRect.top() - 20)
|
||||
.rotate(-90.0));
|
||||
painter.drawStaticText(QPointF{}, m_keyTexts[keyIdx]);
|
||||
painter.setTransform(QTransform());
|
||||
}
|
||||
|
||||
void KeymapView::paintEvent(QPaintEvent* ev) {
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const auto& keyPalette = getEditor()->m_paintPalette;
|
||||
|
||||
const qreal deviceRatio = devicePixelRatioF();
|
||||
const qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
|
||||
|
||||
painter.setFont(m_keyFont);
|
||||
const int kbY = height() / 2 - 100;
|
||||
for (int o = 0; o < 10; ++o) {
|
||||
const QRect thisRect(o * 280, kbY, 280, 200);
|
||||
if (ev->rect().intersects(thisRect)) {
|
||||
m_octaveRenderer.render(&painter, thisRect);
|
||||
for (int k = 0; k < 12; ++k) {
|
||||
drawKey(painter, thisRect, penWidth, keyPalette, o, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QRect thisRect(2800, kbY, 202, 200);
|
||||
if (ev->rect().intersects(thisRect)) {
|
||||
m_lastOctaveRenderer.render(&painter, thisRect);
|
||||
for (int k = 0; k < 8; ++k) {
|
||||
drawKey(painter, thisRect, penWidth, keyPalette, 10, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeymapView::mousePressEvent(QMouseEvent* ev) { mouseMoveEvent(ev); }
|
||||
|
||||
int KeymapView::getKey(const QPoint& localPos) const {
|
||||
const QPointF localPoint = m_widgetToSvg.map(localPos);
|
||||
for (size_t i = 0; i < m_sharp.size(); ++i) {
|
||||
if (m_sharp[i].contains(localPoint)) {
|
||||
return SharpKeyNumbers[i];
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < m_natural.size(); ++i) {
|
||||
if (m_natural[i].contains(localPoint)) {
|
||||
return NaturalKeyNumbers[i];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void KeymapView::mouseMoveEvent(QMouseEvent* ev) {
|
||||
const int octave = ev->position().x() / 280;
|
||||
const int key = getKey(ev->pos() - QPoint(octave * 280, height() / 2 - 100));
|
||||
|
||||
if (octave >= 0 && key >= 0) {
|
||||
getEditor()->touchKey(octave * 12 + key, ev->modifiers() & Qt::ShiftModifier);
|
||||
}
|
||||
}
|
||||
|
||||
void KeymapView::wheelEvent(QWheelEvent* event) {
|
||||
if (const QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||
/* Send wheel event directly to the scroll bar */
|
||||
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
|
||||
}
|
||||
}
|
||||
|
||||
KeymapView::KeymapView(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_octaveRenderer(QStringLiteral(":/bg/keyboard.svg"))
|
||||
, m_lastOctaveRenderer(QStringLiteral(":/bg/keyboard_last.svg")) {
|
||||
setMinimumWidth(3002);
|
||||
|
||||
size_t k = 0;
|
||||
for (size_t i = 0; i < 11; ++i) {
|
||||
for (size_t j = 0; j < KeyStrings.size() && k < m_keyTexts.size(); ++j) {
|
||||
m_keyTexts[k++].setText(QStringLiteral("%1%2").arg(KeyStrings[j]).arg(i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
m_keyFont.setPointSize(12);
|
||||
m_keyPalettes.fill(-1);
|
||||
|
||||
for (size_t i = 0; i < m_natural.size(); ++i) {
|
||||
const auto& naturalKeyName = NaturalKeyNames[i];
|
||||
|
||||
if (m_octaveRenderer.elementExists(naturalKeyName)) {
|
||||
m_natural[i] =
|
||||
m_octaveRenderer.transformForElement(naturalKeyName).mapRect(m_octaveRenderer.boundsOnElement(naturalKeyName));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_sharp.size(); ++i) {
|
||||
const auto& sharpKeyName = SharpKeyNames[i];
|
||||
|
||||
if (m_octaveRenderer.elementExists(sharpKeyName)) {
|
||||
m_sharp[i] =
|
||||
m_octaveRenderer.transformForElement(sharpKeyName).mapRect(m_octaveRenderer.boundsOnElement(sharpKeyName));
|
||||
}
|
||||
}
|
||||
|
||||
m_widgetToSvg = RectToRect(QRect(0, 0, 280, 200), m_octaveRenderer.viewBoxF());
|
||||
}
|
||||
|
||||
KeymapView::~KeymapView() = default;
|
||||
|
||||
KeymapEditor* KeymapControls::getEditor() const { return qobject_cast<KeymapEditor*>(parentWidget()); }
|
||||
|
||||
void KeymapControls::setPaintIdx(int idx) {
|
||||
QPalette palette = m_paintButton->palette();
|
||||
if (idx < 0) {
|
||||
palette.setColor(QPalette::Window, QWidget::palette().color(QPalette::Window));
|
||||
palette.setColor(QPalette::Button, QWidget::palette().color(QPalette::Button));
|
||||
} else {
|
||||
const auto& keyPalette = getEditor()->m_paintPalette;
|
||||
palette.setColor(QPalette::Window, keyPalette[idx]);
|
||||
palette.setColor(QPalette::Button, keyPalette[idx].darker(300));
|
||||
}
|
||||
m_paintButton->setPalette(palette);
|
||||
}
|
||||
|
||||
void KeymapControls::setKeymap(const amuse::Keymap& km) {
|
||||
m_enableUpdate = false;
|
||||
const int idx = m_macro->collection()->indexOfId(km.macro.id);
|
||||
m_macro->setCurrentIndex(idx + 1);
|
||||
m_transpose->setValue(km.transpose);
|
||||
if (km.pan == -128) {
|
||||
m_pan->setDisabled(true);
|
||||
m_pan->setValue(true);
|
||||
m_surround->setChecked(true);
|
||||
} else {
|
||||
m_pan->setEnabled(true);
|
||||
m_pan->setValue(km.pan);
|
||||
m_surround->setChecked(false);
|
||||
}
|
||||
m_prioOffset->setValue(km.prioOffset);
|
||||
m_enableUpdate = true;
|
||||
}
|
||||
|
||||
void KeymapControls::loadData(ProjectModel::KeymapNode* node) {
|
||||
m_enableUpdate = false;
|
||||
m_macro->setCollection(
|
||||
g_MainWindow->projectModel()->getGroupNode(node)->getCollectionOfType(ProjectModel::INode::Type::SoundMacro));
|
||||
m_macro->setDisabled(false);
|
||||
m_transpose->setDisabled(false);
|
||||
m_transpose->setValue(0);
|
||||
m_pan->setDisabled(false);
|
||||
m_pan->setValue(64);
|
||||
m_surround->setDisabled(false);
|
||||
m_surround->setChecked(false);
|
||||
m_prioOffset->setDisabled(false);
|
||||
m_prioOffset->setValue(0);
|
||||
m_paintButton->setDisabled(false);
|
||||
m_paintButton->setPalette(palette());
|
||||
m_enableUpdate = true;
|
||||
}
|
||||
|
||||
void KeymapControls::unloadData() {
|
||||
m_enableUpdate = false;
|
||||
m_macro->setCollection(nullptr);
|
||||
m_macro->setDisabled(true);
|
||||
m_transpose->setDisabled(true);
|
||||
m_pan->setDisabled(true);
|
||||
m_surround->setDisabled(true);
|
||||
m_prioOffset->setDisabled(true);
|
||||
m_paintButton->setDisabled(true);
|
||||
m_paintButton->setPalette(palette());
|
||||
m_enableUpdate = true;
|
||||
}
|
||||
|
||||
void KeymapControls::controlChanged() {
|
||||
if (m_enableUpdate) {
|
||||
amuse::Keymap km;
|
||||
km.macro.id = m_macro->currentIndex() == 0 ? amuse::SoundMacroId{}
|
||||
: m_macro->collection()->idOfIndex(m_macro->currentIndex() - 1);
|
||||
km.transpose = int8_t(m_transpose->value());
|
||||
km.pan = int8_t(m_pan->value());
|
||||
if (m_surround->isChecked()) {
|
||||
km.pan = -128;
|
||||
m_pan->setDisabled(true);
|
||||
} else {
|
||||
m_pan->setEnabled(true);
|
||||
}
|
||||
km.prioOffset = int8_t(m_prioOffset->value());
|
||||
getEditor()->touchControl(km);
|
||||
}
|
||||
}
|
||||
|
||||
void KeymapControls::paintButtonPressed() {
|
||||
KeymapEditor* editor = getEditor();
|
||||
if (!editor->m_inPaint) {
|
||||
editor->setCursor(QCursor(QPixmap(QStringLiteral(":/icons/IconPaintbrush.svg")), 2, 30));
|
||||
editor->m_inPaint = true;
|
||||
m_paintButton->setDown(true);
|
||||
} else {
|
||||
editor->unsetCursor();
|
||||
editor->m_inPaint = false;
|
||||
m_paintButton->setDown(false);
|
||||
}
|
||||
}
|
||||
|
||||
KeymapControls::KeymapControls(QWidget* parent) : QFrame(parent) {
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
setFixedHeight(100);
|
||||
setFrameShape(QFrame::StyledPanel);
|
||||
setFrameShadow(QFrame::Sunken);
|
||||
setBackgroundRole(QPalette::Base);
|
||||
setAutoFillBackground(true);
|
||||
|
||||
QPalette palette = QWidget::palette();
|
||||
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
|
||||
|
||||
QHBoxLayout* mainLayout = new QHBoxLayout;
|
||||
|
||||
QGridLayout* leftLayout = new QGridLayout;
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("SoundMacro")), 0, 0);
|
||||
m_macro = new FieldProjectNode;
|
||||
m_macro->setDisabled(true);
|
||||
connect(m_macro, qOverload<int>(&FieldProjectNode::currentIndexChanged), this, &KeymapControls::controlChanged);
|
||||
leftLayout->addWidget(m_macro, 1, 0);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Transpose")), 0, 1);
|
||||
m_transpose = new QSpinBox;
|
||||
m_transpose->setPalette(palette);
|
||||
m_transpose->setDisabled(true);
|
||||
m_transpose->setRange(-128, 127);
|
||||
m_transpose->setToolTip(tr("Offset resulting MIDI note"));
|
||||
connect(m_transpose, qOverload<int>(&QSpinBox::valueChanged), this, &KeymapControls::controlChanged);
|
||||
leftLayout->addWidget(m_transpose, 1, 1);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Pan")), 0, 2);
|
||||
m_pan = new QSpinBox;
|
||||
m_pan->setPalette(palette);
|
||||
m_pan->setDisabled(true);
|
||||
m_pan->setRange(-127, 127);
|
||||
m_pan->setToolTip(tr("Set initial pan"));
|
||||
connect(m_pan, qOverload<int>(&QSpinBox::valueChanged), this, &KeymapControls::controlChanged);
|
||||
leftLayout->addWidget(m_pan, 1, 2);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Surround")), 0, 3);
|
||||
m_surround = new QCheckBox;
|
||||
m_surround->setPalette(palette);
|
||||
m_surround->setDisabled(true);
|
||||
m_surround->setToolTip(tr("Initially play through surround channels"));
|
||||
connect(m_surround, qOverload<int>(&QCheckBox::stateChanged), this, &KeymapControls::controlChanged);
|
||||
leftLayout->addWidget(m_surround, 1, 3);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Prio Offset")), 0, 4);
|
||||
m_prioOffset = new QSpinBox;
|
||||
m_prioOffset->setPalette(palette);
|
||||
m_prioOffset->setDisabled(true);
|
||||
m_prioOffset->setRange(-128, 127);
|
||||
m_prioOffset->setToolTip(tr("Offset resulting priority"));
|
||||
connect(m_prioOffset, qOverload<int>(&QSpinBox::valueChanged), this, &KeymapControls::controlChanged);
|
||||
leftLayout->addWidget(m_prioOffset, 1, 4);
|
||||
|
||||
leftLayout->setColumnMinimumWidth(0, 200);
|
||||
leftLayout->setColumnMinimumWidth(1, 75);
|
||||
leftLayout->setColumnMinimumWidth(2, 75);
|
||||
leftLayout->setColumnMinimumWidth(3, 50);
|
||||
leftLayout->setColumnMinimumWidth(4, 75);
|
||||
leftLayout->setRowMinimumHeight(0, 22);
|
||||
leftLayout->setRowMinimumHeight(1, 37);
|
||||
leftLayout->setContentsMargins(10, 6, 0, 14);
|
||||
|
||||
QVBoxLayout* rightLayout = new QVBoxLayout;
|
||||
|
||||
m_paintButton = new PaintButton;
|
||||
m_paintButton->setDisabled(true);
|
||||
connect(m_paintButton, &PaintButton::pressed, this, &KeymapControls::paintButtonPressed);
|
||||
rightLayout->addWidget(m_paintButton);
|
||||
rightLayout->setContentsMargins(0, 0, 10, 0);
|
||||
|
||||
mainLayout->addLayout(leftLayout);
|
||||
mainLayout->addStretch(1);
|
||||
mainLayout->addLayout(rightLayout);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
KeymapControls::~KeymapControls() = default;
|
||||
|
||||
void KeymapEditor::_touch() {
|
||||
if (m_controlKeymap.macro.id == 0xffff)
|
||||
m_controls->setPaintIdx(-1);
|
||||
else
|
||||
m_controls->setPaintIdx(getConfigIdx(m_controlKeymap.configKey()));
|
||||
}
|
||||
|
||||
void KeymapEditor::touchKey(int key, bool bulk) {
|
||||
if (m_inPaint) {
|
||||
if (bulk) {
|
||||
uint64_t refKey = (*m_kmView->m_node->m_obj)[key].configKey();
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[i];
|
||||
if (km.configKey() != refKey)
|
||||
continue;
|
||||
if (km.macro.id != 0xffff)
|
||||
deallocateConfigIdx(km.configKey());
|
||||
km = m_controlKeymap;
|
||||
if (km.macro.id == 0xffff)
|
||||
m_kmView->m_keyPalettes[i] = -1;
|
||||
else
|
||||
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
|
||||
}
|
||||
} else {
|
||||
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
|
||||
if (km.macro.id != 0xffff)
|
||||
deallocateConfigIdx(km.configKey());
|
||||
km = m_controlKeymap;
|
||||
if (km.macro.id == 0xffff)
|
||||
m_kmView->m_keyPalettes[key] = -1;
|
||||
else
|
||||
m_kmView->m_keyPalettes[key] = allocateConfigIdx(km.configKey());
|
||||
}
|
||||
|
||||
m_kmView->update();
|
||||
} else {
|
||||
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
|
||||
m_controlKeymap = km;
|
||||
m_controls->setKeymap(km);
|
||||
_touch();
|
||||
}
|
||||
}
|
||||
|
||||
void KeymapEditor::touchControl(const amuse::Keymap& km) {
|
||||
m_controlKeymap = km;
|
||||
_touch();
|
||||
}
|
||||
|
||||
int KeymapEditor::allocateConfigIdx(uint64_t key) {
|
||||
auto search = m_configToIdx.find(key);
|
||||
if (search != m_configToIdx.end()) {
|
||||
++search->second.second;
|
||||
return search->second.first;
|
||||
}
|
||||
for (size_t i = 0; i < m_idxBitmap.size(); ++i)
|
||||
if (!m_idxBitmap[i]) {
|
||||
m_configToIdx.insert_or_assign(key, std::make_pair(int(i), 1));
|
||||
m_idxBitmap.set(i);
|
||||
return int(i);
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
void KeymapEditor::deallocateConfigIdx(uint64_t key) {
|
||||
auto search = m_configToIdx.find(key);
|
||||
if (search != m_configToIdx.end()) {
|
||||
--search->second.second;
|
||||
if (search->second.second == 0) {
|
||||
m_idxBitmap.reset(search->second.first);
|
||||
m_configToIdx.erase(search);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
int KeymapEditor::getConfigIdx(uint64_t key) const {
|
||||
const auto search = m_configToIdx.find(key);
|
||||
if (search != m_configToIdx.end()) {
|
||||
return search->second.first;
|
||||
}
|
||||
for (size_t i = 0; i < m_idxBitmap.size(); ++i) {
|
||||
if (!m_idxBitmap[i]) {
|
||||
return int(i);
|
||||
}
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool KeymapEditor::loadData(ProjectModel::KeymapNode* node) {
|
||||
if (m_kmView->m_node.get() != node) {
|
||||
m_configToIdx.clear();
|
||||
m_idxBitmap.reset();
|
||||
|
||||
for (size_t i = 0; i < m_kmView->m_keyPalettes.size(); ++i) {
|
||||
const amuse::Keymap& km = (*node->m_obj)[i];
|
||||
|
||||
if (km.macro.id == 0xffff) {
|
||||
m_kmView->m_keyPalettes[i] = -1;
|
||||
} else {
|
||||
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
|
||||
}
|
||||
}
|
||||
|
||||
m_controlKeymap = amuse::Keymap();
|
||||
m_kmView->loadData(node);
|
||||
m_controls->loadData(node);
|
||||
}
|
||||
|
||||
m_inPaint = false;
|
||||
m_controls->m_paintButton->setDown(false);
|
||||
unsetCursor();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeymapEditor::unloadData() {
|
||||
m_configToIdx.clear();
|
||||
m_idxBitmap.reset();
|
||||
m_kmView->unloadData();
|
||||
m_controls->unloadData();
|
||||
|
||||
m_inPaint = false;
|
||||
m_controls->m_paintButton->setDown(false);
|
||||
unsetCursor();
|
||||
}
|
||||
|
||||
ProjectModel::INode* KeymapEditor::currentNode() const { return m_kmView->currentNode(); }
|
||||
|
||||
void KeymapEditor::keyPressEvent(QKeyEvent* event) {
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
if (m_inPaint) {
|
||||
m_inPaint = false;
|
||||
m_controls->m_paintButton->setDown(false);
|
||||
unsetCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeymapEditor::KeymapEditor(QWidget* parent)
|
||||
: EditorWidget(parent), m_scrollArea(new QScrollArea), m_kmView(new KeymapView), m_controls(new KeymapControls) {
|
||||
size_t k = 0;
|
||||
for (size_t i = 0; i < ValueTable.size(); ++i) {
|
||||
for (size_t j = 0; j < HueTable.size() && k < m_paintPalette.size(); ++j) {
|
||||
m_paintPalette[k++].setHsv(HueTable[j], SaturationTable[i], ValueTable[i]);
|
||||
}
|
||||
}
|
||||
|
||||
m_scrollArea->setWidget(m_kmView);
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(QMargins());
|
||||
layout->setSpacing(1);
|
||||
layout->addWidget(m_scrollArea);
|
||||
layout->addWidget(m_controls);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
KeymapEditor::~KeymapEditor() = default;
|
|
@ -0,0 +1,122 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QFrame>
|
||||
#include <QPushButton>
|
||||
#include <QStaticText>
|
||||
#include <QSvgRenderer>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/AudioGroupPool.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class KeymapEditor;
|
||||
class QCheckBox;
|
||||
class QScrollArea;
|
||||
class QSpinBox;
|
||||
|
||||
class PaintButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PaintButton(QWidget* parent = Q_NULLPTR);
|
||||
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
void focusOutEvent(QFocusEvent* event) override { event->ignore(); }
|
||||
void keyPressEvent(QKeyEvent* event) override { event->ignore(); }
|
||||
};
|
||||
|
||||
using PaintPalette = std::array<QColor, 129>;
|
||||
|
||||
class KeymapView : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class KeymapControls;
|
||||
friend class KeymapEditor;
|
||||
amuse::ObjToken<ProjectModel::KeymapNode> m_node;
|
||||
QSvgRenderer m_octaveRenderer;
|
||||
QSvgRenderer m_lastOctaveRenderer;
|
||||
std::array<QRectF, 7> m_natural;
|
||||
std::array<QRectF, 5> m_sharp;
|
||||
QTransform m_widgetToSvg;
|
||||
QFont m_keyFont;
|
||||
std::array<QStaticText, 128> m_keyTexts;
|
||||
std::array<int, 128> m_keyPalettes;
|
||||
KeymapEditor* getEditor() const;
|
||||
int getKey(const QPoint& localPos) const;
|
||||
void drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const PaintPalette& keyPalette, int o,
|
||||
int k) const;
|
||||
|
||||
public:
|
||||
explicit KeymapView(QWidget* parent = Q_NULLPTR);
|
||||
~KeymapView() override;
|
||||
|
||||
void loadData(ProjectModel::KeymapNode* node);
|
||||
void unloadData();
|
||||
ProjectModel::INode* currentNode() const;
|
||||
|
||||
void paintEvent(QPaintEvent* ev) override;
|
||||
void mousePressEvent(QMouseEvent* ev) override;
|
||||
void mouseMoveEvent(QMouseEvent* ev) override;
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
};
|
||||
|
||||
class KeymapControls : public QFrame {
|
||||
Q_OBJECT
|
||||
friend class KeymapView;
|
||||
friend class KeymapEditor;
|
||||
FieldProjectNode* m_macro;
|
||||
QSpinBox* m_transpose;
|
||||
QSpinBox* m_pan;
|
||||
QCheckBox* m_surround;
|
||||
QSpinBox* m_prioOffset;
|
||||
PaintButton* m_paintButton;
|
||||
bool m_enableUpdate = true;
|
||||
KeymapEditor* getEditor() const;
|
||||
void setPaintIdx(int idx);
|
||||
void setKeymap(const amuse::Keymap& km);
|
||||
|
||||
public:
|
||||
explicit KeymapControls(QWidget* parent = Q_NULLPTR);
|
||||
~KeymapControls() override;
|
||||
|
||||
void loadData(ProjectModel::KeymapNode* node);
|
||||
void unloadData();
|
||||
|
||||
public slots:
|
||||
void controlChanged();
|
||||
void paintButtonPressed();
|
||||
};
|
||||
|
||||
class KeymapEditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
friend class KeymapView;
|
||||
friend class KeymapControls;
|
||||
QScrollArea* m_scrollArea;
|
||||
KeymapView* m_kmView;
|
||||
KeymapControls* m_controls;
|
||||
PaintPalette m_paintPalette;
|
||||
amuse::Keymap m_controlKeymap;
|
||||
std::unordered_map<uint64_t, std::pair<int, int>> m_configToIdx;
|
||||
std::bitset<129> m_idxBitmap;
|
||||
bool m_inPaint = false;
|
||||
void _touch();
|
||||
void touchKey(int key, bool bulk = false);
|
||||
void touchControl(const amuse::Keymap& km);
|
||||
int allocateConfigIdx(uint64_t key);
|
||||
void deallocateConfigIdx(uint64_t key);
|
||||
int getConfigIdx(uint64_t key) const;
|
||||
|
||||
public:
|
||||
explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
|
||||
~KeymapEditor() override;
|
||||
|
||||
bool loadData(ProjectModel::KeymapNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
|
@ -0,0 +1,636 @@
|
|||
#include "LayersEditor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QScrollBar>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
|
||||
class LayerDataChangeUndoCommand : public EditorUndoCommand {
|
||||
QModelIndex m_index;
|
||||
int m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
explicit LayerDataChangeUndoCommand(ProjectModel::LayersNode* node, const QString& text, QModelIndex index,
|
||||
int redoVal)
|
||||
: EditorUndoCommand(node, text), m_index(index), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::LayerMapping& layer = (*static_cast<ProjectModel::LayersNode*>(m_node.get())->m_obj)[m_index.row()];
|
||||
|
||||
switch (m_index.column()) {
|
||||
case 0:
|
||||
layer.macro.id = m_undoVal;
|
||||
break;
|
||||
case 1:
|
||||
layer.keyLo = m_undoVal;
|
||||
break;
|
||||
case 2:
|
||||
layer.keyHi = m_undoVal;
|
||||
break;
|
||||
case 3:
|
||||
layer.transpose = m_undoVal;
|
||||
break;
|
||||
case 4:
|
||||
layer.volume = m_undoVal;
|
||||
break;
|
||||
case 5:
|
||||
layer.prioOffset = m_undoVal;
|
||||
break;
|
||||
case 6:
|
||||
layer.span = m_undoVal;
|
||||
break;
|
||||
case 7:
|
||||
layer.pan = m_undoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::LayerMapping& layer = (*static_cast<ProjectModel::LayersNode*>(m_node.get())->m_obj)[m_index.row()];
|
||||
|
||||
switch (m_index.column()) {
|
||||
case 0:
|
||||
m_undoVal = layer.macro.id.id;
|
||||
layer.macro.id = m_redoVal;
|
||||
break;
|
||||
case 1:
|
||||
m_undoVal = layer.keyLo;
|
||||
layer.keyLo = m_redoVal;
|
||||
break;
|
||||
case 2:
|
||||
m_undoVal = layer.keyHi;
|
||||
layer.keyHi = m_redoVal;
|
||||
break;
|
||||
case 3:
|
||||
m_undoVal = layer.transpose;
|
||||
layer.transpose = m_redoVal;
|
||||
break;
|
||||
case 4:
|
||||
m_undoVal = layer.volume;
|
||||
layer.volume = m_redoVal;
|
||||
break;
|
||||
case 5:
|
||||
m_undoVal = layer.prioOffset;
|
||||
layer.prioOffset = m_redoVal;
|
||||
break;
|
||||
case 6:
|
||||
m_undoVal = layer.span;
|
||||
layer.span = m_redoVal;
|
||||
break;
|
||||
case 7:
|
||||
m_undoVal = layer.pan;
|
||||
layer.pan = m_redoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
SoundMacroDelegate::SoundMacroDelegate(QObject* parent) : BaseObjectDelegate(parent) {}
|
||||
|
||||
SoundMacroDelegate::~SoundMacroDelegate() = default;
|
||||
|
||||
ProjectModel::INode* SoundMacroDelegate::getNode(const QAbstractItemModel* __model, const QModelIndex& index) const {
|
||||
const LayersModel* model = static_cast<const LayersModel*>(__model);
|
||||
const amuse::LayerMapping& layer = (*model->m_node->m_obj)[index.row()];
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
|
||||
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
|
||||
return smColl->nodeOfId(layer.macro.id);
|
||||
}
|
||||
|
||||
QWidget* SoundMacroDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) const {
|
||||
const LayersModel* model = static_cast<const LayersModel*>(index.model());
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
|
||||
EditorFieldProjectNode* cb =
|
||||
new EditorFieldProjectNode(group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro), parent);
|
||||
connect(cb, qOverload<int>(&EditorFieldProjectNode::currentIndexChanged), this, &SoundMacroDelegate::smIndexChanged);
|
||||
return cb;
|
||||
}
|
||||
|
||||
void SoundMacroDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
|
||||
const LayersModel* model = static_cast<const LayersModel*>(index.model());
|
||||
const amuse::LayerMapping& layer = (*model->m_node->m_obj)[index.row()];
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
|
||||
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
|
||||
static_cast<EditorFieldProjectNode*>(editor)->setCurrentIndex(smColl->indexOfId(layer.macro.id) + 1);
|
||||
if (static_cast<EditorFieldProjectNode*>(editor)->shouldPopupOpen())
|
||||
QApplication::postEvent(editor, new QEvent(QEvent::User));
|
||||
}
|
||||
|
||||
void SoundMacroDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, const QModelIndex& index) const {
|
||||
const LayersModel* model = static_cast<const LayersModel*>(m);
|
||||
amuse::LayerMapping& layer = (*model->m_node->m_obj)[index.row()];
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
|
||||
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
|
||||
int idx = static_cast<EditorFieldProjectNode*>(editor)->currentIndex();
|
||||
amuse::SoundMacroId id;
|
||||
if (idx != 0)
|
||||
id = smColl->idOfIndex(idx - 1);
|
||||
if (layer.macro.id == id)
|
||||
return;
|
||||
g_MainWindow->pushUndoCommand(new LayerDataChangeUndoCommand(
|
||||
model->m_node.get(), tr("Change %1").arg(m->headerData(0, Qt::Horizontal).toString()), index, id.id));
|
||||
emit m->dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
}
|
||||
|
||||
void SoundMacroDelegate::smIndexChanged() { emit commitData(static_cast<QWidget*>(sender())); }
|
||||
|
||||
void LayersModel::loadData(ProjectModel::LayersNode* node) {
|
||||
beginResetModel();
|
||||
m_node = node;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void LayersModel::unloadData() {
|
||||
beginResetModel();
|
||||
m_node.reset();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int LayersModel::rowCount(const QModelIndex& parent) const {
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
if (!m_node)
|
||||
return 0;
|
||||
return int(m_node->m_obj->size()) + 1;
|
||||
}
|
||||
|
||||
int LayersModel::columnCount(const QModelIndex& parent) const {
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return 8;
|
||||
}
|
||||
|
||||
QVariant LayersModel::data(const QModelIndex& index, int role) const {
|
||||
if (!m_node)
|
||||
return QVariant();
|
||||
if (index.row() == m_node->m_obj->size())
|
||||
return QVariant();
|
||||
const amuse::LayerMapping& layer = (*m_node->m_obj)[index.row()];
|
||||
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
switch (index.column()) {
|
||||
case 0: {
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(m_node.get());
|
||||
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
|
||||
if (ProjectModel::BasePoolObjectNode* node = smColl->nodeOfId(layer.macro.id))
|
||||
return node->text();
|
||||
return QVariant();
|
||||
}
|
||||
case 1:
|
||||
return layer.keyLo;
|
||||
case 2:
|
||||
return layer.keyHi;
|
||||
case 3:
|
||||
return layer.transpose;
|
||||
case 4:
|
||||
return layer.volume;
|
||||
case 5:
|
||||
return layer.prioOffset;
|
||||
case 6:
|
||||
return layer.span;
|
||||
case 7:
|
||||
return layer.pan;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool LayersModel::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||
if (!m_node || role != Qt::EditRole)
|
||||
return false;
|
||||
const amuse::LayerMapping& layer = (*m_node->m_obj)[index.row()];
|
||||
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
if (layer.macro.id == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 1:
|
||||
if (layer.keyLo == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 2:
|
||||
if (layer.keyHi == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 3:
|
||||
if (layer.transpose == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 4:
|
||||
if (layer.volume == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 5:
|
||||
if (layer.prioOffset == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 6:
|
||||
if (layer.span == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 7:
|
||||
if (layer.pan == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
g_MainWindow->pushUndoCommand(new LayerDataChangeUndoCommand(
|
||||
m_node.get(), tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()), index, value.toInt()));
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant LayersModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("SoundMacro");
|
||||
case 1:
|
||||
return tr("Key Lo");
|
||||
case 2:
|
||||
return tr("Key Hi");
|
||||
case 3:
|
||||
return tr("Transpose");
|
||||
case 4:
|
||||
return tr("Volume");
|
||||
case 5:
|
||||
return tr("Prio Off");
|
||||
case 6:
|
||||
return tr("Span");
|
||||
case 7:
|
||||
return tr("Pan");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags LayersModel::flags(const QModelIndex& index) const {
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
if (index.row() == m_node->m_obj->size())
|
||||
return Qt::NoItemFlags;
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
|
||||
}
|
||||
|
||||
Qt::DropActions LayersModel::supportedDropActions() const { return Qt::MoveAction; }
|
||||
|
||||
Qt::DropActions LayersModel::supportedDragActions() const { return Qt::MoveAction; }
|
||||
|
||||
class LayerRowMoveCommand : public EditorUndoCommand {
|
||||
LayersTableView* m_view;
|
||||
int m_undoPos, m_redoPos, m_count;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
explicit LayerRowMoveCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view, int undoPos,
|
||||
int redoPos, int count)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_undoPos(undoPos), m_redoPos(redoPos), m_count(count) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
if (m_redoPos > m_undoPos)
|
||||
m_view->model()->moveRows(QModelIndex(), m_redoPos - 1, m_count, QModelIndex(), m_undoPos);
|
||||
else
|
||||
m_view->model()->moveRows(QModelIndex(), m_redoPos, m_count, QModelIndex(), m_undoPos + 1);
|
||||
}
|
||||
void redo() override {
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
m_view->model()->moveRows(QModelIndex(), m_undoPos, m_count, QModelIndex(), m_redoPos);
|
||||
}
|
||||
};
|
||||
|
||||
bool LayersModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
|
||||
const QModelIndex& parent) {
|
||||
// check if the action is supported
|
||||
if (!data || action != Qt::MoveAction)
|
||||
return false;
|
||||
// check if the format is supported
|
||||
QStringList types = mimeTypes();
|
||||
if (types.isEmpty())
|
||||
return false;
|
||||
QString format = types.at(0);
|
||||
if (!data->hasFormat(format))
|
||||
return false;
|
||||
|
||||
// decode and insert
|
||||
QByteArray encoded = data->data(format);
|
||||
QDataStream stream(&encoded, QIODevice::ReadOnly);
|
||||
|
||||
std::unordered_set<int> rows;
|
||||
int lastRow = -1;
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
int r, c;
|
||||
QMap<int, QVariant> v;
|
||||
stream >> r >> c >> v;
|
||||
rows.insert(r);
|
||||
lastRow = r;
|
||||
}
|
||||
|
||||
if (lastRow == -1)
|
||||
return false;
|
||||
|
||||
int start = lastRow;
|
||||
while (rows.find(start - 1) != rows.cend())
|
||||
start -= 1;
|
||||
int count = lastRow - start + 1;
|
||||
while (rows.find(start + count) != rows.cend())
|
||||
count += 1;
|
||||
|
||||
int dest = parent.row();
|
||||
if (dest >= start) {
|
||||
if (dest - start < count)
|
||||
return false;
|
||||
dest += 1;
|
||||
}
|
||||
|
||||
g_MainWindow->pushUndoCommand(new LayerRowMoveCommand(m_node.get(), count > 1 ? tr("Move Layers") : tr("Move Layer"),
|
||||
&static_cast<LayersEditor*>(QObject::parent())->m_tableView,
|
||||
start, dest, count));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class LayerRowUndoCommand : public EditorUndoCommand {
|
||||
protected:
|
||||
LayersTableView* m_view;
|
||||
std::vector<std::pair<amuse::LayerMapping, int>> m_data;
|
||||
bool m_undid = false;
|
||||
void add() {
|
||||
m_view->selectionModel()->clearSelection();
|
||||
for (const auto& p : m_data) {
|
||||
static_cast<LayersModel*>(m_view->model())->_insertRow(p.second, p.first);
|
||||
m_view->setCurrentIndex(m_view->model()->index(p.second, 0));
|
||||
}
|
||||
}
|
||||
void del() {
|
||||
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it) {
|
||||
it->first = static_cast<LayersModel*>(m_view->model())->_removeRow(it->second);
|
||||
}
|
||||
}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit LayerRowUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
|
||||
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
|
||||
};
|
||||
|
||||
class LayerRowAddUndoCommand : public LayerRowUndoCommand {
|
||||
using base = LayerRowUndoCommand;
|
||||
|
||||
public:
|
||||
explicit LayerRowAddUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
|
||||
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
|
||||
: LayerRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() override {
|
||||
base::undo();
|
||||
base::del();
|
||||
}
|
||||
void redo() override {
|
||||
base::redo();
|
||||
base::add();
|
||||
}
|
||||
};
|
||||
|
||||
class LayerRowDelUndoCommand : public LayerRowUndoCommand {
|
||||
using base = LayerRowUndoCommand;
|
||||
|
||||
public:
|
||||
explicit LayerRowDelUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
|
||||
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
|
||||
: LayerRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() override {
|
||||
base::undo();
|
||||
base::add();
|
||||
}
|
||||
void redo() override {
|
||||
base::redo();
|
||||
base::del();
|
||||
}
|
||||
};
|
||||
|
||||
bool LayersModel::insertRows(int row, int count, const QModelIndex& parent) {
|
||||
if (!m_node)
|
||||
return false;
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
|
||||
layers.insert(layers.begin() + row, count, amuse::LayerMapping());
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayersModel::moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
|
||||
const QModelIndex& destinationParent, int destinationChild) {
|
||||
if (!m_node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool moving =
|
||||
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
|
||||
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
|
||||
|
||||
const auto pivot = layers.begin() + sourceRow;
|
||||
const auto begin = layers.begin() + destinationChild;
|
||||
const auto end = pivot + count;
|
||||
|
||||
if (destinationChild < sourceRow) {
|
||||
std::rotate(begin, pivot, end);
|
||||
} else if (destinationChild > sourceRow) {
|
||||
std::rotate(pivot, end, begin);
|
||||
}
|
||||
|
||||
if (moving) {
|
||||
endMoveRows();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayersModel::removeRows(int row, int count, const QModelIndex& parent) {
|
||||
if (!m_node)
|
||||
return false;
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
|
||||
layers.erase(layers.begin() + row, layers.begin() + row + count);
|
||||
endRemoveRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayersModel::_insertRow(int row, const amuse::LayerMapping& data) {
|
||||
if (!m_node)
|
||||
return;
|
||||
beginInsertRows(QModelIndex(), row, row);
|
||||
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
|
||||
layers.insert(layers.begin() + row, data);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
amuse::LayerMapping LayersModel::_removeRow(int row) {
|
||||
if (!m_node)
|
||||
return {};
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
|
||||
amuse::LayerMapping ret = layers[row];
|
||||
layers.erase(layers.begin() + row);
|
||||
endRemoveRows();
|
||||
return ret;
|
||||
}
|
||||
|
||||
LayersModel::LayersModel(QObject* parent) : QAbstractTableModel(parent) {}
|
||||
|
||||
LayersModel::~LayersModel() = default;
|
||||
|
||||
void LayersTableView::deleteSelection() {
|
||||
const QModelIndexList list = selectionModel()->selectedRows();
|
||||
if (list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<amuse::LayerMapping, int>> data;
|
||||
data.reserve(list.size());
|
||||
for (const QModelIndex& idx : list) {
|
||||
data.emplace_back(amuse::LayerMapping{}, idx.row());
|
||||
}
|
||||
|
||||
std::sort(data.begin(), data.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
|
||||
g_MainWindow->pushUndoCommand(new LayerRowDelUndoCommand(static_cast<LayersModel*>(model())->m_node.get(),
|
||||
data.size() > 1 ? tr("Delete Layers") : tr("Delete Layer"),
|
||||
this, std::move(data)));
|
||||
}
|
||||
|
||||
void LayersTableView::setModel(QAbstractItemModel* model) {
|
||||
QTableView::setModel(model);
|
||||
horizontalHeader()->setMinimumSectionSize(75);
|
||||
horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(1, 75);
|
||||
horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(2, 75);
|
||||
horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(3, 75);
|
||||
horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(4, 75);
|
||||
horizontalHeader()->setSectionResizeMode(5, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(5, 75);
|
||||
horizontalHeader()->setSectionResizeMode(6, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(6, 75);
|
||||
horizontalHeader()->setSectionResizeMode(7, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(7, 75);
|
||||
}
|
||||
|
||||
LayersTableView::LayersTableView(QWidget* parent) : QTableView(parent) {
|
||||
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setDragDropMode(QAbstractItemView::InternalMove);
|
||||
setDefaultDropAction(Qt::MoveAction);
|
||||
setDragEnabled(true);
|
||||
setGridStyle(Qt::NoPen);
|
||||
|
||||
m_signedDelegate.setItemEditorFactory(&m_signedFactory);
|
||||
m_unsignedDelegate.setItemEditorFactory(&m_unsignedFactory);
|
||||
|
||||
setItemDelegateForColumn(0, &m_smDelegate);
|
||||
setItemDelegateForColumn(1, &m_unsignedDelegate);
|
||||
setItemDelegateForColumn(2, &m_unsignedDelegate);
|
||||
setItemDelegateForColumn(3, &m_signedDelegate);
|
||||
setItemDelegateForColumn(4, &m_unsignedDelegate);
|
||||
setItemDelegateForColumn(5, &m_signedDelegate);
|
||||
setItemDelegateForColumn(6, &m_unsignedDelegate);
|
||||
setItemDelegateForColumn(7, &m_unsignedDelegate);
|
||||
}
|
||||
|
||||
LayersTableView::~LayersTableView() = default;
|
||||
|
||||
bool LayersEditor::loadData(ProjectModel::LayersNode* node) {
|
||||
m_model.loadData(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayersEditor::unloadData() { m_model.unloadData(); }
|
||||
|
||||
ProjectModel::INode* LayersEditor::currentNode() const { return m_model.m_node.get(); }
|
||||
|
||||
void LayersEditor::resizeEvent(QResizeEvent* ev) {
|
||||
m_tableView.setGeometry(QRect({}, ev->size()));
|
||||
m_addRemoveButtons.move(0, ev->size().height() - 32);
|
||||
}
|
||||
|
||||
void LayersEditor::rowsInserted(const QModelIndex& parent, int first, int last) {
|
||||
m_tableView.scrollTo(m_tableView.model()->index(first, 0));
|
||||
}
|
||||
|
||||
void LayersEditor::rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row) {
|
||||
m_tableView.scrollTo(m_tableView.model()->index(row, 0));
|
||||
}
|
||||
|
||||
void LayersEditor::doAdd() {
|
||||
const QModelIndex idx = m_tableView.selectionModel()->currentIndex();
|
||||
std::vector<std::pair<amuse::LayerMapping, int>> data;
|
||||
|
||||
if (idx.isValid()) {
|
||||
data.emplace_back(amuse::LayerMapping{}, idx.row());
|
||||
} else {
|
||||
data.emplace_back(amuse::LayerMapping{}, m_model.rowCount() - 1);
|
||||
}
|
||||
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new LayerRowAddUndoCommand(m_model.m_node.get(), tr("Add Layer"), &m_tableView, std::move(data)));
|
||||
}
|
||||
|
||||
void LayersEditor::doSelectionChanged() {
|
||||
m_addRemoveButtons.removeAction()->setDisabled(m_tableView.selectionModel()->selectedRows().isEmpty());
|
||||
g_MainWindow->updateFocus();
|
||||
}
|
||||
|
||||
AmuseItemEditFlags LayersEditor::itemEditFlags() const {
|
||||
return m_tableView.selectionModel()->selectedRows().isEmpty() ? AmuseItemNone : AmuseItemDelete;
|
||||
}
|
||||
|
||||
void LayersEditor::itemDeleteAction() { m_tableView.deleteSelection(); }
|
||||
|
||||
LayersEditor::LayersEditor(QWidget* parent)
|
||||
: EditorWidget(parent), m_model(this), m_tableView(this), m_addRemoveButtons(this) {
|
||||
m_tableView.setModel(&m_model);
|
||||
connect(m_tableView.selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
&LayersEditor::doSelectionChanged);
|
||||
connect(&m_model, &LayersModel::rowsInserted, this, &LayersEditor::rowsInserted);
|
||||
connect(&m_model, &LayersModel::rowsMoved, this, &LayersEditor::rowsMoved);
|
||||
|
||||
m_addRemoveButtons.addAction()->setToolTip(tr("Add new layer mapping"));
|
||||
connect(m_addRemoveButtons.addAction(), &QAction::triggered, this, &LayersEditor::doAdd);
|
||||
m_addRemoveButtons.removeAction()->setToolTip(tr("Remove selected layer mappings"));
|
||||
connect(m_addRemoveButtons.removeAction(), &QAction::triggered, this, &LayersEditor::itemDeleteAction);
|
||||
}
|
||||
|
||||
LayersEditor::~LayersEditor() = default;
|
|
@ -0,0 +1,103 @@
|
|||
#pragma once
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QAction>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTableView>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/AudioGroupPool.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class SoundMacroDelegate : public BaseObjectDelegate {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
|
||||
public:
|
||||
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
|
||||
~SoundMacroDelegate() override;
|
||||
|
||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
|
||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
|
||||
private slots:
|
||||
void smIndexChanged();
|
||||
};
|
||||
|
||||
class LayersModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
friend class LayersEditor;
|
||||
friend class SoundMacroDelegate;
|
||||
friend class LayersTableView;
|
||||
amuse::ObjToken<ProjectModel::LayersNode> m_node;
|
||||
|
||||
public:
|
||||
explicit LayersModel(QObject* parent = Q_NULLPTR);
|
||||
~LayersModel() override;
|
||||
|
||||
void loadData(ProjectModel::LayersNode* node);
|
||||
void unloadData();
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
Qt::DropActions supportedDropActions() const override;
|
||||
Qt::DropActions supportedDragActions() const override;
|
||||
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
|
||||
const QModelIndex& parent) override;
|
||||
|
||||
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent,
|
||||
int destinationChild) override;
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
|
||||
|
||||
void _insertRow(int row, const amuse::LayerMapping& data);
|
||||
amuse::LayerMapping _removeRow(int row);
|
||||
};
|
||||
|
||||
class LayersTableView : public QTableView {
|
||||
Q_OBJECT
|
||||
SoundMacroDelegate m_smDelegate;
|
||||
RangedValueFactory<-128, 127> m_signedFactory;
|
||||
RangedValueFactory<0, 127> m_unsignedFactory;
|
||||
QStyledItemDelegate m_signedDelegate, m_unsignedDelegate;
|
||||
|
||||
public:
|
||||
explicit LayersTableView(QWidget* parent = Q_NULLPTR);
|
||||
~LayersTableView() override;
|
||||
|
||||
void setModel(QAbstractItemModel* model) override;
|
||||
void deleteSelection();
|
||||
};
|
||||
|
||||
class LayersEditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
friend class LayersModel;
|
||||
LayersModel m_model;
|
||||
LayersTableView m_tableView;
|
||||
AddRemoveButtons m_addRemoveButtons;
|
||||
|
||||
public:
|
||||
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
|
||||
~LayersEditor() override;
|
||||
|
||||
bool loadData(ProjectModel::LayersNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
void resizeEvent(QResizeEvent* ev) override;
|
||||
AmuseItemEditFlags itemEditFlags() const override;
|
||||
|
||||
private slots:
|
||||
void rowsInserted(const QModelIndex& parent, int first, int last);
|
||||
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||
void doAdd();
|
||||
void doSelectionChanged();
|
||||
void itemDeleteAction() override;
|
||||
};
|
|
@ -0,0 +1,147 @@
|
|||
#include "MIDIReader.hpp"
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
#include <amuse/Engine.hpp>
|
||||
|
||||
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock) : amuse::BooBackendMIDIReader(engine, useLock) {}
|
||||
|
||||
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) {
|
||||
if (g_MainWindow->m_interactiveSeq) {
|
||||
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
auto keySearch = m_chanVoxs.find(key);
|
||||
if (keySearch == m_chanVoxs.cend())
|
||||
return;
|
||||
|
||||
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
|
||||
m_lastVoice.reset();
|
||||
keySearch->second->keyOff();
|
||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||
m_chanVoxs.erase(keySearch);
|
||||
}
|
||||
|
||||
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) {
|
||||
if (g_MainWindow->m_interactiveSeq) {
|
||||
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_lastVoice && m_lastVoice->isDestroyed())
|
||||
m_lastVoice.reset();
|
||||
|
||||
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
||||
if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice) {
|
||||
uint8_t lastNote = lastVoice->getLastNote();
|
||||
if (lastVoice->doPortamento(key)) {
|
||||
m_chanVoxs.erase(lastNote);
|
||||
m_chanVoxs[key] = lastVoice;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure keyoff sent first */
|
||||
auto keySearch = m_chanVoxs.find(key);
|
||||
if (keySearch != m_chanVoxs.cend()) {
|
||||
if (keySearch->second == m_lastVoice)
|
||||
m_lastVoice.reset();
|
||||
keySearch->second->keyOff();
|
||||
keySearch->second->setPedal(false);
|
||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||
m_chanVoxs.erase(keySearch);
|
||||
}
|
||||
|
||||
amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
|
||||
if (newVox) {
|
||||
m_chanVoxs[key] = newVox;
|
||||
m_lastVoice = newVox;
|
||||
}
|
||||
}
|
||||
|
||||
void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
|
||||
|
||||
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) {
|
||||
if (g_MainWindow->m_interactiveSeq) {
|
||||
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (control == 1) {
|
||||
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
|
||||
} else if (control == 64) {
|
||||
g_MainWindow->setSustain(value >= 0x40);
|
||||
} else {
|
||||
for (auto& v : m_engine.getActiveVoices())
|
||||
v->setCtrlValue(control, value);
|
||||
}
|
||||
g_MainWindow->m_ctrlVals[control] = value;
|
||||
}
|
||||
|
||||
void MIDIReader::programChange(uint8_t chan, uint8_t program) {
|
||||
if (g_MainWindow->m_interactiveSeq)
|
||||
g_MainWindow->m_interactiveSeq->setChanProgram(chan, program);
|
||||
}
|
||||
|
||||
void MIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
|
||||
|
||||
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch) {
|
||||
float pWheel = (pitch - 0x2000) / float(0x2000);
|
||||
if (g_MainWindow->m_interactiveSeq)
|
||||
g_MainWindow->m_interactiveSeq->setPitchWheel(chan, pWheel);
|
||||
else
|
||||
g_MainWindow->m_ui.pitchSlider->setValue(int(pWheel * 2048.f));
|
||||
}
|
||||
|
||||
void MIDIReader::allSoundOff(uint8_t chan) {
|
||||
if (g_MainWindow->m_interactiveSeq) {
|
||||
g_MainWindow->m_interactiveSeq->kill();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& v : m_engine.getActiveVoices())
|
||||
v->kill();
|
||||
}
|
||||
|
||||
void MIDIReader::resetAllControllers(uint8_t /*chan*/) {}
|
||||
|
||||
void MIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
|
||||
|
||||
void MIDIReader::allNotesOff(uint8_t chan) {
|
||||
if (g_MainWindow->m_interactiveSeq) {
|
||||
g_MainWindow->m_interactiveSeq->kill();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& v : m_engine.getActiveVoices())
|
||||
v->kill();
|
||||
}
|
||||
|
||||
void MIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||
|
||||
void MIDIReader::polyMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||
|
||||
void MIDIReader::sysex(const void* /*data*/, size_t /*len*/) {}
|
||||
|
||||
void MIDIReader::timeCodeQuarterFrame(uint8_t /*message*/, uint8_t /*value*/) {}
|
||||
|
||||
void MIDIReader::songPositionPointer(uint16_t /*pointer*/) {}
|
||||
|
||||
void MIDIReader::songSelect(uint8_t /*song*/) {}
|
||||
|
||||
void MIDIReader::tuneRequest() {}
|
||||
|
||||
void MIDIReader::startSeq() {}
|
||||
|
||||
void MIDIReader::continueSeq() {}
|
||||
|
||||
void MIDIReader::stopSeq() {}
|
||||
|
||||
void MIDIReader::reset() {}
|
||||
|
||||
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine) : amuse::BooBackendVoiceAllocator(booEngine) {}
|
||||
|
||||
std::unique_ptr<amuse::IMIDIReader> VoiceAllocator::allocateMIDIReader(amuse::Engine& engine) {
|
||||
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <amuse/BooBackend.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
#include <amuse/Voice.hpp>
|
||||
|
||||
namespace amuse {
|
||||
class Engine;
|
||||
}
|
||||
|
||||
class MIDIReader : public amuse::BooBackendMIDIReader {
|
||||
std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
|
||||
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
|
||||
amuse::ObjToken<amuse::Voice> m_lastVoice;
|
||||
|
||||
public:
|
||||
MIDIReader(amuse::Engine& engine, bool useLock);
|
||||
|
||||
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity) override;
|
||||
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity) override;
|
||||
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure) override;
|
||||
void controlChange(uint8_t chan, uint8_t control, uint8_t value) override;
|
||||
void programChange(uint8_t chan, uint8_t program) override;
|
||||
void channelPressure(uint8_t chan, uint8_t pressure) override;
|
||||
void pitchBend(uint8_t chan, int16_t pitch) override;
|
||||
|
||||
void allSoundOff(uint8_t chan) override;
|
||||
void resetAllControllers(uint8_t chan) override;
|
||||
void localControl(uint8_t chan, bool on) override;
|
||||
void allNotesOff(uint8_t chan) override;
|
||||
void omniMode(uint8_t chan, bool on) override;
|
||||
void polyMode(uint8_t chan, bool on) override;
|
||||
|
||||
void sysex(const void* data, size_t len) override;
|
||||
void timeCodeQuarterFrame(uint8_t message, uint8_t value) override;
|
||||
void songPositionPointer(uint16_t pointer) override;
|
||||
void songSelect(uint8_t song) override;
|
||||
void tuneRequest() override;
|
||||
|
||||
void startSeq() override;
|
||||
void continueSeq() override;
|
||||
void stopSeq() override;
|
||||
|
||||
void reset() override;
|
||||
};
|
||||
|
||||
class VoiceAllocator : public amuse::BooBackendVoiceAllocator {
|
||||
public:
|
||||
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
||||
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine) override;
|
||||
};
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QThread>
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include "ui_MainWindow.h"
|
||||
|
||||
#include <amuse/Common.hpp>
|
||||
#include <amuse/Sequencer.hpp>
|
||||
#include <amuse/Voice.hpp>
|
||||
|
||||
#define MaxRecentFiles 4
|
||||
|
||||
namespace amuse {
|
||||
class Engine;
|
||||
}
|
||||
|
||||
namespace boo {
|
||||
struct IAudioVoiceEngine;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class ADSREditor;
|
||||
class CurveEditor;
|
||||
class KeymapEditor;
|
||||
class LayersEditor;
|
||||
class MainWindow;
|
||||
class SampleEditor;
|
||||
class SongGroupEditor;
|
||||
class SoundGroupEditor;
|
||||
class SoundMacroEditor;
|
||||
class StudioSetupWidget;
|
||||
class VoiceAllocator;
|
||||
|
||||
class QProgressDialog;
|
||||
class QUndoStack;
|
||||
|
||||
enum BackgroundTaskId { TaskOpen, TaskImport, TaskExport, TaskReloadSamples };
|
||||
|
||||
class BackgroundTask : public QObject {
|
||||
Q_OBJECT
|
||||
int m_id;
|
||||
std::function<void(BackgroundTask&)> m_task;
|
||||
UIMessenger m_threadMessenger;
|
||||
bool m_cancelled = false;
|
||||
|
||||
public:
|
||||
explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
|
||||
: m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
|
||||
bool isCanceled() const {
|
||||
QCoreApplication::processEvents();
|
||||
return m_cancelled;
|
||||
}
|
||||
UIMessenger& uiMessenger() { return m_threadMessenger; }
|
||||
|
||||
signals:
|
||||
void setMinimum(int minimum);
|
||||
void setMaximum(int maximum);
|
||||
void setValue(int value);
|
||||
void setLabelText(const QString& text);
|
||||
void finished(int id);
|
||||
|
||||
public slots:
|
||||
void run() {
|
||||
m_task(*this);
|
||||
emit finished(m_id);
|
||||
}
|
||||
void cancel() { m_cancelled = true; }
|
||||
};
|
||||
|
||||
class TreeDelegate : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
MainWindow& m_window;
|
||||
|
||||
public:
|
||||
explicit TreeDelegate(MainWindow& window, QObject* parent = Q_NULLPTR)
|
||||
: QStyledItemDelegate(parent), m_window(window) {}
|
||||
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) override;
|
||||
public slots:
|
||||
void doExportGroup();
|
||||
void doFindUsages();
|
||||
void doCut();
|
||||
void doCopy();
|
||||
void doPaste();
|
||||
void doDuplicate();
|
||||
void doDelete();
|
||||
void doRename();
|
||||
};
|
||||
|
||||
class MainWindow : public QMainWindow {
|
||||
friend class MIDIReader;
|
||||
friend class ProjectModel;
|
||||
friend class GroupNodeUndoCommand;
|
||||
friend class TreeDelegate;
|
||||
Q_OBJECT
|
||||
Ui::MainWindow m_ui;
|
||||
QAction* m_goBack;
|
||||
QAction* m_goForward;
|
||||
std::list<ProjectModel::INode*> m_navList;
|
||||
std::list<ProjectModel::INode*>::iterator m_navIt;
|
||||
QAction* m_clearRecentFileAct;
|
||||
QAction* m_recentFileActs[MaxRecentFiles];
|
||||
TreeDelegate m_treeDelegate;
|
||||
UIMessenger m_mainMessenger;
|
||||
ProjectModel* m_projectModel = nullptr;
|
||||
QWidget* m_faceSvg;
|
||||
SongGroupEditor* m_songGroupEditor = nullptr;
|
||||
SoundGroupEditor* m_soundGroupEditor = nullptr;
|
||||
SoundMacroEditor* m_soundMacroEditor = nullptr;
|
||||
ADSREditor* m_adsrEditor = nullptr;
|
||||
CurveEditor* m_curveEditor = nullptr;
|
||||
KeymapEditor* m_keymapEditor = nullptr;
|
||||
LayersEditor* m_layersEditor = nullptr;
|
||||
SampleEditor* m_sampleEditor = nullptr;
|
||||
StudioSetupWidget* m_studioSetup = nullptr;
|
||||
QFileDialog m_openDirectoryDialog;
|
||||
QFileDialog m_openFileDialog;
|
||||
QFileDialog m_newFileDialog;
|
||||
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
|
||||
std::unique_ptr<VoiceAllocator> m_voxAllocator;
|
||||
std::unique_ptr<amuse::Engine> m_engine;
|
||||
amuse::ObjToken<amuse::Voice> m_lastSound;
|
||||
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
|
||||
int m_velocity = 90;
|
||||
float m_pitch = 0.f;
|
||||
int8_t m_ctrlVals[128] = {};
|
||||
float m_auxAVol = 0.f;
|
||||
float m_auxBVol = 0.f;
|
||||
bool m_uiDisabled = false;
|
||||
bool m_clipboardAmuseData = false;
|
||||
|
||||
QUndoStack* m_undoStack;
|
||||
|
||||
QMetaObject::Connection m_cutConn;
|
||||
QMetaObject::Connection m_copyConn;
|
||||
QMetaObject::Connection m_pasteConn;
|
||||
QMetaObject::Connection m_deleteConn;
|
||||
QMetaObject::Connection m_canEditConn;
|
||||
|
||||
BackgroundTask* m_backgroundTask = nullptr;
|
||||
QProgressDialog* m_backgroundDialog = nullptr;
|
||||
QThread m_backgroundThread;
|
||||
|
||||
uint64_t m_timerFireCount = 0;
|
||||
int m_peakVoices = 0;
|
||||
|
||||
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
|
||||
|
||||
void updateWindowTitle();
|
||||
void updateRecentFileActions();
|
||||
void updateNavigationButtons();
|
||||
bool setProjectPath(const QString& path);
|
||||
void refreshAudioIO();
|
||||
void refreshMIDIIO();
|
||||
void timerEvent(QTimerEvent* ev) override;
|
||||
void setSustain(bool sustain);
|
||||
void keyPressEvent(QKeyEvent* ev) override;
|
||||
void keyReleaseEvent(QKeyEvent* ev) override;
|
||||
|
||||
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
|
||||
std::function<void(BackgroundTask&)>&& task);
|
||||
|
||||
bool _setEditor(EditorWidget* widget, bool appendNav = true);
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget* parent = Q_NULLPTR);
|
||||
~MainWindow() override;
|
||||
|
||||
bool openProject(const QString& path);
|
||||
|
||||
bool openEditor(ProjectModel::SongGroupNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::SoundGroupNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::SoundMacroNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::ADSRNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::CurveNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::KeymapNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::LayersNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::SampleNode* node, bool appendNav = true);
|
||||
bool openEditor(ProjectModel::INode* node, bool appendNav = true);
|
||||
void closeEditor();
|
||||
|
||||
ProjectModel::INode* getEditorNode() const;
|
||||
EditorWidget* getEditorWidget() const;
|
||||
amuse::ObjToken<amuse::Voice> startEditorVoice(uint8_t key, uint8_t vel);
|
||||
amuse::ObjToken<amuse::Voice> startSFX(amuse::GroupId groupId, amuse::SFXId sfxId);
|
||||
amuse::ObjToken<amuse::Sequencer> startSong(amuse::GroupId groupId, amuse::SongId songId,
|
||||
const unsigned char* arrData);
|
||||
void pushUndoCommand(EditorUndoCommand* cmd);
|
||||
void updateFocus();
|
||||
void aboutToDeleteNode(ProjectModel::INode* node);
|
||||
bool askAboutSave();
|
||||
void closeEvent(QCloseEvent* ev) override;
|
||||
void showEvent(QShowEvent* ev) override;
|
||||
|
||||
QString getGroupName(ProjectModel::GroupNode* group) const;
|
||||
ProjectModel::GroupNode* getSelectedGroupNode() const;
|
||||
QString getSelectedGroupName() const;
|
||||
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
|
||||
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
|
||||
|
||||
ProjectModel* projectModel() const { return m_projectModel; }
|
||||
UIMessenger& uiMessenger() { return m_mainMessenger; }
|
||||
|
||||
void setItemEditFlags(AmuseItemEditFlags flags);
|
||||
void setItemNewEnabled(bool enabled);
|
||||
AmuseItemEditFlags outlineEditFlags();
|
||||
bool isUiDisabled() const { return m_uiDisabled; }
|
||||
void findUsages(ProjectModel::INode* node);
|
||||
|
||||
public slots:
|
||||
void newAction();
|
||||
void _newAction(const QString& path);
|
||||
void openAction();
|
||||
void _openAction(const QString& path);
|
||||
void openRecentFileAction();
|
||||
void clearRecentFilesAction();
|
||||
void saveAction();
|
||||
void revertAction();
|
||||
void reloadSampleDataAction();
|
||||
void importAction();
|
||||
void _importAction(const QString& path);
|
||||
void importSongsAction();
|
||||
void _importSongsAction(const QString& path);
|
||||
void exportAction();
|
||||
void importHeadersAction();
|
||||
void _importHeadersAction(const QString& path);
|
||||
void exportHeadersAction();
|
||||
void _exportHeadersAction(const QString& path);
|
||||
|
||||
void newSubprojectAction();
|
||||
void newSFXGroupAction();
|
||||
void newSongGroupAction();
|
||||
void newSoundMacroAction();
|
||||
void newADSRAction();
|
||||
void newCurveAction();
|
||||
void newKeymapAction();
|
||||
void newLayersAction();
|
||||
|
||||
void goForward();
|
||||
void goBack();
|
||||
|
||||
void aboutToShowAudioIOMenu();
|
||||
void aboutToShowMIDIIOMenu();
|
||||
|
||||
void setAudioIO();
|
||||
void setMIDIIO(bool checked);
|
||||
|
||||
void aboutAmuseAction();
|
||||
void aboutQtAction();
|
||||
|
||||
void notePressed(int key);
|
||||
void noteReleased();
|
||||
void velocityChanged(int vel);
|
||||
void modulationChanged(int mod);
|
||||
void pitchChanged(int pitch);
|
||||
void killSounds();
|
||||
void fxPressed();
|
||||
void volumeChanged(int vol);
|
||||
void auxAChanged(int vol);
|
||||
void auxBChanged(int vol);
|
||||
|
||||
void outlineCutAction();
|
||||
void outlineCopyAction();
|
||||
void outlinePasteAction();
|
||||
void outlineDeleteAction();
|
||||
|
||||
void onFocusChanged(QWidget* old, QWidget* now);
|
||||
void onClipboardChanged();
|
||||
void outlineItemActivated(const QModelIndex& index);
|
||||
void onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
|
||||
void onTextSelect();
|
||||
void onTextDelete();
|
||||
void cleanChanged(bool clean);
|
||||
|
||||
void studioSetupHidden();
|
||||
void studioSetupShown();
|
||||
|
||||
void onBackgroundTaskFinished(int id);
|
||||
|
||||
QMessageBox::StandardButton msgInformation(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
int msgQuestion(const QString& title, const QString& text, const QString& button0Text,
|
||||
const QString& button1Text = QString(), const QString& button2Text = QString(),
|
||||
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
|
||||
QMessageBox::StandardButton
|
||||
msgQuestion(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
QMessageBox::StandardButton msgWarning(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
QMessageBox::StandardButton msgCritical(const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
};
|
|
@ -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>&File</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuRecent_Projects">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Recent &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&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>&Audio</string>
|
||||
</property>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionSelect_Output_Device"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuMIDI">
|
||||
<property name="title">
|
||||
<string>&MIDI</string>
|
||||
</property>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionInput_Device"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuEdit">
|
||||
<property name="title">
|
||||
<string>&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>&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>&New Project</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpen_Project">
|
||||
<property name="icon">
|
||||
<iconset theme="document-open"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&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>&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&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>&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>&Delete</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Groups">
|
||||
<property name="text">
|
||||
<string>&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&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&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 &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 &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 &Layers</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSelect_Output_Device">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Output Device:</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionInput_Device">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Input Device:</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_GameCube_Groups">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&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>&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 &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 &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>&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>&Revert Project</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReload_Sample_Data">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reload Sample &Data</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Songs">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>I&mport Songs</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout_Amuse">
|
||||
<property name="icon">
|
||||
<iconset theme="help-about"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&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 &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 &C Headers</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQuit">
|
||||
<property name="icon">
|
||||
<iconset theme="application-exit"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&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 &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>
|
|
@ -0,0 +1,159 @@
|
|||
#include "NewSoundMacroDialog.hpp"
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include "amuse/Common.hpp"
|
||||
|
||||
static const uint32_t BasicMacro[] = {
|
||||
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF,
|
||||
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t Looped[] = {
|
||||
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x38000000,
|
||||
0xE8030000, 0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t LoopRelease[] = {
|
||||
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001FA00, 0x07010000, 0x0000FFFF,
|
||||
0x3000D08A, 0x00000000, 0x0F500000, 0x00013200, 0x07000000, 0x00003200, 0x38000000, 0xE8030000,
|
||||
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t LoopSoftRelease[] = {
|
||||
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000, 0x0000FFFF,
|
||||
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
|
||||
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t LoopSoftReleaseNoClick[] = {
|
||||
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000,
|
||||
0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
|
||||
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t LoopADSR[] = {
|
||||
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
|
||||
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x12000000, 0x00000000, 0x07000001, 0x0000E803,
|
||||
0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t LoopADSRSoftRelease[] = {
|
||||
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
|
||||
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00017800, 0x07000001, 0x00007800,
|
||||
0x12000000, 0x00000000, 0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t LoopHold[] = {
|
||||
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010001, 0x0000FFFF,
|
||||
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000001, 0x00006400, 0x38000000, 0xE8030000,
|
||||
0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t OneShot[] = {
|
||||
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000001,
|
||||
0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t OneShotFixedNote[] = {
|
||||
0x38000000, 0xC07A1000, 0x193C0000, 0x00010000, 0x10000000, 0x00000000,
|
||||
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t OneShotNoClick[] = {
|
||||
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000,
|
||||
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t OneShotFixedNoClick[] = {
|
||||
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x193C0000, 0x00010000, 0x10000000,
|
||||
0x00000000, 0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t Bubbles[] = {
|
||||
0x38000000, 0xC07A1000, 0x0D600000, 0x00010000, 0x10000000, 0x00000000, 0x1D08E803,
|
||||
0x00010000, 0x1E0544FD, 0x00010000, 0x0F000000, 0x0001F401, 0x07000000, 0x0000F401,
|
||||
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t DownTrigger[] = {
|
||||
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000000, 0x00001200, 0x11000000, 0x00000000,
|
||||
0x07000000, 0x00000100, 0x18FE0000, 0x00010000, 0x05000000, 0x01000C00, 0x0F000000, 0x00016400,
|
||||
0x07000001, 0x00006400, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t LongFadeInAndStop[] = {
|
||||
0x38000000, 0xC07A1000, 0x147F0000, 0x0001E803, 0x10000000, 0x00000000, 0x07000001,
|
||||
0x0000E803, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t FadeInAndStop[] = {
|
||||
0x38000000, 0xC07A1000, 0x147F0000, 0x0001C800, 0x10000000, 0x00000000, 0x07000001,
|
||||
0x0000C800, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t RandomTrigger[] = {
|
||||
0x38000000, 0xC07A1000, 0x0F000000, 0x0001F401, 0x195A0000, 0x00010000, 0x10000000,
|
||||
0x00000000, 0x1750005A, 0x01000000, 0x07000001, 0x00002300, 0x05000000, 0x03001400,
|
||||
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const uint32_t SimplePlaySample[] = {
|
||||
0x10000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
static const SoundMacroTemplateEntry Entries[] = {
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Basic Macro"), sizeof(BasicMacro), BasicMacro},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Looped"), sizeof(Looped), Looped},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Release"), sizeof(LoopRelease), LoopRelease},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release"), sizeof(LoopSoftRelease), LoopSoftRelease},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release No Click"), sizeof(LoopSoftReleaseNoClick),
|
||||
LoopSoftReleaseNoClick},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR"), sizeof(LoopADSR), LoopADSR},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR Soft Release"), sizeof(LoopADSRSoftRelease),
|
||||
LoopADSRSoftRelease},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Hold"), sizeof(LoopHold), LoopHold},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot"), sizeof(OneShot), OneShot},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed Note"), sizeof(OneShotFixedNote), OneShotFixedNote},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot No Click"), sizeof(OneShotNoClick), OneShotNoClick},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed No Click"), sizeof(OneShotFixedNoClick),
|
||||
OneShotFixedNoClick},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Bubbles"), sizeof(Bubbles), Bubbles},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Down Trigger"), sizeof(DownTrigger), DownTrigger},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Long Fade in and Stop"), sizeof(LongFadeInAndStop), LongFadeInAndStop},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Fade in and Stop"), sizeof(FadeInAndStop), FadeInAndStop},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Random Trigger"), sizeof(RandomTrigger), RandomTrigger},
|
||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Simple Play Sample"), sizeof(SimplePlaySample), SimplePlaySample}};
|
||||
|
||||
const SoundMacroTemplateEntry* NewSoundMacroDialog::getSelectedTemplate() const {
|
||||
return &Entries[m_combo.currentIndex()];
|
||||
}
|
||||
|
||||
NewSoundMacroDialog::NewSoundMacroDialog(const QString& groupName, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_le(QString::fromStdString(amuse::SoundMacroId::CurNameDB->generateDefaultName(amuse::NameDB::Type::SoundMacro)))
|
||||
, m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal) {
|
||||
setWindowTitle(tr("New Sound Macro"));
|
||||
|
||||
int idx = 0;
|
||||
for (const auto& ent : Entries)
|
||||
m_combo.addItem(tr(ent.m_name), idx++);
|
||||
m_combo.setCurrentIndex(0);
|
||||
|
||||
connect(&m_buttonBox, &QDialogButtonBox::accepted, this, &NewSoundMacroDialog::accept);
|
||||
connect(&m_buttonBox, &QDialogButtonBox::rejected, this, &NewSoundMacroDialog::reject);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
|
||||
layout->addWidget(new QLabel(tr("What should the new macro in %1 be named?").arg(groupName)));
|
||||
layout->addWidget(&m_le);
|
||||
layout->addWidget(new QLabel(tr("Sound Macro Template")));
|
||||
layout->addWidget(&m_combo);
|
||||
layout->addWidget(&m_buttonBox);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
NewSoundMacroDialog::~NewSoundMacroDialog() = default;
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLineEdit>
|
||||
|
||||
struct SoundMacroTemplateEntry {
|
||||
const char* m_name;
|
||||
size_t m_length;
|
||||
const uint32_t* m_data;
|
||||
};
|
||||
|
||||
class NewSoundMacroDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
QLineEdit m_le;
|
||||
QComboBox m_combo;
|
||||
QDialogButtonBox m_buttonBox;
|
||||
|
||||
public:
|
||||
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
|
||||
~NewSoundMacroDialog() override;
|
||||
|
||||
QString getName() const { return m_le.text(); }
|
||||
const SoundMacroTemplateEntry* getSelectedTemplate() const;
|
||||
};
|
|
@ -0,0 +1,486 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QDir>
|
||||
#include <QIcon>
|
||||
#include <QIdentityProxyModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "Common.hpp"
|
||||
|
||||
#include <amuse/AudioGroup.hpp>
|
||||
#include <amuse/AudioGroupData.hpp>
|
||||
#include <amuse/AudioGroupProject.hpp>
|
||||
#include <amuse/AudioGroupPool.hpp>
|
||||
#include <amuse/AudioGroupSampleDirectory.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class EditorUndoCommand;
|
||||
class ProjectModel;
|
||||
|
||||
struct SoundMacroTemplateEntry;
|
||||
|
||||
enum AmuseItemEditFlags {
|
||||
AmuseItemNone = 0,
|
||||
AmuseItemCut = 1,
|
||||
AmuseItemCopy = 2,
|
||||
AmuseItemPaste = 4,
|
||||
AmuseItemDelete = 8,
|
||||
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
|
||||
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
|
||||
};
|
||||
|
||||
class OutlineFilterProxyModel : public QSortFilterProxyModel {
|
||||
Q_OBJECT
|
||||
QRegularExpression m_usageKey;
|
||||
|
||||
public:
|
||||
explicit OutlineFilterProxyModel(ProjectModel* source);
|
||||
public slots:
|
||||
void setFilterRegExp(const QString& pattern);
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
|
||||
};
|
||||
|
||||
class NullItemProxyModel : public QIdentityProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NullItemProxyModel(ProjectModel* source);
|
||||
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override;
|
||||
QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
||||
QVariant data(const QModelIndex& proxyIndex, int role) const override;
|
||||
};
|
||||
|
||||
class PageObjectProxyModel : public QIdentityProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PageObjectProxyModel(ProjectModel* source);
|
||||
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override;
|
||||
QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
||||
QVariant data(const QModelIndex& proxyIndex, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const override;
|
||||
};
|
||||
|
||||
class ProjectModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class ImportMode { Original, WAVs, Both };
|
||||
|
||||
struct NameUndoRegistry {
|
||||
std::unordered_map<amuse::SongId, std::string> m_songIDs;
|
||||
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
|
||||
void registerSongName(amuse::SongId id) const;
|
||||
void unregisterSongName(amuse::SongId id);
|
||||
void registerSFXName(amuse::SongId id) const;
|
||||
void unregisterSFXName(amuse::SongId id);
|
||||
void clear() {
|
||||
m_songIDs.clear();
|
||||
m_sfxIDs.clear();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
QDir m_dir;
|
||||
amuse::ProjectDatabase m_projectDatabase;
|
||||
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>> m_groups;
|
||||
|
||||
struct Song {
|
||||
QString m_path;
|
||||
int m_refCount = 0;
|
||||
};
|
||||
std::unordered_map<amuse::SongId, Song> m_midiFiles;
|
||||
|
||||
public:
|
||||
class INode : public amuse::IObj {
|
||||
friend class ProjectModel;
|
||||
virtual void _sortChildren();
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
Null,
|
||||
Root,
|
||||
Group, // Top-level group
|
||||
SongGroup,
|
||||
SoundGroup,
|
||||
Collection, // Classified object collection, one of the following:
|
||||
SoundMacro,
|
||||
ADSR,
|
||||
Curve,
|
||||
Keymap,
|
||||
Layer,
|
||||
Sample
|
||||
};
|
||||
|
||||
protected:
|
||||
QString m_name;
|
||||
INode* m_parent = nullptr;
|
||||
int m_row = -1;
|
||||
std::vector<amuse::IObjToken<INode>> m_children;
|
||||
amuse::IObjToken<INode> m_nullChild;
|
||||
|
||||
public:
|
||||
~INode() override = default;
|
||||
explicit INode(QString name);
|
||||
|
||||
// ONLY USED BY NULL NODE!
|
||||
explicit INode(INode* parent) : m_parent(parent), m_row(0) {}
|
||||
|
||||
int childCount() const { return int(m_children.size()); }
|
||||
INode* child(int row) const {
|
||||
if (row == m_children.size())
|
||||
return nullChild();
|
||||
return m_children[row].get();
|
||||
}
|
||||
INode* nullChild() const { return m_nullChild.get(); }
|
||||
INode* parent() const { return m_parent; }
|
||||
int row() const { return m_row; }
|
||||
|
||||
void reindexRows(int row) {
|
||||
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
|
||||
(*it)->m_row = row++;
|
||||
m_nullChild->m_row = row;
|
||||
}
|
||||
|
||||
void insertChild(amuse::ObjToken<INode> n) {
|
||||
assert(n->m_parent == nullptr && "Inserting already-parented node");
|
||||
n->m_parent = this;
|
||||
int row = hypotheticalIndex(n->name());
|
||||
m_children.insert(m_children.begin() + row, n.get());
|
||||
reindexRows(row);
|
||||
}
|
||||
amuse::ObjToken<INode> removeChild(INode* n) {
|
||||
amuse::ObjToken<INode> ret = n;
|
||||
int row = ret->row();
|
||||
assert(ret.get() == m_children.at(row).get() && "Removing non-child from node");
|
||||
m_children.erase(m_children.begin() + row);
|
||||
reindexRows(row);
|
||||
ret->m_parent = nullptr;
|
||||
ret->m_row = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void reserve(size_t sz) { m_children.reserve(sz); }
|
||||
template <class T, class... _Args>
|
||||
T& makeChild(_Args&&... args) {
|
||||
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
|
||||
insertChild(tok.get());
|
||||
return static_cast<T&>(*tok);
|
||||
}
|
||||
template <class T, class... _Args>
|
||||
T& _appendChild(_Args&&... args) {
|
||||
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
|
||||
tok->m_parent = this;
|
||||
tok->m_row = m_children.size();
|
||||
m_children.push_back(tok.get());
|
||||
m_nullChild->m_row = m_children.size();
|
||||
return static_cast<T&>(*tok);
|
||||
}
|
||||
|
||||
INode* findChild(const QString& name) const {
|
||||
int idx = hypotheticalIndex(name);
|
||||
if (idx >= m_children.size())
|
||||
return nullptr;
|
||||
INode* ret = m_children[idx].get();
|
||||
if (ret->name() == name)
|
||||
return ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool depthTraverse(const std::function<bool(INode* node)>& func) {
|
||||
for (auto& n : m_children)
|
||||
if (!n->depthTraverse(func))
|
||||
break;
|
||||
return func(this);
|
||||
}
|
||||
|
||||
bool oneLevelTraverse(const std::function<bool(INode* node)>& func) {
|
||||
for (auto& n : m_children)
|
||||
if (!func(n.get()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString& name() const { return m_name; }
|
||||
virtual int hypotheticalIndex(const QString& name) const;
|
||||
|
||||
virtual amuse::NameDB* getNameDb() const { return nullptr; }
|
||||
|
||||
virtual Type type() const = 0;
|
||||
virtual QString text() const = 0;
|
||||
virtual QIcon icon() const = 0;
|
||||
virtual Qt::ItemFlags flags() const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; }
|
||||
virtual AmuseItemEditFlags editFlags() const { return AmuseItemNone; }
|
||||
|
||||
virtual void registerNames(const NameUndoRegistry& registry) const {}
|
||||
virtual void unregisterNames(NameUndoRegistry& registry) const {}
|
||||
};
|
||||
struct NullNode final : INode {
|
||||
explicit NullNode(INode* parent) : INode(parent) {}
|
||||
|
||||
Type type() const override { return Type::Null; }
|
||||
QString text() const override { return {}; }
|
||||
QIcon icon() const override { return {}; }
|
||||
};
|
||||
struct RootNode final : INode {
|
||||
RootNode() : INode(QStringLiteral("<root>")) {}
|
||||
|
||||
Type type() const override { return Type::Root; }
|
||||
QString text() const override { return {}; }
|
||||
QIcon icon() const override { return {}; }
|
||||
Qt::ItemFlags flags() const override { return Qt::ItemIsEnabled; }
|
||||
};
|
||||
struct CollectionNode;
|
||||
struct BasePoolObjectNode;
|
||||
struct GroupNode final : INode {
|
||||
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
|
||||
explicit GroupNode(QString name) : INode(std::move(name)) {}
|
||||
explicit GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
|
||||
: INode(it->first), m_it(it) {}
|
||||
|
||||
int hypotheticalIndex(const QString& name) const override;
|
||||
void _sortChildren() override;
|
||||
|
||||
static QIcon Icon;
|
||||
Type type() const override { return Type::Group; }
|
||||
QString text() const override { return m_name; }
|
||||
QIcon icon() const override { return Icon; }
|
||||
AmuseItemEditFlags editFlags() const override { return AmuseItemNoCut; }
|
||||
|
||||
CollectionNode* getCollectionOfType(Type tp) const;
|
||||
amuse::AudioGroupDatabase* getAudioGroup() const { return m_it->second.get(); }
|
||||
BasePoolObjectNode* pageObjectNodeOfId(amuse::ObjectId id) const;
|
||||
};
|
||||
struct SongGroupNode final : INode {
|
||||
amuse::GroupId m_id;
|
||||
amuse::ObjToken<amuse::SongGroupIndex> m_index;
|
||||
explicit SongGroupNode(QString name, amuse::ObjToken<amuse::SongGroupIndex> index)
|
||||
: INode(std::move(name)), m_index(std::move(index)) {}
|
||||
explicit SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
|
||||
: INode(QString::fromUtf8(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()))
|
||||
, m_id(id)
|
||||
, m_index(std::move(index)) {}
|
||||
|
||||
static QIcon Icon;
|
||||
Type type() const override { return Type::SongGroup; }
|
||||
QString text() const override { return m_name; }
|
||||
QIcon icon() const override { return Icon; }
|
||||
AmuseItemEditFlags editFlags() const override { return AmuseItemAll; }
|
||||
|
||||
amuse::NameDB* getNameDb() const override { return amuse::GroupId::CurNameDB; }
|
||||
|
||||
void registerNames(const NameUndoRegistry& registry) const override {
|
||||
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
||||
for (auto& p : m_index->m_midiSetups)
|
||||
registry.registerSongName(p.first);
|
||||
}
|
||||
void unregisterNames(NameUndoRegistry& registry) const override {
|
||||
amuse::GroupId::CurNameDB->remove(m_id);
|
||||
for (auto& p : m_index->m_midiSetups)
|
||||
registry.unregisterSongName(p.first);
|
||||
}
|
||||
};
|
||||
struct SoundGroupNode final : INode {
|
||||
amuse::GroupId m_id;
|
||||
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
|
||||
explicit SoundGroupNode(QString name, amuse::ObjToken<amuse::SFXGroupIndex> index)
|
||||
: INode(std::move(name)), m_index(std::move(index)) {}
|
||||
explicit SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
|
||||
: INode(QString::fromUtf8(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()))
|
||||
, m_id(id)
|
||||
, m_index(std::move(index)) {}
|
||||
|
||||
static QIcon Icon;
|
||||
Type type() const override { return Type::SoundGroup; }
|
||||
QString text() const override { return m_name; }
|
||||
QIcon icon() const override { return Icon; }
|
||||
AmuseItemEditFlags editFlags() const override { return AmuseItemAll; }
|
||||
|
||||
amuse::NameDB* getNameDb() const override { return amuse::GroupId::CurNameDB; }
|
||||
|
||||
void registerNames(const NameUndoRegistry& registry) const override {
|
||||
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
||||
for (auto& p : m_index->m_sfxEntries)
|
||||
registry.registerSFXName(p.first);
|
||||
}
|
||||
void unregisterNames(NameUndoRegistry& registry) const override {
|
||||
amuse::GroupId::CurNameDB->remove(m_id);
|
||||
for (auto& p : m_index->m_sfxEntries)
|
||||
registry.unregisterSFXName(p.first);
|
||||
}
|
||||
};
|
||||
struct CollectionNode final : INode {
|
||||
QIcon m_icon;
|
||||
Type m_collectionType;
|
||||
explicit CollectionNode(QString name, QIcon icon, Type collectionType)
|
||||
: INode(std::move(name)), m_icon(std::move(icon)), m_collectionType(collectionType) {}
|
||||
|
||||
Type type() const override { return Type::Collection; }
|
||||
QString text() const override { return m_name; }
|
||||
QIcon icon() const override { return m_icon; }
|
||||
Qt::ItemFlags flags() const override { return Qt::ItemIsEnabled; }
|
||||
|
||||
Type collectionType() const { return m_collectionType; }
|
||||
int indexOfId(amuse::ObjectId id) const;
|
||||
amuse::ObjectId idOfIndex(int idx) const;
|
||||
BasePoolObjectNode* nodeOfIndex(int idx) const;
|
||||
BasePoolObjectNode* nodeOfId(amuse::ObjectId id) const;
|
||||
};
|
||||
struct BasePoolObjectNode : INode {
|
||||
amuse::ObjectId m_id;
|
||||
explicit BasePoolObjectNode(QString name) : INode(std::move(name)) {}
|
||||
explicit BasePoolObjectNode(amuse::ObjectId id, QString name) : INode(std::move(name)), m_id(id) {}
|
||||
amuse::ObjectId id() const { return m_id; }
|
||||
QString text() const override { return m_name; }
|
||||
QIcon icon() const override { return {}; }
|
||||
};
|
||||
template <class ID, class T, INode::Type TP>
|
||||
struct PoolObjectNode final : BasePoolObjectNode {
|
||||
amuse::ObjToken<T> m_obj;
|
||||
PoolObjectNode(QString name, amuse::ObjToken<T> obj) : BasePoolObjectNode(std::move(name)), m_obj(std::move(obj)) {}
|
||||
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
|
||||
: BasePoolObjectNode(id, QString::fromUtf8(ID::CurNameDB->resolveNameFromId(id).data())), m_obj(std::move(obj)) {}
|
||||
|
||||
Type type() const override { return TP; }
|
||||
AmuseItemEditFlags editFlags() const override { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
|
||||
|
||||
void registerNames(const NameUndoRegistry& registry) const override {
|
||||
ID::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
||||
}
|
||||
void unregisterNames(NameUndoRegistry& registry) const override { ID::CurNameDB->remove(m_id); }
|
||||
amuse::NameDB* getNameDb() const override { return ID::CurNameDB; }
|
||||
};
|
||||
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
|
||||
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
|
||||
using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
|
||||
using KeymapNode = PoolObjectNode<amuse::KeymapId, std::array<amuse::Keymap, 128>, INode::Type::Keymap>;
|
||||
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
|
||||
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
|
||||
|
||||
amuse::ObjToken<RootNode> m_root;
|
||||
|
||||
bool m_needsReset = false;
|
||||
void _buildGroupNodeCollections(GroupNode& gn);
|
||||
void _buildGroupNode(GroupNode& gn, amuse::AudioGroup& group);
|
||||
void _resetModelData();
|
||||
void _resetSongRefCount();
|
||||
QString MakeDedupedSubprojectName(const QString& origName);
|
||||
static QString MakeDedupedName(const QString& origName, amuse::NameDB* db);
|
||||
|
||||
private:
|
||||
OutlineFilterProxyModel m_outlineProxy;
|
||||
NullItemProxyModel m_nullProxy;
|
||||
PageObjectProxyModel m_pageObjectProxy;
|
||||
|
||||
public:
|
||||
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
|
||||
~ProjectModel() override;
|
||||
|
||||
bool clearProjectData();
|
||||
bool openGroupData(QString groupName, UIMessenger& messenger);
|
||||
void openSongsData();
|
||||
void importSongsData(const QString& path);
|
||||
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
|
||||
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data, ImportMode mode,
|
||||
UIMessenger& messenger);
|
||||
void saveSongsIndex();
|
||||
bool saveToFile(UIMessenger& messenger);
|
||||
QStringList getGroupList() const;
|
||||
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
|
||||
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
|
||||
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
|
||||
|
||||
void updateNodeNames();
|
||||
bool ensureModelData();
|
||||
|
||||
QModelIndex proxyCreateIndex(int arow, int acolumn, void* adata) const;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
QModelIndex index(INode* node) const;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
INode* node(const QModelIndex& index) const;
|
||||
GroupNode* getGroupNode(INode* node) const;
|
||||
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
|
||||
RootNode* rootNode() const { return m_root.get(); }
|
||||
|
||||
void _postAddNode(INode* n, const NameUndoRegistry& registry);
|
||||
void _preDelNode(INode* n, NameUndoRegistry& registry);
|
||||
void _addNode(GroupNode* node, std::unique_ptr<amuse::AudioGroupDatabase>&& data, const NameUndoRegistry& registry);
|
||||
std::unique_ptr<amuse::AudioGroupDatabase> _delNode(GroupNode* node, NameUndoRegistry& registry);
|
||||
GroupNode* newSubproject(QString name);
|
||||
template <class NT, class T>
|
||||
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
|
||||
template <class NT, class T>
|
||||
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
|
||||
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||
SoundGroupNode* newSoundGroup(GroupNode* group, QString name);
|
||||
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||
SongGroupNode* newSongGroup(GroupNode* group, QString name);
|
||||
template <class NT, class T>
|
||||
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
|
||||
template <class NT, class T>
|
||||
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
|
||||
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||
SoundMacroNode* newSoundMacro(GroupNode* group, QString name, const SoundMacroTemplateEntry* templ = nullptr);
|
||||
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||
ADSRNode* newADSR(GroupNode* group, QString name);
|
||||
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||
CurveNode* newCurve(GroupNode* group, QString name);
|
||||
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||
KeymapNode* newKeymap(GroupNode* group, QString name);
|
||||
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||
LayersNode* newLayers(GroupNode* group, QString name);
|
||||
void _renameNode(INode* node, const QString& name);
|
||||
|
||||
template <class NT>
|
||||
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
|
||||
template <class NT>
|
||||
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
|
||||
|
||||
QStringList mimeTypes() const override;
|
||||
QMimeData* mimeData(const QModelIndexList& indexes) const override;
|
||||
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
|
||||
const QModelIndex& parent) override;
|
||||
|
||||
void cut(const QModelIndex& index);
|
||||
void copy(const QModelIndex& index);
|
||||
void paste(const QModelIndex& index);
|
||||
QModelIndex duplicate(const QModelIndex& index);
|
||||
void del(const QModelIndex& index);
|
||||
|
||||
const QDir& dir() const { return m_dir; }
|
||||
QString path() const { return m_dir.path(); }
|
||||
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
|
||||
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
|
||||
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
|
||||
|
||||
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
|
||||
QString getMIDIPathOfSong(amuse::SongId id) const;
|
||||
void setMIDIPathOfSong(amuse::SongId id, QString path);
|
||||
std::pair<amuse::SongId, std::string> bootstrapSongId();
|
||||
void allocateSongId(amuse::SongId id, std::string_view name);
|
||||
void deallocateSongId(amuse::SongId oldId);
|
||||
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
|
||||
|
||||
void setIdDatabases(INode* context) const;
|
||||
};
|
|
@ -0,0 +1,784 @@
|
|||
#include "SampleEditor.hpp"
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
#include <amuse/DSPCodec.hpp>
|
||||
|
||||
SampleEditor* SampleView::getEditor() const {
|
||||
return qobject_cast<SampleEditor*>(parentWidget()->parentWidget()->parentWidget());
|
||||
}
|
||||
|
||||
void SampleView::seekToSample(qreal sample) {
|
||||
sample = std::min(sample, qreal(m_sample->getNumSamples()));
|
||||
|
||||
if (m_sample->isFormatDSP()) {
|
||||
if (sample < m_curSamplePos) {
|
||||
m_prev1 = m_prev2 = 0;
|
||||
m_curSamplePos = 0.0;
|
||||
}
|
||||
|
||||
uint32_t startBlock = uint32_t(m_curSamplePos) / 14;
|
||||
uint32_t startRem = uint32_t(m_curSamplePos) % 14;
|
||||
uint32_t endBlock = uint32_t(sample) / 14;
|
||||
uint32_t endRem = uint32_t(sample) % 14;
|
||||
|
||||
if (startRem) {
|
||||
uint32_t end = 14;
|
||||
if (startBlock == endBlock) {
|
||||
end = endRem;
|
||||
endRem = 0;
|
||||
}
|
||||
DSPDecompressFrameRangedStateOnly(m_sampleData + 8 * startBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
|
||||
&m_prev2, startRem, end);
|
||||
if (end == 14)
|
||||
++startBlock;
|
||||
}
|
||||
|
||||
for (uint32_t b = startBlock; b < endBlock; ++b) {
|
||||
DSPDecompressFrameStateOnly(m_sampleData + 8 * b, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, 14);
|
||||
}
|
||||
|
||||
if (endRem) {
|
||||
DSPDecompressFrameStateOnly(m_sampleData + 8 * endBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2,
|
||||
endRem);
|
||||
}
|
||||
}
|
||||
|
||||
m_curSamplePos = sample;
|
||||
}
|
||||
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> SampleView::iterateSampleInterval(qreal interval) {
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> ret = {{0.0, 1.0}, {0.0, -1.0}}; // min,max avg,peak
|
||||
qreal avg = 0.0;
|
||||
qreal div = 0.0;
|
||||
qreal endSample = std::min(m_curSamplePos + interval, qreal(m_sample->getNumSamples()));
|
||||
|
||||
auto accumulate = [&ret, &avg, &div](int16_t sample) {
|
||||
qreal sampleF = sample / 32768.0;
|
||||
avg += sampleF;
|
||||
div += 1.0;
|
||||
ret.first.second = std::min(ret.first.second, sampleF);
|
||||
ret.second.second = std::max(ret.second.second, sampleF);
|
||||
};
|
||||
|
||||
if (m_sample->isFormatDSP()) {
|
||||
uint32_t startBlock = uint32_t(m_curSamplePos) / 14;
|
||||
uint32_t startRem = uint32_t(m_curSamplePos) % 14;
|
||||
uint32_t endBlock = uint32_t(endSample) / 14;
|
||||
uint32_t endRem = uint32_t(endSample) % 14;
|
||||
|
||||
int16_t sampleBlock[14];
|
||||
|
||||
if (startRem) {
|
||||
uint32_t end = 14;
|
||||
if (startBlock == endBlock) {
|
||||
end = endRem;
|
||||
endRem = 0;
|
||||
}
|
||||
DSPDecompressFrameRanged(sampleBlock, m_sampleData + 8 * startBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
|
||||
&m_prev2, startRem, end);
|
||||
for (uint32_t s = 0; s < end - startRem; ++s)
|
||||
accumulate(sampleBlock[s]);
|
||||
if (end == 14)
|
||||
++startBlock;
|
||||
}
|
||||
|
||||
for (uint32_t b = startBlock; b < endBlock; ++b) {
|
||||
DSPDecompressFrame(sampleBlock, m_sampleData + 8 * b, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, 14);
|
||||
for (uint32_t s = 0; s < 14; ++s)
|
||||
accumulate(sampleBlock[s]);
|
||||
}
|
||||
|
||||
if (endRem) {
|
||||
DSPDecompressFrame(sampleBlock, m_sampleData + 8 * endBlock, m_sample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
|
||||
&m_prev2, endRem);
|
||||
for (uint32_t s = 0; s < endRem; ++s)
|
||||
accumulate(sampleBlock[s]);
|
||||
}
|
||||
} else if (m_sample->getSampleFormat() == amuse::SampleFormat::PCM_PC) {
|
||||
for (uint32_t s = uint32_t(m_curSamplePos); s < uint32_t(endSample); ++s)
|
||||
accumulate(reinterpret_cast<const int16_t*>(m_sampleData)[s]);
|
||||
}
|
||||
|
||||
m_curSamplePos = endSample;
|
||||
|
||||
if (div == 0.0)
|
||||
return {};
|
||||
|
||||
avg /= div;
|
||||
|
||||
if (avg > 0.0) {
|
||||
ret.first.first = ret.first.second;
|
||||
ret.second.first = avg;
|
||||
} else {
|
||||
ret.first.first = avg;
|
||||
ret.second.first = ret.second.second;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SampleView::paintEvent(QPaintEvent* ev) {
|
||||
QPainter painter(this);
|
||||
|
||||
constexpr int rulerHeight = 28;
|
||||
int sampleHeight = height() - rulerHeight;
|
||||
|
||||
qreal deviceRatio = devicePixelRatioF();
|
||||
qreal rectStart = ev->rect().x();
|
||||
qreal deviceWidth = ev->rect().width() * deviceRatio;
|
||||
qreal increment = 1.0 / deviceRatio;
|
||||
qreal deviceSamplesPerPx = m_samplesPerPx / deviceRatio;
|
||||
qreal startSample = rectStart * m_samplesPerPx;
|
||||
qreal startSampleRem = std::fmod(startSample, deviceSamplesPerPx);
|
||||
if (startSampleRem > DBL_EPSILON) {
|
||||
startSample -= startSampleRem;
|
||||
deviceWidth += startSampleRem;
|
||||
rectStart = startSample / m_samplesPerPx;
|
||||
}
|
||||
|
||||
if (m_sample) {
|
||||
QPen peakPen(QColor(255, 147, 41), increment);
|
||||
QPen avgPen(QColor(254, 177, 68), increment);
|
||||
|
||||
qreal scale = -sampleHeight / 2.0;
|
||||
qreal trans = sampleHeight / 2.0;
|
||||
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> lastAvgPeak;
|
||||
if (startSample >= deviceSamplesPerPx) {
|
||||
seekToSample(startSample - deviceSamplesPerPx);
|
||||
lastAvgPeak = iterateSampleInterval(deviceSamplesPerPx);
|
||||
} else {
|
||||
seekToSample(startSample);
|
||||
lastAvgPeak = std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>>{};
|
||||
}
|
||||
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> avgPeak;
|
||||
for (qreal i = 0.0; i < deviceWidth; i += increment) {
|
||||
if (m_curSamplePos + deviceSamplesPerPx > m_sample->getNumSamples())
|
||||
break;
|
||||
if (i == 0.0 || std::floor(m_curSamplePos) != std::floor(m_curSamplePos + deviceSamplesPerPx))
|
||||
avgPeak = iterateSampleInterval(deviceSamplesPerPx);
|
||||
else
|
||||
m_curSamplePos += deviceSamplesPerPx;
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> drawAvgPeak = avgPeak;
|
||||
if (lastAvgPeak.first.first > avgPeak.second.first)
|
||||
drawAvgPeak.second.first = lastAvgPeak.first.first;
|
||||
else if (lastAvgPeak.second.first < avgPeak.first.first)
|
||||
drawAvgPeak.first.first = lastAvgPeak.second.first;
|
||||
painter.setPen(peakPen);
|
||||
painter.drawLine(QPointF(rectStart + i, drawAvgPeak.first.second * scale + trans),
|
||||
QPointF(rectStart + i, drawAvgPeak.second.second * scale + trans));
|
||||
painter.setPen(avgPen);
|
||||
painter.drawLine(QPointF(rectStart + i, drawAvgPeak.first.first * scale + trans),
|
||||
QPointF(rectStart + i, drawAvgPeak.second.first * scale + trans));
|
||||
lastAvgPeak = avgPeak;
|
||||
}
|
||||
}
|
||||
|
||||
painter.setBrush(palette().brush(QPalette::Base));
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(QRect(ev->rect().x(), sampleHeight, ev->rect().width(), rulerHeight));
|
||||
|
||||
constexpr qreal minNumSpacing = 40.0;
|
||||
|
||||
qreal binaryDiv = 1.0;
|
||||
while (m_samplesPerPx * minNumSpacing > binaryDiv)
|
||||
binaryDiv *= 2.0;
|
||||
|
||||
qreal numSpacing = binaryDiv / m_samplesPerPx;
|
||||
qreal tickSpacing = numSpacing / 5;
|
||||
|
||||
int startX = std::max(0, int(ev->rect().x() - numSpacing));
|
||||
int lastNumDiv = int(startX / numSpacing);
|
||||
int lastTickDiv = int(startX / tickSpacing);
|
||||
|
||||
qreal samplePos = startX * m_samplesPerPx;
|
||||
painter.setFont(m_rulerFont);
|
||||
painter.setPen(QPen(QColor(127, 127, 127), increment));
|
||||
for (int i = startX; i < ev->rect().x() + ev->rect().width() + numSpacing; ++i) {
|
||||
int thisNumDiv = int(i / numSpacing);
|
||||
int thisTickDiv = int(i / tickSpacing);
|
||||
|
||||
if (thisNumDiv != lastNumDiv) {
|
||||
painter.drawLine(i, sampleHeight + 1, i, sampleHeight + 8);
|
||||
lastNumDiv = thisNumDiv;
|
||||
lastTickDiv = thisTickDiv;
|
||||
|
||||
painter.drawText(QRectF(i - numSpacing / 2.0, sampleHeight + 11.0, numSpacing, 12.0),
|
||||
QString::number(int(samplePos)), QTextOption(Qt::AlignCenter));
|
||||
} else if (thisTickDiv != lastTickDiv) {
|
||||
painter.drawLine(i, sampleHeight + 1, i, sampleHeight + 5);
|
||||
lastTickDiv = thisTickDiv;
|
||||
}
|
||||
|
||||
samplePos += m_samplesPerPx;
|
||||
}
|
||||
|
||||
if (m_sample) {
|
||||
if (m_sample->isLooped()) {
|
||||
int loopStart = m_sample->m_loopStartSample;
|
||||
int loopEnd = loopStart + m_sample->m_loopLengthSamples - 1;
|
||||
QPointF points[4];
|
||||
|
||||
painter.setPen(QPen(Qt::green, increment));
|
||||
painter.setBrush(Qt::green);
|
||||
qreal startPos = loopStart / m_samplesPerPx;
|
||||
painter.drawLine(QPointF(startPos, 0.0), QPointF(startPos, height()));
|
||||
points[0] = QPointF(startPos, height() - 6.0);
|
||||
points[1] = QPointF(startPos - 6.0, height());
|
||||
points[2] = QPointF(startPos + 6.0, height());
|
||||
points[3] = QPointF(startPos, height() - 6.0);
|
||||
painter.drawPolygon(points, 4);
|
||||
|
||||
painter.setPen(QPen(Qt::red, increment));
|
||||
painter.setBrush(Qt::red);
|
||||
qreal endPos = loopEnd / m_samplesPerPx;
|
||||
painter.drawLine(QPointF(endPos, 0.0), QPointF(endPos, height()));
|
||||
points[0] = QPointF(endPos, height() - 6.0);
|
||||
points[1] = QPointF(endPos - 6.0, height());
|
||||
points[2] = QPointF(endPos + 6.0, height());
|
||||
points[3] = QPointF(endPos, height() - 6.0);
|
||||
painter.drawPolygon(points, 4);
|
||||
}
|
||||
|
||||
if (m_displaySamplePos >= 0) {
|
||||
painter.setPen(QPen(Qt::white, increment));
|
||||
qreal pos = m_displaySamplePos / m_samplesPerPx;
|
||||
painter.drawLine(QPointF(pos, 0.0), QPointF(pos, height()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SampleView::calculateSamplesPerPx() {
|
||||
m_samplesPerPx = (1.0 - m_zoomFactor) * m_baseSamplesPerPx + m_zoomFactor * 1.0;
|
||||
}
|
||||
|
||||
void SampleView::resetZoom() {
|
||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||
m_baseSamplesPerPx = m_sample->getNumSamples() / qreal(scroll->width() - 4);
|
||||
calculateSamplesPerPx();
|
||||
setMinimumWidth(int(m_sample->getNumSamples() / m_samplesPerPx) + 1);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void SampleView::setZoom(int zVal) {
|
||||
m_zoomFactor = zVal / 99.0;
|
||||
calculateSamplesPerPx();
|
||||
setMinimumWidth(int(m_sample->getNumSamples() / m_samplesPerPx) + 1);
|
||||
update();
|
||||
}
|
||||
|
||||
void SampleView::showEvent(QShowEvent* ev) { setZoom(0); }
|
||||
|
||||
void SampleView::mousePressEvent(QMouseEvent* ev) {
|
||||
if (m_sample && m_sample->isLooped()) {
|
||||
int loopStart = m_sample->m_loopStartSample;
|
||||
int startPos = int(loopStart / m_samplesPerPx);
|
||||
int startDelta = std::abs(startPos - ev->pos().x());
|
||||
bool startPass = startDelta < 20;
|
||||
|
||||
int loopEnd = loopStart + m_sample->m_loopLengthSamples - 1;
|
||||
int endPos = int(loopEnd / m_samplesPerPx);
|
||||
int endDelta = std::abs(endPos - ev->pos().x());
|
||||
bool endPass = endDelta < 20;
|
||||
|
||||
if (startPass && endPass) {
|
||||
if (startDelta == endDelta) {
|
||||
if (ev->pos().x() < startPos)
|
||||
m_dragState = DragState::Start;
|
||||
else
|
||||
m_dragState = DragState::End;
|
||||
} else if (startDelta < endDelta)
|
||||
m_dragState = DragState::Start;
|
||||
else
|
||||
m_dragState = DragState::End;
|
||||
} else if (startPass)
|
||||
m_dragState = DragState::Start;
|
||||
else if (endPass)
|
||||
m_dragState = DragState::End;
|
||||
|
||||
getEditor()->m_controls->setFileWrite(m_dragState == DragState::None);
|
||||
|
||||
mouseMoveEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
void SampleView::mouseReleaseEvent(QMouseEvent* ev) {
|
||||
m_dragState = DragState::None;
|
||||
getEditor()->m_controls->setFileWrite(true);
|
||||
}
|
||||
|
||||
void SampleView::mouseMoveEvent(QMouseEvent* ev) {
|
||||
if (m_dragState == DragState::Start)
|
||||
getEditor()->m_controls->setLoopStartSample(int(ev->pos().x() * m_samplesPerPx));
|
||||
else if (m_dragState == DragState::End)
|
||||
getEditor()->m_controls->setLoopEndSample(int(ev->pos().x() * m_samplesPerPx));
|
||||
}
|
||||
|
||||
void SampleView::wheelEvent(QWheelEvent* ev) {
|
||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||
/* Send wheel event directly to the scroll bar */
|
||||
QApplication::sendEvent(scroll->horizontalScrollBar(), ev);
|
||||
}
|
||||
}
|
||||
|
||||
bool SampleView::loadData(ProjectModel::SampleNode* node) {
|
||||
bool reset = m_node.get() != node;
|
||||
m_node = node;
|
||||
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(m_node.get());
|
||||
std::tie(m_sample, m_sampleData) = group->getAudioGroup()->getSampleData(m_node->id(), m_node->m_obj.get());
|
||||
if (reset)
|
||||
resetZoom();
|
||||
|
||||
m_playbackMacro = amuse::MakeObj<amuse::SoundMacro>();
|
||||
amuse::SoundMacro::CmdStartSample* startSample = static_cast<amuse::SoundMacro::CmdStartSample*>(
|
||||
m_playbackMacro->insertNewCmd(0, amuse::SoundMacro::CmdOp::StartSample));
|
||||
startSample->sample.id = m_node->id();
|
||||
amuse::SoundMacro::CmdWaitMs* waitMillisec = static_cast<amuse::SoundMacro::CmdWaitMs*>(
|
||||
m_playbackMacro->insertNewCmd(1, amuse::SoundMacro::CmdOp::WaitMs));
|
||||
waitMillisec->keyOff = true;
|
||||
waitMillisec->ms = 65535;
|
||||
m_playbackMacro->insertNewCmd(2, amuse::SoundMacro::CmdOp::StopSample);
|
||||
m_playbackMacro->insertNewCmd(3, amuse::SoundMacro::CmdOp::End);
|
||||
|
||||
update();
|
||||
|
||||
return reset;
|
||||
}
|
||||
|
||||
void SampleView::unloadData() {
|
||||
m_node.reset();
|
||||
m_sample.reset();
|
||||
m_playbackMacro.reset();
|
||||
update();
|
||||
}
|
||||
|
||||
ProjectModel::INode* SampleView::currentNode() const { return m_node.get(); }
|
||||
|
||||
amuse::SampleEntryData* SampleView::entryData() const { return m_sample.get(); }
|
||||
|
||||
const amuse::SoundMacro* SampleView::soundMacro() const { return m_playbackMacro.get(); }
|
||||
|
||||
void SampleView::setSamplePos(int pos) {
|
||||
if (pos != m_displaySamplePos) {
|
||||
if (pos >= 0) {
|
||||
int lastPos = m_displaySamplePos;
|
||||
m_displaySamplePos = pos;
|
||||
updateSampleRange(lastPos, pos);
|
||||
} else {
|
||||
m_displaySamplePos = pos;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SampleView::updateSampleRange(int oldSamp, int newSamp) {
|
||||
qreal lastPos = oldSamp / m_samplesPerPx;
|
||||
qreal newPos = newSamp / m_samplesPerPx;
|
||||
update(int(std::min(lastPos, newPos)) - 10, 0, int(std::fabs(newPos - lastPos)) + 20, height());
|
||||
}
|
||||
|
||||
SampleView::SampleView(QWidget* parent) : QWidget(parent) {
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
m_rulerFont.setPointSize(8);
|
||||
}
|
||||
|
||||
SampleView::~SampleView() = default;
|
||||
|
||||
void SampleControls::zoomSliderChanged(int val) {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
editor->m_sampleView->setZoom(val);
|
||||
}
|
||||
|
||||
class SampLoopUndoCommand : public EditorUndoCommand {
|
||||
uint32_t m_redoStartVal, m_redoEndVal, m_undoStartVal, m_undoEndVal;
|
||||
int m_fieldIdx;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
SampLoopUndoCommand(uint32_t redoStart, uint32_t redoEnd, const QString& fieldName, int fieldIdx,
|
||||
amuse::ObjToken<ProjectModel::SampleNode> node)
|
||||
: EditorUndoCommand(node.get(), SampleControls::tr("Change %1").arg(fieldName))
|
||||
, m_redoStartVal(redoStart)
|
||||
, m_redoEndVal(redoEnd)
|
||||
, m_fieldIdx(fieldIdx) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
|
||||
data->setLoopStartSample(m_undoStartVal);
|
||||
data->setLoopEndSample(m_undoEndVal);
|
||||
EditorUndoCommand::undo();
|
||||
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
|
||||
e->m_controls->doFileWrite();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
|
||||
m_undoStartVal = data->getLoopStartSample();
|
||||
m_undoEndVal = data->getLoopEndSample();
|
||||
data->setLoopStartSample(m_redoStartVal);
|
||||
data->setLoopEndSample(m_redoEndVal);
|
||||
if (m_undid) {
|
||||
EditorUndoCommand::redo();
|
||||
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
|
||||
e->m_controls->doFileWrite();
|
||||
}
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id() && static_cast<const SampLoopUndoCommand*>(other)->m_fieldIdx == m_fieldIdx) {
|
||||
m_redoStartVal = static_cast<const SampLoopUndoCommand*>(other)->m_redoStartVal;
|
||||
m_redoEndVal = static_cast<const SampLoopUndoCommand*>(other)->m_redoEndVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::SampLoop); }
|
||||
};
|
||||
|
||||
void SampleControls::loopStateChanged(int state) {
|
||||
if (m_enableUpdate) {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
|
||||
if (state == Qt::Checked) {
|
||||
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(
|
||||
atUint32(m_loopStart->value()), uint32_t(m_loopEnd->value()), tr("Loop"), 0, editor->m_sampleView->m_node));
|
||||
m_loopStart->setEnabled(true);
|
||||
m_loopEnd->setEnabled(true);
|
||||
if (m_loopStart->value() == 0 && m_loopEnd->value() == 0) {
|
||||
m_enableUpdate = false;
|
||||
m_loopEnd->setValue(data->getNumSamples() - 1);
|
||||
m_loopStart->setMaximum(m_loopEnd->value());
|
||||
m_enableUpdate = true;
|
||||
}
|
||||
data->m_loopStartSample = atUint32(m_loopStart->value());
|
||||
data->m_loopLengthSamples = m_loopEnd->value() + 1 - data->m_loopStartSample;
|
||||
} else {
|
||||
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(0, 0, tr("Loop"), 0, editor->m_sampleView->m_node));
|
||||
m_loopStart->setEnabled(false);
|
||||
m_loopEnd->setEnabled(false);
|
||||
data->m_loopStartSample = 0;
|
||||
data->m_loopLengthSamples = 0;
|
||||
}
|
||||
editor->m_sampleView->update();
|
||||
doFileWrite();
|
||||
}
|
||||
}
|
||||
|
||||
void SampleControls::startValueChanged(int val) {
|
||||
if (m_enableUpdate) {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
|
||||
|
||||
int oldPos = data->getLoopStartSample();
|
||||
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(atUint32(val), data->getLoopEndSample(), tr("Loop Start"), 1,
|
||||
editor->m_sampleView->m_node));
|
||||
|
||||
data->setLoopStartSample(atUint32(val));
|
||||
m_loopEnd->setMinimum(val);
|
||||
|
||||
editor->m_sampleView->updateSampleRange(oldPos, val);
|
||||
doFileWrite();
|
||||
}
|
||||
}
|
||||
|
||||
void SampleControls::endValueChanged(int val) {
|
||||
if (m_enableUpdate) {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
|
||||
|
||||
int oldPos = data->getLoopEndSample();
|
||||
g_MainWindow->pushUndoCommand(new SampLoopUndoCommand(data->getLoopStartSample(), atUint32(val), tr("Loop End"), 2,
|
||||
editor->m_sampleView->m_node));
|
||||
|
||||
data->setLoopEndSample(atUint32(val));
|
||||
m_loopStart->setMaximum(val);
|
||||
|
||||
editor->m_sampleView->updateSampleRange(oldPos, val);
|
||||
doFileWrite();
|
||||
}
|
||||
}
|
||||
|
||||
class SampPitchUndoCommand : public EditorUndoCommand {
|
||||
uint8_t m_redoPitchVal, m_undoPitchVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
SampPitchUndoCommand(atUint8 redoPitch, amuse::ObjToken<ProjectModel::SampleNode> node)
|
||||
: EditorUndoCommand(node.get(), SampleControls::tr("Change Base Pitch")), m_redoPitchVal(redoPitch) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
|
||||
data->m_pitch = m_undoPitchVal;
|
||||
EditorUndoCommand::undo();
|
||||
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
|
||||
e->m_controls->doFileWrite();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::SampleEntryData* data = m_node.cast<ProjectModel::SampleNode>()->m_obj->m_data.get();
|
||||
m_undoPitchVal = data->m_pitch;
|
||||
data->m_pitch = m_redoPitchVal;
|
||||
if (m_undid) {
|
||||
EditorUndoCommand::redo();
|
||||
if (SampleEditor* e = static_cast<SampleEditor*>(g_MainWindow->getEditorWidget()))
|
||||
e->m_controls->doFileWrite();
|
||||
}
|
||||
}
|
||||
bool mergeWith(const QUndoCommand* other) override {
|
||||
if (other->id() == id()) {
|
||||
m_redoPitchVal = static_cast<const SampPitchUndoCommand*>(other)->m_redoPitchVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int id() const override { return int(Id::SampPitch); }
|
||||
};
|
||||
|
||||
void SampleControls::pitchValueChanged(int val) {
|
||||
if (m_enableUpdate) {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
|
||||
|
||||
g_MainWindow->pushUndoCommand(new SampPitchUndoCommand(atUint8(val), editor->m_sampleView->m_node));
|
||||
|
||||
data->m_pitch = atUint8(val);
|
||||
doFileWrite();
|
||||
}
|
||||
}
|
||||
|
||||
void SampleControls::makeWAVVersion() {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
|
||||
g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->makeWAVVersion(node->id(), node->m_obj.get());
|
||||
updateFileState();
|
||||
}
|
||||
|
||||
void SampleControls::makeCompressedVersion() {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
|
||||
g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->makeCompressedVersion(node->id(),
|
||||
node->m_obj.get());
|
||||
updateFileState();
|
||||
}
|
||||
|
||||
void SampleControls::showInBrowser() { ShowInGraphicalShell(this, m_path); }
|
||||
|
||||
void SampleControls::updateFileState() {
|
||||
m_enableUpdate = false;
|
||||
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
|
||||
|
||||
std::string path;
|
||||
amuse::SampleFileState state = g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->getSampleFileState(
|
||||
node->id(), node->m_obj.get(), &path);
|
||||
disconnect(m_makeOtherConn);
|
||||
|
||||
switch (state) {
|
||||
case amuse::SampleFileState::MemoryOnlyWAV:
|
||||
case amuse::SampleFileState::CompressedNoWAV:
|
||||
m_makeOtherVersion->setText(tr("Make WAV Version"));
|
||||
m_makeOtherVersion->setDisabled(false);
|
||||
m_makeOtherConn = connect(m_makeOtherVersion, &QPushButton::clicked, this, &SampleControls::makeWAVVersion);
|
||||
break;
|
||||
case amuse::SampleFileState::MemoryOnlyCompressed:
|
||||
case amuse::SampleFileState::WAVRecent:
|
||||
case amuse::SampleFileState::WAVNoCompressed:
|
||||
m_makeOtherVersion->setText(tr("Make Compressed Version"));
|
||||
m_makeOtherVersion->setDisabled(false);
|
||||
m_makeOtherConn = connect(m_makeOtherVersion, &QPushButton::clicked, this, &SampleControls::makeCompressedVersion);
|
||||
break;
|
||||
default:
|
||||
m_makeOtherVersion->setText(tr("Up To Date"));
|
||||
m_makeOtherVersion->setDisabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
amuse::SampleEntryData* data = editor->m_sampleView->entryData();
|
||||
m_loopCheck->setChecked(data->isLooped());
|
||||
int loopStart = 0;
|
||||
int loopEnd = 0;
|
||||
int loopMax = data->getNumSamples() - 1;
|
||||
if (data->isLooped()) {
|
||||
loopStart = data->m_loopStartSample;
|
||||
loopEnd = loopStart + data->m_loopLengthSamples - 1;
|
||||
}
|
||||
m_loopStart->setMaximum(loopEnd);
|
||||
m_loopEnd->setMinimum(loopStart);
|
||||
m_loopEnd->setMaximum(loopMax);
|
||||
m_loopStart->setValue(loopStart);
|
||||
m_loopEnd->setValue(loopEnd);
|
||||
m_basePitch->setValue(data->m_pitch);
|
||||
m_loopStart->setEnabled(data->isLooped());
|
||||
m_loopEnd->setEnabled(data->isLooped());
|
||||
|
||||
if (!path.empty()) {
|
||||
m_path = UTF8ToQString(path);
|
||||
m_showInBrowser->setDisabled(false);
|
||||
} else {
|
||||
m_path = QString();
|
||||
m_showInBrowser->setDisabled(true);
|
||||
}
|
||||
|
||||
m_enableUpdate = true;
|
||||
}
|
||||
|
||||
void SampleControls::doFileWrite() {
|
||||
if (m_enableFileWrite) {
|
||||
SampleEditor* editor = qobject_cast<SampleEditor*>(parentWidget());
|
||||
ProjectModel::SampleNode* node = static_cast<ProjectModel::SampleNode*>(editor->currentNode());
|
||||
g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup()->patchSampleMetadata(node->id(),
|
||||
node->m_obj.get());
|
||||
}
|
||||
}
|
||||
|
||||
void SampleControls::setFileWrite(bool w) {
|
||||
m_enableFileWrite = w;
|
||||
doFileWrite();
|
||||
}
|
||||
|
||||
void SampleControls::loadData(bool reset) {
|
||||
m_zoomSlider->setDisabled(false);
|
||||
m_loopCheck->setDisabled(false);
|
||||
m_loopStart->setDisabled(false);
|
||||
m_loopEnd->setDisabled(false);
|
||||
m_basePitch->setDisabled(false);
|
||||
if (reset)
|
||||
m_zoomSlider->setValue(0);
|
||||
updateFileState();
|
||||
}
|
||||
|
||||
void SampleControls::unloadData() {
|
||||
m_zoomSlider->setDisabled(true);
|
||||
m_loopCheck->setDisabled(true);
|
||||
m_loopStart->setDisabled(true);
|
||||
m_loopEnd->setDisabled(true);
|
||||
m_basePitch->setDisabled(true);
|
||||
m_makeOtherVersion->setText(tr("Nothing Loaded"));
|
||||
m_makeOtherVersion->setDisabled(true);
|
||||
m_showInBrowser->setDisabled(true);
|
||||
}
|
||||
|
||||
SampleControls::SampleControls(QWidget* parent) : QFrame(parent) {
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
setFixedHeight(100);
|
||||
setFrameShape(QFrame::StyledPanel);
|
||||
setFrameShadow(QFrame::Sunken);
|
||||
setBackgroundRole(QPalette::Base);
|
||||
setAutoFillBackground(true);
|
||||
|
||||
QPalette palette = QWidget::palette();
|
||||
palette.setColor(QPalette::Base, palette.color(QPalette::Window));
|
||||
|
||||
QHBoxLayout* mainLayout = new QHBoxLayout;
|
||||
|
||||
QGridLayout* leftLayout = new QGridLayout;
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Zoom")), 0, 0);
|
||||
m_zoomSlider = new QSlider(Qt::Horizontal);
|
||||
m_zoomSlider->setDisabled(true);
|
||||
m_zoomSlider->setRange(0, 99);
|
||||
connect(m_zoomSlider, qOverload<int>(&QSlider::valueChanged), this, &SampleControls::zoomSliderChanged);
|
||||
leftLayout->addWidget(m_zoomSlider, 1, 0);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Loop")), 0, 1);
|
||||
m_loopCheck = new QCheckBox;
|
||||
m_loopCheck->setPalette(palette);
|
||||
m_loopCheck->setDisabled(true);
|
||||
connect(m_loopCheck, qOverload<int>(&QCheckBox::stateChanged), this, &SampleControls::loopStateChanged);
|
||||
leftLayout->addWidget(m_loopCheck, 1, 1);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Start")), 0, 2);
|
||||
m_loopStart = new QSpinBox;
|
||||
m_loopStart->setPalette(palette);
|
||||
m_loopStart->setDisabled(true);
|
||||
connect(m_loopStart, qOverload<int>(&QSpinBox::valueChanged), this, &SampleControls::startValueChanged);
|
||||
leftLayout->addWidget(m_loopStart, 1, 2);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("End")), 0, 3);
|
||||
m_loopEnd = new QSpinBox;
|
||||
m_loopEnd->setPalette(palette);
|
||||
m_loopEnd->setDisabled(true);
|
||||
connect(m_loopEnd, qOverload<int>(&QSpinBox::valueChanged), this, &SampleControls::endValueChanged);
|
||||
leftLayout->addWidget(m_loopEnd, 1, 3);
|
||||
|
||||
leftLayout->addWidget(new QLabel(tr("Base Pitch")), 0, 4);
|
||||
m_basePitch = new QSpinBox;
|
||||
m_basePitch->setPalette(palette);
|
||||
m_basePitch->setDisabled(true);
|
||||
m_basePitch->setMinimum(0);
|
||||
m_basePitch->setMaximum(127);
|
||||
connect(m_basePitch, qOverload<int>(&QSpinBox::valueChanged), this, &SampleControls::pitchValueChanged);
|
||||
leftLayout->addWidget(m_basePitch, 1, 4);
|
||||
|
||||
leftLayout->setColumnMinimumWidth(0, 100);
|
||||
leftLayout->setColumnMinimumWidth(1, 50);
|
||||
leftLayout->setColumnMinimumWidth(2, 75);
|
||||
leftLayout->setColumnMinimumWidth(3, 75);
|
||||
leftLayout->setColumnMinimumWidth(4, 75);
|
||||
leftLayout->setRowMinimumHeight(0, 22);
|
||||
leftLayout->setRowMinimumHeight(1, 37);
|
||||
leftLayout->setContentsMargins(10, 6, 0, 14);
|
||||
|
||||
QVBoxLayout* rightLayout = new QVBoxLayout;
|
||||
|
||||
m_makeOtherVersion = new QPushButton(tr("Nothing Loaded"));
|
||||
m_makeOtherVersion->setMinimumWidth(250);
|
||||
m_makeOtherVersion->setDisabled(true);
|
||||
rightLayout->addWidget(m_makeOtherVersion);
|
||||
m_showInBrowser = new QPushButton(ShowInGraphicalShellString());
|
||||
m_showInBrowser->setMinimumWidth(250);
|
||||
m_showInBrowser->setDisabled(true);
|
||||
connect(m_showInBrowser, &QPushButton::clicked, this, &SampleControls::showInBrowser);
|
||||
rightLayout->addWidget(m_showInBrowser);
|
||||
|
||||
mainLayout->addLayout(leftLayout);
|
||||
mainLayout->addStretch(1);
|
||||
mainLayout->addLayout(rightLayout);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
SampleControls::~SampleControls() = default;
|
||||
|
||||
bool SampleEditor::loadData(ProjectModel::SampleNode* node) {
|
||||
m_controls->loadData(m_sampleView->loadData(node));
|
||||
return true;
|
||||
}
|
||||
|
||||
void SampleEditor::unloadData() {
|
||||
m_sampleView->unloadData();
|
||||
m_controls->unloadData();
|
||||
}
|
||||
|
||||
ProjectModel::INode* SampleEditor::currentNode() const { return m_sampleView->currentNode(); }
|
||||
|
||||
const amuse::SoundMacro* SampleEditor::soundMacro() const { return m_sampleView->soundMacro(); }
|
||||
|
||||
void SampleEditor::setSamplePos(int pos) { m_sampleView->setSamplePos(pos); }
|
||||
|
||||
void SampleEditor::resizeEvent(QResizeEvent* ev) { m_sampleView->resetZoom(); }
|
||||
|
||||
SampleEditor::SampleEditor(QWidget* parent)
|
||||
: EditorWidget(parent), m_scrollArea(new QScrollArea), m_sampleView(new SampleView), m_controls(new SampleControls) {
|
||||
m_scrollArea->setWidget(m_sampleView);
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(QMargins());
|
||||
layout->setSpacing(1);
|
||||
layout->addWidget(m_scrollArea);
|
||||
layout->addWidget(m_controls);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
SampleEditor::~SampleEditor() = default;
|
|
@ -0,0 +1,125 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include <QFont>
|
||||
#include <QWidget>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/AudioGroupPool.hpp>
|
||||
#include <amuse/AudioGroupSampleDirectory.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class SampleEditor;
|
||||
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QScrollArea;
|
||||
class QSlider;
|
||||
|
||||
class SampleView : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class SampleControls;
|
||||
qreal m_baseSamplesPerPx = 100.0;
|
||||
qreal m_samplesPerPx = 100.0;
|
||||
qreal m_zoomFactor = 1.0;
|
||||
amuse::ObjToken<ProjectModel::SampleNode> m_node;
|
||||
amuse::ObjToken<amuse::SampleEntryData> m_sample;
|
||||
amuse::ObjToken<amuse::SoundMacro> m_playbackMacro;
|
||||
const unsigned char* m_sampleData = nullptr;
|
||||
qreal m_curSamplePos = 0.0;
|
||||
int16_t m_prev1 = 0;
|
||||
int16_t m_prev2 = 0;
|
||||
QFont m_rulerFont;
|
||||
int m_displaySamplePos = -1;
|
||||
enum class DragState { None, Start, End };
|
||||
DragState m_dragState = DragState::None;
|
||||
void seekToSample(qreal sample);
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> iterateSampleInterval(qreal interval);
|
||||
void calculateSamplesPerPx();
|
||||
SampleEditor* getEditor() const;
|
||||
|
||||
public:
|
||||
explicit SampleView(QWidget* parent = Q_NULLPTR);
|
||||
~SampleView() override;
|
||||
|
||||
bool loadData(ProjectModel::SampleNode* node);
|
||||
void unloadData();
|
||||
ProjectModel::INode* currentNode() const;
|
||||
amuse::SampleEntryData* entryData() const;
|
||||
const amuse::SoundMacro* soundMacro() const;
|
||||
void setSamplePos(int pos);
|
||||
void updateSampleRange(int oldSamp, int newSamp);
|
||||
|
||||
void paintEvent(QPaintEvent* ev) override;
|
||||
void resetZoom();
|
||||
void setZoom(int zVal);
|
||||
void showEvent(QShowEvent* ev) override;
|
||||
void mousePressEvent(QMouseEvent* ev) override;
|
||||
void mouseReleaseEvent(QMouseEvent* ev) override;
|
||||
void mouseMoveEvent(QMouseEvent* ev) override;
|
||||
void wheelEvent(QWheelEvent* ev) override;
|
||||
};
|
||||
|
||||
class SampleControls : public QFrame {
|
||||
Q_OBJECT
|
||||
QString m_path;
|
||||
QSlider* m_zoomSlider;
|
||||
QCheckBox* m_loopCheck;
|
||||
QSpinBox* m_loopStart;
|
||||
QSpinBox* m_loopEnd;
|
||||
QSpinBox* m_basePitch;
|
||||
QPushButton* m_makeOtherVersion;
|
||||
QMetaObject::Connection m_makeOtherConn;
|
||||
QPushButton* m_showInBrowser;
|
||||
bool m_enableUpdate = true;
|
||||
bool m_enableFileWrite = true;
|
||||
|
||||
public:
|
||||
explicit SampleControls(QWidget* parent = Q_NULLPTR);
|
||||
~SampleControls() override;
|
||||
|
||||
void doFileWrite();
|
||||
void setFileWrite(bool w);
|
||||
void updateFileState();
|
||||
void setLoopStartSample(int sample) { m_loopStart->setValue(sample); }
|
||||
void setLoopEndSample(int sample) { m_loopEnd->setValue(sample); }
|
||||
void loadData(bool reset);
|
||||
void unloadData();
|
||||
|
||||
public slots:
|
||||
void zoomSliderChanged(int val);
|
||||
void loopStateChanged(int state);
|
||||
void startValueChanged(int val);
|
||||
void endValueChanged(int val);
|
||||
void pitchValueChanged(int val);
|
||||
void makeWAVVersion();
|
||||
void makeCompressedVersion();
|
||||
void showInBrowser();
|
||||
};
|
||||
|
||||
class SampleEditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
friend class SampleView;
|
||||
friend class SampleControls;
|
||||
friend class SampLoopUndoCommand;
|
||||
friend class SampPitchUndoCommand;
|
||||
QScrollArea* m_scrollArea;
|
||||
SampleView* m_sampleView;
|
||||
SampleControls* m_controls;
|
||||
|
||||
public:
|
||||
explicit SampleEditor(QWidget* parent = Q_NULLPTR);
|
||||
~SampleEditor() override;
|
||||
|
||||
bool loadData(ProjectModel::SampleNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
const amuse::SoundMacro* soundMacro() const;
|
||||
void setSamplePos(int pos);
|
||||
|
||||
void resizeEvent(QResizeEvent* ev) override;
|
||||
};
|
|
@ -0,0 +1,324 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QProxyStyle>
|
||||
#include <QPushButton>
|
||||
#include <QSplitter>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTabWidget>
|
||||
#include <QTableView>
|
||||
#include <QToolButton>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/AudioGroupProject.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
#include <amuse/Sequencer.hpp>
|
||||
|
||||
class SetupTableView;
|
||||
|
||||
class PageObjectDelegate : public BaseObjectDelegate {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
|
||||
public:
|
||||
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
|
||||
~PageObjectDelegate() override;
|
||||
|
||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
|
||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
|
||||
private slots:
|
||||
void objIndexChanged();
|
||||
};
|
||||
|
||||
class MIDIFileFieldWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
QLineEdit m_le;
|
||||
QPushButton m_button;
|
||||
QFileDialog m_dialog;
|
||||
|
||||
public:
|
||||
explicit MIDIFileFieldWidget(QWidget* parent = Q_NULLPTR);
|
||||
~MIDIFileFieldWidget() override;
|
||||
|
||||
QString path() const { return m_le.text(); }
|
||||
void setPath(const QString& path) { m_le.setText(path); }
|
||||
|
||||
public slots:
|
||||
void buttonPressed();
|
||||
void fileDialogOpened(const QString& path);
|
||||
|
||||
signals:
|
||||
void pathChanged();
|
||||
};
|
||||
|
||||
class MIDIFileDelegate : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
QFileDialog m_fileDialogMid, m_fileDialogSng;
|
||||
std::vector<uint8_t> m_exportData;
|
||||
|
||||
public:
|
||||
explicit MIDIFileDelegate(SetupTableView* parent = Q_NULLPTR);
|
||||
~MIDIFileDelegate() override;
|
||||
|
||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
void destroyEditor(QWidget* editor, const QModelIndex& index) const override;
|
||||
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
|
||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) override;
|
||||
|
||||
private slots:
|
||||
void doExportMIDI();
|
||||
void _doExportMIDI(const QString& path);
|
||||
void doExportSNG();
|
||||
void _doExportSNG(const QString& path);
|
||||
|
||||
public slots:
|
||||
void pathChanged();
|
||||
};
|
||||
|
||||
class PageModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
friend class SongGroupEditor;
|
||||
friend class PageObjectDelegate;
|
||||
friend class PageTableView;
|
||||
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
||||
struct Iterator {
|
||||
using ItTp = std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>::iterator;
|
||||
ItTp m_it;
|
||||
Iterator(ItTp it) : m_it(it) {}
|
||||
ItTp::pointer operator->() { return m_it.operator->(); }
|
||||
bool operator<(const Iterator& other) const { return m_it->first < other.m_it->first; }
|
||||
bool operator<(uint8_t other) const { return m_it->first < other; }
|
||||
};
|
||||
std::vector<Iterator> m_sorted;
|
||||
bool m_drum;
|
||||
std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>& _getMap() const;
|
||||
void _buildSortedList();
|
||||
QModelIndex _indexOfProgram(uint8_t prog) const;
|
||||
int _hypotheticalIndexOfProgram(uint8_t prog) const;
|
||||
|
||||
public:
|
||||
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
|
||||
~PageModel() override;
|
||||
|
||||
void loadData(ProjectModel::SongGroupNode* node);
|
||||
void unloadData();
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
|
||||
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
|
||||
};
|
||||
|
||||
class SetupListModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
friend class SongGroupEditor;
|
||||
friend class MIDIFileDelegate;
|
||||
friend class SetupTableView;
|
||||
friend class SetupModel;
|
||||
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
||||
struct Iterator {
|
||||
using ItTp = std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>::iterator;
|
||||
ItTp m_it;
|
||||
Iterator(ItTp it) : m_it(it) {}
|
||||
ItTp::pointer operator->() { return m_it.operator->(); }
|
||||
bool operator<(const Iterator& other) const {
|
||||
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||
amuse::SongId::CurNameDB->resolveNameFromId(other.m_it->first);
|
||||
}
|
||||
bool operator<(amuse::SongId other) const {
|
||||
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||
amuse::SongId::CurNameDB->resolveNameFromId(other);
|
||||
}
|
||||
bool operator<(const std::string& name) const {
|
||||
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) < name;
|
||||
}
|
||||
};
|
||||
std::vector<Iterator> m_sorted;
|
||||
std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& _getMap() const;
|
||||
void _buildSortedList();
|
||||
QModelIndex _indexOfSong(amuse::SongId id) const;
|
||||
int _hypotheticalIndexOfSong(const std::string& songName) const;
|
||||
|
||||
public:
|
||||
explicit SetupListModel(QObject* parent = Q_NULLPTR);
|
||||
~SetupListModel() override;
|
||||
|
||||
void loadData(ProjectModel::SongGroupNode* node);
|
||||
void unloadData();
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
int _insertRow(std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data);
|
||||
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>> _removeRow(amuse::SongId id);
|
||||
};
|
||||
|
||||
class SetupModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
friend class SongGroupEditor;
|
||||
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
|
||||
|
||||
public:
|
||||
explicit SetupModel(QObject* parent = Q_NULLPTR);
|
||||
~SetupModel() override;
|
||||
|
||||
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
|
||||
void unloadData();
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
};
|
||||
|
||||
class PageTableView : public QTableView {
|
||||
Q_OBJECT
|
||||
PageObjectDelegate m_poDelegate;
|
||||
RangedValueFactory<0, 127> m_127Factory;
|
||||
RangedValueFactory<1, 128> m_128Factory;
|
||||
RangedValueFactory<0, 255> m_255Factory;
|
||||
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
|
||||
|
||||
public:
|
||||
explicit PageTableView(QWidget* parent = Q_NULLPTR);
|
||||
~PageTableView() override;
|
||||
|
||||
void setModel(QAbstractItemModel* model) override;
|
||||
void deleteSelection();
|
||||
};
|
||||
|
||||
class SetupTableView : public QSplitter {
|
||||
Q_OBJECT
|
||||
friend class SongGroupEditor;
|
||||
friend class SetupRowUndoCommand;
|
||||
QTableView* m_listView;
|
||||
QTableView* m_tableView;
|
||||
MIDIFileDelegate m_midiDelegate;
|
||||
RangedValueFactory<0, 127> m_127Factory;
|
||||
RangedValueFactory<1, 128> m_128Factory;
|
||||
QStyledItemDelegate m_127Delegate, m_128Delegate;
|
||||
|
||||
public:
|
||||
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
|
||||
~SetupTableView() override;
|
||||
|
||||
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
|
||||
void deleteSelection();
|
||||
void showEvent(QShowEvent* event) override;
|
||||
};
|
||||
|
||||
class ColoredTabBarStyle : public QProxyStyle {
|
||||
public:
|
||||
using QProxyStyle::QProxyStyle;
|
||||
void drawControl(QStyle::ControlElement element, const QStyleOption* option, QPainter* painter,
|
||||
const QWidget* widget = nullptr) const override;
|
||||
};
|
||||
|
||||
class ColoredTabBar : public QTabBar {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
|
||||
};
|
||||
|
||||
class ColoredTabWidget : public QTabWidget {
|
||||
Q_OBJECT
|
||||
ColoredTabBar m_tabBar;
|
||||
|
||||
public:
|
||||
explicit ColoredTabWidget(QWidget* parent = Q_NULLPTR);
|
||||
};
|
||||
|
||||
class MIDIPlayerWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
QAction m_playAction;
|
||||
QToolButton m_button;
|
||||
QModelIndex m_index;
|
||||
amuse::GroupId m_groupId;
|
||||
amuse::SongId m_songId;
|
||||
QString m_path;
|
||||
std::vector<uint8_t> m_arrData;
|
||||
amuse::ObjToken<amuse::Sequencer> m_seq;
|
||||
|
||||
public:
|
||||
explicit MIDIPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SongId id, const QString& path,
|
||||
QWidget* parent = Q_NULLPTR);
|
||||
~MIDIPlayerWidget() override;
|
||||
amuse::SongId songId() const { return m_songId; }
|
||||
amuse::Sequencer* sequencer() const { return m_seq.get(); }
|
||||
void stopped();
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
public slots:
|
||||
void clicked();
|
||||
};
|
||||
|
||||
class SongGroupEditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
friend class SetupModel;
|
||||
PageModel m_normPages;
|
||||
PageModel m_drumPages;
|
||||
SetupListModel m_setupList;
|
||||
SetupModel m_setup;
|
||||
PageTableView* m_normTable;
|
||||
PageTableView* m_drumTable;
|
||||
SetupTableView* m_setupTable;
|
||||
ColoredTabWidget m_tabs;
|
||||
AddRemoveButtons m_addRemoveButtons;
|
||||
|
||||
public:
|
||||
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
|
||||
~SongGroupEditor() override;
|
||||
|
||||
bool loadData(ProjectModel::SongGroupNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
void setEditorEnabled(bool en) override {}
|
||||
void resizeEvent(QResizeEvent* ev) override;
|
||||
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
|
||||
AmuseItemEditFlags itemEditFlags() const override;
|
||||
|
||||
private slots:
|
||||
void doAdd();
|
||||
void doSelectionChanged();
|
||||
void doSetupSelectionChanged();
|
||||
void currentTabChanged(int idx);
|
||||
void normRowsInserted(const QModelIndex& parent, int first, int last);
|
||||
void normRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||
void drumRowsInserted(const QModelIndex& parent, int first, int last);
|
||||
void drumRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||
void setupRowsInserted(const QModelIndex& parent, int first, int last);
|
||||
void setupRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||
void setupRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
|
||||
void setupModelAboutToBeReset();
|
||||
void setupDataChanged();
|
||||
void itemDeleteAction() override;
|
||||
};
|
|
@ -0,0 +1,645 @@
|
|||
#include "SoundGroupEditor.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
class SFXDataChangeUndoCommand : public EditorUndoCommand {
|
||||
amuse::SFXId m_sfx;
|
||||
int m_column;
|
||||
int m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
explicit SFXDataChangeUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, amuse::SFXId sfx,
|
||||
int column, int redoVal)
|
||||
: EditorUndoCommand(node, text), m_sfx(sfx), m_column(column), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
amuse::SFXGroupIndex::SFXEntry& entry = map[m_sfx];
|
||||
|
||||
switch (m_column) {
|
||||
case 1:
|
||||
entry.objId.id = m_undoVal;
|
||||
break;
|
||||
case 2:
|
||||
entry.priority = m_undoVal;
|
||||
break;
|
||||
case 3:
|
||||
entry.maxVoices = m_undoVal;
|
||||
break;
|
||||
case 4:
|
||||
entry.defVel = m_undoVal;
|
||||
break;
|
||||
case 5:
|
||||
entry.panning = m_undoVal;
|
||||
break;
|
||||
case 6:
|
||||
entry.defKey = m_undoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
amuse::SFXGroupIndex::SFXEntry& entry = map[m_sfx];
|
||||
|
||||
switch (m_column) {
|
||||
case 1:
|
||||
m_undoVal = entry.objId.id.id;
|
||||
entry.objId.id = m_redoVal;
|
||||
break;
|
||||
case 2:
|
||||
m_undoVal = entry.priority;
|
||||
entry.priority = m_redoVal;
|
||||
break;
|
||||
case 3:
|
||||
m_undoVal = entry.maxVoices;
|
||||
entry.maxVoices = m_redoVal;
|
||||
break;
|
||||
case 4:
|
||||
m_undoVal = entry.defVel;
|
||||
entry.defVel = m_redoVal;
|
||||
break;
|
||||
case 5:
|
||||
m_undoVal = entry.panning;
|
||||
entry.panning = m_redoVal;
|
||||
break;
|
||||
case 6:
|
||||
m_undoVal = entry.defKey;
|
||||
entry.defKey = m_redoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
class SFXNameChangeUndoCommand : public EditorUndoCommand {
|
||||
amuse::SFXId m_sfx;
|
||||
std::string m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
|
||||
public:
|
||||
explicit SFXNameChangeUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, amuse::SFXId sfx,
|
||||
std::string_view redoVal)
|
||||
: EditorUndoCommand(node, text), m_sfx(sfx), m_redoVal(redoVal) {}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
|
||||
amuse::SFXId::CurNameDB->rename(m_sfx, m_undoVal);
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
|
||||
m_undoVal = amuse::SFXId::CurNameDB->resolveNameFromId(m_sfx);
|
||||
amuse::SFXId::CurNameDB->rename(m_sfx, m_redoVal);
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
SFXObjectDelegate::SFXObjectDelegate(QObject* parent) : BaseObjectDelegate(parent) {}
|
||||
|
||||
SFXObjectDelegate::~SFXObjectDelegate() = default;
|
||||
|
||||
ProjectModel::INode* SFXObjectDelegate::getNode(const QAbstractItemModel* __model, const QModelIndex& index) const {
|
||||
const SFXModel* model = static_cast<const SFXModel*>(__model);
|
||||
auto entry = model->m_sorted[index.row()];
|
||||
if (entry->second.objId.id.id == 0xffff)
|
||||
return nullptr;
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
|
||||
return group->pageObjectNodeOfId(entry->second.objId.id);
|
||||
}
|
||||
|
||||
QWidget* SFXObjectDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) const {
|
||||
const SFXModel* model = static_cast<const SFXModel*>(index.model());
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
|
||||
EditorFieldPageObjectNode* cb = new EditorFieldPageObjectNode(group, parent);
|
||||
connect(cb, &EditorFieldPageObjectNode::currentIndexChanged, this, &SFXObjectDelegate::objIndexChanged);
|
||||
return cb;
|
||||
}
|
||||
|
||||
void SFXObjectDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
|
||||
int idx = 0;
|
||||
if (ProjectModel::BasePoolObjectNode* node =
|
||||
static_cast<ProjectModel::BasePoolObjectNode*>(getNode(index.model(), index)))
|
||||
idx = g_MainWindow->projectModel()
|
||||
->getPageObjectProxy()
|
||||
->mapFromSource(g_MainWindow->projectModel()->index(node))
|
||||
.row();
|
||||
static_cast<EditorFieldPageObjectNode*>(editor)->setCurrentIndex(idx);
|
||||
if (static_cast<EditorFieldPageObjectNode*>(editor)->shouldPopupOpen())
|
||||
QApplication::postEvent(editor, new QEvent(QEvent::User));
|
||||
}
|
||||
|
||||
void SFXObjectDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, const QModelIndex& index) const {
|
||||
const SFXModel* model = static_cast<const SFXModel*>(m);
|
||||
auto entry = model->m_sorted[index.row()];
|
||||
ProjectModel::BasePoolObjectNode* node = static_cast<EditorFieldPageObjectNode*>(editor)->currentNode();
|
||||
amuse::ObjectId id;
|
||||
if (node)
|
||||
id = node->id();
|
||||
if (id == entry->second.objId.id) {
|
||||
emit m->dataChanged(index, index);
|
||||
return;
|
||||
}
|
||||
g_MainWindow->pushUndoCommand(new SFXDataChangeUndoCommand(
|
||||
model->m_node.get(), tr("Change %1").arg(m->headerData(1, Qt::Horizontal).toString()), entry->first, 1, id.id));
|
||||
emit m->dataChanged(index, index);
|
||||
}
|
||||
|
||||
void SFXObjectDelegate::objIndexChanged() { emit commitData(static_cast<QWidget*>(sender())); }
|
||||
|
||||
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& SFXModel::_getMap() const {
|
||||
return m_node->m_index->m_sfxEntries;
|
||||
}
|
||||
|
||||
void SFXModel::_buildSortedList() {
|
||||
m_sorted.clear();
|
||||
if (!m_node)
|
||||
return;
|
||||
auto& map = _getMap();
|
||||
m_sorted.reserve(map.size());
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
m_sorted.emplace_back(it);
|
||||
std::sort(m_sorted.begin(), m_sorted.end());
|
||||
}
|
||||
|
||||
QModelIndex SFXModel::_indexOfSFX(amuse::SFXId sfx) const {
|
||||
auto search = std::lower_bound(m_sorted.cbegin(), m_sorted.cend(), sfx);
|
||||
if (search == m_sorted.cend() || search->m_it->first != sfx)
|
||||
return QModelIndex();
|
||||
else
|
||||
return createIndex(search - m_sorted.begin(), 0);
|
||||
}
|
||||
|
||||
int SFXModel::_hypotheticalIndexOfSFX(const std::string& sfxName) const {
|
||||
auto search = std::lower_bound(m_sorted.cbegin(), m_sorted.cend(), sfxName);
|
||||
return search - m_sorted.begin();
|
||||
}
|
||||
|
||||
void SFXModel::loadData(ProjectModel::SoundGroupNode* node) {
|
||||
beginResetModel();
|
||||
m_node = node;
|
||||
_buildSortedList();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void SFXModel::unloadData() {
|
||||
beginResetModel();
|
||||
m_node.reset();
|
||||
m_sorted.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int SFXModel::rowCount(const QModelIndex& parent) const {
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
if (!m_node)
|
||||
return 0;
|
||||
return int(m_sorted.size()) + 1;
|
||||
}
|
||||
|
||||
int SFXModel::columnCount(const QModelIndex& parent) const {
|
||||
if (parent.isValid())
|
||||
return 0;
|
||||
return 7;
|
||||
}
|
||||
|
||||
QVariant SFXModel::data(const QModelIndex& index, int role) const {
|
||||
if (!m_node)
|
||||
return QVariant();
|
||||
if (index.row() == m_sorted.size())
|
||||
return QVariant();
|
||||
|
||||
auto entry = m_sorted[index.row()];
|
||||
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
switch (index.column()) {
|
||||
case 0: {
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
return QString::fromUtf8(amuse::SFXId::CurNameDB->resolveNameFromId(entry->first.id).data());
|
||||
}
|
||||
case 1: {
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(m_node.get());
|
||||
if (ProjectModel::BasePoolObjectNode* node = group->pageObjectNodeOfId(entry->second.objId.id))
|
||||
return node->text();
|
||||
return QVariant();
|
||||
}
|
||||
case 2:
|
||||
return entry->second.priority;
|
||||
case 3:
|
||||
return entry->second.maxVoices;
|
||||
case 4:
|
||||
return entry->second.defVel;
|
||||
case 5:
|
||||
return entry->second.panning;
|
||||
case 6:
|
||||
return entry->second.defKey;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool SFXModel::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||
if (!m_node || role != Qt::EditRole)
|
||||
return false;
|
||||
|
||||
auto& map = _getMap();
|
||||
auto entry = m_sorted[index.row()];
|
||||
|
||||
switch (index.column()) {
|
||||
case 0: {
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
auto utf8key = value.toString().toUtf8();
|
||||
std::unordered_map<std::string, amuse::ObjectId>::iterator idIt;
|
||||
// TODO: Heterogeneous lookup when C++20 available
|
||||
if ((idIt = amuse::SFXId::CurNameDB->m_stringToId.find(utf8key.data())) !=
|
||||
amuse::SFXId::CurNameDB->m_stringToId.cend()) {
|
||||
if (idIt->second == entry->first)
|
||||
return false;
|
||||
g_MainWindow->uiMessenger().critical(tr("SFX Conflict"),
|
||||
tr("SFX %1 is already defined in project").arg(value.toString()));
|
||||
return false;
|
||||
}
|
||||
int newIdx = _hypotheticalIndexOfSFX(utf8key.data());
|
||||
bool moving = beginMoveRows(index.parent(), index.row(), index.row(), index.parent(), newIdx);
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new SFXNameChangeUndoCommand(m_node.get(), tr("Change SFX Name"), entry->first, utf8key.data()));
|
||||
_buildSortedList();
|
||||
if (moving)
|
||||
endMoveRows();
|
||||
QModelIndex newIndex = _indexOfSFX(entry->first);
|
||||
emit dataChanged(newIndex, newIndex, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
}
|
||||
case 2:
|
||||
if (entry->second.priority == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 3:
|
||||
if (entry->second.maxVoices == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 4:
|
||||
if (entry->second.defVel == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 5:
|
||||
if (entry->second.panning == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 6:
|
||||
if (entry->second.defKey == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
g_MainWindow->pushUndoCommand(new SFXDataChangeUndoCommand(
|
||||
m_node.get(), tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()), entry->first,
|
||||
index.column(), value.toInt()));
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant SFXModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("SFX");
|
||||
case 1:
|
||||
return tr("Object");
|
||||
case 2:
|
||||
return tr("Priority");
|
||||
case 3:
|
||||
return tr("Max Voices");
|
||||
case 4:
|
||||
return tr("Velocity");
|
||||
case 5:
|
||||
return tr("Panning");
|
||||
case 6:
|
||||
return tr("Key");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags SFXModel::flags(const QModelIndex& index) const {
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
if (index.row() == m_sorted.size())
|
||||
return Qt::NoItemFlags;
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
class SFXRowUndoCommand : public EditorUndoCommand {
|
||||
protected:
|
||||
SFXTableView* m_view;
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> m_data;
|
||||
bool m_undid = false;
|
||||
void add() {
|
||||
m_view->selectionModel()->clearSelection();
|
||||
for (auto& p : m_data) {
|
||||
int row = static_cast<SFXModel*>(m_view->model())->_insertRow(p);
|
||||
m_view->setCurrentIndex(m_view->model()->index(row, 0));
|
||||
}
|
||||
}
|
||||
void del() {
|
||||
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it) {
|
||||
*it = static_cast<SFXModel*>(m_view->model())->_removeRow(std::get<0>(*it));
|
||||
}
|
||||
}
|
||||
void undo() override {
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo() override {
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit SFXRowUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
|
||||
};
|
||||
|
||||
class SFXRowAddUndoCommand : public SFXRowUndoCommand {
|
||||
using base = SFXRowUndoCommand;
|
||||
|
||||
public:
|
||||
explicit SFXRowAddUndoCommand(
|
||||
ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
|
||||
: SFXRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() override {
|
||||
base::undo();
|
||||
base::del();
|
||||
}
|
||||
void redo() override {
|
||||
base::redo();
|
||||
base::add();
|
||||
}
|
||||
};
|
||||
|
||||
class SFXRowDelUndoCommand : public SFXRowUndoCommand {
|
||||
using base = SFXRowUndoCommand;
|
||||
|
||||
public:
|
||||
explicit SFXRowDelUndoCommand(
|
||||
ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
|
||||
: SFXRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() override {
|
||||
base::undo();
|
||||
base::add();
|
||||
}
|
||||
void redo() override {
|
||||
base::redo();
|
||||
base::del();
|
||||
}
|
||||
};
|
||||
|
||||
int SFXModel::_insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data) {
|
||||
if (!m_node)
|
||||
return 0;
|
||||
auto& map = _getMap();
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SFXId::CurNameDB->registerPair(std::get<1>(data), std::get<0>(data));
|
||||
int idx = _hypotheticalIndexOfSFX(std::get<1>(data));
|
||||
beginInsertRows(QModelIndex(), idx, idx);
|
||||
map.emplace(std::make_pair(std::get<0>(data), std::get<2>(data)));
|
||||
_buildSortedList();
|
||||
endInsertRows();
|
||||
return idx;
|
||||
}
|
||||
|
||||
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> SFXModel::_removeRow(amuse::SFXId sfx) {
|
||||
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> ret;
|
||||
if (!m_node)
|
||||
return ret;
|
||||
auto& map = _getMap();
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
int idx = _indexOfSFX(sfx).row();
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
std::get<0>(ret) = sfx;
|
||||
std::get<1>(ret) = amuse::SFXId::CurNameDB->resolveNameFromId(sfx);
|
||||
auto search = map.find(sfx);
|
||||
if (search != map.cend()) {
|
||||
std::get<2>(ret) = search->second;
|
||||
amuse::SFXId::CurNameDB->remove(sfx);
|
||||
map.erase(search);
|
||||
}
|
||||
_buildSortedList();
|
||||
endRemoveRows();
|
||||
return ret;
|
||||
}
|
||||
|
||||
SFXModel::SFXModel(QObject* parent) : QAbstractTableModel(parent) {}
|
||||
|
||||
SFXModel::~SFXModel() = default;
|
||||
|
||||
void SFXTableView::deleteSelection() {
|
||||
QModelIndexList list = selectionModel()->selectedRows();
|
||||
if (list.isEmpty())
|
||||
return;
|
||||
std::sort(list.begin(), list.end(), [](const auto& a, const auto& b) { return a.row() < b.row(); });
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> data;
|
||||
data.reserve(list.size());
|
||||
for (QModelIndex idx : list) {
|
||||
auto& entry = *static_cast<SFXModel*>(model())->m_sorted[idx.row()].m_it;
|
||||
data.push_back({entry.first, {}, {}});
|
||||
}
|
||||
g_MainWindow->pushUndoCommand(new SFXRowDelUndoCommand(
|
||||
static_cast<SFXModel*>(model())->m_node.get(),
|
||||
data.size() > 1 ? tr("Delete SFX Entries") : tr("Delete SFX Entry"), this, std::move(data)));
|
||||
}
|
||||
|
||||
void SFXTableView::setModel(QAbstractItemModel* model) {
|
||||
QTableView::setModel(model);
|
||||
horizontalHeader()->setMinimumSectionSize(75);
|
||||
horizontalHeader()->resizeSection(0, 200);
|
||||
horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(2, 75);
|
||||
horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(3, 100);
|
||||
horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(4, 75);
|
||||
horizontalHeader()->setSectionResizeMode(5, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(5, 75);
|
||||
horizontalHeader()->setSectionResizeMode(6, QHeaderView::Fixed);
|
||||
horizontalHeader()->resizeSection(6, 75);
|
||||
}
|
||||
|
||||
SFXTableView::SFXTableView(QWidget* parent) : QTableView(parent) {
|
||||
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setGridStyle(Qt::NoPen);
|
||||
|
||||
m_127Delegate.setItemEditorFactory(&m_127Factory);
|
||||
m_255Delegate.setItemEditorFactory(&m_255Factory);
|
||||
|
||||
setItemDelegateForColumn(1, &m_sfxDelegate);
|
||||
setItemDelegateForColumn(2, &m_255Delegate);
|
||||
setItemDelegateForColumn(3, &m_255Delegate);
|
||||
setItemDelegateForColumn(4, &m_127Delegate);
|
||||
setItemDelegateForColumn(5, &m_127Delegate);
|
||||
setItemDelegateForColumn(6, &m_127Delegate);
|
||||
}
|
||||
|
||||
SFXTableView::~SFXTableView() = default;
|
||||
|
||||
void SFXPlayerWidget::clicked() {
|
||||
if (!m_vox) {
|
||||
m_vox = g_MainWindow->startSFX(m_groupId, m_sfxId);
|
||||
if (m_vox) {
|
||||
m_playAction.setText(tr("Stop"));
|
||||
m_playAction.setIcon(QIcon(QStringLiteral(":/icons/IconStop.svg")));
|
||||
}
|
||||
} else {
|
||||
stopped();
|
||||
}
|
||||
}
|
||||
|
||||
void SFXPlayerWidget::stopped() {
|
||||
m_vox->keyOff();
|
||||
m_vox.reset();
|
||||
m_playAction.setText(tr("Play"));
|
||||
m_playAction.setIcon(QIcon(QStringLiteral(":/icons/IconSoundMacro.svg")));
|
||||
}
|
||||
|
||||
void SFXPlayerWidget::resizeEvent(QResizeEvent* event) {
|
||||
m_button.setGeometry(event->size().width() - event->size().height(), 0, event->size().height(),
|
||||
event->size().height());
|
||||
}
|
||||
|
||||
void SFXPlayerWidget::mouseDoubleClickEvent(QMouseEvent* event) {
|
||||
qobject_cast<QTableView*>(parentWidget()->parentWidget())->setIndexWidget(m_index, nullptr);
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void SFXPlayerWidget::mousePressEvent(QMouseEvent* event) {
|
||||
if (event->button() == Qt::RightButton) {
|
||||
QTableView* view = qobject_cast<QTableView*>(parentWidget()->parentWidget());
|
||||
QAbstractItemDelegate* delegate = view->itemDelegateForColumn(1);
|
||||
delegate->editorEvent(event, view->model(), {}, m_index);
|
||||
}
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
SFXPlayerWidget::~SFXPlayerWidget() {
|
||||
if (m_vox)
|
||||
m_vox->keyOff();
|
||||
}
|
||||
|
||||
SFXPlayerWidget::SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id, QWidget* parent)
|
||||
: QWidget(parent), m_playAction(tr("Play")), m_button(this), m_index(index), m_groupId(gid), m_sfxId(id) {
|
||||
m_playAction.setIcon(QIcon(QStringLiteral(":/icons/IconSoundMacro.svg")));
|
||||
m_button.setDefaultAction(&m_playAction);
|
||||
connect(&m_playAction, &QAction::triggered, this, &SFXPlayerWidget::clicked);
|
||||
}
|
||||
|
||||
bool SoundGroupEditor::loadData(ProjectModel::SoundGroupNode* node) {
|
||||
m_sfxs.loadData(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundGroupEditor::unloadData() { m_sfxs.unloadData(); }
|
||||
|
||||
ProjectModel::INode* SoundGroupEditor::currentNode() const { return m_sfxs.m_node.get(); }
|
||||
|
||||
void SoundGroupEditor::resizeEvent(QResizeEvent* ev) {
|
||||
m_sfxTable->setGeometry(QRect({}, ev->size()));
|
||||
m_addRemoveButtons.move(0, ev->size().height() - 32);
|
||||
}
|
||||
|
||||
AmuseItemEditFlags SoundGroupEditor::itemEditFlags() const {
|
||||
return (m_sfxTable->hasFocus() && !m_sfxTable->selectionModel()->selectedRows().isEmpty()) ? AmuseItemDelete
|
||||
: AmuseItemNone;
|
||||
}
|
||||
|
||||
void SoundGroupEditor::doAdd() {
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_sfxs.m_node.get());
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> data;
|
||||
amuse::SFXId sfxId = amuse::SFXId::CurNameDB->generateId(amuse::NameDB::Type::SFX);
|
||||
std::string sfxName = amuse::SFXId::CurNameDB->generateName(sfxId, amuse::NameDB::Type::SFX);
|
||||
data.emplace_back(sfxId, sfxName, amuse::SFXGroupIndex::SFXEntry{});
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new SFXRowAddUndoCommand(m_sfxs.m_node.get(), tr("Add SFX Entry"), m_sfxTable, std::move(data)));
|
||||
}
|
||||
|
||||
void SoundGroupEditor::doSelectionChanged() {
|
||||
m_addRemoveButtons.removeAction()->setDisabled(m_sfxTable->selectionModel()->selectedRows().isEmpty());
|
||||
g_MainWindow->updateFocus();
|
||||
}
|
||||
|
||||
void SoundGroupEditor::rowsInserted(const QModelIndex& parent, int first, int last) {
|
||||
m_sfxTable->scrollTo(m_sfxTable->model()->index(first, 0));
|
||||
sfxDataChanged();
|
||||
}
|
||||
|
||||
void SoundGroupEditor::rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination,
|
||||
int row) {
|
||||
m_sfxTable->scrollTo(m_sfxTable->model()->index(row, 0));
|
||||
sfxDataChanged();
|
||||
}
|
||||
|
||||
void SoundGroupEditor::sfxDataChanged() {
|
||||
int idx = 0;
|
||||
for (const auto& p : m_sfxs.m_sorted) {
|
||||
QModelIndex index = m_sfxs.index(idx, 1);
|
||||
SFXPlayerWidget* w = qobject_cast<SFXPlayerWidget*>(m_sfxTable->indexWidget(index));
|
||||
if (!w || w->sfxId() != p->first) {
|
||||
SFXPlayerWidget* newW = new SFXPlayerWidget(index, m_sfxs.m_node->m_id, p->first, m_sfxTable->viewport());
|
||||
m_sfxTable->setIndexWidget(index, newW);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundGroupEditor::itemDeleteAction() { m_sfxTable->deleteSelection(); }
|
||||
|
||||
SoundGroupEditor::SoundGroupEditor(QWidget* parent)
|
||||
: EditorWidget(parent), m_sfxs(this), m_sfxTable(new SFXTableView(this)), m_addRemoveButtons(this) {
|
||||
m_sfxTable->setModel(&m_sfxs);
|
||||
connect(m_sfxTable->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
&SoundGroupEditor::doSelectionChanged);
|
||||
|
||||
connect(&m_sfxs, &SFXModel::rowsInserted, this, &SoundGroupEditor::rowsInserted);
|
||||
connect(&m_sfxs, &SFXModel::rowsRemoved, this, &SoundGroupEditor::sfxDataChanged);
|
||||
connect(&m_sfxs, &SFXModel::dataChanged, this, &SoundGroupEditor::sfxDataChanged);
|
||||
connect(&m_sfxs, &SFXModel::rowsMoved, this, &SoundGroupEditor::rowsMoved);
|
||||
connect(&m_sfxs, &SFXModel::modelReset, this, &SoundGroupEditor::sfxDataChanged);
|
||||
|
||||
m_addRemoveButtons.addAction()->setToolTip(tr("Add new SFX entry"));
|
||||
connect(m_addRemoveButtons.addAction(), &QAction::triggered, this, &SoundGroupEditor::doAdd);
|
||||
m_addRemoveButtons.removeAction()->setToolTip(tr("Remove selected SFX entries"));
|
||||
connect(m_addRemoveButtons.removeAction(), &QAction::triggered, this, &SoundGroupEditor::itemDeleteAction);
|
||||
}
|
||||
|
||||
SoundGroupEditor::~SoundGroupEditor() = default;
|
|
@ -0,0 +1,146 @@
|
|||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <QAction>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTableView>
|
||||
#include <QToolButton>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/AudioGroupProject.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
#include <amuse/Voice.hpp>
|
||||
|
||||
class SFXObjectDelegate : public BaseObjectDelegate {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
|
||||
public:
|
||||
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
|
||||
~SFXObjectDelegate() override;
|
||||
|
||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
|
||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
|
||||
|
||||
private slots:
|
||||
void objIndexChanged();
|
||||
};
|
||||
|
||||
class SFXModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
friend class SoundGroupEditor;
|
||||
friend class SFXObjectDelegate;
|
||||
friend class SFXTableView;
|
||||
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
|
||||
struct Iterator {
|
||||
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
|
||||
ItTp m_it;
|
||||
Iterator(ItTp it) : m_it(it) {}
|
||||
ItTp::pointer operator->() const { return m_it.operator->(); }
|
||||
bool operator<(const Iterator& other) const {
|
||||
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||
amuse::SFXId::CurNameDB->resolveNameFromId(other.m_it->first);
|
||||
}
|
||||
bool operator<(amuse::SongId other) const {
|
||||
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||
amuse::SFXId::CurNameDB->resolveNameFromId(other);
|
||||
}
|
||||
bool operator<(const std::string& name) const {
|
||||
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) < name;
|
||||
}
|
||||
};
|
||||
std::vector<Iterator> m_sorted;
|
||||
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& _getMap() const;
|
||||
void _buildSortedList();
|
||||
QModelIndex _indexOfSFX(amuse::SFXId sfx) const;
|
||||
int _hypotheticalIndexOfSFX(const std::string& sfxName) const;
|
||||
|
||||
public:
|
||||
explicit SFXModel(QObject* parent = Q_NULLPTR);
|
||||
~SFXModel() override;
|
||||
|
||||
void loadData(ProjectModel::SoundGroupNode* node);
|
||||
void unloadData();
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
int _insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data);
|
||||
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> _removeRow(amuse::SFXId sfx);
|
||||
};
|
||||
|
||||
class SFXTableView : public QTableView {
|
||||
Q_OBJECT
|
||||
SFXObjectDelegate m_sfxDelegate;
|
||||
RangedValueFactory<0, 127> m_127Factory;
|
||||
RangedValueFactory<0, 255> m_255Factory;
|
||||
QStyledItemDelegate m_127Delegate, m_255Delegate;
|
||||
|
||||
public:
|
||||
explicit SFXTableView(QWidget* parent = Q_NULLPTR);
|
||||
~SFXTableView() override;
|
||||
|
||||
void setModel(QAbstractItemModel* model) override;
|
||||
void deleteSelection();
|
||||
};
|
||||
|
||||
class SFXPlayerWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
QAction m_playAction;
|
||||
QToolButton m_button;
|
||||
QModelIndex m_index;
|
||||
amuse::GroupId m_groupId;
|
||||
amuse::SFXId m_sfxId;
|
||||
amuse::ObjToken<amuse::Voice> m_vox;
|
||||
|
||||
public:
|
||||
explicit SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id, QWidget* parent = Q_NULLPTR);
|
||||
~SFXPlayerWidget() override;
|
||||
amuse::SongId sfxId() const { return m_sfxId; }
|
||||
amuse::Voice* voice() const { return m_vox.get(); }
|
||||
void stopped();
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
public slots:
|
||||
void clicked();
|
||||
};
|
||||
|
||||
class SoundGroupEditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
SFXModel m_sfxs;
|
||||
SFXTableView* m_sfxTable;
|
||||
AddRemoveButtons m_addRemoveButtons;
|
||||
|
||||
public:
|
||||
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
|
||||
~SoundGroupEditor() override;
|
||||
|
||||
bool loadData(ProjectModel::SoundGroupNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
void setEditorEnabled(bool en) override {}
|
||||
void resizeEvent(QResizeEvent* ev) override;
|
||||
QTableView* getSFXListView() const { return m_sfxTable; }
|
||||
AmuseItemEditFlags itemEditFlags() const override;
|
||||
|
||||
private slots:
|
||||
void rowsInserted(const QModelIndex& parent, int first, int last);
|
||||
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||
void doAdd();
|
||||
void doSelectionChanged();
|
||||
void sfxDataChanged();
|
||||
void itemDeleteAction() override;
|
||||
};
|
|
@ -0,0 +1,200 @@
|
|||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QStaticText>
|
||||
#include <QTreeWidget>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
#include <amuse/AudioGroupPool.hpp>
|
||||
#include <amuse/Common.hpp>
|
||||
|
||||
class CatalogueItem;
|
||||
class SoundMacroEditor;
|
||||
class SoundMacroListing;
|
||||
|
||||
class QLayoutItem;
|
||||
class QPropertyAnimation;
|
||||
class QSplitter;
|
||||
class QVBoxLayout;
|
||||
|
||||
class TargetButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TargetButton(QWidget* parent = Q_NULLPTR);
|
||||
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
void focusOutEvent(QFocusEvent* event) override { event->ignore(); }
|
||||
void keyPressEvent(QKeyEvent* event) override { event->ignore(); }
|
||||
};
|
||||
|
||||
class FieldSoundMacroStep : public QWidget {
|
||||
friend class CommandWidget;
|
||||
Q_OBJECT
|
||||
FieldProjectNode* m_macroField;
|
||||
FieldSpinBox m_spinBox;
|
||||
TargetButton m_targetButton;
|
||||
SoundMacroEditor* getEditor() const;
|
||||
SoundMacroListing* getListing() const;
|
||||
signals:
|
||||
void valueChanged(int);
|
||||
public slots:
|
||||
void targetPressed();
|
||||
void updateMacroField();
|
||||
|
||||
public:
|
||||
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
||||
~FieldSoundMacroStep() override;
|
||||
void setIndex(int index);
|
||||
void cancel();
|
||||
};
|
||||
|
||||
class CommandWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class SoundMacroListing;
|
||||
QFont m_numberFont;
|
||||
QLabel m_titleLabel;
|
||||
ListingDeleteButton m_deleteButton;
|
||||
QStaticText m_numberText;
|
||||
int m_index = -1;
|
||||
amuse::SoundMacro::ICmd* m_cmd;
|
||||
const amuse::SoundMacro::CmdIntrospection* m_introspection;
|
||||
FieldSoundMacroStep* m_stepField = nullptr;
|
||||
void setIndex(int index);
|
||||
SoundMacroListing* getParent() const;
|
||||
private slots:
|
||||
void boolChanged(int);
|
||||
void numChanged(int);
|
||||
void nodeChanged(int);
|
||||
void deleteClicked();
|
||||
|
||||
private:
|
||||
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
||||
|
||||
public:
|
||||
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
|
||||
CommandWidget(QWidget* parent, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
||||
~CommandWidget() override;
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
QString getText() const { return m_titleLabel.text(); }
|
||||
};
|
||||
|
||||
class CommandWidgetContainer : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class SoundMacroListing;
|
||||
CommandWidget* m_commandWidget;
|
||||
QPropertyAnimation* m_animation = nullptr;
|
||||
void animateOpen();
|
||||
void animateClosed();
|
||||
void snapOpen();
|
||||
void snapClosed();
|
||||
|
||||
private slots:
|
||||
void animationDestroyed();
|
||||
|
||||
public:
|
||||
template <class... _Args>
|
||||
CommandWidgetContainer(QWidget* parent, _Args&&... args);
|
||||
~CommandWidgetContainer() override;
|
||||
};
|
||||
|
||||
class SoundMacroListing : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class CommandWidget;
|
||||
friend class SoundMacroEditor;
|
||||
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
|
||||
QVBoxLayout* m_layout;
|
||||
QLayoutItem* m_dragItem = nullptr;
|
||||
int m_origIdx = -1;
|
||||
int m_dragOpenIdx = -1;
|
||||
CommandWidgetContainer* m_prevDragOpen = nullptr;
|
||||
int m_autoscrollTimer = -1;
|
||||
int m_autoscrollDelta = 0;
|
||||
QWidget* m_autoscrollSource = nullptr;
|
||||
QMouseEvent* m_autoscrollEvent = nullptr;
|
||||
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
|
||||
void stopAutoscroll();
|
||||
bool beginDrag(CommandWidget* widget);
|
||||
void endDrag();
|
||||
void cancelDrag();
|
||||
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||
void moveDrag(CommandWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||
void insertDragout();
|
||||
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
|
||||
void deleteCommand(int index);
|
||||
void reindex();
|
||||
void clear();
|
||||
|
||||
public:
|
||||
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
|
||||
~SoundMacroListing() override;
|
||||
|
||||
bool loadData(ProjectModel::SoundMacroNode* node);
|
||||
void unloadData();
|
||||
ProjectModel::INode* currentNode() const;
|
||||
void timerEvent(QTimerEvent* event) override;
|
||||
};
|
||||
|
||||
class CatalogueItem : public QWidget {
|
||||
Q_OBJECT
|
||||
amuse::SoundMacro::CmdOp m_op;
|
||||
QLabel m_iconLab;
|
||||
QLabel m_label;
|
||||
|
||||
public:
|
||||
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name, const QString& doc,
|
||||
QWidget* parent = Q_NULLPTR);
|
||||
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
|
||||
~CatalogueItem() override;
|
||||
|
||||
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
|
||||
QString getText() const { return m_label.text(); }
|
||||
};
|
||||
|
||||
class SoundMacroCatalogue : public QTreeWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
};
|
||||
|
||||
class SoundMacroEditor : public EditorWidget {
|
||||
Q_OBJECT
|
||||
friend class SoundMacroCatalogue;
|
||||
friend class FieldSoundMacroStep;
|
||||
QSplitter* m_splitter;
|
||||
SoundMacroListing* m_listing;
|
||||
SoundMacroCatalogue* m_catalogue;
|
||||
CommandWidget* m_draggedCmd = nullptr;
|
||||
CatalogueItem* m_draggedItem = nullptr;
|
||||
FieldSoundMacroStep* m_targetField = nullptr;
|
||||
QPoint m_draggedPt;
|
||||
int m_dragInsertIdx = -1;
|
||||
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
||||
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
||||
void beginStepTarget(FieldSoundMacroStep* stepField);
|
||||
void endStepTarget();
|
||||
|
||||
public:
|
||||
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
|
||||
~SoundMacroEditor() override;
|
||||
|
||||
bool loadData(ProjectModel::SoundMacroNode* node);
|
||||
void unloadData() override;
|
||||
ProjectModel::INode* currentNode() const override;
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
public slots:
|
||||
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
#include "StatusBarWidget.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
FXButton::FXButton(QWidget* parent) : QPushButton(parent) {
|
||||
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
|
||||
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
|
||||
}
|
||||
|
||||
StatusBarWidget::StatusBarWidget(QWidget* parent)
|
||||
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal), m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal) {
|
||||
addWidget(&m_normalMessage);
|
||||
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
|
||||
m_killButton.setVisible(false);
|
||||
m_killButton.setToolTip(tr("Immediately kill active voices"));
|
||||
m_voiceCount.setVisible(false);
|
||||
for (size_t i = 0; i < m_volumeIcons.size(); i++) {
|
||||
m_volumeIcons[i] = QIcon(QStringLiteral(":/icons/IconVolume%1.svg").arg(i));
|
||||
}
|
||||
m_aIcon.setFixedSize(16, 16);
|
||||
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
|
||||
QString aTip = tr("Aux A send level for all voices");
|
||||
m_aIcon.setToolTip(aTip);
|
||||
m_aSlider.setRange(0, 100);
|
||||
m_aSlider.setFixedWidth(100);
|
||||
m_aSlider.setToolTip(aTip);
|
||||
m_bIcon.setFixedSize(16, 16);
|
||||
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
|
||||
QString bTip = tr("Aux B send level for all voices");
|
||||
m_bIcon.setToolTip(bTip);
|
||||
m_bSlider.setRange(0, 100);
|
||||
m_bSlider.setFixedWidth(100);
|
||||
m_bSlider.setToolTip(bTip);
|
||||
m_volumeIcon.setFixedSize(16, 16);
|
||||
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
|
||||
QString volTip = tr("Master volume level");
|
||||
m_volumeIcon.setToolTip(volTip);
|
||||
connect(&m_volumeSlider, qOverload<int>(&QSlider::valueChanged), this, &StatusBarWidget::volumeChanged);
|
||||
m_volumeSlider.setRange(0, 100);
|
||||
m_volumeSlider.setFixedWidth(100);
|
||||
m_volumeSlider.setToolTip(volTip);
|
||||
addPermanentWidget(&m_voiceCount);
|
||||
addPermanentWidget(&m_killButton);
|
||||
addPermanentWidget(&m_fxButton);
|
||||
addPermanentWidget(&m_aIcon);
|
||||
addPermanentWidget(&m_aSlider);
|
||||
addPermanentWidget(&m_bIcon);
|
||||
addPermanentWidget(&m_bSlider);
|
||||
addPermanentWidget(&m_volumeIcon);
|
||||
addPermanentWidget(&m_volumeSlider);
|
||||
}
|
||||
|
||||
StatusBarWidget::~StatusBarWidget() = default;
|
||||
|
||||
void StatusBarWidget::setVoiceCount(int voices) {
|
||||
if (voices != m_cachedVoiceCount) {
|
||||
m_voiceCount.setText(QString::number(voices));
|
||||
m_cachedVoiceCount = voices;
|
||||
setKillVisible(voices != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBarWidget::volumeChanged(int vol) {
|
||||
const int idx = int(std::round(vol * (3.f / 100.f)));
|
||||
|
||||
if (idx == m_lastVolIdx) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastVolIdx = idx;
|
||||
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
|
||||
}
|
||||
|
||||
void StatusBarFocus::setMessage(const QString& message) {
|
||||
m_message = message;
|
||||
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
|
||||
if (widget->m_curFocus == this) {
|
||||
if (m_message.isEmpty())
|
||||
widget->clearMessage();
|
||||
else
|
||||
widget->showMessage(m_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBarFocus::enter() {
|
||||
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
|
||||
widget->m_curFocus = this;
|
||||
if (m_message.isEmpty())
|
||||
widget->clearMessage();
|
||||
else
|
||||
widget->showMessage(m_message);
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBarFocus::exit() {
|
||||
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
|
||||
if (widget->m_curFocus == this) {
|
||||
widget->clearMessage();
|
||||
widget->m_curFocus = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QSlider>
|
||||
#include <QStatusBar>
|
||||
|
||||
class StatusBarFocus;
|
||||
|
||||
class FXButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FXButton(QWidget* parent = Q_NULLPTR);
|
||||
void mouseReleaseEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); }
|
||||
void focusOutEvent(QFocusEvent* event) override { event->ignore(); }
|
||||
void keyPressEvent(QKeyEvent* event) override { event->ignore(); }
|
||||
};
|
||||
|
||||
class StatusBarWidget : public QStatusBar {
|
||||
friend class StatusBarFocus;
|
||||
Q_OBJECT
|
||||
QLabel m_normalMessage;
|
||||
QPushButton m_killButton;
|
||||
FXButton m_fxButton;
|
||||
std::array<QIcon, 4> m_volumeIcons;
|
||||
QLabel m_volumeIcon;
|
||||
QSlider m_volumeSlider;
|
||||
QLabel m_aIcon;
|
||||
QSlider m_aSlider;
|
||||
QLabel m_bIcon;
|
||||
QSlider m_bSlider;
|
||||
int m_lastVolIdx = 0;
|
||||
QLabel m_voiceCount;
|
||||
int m_cachedVoiceCount = -1;
|
||||
StatusBarFocus* m_curFocus = nullptr;
|
||||
void setKillVisible(bool vis) {
|
||||
m_killButton.setVisible(vis);
|
||||
m_voiceCount.setVisible(vis);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
|
||||
~StatusBarWidget() override;
|
||||
|
||||
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
|
||||
void setVoiceCount(int voices);
|
||||
|
||||
template <typename Receiver>
|
||||
void connectKillClicked(const Receiver* receiver, void (Receiver::*method)()) {
|
||||
connect(&m_killButton, &QPushButton::clicked, receiver, method);
|
||||
}
|
||||
template <typename Receiver>
|
||||
void connectFXPressed(const Receiver* receiver, void (Receiver::*method)()) {
|
||||
connect(&m_fxButton, &FXButton::pressed, receiver, method);
|
||||
}
|
||||
void setFXDown(bool down) { m_fxButton.setDown(down); }
|
||||
|
||||
template <typename Receiver>
|
||||
void connectVolumeSlider(const Receiver* receiver, void (Receiver::*method)(int)) {
|
||||
connect(&m_volumeSlider, qOverload<int>(&QSlider::valueChanged), receiver, method);
|
||||
}
|
||||
template <typename Receiver>
|
||||
void connectASlider(const Receiver* receiver, void (Receiver::*method)(int)) {
|
||||
connect(&m_aSlider, qOverload<int>(&QSlider::valueChanged), receiver, method);
|
||||
}
|
||||
template <typename Receiver>
|
||||
void connectBSlider(const Receiver* receiver, void (Receiver::*method)(int)) {
|
||||
connect(&m_bSlider, qOverload<int>(&QSlider::valueChanged), receiver, method);
|
||||
}
|
||||
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
|
||||
|
||||
private slots:
|
||||
void volumeChanged(int vol);
|
||||
};
|
||||
|
||||
class StatusBarFocus : public QObject {
|
||||
Q_OBJECT
|
||||
QString m_message;
|
||||
|
||||
public:
|
||||
explicit StatusBarFocus(StatusBarWidget* statusWidget) : QObject(statusWidget) {}
|
||||
~StatusBarFocus() override { exit(); }
|
||||
void setMessage(const QString& message);
|
||||
void enter();
|
||||
void exit();
|
||||
};
|
|
@ -0,0 +1,976 @@
|
|||
#include "StudioSetupWidget.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QScrollBar>
|
||||
#include <QStylePainter>
|
||||
|
||||
#include <amuse/EffectBase.hpp>
|
||||
#include <amuse/EffectChorus.hpp>
|
||||
#include <amuse/EffectDelay.hpp>
|
||||
#include <amuse/EffectReverb.hpp>
|
||||
#include <amuse/Studio.hpp>
|
||||
#include <amuse/Submix.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
struct EffectIntrospection {
|
||||
struct Field {
|
||||
enum class Type {
|
||||
Invalid,
|
||||
UInt32,
|
||||
UInt32x8,
|
||||
Float,
|
||||
};
|
||||
|
||||
Type m_tp{};
|
||||
std::string_view m_name;
|
||||
float m_min{};
|
||||
float m_max{};
|
||||
float m_default{};
|
||||
};
|
||||
amuse::EffectType m_tp;
|
||||
std::string_view m_name;
|
||||
std::string_view m_description;
|
||||
std::array<Field, 7> m_fields;
|
||||
};
|
||||
|
||||
namespace {
|
||||
constexpr EffectIntrospection ReverbStdIntrospective = {
|
||||
amuse::EffectType::ReverbStd,
|
||||
"Reverb Std"sv,
|
||||
"Standard Reverb"sv,
|
||||
{
|
||||
{{EffectIntrospection::Field::Type::Float, "Coloration"sv, 0.f, 1.f, 0.f},
|
||||
{EffectIntrospection::Field::Type::Float, "Mix"sv, 0.f, 1.f, 0.f},
|
||||
{EffectIntrospection::Field::Type::Float, "Time"sv, 0.01f, 10.f, 0.01f},
|
||||
{EffectIntrospection::Field::Type::Float, "Damping"sv, 0.f, 1.f, 0.f},
|
||||
{EffectIntrospection::Field::Type::Float, "Pre Delay"sv, 0.f, 0.1f, 0.f}},
|
||||
},
|
||||
};
|
||||
|
||||
using ReverbStdGetFunc = float (amuse::EffectReverbStd::*)() const;
|
||||
using ReverbStdSetFunc = void (amuse::EffectReverbStd::*)(float);
|
||||
constexpr std::array<ReverbStdGetFunc, 5> ReverbStdGetters{
|
||||
&amuse::EffectReverbStd::getColoration, &amuse::EffectReverbStd::getMix, &amuse::EffectReverbStd::getTime,
|
||||
&amuse::EffectReverbStd::getDamping, &amuse::EffectReverbStd::getPreDelay,
|
||||
};
|
||||
constexpr std::array<ReverbStdSetFunc, 5> ReverbStdSetters{
|
||||
&amuse::EffectReverbStd::setColoration, &amuse::EffectReverbStd::setMix, &amuse::EffectReverbStd::setTime,
|
||||
&amuse::EffectReverbStd::setDamping, &amuse::EffectReverbStd::setPreDelay,
|
||||
};
|
||||
|
||||
constexpr EffectIntrospection ReverbHiIntrospective = {
|
||||
amuse::EffectType::ReverbHi,
|
||||
"Reverb Hi"sv,
|
||||
"High Reverb"sv,
|
||||
{
|
||||
{{EffectIntrospection::Field::Type::Float, "Coloration"sv, 0.f, 1.f, 0.f},
|
||||
{EffectIntrospection::Field::Type::Float, "Mix"sv, 0.f, 1.f, 0.f},
|
||||
{EffectIntrospection::Field::Type::Float, "Time"sv, 0.01f, 10.f, 0.01f},
|
||||
{EffectIntrospection::Field::Type::Float, "Damping"sv, 0.f, 1.f, 0.f},
|
||||
{EffectIntrospection::Field::Type::Float, "Pre Delay"sv, 0.f, 0.1f, 0.f},
|
||||
{EffectIntrospection::Field::Type::Float, "Crosstalk"sv, 0.f, 1.f, 0.f}},
|
||||
},
|
||||
};
|
||||
|
||||
using ReverbHiGetFunc = float (amuse::EffectReverbHi::*)() const;
|
||||
using ReverbHiSetFunc = void (amuse::EffectReverbHi::*)(float);
|
||||
constexpr std::array<ReverbHiGetFunc, 6> ReverbHiGetters{
|
||||
&amuse::EffectReverbHi::getColoration, &amuse::EffectReverbHi::getMix, &amuse::EffectReverbHi::getTime,
|
||||
&amuse::EffectReverbHi::getDamping, &amuse::EffectReverbHi::getPreDelay, &amuse::EffectReverbHi::getCrosstalk,
|
||||
};
|
||||
constexpr std::array<ReverbHiSetFunc, 6> ReverbHiSetters{
|
||||
&amuse::EffectReverbHi::setColoration, &amuse::EffectReverbHi::setMix, &amuse::EffectReverbHi::setTime,
|
||||
&amuse::EffectReverbHi::setDamping, &amuse::EffectReverbHi::setPreDelay, &amuse::EffectReverbHi::setCrosstalk,
|
||||
};
|
||||
|
||||
constexpr EffectIntrospection DelayIntrospective = {
|
||||
amuse::EffectType::Delay,
|
||||
"Delay"sv,
|
||||
"Delay"sv,
|
||||
{{
|
||||
{EffectIntrospection::Field::Type::UInt32x8, "Delay"sv, 10, 5000, 10},
|
||||
{EffectIntrospection::Field::Type::UInt32x8, "Feedback"sv, 0, 100, 0},
|
||||
{EffectIntrospection::Field::Type::UInt32x8, "Output"sv, 0, 100, 0},
|
||||
}},
|
||||
};
|
||||
|
||||
using DelayGetFunc = uint32_t (amuse::EffectDelay::*)(int) const;
|
||||
using DelaySetFunc = void (amuse::EffectDelay::*)(int, uint32_t);
|
||||
constexpr std::array<DelayGetFunc, 3> DelayGetters{
|
||||
&amuse::EffectDelay::getChanDelay,
|
||||
&amuse::EffectDelay::getChanFeedback,
|
||||
&amuse::EffectDelay::getChanOutput,
|
||||
};
|
||||
constexpr std::array<DelaySetFunc, 3> DelaySetters{
|
||||
&amuse::EffectDelay::setChanDelay,
|
||||
&amuse::EffectDelay::setChanFeedback,
|
||||
&amuse::EffectDelay::setChanOutput,
|
||||
};
|
||||
|
||||
constexpr EffectIntrospection ChorusIntrospective = {
|
||||
amuse::EffectType::Chorus,
|
||||
"Chorus"sv,
|
||||
"Chorus"sv,
|
||||
{{
|
||||
{EffectIntrospection::Field::Type::UInt32, "Base Delay"sv, 5, 15, 5},
|
||||
{EffectIntrospection::Field::Type::UInt32, "Variation"sv, 0, 5, 0},
|
||||
{EffectIntrospection::Field::Type::UInt32, "Period"sv, 500, 10000, 500},
|
||||
}},
|
||||
};
|
||||
|
||||
using ChorusGetFunc = uint32_t (amuse::EffectChorus::*)() const;
|
||||
using ChorusSetFunc = void (amuse::EffectChorus::*)(uint32_t);
|
||||
constexpr std::array<ChorusGetFunc, 3> ChorusGetters{
|
||||
&amuse::EffectChorus::getBaseDelay,
|
||||
&amuse::EffectChorus::getVariation,
|
||||
&amuse::EffectChorus::getPeriod,
|
||||
};
|
||||
constexpr std::array<ChorusSetFunc, 3> ChorusSetters{
|
||||
&amuse::EffectChorus::setBaseDelay,
|
||||
&amuse::EffectChorus::setVariation,
|
||||
&amuse::EffectChorus::setPeriod,
|
||||
};
|
||||
|
||||
constexpr const EffectIntrospection* GetEffectIntrospection(amuse::EffectType type) {
|
||||
switch (type) {
|
||||
case amuse::EffectType::ReverbStd:
|
||||
return &ReverbStdIntrospective;
|
||||
case amuse::EffectType::ReverbHi:
|
||||
return &ReverbHiIntrospective;
|
||||
case amuse::EffectType::Delay:
|
||||
return &DelayIntrospective;
|
||||
case amuse::EffectType::Chorus:
|
||||
return &ChorusIntrospective;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetEffectParm(const amuse::EffectBaseTypeless* effect, int idx, int chanIdx) {
|
||||
switch (effect->Isa()) {
|
||||
case amuse::EffectType::ReverbStd:
|
||||
return (static_cast<const amuse::EffectReverbStdImp<float>*>(effect)->*ReverbStdGetters[idx])();
|
||||
case amuse::EffectType::ReverbHi:
|
||||
return (static_cast<const amuse::EffectReverbHiImp<float>*>(effect)->*ReverbHiGetters[idx])();
|
||||
case amuse::EffectType::Delay:
|
||||
return (static_cast<const amuse::EffectDelayImp<float>*>(effect)->*DelayGetters[idx])(chanIdx);
|
||||
case amuse::EffectType::Chorus:
|
||||
return (static_cast<const amuse::EffectChorusImp<float>*>(effect)->*ChorusGetters[idx])();
|
||||
default:
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetEffectParm(amuse::EffectBaseTypeless* effect, int idx, int chanIdx, T val) {
|
||||
switch (effect->Isa()) {
|
||||
case amuse::EffectType::ReverbStd:
|
||||
(static_cast<amuse::EffectReverbStdImp<float>*>(effect)->*ReverbStdSetters[idx])(val);
|
||||
break;
|
||||
case amuse::EffectType::ReverbHi:
|
||||
(static_cast<amuse::EffectReverbHiImp<float>*>(effect)->*ReverbHiSetters[idx])(val);
|
||||
break;
|
||||
case amuse::EffectType::Delay:
|
||||
(static_cast<amuse::EffectDelayImp<float>*>(effect)->*DelaySetters[idx])(chanIdx, val);
|
||||
break;
|
||||
case amuse::EffectType::Chorus:
|
||||
(static_cast<amuse::EffectChorusImp<float>*>(effect)->*ChorusSetters[idx])(val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int NumChanNames = 8;
|
||||
constexpr std::array<const char*, NumChanNames> ChanNames{
|
||||
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Left"), QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Right"),
|
||||
QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Left"), QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Right"),
|
||||
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Center"), QT_TRANSLATE_NOOP("Uint32X8Popup", "LFE"),
|
||||
QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Left"), QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Right"),
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 4> EffectStrings{
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"),
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"),
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus"),
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 4> EffectDocStrings{
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb Standard"),
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Reverb High"),
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Delay"),
|
||||
QT_TRANSLATE_NOOP("EffectCatalogue", "Chorus"),
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
Uint32X8Popup::Uint32X8Popup(int min, int max, QWidget* parent) : QFrame(parent, Qt::Popup) {
|
||||
setAttribute(Qt::WA_WindowPropagation);
|
||||
setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
|
||||
Uint32X8Button* combo = static_cast<Uint32X8Button*>(parent);
|
||||
QStyleOptionComboBox opt = combo->comboStyleOption();
|
||||
setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
|
||||
|
||||
QGridLayout* layout = new QGridLayout;
|
||||
for (int i = 0; i < NumChanNames; ++i) {
|
||||
layout->addWidget(new QLabel(tr(ChanNames[i])), i, 0);
|
||||
FieldSlider* slider = new FieldSlider(this);
|
||||
m_sliders[i] = slider;
|
||||
slider->setToolTip(QStringLiteral("[%1,%2]").arg(min).arg(max));
|
||||
slider->setProperty("chanIdx", i);
|
||||
slider->setRange(min, max);
|
||||
connect(slider, qOverload<int>(&FieldSlider::valueChanged), this, &Uint32X8Popup::doValueChanged);
|
||||
layout->addWidget(slider, i, 1);
|
||||
}
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
Uint32X8Popup::~Uint32X8Popup() = default;
|
||||
|
||||
void Uint32X8Popup::setValue(int chanIdx, int val) { m_sliders[chanIdx]->setValue(val); }
|
||||
|
||||
void Uint32X8Popup::doValueChanged(int val) {
|
||||
FieldSlider* slider = static_cast<FieldSlider*>(sender());
|
||||
int chanIdx = slider->property("chanIdx").toInt();
|
||||
emit valueChanged(chanIdx, val);
|
||||
}
|
||||
|
||||
Uint32X8Button::Uint32X8Button(int min, int max, QWidget* parent)
|
||||
: QPushButton(parent), m_popup(new Uint32X8Popup(min, max, this)) {
|
||||
connect(this, &Uint32X8Button::pressed, this, &Uint32X8Button::onPressed);
|
||||
}
|
||||
|
||||
Uint32X8Button::~Uint32X8Button() = default;
|
||||
|
||||
void Uint32X8Button::paintEvent(QPaintEvent*) {
|
||||
QStylePainter painter(this);
|
||||
painter.setPen(palette().color(QPalette::Text));
|
||||
|
||||
// draw the combobox frame, focusrect and selected etc.
|
||||
QStyleOptionComboBox opt = comboStyleOption();
|
||||
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
|
||||
|
||||
// draw the icon and text
|
||||
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
|
||||
}
|
||||
|
||||
QStyleOptionComboBox Uint32X8Button::comboStyleOption() const {
|
||||
QStyleOptionComboBox opt;
|
||||
opt.initFrom(this);
|
||||
opt.editable = false;
|
||||
opt.frame = true;
|
||||
opt.currentText = tr("Channels");
|
||||
return opt;
|
||||
}
|
||||
|
||||
void Uint32X8Button::onPressed() {
|
||||
QPoint pt = parentWidget()->mapToGlobal(pos());
|
||||
m_popup->move(pt.x(), pt.y());
|
||||
m_popup->show();
|
||||
}
|
||||
|
||||
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type)
|
||||
: QWidget(parent)
|
||||
, m_titleLabel(this)
|
||||
, m_deleteButton(this)
|
||||
, m_effect(effect)
|
||||
, m_introspection(GetEffectIntrospection(type)) {
|
||||
QFont titleFont = m_titleLabel.font();
|
||||
titleFont.setWeight(QFont::Bold);
|
||||
m_titleLabel.setFont(titleFont);
|
||||
m_titleLabel.setForegroundRole(QPalette::Window);
|
||||
m_titleLabel.setContentsMargins(46, 0, 0, 0);
|
||||
m_titleLabel.setFixedHeight(20);
|
||||
m_numberText.setTextOption(QTextOption(Qt::AlignRight));
|
||||
m_numberText.setTextWidth(25);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
m_numberFont.setWeight(QFont::Bold);
|
||||
m_numberFont.setStyleHint(QFont::Monospace);
|
||||
m_numberFont.setPointSize(m_numberFont.pointSize() * 2);
|
||||
|
||||
setContentsMargins(QMargins());
|
||||
setFixedHeight(100);
|
||||
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout;
|
||||
mainLayout->setContentsMargins(QMargins());
|
||||
mainLayout->setSpacing(0);
|
||||
|
||||
QHBoxLayout* headLayout = new QHBoxLayout;
|
||||
headLayout->setContentsMargins(QMargins());
|
||||
headLayout->setSpacing(0);
|
||||
headLayout->addWidget(&m_titleLabel);
|
||||
|
||||
m_deleteButton.setVisible(true);
|
||||
connect(&m_deleteButton, &ListingDeleteButton::clicked, this, &EffectWidget::deleteClicked);
|
||||
headLayout->addWidget(&m_deleteButton);
|
||||
|
||||
mainLayout->addLayout(headLayout);
|
||||
mainLayout->addSpacing(8);
|
||||
|
||||
QGridLayout* layout = new QGridLayout;
|
||||
layout->setSpacing(6);
|
||||
layout->setContentsMargins(64, 0, 12, 12);
|
||||
if (m_introspection) {
|
||||
m_titleLabel.setText(tr(m_introspection->m_name.data()));
|
||||
m_titleLabel.setToolTip(tr(m_introspection->m_description.data()));
|
||||
|
||||
for (int f = 0; f < int(m_introspection->m_fields.size()); ++f) {
|
||||
const EffectIntrospection::Field& field = m_introspection->m_fields[f];
|
||||
if (!field.m_name.empty()) {
|
||||
QString fieldName = tr(field.m_name.data());
|
||||
layout->addWidget(new QLabel(fieldName, this), 0, f);
|
||||
switch (field.m_tp) {
|
||||
case EffectIntrospection::Field::Type::UInt32: {
|
||||
FieldSlider* sb = new FieldSlider(this);
|
||||
sb->setProperty("fieldIndex", f);
|
||||
sb->setRange(int(field.m_min), int(field.m_max));
|
||||
sb->setToolTip(QStringLiteral("[%1,%2]").arg(int(field.m_min)).arg(int(field.m_max)));
|
||||
sb->setValue(GetEffectParm<uint32_t>(m_effect, f, 0));
|
||||
connect(sb, qOverload<int>(&FieldSlider::valueChanged), this, qOverload<int>(&EffectWidget::numChanged));
|
||||
layout->addWidget(sb, 1, f);
|
||||
break;
|
||||
}
|
||||
case EffectIntrospection::Field::Type::UInt32x8: {
|
||||
Uint32X8Button* sb = new Uint32X8Button(int(field.m_min), int(field.m_max), this);
|
||||
sb->popup()->setProperty("fieldIndex", f);
|
||||
for (int i = 0; i < Uint32X8Popup::NumSliders; ++i) {
|
||||
sb->popup()->setValue(i, GetEffectParm<uint32_t>(m_effect, f, i));
|
||||
}
|
||||
connect(sb->popup(), &Uint32X8Popup::valueChanged, this, &EffectWidget::chanNumChanged);
|
||||
layout->addWidget(sb, 1, f);
|
||||
break;
|
||||
}
|
||||
case EffectIntrospection::Field::Type::Float: {
|
||||
FieldDoubleSlider* sb = new FieldDoubleSlider(this);
|
||||
sb->setProperty("fieldIndex", f);
|
||||
sb->setRange(field.m_min, field.m_max);
|
||||
sb->setToolTip(QStringLiteral("[%1,%2]").arg(field.m_min).arg(field.m_max));
|
||||
sb->setValue(GetEffectParm<float>(m_effect, f, 0));
|
||||
connect(sb, qOverload<double>(&FieldDoubleSlider::valueChanged), this,
|
||||
qOverload<double>(&EffectWidget::numChanged));
|
||||
layout->addWidget(sb, 1, f);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mainLayout->addLayout(layout);
|
||||
layout->setRowMinimumHeight(0, 22);
|
||||
layout->setRowMinimumHeight(1, 22);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
EffectWidget::~EffectWidget() = default;
|
||||
|
||||
void EffectWidget::paintEvent(QPaintEvent* event) {
|
||||
/* Rounded frame */
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const std::array<QPoint, 6> points{{
|
||||
{1, 20},
|
||||
{1, 99},
|
||||
{width() - 1, 99},
|
||||
{width() - 1, 1},
|
||||
{20, 1},
|
||||
{1, 20},
|
||||
}};
|
||||
painter.setBrush(palette().brush(QPalette::Window));
|
||||
painter.drawPolygon(points.data(), int(points.size()));
|
||||
painter.setPen(QPen(QColor(127, 127, 127), 2.0));
|
||||
painter.drawPolyline(points.data(), int(points.size()));
|
||||
|
||||
const std::array<QPoint, 7> headPoints{{
|
||||
{1, 20},
|
||||
{1, 55},
|
||||
{35, 20},
|
||||
{width() - 1, 20},
|
||||
{width() - 1, 1},
|
||||
{20, 1},
|
||||
{1, 20},
|
||||
}};
|
||||
painter.setBrush(QColor(127, 127, 127));
|
||||
painter.drawPolygon(headPoints.data(), int(headPoints.size()));
|
||||
|
||||
painter.drawRect(17, 51, 32, 32);
|
||||
|
||||
QTransform rotate;
|
||||
rotate.rotate(-45.0).translate(-15, 8);
|
||||
painter.setTransform(rotate);
|
||||
painter.setFont(m_numberFont);
|
||||
painter.setPen(palette().color(QPalette::Window));
|
||||
painter.drawStaticText(0, 0, m_numberText);
|
||||
}
|
||||
|
||||
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* cmd) : EffectWidget(parent, cmd, cmd->Isa()) {}
|
||||
|
||||
EffectWidget::EffectWidget(QWidget* parent, amuse::EffectType type) : EffectWidget(parent, nullptr, type) {}
|
||||
|
||||
void EffectWidget::numChanged(int value) {
|
||||
SetEffectParm<uint32_t>(m_effect, sender()->property("fieldIndex").toInt(), 0, value);
|
||||
}
|
||||
|
||||
void EffectWidget::numChanged(double value) {
|
||||
SetEffectParm<float>(m_effect, sender()->property("fieldIndex").toInt(), 0, value);
|
||||
}
|
||||
|
||||
void EffectWidget::chanNumChanged(int chanIdx, int value) {
|
||||
SetEffectParm<uint32_t>(m_effect, sender()->property("fieldIndex").toInt(), chanIdx, value);
|
||||
}
|
||||
|
||||
void EffectWidget::deleteClicked() {
|
||||
if (m_index != -1)
|
||||
if (EffectListing* listing = getParent())
|
||||
listing->deleteEffect(m_index);
|
||||
}
|
||||
|
||||
EffectListing* EffectWidget::getParent() const { return qobject_cast<EffectListing*>(parentWidget()->parentWidget()); }
|
||||
|
||||
void EffectWidget::setIndex(int index) {
|
||||
m_index = index;
|
||||
m_numberText.setText(QString::number(index));
|
||||
update();
|
||||
}
|
||||
|
||||
void EffectWidgetContainer::animateOpen() {
|
||||
int newHeight = 200 + parentWidget()->layout()->spacing();
|
||||
m_animation = new QPropertyAnimation(this, "minimumHeight");
|
||||
m_animation->setDuration(abs(minimumHeight() - newHeight) * 4);
|
||||
m_animation->setStartValue(minimumHeight());
|
||||
m_animation->setEndValue(newHeight);
|
||||
m_animation->setEasingCurve(QEasingCurve::InOutExpo);
|
||||
connect(m_animation, &QPropertyAnimation::valueChanged, parentWidget(), qOverload<>(&QWidget::update));
|
||||
connect(m_animation, &QPropertyAnimation::destroyed, this, &EffectWidgetContainer::animationDestroyed);
|
||||
m_animation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void EffectWidgetContainer::animateClosed() {
|
||||
m_animation = new QPropertyAnimation(this, "minimumHeight");
|
||||
m_animation->setDuration(abs(minimumHeight() - 100) * 4);
|
||||
m_animation->setStartValue(minimumHeight());
|
||||
m_animation->setEndValue(100);
|
||||
m_animation->setEasingCurve(QEasingCurve::InOutExpo);
|
||||
connect(m_animation, &QPropertyAnimation::valueChanged, parentWidget(), qOverload<>(&QWidget::update));
|
||||
connect(m_animation, &QPropertyAnimation::destroyed, this, &EffectWidgetContainer::animationDestroyed);
|
||||
m_animation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void EffectWidgetContainer::snapOpen() {
|
||||
if (m_animation)
|
||||
m_animation->stop();
|
||||
setMinimumHeight(200 + parentWidget()->layout()->spacing());
|
||||
}
|
||||
|
||||
void EffectWidgetContainer::snapClosed() {
|
||||
if (m_animation)
|
||||
m_animation->stop();
|
||||
setMinimumHeight(100);
|
||||
}
|
||||
|
||||
void EffectWidgetContainer::animationDestroyed() { m_animation = nullptr; }
|
||||
|
||||
template <class... _Args>
|
||||
EffectWidgetContainer::EffectWidgetContainer(QWidget* parent, _Args&&... args)
|
||||
: QWidget(parent), m_effectWidget(new EffectWidget(this, std::forward<_Args>(args)...)) {
|
||||
setMinimumHeight(100);
|
||||
setContentsMargins(QMargins());
|
||||
QVBoxLayout* outerLayout = new QVBoxLayout;
|
||||
outerLayout->setAlignment(Qt::AlignBottom);
|
||||
outerLayout->setContentsMargins(QMargins());
|
||||
outerLayout->setSpacing(0);
|
||||
outerLayout->addWidget(m_effectWidget);
|
||||
setLayout(outerLayout);
|
||||
}
|
||||
|
||||
EffectWidgetContainer::~EffectWidgetContainer() = default;
|
||||
|
||||
void EffectListing::startAutoscroll(QWidget* source, QMouseEvent* event, int delta) {
|
||||
if (m_autoscrollTimer == -1)
|
||||
m_autoscrollTimer = startTimer(50);
|
||||
m_autoscrollDelta = delta;
|
||||
m_autoscrollSource = source;
|
||||
if (m_autoscrollEvent != nullptr) {
|
||||
delete m_autoscrollEvent;
|
||||
m_autoscrollEvent = nullptr;
|
||||
}
|
||||
m_autoscrollEvent = event->clone();
|
||||
}
|
||||
|
||||
void EffectListing::stopAutoscroll() {
|
||||
if (m_autoscrollTimer != -1) {
|
||||
killTimer(m_autoscrollTimer);
|
||||
m_autoscrollTimer = -1;
|
||||
}
|
||||
m_autoscrollDelta = 0;
|
||||
if (m_autoscrollEvent != nullptr) {
|
||||
delete m_autoscrollEvent;
|
||||
m_autoscrollEvent = nullptr;
|
||||
}
|
||||
m_autoscrollSource = nullptr;
|
||||
}
|
||||
|
||||
void EffectListing::timerEvent(QTimerEvent* event) {
|
||||
if (QScrollArea* scrollArea = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||
QScrollBar* bar = scrollArea->verticalScrollBar();
|
||||
int oldValue = bar->value();
|
||||
bar->setValue(oldValue + m_autoscrollDelta);
|
||||
int valueDelta = bar->value() - oldValue;
|
||||
if (valueDelta != 0) {
|
||||
if (m_autoscrollSource)
|
||||
QApplication::sendEvent(m_autoscrollSource, m_autoscrollEvent);
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectListing::beginDrag(EffectWidget* widget) {
|
||||
int origIdx = m_layout->indexOf(widget->parentWidget());
|
||||
if (origIdx < 0 || origIdx >= m_layout->count() - 1)
|
||||
return false;
|
||||
if (origIdx < m_layout->count() - 1) {
|
||||
// Animate next item open
|
||||
m_dragOpenIdx = origIdx;
|
||||
if (EffectWidgetContainer* nextItem = qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(origIdx + 1)->widget()))
|
||||
nextItem->snapOpen();
|
||||
} else {
|
||||
m_dragOpenIdx = -1;
|
||||
}
|
||||
m_origIdx = origIdx;
|
||||
m_dragItem = m_layout->takeAt(origIdx);
|
||||
m_dragItem->widget()->raise();
|
||||
return true;
|
||||
}
|
||||
|
||||
void EffectListing::endDrag() {
|
||||
int insertIdx;
|
||||
if (m_dragOpenIdx != -1) {
|
||||
if (EffectWidgetContainer* prevItem =
|
||||
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
|
||||
prevItem->snapClosed();
|
||||
insertIdx = m_dragOpenIdx;
|
||||
m_dragOpenIdx = -1;
|
||||
} else {
|
||||
insertIdx = m_layout->count() - 1;
|
||||
}
|
||||
|
||||
if (m_prevDragOpen) {
|
||||
m_prevDragOpen->snapClosed();
|
||||
m_prevDragOpen = nullptr;
|
||||
}
|
||||
|
||||
if (m_origIdx != insertIdx)
|
||||
std::swap(m_submix->getEffectStack()[m_origIdx], m_submix->getEffectStack()[insertIdx]);
|
||||
m_layout->insertItem(insertIdx, m_dragItem);
|
||||
m_dragItem = nullptr;
|
||||
stopAutoscroll();
|
||||
reindex();
|
||||
}
|
||||
|
||||
void EffectListing::cancelDrag() {
|
||||
if (m_dragOpenIdx != -1) {
|
||||
if (EffectWidgetContainer* prevItem =
|
||||
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
|
||||
prevItem->snapClosed();
|
||||
m_dragOpenIdx = -1;
|
||||
}
|
||||
m_layout->insertItem(m_origIdx, m_dragItem);
|
||||
if (m_prevDragOpen) {
|
||||
m_prevDragOpen->snapClosed();
|
||||
m_prevDragOpen = nullptr;
|
||||
}
|
||||
m_dragItem = nullptr;
|
||||
stopAutoscroll();
|
||||
}
|
||||
|
||||
void EffectListing::_moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event) {
|
||||
QRect scrollVpRect = parentWidget()->parentWidget()->rect();
|
||||
QPoint scrollVpPoint = mapTo(parentWidget()->parentWidget(), pt);
|
||||
if (scrollVpRect.bottom() - scrollVpPoint.y() < 50)
|
||||
startAutoscroll(source, event, 10); // Scroll Down
|
||||
else if (scrollVpRect.top() - scrollVpPoint.y() > -50)
|
||||
startAutoscroll(source, event, -10);
|
||||
else
|
||||
stopAutoscroll();
|
||||
|
||||
hoverIdx = std::max(0, std::min(hoverIdx, m_layout->count() - 1));
|
||||
if (hoverIdx != m_dragOpenIdx) {
|
||||
if (m_dragOpenIdx != -1)
|
||||
if (EffectWidgetContainer* prevItem =
|
||||
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget())) {
|
||||
m_prevDragOpen = prevItem;
|
||||
prevItem->animateClosed();
|
||||
}
|
||||
if (EffectWidgetContainer* nextItem = qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(hoverIdx)->widget()))
|
||||
nextItem->animateOpen();
|
||||
m_dragOpenIdx = hoverIdx;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void EffectListing::moveDrag(EffectWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event) {
|
||||
EffectWidgetContainer* container = static_cast<EffectWidgetContainer*>(widget->parentWidget());
|
||||
int pitch = 100 + m_layout->spacing();
|
||||
_moveDrag((container->pos().y() - m_layout->contentsMargins().top() + pitch / 2) / pitch, pt, source, event);
|
||||
}
|
||||
|
||||
int EffectListing::moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event) {
|
||||
int pitch = 100 + m_layout->spacing();
|
||||
_moveDrag(pt.y() / pitch, pt, source, event);
|
||||
return m_dragOpenIdx;
|
||||
}
|
||||
|
||||
void EffectListing::insertDragout() {
|
||||
if (m_dragOpenIdx != -1) {
|
||||
if (EffectWidgetContainer* prevItem =
|
||||
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget())) {
|
||||
m_prevDragOpen = prevItem;
|
||||
prevItem->animateClosed();
|
||||
}
|
||||
m_dragOpenIdx = -1;
|
||||
}
|
||||
stopAutoscroll();
|
||||
}
|
||||
|
||||
void EffectListing::insert(amuse::EffectType type, const QString& text) {
|
||||
int insertIdx;
|
||||
if (m_dragOpenIdx != -1) {
|
||||
if (EffectWidgetContainer* prevItem =
|
||||
qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(m_dragOpenIdx)->widget()))
|
||||
prevItem->snapClosed();
|
||||
insertIdx = m_dragOpenIdx;
|
||||
m_dragOpenIdx = -1;
|
||||
} else {
|
||||
insertIdx = m_layout->count() - 1;
|
||||
}
|
||||
|
||||
if (m_prevDragOpen) {
|
||||
m_prevDragOpen->snapClosed();
|
||||
m_prevDragOpen = nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<amuse::EffectBaseTypeless> newEffect;
|
||||
switch (type) {
|
||||
case amuse::EffectType::ReverbStd:
|
||||
newEffect = m_submix->_makeEffect<amuse::EffectReverbStd>(amuse::EffectReverbStdInfo{});
|
||||
break;
|
||||
case amuse::EffectType::ReverbHi:
|
||||
newEffect = m_submix->_makeEffect<amuse::EffectReverbHi>(amuse::EffectReverbHiInfo{});
|
||||
break;
|
||||
case amuse::EffectType::Delay:
|
||||
newEffect = m_submix->_makeEffect<amuse::EffectDelay>(amuse::EffectDelayInfo{});
|
||||
break;
|
||||
case amuse::EffectType::Chorus:
|
||||
newEffect = m_submix->_makeEffect<amuse::EffectChorus>(amuse::EffectChorusInfo{});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
auto it = m_submix->getEffectStack().insert(m_submix->getEffectStack().begin() + insertIdx, std::move(newEffect));
|
||||
m_layout->insertWidget(insertIdx, new EffectWidgetContainer(this, it->get()));
|
||||
|
||||
stopAutoscroll();
|
||||
reindex();
|
||||
}
|
||||
|
||||
void EffectListing::deleteEffect(int index) {
|
||||
QLayoutItem* item = m_layout->takeAt(index);
|
||||
m_submix->getEffectStack().erase(m_submix->getEffectStack().begin() + index);
|
||||
item->widget()->deleteLater();
|
||||
delete item;
|
||||
reindex();
|
||||
}
|
||||
|
||||
void EffectListing::reindex() {
|
||||
for (int i = 0; i < m_layout->count() - 1; ++i)
|
||||
if (EffectWidgetContainer* item = qobject_cast<EffectWidgetContainer*>(m_layout->itemAt(i)->widget()))
|
||||
item->m_effectWidget->setIndex(i);
|
||||
}
|
||||
|
||||
void EffectListing::clear() {
|
||||
while (m_layout->count() > 2) {
|
||||
QLayoutItem* item = m_layout->takeAt(0);
|
||||
item->widget()->deleteLater();
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectListing::loadData(amuse::Submix* submix) {
|
||||
m_submix = submix;
|
||||
clear();
|
||||
int i = 0;
|
||||
for (auto& effect : submix->getEffectStack())
|
||||
m_layout->insertWidget(i++, new EffectWidgetContainer(this, effect.get()));
|
||||
reindex();
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
|
||||
void EffectListing::unloadData() {
|
||||
m_submix = nullptr;
|
||||
clear();
|
||||
reindex();
|
||||
update();
|
||||
}
|
||||
|
||||
EffectListing::EffectListing(QWidget* parent) : QWidget(parent), m_layout(new QVBoxLayout) {
|
||||
m_layout->addStretch();
|
||||
setLayout(m_layout);
|
||||
reindex();
|
||||
}
|
||||
|
||||
EffectListing::~EffectListing() = default;
|
||||
|
||||
EffectCatalogueItem::EffectCatalogueItem(amuse::EffectType type, const QString& name, const QString& doc,
|
||||
QWidget* parent)
|
||||
: QWidget(parent), m_type(type), m_iconLab(this), m_label(name, this) {
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
QString iconPath = QStringLiteral(":/commands/%1.svg").arg(name);
|
||||
if (QFile(iconPath).exists())
|
||||
m_iconLab.setPixmap(QIcon(iconPath).pixmap(32, 32));
|
||||
else
|
||||
m_iconLab.setPixmap(QIcon(QStringLiteral(":/icons/IconOpen.svg")).pixmap(32, 32));
|
||||
layout->addWidget(&m_iconLab);
|
||||
layout->addWidget(&m_label);
|
||||
layout->addStretch();
|
||||
layout->setContentsMargins(QMargins());
|
||||
setLayout(layout);
|
||||
setToolTip(doc);
|
||||
}
|
||||
|
||||
EffectCatalogueItem::EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent)
|
||||
: QWidget(parent), m_type(other.getType()) {
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
QHBoxLayout* oldLayout = static_cast<QHBoxLayout*>(other.layout());
|
||||
m_iconLab.setPixmap(static_cast<QLabel*>(oldLayout->itemAt(0)->widget())->pixmap(Qt::ReturnByValue));
|
||||
layout->addWidget(&m_iconLab);
|
||||
m_label.setText(static_cast<QLabel*>(oldLayout->itemAt(1)->widget())->text());
|
||||
layout->addWidget(&m_label);
|
||||
layout->addStretch();
|
||||
layout->setContentsMargins(QMargins());
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
EffectCatalogueItem::~EffectCatalogueItem() = default;
|
||||
|
||||
EffectCatalogue::EffectCatalogue(QWidget* parent) : QTreeWidget(parent) {
|
||||
setSelectionMode(QAbstractItemView::NoSelection);
|
||||
setColumnCount(1);
|
||||
setHeaderHidden(true);
|
||||
|
||||
for (int i = 1; i < int(amuse::EffectType::EffectTypeMAX); ++i) {
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem(this);
|
||||
setItemWidget(
|
||||
item, 0,
|
||||
new EffectCatalogueItem(amuse::EffectType(i), tr(EffectStrings[i - 1]), tr(EffectDocStrings[i - 1]), this));
|
||||
}
|
||||
}
|
||||
|
||||
void EffectCatalogue::mousePressEvent(QMouseEvent* event) {
|
||||
QTreeWidget::mousePressEvent(event);
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void EffectCatalogue::mouseReleaseEvent(QMouseEvent* event) {
|
||||
QTreeWidget::mouseReleaseEvent(event);
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void EffectCatalogue::mouseMoveEvent(QMouseEvent* event) {
|
||||
StudioSetupWidget* editor = qobject_cast<StudioSetupWidget*>(parentWidget()->parentWidget()->parentWidget());
|
||||
if (!editor || !editor->m_draggedItem)
|
||||
QTreeWidget::mouseMoveEvent(event);
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
EffectListing* StudioSetupWidget::getCurrentListing() const {
|
||||
return static_cast<EffectListing*>(static_cast<QScrollArea*>(m_tabs->currentWidget())->widget());
|
||||
}
|
||||
|
||||
void StudioSetupWidget::beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt) {
|
||||
if (widget->getParent()->beginDrag(widget)) {
|
||||
m_draggedPt = pt;
|
||||
m_draggedCmd = widget;
|
||||
}
|
||||
}
|
||||
|
||||
void StudioSetupWidget::beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt) {
|
||||
m_draggedPt = pt;
|
||||
m_draggedItem = new EffectCatalogueItem(*item, this);
|
||||
m_draggedItem->setGeometry(item->geometry());
|
||||
m_draggedItem->move(eventPt - m_draggedPt);
|
||||
m_draggedItem->raise();
|
||||
m_draggedItem->show();
|
||||
}
|
||||
|
||||
void StudioSetupWidget::mousePressEvent(QMouseEvent* event) {
|
||||
if (m_catalogue->geometry().contains(event->pos())) {
|
||||
QPoint fromParent1 = m_catalogue->mapFrom(this, event->pos());
|
||||
QWidget* ch = m_catalogue->childAt(fromParent1);
|
||||
if (ch) {
|
||||
EffectCatalogueItem* child = nullptr;
|
||||
while (ch && !(child = qobject_cast<EffectCatalogueItem*>(ch)))
|
||||
ch = ch->parentWidget();
|
||||
if (child) {
|
||||
QPoint fromParent2 = child->mapFrom(m_catalogue, fromParent1);
|
||||
beginCatalogueDrag(child, event->pos(), fromParent2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
EffectListing* listing = getCurrentListing();
|
||||
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos())) {
|
||||
QPoint fromParent1 = listing->mapFrom(this, event->pos());
|
||||
QWidget* ch = listing->childAt(fromParent1);
|
||||
if (ch) {
|
||||
EffectWidget* child = nullptr;
|
||||
while (ch && !(child = qobject_cast<EffectWidget*>(ch)))
|
||||
ch = ch->parentWidget();
|
||||
if (child) {
|
||||
QPoint fromParent2 = child->mapFrom(listing, fromParent1);
|
||||
beginCommandDrag(child, event->pos(), fromParent2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StudioSetupWidget::mouseReleaseEvent(QMouseEvent* event) {
|
||||
EffectListing* listing = getCurrentListing();
|
||||
if (m_draggedItem) {
|
||||
amuse::EffectType type = m_draggedItem->getType();
|
||||
QString text = m_draggedItem->getText();
|
||||
m_draggedItem->deleteLater();
|
||||
m_draggedItem = nullptr;
|
||||
|
||||
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos())) {
|
||||
if (m_dragInsertIdx != -1)
|
||||
listing->insert(type, text);
|
||||
else
|
||||
listing->insertDragout();
|
||||
} else {
|
||||
listing->insertDragout();
|
||||
}
|
||||
m_dragInsertIdx = -1;
|
||||
} else if (m_draggedCmd) {
|
||||
listing->endDrag();
|
||||
m_draggedCmd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void StudioSetupWidget::mouseMoveEvent(QMouseEvent* event) {
|
||||
EffectListing* listing = getCurrentListing();
|
||||
if (m_draggedItem) {
|
||||
m_draggedItem->move(event->pos() - m_draggedPt);
|
||||
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos())) {
|
||||
m_dragInsertIdx = listing->moveInsertDrag(listing->mapFrom(this, event->pos()), this, event);
|
||||
} else if (m_dragInsertIdx != -1) {
|
||||
listing->insertDragout();
|
||||
m_dragInsertIdx = -1;
|
||||
}
|
||||
m_catalogue->update();
|
||||
update();
|
||||
} else if (m_draggedCmd) {
|
||||
QPoint listingPt = listing->mapFrom(this, event->pos());
|
||||
EffectWidgetContainer* container = static_cast<EffectWidgetContainer*>(m_draggedCmd->parentWidget());
|
||||
container->move(container->x(), listingPt.y() - m_draggedPt.y());
|
||||
if (listing->parentWidget()->parentWidget()->parentWidget()->geometry().contains(event->pos()))
|
||||
listing->moveDrag(m_draggedCmd, listingPt, this, event);
|
||||
listing->update();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void StudioSetupWidget::keyPressEvent(QKeyEvent* event) {
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
EffectListing* listing = getCurrentListing();
|
||||
if (m_draggedItem) {
|
||||
m_draggedItem->deleteLater();
|
||||
m_draggedItem = nullptr;
|
||||
listing->insertDragout();
|
||||
} else if (m_draggedCmd) {
|
||||
listing->cancelDrag();
|
||||
m_draggedCmd = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StudioSetupWidget::hideEvent(QHideEvent* event) { emit hidden(); }
|
||||
|
||||
void StudioSetupWidget::showEvent(QShowEvent* event) { emit shown(); }
|
||||
|
||||
void StudioSetupWidget::updateWindowPosition() {
|
||||
QWidget* parent = parentWidget();
|
||||
move(parent->width() / 2 - width() / 2 + parent->x(), parent->height() / 2 - height() / 2 + parent->y());
|
||||
}
|
||||
|
||||
void StudioSetupWidget::catalogueDoubleClicked(QTreeWidgetItem* item, int column) {
|
||||
if (EffectCatalogueItem* cItem = qobject_cast<EffectCatalogueItem*>(m_catalogue->itemWidget(item, column))) {
|
||||
amuse::EffectType type = cItem->getType();
|
||||
if (type != amuse::EffectType::Invalid) {
|
||||
EffectListing* listing = getCurrentListing();
|
||||
listing->insert(type, cItem->getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StudioSetupWidget::loadData(amuse::Studio* studio) {
|
||||
m_listing[0]->loadData(&studio->getAuxA());
|
||||
m_listing[1]->loadData(&studio->getAuxB());
|
||||
return true;
|
||||
}
|
||||
|
||||
void StudioSetupWidget::unloadData() {
|
||||
m_listing[0]->unloadData();
|
||||
m_listing[1]->unloadData();
|
||||
}
|
||||
|
||||
StudioSetupWidget::StudioSetupWidget(QWidget* parent)
|
||||
: QWidget(parent, Qt::Tool), m_splitter(new QSplitter), m_tabs(new QTabWidget), m_catalogue(new EffectCatalogue) {
|
||||
setWindowTitle(tr("Studio Setup"));
|
||||
setGeometry(0, 0, 900, 450);
|
||||
|
||||
std::array<QScrollArea*, 2> scrollAreas{};
|
||||
for (size_t i = 0; i < m_listing.size(); ++i) {
|
||||
m_listing[i] = new EffectListing;
|
||||
QScrollArea* listingScroll = new QScrollArea;
|
||||
scrollAreas[i] = listingScroll;
|
||||
listingScroll->setWidget(m_listing[i]);
|
||||
listingScroll->setWidgetResizable(true);
|
||||
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
sizePolicy.setHorizontalStretch(1);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
listingScroll->setSizePolicy(sizePolicy);
|
||||
listingScroll->setMinimumWidth(350);
|
||||
m_splitter->addWidget(listingScroll);
|
||||
}
|
||||
m_tabs->addTab(scrollAreas[0], tr("Aux A"));
|
||||
m_tabs->addTab(scrollAreas[1], tr("Aux B"));
|
||||
m_splitter->addWidget(m_tabs);
|
||||
|
||||
{
|
||||
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
m_catalogue->setSizePolicy(sizePolicy);
|
||||
m_catalogue->setMinimumWidth(150);
|
||||
m_catalogue->setGeometry(0, 0, 215, 0);
|
||||
m_catalogue->setMaximumWidth(300);
|
||||
m_splitter->addWidget(m_catalogue);
|
||||
}
|
||||
|
||||
connect(m_catalogue, &EffectCatalogue::itemDoubleClicked, this, &StudioSetupWidget::catalogueDoubleClicked);
|
||||
|
||||
m_splitter->setCollapsible(0, false);
|
||||
QGridLayout* layout = new QGridLayout;
|
||||
layout->setContentsMargins(QMargins());
|
||||
layout->addWidget(m_splitter);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
StudioSetupWidget::~StudioSetupWidget() = default;
|
|
@ -0,0 +1,215 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLayoutItem>
|
||||
#include <QMouseEvent>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QSplitter>
|
||||
#include <QStaticText>
|
||||
#include <QTreeWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
|
||||
namespace amuse {
|
||||
class EffectBaseTypeless;
|
||||
class Studio;
|
||||
class Submix;
|
||||
|
||||
enum class EffectType;
|
||||
} // namespace amuse
|
||||
|
||||
class EffectListing;
|
||||
|
||||
struct EffectIntrospection;
|
||||
|
||||
class Uint32X8Popup : public QFrame {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static constexpr int NumSliders = 8;
|
||||
|
||||
explicit Uint32X8Popup(int min, int max, QWidget* parent = Q_NULLPTR);
|
||||
~Uint32X8Popup() override;
|
||||
|
||||
void setValue(int chanIdx, int val);
|
||||
|
||||
private slots:
|
||||
void doValueChanged(int val);
|
||||
|
||||
signals:
|
||||
void valueChanged(int chanIdx, int val);
|
||||
|
||||
private:
|
||||
std::array<FieldSlider*, NumSliders> m_sliders {};
|
||||
};
|
||||
|
||||
class Uint32X8Button : public QPushButton {
|
||||
Q_OBJECT
|
||||
Uint32X8Popup* m_popup;
|
||||
|
||||
public:
|
||||
explicit Uint32X8Button(int min, int max, QWidget* parent = Q_NULLPTR);
|
||||
~Uint32X8Button() override;
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
Uint32X8Popup* popup() const { return m_popup; }
|
||||
QStyleOptionComboBox comboStyleOption() const;
|
||||
|
||||
private slots:
|
||||
void onPressed();
|
||||
};
|
||||
|
||||
class EffectWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class EffectListing;
|
||||
QFont m_numberFont;
|
||||
QLabel m_titleLabel;
|
||||
ListingDeleteButton m_deleteButton;
|
||||
QStaticText m_numberText;
|
||||
int m_index = -1;
|
||||
amuse::EffectBaseTypeless* m_effect;
|
||||
const EffectIntrospection* m_introspection;
|
||||
void setIndex(int index);
|
||||
|
||||
private slots:
|
||||
void numChanged(int);
|
||||
void numChanged(double);
|
||||
void chanNumChanged(int, int);
|
||||
void deleteClicked();
|
||||
|
||||
private:
|
||||
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type);
|
||||
~EffectWidget() override;
|
||||
|
||||
public:
|
||||
EffectListing* getParent() const;
|
||||
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect);
|
||||
explicit EffectWidget(QWidget* parent, amuse::EffectType op);
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
QString getText() const { return m_titleLabel.text(); }
|
||||
};
|
||||
|
||||
class EffectWidgetContainer : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class EffectListing;
|
||||
EffectWidget* m_effectWidget;
|
||||
QPropertyAnimation* m_animation = nullptr;
|
||||
void animateOpen();
|
||||
void animateClosed();
|
||||
void snapOpen();
|
||||
void snapClosed();
|
||||
private slots:
|
||||
void animationDestroyed();
|
||||
|
||||
public:
|
||||
template <class... _Args>
|
||||
EffectWidgetContainer(QWidget* parent, _Args&&... args);
|
||||
~EffectWidgetContainer() override;
|
||||
};
|
||||
|
||||
class EffectListing : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class EffectWidget;
|
||||
friend class StudioSetupWidget;
|
||||
amuse::Submix* m_submix = nullptr;
|
||||
QVBoxLayout* m_layout;
|
||||
QLayoutItem* m_dragItem = nullptr;
|
||||
int m_origIdx = -1;
|
||||
int m_dragOpenIdx = -1;
|
||||
EffectWidgetContainer* m_prevDragOpen = nullptr;
|
||||
int m_autoscrollTimer = -1;
|
||||
int m_autoscrollDelta = 0;
|
||||
QWidget* m_autoscrollSource = nullptr;
|
||||
QMouseEvent* m_autoscrollEvent = nullptr;
|
||||
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
|
||||
void stopAutoscroll();
|
||||
bool beginDrag(EffectWidget* widget);
|
||||
void endDrag();
|
||||
void cancelDrag();
|
||||
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||
void moveDrag(EffectWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||
void insertDragout();
|
||||
void insert(amuse::EffectType type, const QString& text);
|
||||
void deleteEffect(int index);
|
||||
void reindex();
|
||||
void clear();
|
||||
|
||||
public:
|
||||
explicit EffectListing(QWidget* parent = Q_NULLPTR);
|
||||
~EffectListing() override;
|
||||
|
||||
bool loadData(amuse::Submix* submix);
|
||||
void unloadData();
|
||||
void timerEvent(QTimerEvent* event) override;
|
||||
};
|
||||
|
||||
class EffectCatalogueItem : public QWidget {
|
||||
Q_OBJECT
|
||||
amuse::EffectType m_type;
|
||||
QLabel m_iconLab;
|
||||
QLabel m_label;
|
||||
|
||||
public:
|
||||
explicit EffectCatalogueItem(amuse::EffectType type, const QString& name, const QString& doc,
|
||||
QWidget* parent = Q_NULLPTR);
|
||||
explicit EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent = Q_NULLPTR);
|
||||
~EffectCatalogueItem() override;
|
||||
|
||||
amuse::EffectType getType() const { return m_type; }
|
||||
QString getText() const { return m_label.text(); }
|
||||
};
|
||||
|
||||
class EffectCatalogue : public QTreeWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EffectCatalogue(QWidget* parent = Q_NULLPTR);
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
};
|
||||
|
||||
class StudioSetupWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
friend class EffectCatalogue;
|
||||
QSplitter* m_splitter;
|
||||
QTabWidget* m_tabs;
|
||||
std::array<EffectListing*, 2> m_listing{};
|
||||
EffectCatalogue* m_catalogue;
|
||||
EffectWidget* m_draggedCmd = nullptr;
|
||||
EffectCatalogueItem* m_draggedItem = nullptr;
|
||||
QPoint m_draggedPt;
|
||||
int m_dragInsertIdx = -1;
|
||||
EffectListing* getCurrentListing() const;
|
||||
void beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
||||
void beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
||||
|
||||
public:
|
||||
explicit StudioSetupWidget(QWidget* parent = Q_NULLPTR);
|
||||
~StudioSetupWidget() override;
|
||||
|
||||
bool loadData(amuse::Studio* studio);
|
||||
void unloadData();
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void updateWindowPosition();
|
||||
|
||||
public slots:
|
||||
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
|
||||
|
||||
signals:
|
||||
void hidden();
|
||||
void shown();
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
#include <cstdint>
|
||||
#include <QApplication>
|
||||
#include <QStyleFactory>
|
||||
#include <QTranslator>
|
||||
#include "MainWindow.hpp"
|
||||
#include "SongGroupEditor.hpp"
|
||||
#include <QResource>
|
||||
#include <QCommandLineParser>
|
||||
#include <logvisor/logvisor.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
#ifdef __APPLE__
|
||||
void MacOSSetDarkAppearance();
|
||||
#endif
|
||||
|
||||
extern "C" const uint8_t MAINICON_QT[];
|
||||
|
||||
static QIcon MakeAppIcon() {
|
||||
QIcon ret;
|
||||
|
||||
const uint8_t* ptr = MAINICON_QT;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
uint32_t size = *reinterpret_cast<const uint32_t*>(ptr);
|
||||
ptr += 4;
|
||||
|
||||
QPixmap pm;
|
||||
pm.loadFromData(ptr, size);
|
||||
ret.addPixmap(pm);
|
||||
ptr += size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MainWindow* g_MainWindow = nullptr;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QApplication::setAttribute(Qt::AA_Use96Dpi);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create(QStringLiteral("Fusion"))));
|
||||
QApplication a(argc, argv);
|
||||
QApplication::setWindowIcon(MakeAppIcon());
|
||||
|
||||
a.setOrganizationName(QStringLiteral("AxioDL"));
|
||||
a.setApplicationName(QStringLiteral("Amuse"));
|
||||
|
||||
QPalette darkPalette;
|
||||
darkPalette.setColor(QPalette::Window, QColor(53, 53, 53));
|
||||
darkPalette.setColor(QPalette::WindowText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Base, QColor(42, 42, 42));
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25, 25, 25, 53));
|
||||
darkPalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
|
||||
darkPalette.setColor(QPalette::ToolTipBase, QColor(42, 42, 42));
|
||||
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Text, Qt::white);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255, 255, 255, 120));
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Light, QColor(0, 0, 0, 0));
|
||||
darkPalette.setColor(QPalette::Button, QColor(53, 53, 53));
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53, 53, 53, 53));
|
||||
darkPalette.setColor(QPalette::ButtonText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255, 255, 255, 120));
|
||||
darkPalette.setColor(QPalette::BrightText, Qt::red);
|
||||
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||
darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(42, 130, 218, 53));
|
||||
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
|
||||
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255, 255, 255, 120));
|
||||
a.setPalette(darkPalette);
|
||||
|
||||
#ifdef __APPLE__
|
||||
MacOSSetDarkAppearance();
|
||||
#endif
|
||||
|
||||
logvisor::RegisterConsoleLogger();
|
||||
logvisor::RegisterStandardExceptions();
|
||||
|
||||
Q_INIT_RESOURCE(translation_res);
|
||||
QTranslator translator;
|
||||
if (translator.load(QLocale(), QStringLiteral("lang"), QStringLiteral("_"), QStringLiteral(":/translations"))) {
|
||||
a.installTranslator(&translator);
|
||||
}
|
||||
|
||||
MainWindow w;
|
||||
g_MainWindow = &w;
|
||||
w.show();
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.process(a);
|
||||
QStringList args = parser.positionalArguments();
|
||||
if (!args.empty())
|
||||
w.openProject(args.back());
|
||||
|
||||
return a.exec();
|
||||
}
|
After Width: | Height: | Size: 216 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 924 B |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 7.5 KiB |
|
@ -0,0 +1,17 @@
|
|||
add_executable(amuse-mkqticon mkqticon.c)
|
||||
target_link_libraries(amuse-mkqticon ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
|
||||
|
||||
macro(declare_qticon_target)
|
||||
add_custom_command(OUTPUT ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin
|
||||
COMMAND $<TARGET_FILE:amuse-mkqticon>
|
||||
ARGS ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin
|
||||
DEPENDS
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/128x128/apps/amuse-gui.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/64x64/apps/amuse-gui.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/48x48/apps/amuse-gui.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/32x32/apps/amuse-gui.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/16x16/apps/amuse-gui.png
|
||||
WORKING_DIRECTORY ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop
|
||||
COMMENT "Generating mainicon_qt.bin")
|
||||
bintoc(mainicon_qt.cpp ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin MAINICON_QT)
|
||||
endmacro()
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env xdg-open
|
||||
[Desktop Entry]
|
||||
Name=Amuse
|
||||
GenericName=MusyX Game Audio Editor
|
||||
Comment=Edit Audio Data of MusyX Sound Groups
|
||||
Exec=amuse-gui
|
||||
Icon=amuse-gui
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Audio
|
|
@ -0,0 +1,71 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const int DIMS[] =
|
||||
{
|
||||
16,
|
||||
32,
|
||||
48,
|
||||
64,
|
||||
128,
|
||||
256,
|
||||
0
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: makeqticon <out.bin>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE* ofp = fopen(argv[1], "wb");
|
||||
if (!ofp)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not able to be opened for writing as a regular file\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char command[2048];
|
||||
|
||||
for (const int* d = DIMS ; *d != 0 ; ++d)
|
||||
{
|
||||
printf("Rendering main icon @%dx%d\n", *d, *d);
|
||||
fflush(stdout);
|
||||
|
||||
snprintf(command, 2048, "%dx%d/apps/amuse-gui.png", *d, *d);
|
||||
FILE* fp = fopen(command, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
fprintf(stderr, "unable to open '%s' for reading\n", command);
|
||||
fclose(ofp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
uint32_t size = (uint32_t)ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
fwrite(&size, 1, 4, ofp);
|
||||
|
||||
uint8_t buf[1024];
|
||||
while (size > 0)
|
||||
{
|
||||
long thisSize = size;
|
||||
if (thisSize > 1024)
|
||||
thisSize = 1024;
|
||||
|
||||
fread(buf, 1, (size_t)thisSize, fp);
|
||||
fwrite(buf, 1, (size_t)thisSize, ofp);
|
||||
size -= thisSize;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
fclose(ofp);
|
||||
return 0;
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
After Width: | Height: | Size: 118 KiB |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |