mirror of https://github.com/AxioDL/amuse.git
New code style refactor
This commit is contained in:
parent
b4c073c373
commit
a7a408cc66
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
IndentWidth: 4
|
BasedOnStyle: LLVM
|
||||||
ColumnLimit: 120
|
ColumnLimit: 120
|
||||||
UseTab: Never
|
UseTab: Never
|
||||||
---
|
---
|
||||||
|
@ -8,7 +8,6 @@ DerivePointerAlignment: false
|
||||||
PointerAlignment: Left
|
PointerAlignment: Left
|
||||||
AlignAfterOpenBracket: Align
|
AlignAfterOpenBracket: Align
|
||||||
AlignConsecutiveAssignments: false
|
AlignConsecutiveAssignments: false
|
||||||
BreakBeforeBraces: Allman
|
|
||||||
IndentCaseLabels: false
|
IndentCaseLabels: false
|
||||||
AllowShortBlocksOnASingleLine: true
|
AllowShortBlocksOnASingleLine: true
|
||||||
AlignOperands: true
|
AlignOperands: true
|
||||||
|
@ -16,7 +15,6 @@ AlignTrailingComments: true
|
||||||
AlwaysBreakBeforeMultilineStrings: true
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
AlwaysBreakTemplateDeclarations: true
|
AlwaysBreakTemplateDeclarations: true
|
||||||
BreakConstructorInitializersBeforeComma: true
|
BreakConstructorInitializersBeforeComma: true
|
||||||
BreakStringLiterals: true
|
|
||||||
AlwaysBreakAfterReturnType: None
|
AlwaysBreakAfterReturnType: None
|
||||||
AlwaysBreakAfterDefinitionReturnType: None
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
AllowShortFunctionsOnASingleLine: All
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
@ -25,6 +23,6 @@ NamespaceIndentation: None
|
||||||
BinPackArguments: true
|
BinPackArguments: true
|
||||||
BinPackParameters: true
|
BinPackParameters: true
|
||||||
SortIncludes: false
|
SortIncludes: false
|
||||||
AccessModifierOffset: -4
|
AccessModifierOffset: -2
|
||||||
ConstructorInitializerIndentWidth: 0
|
ConstructorInitializerIndentWidth: 0
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
|
|
@ -5,50 +5,46 @@
|
||||||
#include <amuse/BooBackend.hpp>
|
#include <amuse/BooBackend.hpp>
|
||||||
#include <boo/audiodev/IAudioVoiceEngine.hpp>
|
#include <boo/audiodev/IAudioVoiceEngine.hpp>
|
||||||
|
|
||||||
@interface DataOutlineView : NSOutlineView
|
@interface DataOutlineView : NSOutlineView {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
IBOutlet NSButton* removeDataButton;
|
IBOutlet NSButton* removeDataButton;
|
||||||
IBOutlet NSMenuItem* deleteMenuItem;
|
IBOutlet NSMenuItem* deleteMenuItem;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
|
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
|
||||||
{
|
AudioGroupFilePresenter* presenter;
|
||||||
AudioGroupFilePresenter* presenter;
|
|
||||||
}
|
}
|
||||||
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
|
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
|
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
|
||||||
{
|
AudioGroupFilePresenter* presenter;
|
||||||
AudioGroupFilePresenter* presenter;
|
|
||||||
}
|
}
|
||||||
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
|
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient>
|
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient> {
|
||||||
{
|
IBOutlet NSWindow* mainWindow;
|
||||||
IBOutlet NSWindow* mainWindow;
|
IBOutlet NSOutlineView* dataOutline;
|
||||||
IBOutlet NSOutlineView* dataOutline;
|
IBOutlet NSSearchField* dataSearchField;
|
||||||
IBOutlet NSSearchField* dataSearchField;
|
IBOutlet NSTableView* sfxTable;
|
||||||
IBOutlet NSTableView* sfxTable;
|
IBOutlet NSTableView* samplesTable;
|
||||||
IBOutlet NSTableView* samplesTable;
|
IBOutlet NSTextView* creditsView;
|
||||||
IBOutlet NSTextView* creditsView;
|
|
||||||
|
|
||||||
IBOutlet NSButton* removeDataButton;
|
IBOutlet NSButton* removeDataButton;
|
||||||
IBOutlet NSMenuItem* removeDataMenu;
|
IBOutlet NSMenuItem* removeDataMenu;
|
||||||
|
|
||||||
AudioGroupFilePresenter* groupFilePresenter;
|
AudioGroupFilePresenter* groupFilePresenter;
|
||||||
|
|
||||||
SamplesTableController* samplesController;
|
SamplesTableController* samplesController;
|
||||||
SFXTableController* sfxController;
|
SFXTableController* sfxController;
|
||||||
|
|
||||||
@public
|
@public
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
|
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
|
||||||
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
|
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
|
||||||
std::experimental::optional<amuse::Engine> amuseEngine;
|
std::experimental::optional<amuse::Engine> amuseEngine;
|
||||||
std::shared_ptr<amuse::Voice> activeSFXVox;
|
std::shared_ptr<amuse::Voice> activeSFXVox;
|
||||||
}
|
}
|
||||||
- (BOOL)importURL:(NSURL*)url;
|
- (BOOL)importURL:(NSURL*)url;
|
||||||
- (void)outlineView:(DataOutlineView*)ov selectionChanged:(id)item;
|
- (void)outlineView:(DataOutlineView*)ov selectionChanged:(id)item;
|
||||||
|
@ -56,4 +52,3 @@
|
||||||
- (void)startSFX:(int)sfxId;
|
- (void)startSFX:(int)sfxId;
|
||||||
- (void)startSample:(int)sampId;
|
- (void)startSample:(int)sampId;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -19,142 +19,127 @@
|
||||||
- (amuse::Engine&)getAmuseEngine;
|
- (amuse::Engine&)getAmuseEngine;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
struct AudioGroupDataCollection
|
struct AudioGroupDataCollection {
|
||||||
{
|
std::string m_name;
|
||||||
std::string m_name;
|
NSURL* m_proj;
|
||||||
NSURL* m_proj;
|
NSURL* m_pool;
|
||||||
NSURL* m_pool;
|
NSURL* m_sdir;
|
||||||
NSURL* m_sdir;
|
NSURL* m_samp;
|
||||||
NSURL* m_samp;
|
NSURL* m_meta;
|
||||||
NSURL* m_meta;
|
|
||||||
|
|
||||||
AudioGroupDataToken* m_token;
|
AudioGroupDataToken* m_token;
|
||||||
|
|
||||||
std::vector<uint8_t> m_projData;
|
std::vector<uint8_t> m_projData;
|
||||||
std::vector<uint8_t> m_poolData;
|
std::vector<uint8_t> m_poolData;
|
||||||
std::vector<uint8_t> m_sdirData;
|
std::vector<uint8_t> m_sdirData;
|
||||||
std::vector<uint8_t> m_sampData;
|
std::vector<uint8_t> m_sampData;
|
||||||
|
|
||||||
struct MetaData
|
struct MetaData {
|
||||||
{
|
amuse::DataFormat fmt;
|
||||||
amuse::DataFormat fmt;
|
uint32_t absOffs;
|
||||||
uint32_t absOffs;
|
uint32_t active;
|
||||||
uint32_t active;
|
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
||||||
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {}
|
||||||
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
|
MetaData(athena::io::FileReader& r)
|
||||||
{
|
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
|
||||||
}
|
};
|
||||||
MetaData(athena::io::FileReader& r)
|
std::experimental::optional<MetaData> m_metaData;
|
||||||
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::experimental::optional<MetaData> m_metaData;
|
|
||||||
|
|
||||||
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
|
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
|
||||||
const amuse::AudioGroup* m_loadedGroup;
|
const amuse::AudioGroup* m_loadedGroup;
|
||||||
std::vector<AudioGroupToken*> m_groupTokens;
|
std::vector<AudioGroupToken*> m_groupTokens;
|
||||||
|
|
||||||
void moveURL(NSURL* oldUrl, NSURL* newUrl);
|
void moveURL(NSURL* oldUrl, NSURL* newUrl);
|
||||||
|
|
||||||
bool loadProj(AudioGroupFilePresenter* presenter);
|
bool loadProj(AudioGroupFilePresenter* presenter);
|
||||||
bool loadPool(AudioGroupFilePresenter* presenter);
|
bool loadPool(AudioGroupFilePresenter* presenter);
|
||||||
bool loadSdir(AudioGroupFilePresenter* presenter);
|
bool loadSdir(AudioGroupFilePresenter* presenter);
|
||||||
bool loadSamp(AudioGroupFilePresenter* presenter);
|
bool loadSamp(AudioGroupFilePresenter* presenter);
|
||||||
bool loadMeta(AudioGroupFilePresenter* presenter);
|
bool loadMeta(AudioGroupFilePresenter* presenter);
|
||||||
|
|
||||||
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
|
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
|
||||||
bool isDataComplete() const
|
bool isDataComplete() const {
|
||||||
{
|
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
|
||||||
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
|
}
|
||||||
}
|
bool _attemptLoad(AudioGroupFilePresenter* presenter);
|
||||||
bool _attemptLoad(AudioGroupFilePresenter* presenter);
|
bool _indexData(AudioGroupFilePresenter* presenter);
|
||||||
bool _indexData(AudioGroupFilePresenter* presenter);
|
|
||||||
|
|
||||||
void enable(AudioGroupFilePresenter* presenter);
|
void enable(AudioGroupFilePresenter* presenter);
|
||||||
void disable(AudioGroupFilePresenter* presenter);
|
void disable(AudioGroupFilePresenter* presenter);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioGroupCollection
|
struct AudioGroupCollection {
|
||||||
{
|
NSURL* m_url;
|
||||||
NSURL* m_url;
|
|
||||||
|
|
||||||
AudioGroupCollectionToken* m_token;
|
AudioGroupCollectionToken* m_token;
|
||||||
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
||||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
|
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
|
||||||
|
|
||||||
AudioGroupCollection(NSURL* url);
|
AudioGroupCollection(NSURL* url);
|
||||||
void addCollection(AudioGroupFilePresenter* presenter,
|
void addCollection(AudioGroupFilePresenter* presenter,
|
||||||
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
|
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||||
void update(AudioGroupFilePresenter* presenter);
|
void update(AudioGroupFilePresenter* presenter);
|
||||||
bool doSearch(std::string_view str);
|
bool doSearch(std::string_view str);
|
||||||
bool doActiveFilter();
|
bool doActiveFilter();
|
||||||
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
|
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
|
||||||
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
|
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface AudioGroupDataToken : NSObject
|
@interface AudioGroupDataToken : NSObject {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
AudioGroupDataCollection* m_collection;
|
AudioGroupDataCollection* m_collection;
|
||||||
}
|
}
|
||||||
- (id)initWithDataCollection:(AudioGroupDataCollection*)collection;
|
- (id)initWithDataCollection:(AudioGroupDataCollection*)collection;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AudioGroupCollectionToken : NSObject
|
@interface AudioGroupCollectionToken : NSObject {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
AudioGroupCollection* m_collection;
|
AudioGroupCollection* m_collection;
|
||||||
}
|
}
|
||||||
- (id)initWithCollection:(AudioGroupCollection*)collection;
|
- (id)initWithCollection:(AudioGroupCollection*)collection;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AudioGroupSFXToken : NSObject
|
@interface AudioGroupSFXToken : NSObject {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
NSAttributedString* m_name;
|
NSAttributedString* m_name;
|
||||||
int m_loadId;
|
int m_loadId;
|
||||||
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
|
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
|
||||||
}
|
}
|
||||||
- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx;
|
- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AudioGroupSampleToken : NSObject
|
@interface AudioGroupSampleToken : NSObject {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
NSAttributedString* m_name;
|
NSAttributedString* m_name;
|
||||||
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
|
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
|
||||||
}
|
}
|
||||||
- (id)
|
- (id)initWithName:(NSAttributedString*)name
|
||||||
initWithName:(NSAttributedString*)name
|
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry,
|
||||||
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>*)
|
amuse::AudioGroupSampleDirectory::ADPCMParms>*)sample;
|
||||||
sample;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AudioGroupToken : NSObject
|
@interface AudioGroupToken : NSObject {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
NSString* m_name;
|
NSString* m_name;
|
||||||
int m_id;
|
int m_id;
|
||||||
const amuse::SongGroupIndex* m_song;
|
const amuse::SongGroupIndex* m_song;
|
||||||
const amuse::SFXGroupIndex* m_sfx;
|
const amuse::SFXGroupIndex* m_sfx;
|
||||||
}
|
}
|
||||||
- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group;
|
- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group;
|
||||||
- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group;
|
- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate>
|
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate> {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
id<AudioGroupClient> m_audioGroupClient;
|
id<AudioGroupClient> m_audioGroupClient;
|
||||||
NSURL* m_groupURL;
|
NSURL* m_groupURL;
|
||||||
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
|
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
|
||||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
|
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
|
||||||
NSOutlineView* m_lastOutlineView;
|
NSOutlineView* m_lastOutlineView;
|
||||||
NSString* m_searchStr;
|
NSString* m_searchStr;
|
||||||
|
|
||||||
std::vector<AudioGroupSFXToken*> m_sfxTableData;
|
std::vector<AudioGroupSFXToken*> m_sfxTableData;
|
||||||
std::vector<AudioGroupSampleToken*> m_sampleTableData;
|
std::vector<AudioGroupSampleToken*> m_sampleTableData;
|
||||||
}
|
}
|
||||||
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
|
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
|
||||||
- (BOOL)addCollectionName:(std::string&&)name
|
- (BOOL)addCollectionName:(std::string&&)name
|
||||||
|
@ -164,4 +149,3 @@ initWithName:(NSAttributedString*)name
|
||||||
- (void)setSearchFilter:(NSString*)str;
|
- (void)setSearchFilter:(NSString*)str;
|
||||||
- (void)removeSelectedItem;
|
- (void)removeSelectedItem;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -19,29 +19,26 @@
|
||||||
|
|
||||||
@class AudioUnitViewController;
|
@class AudioUnitViewController;
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
/** Backend voice allocator implementation for AudioUnit mixer */
|
/** Backend voice allocator implementation for AudioUnit mixer */
|
||||||
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator
|
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
|
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterAudioUnit();
|
void RegisterAudioUnit();
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient>
|
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient> {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
AudioUnitViewController* m_viewController;
|
AudioUnitViewController* m_viewController;
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
||||||
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
|
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
|
||||||
std::experimental::optional<amuse::Engine> m_engine;
|
std::experimental::optional<amuse::Engine> m_engine;
|
||||||
AudioGroupFilePresenter* m_filePresenter;
|
AudioGroupFilePresenter* m_filePresenter;
|
||||||
AUAudioUnitBus* m_outBus;
|
AUAudioUnitBus* m_outBus;
|
||||||
AUAudioUnitBusArray* m_outs;
|
AUAudioUnitBusArray* m_outs;
|
||||||
}
|
}
|
||||||
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
|
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
|
||||||
error:(NSError* __nullable* __nonnull)outError
|
error:(NSError* __nullable* __nonnull)outError
|
||||||
|
|
|
@ -5,19 +5,16 @@
|
||||||
|
|
||||||
@class AmuseAudioUnit;
|
@class AmuseAudioUnit;
|
||||||
|
|
||||||
@interface GroupBrowserDelegate : NSObject <NSBrowserDelegate>
|
@interface GroupBrowserDelegate : NSObject <NSBrowserDelegate> {
|
||||||
{
|
AmuseAudioUnit* m_audioUnit;
|
||||||
AmuseAudioUnit* m_audioUnit;
|
|
||||||
}
|
}
|
||||||
- (id)initWithAudioUnit:(AmuseAudioUnit*)au;
|
- (id)initWithAudioUnit:(AmuseAudioUnit*)au;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AudioUnitViewController : AUViewController <AUAudioUnitFactory>
|
@interface AudioUnitViewController : AUViewController <AUAudioUnitFactory> {
|
||||||
{
|
|
||||||
@public
|
@public
|
||||||
AmuseAudioUnit* m_audioUnit;
|
AmuseAudioUnit* m_audioUnit;
|
||||||
IBOutlet NSBrowser* m_groupBrowser;
|
IBOutlet NSBrowser* m_groupBrowser;
|
||||||
GroupBrowserDelegate* m_groupBrowserDelegate;
|
GroupBrowserDelegate* m_groupBrowserDelegate;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,73 +10,71 @@
|
||||||
|
|
||||||
class ADSREditor;
|
class ADSREditor;
|
||||||
|
|
||||||
class ADSRView : public QWidget
|
class ADSRView : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class ADSRControls;
|
||||||
friend class ADSRControls;
|
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
|
QFont m_gridFont;
|
||||||
QFont m_gridFont;
|
QStaticText m_percentTexts[11];
|
||||||
QStaticText m_percentTexts[11];
|
std::vector<QStaticText> m_timeTexts;
|
||||||
std::vector<QStaticText> m_timeTexts;
|
int m_dragPoint = -1;
|
||||||
int m_dragPoint = -1;
|
uint64_t m_cycleIdx = 0;
|
||||||
uint64_t m_cycleIdx = 0;
|
ADSREditor* getEditor() const;
|
||||||
ADSREditor* getEditor() const;
|
|
||||||
public:
|
|
||||||
explicit ADSRView(QWidget* parent = Q_NULLPTR);
|
|
||||||
void loadData(ProjectModel::ADSRNode* node);
|
|
||||||
void unloadData();
|
|
||||||
ProjectModel::INode* currentNode() const;
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent* ev);
|
public:
|
||||||
void mousePressEvent(QMouseEvent* ev);
|
explicit ADSRView(QWidget* parent = Q_NULLPTR);
|
||||||
void mouseReleaseEvent(QMouseEvent* ev);
|
void loadData(ProjectModel::ADSRNode* node);
|
||||||
void mouseMoveEvent(QMouseEvent* ev);
|
void unloadData();
|
||||||
|
ProjectModel::INode* currentNode() const;
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent* ev);
|
||||||
|
void mousePressEvent(QMouseEvent* ev);
|
||||||
|
void mouseReleaseEvent(QMouseEvent* ev);
|
||||||
|
void mouseMoveEvent(QMouseEvent* ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ADSRControls : public QFrame
|
class ADSRControls : public QFrame {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class ADSRView;
|
||||||
friend class ADSRView;
|
QDoubleSpinBox* m_attack;
|
||||||
QDoubleSpinBox* m_attack;
|
QDoubleSpinBox* m_decay;
|
||||||
QDoubleSpinBox* m_decay;
|
QDoubleSpinBox* m_sustain;
|
||||||
QDoubleSpinBox* m_sustain;
|
QDoubleSpinBox* m_release;
|
||||||
QDoubleSpinBox* m_release;
|
QCheckBox* m_dls;
|
||||||
QCheckBox* m_dls;
|
QLabel* m_velToAttackLab;
|
||||||
QLabel* m_velToAttackLab;
|
QDoubleSpinBox* m_velToAttack;
|
||||||
QDoubleSpinBox* m_velToAttack;
|
QLabel* m_keyToDecayLab;
|
||||||
QLabel* m_keyToDecayLab;
|
QDoubleSpinBox* m_keyToDecay;
|
||||||
QDoubleSpinBox* m_keyToDecay;
|
bool m_enableUpdate = true;
|
||||||
bool m_enableUpdate = true;
|
ADSREditor* getEditor() const;
|
||||||
ADSREditor* getEditor() const;
|
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
|
||||||
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
|
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
|
||||||
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
|
void setRelease(double release, uint64_t cycleCount);
|
||||||
void setRelease(double release, uint64_t cycleCount);
|
|
||||||
public:
|
public:
|
||||||
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
|
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
|
||||||
void loadData();
|
void loadData();
|
||||||
void unloadData();
|
void unloadData();
|
||||||
public slots:
|
public slots:
|
||||||
void attackChanged(double val);
|
void attackChanged(double val);
|
||||||
void decayChanged(double val);
|
void decayChanged(double val);
|
||||||
void sustainChanged(double val);
|
void sustainChanged(double val);
|
||||||
void releaseChanged(double val);
|
void releaseChanged(double val);
|
||||||
void dlsStateChanged(int state);
|
void dlsStateChanged(int state);
|
||||||
void velToAttackChanged(double val);
|
void velToAttackChanged(double val);
|
||||||
void keyToDecayChanged(double val);
|
void keyToDecayChanged(double val);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ADSREditor : public EditorWidget
|
class ADSREditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class ADSRView;
|
||||||
friend class ADSRView;
|
friend class ADSRControls;
|
||||||
friend class ADSRControls;
|
ADSRView* m_adsrView;
|
||||||
ADSRView* m_adsrView;
|
ADSRControls* m_controls;
|
||||||
ADSRControls* m_controls;
|
|
||||||
public:
|
public:
|
||||||
explicit ADSREditor(QWidget* parent = Q_NULLPTR);
|
explicit ADSREditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::ADSRNode* node);
|
bool loadData(ProjectModel::ADSRNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,105 +4,93 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
boo::SystemString QStringToSysString(const QString& str)
|
boo::SystemString QStringToSysString(const QString& str) {
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return (wchar_t*)str.utf16();
|
return (wchar_t*)str.utf16();
|
||||||
#else
|
#else
|
||||||
return str.toUtf8().toStdString();
|
return str.toUtf8().toStdString();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SysStringToQString(const boo::SystemString& str)
|
QString SysStringToQString(const boo::SystemString& str) {
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return QString::fromStdWString(str);
|
return QString::fromStdWString(str);
|
||||||
#else
|
#else
|
||||||
return QString::fromStdString(str);
|
return QString::fromStdString(str);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MkPath(const QString& path, UIMessenger& messenger)
|
bool MkPath(const QString& path, UIMessenger& messenger) {
|
||||||
{
|
QFileInfo fInfo(path);
|
||||||
QFileInfo fInfo(path);
|
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
|
||||||
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger)
|
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger) {
|
||||||
{
|
if (!dir.mkpath(file)) {
|
||||||
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);
|
||||||
QString msg = QString(MainWindow::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
|
return false;
|
||||||
messenger.critical(MainWindow::tr("Unable to create directory"), msg);
|
}
|
||||||
return false;
|
return true;
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn)
|
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn) {
|
||||||
{
|
const QFileInfo fileInfo(pathIn);
|
||||||
const QFileInfo fileInfo(pathIn);
|
// Mac, Windows support folder or file.
|
||||||
// Mac, Windows support folder or file.
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path"));
|
QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path"));
|
||||||
QString explorer;
|
QString explorer;
|
||||||
for (QString path : paths.split(QStringLiteral(";")))
|
for (QString path : paths.split(QStringLiteral(";"))) {
|
||||||
{
|
QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe"));
|
||||||
QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe"));
|
if (finfo.exists()) {
|
||||||
if (finfo.exists())
|
explorer = finfo.filePath();
|
||||||
{
|
break;
|
||||||
explorer = finfo.filePath();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (explorer.isEmpty()) {
|
}
|
||||||
QMessageBox::warning(parent,
|
if (explorer.isEmpty()) {
|
||||||
MainWindow::tr("Launching Windows Explorer Failed"),
|
QMessageBox::warning(parent, MainWindow::tr("Launching Windows Explorer Failed"),
|
||||||
MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer."));
|
MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QStringList param;
|
QStringList param;
|
||||||
if (!fileInfo.isDir())
|
if (!fileInfo.isDir())
|
||||||
param += QLatin1String("/select,");
|
param += QLatin1String("/select,");
|
||||||
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
|
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
|
||||||
QProcess::startDetached(explorer, param);
|
QProcess::startDetached(explorer, param);
|
||||||
#elif defined(Q_OS_MAC)
|
#elif defined(Q_OS_MAC)
|
||||||
QStringList scriptArgs;
|
QStringList scriptArgs;
|
||||||
scriptArgs << QLatin1String("-e")
|
scriptArgs << QLatin1String("-e")
|
||||||
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
|
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
|
||||||
.arg(fileInfo.canonicalFilePath());
|
.arg(fileInfo.canonicalFilePath());
|
||||||
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
||||||
scriptArgs.clear();
|
scriptArgs.clear();
|
||||||
scriptArgs << QLatin1String("-e")
|
scriptArgs << QLatin1String("-e") << QLatin1String("tell application \"Finder\" to activate");
|
||||||
<< QLatin1String("tell application \"Finder\" to activate");
|
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
||||||
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
|
|
||||||
#else
|
#else
|
||||||
// we cannot select a file here, because no file browser really supports it...
|
// we cannot select a file here, because no file browser really supports it...
|
||||||
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
|
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
|
||||||
QProcess browserProc;
|
QProcess browserProc;
|
||||||
const QString browserArgs = QStringLiteral("xdg-open \"%1\"").arg(QFileInfo(folder).path());
|
const QString browserArgs = QStringLiteral("xdg-open \"%1\"").arg(QFileInfo(folder).path());
|
||||||
browserProc.startDetached(browserArgs);
|
browserProc.startDetached(browserArgs);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ShowInGraphicalShellString()
|
QString ShowInGraphicalShellString() {
|
||||||
{
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
return MainWindow::tr("Show in Explorer");
|
return MainWindow::tr("Show in Explorer");
|
||||||
#elif defined(Q_OS_MAC)
|
#elif defined(Q_OS_MAC)
|
||||||
return MainWindow::tr("Show in Finder");
|
return MainWindow::tr("Show in Finder");
|
||||||
#else
|
#else
|
||||||
return MainWindow::tr("Show in Browser");
|
return MainWindow::tr("Show in Browser");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QTransform RectToRect(const QRectF& from, const QRectF& to)
|
QTransform RectToRect(const QRectF& from, const QRectF& to) {
|
||||||
{
|
QPolygonF orig(from);
|
||||||
QPolygonF orig(from);
|
orig.pop_back();
|
||||||
orig.pop_back();
|
QPolygonF resize(to);
|
||||||
QPolygonF resize(to);
|
resize.pop_back();
|
||||||
resize.pop_back();
|
QTransform ret;
|
||||||
QTransform ret;
|
QTransform::quadToQuad(orig, resize, ret);
|
||||||
QTransform::quadToQuad(orig, resize, ret);
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,32 +8,27 @@
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
extern MainWindow* g_MainWindow;
|
extern MainWindow* g_MainWindow;
|
||||||
|
|
||||||
class UIMessenger : public QObject
|
class UIMessenger : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
using QObject::QObject;
|
using QObject::QObject;
|
||||||
signals:
|
signals:
|
||||||
QMessageBox::StandardButton information(const QString &title,
|
QMessageBox::StandardButton information(const QString& title, const QString& text,
|
||||||
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
int question(const QString &title,
|
int question(const QString& title, const QString& text, const QString& button0Text,
|
||||||
const QString& text,
|
const QString& button1Text = QString(), const QString& button2Text = QString(),
|
||||||
const QString& button0Text,
|
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
|
||||||
const QString& button1Text = QString(),
|
QMessageBox::StandardButton
|
||||||
const QString& button2Text = QString(),
|
question(const QString& title, const QString& text,
|
||||||
int defaultButtonNumber = 0,
|
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||||
int escapeButtonNumber = -1);
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
QMessageBox::StandardButton question(const QString &title,
|
QMessageBox::StandardButton warning(const QString& title, const QString& text,
|
||||||
const QString &text, QMessageBox::StandardButtons buttons =
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||||
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
QMessageBox::StandardButton critical(const QString& title, const QString& text,
|
||||||
QMessageBox::StandardButton warning(const QString &title,
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||||
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
|
||||||
QMessageBox::StandardButton critical(const QString &title,
|
|
||||||
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
|
||||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
boo::SystemString QStringToSysString(const QString& str);
|
boo::SystemString QStringToSysString(const QString& str);
|
||||||
|
@ -45,22 +40,14 @@ bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger);
|
||||||
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn);
|
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn);
|
||||||
QString ShowInGraphicalShellString();
|
QString ShowInGraphicalShellString();
|
||||||
|
|
||||||
static QLatin1String StringViewToQString(std::string_view sv)
|
static QLatin1String StringViewToQString(std::string_view sv) { return QLatin1String(sv.data(), int(sv.size())); }
|
||||||
{
|
|
||||||
return QLatin1String(sv.data(), int(sv.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Used for generating transform matrices to map SVG coordinate space */
|
/* Used for generating transform matrices to map SVG coordinate space */
|
||||||
QTransform RectToRect(const QRectF& from, const QRectF& to);
|
QTransform RectToRect(const QRectF& from, const QRectF& to);
|
||||||
|
|
||||||
namespace std
|
namespace std {
|
||||||
{
|
template <>
|
||||||
template<> struct hash<QString>
|
struct hash<QString> {
|
||||||
{
|
std::size_t operator()(const QString& s) const noexcept { return qHash(s); }
|
||||||
std::size_t operator()(const QString& s) const noexcept
|
|
||||||
{
|
|
||||||
return qHash(s);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
} // namespace std
|
||||||
|
|
||||||
|
|
|
@ -6,321 +6,266 @@
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QJSValueIterator>
|
#include <QJSValueIterator>
|
||||||
|
|
||||||
class CurveEditUndoCommand : public EditorUndoCommand
|
class CurveEditUndoCommand : public EditorUndoCommand {
|
||||||
{
|
uint8_t m_redoData[128];
|
||||||
uint8_t m_redoData[128];
|
uint8_t m_undoData[128];
|
||||||
uint8_t m_undoData[128];
|
bool m_usedExpr;
|
||||||
bool m_usedExpr;
|
bool m_undid = false;
|
||||||
bool m_undid = false;
|
|
||||||
public:
|
public:
|
||||||
CurveEditUndoCommand(const uint8_t* redoData, bool usedExpr,
|
CurveEditUndoCommand(const uint8_t* redoData, bool usedExpr, amuse::ObjToken<ProjectModel::CurveNode> node)
|
||||||
amuse::ObjToken<ProjectModel::CurveNode> node)
|
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")), m_usedExpr(usedExpr) {
|
||||||
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")), m_usedExpr(usedExpr)
|
std::memcpy(m_redoData, redoData, 128);
|
||||||
{
|
}
|
||||||
std::memcpy(m_redoData, redoData, 128);
|
void undo() {
|
||||||
|
m_undid = true;
|
||||||
|
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
|
||||||
|
if (table.Isa() == amuse::ITable::Type::Curve) {
|
||||||
|
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
||||||
|
curve.data.resize(128);
|
||||||
|
std::memcpy(curve.data.data(), m_undoData, 128);
|
||||||
}
|
}
|
||||||
void undo()
|
EditorUndoCommand::undo();
|
||||||
{
|
}
|
||||||
m_undid = true;
|
void redo() {
|
||||||
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
|
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
|
||||||
if (table.Isa() == amuse::ITable::Type::Curve)
|
if (table.Isa() == amuse::ITable::Type::Curve) {
|
||||||
{
|
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
||||||
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
curve.data.resize(128);
|
||||||
curve.data.resize(128);
|
std::memcpy(m_undoData, curve.data.data(), 128);
|
||||||
std::memcpy(curve.data.data(), m_undoData, 128);
|
std::memcpy(curve.data.data(), m_redoData, 128);
|
||||||
}
|
|
||||||
EditorUndoCommand::undo();
|
|
||||||
}
|
}
|
||||||
void redo()
|
if (m_undid)
|
||||||
{
|
EditorUndoCommand::redo();
|
||||||
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
|
}
|
||||||
if (table.Isa() == amuse::ITable::Type::Curve)
|
bool mergeWith(const QUndoCommand* other) {
|
||||||
{
|
if (other->id() == id() && !m_usedExpr && !static_cast<const CurveEditUndoCommand*>(other)->m_usedExpr) {
|
||||||
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
std::memcpy(m_redoData, static_cast<const CurveEditUndoCommand*>(other)->m_redoData, 128);
|
||||||
curve.data.resize(128);
|
return true;
|
||||||
std::memcpy(m_undoData, curve.data.data(), 128);
|
|
||||||
std::memcpy(curve.data.data(), m_redoData, 128);
|
|
||||||
}
|
|
||||||
if (m_undid)
|
|
||||||
EditorUndoCommand::redo();
|
|
||||||
}
|
}
|
||||||
bool mergeWith(const QUndoCommand* other)
|
return false;
|
||||||
{
|
}
|
||||||
if (other->id() == id() && !m_usedExpr && !static_cast<const CurveEditUndoCommand*>(other)->m_usedExpr)
|
int id() const { return int(Id::CurveEdit); }
|
||||||
{
|
|
||||||
std::memcpy(m_redoData, static_cast<const CurveEditUndoCommand*>(other)->m_redoData, 128);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int id() const { return int(Id::CurveEdit); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CurveEditor* CurveView::getEditor() const
|
CurveEditor* CurveView::getEditor() const { return qobject_cast<CurveEditor*>(parentWidget()); }
|
||||||
{
|
|
||||||
return qobject_cast<CurveEditor*>(parentWidget());
|
void CurveView::loadData(ProjectModel::CurveNode* node) { m_node = node; }
|
||||||
|
|
||||||
|
void CurveView::unloadData() { m_node.reset(); }
|
||||||
|
|
||||||
|
ProjectModel::INode* CurveView::currentNode() const { return m_node.get(); }
|
||||||
|
|
||||||
|
void CurveView::paintEvent(QPaintEvent* ev) {
|
||||||
|
if (!m_node)
|
||||||
|
return;
|
||||||
|
amuse::ITable& table = **m_node->m_obj;
|
||||||
|
if (table.Isa() != amuse::ITable::Type::Curve)
|
||||||
|
return;
|
||||||
|
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
qreal deviceRatio = devicePixelRatioF();
|
||||||
|
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
|
||||||
|
|
||||||
|
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
|
||||||
|
painter.setFont(m_gridFont);
|
||||||
|
qreal yIncrement = (height() - 16.0) / 10.0;
|
||||||
|
for (int i = 0; i < 11; ++i) {
|
||||||
|
qreal thisY = i * yIncrement;
|
||||||
|
qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0));
|
||||||
|
painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]);
|
||||||
|
painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY));
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal xIncrement = (width() - 30.0) / 10.0;
|
||||||
|
for (int i = 0; i < 11; ++i) {
|
||||||
|
qreal thisX = i * xIncrement + 30.0;
|
||||||
|
qreal textX = thisX - (i == 10 ? 30.0 : 15.0);
|
||||||
|
painter.drawStaticText(QPointF(textX, height() - 16.0), m_percentTextsCenter[i]);
|
||||||
|
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
xIncrement = (width() - 30.0) / 127.0;
|
||||||
|
QPointF lastPt;
|
||||||
|
painter.setPen(QPen(Qt::white, penWidth * 3.0));
|
||||||
|
for (i = 0; i < curve.data.size(); ++i) {
|
||||||
|
QPointF thisPt(i * xIncrement + 30.0, (height() - 16.0) - (height() - 16.0) * (curve.data[i] / 127.0));
|
||||||
|
if (i)
|
||||||
|
painter.drawLine(lastPt, thisPt);
|
||||||
|
lastPt = thisPt;
|
||||||
|
}
|
||||||
|
for (; i < 128; ++i) {
|
||||||
|
QPointF thisPt(i * xIncrement + 30.0, height() - 16.0);
|
||||||
|
if (i)
|
||||||
|
painter.drawLine(lastPt, thisPt);
|
||||||
|
lastPt = thisPt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::loadData(ProjectModel::CurveNode* node)
|
void CurveView::mousePressEvent(QMouseEvent* ev) { mouseMoveEvent(ev); }
|
||||||
{
|
|
||||||
m_node = node;
|
void CurveView::mouseMoveEvent(QMouseEvent* ev) {
|
||||||
|
CurveView* view = getEditor()->m_curveView;
|
||||||
|
amuse::ITable& table = **view->m_node->m_obj;
|
||||||
|
if (table.Isa() != amuse::ITable::Type::Curve)
|
||||||
|
return;
|
||||||
|
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
||||||
|
|
||||||
|
qreal xIncrement = (width() - 30.0) / 127.0;
|
||||||
|
int idx = int(std::round((ev->localPos().x() - 30.0) / xIncrement));
|
||||||
|
if (idx < 0 || idx > 127)
|
||||||
|
return;
|
||||||
|
int val = 127 - amuse::clamp(0, int(std::round(ev->localPos().y() / (height() - 16.0) * 127.0)), 127);
|
||||||
|
|
||||||
|
curve.data.resize(128);
|
||||||
|
uint8_t newData[128];
|
||||||
|
std::memcpy(newData, curve.data.data(), 128);
|
||||||
|
newData[idx] = uint8_t(val);
|
||||||
|
|
||||||
|
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, false, m_node));
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::unloadData()
|
CurveView::CurveView(QWidget* parent) : QWidget(parent) {
|
||||||
{
|
for (int i = 0; i < 11; ++i) {
|
||||||
m_node.reset();
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectModel::INode* CurveView::currentNode() const
|
CurveEditor* CurveControls::getEditor() const { return qobject_cast<CurveEditor*>(parentWidget()); }
|
||||||
{
|
|
||||||
return m_node.get();
|
void CurveControls::loadData() {
|
||||||
|
m_lineEdit->setDisabled(false);
|
||||||
|
m_setExpr->setDisabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::paintEvent(QPaintEvent* ev)
|
void CurveControls::unloadData() {
|
||||||
{
|
m_lineEdit->setDisabled(true);
|
||||||
if (!m_node)
|
m_setExpr->setDisabled(true);
|
||||||
return;
|
}
|
||||||
amuse::ITable& table = **m_node->m_obj;
|
|
||||||
if (table.Isa() != amuse::ITable::Type::Curve)
|
|
||||||
return;
|
|
||||||
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
|
||||||
|
|
||||||
QPainter painter(this);
|
void CurveControls::exprCommit() {
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
CurveView* view = getEditor()->m_curveView;
|
||||||
|
amuse::ITable& table = **view->m_node->m_obj;
|
||||||
|
if (table.Isa() != amuse::ITable::Type::Curve)
|
||||||
|
return;
|
||||||
|
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
||||||
|
|
||||||
qreal deviceRatio = devicePixelRatioF();
|
QString progText = m_lineEdit->text();
|
||||||
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
|
curve.data.resize(128);
|
||||||
|
uint8_t newData[128];
|
||||||
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
|
std::memcpy(newData, curve.data.data(), 128);
|
||||||
painter.setFont(m_gridFont);
|
bool notANumber = false;
|
||||||
qreal yIncrement = (height() - 16.0) / 10.0;
|
for (int i = 0; i < 128; ++i) {
|
||||||
for (int i = 0; i < 11; ++i)
|
m_engine.globalObject().setProperty(QStringLiteral("x"), i / 127.0);
|
||||||
{
|
QJSValue val = m_engine.evaluate(progText);
|
||||||
qreal thisY = i * yIncrement;
|
if (val.isError()) {
|
||||||
qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0));
|
m_errLabel->setText(val.toString());
|
||||||
painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]);
|
return;
|
||||||
painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY));
|
} else if (val.isNumber()) {
|
||||||
|
newData[i] = uint8_t(amuse::clamp(0, int(std::round(val.toNumber() * 127.0)), 127));
|
||||||
|
} else {
|
||||||
|
notANumber = true;
|
||||||
|
newData[i] = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (notANumber)
|
||||||
|
m_errLabel->setText(tr("Did not evaluate as a number"));
|
||||||
|
|
||||||
qreal xIncrement = (width() - 30.0) / 10.0;
|
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, true, view->m_node));
|
||||||
for (int i = 0; i < 11; ++i)
|
view->update();
|
||||||
{
|
|
||||||
qreal thisX = i * xIncrement + 30.0;
|
|
||||||
qreal textX = thisX - (i == 10 ? 30.0 : 15.0);
|
|
||||||
painter.drawStaticText(QPointF(textX, height() - 16.0), m_percentTextsCenter[i]);
|
|
||||||
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
xIncrement = (width() - 30.0) / 127.0;
|
|
||||||
QPointF lastPt;
|
|
||||||
painter.setPen(QPen(Qt::white, penWidth * 3.0));
|
|
||||||
for (i = 0; i < curve.data.size(); ++i)
|
|
||||||
{
|
|
||||||
QPointF thisPt(i * xIncrement + 30.0, (height() - 16.0) - (height() - 16.0) * (curve.data[i] / 127.0));
|
|
||||||
if (i)
|
|
||||||
painter.drawLine(lastPt, thisPt);
|
|
||||||
lastPt = thisPt;
|
|
||||||
}
|
|
||||||
for (; i < 128; ++i)
|
|
||||||
{
|
|
||||||
QPointF thisPt(i * xIncrement + 30.0, height() - 16.0);
|
|
||||||
if (i)
|
|
||||||
painter.drawLine(lastPt, thisPt);
|
|
||||||
lastPt = thisPt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::mousePressEvent(QMouseEvent* ev)
|
void CurveControls::resizeEvent(QResizeEvent* ev) { m_errLabel->setGeometry(22, 78, width() - 44, 14); }
|
||||||
{
|
|
||||||
mouseMoveEvent(ev);
|
CurveControls::CurveControls(QWidget* parent) : QFrame(parent) {
|
||||||
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||||
|
setFixedHeight(100);
|
||||||
|
setFrameShape(QFrame::StyledPanel);
|
||||||
|
setFrameShadow(QFrame::Sunken);
|
||||||
|
setBackgroundRole(QPalette::Base);
|
||||||
|
setAutoFillBackground(true);
|
||||||
|
|
||||||
|
QHBoxLayout* mainLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
QGridLayout* leftLayout = new QGridLayout;
|
||||||
|
|
||||||
|
leftLayout->addWidget(new QLabel(tr("Expression")), 0, 0);
|
||||||
|
m_lineEdit = new QLineEdit;
|
||||||
|
m_lineEdit->setDisabled(true);
|
||||||
|
QPalette palette = m_lineEdit->palette();
|
||||||
|
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
|
||||||
|
m_lineEdit->setPalette(palette);
|
||||||
|
connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(exprCommit()));
|
||||||
|
leftLayout->addWidget(m_lineEdit, 1, 0);
|
||||||
|
|
||||||
|
m_setExpr = new QPushButton(tr("Apply"));
|
||||||
|
m_setExpr->setDisabled(true);
|
||||||
|
connect(m_setExpr, SIGNAL(clicked(bool)), this, SLOT(exprCommit()));
|
||||||
|
leftLayout->addWidget(m_setExpr, 1, 1);
|
||||||
|
|
||||||
|
m_errLabel = new QLabel(this);
|
||||||
|
QFont font = m_errLabel->font();
|
||||||
|
font.setPointSize(8);
|
||||||
|
m_errLabel->setFont(font);
|
||||||
|
palette.setColor(QPalette::Text, Qt::red);
|
||||||
|
m_errLabel->setPalette(palette);
|
||||||
|
|
||||||
|
leftLayout->setColumnMinimumWidth(0, 500);
|
||||||
|
leftLayout->setColumnStretch(0, 1);
|
||||||
|
leftLayout->setColumnMinimumWidth(1, 75);
|
||||||
|
leftLayout->setColumnStretch(1, 0);
|
||||||
|
leftLayout->setRowMinimumHeight(0, 22);
|
||||||
|
leftLayout->setRowMinimumHeight(1, 37);
|
||||||
|
leftLayout->setContentsMargins(10, 6, 10, 14);
|
||||||
|
|
||||||
|
mainLayout->addLayout(leftLayout);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
QJSValueIterator it(m_engine.globalObject().property(QStringLiteral("Math")));
|
||||||
|
QString docStr =
|
||||||
|
tr("Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available:\n");
|
||||||
|
bool needsComma = false;
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
m_engine.globalObject().setProperty(it.name(), it.value());
|
||||||
|
if (needsComma)
|
||||||
|
docStr += QStringLiteral(", ");
|
||||||
|
needsComma = true;
|
||||||
|
docStr += it.name();
|
||||||
|
}
|
||||||
|
m_lineEdit->setToolTip(docStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CurveView::mouseMoveEvent(QMouseEvent* ev)
|
bool CurveEditor::loadData(ProjectModel::CurveNode* node) {
|
||||||
{
|
m_curveView->loadData(node);
|
||||||
CurveView* view = getEditor()->m_curveView;
|
m_controls->loadData();
|
||||||
amuse::ITable& table = **view->m_node->m_obj;
|
return true;
|
||||||
if (table.Isa() != amuse::ITable::Type::Curve)
|
|
||||||
return;
|
|
||||||
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
|
||||||
|
|
||||||
qreal xIncrement = (width() - 30.0) / 127.0;
|
|
||||||
int idx = int(std::round((ev->localPos().x() - 30.0) / xIncrement));
|
|
||||||
if (idx < 0 || idx > 127)
|
|
||||||
return;
|
|
||||||
int val = 127 - amuse::clamp(0, int(std::round(ev->localPos().y() / (height() - 16.0) * 127.0)), 127);
|
|
||||||
|
|
||||||
curve.data.resize(128);
|
|
||||||
uint8_t newData[128];
|
|
||||||
std::memcpy(newData, curve.data.data(), 128);
|
|
||||||
newData[idx] = uint8_t(val);
|
|
||||||
|
|
||||||
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, false, m_node));
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CurveView::CurveView(QWidget* parent)
|
void CurveEditor::unloadData() {
|
||||||
: QWidget(parent)
|
m_curveView->unloadData();
|
||||||
{
|
m_controls->unloadData();
|
||||||
for (int i = 0; i < 11; ++i)
|
|
||||||
{
|
|
||||||
m_percentTexts[i].setText(QStringLiteral("%1%").arg(100 - i * 10));
|
|
||||||
m_percentTexts[i].setTextOption(QTextOption(Qt::AlignVCenter | Qt::AlignRight));
|
|
||||||
m_percentTexts[i].setTextWidth(28.0);
|
|
||||||
m_percentTextsCenter[i].setText(QStringLiteral("%1%").arg(i * 10));
|
|
||||||
m_percentTextsCenter[i].setTextOption(QTextOption(Qt::AlignBaseline | Qt::AlignCenter));
|
|
||||||
m_percentTextsCenter[i].setTextWidth(28.0);
|
|
||||||
}
|
|
||||||
m_gridFont.setPointSize(8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CurveEditor* CurveControls::getEditor() const
|
ProjectModel::INode* CurveEditor::currentNode() const { return m_curveView->currentNode(); }
|
||||||
{
|
|
||||||
return qobject_cast<CurveEditor*>(parentWidget());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CurveControls::loadData()
|
|
||||||
{
|
|
||||||
m_lineEdit->setDisabled(false);
|
|
||||||
m_setExpr->setDisabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CurveControls::unloadData()
|
|
||||||
{
|
|
||||||
m_lineEdit->setDisabled(true);
|
|
||||||
m_setExpr->setDisabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CurveControls::exprCommit()
|
|
||||||
{
|
|
||||||
CurveView* view = getEditor()->m_curveView;
|
|
||||||
amuse::ITable& table = **view->m_node->m_obj;
|
|
||||||
if (table.Isa() != amuse::ITable::Type::Curve)
|
|
||||||
return;
|
|
||||||
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
|
|
||||||
|
|
||||||
QString progText = m_lineEdit->text();
|
|
||||||
curve.data.resize(128);
|
|
||||||
uint8_t newData[128];
|
|
||||||
std::memcpy(newData, curve.data.data(), 128);
|
|
||||||
bool notANumber = false;
|
|
||||||
for (int i = 0; i < 128; ++i)
|
|
||||||
{
|
|
||||||
m_engine.globalObject().setProperty(QStringLiteral("x"), i / 127.0);
|
|
||||||
QJSValue val = m_engine.evaluate(progText);
|
|
||||||
if (val.isError())
|
|
||||||
{
|
|
||||||
m_errLabel->setText(val.toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (val.isNumber())
|
|
||||||
{
|
|
||||||
newData[i] = uint8_t(amuse::clamp(0, int(std::round(val.toNumber() * 127.0)), 127));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
notANumber = true;
|
|
||||||
newData[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (notANumber)
|
|
||||||
m_errLabel->setText(tr("Did not evaluate as a number"));
|
|
||||||
|
|
||||||
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, true, view->m_node));
|
|
||||||
view->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CurveControls::resizeEvent(QResizeEvent* ev)
|
|
||||||
{
|
|
||||||
m_errLabel->setGeometry(22, 78, width() - 44, 14);
|
|
||||||
}
|
|
||||||
|
|
||||||
CurveControls::CurveControls(QWidget* parent)
|
|
||||||
: QFrame(parent)
|
|
||||||
{
|
|
||||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
||||||
setFixedHeight(100);
|
|
||||||
setFrameShape(QFrame::StyledPanel);
|
|
||||||
setFrameShadow(QFrame::Sunken);
|
|
||||||
setBackgroundRole(QPalette::Base);
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
|
|
||||||
QHBoxLayout* mainLayout = new QHBoxLayout;
|
|
||||||
|
|
||||||
QGridLayout* leftLayout = new QGridLayout;
|
|
||||||
|
|
||||||
leftLayout->addWidget(new QLabel(tr("Expression")), 0, 0);
|
|
||||||
m_lineEdit = new QLineEdit;
|
|
||||||
m_lineEdit->setDisabled(true);
|
|
||||||
QPalette palette = m_lineEdit->palette();
|
|
||||||
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
|
|
||||||
m_lineEdit->setPalette(palette);
|
|
||||||
connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(exprCommit()));
|
|
||||||
leftLayout->addWidget(m_lineEdit, 1, 0);
|
|
||||||
|
|
||||||
m_setExpr = new QPushButton(tr("Apply"));
|
|
||||||
m_setExpr->setDisabled(true);
|
|
||||||
connect(m_setExpr, SIGNAL(clicked(bool)), this, SLOT(exprCommit()));
|
|
||||||
leftLayout->addWidget(m_setExpr, 1, 1);
|
|
||||||
|
|
||||||
m_errLabel = new QLabel(this);
|
|
||||||
QFont font = m_errLabel->font();
|
|
||||||
font.setPointSize(8);
|
|
||||||
m_errLabel->setFont(font);
|
|
||||||
palette.setColor(QPalette::Text, Qt::red);
|
|
||||||
m_errLabel->setPalette(palette);
|
|
||||||
|
|
||||||
leftLayout->setColumnMinimumWidth(0, 500);
|
|
||||||
leftLayout->setColumnStretch(0, 1);
|
|
||||||
leftLayout->setColumnMinimumWidth(1, 75);
|
|
||||||
leftLayout->setColumnStretch(1, 0);
|
|
||||||
leftLayout->setRowMinimumHeight(0, 22);
|
|
||||||
leftLayout->setRowMinimumHeight(1, 37);
|
|
||||||
leftLayout->setContentsMargins(10, 6, 10, 14);
|
|
||||||
|
|
||||||
mainLayout->addLayout(leftLayout);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
|
|
||||||
QJSValueIterator it(m_engine.globalObject().property(QStringLiteral("Math")));
|
|
||||||
QString docStr = tr("Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available:\n");
|
|
||||||
bool needsComma = false;
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
it.next();
|
|
||||||
m_engine.globalObject().setProperty(it.name(), it.value());
|
|
||||||
if (needsComma)
|
|
||||||
docStr += QStringLiteral(", ");
|
|
||||||
needsComma = true;
|
|
||||||
docStr += it.name();
|
|
||||||
}
|
|
||||||
m_lineEdit->setToolTip(docStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CurveEditor::loadData(ProjectModel::CurveNode* node)
|
|
||||||
{
|
|
||||||
m_curveView->loadData(node);
|
|
||||||
m_controls->loadData();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CurveEditor::unloadData()
|
|
||||||
{
|
|
||||||
m_curveView->unloadData();
|
|
||||||
m_controls->unloadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
ProjectModel::INode* CurveEditor::currentNode() const
|
|
||||||
{
|
|
||||||
return m_curveView->currentNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
CurveEditor::CurveEditor(QWidget* parent)
|
CurveEditor::CurveEditor(QWidget* parent)
|
||||||
: EditorWidget(parent), m_curveView(new CurveView), m_controls(new CurveControls)
|
: EditorWidget(parent), m_curveView(new CurveView), m_controls(new CurveControls) {
|
||||||
{
|
QVBoxLayout* layout = new QVBoxLayout;
|
||||||
QVBoxLayout* layout = new QVBoxLayout;
|
layout->setContentsMargins(QMargins());
|
||||||
layout->setContentsMargins(QMargins());
|
layout->setSpacing(1);
|
||||||
layout->setSpacing(1);
|
layout->addWidget(m_curveView);
|
||||||
layout->addWidget(m_curveView);
|
layout->addWidget(m_controls);
|
||||||
layout->addWidget(m_controls);
|
setLayout(layout);
|
||||||
setLayout(layout);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,55 +10,54 @@
|
||||||
|
|
||||||
class CurveEditor;
|
class CurveEditor;
|
||||||
|
|
||||||
class CurveView : public QWidget
|
class CurveView : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class CurveControls;
|
||||||
friend class CurveControls;
|
amuse::ObjToken<ProjectModel::CurveNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::CurveNode> m_node;
|
QFont m_gridFont;
|
||||||
QFont m_gridFont;
|
QStaticText m_percentTexts[11];
|
||||||
QStaticText m_percentTexts[11];
|
QStaticText m_percentTextsCenter[11];
|
||||||
QStaticText m_percentTextsCenter[11];
|
CurveEditor* getEditor() const;
|
||||||
CurveEditor* getEditor() const;
|
|
||||||
public:
|
|
||||||
explicit CurveView(QWidget* parent = Q_NULLPTR);
|
|
||||||
void loadData(ProjectModel::CurveNode* node);
|
|
||||||
void unloadData();
|
|
||||||
ProjectModel::INode* currentNode() const;
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent* ev);
|
public:
|
||||||
void mousePressEvent(QMouseEvent* ev);
|
explicit CurveView(QWidget* parent = Q_NULLPTR);
|
||||||
void mouseMoveEvent(QMouseEvent* ev);
|
void loadData(ProjectModel::CurveNode* node);
|
||||||
|
void unloadData();
|
||||||
|
ProjectModel::INode* currentNode() const;
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent* ev);
|
||||||
|
void mousePressEvent(QMouseEvent* ev);
|
||||||
|
void mouseMoveEvent(QMouseEvent* ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CurveControls : public QFrame
|
class CurveControls : public QFrame {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class CurveView;
|
||||||
friend class CurveView;
|
QLineEdit* m_lineEdit;
|
||||||
QLineEdit* m_lineEdit;
|
QLabel* m_errLabel;
|
||||||
QLabel* m_errLabel;
|
QPushButton* m_setExpr;
|
||||||
QPushButton* m_setExpr;
|
QJSEngine m_engine;
|
||||||
QJSEngine m_engine;
|
CurveEditor* getEditor() const;
|
||||||
CurveEditor* getEditor() const;
|
|
||||||
public:
|
public:
|
||||||
explicit CurveControls(QWidget* parent = Q_NULLPTR);
|
explicit CurveControls(QWidget* parent = Q_NULLPTR);
|
||||||
void loadData();
|
void loadData();
|
||||||
void unloadData();
|
void unloadData();
|
||||||
void resizeEvent(QResizeEvent* ev);
|
void resizeEvent(QResizeEvent* ev);
|
||||||
public slots:
|
public slots:
|
||||||
void exprCommit();
|
void exprCommit();
|
||||||
};
|
};
|
||||||
|
|
||||||
class CurveEditor : public EditorWidget
|
class CurveEditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class CurveView;
|
||||||
friend class CurveView;
|
friend class CurveControls;
|
||||||
friend class CurveControls;
|
CurveView* m_curveView;
|
||||||
CurveView* m_curveView;
|
CurveControls* m_controls;
|
||||||
CurveControls* m_controls;
|
|
||||||
public:
|
public:
|
||||||
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
|
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::CurveNode* node);
|
bool loadData(ProjectModel::CurveNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,322 +3,273 @@
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
EditorWidget::EditorWidget(QWidget* parent)
|
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) {}
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
void EditorUndoCommand::undo() { g_MainWindow->openEditor(m_node.get()); }
|
||||||
|
|
||||||
|
void EditorUndoCommand::redo() { g_MainWindow->openEditor(m_node.get()); }
|
||||||
|
|
||||||
|
FieldSlider::FieldSlider(QWidget* parent) : QWidget(parent), m_slider(Qt::Horizontal) {
|
||||||
|
setFixedHeight(22);
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
QHBoxLayout* layout = new QHBoxLayout;
|
||||||
|
layout->addWidget(&m_slider);
|
||||||
|
layout->addWidget(&m_value);
|
||||||
|
layout->setContentsMargins(QMargins());
|
||||||
|
setLayout(layout);
|
||||||
|
m_value.setFixedWidth(42);
|
||||||
|
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
|
||||||
|
m_slider.setValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorUndoCommand::undo()
|
void FieldSlider::doValueChanged(int value) {
|
||||||
{
|
m_value.setText(QString::number(value));
|
||||||
g_MainWindow->openEditor(m_node.get());
|
emit valueChanged(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorUndoCommand::redo()
|
FieldDoubleSlider::FieldDoubleSlider(QWidget* parent) : QWidget(parent), m_slider(Qt::Horizontal) {
|
||||||
{
|
setFixedHeight(22);
|
||||||
g_MainWindow->openEditor(m_node.get());
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
QHBoxLayout* layout = new QHBoxLayout;
|
||||||
|
layout->addWidget(&m_slider);
|
||||||
|
layout->addWidget(&m_value);
|
||||||
|
layout->setContentsMargins(QMargins());
|
||||||
|
setLayout(layout);
|
||||||
|
m_value.setFixedWidth(42);
|
||||||
|
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
|
||||||
|
m_slider.setRange(0, 1000);
|
||||||
|
m_slider.setValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldSlider::FieldSlider(QWidget* parent)
|
double FieldDoubleSlider::value() const {
|
||||||
: QWidget(parent), m_slider(Qt::Horizontal)
|
double t = m_slider.value() / 1000.0;
|
||||||
{
|
return t * (m_max - m_min) + m_min;
|
||||||
setFixedHeight(22);
|
}
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
void FieldDoubleSlider::setValue(double value) {
|
||||||
QHBoxLayout* layout = new QHBoxLayout;
|
if (value < m_min)
|
||||||
layout->addWidget(&m_slider);
|
value = m_min;
|
||||||
layout->addWidget(&m_value);
|
else if (value > m_max)
|
||||||
layout->setContentsMargins(QMargins());
|
value = m_max;
|
||||||
setLayout(layout);
|
|
||||||
m_value.setFixedWidth(42);
|
double t = (value - m_min) / (m_max - m_min);
|
||||||
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
|
m_slider.setValue(int(t * 1000.0));
|
||||||
m_slider.setValue(0);
|
doValueChanged(int(t * 1000.0));
|
||||||
|
}
|
||||||
|
void FieldDoubleSlider::setRange(double min, double max) {
|
||||||
|
m_min = min;
|
||||||
|
m_max = max;
|
||||||
|
double curValue = value();
|
||||||
|
if (curValue < min)
|
||||||
|
setValue(min);
|
||||||
|
else if (curValue > max)
|
||||||
|
setValue(max);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldSlider::doValueChanged(int value)
|
void FieldDoubleSlider::doValueChanged(int value) {
|
||||||
{
|
double t = value / 1000.0;
|
||||||
m_value.setText(QString::number(value));
|
t = t * (m_max - m_min) + m_min;
|
||||||
emit valueChanged(value);
|
m_value.setText(QString::number(t, 'g', 2));
|
||||||
}
|
emit valueChanged(t);
|
||||||
|
|
||||||
FieldDoubleSlider::FieldDoubleSlider(QWidget* parent)
|
|
||||||
: QWidget(parent), m_slider(Qt::Horizontal)
|
|
||||||
{
|
|
||||||
setFixedHeight(22);
|
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
||||||
QHBoxLayout* layout = new QHBoxLayout;
|
|
||||||
layout->addWidget(&m_slider);
|
|
||||||
layout->addWidget(&m_value);
|
|
||||||
layout->setContentsMargins(QMargins());
|
|
||||||
setLayout(layout);
|
|
||||||
m_value.setFixedWidth(42);
|
|
||||||
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
|
|
||||||
m_slider.setRange(0, 1000);
|
|
||||||
m_slider.setValue(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
double FieldDoubleSlider::value() const
|
|
||||||
{
|
|
||||||
double t = m_slider.value() / 1000.0;
|
|
||||||
return t * (m_max - m_min) + m_min;
|
|
||||||
}
|
|
||||||
void FieldDoubleSlider::setValue(double value)
|
|
||||||
{
|
|
||||||
if (value < m_min)
|
|
||||||
value = m_min;
|
|
||||||
else if (value > m_max)
|
|
||||||
value = m_max;
|
|
||||||
|
|
||||||
double t = (value - m_min) / (m_max - m_min);
|
|
||||||
m_slider.setValue(int(t * 1000.0));
|
|
||||||
doValueChanged(int(t * 1000.0));
|
|
||||||
}
|
|
||||||
void FieldDoubleSlider::setRange(double min, double max)
|
|
||||||
{
|
|
||||||
m_min = min; m_max = max;
|
|
||||||
double curValue = value();
|
|
||||||
if (curValue < min)
|
|
||||||
setValue(min);
|
|
||||||
else if (curValue > max)
|
|
||||||
setValue(max);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FieldDoubleSlider::doValueChanged(int value)
|
|
||||||
{
|
|
||||||
double t = value / 1000.0;
|
|
||||||
t = t * (m_max - m_min) + m_min;
|
|
||||||
m_value.setText(QString::number(t, 'g', 2));
|
|
||||||
emit valueChanged(t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
|
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
|
||||||
: QWidget(parent), m_comboBox(this), m_button(this)
|
: QWidget(parent), m_comboBox(this), m_button(this) {
|
||||||
{
|
m_button.setDisabled(true);
|
||||||
|
m_button.setFixedSize(30, 30);
|
||||||
|
m_comboBox.setFixedHeight(30);
|
||||||
|
connect(&m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
|
||||||
|
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
|
||||||
|
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
|
||||||
|
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
|
||||||
|
m_button.setIcon(icon);
|
||||||
|
QHBoxLayout* layout = new QHBoxLayout;
|
||||||
|
layout->setContentsMargins({});
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->addWidget(&m_comboBox);
|
||||||
|
layout->addWidget(&m_button);
|
||||||
|
setLayout(layout);
|
||||||
|
setCollection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldProjectNode::setCollection(ProjectModel::CollectionNode* collection) {
|
||||||
|
m_collection = collection;
|
||||||
|
|
||||||
|
if (!collection) {
|
||||||
|
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
|
||||||
m_button.setDisabled(true);
|
m_button.setDisabled(true);
|
||||||
m_button.setFixedSize(30, 30);
|
return;
|
||||||
m_comboBox.setFixedHeight(30);
|
}
|
||||||
connect(&m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
|
|
||||||
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
|
ProjectModel* model = g_MainWindow->projectModel();
|
||||||
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
|
m_comboBox.setModel(model->getNullProxy());
|
||||||
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
|
m_comboBox.setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
|
||||||
m_button.setIcon(icon);
|
|
||||||
QHBoxLayout* layout = new QHBoxLayout;
|
|
||||||
layout->setContentsMargins({});
|
|
||||||
layout->setSpacing(0);
|
|
||||||
layout->addWidget(&m_comboBox);
|
|
||||||
layout->addWidget(&m_button);
|
|
||||||
setLayout(layout);
|
|
||||||
setCollection(collection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldProjectNode::setCollection(ProjectModel::CollectionNode* collection)
|
void FieldProjectNode::_currentIndexChanged(int index) {
|
||||||
{
|
m_button.setEnabled(index != 0);
|
||||||
m_collection = collection;
|
emit currentIndexChanged(index);
|
||||||
|
|
||||||
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)
|
ProjectModel::BasePoolObjectNode* FieldProjectNode::currentNode() const {
|
||||||
{
|
int index = m_comboBox.currentIndex();
|
||||||
m_button.setEnabled(index != 0);
|
if (index == 0)
|
||||||
emit currentIndexChanged(index);
|
return nullptr;
|
||||||
|
else
|
||||||
|
return m_collection->nodeOfIndex(index - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectModel::BasePoolObjectNode* FieldProjectNode::currentNode() const
|
bool FieldProjectNode::event(QEvent* ev) {
|
||||||
{
|
if (ev->type() == QEvent::User) {
|
||||||
int index = m_comboBox.currentIndex();
|
showPopup();
|
||||||
if (index == 0)
|
return true;
|
||||||
return nullptr;
|
}
|
||||||
else
|
return QWidget::event(ev);
|
||||||
return m_collection->nodeOfIndex(index - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FieldProjectNode::event(QEvent* ev)
|
void FieldProjectNode::openCurrent() {
|
||||||
{
|
if (ProjectModel::BasePoolObjectNode* node = currentNode())
|
||||||
if (ev->type() == QEvent::User)
|
if (!g_MainWindow->isUiDisabled())
|
||||||
{
|
g_MainWindow->openEditor(node);
|
||||||
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)
|
FieldPageObjectNode::FieldPageObjectNode(ProjectModel::GroupNode* group, QWidget* parent)
|
||||||
: QWidget(parent), m_comboBox(this), m_button(this)
|
: QWidget(parent), m_comboBox(this), m_button(this) {
|
||||||
{
|
m_button.setDisabled(true);
|
||||||
|
m_button.setFixedSize(30, 30);
|
||||||
|
m_comboBox.setFixedHeight(30);
|
||||||
|
connect(&m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
|
||||||
|
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
|
||||||
|
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
|
||||||
|
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
|
||||||
|
m_button.setIcon(icon);
|
||||||
|
QHBoxLayout* layout = new QHBoxLayout;
|
||||||
|
layout->setContentsMargins({});
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->addWidget(&m_comboBox);
|
||||||
|
layout->addWidget(&m_button);
|
||||||
|
setLayout(layout);
|
||||||
|
setGroup(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldPageObjectNode::setGroup(ProjectModel::GroupNode* group) {
|
||||||
|
m_group = group;
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
|
||||||
m_button.setDisabled(true);
|
m_button.setDisabled(true);
|
||||||
m_button.setFixedSize(30, 30);
|
return;
|
||||||
m_comboBox.setFixedHeight(30);
|
}
|
||||||
connect(&m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_currentIndexChanged(int)));
|
|
||||||
connect(&m_button, SIGNAL(clicked(bool)), this, SLOT(openCurrent()));
|
ProjectModel* model = g_MainWindow->projectModel();
|
||||||
QIcon icon(QStringLiteral(":/icons/IconForward.svg"));
|
m_comboBox.setModel(model->getPageObjectProxy());
|
||||||
icon.addFile(QStringLiteral(":/icons/IconForwardDisabled.svg"), {}, QIcon::Disabled);
|
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
|
||||||
m_button.setIcon(icon);
|
|
||||||
QHBoxLayout* layout = new QHBoxLayout;
|
|
||||||
layout->setContentsMargins({});
|
|
||||||
layout->setSpacing(0);
|
|
||||||
layout->addWidget(&m_comboBox);
|
|
||||||
layout->addWidget(&m_button);
|
|
||||||
setLayout(layout);
|
|
||||||
setGroup(group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldPageObjectNode::setGroup(ProjectModel::GroupNode* group)
|
void FieldPageObjectNode::_currentIndexChanged(int index) {
|
||||||
{
|
m_button.setEnabled(index != 0);
|
||||||
m_group = group;
|
emit currentIndexChanged(index);
|
||||||
|
}
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
|
|
||||||
m_button.setDisabled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ProjectModel::BasePoolObjectNode* FieldPageObjectNode::currentNode() const {
|
||||||
|
int index = m_comboBox.currentIndex();
|
||||||
|
if (index == 0) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
ProjectModel* model = g_MainWindow->projectModel();
|
ProjectModel* model = g_MainWindow->projectModel();
|
||||||
m_comboBox.setModel(model->getPageObjectProxy());
|
return static_cast<ProjectModel::BasePoolObjectNode*>(model->node(model->getPageObjectProxy()->mapToSource(
|
||||||
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
|
model->getPageObjectProxy()->index(index, 0, m_comboBox.rootModelIndex()))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldPageObjectNode::_currentIndexChanged(int index)
|
bool FieldPageObjectNode::event(QEvent* ev) {
|
||||||
{
|
if (ev->type() == QEvent::User) {
|
||||||
m_button.setEnabled(index != 0);
|
showPopup();
|
||||||
emit currentIndexChanged(index);
|
return true;
|
||||||
|
}
|
||||||
|
return QWidget::event(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectModel::BasePoolObjectNode* FieldPageObjectNode::currentNode() const
|
void FieldPageObjectNode::openCurrent() {
|
||||||
{
|
if (ProjectModel::BasePoolObjectNode* node = currentNode())
|
||||||
int index = m_comboBox.currentIndex();
|
if (!g_MainWindow->isUiDisabled())
|
||||||
if (index == 0)
|
g_MainWindow->openEditor(node);
|
||||||
{
|
|
||||||
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)
|
AddRemoveButtons::AddRemoveButtons(QWidget* parent)
|
||||||
: QWidget(parent), m_addAction(tr("Add Row")), m_addButton(this),
|
: QWidget(parent)
|
||||||
m_removeAction(tr("Remove Row")), m_removeButton(this)
|
, m_addAction(tr("Add Row"))
|
||||||
{
|
, m_addButton(this)
|
||||||
setFixedSize(64, 32);
|
, m_removeAction(tr("Remove Row"))
|
||||||
|
, m_removeButton(this) {
|
||||||
|
setFixedSize(64, 32);
|
||||||
|
|
||||||
m_addAction.setIcon(QIcon(QStringLiteral(":/icons/IconAdd.svg")));
|
m_addAction.setIcon(QIcon(QStringLiteral(":/icons/IconAdd.svg")));
|
||||||
m_addButton.setDefaultAction(&m_addAction);
|
m_addButton.setDefaultAction(&m_addAction);
|
||||||
m_addButton.setFixedSize(32, 32);
|
m_addButton.setFixedSize(32, 32);
|
||||||
m_addButton.move(0, 0);
|
m_addButton.move(0, 0);
|
||||||
|
|
||||||
m_removeAction.setIcon(QIcon(QStringLiteral(":/icons/IconRemove.svg")));
|
m_removeAction.setIcon(QIcon(QStringLiteral(":/icons/IconRemove.svg")));
|
||||||
m_removeButton.setDefaultAction(&m_removeAction);
|
m_removeButton.setDefaultAction(&m_removeAction);
|
||||||
m_removeButton.setFixedSize(32, 32);
|
m_removeButton.setFixedSize(32, 32);
|
||||||
m_removeButton.move(32, 0);
|
m_removeButton.move(32, 0);
|
||||||
m_removeAction.setEnabled(false);
|
m_removeAction.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QIcon ListingDeleteIcon;
|
static QIcon ListingDeleteIcon;
|
||||||
static QIcon ListingDeleteHoveredIcon;
|
static QIcon ListingDeleteHoveredIcon;
|
||||||
|
|
||||||
void ListingDeleteButton::enterEvent(QEvent* event)
|
void ListingDeleteButton::enterEvent(QEvent* event) { setIcon(ListingDeleteHoveredIcon); }
|
||||||
{
|
|
||||||
setIcon(ListingDeleteHoveredIcon);
|
void ListingDeleteButton::leaveEvent(QEvent* event) { setIcon(ListingDeleteIcon); }
|
||||||
|
|
||||||
|
ListingDeleteButton::ListingDeleteButton(QWidget* parent) : QPushButton(parent) {
|
||||||
|
if (ListingDeleteIcon.isNull())
|
||||||
|
ListingDeleteIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg"));
|
||||||
|
if (ListingDeleteHoveredIcon.isNull())
|
||||||
|
ListingDeleteHoveredIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDeleteHovered.svg"));
|
||||||
|
|
||||||
|
setVisible(false);
|
||||||
|
setFixedSize(21, 21);
|
||||||
|
setFlat(true);
|
||||||
|
setToolTip(tr("Delete this SoundMacro"));
|
||||||
|
setIcon(ListingDeleteIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListingDeleteButton::leaveEvent(QEvent* event)
|
bool BaseObjectDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||||
{
|
const QModelIndex& index) {
|
||||||
setIcon(ListingDeleteIcon);
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
}
|
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
|
||||||
|
if (ev->button() == Qt::RightButton) {
|
||||||
|
ProjectModel::INode* node = getNode(model, index);
|
||||||
|
|
||||||
ListingDeleteButton::ListingDeleteButton(QWidget* parent)
|
ContextMenu* menu = new ContextMenu;
|
||||||
: QPushButton(parent)
|
|
||||||
{
|
|
||||||
if (ListingDeleteIcon.isNull())
|
|
||||||
ListingDeleteIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg"));
|
|
||||||
if (ListingDeleteHoveredIcon.isNull())
|
|
||||||
ListingDeleteHoveredIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDeleteHovered.svg"));
|
|
||||||
|
|
||||||
setVisible(false);
|
QAction* openEditorAction = new QAction(tr("Open in Editor"), menu);
|
||||||
setFixedSize(21, 21);
|
openEditorAction->setData(QVariant::fromValue((void*)node));
|
||||||
setFlat(true);
|
openEditorAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
|
||||||
setToolTip(tr("Delete this SoundMacro"));
|
connect(openEditorAction, SIGNAL(triggered()), this, SLOT(doOpenEditor()));
|
||||||
setIcon(ListingDeleteIcon);
|
menu->addAction(openEditorAction);
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseObjectDelegate::editorEvent(QEvent *event, QAbstractItemModel* model,
|
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
|
||||||
const QStyleOptionViewItem &option, const QModelIndex &index)
|
findUsagesAction->setData(QVariant::fromValue((void*)node));
|
||||||
{
|
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
|
||||||
if (event->type() == QEvent::MouseButtonPress)
|
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(doFindUsages()));
|
||||||
{
|
menu->addAction(findUsagesAction);
|
||||||
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
|
|
||||||
if (ev->button() == Qt::RightButton)
|
|
||||||
{
|
|
||||||
ProjectModel::INode* node = getNode(model, index);
|
|
||||||
|
|
||||||
ContextMenu* menu = new ContextMenu;
|
menu->popup(ev->globalPos());
|
||||||
|
|
||||||
QAction* openEditorAction = new QAction(tr("Open in Editor"), menu);
|
|
||||||
openEditorAction->setData(QVariant::fromValue((void*)node));
|
|
||||||
openEditorAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
|
|
||||||
connect(openEditorAction, SIGNAL(triggered()), this, SLOT(doOpenEditor()));
|
|
||||||
menu->addAction(openEditorAction);
|
|
||||||
|
|
||||||
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
|
|
||||||
findUsagesAction->setData(QVariant::fromValue((void*)node));
|
|
||||||
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
|
|
||||||
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(doFindUsages()));
|
|
||||||
menu->addAction(findUsagesAction);
|
|
||||||
|
|
||||||
menu->popup(ev->globalPos());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseObjectDelegate::doOpenEditor()
|
void BaseObjectDelegate::doOpenEditor() {
|
||||||
{
|
QAction* act = static_cast<QAction*>(sender());
|
||||||
QAction* act = static_cast<QAction*>(sender());
|
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
|
||||||
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
|
g_MainWindow->openEditor(node);
|
||||||
g_MainWindow->openEditor(node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseObjectDelegate::doFindUsages()
|
void BaseObjectDelegate::doFindUsages() {
|
||||||
{
|
QAction* act = static_cast<QAction*>(sender());
|
||||||
QAction* act = static_cast<QAction*>(sender());
|
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
|
||||||
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
|
g_MainWindow->findUsages(node);
|
||||||
g_MainWindow->findUsages(node);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,238 +15,227 @@
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include "ProjectModel.hpp"
|
#include "ProjectModel.hpp"
|
||||||
|
|
||||||
class EditorWidget : public QWidget
|
class EditorWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
|
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
|
||||||
virtual bool valid() const { return true; }
|
virtual bool valid() const { return true; }
|
||||||
virtual void unloadData() {}
|
virtual void unloadData() {}
|
||||||
virtual ProjectModel::INode* currentNode() const { return nullptr; }
|
virtual ProjectModel::INode* currentNode() const { return nullptr; }
|
||||||
virtual void setEditorEnabled(bool en) { setEnabled(en); }
|
virtual void setEditorEnabled(bool en) { setEnabled(en); }
|
||||||
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
|
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
|
||||||
public slots:
|
public slots:
|
||||||
virtual void itemCutAction() {}
|
virtual void itemCutAction() {}
|
||||||
virtual void itemCopyAction() {}
|
virtual void itemCopyAction() {}
|
||||||
virtual void itemPasteAction() {}
|
virtual void itemPasteAction() {}
|
||||||
virtual void itemDeleteAction() {}
|
virtual void itemDeleteAction() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditorUndoCommand : public QUndoCommand
|
class EditorUndoCommand : public QUndoCommand {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
amuse::ObjToken<ProjectModel::INode> m_node;
|
amuse::ObjToken<ProjectModel::INode> m_node;
|
||||||
enum class Id
|
enum class Id {
|
||||||
{
|
SMChangeVal,
|
||||||
SMChangeVal,
|
SampLoop,
|
||||||
SampLoop,
|
SampPitch,
|
||||||
SampPitch,
|
ADSRAttack,
|
||||||
ADSRAttack,
|
ADSRDecay,
|
||||||
ADSRDecay,
|
ADSRSustain,
|
||||||
ADSRSustain,
|
ADSRAttackAndDecay,
|
||||||
ADSRAttackAndDecay,
|
ADSRDecayAndSustain,
|
||||||
ADSRDecayAndSustain,
|
ADSRRelease,
|
||||||
ADSRRelease,
|
ADSRDLS,
|
||||||
ADSRDLS,
|
ADSRVelToAttack,
|
||||||
ADSRVelToAttack,
|
ADSRKeyToDecay,
|
||||||
ADSRKeyToDecay,
|
CurveEdit
|
||||||
CurveEdit
|
};
|
||||||
};
|
|
||||||
public:
|
public:
|
||||||
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node,
|
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node, const QString& text, QUndoCommand* parent = nullptr)
|
||||||
const QString& text, QUndoCommand* parent = nullptr)
|
: QUndoCommand(text, parent), m_node(node) {}
|
||||||
: QUndoCommand(text, parent), m_node(node) {}
|
void undo();
|
||||||
void undo();
|
void redo();
|
||||||
void redo();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldSpinBox : public QSpinBox
|
class FieldSpinBox : public QSpinBox {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR)
|
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR) : QSpinBox(parent) {}
|
||||||
: QSpinBox(parent) {}
|
|
||||||
|
|
||||||
/* Don't scroll */
|
/* Don't scroll */
|
||||||
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldSlider : public QWidget
|
class FieldSlider : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QSlider m_slider;
|
||||||
QSlider m_slider;
|
QLabel m_value;
|
||||||
QLabel m_value;
|
|
||||||
public:
|
public:
|
||||||
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
|
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
|
||||||
|
|
||||||
/* Don't scroll */
|
/* Don't scroll */
|
||||||
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
||||||
|
|
||||||
int value() const { return m_slider.value(); }
|
int value() const { return m_slider.value(); }
|
||||||
void setValue(int value) { m_slider.setValue(value); doValueChanged(value); }
|
void setValue(int value) {
|
||||||
void setRange(int min, int max) { m_slider.setRange(min, max); }
|
m_slider.setValue(value);
|
||||||
|
doValueChanged(value);
|
||||||
|
}
|
||||||
|
void setRange(int min, int max) { m_slider.setRange(min, max); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void doValueChanged(int value);
|
void doValueChanged(int value);
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(int value);
|
void valueChanged(int value);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldDoubleSlider : public QWidget
|
class FieldDoubleSlider : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QSlider m_slider;
|
||||||
QSlider m_slider;
|
QLabel m_value;
|
||||||
QLabel m_value;
|
double m_min = 0.0;
|
||||||
double m_min = 0.0;
|
double m_max = 1.0;
|
||||||
double m_max = 1.0;
|
|
||||||
public:
|
public:
|
||||||
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
|
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
|
||||||
|
|
||||||
/* Don't scroll */
|
/* Don't scroll */
|
||||||
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
||||||
|
|
||||||
double value() const;
|
double value() const;
|
||||||
void setValue(double value);
|
void setValue(double value);
|
||||||
void setRange(double min, double max);
|
void setRange(double min, double max);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void doValueChanged(int value);
|
void doValueChanged(int value);
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(double value);
|
void valueChanged(double value);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldComboBox : public QComboBox
|
class FieldComboBox : public QComboBox {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit FieldComboBox(QWidget* parent = Q_NULLPTR)
|
explicit FieldComboBox(QWidget* parent = Q_NULLPTR) : QComboBox(parent) {}
|
||||||
: QComboBox(parent) {}
|
|
||||||
|
|
||||||
/* Don't scroll */
|
/* Don't scroll */
|
||||||
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldProjectNode : public QWidget
|
class FieldProjectNode : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
ProjectModel::CollectionNode* m_collection;
|
||||||
ProjectModel::CollectionNode* m_collection;
|
FieldComboBox m_comboBox;
|
||||||
FieldComboBox m_comboBox;
|
QPushButton m_button;
|
||||||
QPushButton m_button;
|
|
||||||
public:
|
public:
|
||||||
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
||||||
void setCollection(ProjectModel::CollectionNode* collection);
|
void setCollection(ProjectModel::CollectionNode* collection);
|
||||||
ProjectModel::CollectionNode* collection() const { return m_collection; }
|
ProjectModel::CollectionNode* collection() const { return m_collection; }
|
||||||
int currentIndex() const { return m_comboBox.currentIndex(); }
|
int currentIndex() const { return m_comboBox.currentIndex(); }
|
||||||
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
|
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
|
||||||
void showPopup() { m_comboBox.showPopup(); }
|
void showPopup() { m_comboBox.showPopup(); }
|
||||||
ProjectModel::BasePoolObjectNode* currentNode() const;
|
ProjectModel::BasePoolObjectNode* currentNode() const;
|
||||||
bool event(QEvent* ev);
|
bool event(QEvent* ev);
|
||||||
private slots:
|
private slots:
|
||||||
void _currentIndexChanged(int);
|
void _currentIndexChanged(int);
|
||||||
public slots:
|
public slots:
|
||||||
void openCurrent();
|
void openCurrent();
|
||||||
signals:
|
signals:
|
||||||
void currentIndexChanged(int);
|
void currentIndexChanged(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldPageObjectNode : public QWidget
|
class FieldPageObjectNode : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
ProjectModel::GroupNode* m_group;
|
||||||
ProjectModel::GroupNode* m_group;
|
FieldComboBox m_comboBox;
|
||||||
FieldComboBox m_comboBox;
|
QPushButton m_button;
|
||||||
QPushButton m_button;
|
|
||||||
public:
|
public:
|
||||||
explicit FieldPageObjectNode(ProjectModel::GroupNode* group = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
explicit FieldPageObjectNode(ProjectModel::GroupNode* group = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
||||||
void setGroup(ProjectModel::GroupNode* group);
|
void setGroup(ProjectModel::GroupNode* group);
|
||||||
ProjectModel::GroupNode* group() const { return m_group; }
|
ProjectModel::GroupNode* group() const { return m_group; }
|
||||||
int currentIndex() const { return m_comboBox.currentIndex(); }
|
int currentIndex() const { return m_comboBox.currentIndex(); }
|
||||||
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
|
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
|
||||||
QModelIndex rootModelIndex() const { return m_comboBox.rootModelIndex(); }
|
QModelIndex rootModelIndex() const { return m_comboBox.rootModelIndex(); }
|
||||||
void showPopup() { m_comboBox.showPopup(); }
|
void showPopup() { m_comboBox.showPopup(); }
|
||||||
ProjectModel::BasePoolObjectNode* currentNode() const;
|
ProjectModel::BasePoolObjectNode* currentNode() const;
|
||||||
bool event(QEvent* ev);
|
bool event(QEvent* ev);
|
||||||
private slots:
|
private slots:
|
||||||
void _currentIndexChanged(int);
|
void _currentIndexChanged(int);
|
||||||
public slots:
|
public slots:
|
||||||
void openCurrent();
|
void openCurrent();
|
||||||
signals:
|
signals:
|
||||||
void currentIndexChanged(int);
|
void currentIndexChanged(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class EditorFieldNode : public T
|
class EditorFieldNode : public T {
|
||||||
{
|
bool m_deferPopupOpen = true;
|
||||||
bool m_deferPopupOpen = true;
|
|
||||||
public:
|
public:
|
||||||
using T::T;
|
using T::T;
|
||||||
bool shouldPopupOpen()
|
bool shouldPopupOpen() {
|
||||||
{
|
bool ret = m_deferPopupOpen;
|
||||||
bool ret = m_deferPopupOpen;
|
m_deferPopupOpen = false;
|
||||||
m_deferPopupOpen = false;
|
return ret;
|
||||||
return ret;
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using EditorFieldProjectNode = EditorFieldNode<FieldProjectNode>;
|
using EditorFieldProjectNode = EditorFieldNode<FieldProjectNode>;
|
||||||
using EditorFieldPageObjectNode = EditorFieldNode<FieldPageObjectNode>;
|
using EditorFieldPageObjectNode = EditorFieldNode<FieldPageObjectNode>;
|
||||||
|
|
||||||
template <int MIN, int MAX>
|
template <int MIN, int MAX>
|
||||||
class RangedValueFactory : public QItemEditorFactory
|
class RangedValueFactory : public QItemEditorFactory {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
QWidget* createEditor(int userType, QWidget *parent) const
|
QWidget* createEditor(int userType, QWidget* parent) const {
|
||||||
{
|
QSpinBox* sb = new QSpinBox(parent);
|
||||||
QSpinBox* sb = new QSpinBox(parent);
|
sb->setFrame(false);
|
||||||
sb->setFrame(false);
|
sb->setMinimum(MIN);
|
||||||
sb->setMinimum(MIN);
|
sb->setMaximum(MAX);
|
||||||
sb->setMaximum(MAX);
|
return sb;
|
||||||
return sb;
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddRemoveButtons : public QWidget
|
class AddRemoveButtons : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QAction m_addAction;
|
||||||
QAction m_addAction;
|
QToolButton m_addButton;
|
||||||
QToolButton m_addButton;
|
QAction m_removeAction;
|
||||||
QAction m_removeAction;
|
QToolButton m_removeButton;
|
||||||
QToolButton m_removeButton;
|
|
||||||
public:
|
public:
|
||||||
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
|
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
|
||||||
QAction* addAction() { return &m_addAction; }
|
QAction* addAction() { return &m_addAction; }
|
||||||
QAction* removeAction() { return &m_removeAction; }
|
QAction* removeAction() { return &m_removeAction; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ListingDeleteButton : public QPushButton
|
class ListingDeleteButton : public QPushButton {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
|
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
|
||||||
void enterEvent(QEvent* event);
|
void enterEvent(QEvent* event);
|
||||||
void leaveEvent(QEvent* event);
|
void leaveEvent(QEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContextMenu : public QMenu
|
class ContextMenu : public QMenu {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
void hideEvent(QHideEvent* ev)
|
void hideEvent(QHideEvent* ev) {
|
||||||
{
|
QMenu::hideEvent(ev);
|
||||||
QMenu::hideEvent(ev);
|
deleteLater();
|
||||||
deleteLater();
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BaseObjectDelegate : public QStyledItemDelegate
|
class BaseObjectDelegate : public QStyledItemDelegate {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
protected:
|
protected:
|
||||||
virtual ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const = 0;
|
virtual ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const = 0;
|
||||||
public:
|
|
||||||
explicit BaseObjectDelegate(QObject* parent = Q_NULLPTR) : QStyledItemDelegate(parent) {}
|
|
||||||
bool editorEvent(QEvent *event, QAbstractItemModel *model,
|
|
||||||
const QStyleOptionViewItem &option, const QModelIndex &index);
|
|
||||||
private slots:
|
|
||||||
void doOpenEditor();
|
|
||||||
void doFindUsages();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BaseObjectDelegate(QObject* parent = Q_NULLPTR) : QStyledItemDelegate(parent) {}
|
||||||
|
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||||
|
const QModelIndex& index);
|
||||||
|
private slots:
|
||||||
|
void doOpenEditor();
|
||||||
|
void doFindUsages();
|
||||||
|
};
|
||||||
|
|
|
@ -6,266 +6,183 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
|
||||||
const QString NaturalKeyNames[] =
|
const QString NaturalKeyNames[] = {QStringLiteral("C"), QStringLiteral("D"), QStringLiteral("E"), QStringLiteral("F"),
|
||||||
{
|
QStringLiteral("G"), QStringLiteral("A"), QStringLiteral("B")};
|
||||||
QStringLiteral("C"),
|
|
||||||
QStringLiteral("D"),
|
|
||||||
QStringLiteral("E"),
|
|
||||||
QStringLiteral("F"),
|
|
||||||
QStringLiteral("G"),
|
|
||||||
QStringLiteral("A"),
|
|
||||||
QStringLiteral("B")
|
|
||||||
};
|
|
||||||
|
|
||||||
const QString SharpKeyNames[] =
|
const QString SharpKeyNames[] = {QStringLiteral("Cs"), QStringLiteral("Ds"), QStringLiteral("Fs"), QStringLiteral("Gs"),
|
||||||
{
|
QStringLiteral("As")};
|
||||||
QStringLiteral("Cs"),
|
|
||||||
QStringLiteral("Ds"),
|
|
||||||
QStringLiteral("Fs"),
|
|
||||||
QStringLiteral("Gs"),
|
|
||||||
QStringLiteral("As")
|
|
||||||
};
|
|
||||||
|
|
||||||
const QString KeyStrings[] =
|
const QString KeyStrings[] = {QStringLiteral("C"), QStringLiteral("C#"), QStringLiteral("D"), QStringLiteral("D#"),
|
||||||
{
|
QStringLiteral("E"), QStringLiteral("F"), QStringLiteral("F#"), QStringLiteral("G"),
|
||||||
QStringLiteral("C"),
|
QStringLiteral("G#"), QStringLiteral("A"), QStringLiteral("A#"), QStringLiteral("B")};
|
||||||
QStringLiteral("C#"),
|
|
||||||
QStringLiteral("D"),
|
|
||||||
QStringLiteral("D#"),
|
|
||||||
QStringLiteral("E"),
|
|
||||||
QStringLiteral("F"),
|
|
||||||
QStringLiteral("F#"),
|
|
||||||
QStringLiteral("G"),
|
|
||||||
QStringLiteral("G#"),
|
|
||||||
QStringLiteral("A"),
|
|
||||||
QStringLiteral("A#"),
|
|
||||||
QStringLiteral("B")
|
|
||||||
};
|
|
||||||
|
|
||||||
const int NaturalKeyNumbers[] =
|
const int NaturalKeyNumbers[] = {0, 2, 4, 5, 7, 9, 11};
|
||||||
{
|
|
||||||
0, 2, 4, 5, 7, 9, 11
|
|
||||||
};
|
|
||||||
|
|
||||||
const int SharpKeyNumbers[] =
|
const int SharpKeyNumbers[] = {1, 3, 6, 8, 10};
|
||||||
{
|
|
||||||
1, 3, 6, 8, 10
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyboardOctave::KeyboardOctave(int octave, const QString& svgPath, QWidget* parent)
|
KeyboardOctave::KeyboardOctave(int octave, const QString& svgPath, QWidget* parent)
|
||||||
: QSvgWidget(svgPath, parent), m_octave(octave)
|
: QSvgWidget(svgPath, parent), m_octave(octave) {
|
||||||
{
|
for (int i = 0; i < 7; ++i)
|
||||||
for (int i = 0; i < 7; ++i)
|
if (renderer()->elementExists(NaturalKeyNames[i]))
|
||||||
if (renderer()->elementExists(NaturalKeyNames[i]))
|
m_natural[i] =
|
||||||
m_natural[i] = renderer()->matrixForElement(NaturalKeyNames[i]).
|
renderer()->matrixForElement(NaturalKeyNames[i]).mapRect(renderer()->boundsOnElement(NaturalKeyNames[i]));
|
||||||
mapRect(renderer()->boundsOnElement(NaturalKeyNames[i]));
|
|
||||||
|
|
||||||
for (int i = 0; i < 5; ++i)
|
for (int i = 0; i < 5; ++i)
|
||||||
if (renderer()->elementExists(SharpKeyNames[i]))
|
if (renderer()->elementExists(SharpKeyNames[i]))
|
||||||
m_sharp[i] = renderer()->matrixForElement(SharpKeyNames[i]).
|
m_sharp[i] =
|
||||||
mapRect(renderer()->boundsOnElement(SharpKeyNames[i]));
|
renderer()->matrixForElement(SharpKeyNames[i]).mapRect(renderer()->boundsOnElement(SharpKeyNames[i]));
|
||||||
|
|
||||||
/* The parent keyboard manages all mouse events */
|
/* The parent keyboard manages all mouse events */
|
||||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeyboardOctave::getKey(const QPoint& localPos) const
|
int KeyboardOctave::getKey(const QPoint& localPos) const {
|
||||||
{
|
QPointF localPoint = m_widgetToSvg.map(localPos);
|
||||||
QPointF localPoint = m_widgetToSvg.map(localPos);
|
for (int i = 0; i < 5; ++i)
|
||||||
for (int i = 0; i < 5; ++i)
|
if (m_sharp[i].contains(localPoint))
|
||||||
if (m_sharp[i].contains(localPoint))
|
return SharpKeyNumbers[i];
|
||||||
return SharpKeyNumbers[i];
|
for (int i = 0; i < 7; ++i)
|
||||||
for (int i = 0; i < 7; ++i)
|
if (m_natural[i].contains(localPoint))
|
||||||
if (m_natural[i].contains(localPoint))
|
return NaturalKeyNumbers[i];
|
||||||
return NaturalKeyNumbers[i];
|
return -1;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardOctave::resizeEvent(QResizeEvent *event)
|
void KeyboardOctave::resizeEvent(QResizeEvent* event) { m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF()); }
|
||||||
{
|
|
||||||
m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF());
|
KeyboardWidget::KeyboardWidget(QWidget* parent) : QWidget(parent) {
|
||||||
|
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||||
|
layout->setContentsMargins(QMargins());
|
||||||
|
layout->setSpacing(0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i) {
|
||||||
|
m_widgets[i] = new KeyboardOctave(i, QStringLiteral(":/bg/keyboard.svg"), this);
|
||||||
|
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
|
||||||
|
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
||||||
|
layout->addWidget(m_widgets[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
|
||||||
|
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
|
||||||
|
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
||||||
|
layout->addWidget(m_widgets[10]);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardWidget::KeyboardWidget(QWidget* parent)
|
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const {
|
||||||
: QWidget(parent)
|
for (KeyboardOctave* oct : m_widgets) {
|
||||||
{
|
QPoint localPos = oct->mapFromParent(event->pos());
|
||||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
if (oct->rect().contains(localPos))
|
||||||
layout->setContentsMargins(QMargins());
|
return {oct->getOctave(), oct->getKey(localPos)};
|
||||||
layout->setSpacing(0);
|
}
|
||||||
|
return {-1, -1};
|
||||||
for (int i = 0; i < 10; ++i)
|
|
||||||
{
|
|
||||||
m_widgets[i] = new KeyboardOctave(i, QStringLiteral(":/bg/keyboard.svg"), this);
|
|
||||||
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
|
|
||||||
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
|
||||||
layout->addWidget(m_widgets[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
|
|
||||||
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
|
|
||||||
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
|
|
||||||
layout->addWidget(m_widgets[10]);
|
|
||||||
|
|
||||||
setLayout(layout);
|
|
||||||
setMouseTracking(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const
|
void KeyboardWidget::_startKey(int octave, int key) { emit notePressed(octave * 12 + key); }
|
||||||
{
|
|
||||||
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)
|
void KeyboardWidget::_stopKey() { emit noteReleased(); }
|
||||||
{
|
|
||||||
emit notePressed(octave * 12 + key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeyboardWidget::_stopKey()
|
void KeyboardWidget::_moveOnKey(int octave, int key) {
|
||||||
{
|
if (m_lastOctave != octave || m_lastKey != key) {
|
||||||
emit noteReleased();
|
m_lastOctave = octave;
|
||||||
}
|
m_lastKey = key;
|
||||||
|
|
||||||
void KeyboardWidget::_moveOnKey(int octave, int key)
|
|
||||||
{
|
|
||||||
if (m_lastOctave != octave || m_lastKey != key)
|
|
||||||
{
|
|
||||||
m_lastOctave = octave;
|
|
||||||
m_lastKey = key;
|
|
||||||
if (m_statusFocus)
|
|
||||||
m_statusFocus->setMessage(QStringLiteral("%1%2 (%3)").
|
|
||||||
arg(KeyStrings[key]).arg(octave - 1).arg(octave * 12 + key));
|
|
||||||
if (m_holding)
|
|
||||||
_startKey(octave, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeyboardWidget::_pressOnKey(int octave, int key)
|
|
||||||
{
|
|
||||||
_moveOnKey(octave, key);
|
|
||||||
m_holding = true;
|
|
||||||
_startKey(octave, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeyboardWidget::mouseMoveEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
std::pair<int, int> ok = _getOctaveAndKey(event);
|
|
||||||
if (ok.first != -1 && ok.second != -1)
|
|
||||||
_moveOnKey(ok.first, ok.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeyboardWidget::mousePressEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
std::pair<int, int> ok = _getOctaveAndKey(event);
|
|
||||||
if (ok.first != -1 && ok.second != -1)
|
|
||||||
_pressOnKey(ok.first, ok.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
_stopKey();
|
|
||||||
m_holding = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeyboardWidget::enterEvent(QEvent* event)
|
|
||||||
{
|
|
||||||
if (m_statusFocus)
|
if (m_statusFocus)
|
||||||
m_statusFocus->enter();
|
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::leaveEvent(QEvent* event)
|
void KeyboardWidget::_pressOnKey(int octave, int key) {
|
||||||
{
|
_moveOnKey(octave, key);
|
||||||
if (m_statusFocus)
|
m_holding = true;
|
||||||
m_statusFocus->exit();
|
_startKey(octave, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardWidget::wheelEvent(QWheelEvent* event)
|
void KeyboardWidget::mouseMoveEvent(QMouseEvent* event) {
|
||||||
{
|
std::pair<int, int> ok = _getOctaveAndKey(event);
|
||||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
|
if (ok.first != -1 && ok.second != -1)
|
||||||
{
|
_moveOnKey(ok.first, ok.second);
|
||||||
/* Send wheel event directly to the scroll bar */
|
|
||||||
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardWidget::showEvent(QShowEvent* event)
|
void KeyboardWidget::mousePressEvent(QMouseEvent* event) {
|
||||||
{
|
std::pair<int, int> ok = _getOctaveAndKey(event);
|
||||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
|
if (ok.first != -1 && ok.second != -1)
|
||||||
{
|
_pressOnKey(ok.first, ok.second);
|
||||||
/* Scroll to C1 */
|
|
||||||
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardSlider::KeyboardSlider(QWidget* parent)
|
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
: QSlider(parent)
|
_stopKey();
|
||||||
{}
|
m_holding = false;
|
||||||
|
|
||||||
void KeyboardSlider::enterEvent(QEvent* event)
|
|
||||||
{
|
|
||||||
if (m_statusFocus)
|
|
||||||
m_statusFocus->enter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardSlider::leaveEvent(QEvent* event)
|
void KeyboardWidget::enterEvent(QEvent* event) {
|
||||||
{
|
if (m_statusFocus)
|
||||||
if (m_statusFocus)
|
m_statusFocus->enter();
|
||||||
m_statusFocus->exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus)
|
void KeyboardWidget::leaveEvent(QEvent* event) {
|
||||||
{
|
if (m_statusFocus)
|
||||||
m_statusFocus = statusFocus;
|
m_statusFocus->exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardWidget::wheelEvent(QWheelEvent* event) {
|
||||||
|
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||||
|
/* Send wheel event directly to the scroll bar */
|
||||||
|
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardWidget::showEvent(QShowEvent* event) {
|
||||||
|
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||||
|
/* Scroll to C1 */
|
||||||
|
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardSlider::KeyboardSlider(QWidget* parent) : QSlider(parent) {}
|
||||||
|
|
||||||
|
void KeyboardSlider::enterEvent(QEvent* event) {
|
||||||
|
if (m_statusFocus)
|
||||||
|
m_statusFocus->enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardSlider::leaveEvent(QEvent* event) {
|
||||||
|
if (m_statusFocus)
|
||||||
|
m_statusFocus->exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus) {
|
||||||
|
m_statusFocus = statusFocus;
|
||||||
|
QString str = stringOfValue(value());
|
||||||
|
m_statusFocus->setMessage(str);
|
||||||
|
setToolTip(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardSlider::sliderChange(SliderChange change) {
|
||||||
|
QSlider::sliderChange(change);
|
||||||
|
if (m_statusFocus && change == QAbstractSlider::SliderValueChange) {
|
||||||
QString str = stringOfValue(value());
|
QString str = stringOfValue(value());
|
||||||
m_statusFocus->setMessage(str);
|
m_statusFocus->setMessage(str);
|
||||||
setToolTip(str);
|
setToolTip(str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardSlider::sliderChange(SliderChange change)
|
VelocitySlider::VelocitySlider(QWidget* parent) : KeyboardSlider(parent) {}
|
||||||
{
|
|
||||||
QSlider::sliderChange(change);
|
QString VelocitySlider::stringOfValue(int value) const { return tr("Velocity: %1").arg(value); }
|
||||||
if (m_statusFocus && change == QAbstractSlider::SliderValueChange)
|
|
||||||
{
|
ModulationSlider::ModulationSlider(QWidget* parent) : KeyboardSlider(parent) {}
|
||||||
QString str = stringOfValue(value());
|
|
||||||
m_statusFocus->setMessage(str);
|
QString ModulationSlider::stringOfValue(int value) const { return tr("Modulation: %1").arg(value); }
|
||||||
setToolTip(str);
|
|
||||||
}
|
PitchSlider::PitchSlider(QWidget* parent) : KeyboardSlider(parent) {}
|
||||||
}
|
|
||||||
|
QString PitchSlider::stringOfValue(int value) const { return tr("Pitch: %1").arg(value / 2048.0, 0, 'g', 2); }
|
||||||
VelocitySlider::VelocitySlider(QWidget* parent)
|
|
||||||
: KeyboardSlider(parent)
|
void PitchSlider::mouseReleaseEvent(QMouseEvent* ev) {
|
||||||
{}
|
KeyboardSlider::mouseReleaseEvent(ev);
|
||||||
|
setValue(0);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,90 +15,87 @@ extern const int SharpKeyNumbers[5];
|
||||||
|
|
||||||
class KeyboardWidget;
|
class KeyboardWidget;
|
||||||
|
|
||||||
class KeyboardOctave : public QSvgWidget
|
class KeyboardOctave : public QSvgWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
int m_octave;
|
||||||
int m_octave;
|
QRectF m_natural[7];
|
||||||
QRectF m_natural[7];
|
QRectF m_sharp[5];
|
||||||
QRectF m_sharp[5];
|
QTransform m_widgetToSvg;
|
||||||
QTransform m_widgetToSvg;
|
|
||||||
public:
|
public:
|
||||||
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
|
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
|
||||||
int getOctave() const { return m_octave; }
|
int getOctave() const { return m_octave; }
|
||||||
int getKey(const QPoint& localPos) const;
|
int getKey(const QPoint& localPos) const;
|
||||||
void resizeEvent(QResizeEvent *event);
|
void resizeEvent(QResizeEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyboardWidget : public QWidget
|
class KeyboardWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
KeyboardOctave* m_widgets[11];
|
||||||
KeyboardOctave* m_widgets[11];
|
StatusBarFocus* m_statusFocus = nullptr;
|
||||||
StatusBarFocus* m_statusFocus = nullptr;
|
int m_lastOctave = -1;
|
||||||
int m_lastOctave = -1;
|
int m_lastKey = -1;
|
||||||
int m_lastKey = -1;
|
bool m_holding = false;
|
||||||
bool m_holding = false;
|
|
||||||
|
|
||||||
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
|
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
|
||||||
void _startKey(int octave, int key);
|
void _startKey(int octave, int key);
|
||||||
void _stopKey();
|
void _stopKey();
|
||||||
void _moveOnKey(int octave, int key);
|
void _moveOnKey(int octave, int key);
|
||||||
void _pressOnKey(int octave, int key);
|
void _pressOnKey(int octave, int key);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
|
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
|
||||||
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
|
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
|
||||||
|
|
||||||
void mouseMoveEvent(QMouseEvent* event);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
void enterEvent(QEvent* event);
|
void enterEvent(QEvent* event);
|
||||||
void leaveEvent(QEvent* event);
|
void leaveEvent(QEvent* event);
|
||||||
void wheelEvent(QWheelEvent *event);
|
void wheelEvent(QWheelEvent* event);
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent* event);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void notePressed(int key);
|
void notePressed(int key);
|
||||||
void noteReleased();
|
void noteReleased();
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyboardSlider : public QSlider
|
class KeyboardSlider : public QSlider {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
protected:
|
protected:
|
||||||
StatusBarFocus* m_statusFocus = nullptr;
|
StatusBarFocus* m_statusFocus = nullptr;
|
||||||
virtual QString stringOfValue(int value) const = 0;
|
virtual QString stringOfValue(int value) const = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
|
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
|
||||||
void enterEvent(QEvent* event);
|
void enterEvent(QEvent* event);
|
||||||
void leaveEvent(QEvent* event);
|
void leaveEvent(QEvent* event);
|
||||||
void setStatusFocus(StatusBarFocus* statusFocus);
|
void setStatusFocus(StatusBarFocus* statusFocus);
|
||||||
void sliderChange(SliderChange change);
|
void sliderChange(SliderChange change);
|
||||||
};
|
};
|
||||||
|
|
||||||
class VelocitySlider : public KeyboardSlider
|
class VelocitySlider : public KeyboardSlider {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QString stringOfValue(int value) const;
|
||||||
QString stringOfValue(int value) const;
|
|
||||||
public:
|
|
||||||
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModulationSlider : public KeyboardSlider
|
class ModulationSlider : public KeyboardSlider {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QString stringOfValue(int value) const;
|
||||||
QString stringOfValue(int value) const;
|
|
||||||
public:
|
public:
|
||||||
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
|
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PitchSlider : public KeyboardSlider
|
class PitchSlider : public KeyboardSlider {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QString stringOfValue(int value) const;
|
||||||
QString stringOfValue(int value) const;
|
|
||||||
public:
|
|
||||||
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
|
|
||||||
void mouseReleaseEvent(QMouseEvent *ev);
|
|
||||||
void wheelEvent(QWheelEvent* ev) { ev->ignore(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
|
||||||
|
void mouseReleaseEvent(QMouseEvent* ev);
|
||||||
|
void wheelEvent(QWheelEvent* ev) { ev->ignore(); }
|
||||||
|
};
|
||||||
|
|
|
@ -5,596 +5,461 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
|
||||||
static const int HueTable[] =
|
static const int HueTable[] = {0, 30, 60, 80, 120, 170, 200, 240, 280, 320};
|
||||||
{
|
|
||||||
0, 30, 60, 80, 120, 170, 200, 240, 280, 320
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int SaturationTable[] =
|
static const int SaturationTable[] = {255, 255, 255, 255, 255, 127, 127, 127, 127, 127, 63, 63, 63};
|
||||||
{
|
|
||||||
255, 255, 255, 255, 255, 127, 127, 127, 127, 127, 63, 63, 63
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int ValueTable[] =
|
static const int ValueTable[] = {240, 200, 160, 120, 80, 240, 200, 160, 120, 80, 240, 200, 160};
|
||||||
{
|
|
||||||
240, 200, 160, 120, 80, 240, 200, 160, 120, 80, 240, 200, 160
|
|
||||||
};
|
|
||||||
|
|
||||||
static const QPoint PointTable[] =
|
static const QPoint PointTable[] = {{21, 180}, {41, 104}, {61, 180}, {86, 104}, {101, 180}, {141, 180},
|
||||||
{
|
{156, 104}, {181, 180}, {201, 104}, {221, 180}, {246, 104}, {261, 180}};
|
||||||
{21, 180},
|
|
||||||
{41, 104},
|
|
||||||
{61, 180},
|
|
||||||
{86, 104},
|
|
||||||
{101, 180},
|
|
||||||
{141, 180},
|
|
||||||
{156, 104},
|
|
||||||
{181, 180},
|
|
||||||
{201, 104},
|
|
||||||
{221, 180},
|
|
||||||
{246, 104},
|
|
||||||
{261, 180}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int RadiusTable[] =
|
static const int RadiusTable[] = {14, 10, 14, 10, 14, 14, 10, 14, 10, 14, 10, 14};
|
||||||
{
|
|
||||||
14,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
14,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
10,
|
|
||||||
14,
|
|
||||||
10,
|
|
||||||
14
|
|
||||||
};
|
|
||||||
|
|
||||||
static const QColor PenColorTable[] =
|
static const QColor PenColorTable[] = {Qt::black, Qt::white, Qt::black, Qt::white, Qt::black, Qt::black,
|
||||||
{
|
Qt::white, Qt::black, Qt::white, Qt::black, Qt::white, Qt::black};
|
||||||
Qt::black,
|
|
||||||
Qt::white,
|
|
||||||
Qt::black,
|
|
||||||
Qt::white,
|
|
||||||
Qt::black,
|
|
||||||
Qt::black,
|
|
||||||
Qt::white,
|
|
||||||
Qt::black,
|
|
||||||
Qt::white,
|
|
||||||
Qt::black,
|
|
||||||
Qt::white,
|
|
||||||
Qt::black
|
|
||||||
};
|
|
||||||
|
|
||||||
static const QColor NeutralColorTable[] =
|
static const QColor NeutralColorTable[] = {Qt::white, Qt::black, Qt::white, Qt::black, Qt::white, Qt::white,
|
||||||
{
|
Qt::black, Qt::white, Qt::black, Qt::white, Qt::black, Qt::white};
|
||||||
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)
|
PaintButton::PaintButton(QWidget* parent) : QPushButton(parent) {
|
||||||
: QPushButton(parent)
|
setIcon(QIcon(QStringLiteral(":/icons/IconPaintbrush.svg")));
|
||||||
{
|
setIconSize(QSize(26, 26));
|
||||||
setIcon(QIcon(QStringLiteral(":/icons/IconPaintbrush.svg")));
|
setFixedSize(46, 46);
|
||||||
setIconSize(QSize(26, 26));
|
setToolTip(tr("Activate brush to apply values to keys"));
|
||||||
setFixedSize(46, 46);
|
|
||||||
setToolTip(tr("Activate brush to apply values to keys"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeymapEditor* KeymapView::getEditor() const
|
KeymapEditor* KeymapView::getEditor() const {
|
||||||
{
|
return qobject_cast<KeymapEditor*>(parentWidget()->parentWidget()->parentWidget());
|
||||||
return qobject_cast<KeymapEditor*>(parentWidget()->parentWidget()->parentWidget());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeymapView::loadData(ProjectModel::KeymapNode* node)
|
void KeymapView::loadData(ProjectModel::KeymapNode* node) { m_node = node; }
|
||||||
{
|
|
||||||
m_node = node;
|
void KeymapView::unloadData() {
|
||||||
|
m_node.reset();
|
||||||
|
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeymapView::unloadData()
|
ProjectModel::INode* KeymapView::currentNode() const { return m_node.get(); }
|
||||||
{
|
|
||||||
m_node.reset();
|
void KeymapView::drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const QColor* keyPalette, int o,
|
||||||
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
|
int k) const {
|
||||||
|
int keyIdx = o * 12 + k;
|
||||||
|
int keyPalIdx = m_keyPalettes[keyIdx];
|
||||||
|
painter.setPen(QPen(PenColorTable[k], penWidth));
|
||||||
|
painter.setBrush(keyPalIdx < 0 ? NeutralColorTable[k] : keyPalette[keyPalIdx]);
|
||||||
|
painter.drawEllipse(PointTable[k] + octaveRect.topLeft(), RadiusTable[k], RadiusTable[k]);
|
||||||
|
painter.setTransform(
|
||||||
|
QTransform()
|
||||||
|
.translate(PointTable[k].x() + octaveRect.left() - 13, PointTable[k].y() + octaveRect.top() - 20)
|
||||||
|
.rotate(-90.0));
|
||||||
|
painter.drawStaticText(QPointF{}, m_keyTexts[keyIdx]);
|
||||||
|
painter.setTransform(QTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectModel::INode* KeymapView::currentNode() const
|
void KeymapView::paintEvent(QPaintEvent* ev) {
|
||||||
{
|
QPainter painter(this);
|
||||||
return m_node.get();
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapView::drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth,
|
const QColor* keyPalette = getEditor()->m_paintPalette;
|
||||||
const QColor* keyPalette, int o, int k) const
|
|
||||||
{
|
|
||||||
int keyIdx = o * 12 + k;
|
|
||||||
int keyPalIdx = m_keyPalettes[keyIdx];
|
|
||||||
painter.setPen(QPen(PenColorTable[k], penWidth));
|
|
||||||
painter.setBrush(keyPalIdx < 0 ? NeutralColorTable[k] : keyPalette[keyPalIdx]);
|
|
||||||
painter.drawEllipse(PointTable[k] + octaveRect.topLeft(), RadiusTable[k], RadiusTable[k]);
|
|
||||||
painter.setTransform(QTransform().translate(
|
|
||||||
PointTable[k].x() + octaveRect.left() - 13, PointTable[k].y() + octaveRect.top() - 20).rotate(-90.0));
|
|
||||||
painter.drawStaticText(QPointF{}, m_keyTexts[keyIdx]);
|
|
||||||
painter.setTransform(QTransform());
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapView::paintEvent(QPaintEvent* ev)
|
qreal deviceRatio = devicePixelRatioF();
|
||||||
{
|
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
|
||||||
QPainter painter(this);
|
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
|
||||||
|
|
||||||
const QColor* keyPalette = getEditor()->m_paintPalette;
|
painter.setFont(m_keyFont);
|
||||||
|
int kbY = height() / 2 - 100;
|
||||||
qreal deviceRatio = devicePixelRatioF();
|
for (int o = 0; o < 10; ++o) {
|
||||||
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
|
QRect thisRect(o * 280, kbY, 280, 200);
|
||||||
|
if (ev->rect().intersects(thisRect)) {
|
||||||
painter.setFont(m_keyFont);
|
m_octaveRenderer.render(&painter, thisRect);
|
||||||
int kbY = height() / 2 - 100;
|
for (int k = 0; k < 12; ++k)
|
||||||
for (int o = 0; o < 10; ++o)
|
drawKey(painter, thisRect, penWidth, keyPalette, o, k);
|
||||||
{
|
|
||||||
QRect thisRect(o * 280, kbY, 280, 200);
|
|
||||||
if (ev->rect().intersects(thisRect))
|
|
||||||
{
|
|
||||||
m_octaveRenderer.render(&painter, thisRect);
|
|
||||||
for (int k = 0; k < 12; ++k)
|
|
||||||
drawKey(painter, thisRect, penWidth, keyPalette, o, k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QRect thisRect(2800, kbY, 202, 200);
|
|
||||||
if (ev->rect().intersects(thisRect))
|
|
||||||
{
|
|
||||||
m_lastOctaveRenderer.render(&painter, thisRect);
|
|
||||||
for (int k = 0; k < 8; ++k)
|
|
||||||
drawKey(painter, thisRect, penWidth, keyPalette, 10, k);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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)
|
void KeymapView::mousePressEvent(QMouseEvent* ev) { mouseMoveEvent(ev); }
|
||||||
{
|
|
||||||
mouseMoveEvent(ev);
|
int KeymapView::getKey(const QPoint& localPos) const {
|
||||||
|
QPointF localPoint = m_widgetToSvg.map(localPos);
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
if (m_sharp[i].contains(localPoint))
|
||||||
|
return SharpKeyNumbers[i];
|
||||||
|
for (int i = 0; i < 7; ++i)
|
||||||
|
if (m_natural[i].contains(localPoint))
|
||||||
|
return NaturalKeyNumbers[i];
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeymapView::getKey(const QPoint& localPos) const
|
void KeymapView::mouseMoveEvent(QMouseEvent* ev) {
|
||||||
{
|
int octave = ev->x() / 280;
|
||||||
QPointF localPoint = m_widgetToSvg.map(localPos);
|
int key = getKey(ev->pos() - QPoint(octave * 280, height() / 2 - 100));
|
||||||
for (int i = 0; i < 5; ++i)
|
if (octave >= 0 && key >= 0)
|
||||||
if (m_sharp[i].contains(localPoint))
|
getEditor()->touchKey(octave * 12 + key, ev->modifiers() & Qt::ShiftModifier);
|
||||||
return SharpKeyNumbers[i];
|
|
||||||
for (int i = 0; i < 7; ++i)
|
|
||||||
if (m_natural[i].contains(localPoint))
|
|
||||||
return NaturalKeyNumbers[i];
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeymapView::mouseMoveEvent(QMouseEvent* ev)
|
void KeymapView::wheelEvent(QWheelEvent* event) {
|
||||||
{
|
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
|
||||||
int octave = ev->x() / 280;
|
/* Send wheel event directly to the scroll bar */
|
||||||
int key = getKey(ev->pos() - QPoint(octave * 280, height() / 2 - 100));
|
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
|
||||||
if (octave >= 0 && key >= 0)
|
}
|
||||||
getEditor()->touchKey(octave * 12 + key, ev->modifiers() & Qt::ShiftModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapView::wheelEvent(QWheelEvent* event)
|
|
||||||
{
|
|
||||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
|
|
||||||
{
|
|
||||||
/* Send wheel event directly to the scroll bar */
|
|
||||||
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeymapView::KeymapView(QWidget* parent)
|
KeymapView::KeymapView(QWidget* parent)
|
||||||
: QWidget(parent), m_octaveRenderer(QStringLiteral(":/bg/keyboard.svg")),
|
: QWidget(parent)
|
||||||
m_lastOctaveRenderer(QStringLiteral(":/bg/keyboard_last.svg"))
|
, m_octaveRenderer(QStringLiteral(":/bg/keyboard.svg"))
|
||||||
{
|
, m_lastOctaveRenderer(QStringLiteral(":/bg/keyboard_last.svg")) {
|
||||||
setMinimumWidth(3002);
|
setMinimumWidth(3002);
|
||||||
|
|
||||||
int k = 0;
|
int k = 0;
|
||||||
for (int i = 0; i < 11; ++i)
|
for (int i = 0; i < 11; ++i)
|
||||||
for (int j = 0; j < 12 && k < 128; ++j)
|
for (int j = 0; j < 12 && k < 128; ++j)
|
||||||
m_keyTexts[k++].setText(QStringLiteral("%1%2").arg(KeyStrings[j]).arg(i - 1));
|
m_keyTexts[k++].setText(QStringLiteral("%1%2").arg(KeyStrings[j]).arg(i - 1));
|
||||||
m_keyFont.setPointSize(12);
|
m_keyFont.setPointSize(12);
|
||||||
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
|
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
|
||||||
|
|
||||||
for (int i = 0; i < 7; ++i)
|
for (int i = 0; i < 7; ++i)
|
||||||
if (m_octaveRenderer.elementExists(NaturalKeyNames[i]))
|
if (m_octaveRenderer.elementExists(NaturalKeyNames[i]))
|
||||||
m_natural[i] = m_octaveRenderer.matrixForElement(NaturalKeyNames[i]).
|
m_natural[i] = m_octaveRenderer.matrixForElement(NaturalKeyNames[i])
|
||||||
mapRect(m_octaveRenderer.boundsOnElement(NaturalKeyNames[i]));
|
.mapRect(m_octaveRenderer.boundsOnElement(NaturalKeyNames[i]));
|
||||||
|
|
||||||
for (int i = 0; i < 5; ++i)
|
for (int i = 0; i < 5; ++i)
|
||||||
if (m_octaveRenderer.elementExists(SharpKeyNames[i]))
|
if (m_octaveRenderer.elementExists(SharpKeyNames[i]))
|
||||||
m_sharp[i] = m_octaveRenderer.matrixForElement(SharpKeyNames[i]).
|
m_sharp[i] = m_octaveRenderer.matrixForElement(SharpKeyNames[i])
|
||||||
mapRect(m_octaveRenderer.boundsOnElement(SharpKeyNames[i]));
|
.mapRect(m_octaveRenderer.boundsOnElement(SharpKeyNames[i]));
|
||||||
|
|
||||||
m_widgetToSvg = RectToRect(QRect(0, 0, 280, 200), m_octaveRenderer.viewBoxF());
|
m_widgetToSvg = RectToRect(QRect(0, 0, 280, 200), m_octaveRenderer.viewBoxF());
|
||||||
}
|
}
|
||||||
|
|
||||||
KeymapEditor* KeymapControls::getEditor() const
|
KeymapEditor* KeymapControls::getEditor() const { return qobject_cast<KeymapEditor*>(parentWidget()); }
|
||||||
{
|
|
||||||
return qobject_cast<KeymapEditor*>(parentWidget());
|
void KeymapControls::setPaintIdx(int idx) {
|
||||||
|
QPalette palette = m_paintButton->palette();
|
||||||
|
if (idx < 0) {
|
||||||
|
palette.setColor(QPalette::Background, QWidget::palette().color(QPalette::Background));
|
||||||
|
palette.setColor(QPalette::Button, QWidget::palette().color(QPalette::Button));
|
||||||
|
} else {
|
||||||
|
const QColor* keyPalette = getEditor()->m_paintPalette;
|
||||||
|
palette.setColor(QPalette::Background, keyPalette[idx]);
|
||||||
|
palette.setColor(QPalette::Button, keyPalette[idx].darker(300));
|
||||||
|
}
|
||||||
|
m_paintButton->setPalette(palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeymapControls::setPaintIdx(int idx)
|
void KeymapControls::setKeymap(const amuse::Keymap& km) {
|
||||||
{
|
m_enableUpdate = false;
|
||||||
QPalette palette = m_paintButton->palette();
|
int idx = m_macro->collection()->indexOfId(km.macro.id);
|
||||||
if (idx < 0)
|
m_macro->setCurrentIndex(idx + 1);
|
||||||
{
|
m_transpose->setValue(km.transpose);
|
||||||
palette.setColor(QPalette::Background, QWidget::palette().color(QPalette::Background));
|
if (km.pan == -128) {
|
||||||
palette.setColor(QPalette::Button, QWidget::palette().color(QPalette::Button));
|
m_pan->setDisabled(true);
|
||||||
}
|
m_pan->setValue(true);
|
||||||
else
|
m_surround->setChecked(true);
|
||||||
{
|
} else {
|
||||||
const QColor* keyPalette = getEditor()->m_paintPalette;
|
m_pan->setEnabled(true);
|
||||||
palette.setColor(QPalette::Background, keyPalette[idx]);
|
m_pan->setValue(km.pan);
|
||||||
palette.setColor(QPalette::Button, keyPalette[idx].darker(300));
|
|
||||||
}
|
|
||||||
m_paintButton->setPalette(palette);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapControls::setKeymap(const amuse::Keymap& km)
|
|
||||||
{
|
|
||||||
m_enableUpdate = false;
|
|
||||||
int idx = m_macro->collection()->indexOfId(km.macro.id);
|
|
||||||
m_macro->setCurrentIndex(idx + 1);
|
|
||||||
m_transpose->setValue(km.transpose);
|
|
||||||
if (km.pan == -128)
|
|
||||||
{
|
|
||||||
m_pan->setDisabled(true);
|
|
||||||
m_pan->setValue(true);
|
|
||||||
m_surround->setChecked(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_pan->setEnabled(true);
|
|
||||||
m_pan->setValue(km.pan);
|
|
||||||
m_surround->setChecked(false);
|
|
||||||
}
|
|
||||||
m_prioOffset->setValue(km.prioOffset);
|
|
||||||
m_enableUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapControls::loadData(ProjectModel::KeymapNode* node)
|
|
||||||
{
|
|
||||||
m_enableUpdate = false;
|
|
||||||
m_macro->setCollection(g_MainWindow->projectModel()->getGroupNode(node)->
|
|
||||||
getCollectionOfType(ProjectModel::INode::Type::SoundMacro));
|
|
||||||
m_macro->setDisabled(false);
|
|
||||||
m_transpose->setDisabled(false);
|
|
||||||
m_transpose->setValue(0);
|
|
||||||
m_pan->setDisabled(false);
|
|
||||||
m_pan->setValue(64);
|
|
||||||
m_surround->setDisabled(false);
|
|
||||||
m_surround->setChecked(false);
|
m_surround->setChecked(false);
|
||||||
m_prioOffset->setDisabled(false);
|
}
|
||||||
m_prioOffset->setValue(0);
|
m_prioOffset->setValue(km.prioOffset);
|
||||||
m_paintButton->setDisabled(false);
|
m_enableUpdate = true;
|
||||||
m_paintButton->setPalette(palette());
|
|
||||||
m_enableUpdate = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeymapControls::unloadData()
|
void KeymapControls::loadData(ProjectModel::KeymapNode* node) {
|
||||||
{
|
m_enableUpdate = false;
|
||||||
m_enableUpdate = false;
|
m_macro->setCollection(
|
||||||
m_macro->setCollection(nullptr);
|
g_MainWindow->projectModel()->getGroupNode(node)->getCollectionOfType(ProjectModel::INode::Type::SoundMacro));
|
||||||
m_macro->setDisabled(true);
|
m_macro->setDisabled(false);
|
||||||
m_transpose->setDisabled(true);
|
m_transpose->setDisabled(false);
|
||||||
m_pan->setDisabled(true);
|
m_transpose->setValue(0);
|
||||||
m_surround->setDisabled(true);
|
m_pan->setDisabled(false);
|
||||||
m_prioOffset->setDisabled(true);
|
m_pan->setValue(64);
|
||||||
m_paintButton->setDisabled(true);
|
m_surround->setDisabled(false);
|
||||||
m_paintButton->setPalette(palette());
|
m_surround->setChecked(false);
|
||||||
m_enableUpdate = true;
|
m_prioOffset->setDisabled(false);
|
||||||
|
m_prioOffset->setValue(0);
|
||||||
|
m_paintButton->setDisabled(false);
|
||||||
|
m_paintButton->setPalette(palette());
|
||||||
|
m_enableUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeymapControls::controlChanged()
|
void KeymapControls::unloadData() {
|
||||||
{
|
m_enableUpdate = false;
|
||||||
if (m_enableUpdate)
|
m_macro->setCollection(nullptr);
|
||||||
{
|
m_macro->setDisabled(true);
|
||||||
amuse::Keymap km;
|
m_transpose->setDisabled(true);
|
||||||
km.macro.id = m_macro->currentIndex() == 0 ? amuse::SoundMacroId{} :
|
m_pan->setDisabled(true);
|
||||||
m_macro->collection()->idOfIndex(m_macro->currentIndex() - 1);
|
m_surround->setDisabled(true);
|
||||||
km.transpose = int8_t(m_transpose->value());
|
m_prioOffset->setDisabled(true);
|
||||||
km.pan = int8_t(m_pan->value());
|
m_paintButton->setDisabled(true);
|
||||||
if (m_surround->isChecked())
|
m_paintButton->setPalette(palette());
|
||||||
{
|
m_enableUpdate = true;
|
||||||
km.pan = -128;
|
}
|
||||||
m_pan->setDisabled(true);
|
|
||||||
}
|
void KeymapControls::controlChanged() {
|
||||||
|
if (m_enableUpdate) {
|
||||||
|
amuse::Keymap km;
|
||||||
|
km.macro.id = m_macro->currentIndex() == 0 ? amuse::SoundMacroId{}
|
||||||
|
: m_macro->collection()->idOfIndex(m_macro->currentIndex() - 1);
|
||||||
|
km.transpose = int8_t(m_transpose->value());
|
||||||
|
km.pan = int8_t(m_pan->value());
|
||||||
|
if (m_surround->isChecked()) {
|
||||||
|
km.pan = -128;
|
||||||
|
m_pan->setDisabled(true);
|
||||||
|
} else {
|
||||||
|
m_pan->setEnabled(true);
|
||||||
|
}
|
||||||
|
km.prioOffset = int8_t(m_prioOffset->value());
|
||||||
|
getEditor()->touchControl(km);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeymapControls::paintButtonPressed() {
|
||||||
|
KeymapEditor* editor = getEditor();
|
||||||
|
if (!editor->m_inPaint) {
|
||||||
|
editor->setCursor(QCursor(QPixmap(QStringLiteral(":/icons/IconPaintbrush.svg")), 2, 30));
|
||||||
|
editor->m_inPaint = true;
|
||||||
|
m_paintButton->setDown(true);
|
||||||
|
} else {
|
||||||
|
editor->unsetCursor();
|
||||||
|
editor->m_inPaint = false;
|
||||||
|
m_paintButton->setDown(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeymapControls::KeymapControls(QWidget* parent) : QFrame(parent) {
|
||||||
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||||
|
setFixedHeight(100);
|
||||||
|
setFrameShape(QFrame::StyledPanel);
|
||||||
|
setFrameShadow(QFrame::Sunken);
|
||||||
|
setBackgroundRole(QPalette::Base);
|
||||||
|
setAutoFillBackground(true);
|
||||||
|
|
||||||
|
QPalette palette = QWidget::palette();
|
||||||
|
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
|
||||||
|
|
||||||
|
QHBoxLayout* mainLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
QGridLayout* leftLayout = new QGridLayout;
|
||||||
|
|
||||||
|
leftLayout->addWidget(new QLabel(tr("SoundMacro")), 0, 0);
|
||||||
|
m_macro = new FieldProjectNode;
|
||||||
|
m_macro->setDisabled(true);
|
||||||
|
connect(m_macro, SIGNAL(currentIndexChanged(int)), this, SLOT(controlChanged()));
|
||||||
|
leftLayout->addWidget(m_macro, 1, 0);
|
||||||
|
|
||||||
|
leftLayout->addWidget(new QLabel(tr("Transpose")), 0, 1);
|
||||||
|
m_transpose = new QSpinBox;
|
||||||
|
m_transpose->setPalette(palette);
|
||||||
|
m_transpose->setDisabled(true);
|
||||||
|
m_transpose->setRange(-128, 127);
|
||||||
|
m_transpose->setToolTip(tr("Offset resulting MIDI note"));
|
||||||
|
connect(m_transpose, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
|
||||||
|
leftLayout->addWidget(m_transpose, 1, 1);
|
||||||
|
|
||||||
|
leftLayout->addWidget(new QLabel(tr("Pan")), 0, 2);
|
||||||
|
m_pan = new QSpinBox;
|
||||||
|
m_pan->setPalette(palette);
|
||||||
|
m_pan->setDisabled(true);
|
||||||
|
m_pan->setRange(-127, 127);
|
||||||
|
m_pan->setToolTip(tr("Set initial pan"));
|
||||||
|
connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
|
||||||
|
leftLayout->addWidget(m_pan, 1, 2);
|
||||||
|
|
||||||
|
leftLayout->addWidget(new QLabel(tr("Surround")), 0, 3);
|
||||||
|
m_surround = new QCheckBox;
|
||||||
|
m_surround->setPalette(palette);
|
||||||
|
m_surround->setDisabled(true);
|
||||||
|
m_surround->setToolTip(tr("Initially play through surround channels"));
|
||||||
|
connect(m_surround, SIGNAL(stateChanged(int)), this, SLOT(controlChanged()));
|
||||||
|
leftLayout->addWidget(m_surround, 1, 3);
|
||||||
|
|
||||||
|
leftLayout->addWidget(new QLabel(tr("Prio Offset")), 0, 4);
|
||||||
|
m_prioOffset = new QSpinBox;
|
||||||
|
m_prioOffset->setPalette(palette);
|
||||||
|
m_prioOffset->setDisabled(true);
|
||||||
|
m_prioOffset->setRange(-128, 127);
|
||||||
|
m_prioOffset->setToolTip(tr("Offset resulting priority"));
|
||||||
|
connect(m_prioOffset, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
|
||||||
|
leftLayout->addWidget(m_prioOffset, 1, 4);
|
||||||
|
|
||||||
|
leftLayout->setColumnMinimumWidth(0, 200);
|
||||||
|
leftLayout->setColumnMinimumWidth(1, 75);
|
||||||
|
leftLayout->setColumnMinimumWidth(2, 75);
|
||||||
|
leftLayout->setColumnMinimumWidth(3, 50);
|
||||||
|
leftLayout->setColumnMinimumWidth(4, 75);
|
||||||
|
leftLayout->setRowMinimumHeight(0, 22);
|
||||||
|
leftLayout->setRowMinimumHeight(1, 37);
|
||||||
|
leftLayout->setContentsMargins(10, 6, 0, 14);
|
||||||
|
|
||||||
|
QVBoxLayout* rightLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
m_paintButton = new PaintButton;
|
||||||
|
m_paintButton->setDisabled(true);
|
||||||
|
connect(m_paintButton, SIGNAL(pressed()), this, SLOT(paintButtonPressed()));
|
||||||
|
rightLayout->addWidget(m_paintButton);
|
||||||
|
rightLayout->setContentsMargins(0, 0, 10, 0);
|
||||||
|
|
||||||
|
mainLayout->addLayout(leftLayout);
|
||||||
|
mainLayout->addStretch(1);
|
||||||
|
mainLayout->addLayout(rightLayout);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeymapEditor::_touch() {
|
||||||
|
if (m_controlKeymap.macro.id == 0xffff)
|
||||||
|
m_controls->setPaintIdx(-1);
|
||||||
|
else
|
||||||
|
m_controls->setPaintIdx(getConfigIdx(m_controlKeymap.configKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeymapEditor::touchKey(int key, bool bulk) {
|
||||||
|
if (m_inPaint) {
|
||||||
|
if (bulk) {
|
||||||
|
uint64_t refKey = (*m_kmView->m_node->m_obj)[key].configKey();
|
||||||
|
for (int i = 0; i < 128; ++i) {
|
||||||
|
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[i];
|
||||||
|
if (km.configKey() != refKey)
|
||||||
|
continue;
|
||||||
|
if (km.macro.id != 0xffff)
|
||||||
|
deallocateConfigIdx(km.configKey());
|
||||||
|
km = m_controlKeymap;
|
||||||
|
if (km.macro.id == 0xffff)
|
||||||
|
m_kmView->m_keyPalettes[i] = -1;
|
||||||
else
|
else
|
||||||
{
|
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
|
||||||
m_pan->setEnabled(true);
|
}
|
||||||
}
|
} else {
|
||||||
km.prioOffset = int8_t(m_prioOffset->value());
|
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
|
||||||
getEditor()->touchControl(km);
|
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());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapControls::paintButtonPressed()
|
m_kmView->update();
|
||||||
{
|
} else {
|
||||||
KeymapEditor* editor = getEditor();
|
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
|
||||||
if (!editor->m_inPaint)
|
|
||||||
{
|
|
||||||
editor->setCursor(QCursor(QPixmap(QStringLiteral(":/icons/IconPaintbrush.svg")), 2, 30));
|
|
||||||
editor->m_inPaint = true;
|
|
||||||
m_paintButton->setDown(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
editor->unsetCursor();
|
|
||||||
editor->m_inPaint = false;
|
|
||||||
m_paintButton->setDown(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KeymapControls::KeymapControls(QWidget* parent)
|
|
||||||
: QFrame(parent)
|
|
||||||
{
|
|
||||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
||||||
setFixedHeight(100);
|
|
||||||
setFrameShape(QFrame::StyledPanel);
|
|
||||||
setFrameShadow(QFrame::Sunken);
|
|
||||||
setBackgroundRole(QPalette::Base);
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
|
|
||||||
QPalette palette = QWidget::palette();
|
|
||||||
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
|
|
||||||
|
|
||||||
QHBoxLayout* mainLayout = new QHBoxLayout;
|
|
||||||
|
|
||||||
QGridLayout* leftLayout = new QGridLayout;
|
|
||||||
|
|
||||||
leftLayout->addWidget(new QLabel(tr("SoundMacro")), 0, 0);
|
|
||||||
m_macro = new FieldProjectNode;
|
|
||||||
m_macro->setDisabled(true);
|
|
||||||
connect(m_macro, SIGNAL(currentIndexChanged(int)), this, SLOT(controlChanged()));
|
|
||||||
leftLayout->addWidget(m_macro, 1, 0);
|
|
||||||
|
|
||||||
leftLayout->addWidget(new QLabel(tr("Transpose")), 0, 1);
|
|
||||||
m_transpose = new QSpinBox;
|
|
||||||
m_transpose->setPalette(palette);
|
|
||||||
m_transpose->setDisabled(true);
|
|
||||||
m_transpose->setRange(-128, 127);
|
|
||||||
m_transpose->setToolTip(tr("Offset resulting MIDI note"));
|
|
||||||
connect(m_transpose, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
|
|
||||||
leftLayout->addWidget(m_transpose, 1, 1);
|
|
||||||
|
|
||||||
leftLayout->addWidget(new QLabel(tr("Pan")), 0, 2);
|
|
||||||
m_pan = new QSpinBox;
|
|
||||||
m_pan->setPalette(palette);
|
|
||||||
m_pan->setDisabled(true);
|
|
||||||
m_pan->setRange(-127, 127);
|
|
||||||
m_pan->setToolTip(tr("Set initial pan"));
|
|
||||||
connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
|
|
||||||
leftLayout->addWidget(m_pan, 1, 2);
|
|
||||||
|
|
||||||
leftLayout->addWidget(new QLabel(tr("Surround")), 0, 3);
|
|
||||||
m_surround = new QCheckBox;
|
|
||||||
m_surround->setPalette(palette);
|
|
||||||
m_surround->setDisabled(true);
|
|
||||||
m_surround->setToolTip(tr("Initially play through surround channels"));
|
|
||||||
connect(m_surround, SIGNAL(stateChanged(int)), this, SLOT(controlChanged()));
|
|
||||||
leftLayout->addWidget(m_surround, 1, 3);
|
|
||||||
|
|
||||||
leftLayout->addWidget(new QLabel(tr("Prio Offset")), 0, 4);
|
|
||||||
m_prioOffset = new QSpinBox;
|
|
||||||
m_prioOffset->setPalette(palette);
|
|
||||||
m_prioOffset->setDisabled(true);
|
|
||||||
m_prioOffset->setRange(-128, 127);
|
|
||||||
m_prioOffset->setToolTip(tr("Offset resulting priority"));
|
|
||||||
connect(m_prioOffset, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
|
|
||||||
leftLayout->addWidget(m_prioOffset, 1, 4);
|
|
||||||
|
|
||||||
leftLayout->setColumnMinimumWidth(0, 200);
|
|
||||||
leftLayout->setColumnMinimumWidth(1, 75);
|
|
||||||
leftLayout->setColumnMinimumWidth(2, 75);
|
|
||||||
leftLayout->setColumnMinimumWidth(3, 50);
|
|
||||||
leftLayout->setColumnMinimumWidth(4, 75);
|
|
||||||
leftLayout->setRowMinimumHeight(0, 22);
|
|
||||||
leftLayout->setRowMinimumHeight(1, 37);
|
|
||||||
leftLayout->setContentsMargins(10, 6, 0, 14);
|
|
||||||
|
|
||||||
QVBoxLayout* rightLayout = new QVBoxLayout;
|
|
||||||
|
|
||||||
m_paintButton = new PaintButton;
|
|
||||||
m_paintButton->setDisabled(true);
|
|
||||||
connect(m_paintButton, SIGNAL(pressed()), this, SLOT(paintButtonPressed()));
|
|
||||||
rightLayout->addWidget(m_paintButton);
|
|
||||||
rightLayout->setContentsMargins(0, 0, 10, 0);
|
|
||||||
|
|
||||||
mainLayout->addLayout(leftLayout);
|
|
||||||
mainLayout->addStretch(1);
|
|
||||||
mainLayout->addLayout(rightLayout);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapEditor::_touch()
|
|
||||||
{
|
|
||||||
if (m_controlKeymap.macro.id == 0xffff)
|
|
||||||
m_controls->setPaintIdx(-1);
|
|
||||||
else
|
|
||||||
m_controls->setPaintIdx(getConfigIdx(m_controlKeymap.configKey()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapEditor::touchKey(int key, bool bulk)
|
|
||||||
{
|
|
||||||
if (m_inPaint)
|
|
||||||
{
|
|
||||||
if (bulk)
|
|
||||||
{
|
|
||||||
uint64_t refKey = (*m_kmView->m_node->m_obj)[key].configKey();
|
|
||||||
for (int i = 0; i < 128; ++i)
|
|
||||||
{
|
|
||||||
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[i];
|
|
||||||
if (km.configKey() != refKey)
|
|
||||||
continue;
|
|
||||||
if (km.macro.id != 0xffff)
|
|
||||||
deallocateConfigIdx(km.configKey());
|
|
||||||
km = m_controlKeymap;
|
|
||||||
if (km.macro.id == 0xffff)
|
|
||||||
m_kmView->m_keyPalettes[i] = -1;
|
|
||||||
else
|
|
||||||
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
|
|
||||||
if (km.macro.id != 0xffff)
|
|
||||||
deallocateConfigIdx(km.configKey());
|
|
||||||
km = m_controlKeymap;
|
|
||||||
if (km.macro.id == 0xffff)
|
|
||||||
m_kmView->m_keyPalettes[key] = -1;
|
|
||||||
else
|
|
||||||
m_kmView->m_keyPalettes[key] = allocateConfigIdx(km.configKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_kmView->update();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
|
|
||||||
m_controlKeymap = km;
|
|
||||||
m_controls->setKeymap(km);
|
|
||||||
_touch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapEditor::touchControl(const amuse::Keymap& km)
|
|
||||||
{
|
|
||||||
m_controlKeymap = km;
|
m_controlKeymap = km;
|
||||||
|
m_controls->setKeymap(km);
|
||||||
_touch();
|
_touch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeymapEditor::allocateConfigIdx(uint64_t key)
|
void KeymapEditor::touchControl(const amuse::Keymap& km) {
|
||||||
{
|
m_controlKeymap = km;
|
||||||
auto search = m_configToIdx.find(key);
|
_touch();
|
||||||
if (search != m_configToIdx.end())
|
}
|
||||||
{
|
|
||||||
++search->second.second;
|
int KeymapEditor::allocateConfigIdx(uint64_t key) {
|
||||||
return search->second.first;
|
auto search = m_configToIdx.find(key);
|
||||||
|
if (search != m_configToIdx.end()) {
|
||||||
|
++search->second.second;
|
||||||
|
return search->second.first;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 129; ++i)
|
||||||
|
if (!m_idxBitmap[i]) {
|
||||||
|
m_configToIdx[key] = std::make_pair(i, 1);
|
||||||
|
m_idxBitmap.set(i);
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 129; ++i)
|
Q_UNREACHABLE();
|
||||||
if (!m_idxBitmap[i])
|
|
||||||
{
|
|
||||||
m_configToIdx[key] = std::make_pair(i, 1);
|
|
||||||
m_idxBitmap.set(i);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeymapEditor::deallocateConfigIdx(uint64_t key)
|
void KeymapEditor::deallocateConfigIdx(uint64_t key) {
|
||||||
{
|
auto search = m_configToIdx.find(key);
|
||||||
auto search = m_configToIdx.find(key);
|
if (search != m_configToIdx.end()) {
|
||||||
if (search != m_configToIdx.end())
|
--search->second.second;
|
||||||
{
|
if (search->second.second == 0) {
|
||||||
--search->second.second;
|
m_idxBitmap.reset(search->second.first);
|
||||||
if (search->second.second == 0)
|
m_configToIdx.erase(search);
|
||||||
{
|
|
||||||
m_idxBitmap.reset(search->second.first);
|
|
||||||
m_configToIdx.erase(search);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Q_UNREACHABLE();
|
return;
|
||||||
|
}
|
||||||
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
int KeymapEditor::getConfigIdx(uint64_t key) const
|
int KeymapEditor::getConfigIdx(uint64_t key) const {
|
||||||
{
|
auto search = m_configToIdx.find(key);
|
||||||
auto search = m_configToIdx.find(key);
|
if (search != m_configToIdx.end())
|
||||||
if (search != m_configToIdx.end())
|
return search->second.first;
|
||||||
return search->second.first;
|
for (int i = 0; i < 129; ++i)
|
||||||
for (int i = 0; i < 129; ++i)
|
if (!m_idxBitmap[i])
|
||||||
if (!m_idxBitmap[i])
|
return i;
|
||||||
return i;
|
Q_UNREACHABLE();
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KeymapEditor::loadData(ProjectModel::KeymapNode* node)
|
bool KeymapEditor::loadData(ProjectModel::KeymapNode* node) {
|
||||||
{
|
if (m_kmView->m_node.get() != node) {
|
||||||
if (m_kmView->m_node.get() != node)
|
|
||||||
{
|
|
||||||
m_configToIdx.clear();
|
|
||||||
m_idxBitmap.reset();
|
|
||||||
|
|
||||||
for (int i = 0; i < 128; ++i)
|
|
||||||
{
|
|
||||||
amuse::Keymap& km = (*node->m_obj)[i];
|
|
||||||
if (km.macro.id == 0xffff)
|
|
||||||
m_kmView->m_keyPalettes[i] = -1;
|
|
||||||
else
|
|
||||||
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_controlKeymap = amuse::Keymap();
|
|
||||||
m_kmView->loadData(node);
|
|
||||||
m_controls->loadData(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_inPaint = false;
|
|
||||||
m_controls->m_paintButton->setDown(false);
|
|
||||||
unsetCursor();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeymapEditor::unloadData()
|
|
||||||
{
|
|
||||||
m_configToIdx.clear();
|
m_configToIdx.clear();
|
||||||
m_idxBitmap.reset();
|
m_idxBitmap.reset();
|
||||||
m_kmView->unloadData();
|
|
||||||
m_controls->unloadData();
|
|
||||||
|
|
||||||
m_inPaint = false;
|
for (int i = 0; i < 128; ++i) {
|
||||||
m_controls->m_paintButton->setDown(false);
|
amuse::Keymap& km = (*node->m_obj)[i];
|
||||||
unsetCursor();
|
if (km.macro.id == 0xffff)
|
||||||
}
|
m_kmView->m_keyPalettes[i] = -1;
|
||||||
|
else
|
||||||
ProjectModel::INode* KeymapEditor::currentNode() const
|
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
KeymapEditor::KeymapEditor(QWidget* parent)
|
||||||
: EditorWidget(parent), m_scrollArea(new QScrollArea),
|
: EditorWidget(parent), m_scrollArea(new QScrollArea), m_kmView(new KeymapView), m_controls(new KeymapControls) {
|
||||||
m_kmView(new KeymapView), m_controls(new KeymapControls)
|
|
||||||
{
|
|
||||||
|
|
||||||
int k = 0;
|
int k = 0;
|
||||||
for (int i = 0; i < 13; ++i)
|
for (int i = 0; i < 13; ++i)
|
||||||
for (int j = 0; j < 10 && k < 129; ++j)
|
for (int j = 0; j < 10 && k < 129; ++j)
|
||||||
m_paintPalette[k++].setHsv(HueTable[j], SaturationTable[i], ValueTable[i]);
|
m_paintPalette[k++].setHsv(HueTable[j], SaturationTable[i], ValueTable[i]);
|
||||||
|
|
||||||
m_scrollArea->setWidget(m_kmView);
|
m_scrollArea->setWidget(m_kmView);
|
||||||
m_scrollArea->setWidgetResizable(true);
|
m_scrollArea->setWidgetResizable(true);
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout;
|
QVBoxLayout* layout = new QVBoxLayout;
|
||||||
layout->setContentsMargins(QMargins());
|
layout->setContentsMargins(QMargins());
|
||||||
layout->setSpacing(1);
|
layout->setSpacing(1);
|
||||||
layout->addWidget(m_scrollArea);
|
layout->addWidget(m_scrollArea);
|
||||||
layout->addWidget(m_controls);
|
layout->addWidget(m_controls);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,95 +13,93 @@
|
||||||
|
|
||||||
class KeymapEditor;
|
class KeymapEditor;
|
||||||
|
|
||||||
class PaintButton : public QPushButton
|
class PaintButton : public QPushButton {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit PaintButton(QWidget* parent = Q_NULLPTR);
|
explicit PaintButton(QWidget* parent = Q_NULLPTR);
|
||||||
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
|
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
|
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
|
||||||
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
|
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeymapView : public QWidget
|
class KeymapView : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class KeymapControls;
|
||||||
friend class KeymapControls;
|
friend class KeymapEditor;
|
||||||
friend class KeymapEditor;
|
amuse::ObjToken<ProjectModel::KeymapNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::KeymapNode> m_node;
|
QSvgRenderer m_octaveRenderer;
|
||||||
QSvgRenderer m_octaveRenderer;
|
QSvgRenderer m_lastOctaveRenderer;
|
||||||
QSvgRenderer m_lastOctaveRenderer;
|
QRectF m_natural[7];
|
||||||
QRectF m_natural[7];
|
QRectF m_sharp[5];
|
||||||
QRectF m_sharp[5];
|
QTransform m_widgetToSvg;
|
||||||
QTransform m_widgetToSvg;
|
QFont m_keyFont;
|
||||||
QFont m_keyFont;
|
QStaticText m_keyTexts[128];
|
||||||
QStaticText m_keyTexts[128];
|
int m_keyPalettes[128];
|
||||||
int m_keyPalettes[128];
|
KeymapEditor* getEditor() const;
|
||||||
KeymapEditor* getEditor() const;
|
int getKey(const QPoint& localPos) const;
|
||||||
int getKey(const QPoint& localPos) const;
|
void drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth, const QColor* keyPalette, int o,
|
||||||
void drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth,
|
int k) const;
|
||||||
const QColor* keyPalette, int o, int k) const;
|
|
||||||
public:
|
|
||||||
explicit KeymapView(QWidget* parent = Q_NULLPTR);
|
|
||||||
void loadData(ProjectModel::KeymapNode* node);
|
|
||||||
void unloadData();
|
|
||||||
ProjectModel::INode* currentNode() const;
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent* ev);
|
public:
|
||||||
void mousePressEvent(QMouseEvent* ev);
|
explicit KeymapView(QWidget* parent = Q_NULLPTR);
|
||||||
void mouseMoveEvent(QMouseEvent* ev);
|
void loadData(ProjectModel::KeymapNode* node);
|
||||||
void wheelEvent(QWheelEvent* event);
|
void unloadData();
|
||||||
|
ProjectModel::INode* currentNode() const;
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent* ev);
|
||||||
|
void mousePressEvent(QMouseEvent* ev);
|
||||||
|
void mouseMoveEvent(QMouseEvent* ev);
|
||||||
|
void wheelEvent(QWheelEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeymapControls : public QFrame
|
class KeymapControls : public QFrame {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class KeymapView;
|
||||||
friend class KeymapView;
|
friend class KeymapEditor;
|
||||||
friend class KeymapEditor;
|
FieldProjectNode* m_macro;
|
||||||
FieldProjectNode* m_macro;
|
QSpinBox* m_transpose;
|
||||||
QSpinBox* m_transpose;
|
QSpinBox* m_pan;
|
||||||
QSpinBox* m_pan;
|
QCheckBox* m_surround;
|
||||||
QCheckBox* m_surround;
|
QSpinBox* m_prioOffset;
|
||||||
QSpinBox* m_prioOffset;
|
PaintButton* m_paintButton;
|
||||||
PaintButton* m_paintButton;
|
bool m_enableUpdate = true;
|
||||||
bool m_enableUpdate = true;
|
KeymapEditor* getEditor() const;
|
||||||
KeymapEditor* getEditor() const;
|
void setPaintIdx(int idx);
|
||||||
void setPaintIdx(int idx);
|
void setKeymap(const amuse::Keymap& km);
|
||||||
void setKeymap(const amuse::Keymap& km);
|
|
||||||
public:
|
public:
|
||||||
explicit KeymapControls(QWidget* parent = Q_NULLPTR);
|
explicit KeymapControls(QWidget* parent = Q_NULLPTR);
|
||||||
void loadData(ProjectModel::KeymapNode* node);
|
void loadData(ProjectModel::KeymapNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
public slots:
|
public slots:
|
||||||
void controlChanged();
|
void controlChanged();
|
||||||
void paintButtonPressed();
|
void paintButtonPressed();
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeymapEditor : public EditorWidget
|
class KeymapEditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class KeymapView;
|
||||||
friend class KeymapView;
|
friend class KeymapControls;
|
||||||
friend class KeymapControls;
|
QScrollArea* m_scrollArea;
|
||||||
QScrollArea* m_scrollArea;
|
KeymapView* m_kmView;
|
||||||
KeymapView* m_kmView;
|
KeymapControls* m_controls;
|
||||||
KeymapControls* m_controls;
|
QColor m_paintPalette[129];
|
||||||
QColor m_paintPalette[129];
|
amuse::Keymap m_controlKeymap;
|
||||||
amuse::Keymap m_controlKeymap;
|
std::unordered_map<uint64_t, std::pair<int, int>> m_configToIdx;
|
||||||
std::unordered_map<uint64_t, std::pair<int, int>> m_configToIdx;
|
std::bitset<129> m_idxBitmap;
|
||||||
std::bitset<129> m_idxBitmap;
|
bool m_inPaint = false;
|
||||||
bool m_inPaint = false;
|
void _touch();
|
||||||
void _touch();
|
void touchKey(int key, bool bulk = false);
|
||||||
void touchKey(int key, bool bulk = false);
|
void touchControl(const amuse::Keymap& km);
|
||||||
void touchControl(const amuse::Keymap& km);
|
int allocateConfigIdx(uint64_t key);
|
||||||
int allocateConfigIdx(uint64_t key);
|
void deallocateConfigIdx(uint64_t key);
|
||||||
void deallocateConfigIdx(uint64_t key);
|
int getConfigIdx(uint64_t key) const;
|
||||||
int getConfigIdx(uint64_t key) const;
|
|
||||||
public:
|
public:
|
||||||
explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
|
explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::KeymapNode* node);
|
bool loadData(ProjectModel::KeymapNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
void keyPressEvent(QKeyEvent* event);
|
void keyPressEvent(QKeyEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,84 +7,82 @@
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
class SoundMacroDelegate : public BaseObjectDelegate
|
class SoundMacroDelegate : public BaseObjectDelegate {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
protected:
|
protected:
|
||||||
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
|
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
|
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
|
||||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||||
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
||||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
||||||
private slots:
|
private slots:
|
||||||
void smIndexChanged();
|
void smIndexChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
class LayersModel : public QAbstractTableModel
|
class LayersModel : public QAbstractTableModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class LayersEditor;
|
||||||
friend class LayersEditor;
|
friend class SoundMacroDelegate;
|
||||||
friend class SoundMacroDelegate;
|
friend class LayersTableView;
|
||||||
friend class LayersTableView;
|
amuse::ObjToken<ProjectModel::LayersNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::LayersNode> m_node;
|
|
||||||
public:
|
public:
|
||||||
explicit LayersModel(QObject* parent = Q_NULLPTR);
|
explicit LayersModel(QObject* parent = Q_NULLPTR);
|
||||||
void loadData(ProjectModel::LayersNode* node);
|
void loadData(ProjectModel::LayersNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
Qt::DropActions supportedDropActions() const;
|
Qt::DropActions supportedDropActions() const;
|
||||||
Qt::DropActions supportedDragActions() const;
|
Qt::DropActions supportedDragActions() const;
|
||||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
|
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
|
||||||
int row, int column, const QModelIndex &parent);
|
|
||||||
|
|
||||||
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||||
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
|
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent,
|
||||||
const QModelIndex& destinationParent, int destinationChild);
|
int destinationChild);
|
||||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||||
|
|
||||||
void _insertRow(int row, const amuse::LayerMapping& data);
|
void _insertRow(int row, const amuse::LayerMapping& data);
|
||||||
amuse::LayerMapping _removeRow(int row);
|
amuse::LayerMapping _removeRow(int row);
|
||||||
};
|
};
|
||||||
|
|
||||||
class LayersTableView : public QTableView
|
class LayersTableView : public QTableView {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
SoundMacroDelegate m_smDelegate;
|
||||||
SoundMacroDelegate m_smDelegate;
|
RangedValueFactory<-128, 127> m_signedFactory;
|
||||||
RangedValueFactory<-128, 127> m_signedFactory;
|
RangedValueFactory<0, 127> m_unsignedFactory;
|
||||||
RangedValueFactory<0, 127> m_unsignedFactory;
|
QStyledItemDelegate m_signedDelegate, m_unsignedDelegate;
|
||||||
QStyledItemDelegate m_signedDelegate, m_unsignedDelegate;
|
|
||||||
public:
|
public:
|
||||||
explicit LayersTableView(QWidget* parent = Q_NULLPTR);
|
explicit LayersTableView(QWidget* parent = Q_NULLPTR);
|
||||||
void setModel(QAbstractItemModel* model);
|
void setModel(QAbstractItemModel* model);
|
||||||
void deleteSelection();
|
void deleteSelection();
|
||||||
};
|
};
|
||||||
|
|
||||||
class LayersEditor : public EditorWidget
|
class LayersEditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class LayersModel;
|
||||||
friend class LayersModel;
|
LayersModel m_model;
|
||||||
LayersModel m_model;
|
LayersTableView m_tableView;
|
||||||
LayersTableView m_tableView;
|
AddRemoveButtons m_addRemoveButtons;
|
||||||
AddRemoveButtons m_addRemoveButtons;
|
|
||||||
public:
|
public:
|
||||||
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
|
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::LayersNode* node);
|
bool loadData(ProjectModel::LayersNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
void resizeEvent(QResizeEvent* ev);
|
void resizeEvent(QResizeEvent* ev);
|
||||||
AmuseItemEditFlags itemEditFlags() const;
|
AmuseItemEditFlags itemEditFlags() const;
|
||||||
private slots:
|
private slots:
|
||||||
void rowsInserted(const QModelIndex& parent, int first, int last);
|
void rowsInserted(const QModelIndex& parent, int first, int last);
|
||||||
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||||
void doAdd();
|
void doAdd();
|
||||||
void doSelectionChanged();
|
void doSelectionChanged();
|
||||||
void itemDeleteAction();
|
void itemDeleteAction();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,140 +1,118 @@
|
||||||
#include "MIDIReader.hpp"
|
#include "MIDIReader.hpp"
|
||||||
#include "MainWindow.hpp"
|
#include "MainWindow.hpp"
|
||||||
|
|
||||||
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock)
|
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock) : amuse::BooBackendMIDIReader(engine, useLock) {}
|
||||||
: amuse::BooBackendMIDIReader(engine, useLock) {}
|
|
||||||
|
|
||||||
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
|
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) {
|
||||||
{
|
if (g_MainWindow->m_interactiveSeq) {
|
||||||
if (g_MainWindow->m_interactiveSeq)
|
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
|
||||||
{
|
return;
|
||||||
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto keySearch = m_chanVoxs.find(key);
|
auto keySearch = m_chanVoxs.find(key);
|
||||||
if (keySearch == m_chanVoxs.cend())
|
if (keySearch == m_chanVoxs.cend())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
|
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
|
||||||
m_lastVoice.reset();
|
m_lastVoice.reset();
|
||||||
keySearch->second->keyOff();
|
keySearch->second->keyOff();
|
||||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||||
m_chanVoxs.erase(keySearch);
|
m_chanVoxs.erase(keySearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
|
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) {
|
||||||
{
|
if (g_MainWindow->m_interactiveSeq) {
|
||||||
if (g_MainWindow->m_interactiveSeq)
|
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
|
||||||
{
|
return;
|
||||||
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_lastVoice && m_lastVoice->isDestroyed())
|
if (m_lastVoice && m_lastVoice->isDestroyed())
|
||||||
m_lastVoice.reset();
|
m_lastVoice.reset();
|
||||||
|
|
||||||
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
||||||
if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice)
|
if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice) {
|
||||||
{
|
uint8_t lastNote = lastVoice->getLastNote();
|
||||||
uint8_t lastNote = lastVoice->getLastNote();
|
if (lastVoice->doPortamento(key)) {
|
||||||
if (lastVoice->doPortamento(key))
|
m_chanVoxs.erase(lastNote);
|
||||||
{
|
m_chanVoxs[key] = lastVoice;
|
||||||
m_chanVoxs.erase(lastNote);
|
return;
|
||||||
m_chanVoxs[key] = lastVoice;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Ensure keyoff sent first */
|
/* Ensure keyoff sent first */
|
||||||
auto keySearch = m_chanVoxs.find(key);
|
auto keySearch = m_chanVoxs.find(key);
|
||||||
if (keySearch != m_chanVoxs.cend())
|
if (keySearch != m_chanVoxs.cend()) {
|
||||||
{
|
if (keySearch->second == m_lastVoice)
|
||||||
if (keySearch->second == m_lastVoice)
|
m_lastVoice.reset();
|
||||||
m_lastVoice.reset();
|
keySearch->second->keyOff();
|
||||||
keySearch->second->keyOff();
|
keySearch->second->setPedal(false);
|
||||||
keySearch->second->setPedal(false);
|
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
m_chanVoxs.erase(keySearch);
|
||||||
m_chanVoxs.erase(keySearch);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
|
amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
|
||||||
if (newVox)
|
if (newVox) {
|
||||||
{
|
m_chanVoxs[key] = newVox;
|
||||||
m_chanVoxs[key] = newVox;
|
m_lastVoice = newVox;
|
||||||
m_lastVoice = newVox;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
|
void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
|
||||||
|
|
||||||
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
|
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) {
|
||||||
{
|
if (g_MainWindow->m_interactiveSeq) {
|
||||||
if (g_MainWindow->m_interactiveSeq)
|
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
|
||||||
{
|
return;
|
||||||
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (control == 1)
|
if (control == 1) {
|
||||||
{
|
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
|
||||||
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
|
} else if (control == 64) {
|
||||||
}
|
g_MainWindow->setSustain(value >= 0x40);
|
||||||
else if (control == 64)
|
} else {
|
||||||
{
|
for (auto& v : m_engine.getActiveVoices())
|
||||||
g_MainWindow->setSustain(value >= 0x40);
|
v->setCtrlValue(control, value);
|
||||||
}
|
}
|
||||||
else
|
g_MainWindow->m_ctrlVals[control] = value;
|
||||||
{
|
|
||||||
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)
|
void MIDIReader::programChange(uint8_t chan, uint8_t program) {
|
||||||
{
|
if (g_MainWindow->m_interactiveSeq)
|
||||||
if (g_MainWindow->m_interactiveSeq)
|
g_MainWindow->m_interactiveSeq->setChanProgram(chan, program);
|
||||||
g_MainWindow->m_interactiveSeq->setChanProgram(chan, program);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
|
void MIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
|
||||||
|
|
||||||
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch)
|
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch) {
|
||||||
{
|
float pWheel = (pitch - 0x2000) / float(0x2000);
|
||||||
float pWheel = (pitch - 0x2000) / float(0x2000);
|
if (g_MainWindow->m_interactiveSeq)
|
||||||
if (g_MainWindow->m_interactiveSeq)
|
g_MainWindow->m_interactiveSeq->setPitchWheel(chan, pWheel);
|
||||||
g_MainWindow->m_interactiveSeq->setPitchWheel(chan, pWheel);
|
else
|
||||||
else
|
g_MainWindow->m_ui.pitchSlider->setValue(int(pWheel * 2048.f));
|
||||||
g_MainWindow->m_ui.pitchSlider->setValue(int(pWheel * 2048.f));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIReader::allSoundOff(uint8_t chan)
|
void MIDIReader::allSoundOff(uint8_t chan) {
|
||||||
{
|
if (g_MainWindow->m_interactiveSeq) {
|
||||||
if (g_MainWindow->m_interactiveSeq)
|
g_MainWindow->m_interactiveSeq->kill();
|
||||||
{
|
return;
|
||||||
g_MainWindow->m_interactiveSeq->kill();
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& v : m_engine.getActiveVoices())
|
for (auto& v : m_engine.getActiveVoices())
|
||||||
v->kill();
|
v->kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIReader::resetAllControllers(uint8_t /*chan*/) {}
|
void MIDIReader::resetAllControllers(uint8_t /*chan*/) {}
|
||||||
|
|
||||||
void MIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
|
void MIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
|
||||||
|
|
||||||
void MIDIReader::allNotesOff(uint8_t chan)
|
void MIDIReader::allNotesOff(uint8_t chan) {
|
||||||
{
|
if (g_MainWindow->m_interactiveSeq) {
|
||||||
if (g_MainWindow->m_interactiveSeq)
|
g_MainWindow->m_interactiveSeq->kill();
|
||||||
{
|
return;
|
||||||
g_MainWindow->m_interactiveSeq->kill();
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& v : m_engine.getActiveVoices())
|
for (auto& v : m_engine.getActiveVoices())
|
||||||
v->kill();
|
v->kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
|
void MIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||||
|
@ -159,11 +137,8 @@ void MIDIReader::stopSeq() {}
|
||||||
|
|
||||||
void MIDIReader::reset() {}
|
void MIDIReader::reset() {}
|
||||||
|
|
||||||
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine) : amuse::BooBackendVoiceAllocator(booEngine) {}
|
||||||
: amuse::BooBackendVoiceAllocator(booEngine) {}
|
|
||||||
|
|
||||||
std::unique_ptr<amuse::IMIDIReader>
|
std::unique_ptr<amuse::IMIDIReader> VoiceAllocator::allocateMIDIReader(amuse::Engine& engine) {
|
||||||
VoiceAllocator::allocateMIDIReader(amuse::Engine& engine)
|
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
|
||||||
{
|
|
||||||
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,46 +5,44 @@
|
||||||
#include "amuse/Common.hpp"
|
#include "amuse/Common.hpp"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
class MIDIReader : public amuse::BooBackendMIDIReader
|
class MIDIReader : public amuse::BooBackendMIDIReader {
|
||||||
{
|
std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
|
||||||
std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
|
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
|
||||||
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
|
amuse::ObjToken<amuse::Voice> m_lastVoice;
|
||||||
amuse::ObjToken<amuse::Voice> m_lastVoice;
|
|
||||||
public:
|
public:
|
||||||
MIDIReader(amuse::Engine& engine, bool useLock);
|
MIDIReader(amuse::Engine& engine, bool useLock);
|
||||||
|
|
||||||
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
|
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
|
||||||
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
|
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
|
||||||
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
|
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
|
||||||
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
|
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
|
||||||
void programChange(uint8_t chan, uint8_t program);
|
void programChange(uint8_t chan, uint8_t program);
|
||||||
void channelPressure(uint8_t chan, uint8_t pressure);
|
void channelPressure(uint8_t chan, uint8_t pressure);
|
||||||
void pitchBend(uint8_t chan, int16_t pitch);
|
void pitchBend(uint8_t chan, int16_t pitch);
|
||||||
|
|
||||||
void allSoundOff(uint8_t chan);
|
void allSoundOff(uint8_t chan);
|
||||||
void resetAllControllers(uint8_t chan);
|
void resetAllControllers(uint8_t chan);
|
||||||
void localControl(uint8_t chan, bool on);
|
void localControl(uint8_t chan, bool on);
|
||||||
void allNotesOff(uint8_t chan);
|
void allNotesOff(uint8_t chan);
|
||||||
void omniMode(uint8_t chan, bool on);
|
void omniMode(uint8_t chan, bool on);
|
||||||
void polyMode(uint8_t chan, bool on);
|
void polyMode(uint8_t chan, bool on);
|
||||||
|
|
||||||
void sysex(const void* data, size_t len);
|
void sysex(const void* data, size_t len);
|
||||||
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
|
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
|
||||||
void songPositionPointer(uint16_t pointer);
|
void songPositionPointer(uint16_t pointer);
|
||||||
void songSelect(uint8_t song);
|
void songSelect(uint8_t song);
|
||||||
void tuneRequest();
|
void tuneRequest();
|
||||||
|
|
||||||
void startSeq();
|
void startSeq();
|
||||||
void continueSeq();
|
void continueSeq();
|
||||||
void stopSeq();
|
void stopSeq();
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
class VoiceAllocator : public amuse::BooBackendVoiceAllocator
|
class VoiceAllocator : public amuse::BooBackendVoiceAllocator {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
||||||
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine);
|
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,275 +33,265 @@ class KeymapEditor;
|
||||||
class LayersEditor;
|
class LayersEditor;
|
||||||
class SampleEditor;
|
class SampleEditor;
|
||||||
|
|
||||||
enum BackgroundTaskId
|
enum BackgroundTaskId { TaskOpen, TaskImport, TaskExport, TaskReloadSamples };
|
||||||
{
|
|
||||||
TaskOpen,
|
class BackgroundTask : public QObject {
|
||||||
TaskImport,
|
Q_OBJECT
|
||||||
TaskExport,
|
int m_id;
|
||||||
TaskReloadSamples
|
std::function<void(BackgroundTask&)> m_task;
|
||||||
};
|
UIMessenger m_threadMessenger;
|
||||||
|
bool m_cancelled = false;
|
||||||
|
|
||||||
class BackgroundTask : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
int m_id;
|
|
||||||
std::function<void(BackgroundTask&)> m_task;
|
|
||||||
UIMessenger m_threadMessenger;
|
|
||||||
bool m_cancelled = false;
|
|
||||||
public:
|
public:
|
||||||
explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
|
explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
|
||||||
: m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
|
: m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
|
||||||
bool isCanceled() const { QCoreApplication::processEvents(); return m_cancelled; }
|
bool isCanceled() const {
|
||||||
UIMessenger& uiMessenger() { return m_threadMessenger; }
|
QCoreApplication::processEvents();
|
||||||
|
return m_cancelled;
|
||||||
|
}
|
||||||
|
UIMessenger& uiMessenger() { return m_threadMessenger; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void setMinimum(int minimum);
|
void setMinimum(int minimum);
|
||||||
void setMaximum(int maximum);
|
void setMaximum(int maximum);
|
||||||
void setValue(int value);
|
void setValue(int value);
|
||||||
void setLabelText(const QString& text);
|
void setLabelText(const QString& text);
|
||||||
void finished(int id);
|
void finished(int id);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void run() { m_task(*this); emit finished(m_id); }
|
void run() {
|
||||||
void cancel() { m_cancelled = true; }
|
m_task(*this);
|
||||||
|
emit finished(m_id);
|
||||||
|
}
|
||||||
|
void cancel() { m_cancelled = true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class TreeDelegate : public QStyledItemDelegate
|
class TreeDelegate : public QStyledItemDelegate {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
MainWindow& m_window;
|
||||||
MainWindow& m_window;
|
|
||||||
public:
|
|
||||||
explicit TreeDelegate(MainWindow& window, QObject* parent = Q_NULLPTR)
|
|
||||||
: QStyledItemDelegate(parent), m_window(window) {}
|
|
||||||
|
|
||||||
bool editorEvent(QEvent *event,
|
|
||||||
QAbstractItemModel *model,
|
|
||||||
const QStyleOptionViewItem &option,
|
|
||||||
const QModelIndex &index);
|
|
||||||
public slots:
|
|
||||||
void doExportGroup();
|
|
||||||
void doFindUsages();
|
|
||||||
void doCut();
|
|
||||||
void doCopy();
|
|
||||||
void doPaste();
|
|
||||||
void doDuplicate();
|
|
||||||
void doDelete();
|
|
||||||
void doRename();
|
|
||||||
};
|
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
|
||||||
{
|
|
||||||
friend class MIDIReader;
|
|
||||||
friend class ProjectModel;
|
|
||||||
friend class GroupNodeUndoCommand;
|
|
||||||
friend class TreeDelegate;
|
|
||||||
Q_OBJECT
|
|
||||||
Ui::MainWindow m_ui;
|
|
||||||
QAction* m_goBack;
|
|
||||||
QAction* m_goForward;
|
|
||||||
QLinkedList<ProjectModel::INode*> m_navList;
|
|
||||||
QLinkedList<ProjectModel::INode*>::iterator m_navIt;
|
|
||||||
QAction* m_clearRecentFileAct;
|
|
||||||
QAction* m_recentFileActs[MaxRecentFiles];
|
|
||||||
TreeDelegate m_treeDelegate;
|
|
||||||
UIMessenger m_mainMessenger;
|
|
||||||
ProjectModel* m_projectModel = nullptr;
|
|
||||||
QWidget* m_faceSvg;
|
|
||||||
SongGroupEditor* m_songGroupEditor = nullptr;
|
|
||||||
SoundGroupEditor* m_soundGroupEditor = nullptr;
|
|
||||||
SoundMacroEditor* m_soundMacroEditor = nullptr;
|
|
||||||
ADSREditor* m_adsrEditor = nullptr;
|
|
||||||
CurveEditor* m_curveEditor = nullptr;
|
|
||||||
KeymapEditor* m_keymapEditor = nullptr;
|
|
||||||
LayersEditor* m_layersEditor = nullptr;
|
|
||||||
SampleEditor* m_sampleEditor = nullptr;
|
|
||||||
StudioSetupWidget* m_studioSetup = nullptr;
|
|
||||||
QFileDialog m_openDirectoryDialog;
|
|
||||||
QFileDialog m_openFileDialog;
|
|
||||||
QFileDialog m_newFileDialog;
|
|
||||||
|
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
|
|
||||||
std::unique_ptr<VoiceAllocator> m_voxAllocator;
|
|
||||||
std::unique_ptr<amuse::Engine> m_engine;
|
|
||||||
amuse::ObjToken<amuse::Voice> m_lastSound;
|
|
||||||
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
|
|
||||||
int m_velocity = 90;
|
|
||||||
float m_pitch = 0.f;
|
|
||||||
int8_t m_ctrlVals[128] = {};
|
|
||||||
float m_auxAVol = 0.f;
|
|
||||||
float m_auxBVol = 0.f;
|
|
||||||
bool m_uiDisabled = false;
|
|
||||||
bool m_clipboardAmuseData = false;
|
|
||||||
|
|
||||||
QUndoStack* m_undoStack;
|
|
||||||
|
|
||||||
QMetaObject::Connection m_cutConn;
|
|
||||||
QMetaObject::Connection m_copyConn;
|
|
||||||
QMetaObject::Connection m_pasteConn;
|
|
||||||
QMetaObject::Connection m_deleteConn;
|
|
||||||
QMetaObject::Connection m_canEditConn;
|
|
||||||
|
|
||||||
BackgroundTask* m_backgroundTask = nullptr;
|
|
||||||
QProgressDialog* m_backgroundDialog = nullptr;
|
|
||||||
QThread m_backgroundThread;
|
|
||||||
|
|
||||||
uint64_t m_timerFireCount = 0;
|
|
||||||
int m_peakVoices = 0;
|
|
||||||
|
|
||||||
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
|
|
||||||
|
|
||||||
void updateWindowTitle();
|
|
||||||
void updateRecentFileActions();
|
|
||||||
void updateNavigationButtons();
|
|
||||||
bool setProjectPath(const QString& path);
|
|
||||||
void refreshAudioIO();
|
|
||||||
void refreshMIDIIO();
|
|
||||||
void timerEvent(QTimerEvent* ev);
|
|
||||||
void setSustain(bool sustain);
|
|
||||||
void keyPressEvent(QKeyEvent* ev);
|
|
||||||
void keyReleaseEvent(QKeyEvent* ev);
|
|
||||||
|
|
||||||
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
|
|
||||||
std::function<void(BackgroundTask&)>&& task);
|
|
||||||
|
|
||||||
bool _setEditor(EditorWidget* widget, bool appendNav = true);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget* parent = Q_NULLPTR);
|
explicit TreeDelegate(MainWindow& window, QObject* parent = Q_NULLPTR)
|
||||||
~MainWindow();
|
: QStyledItemDelegate(parent), m_window(window) {}
|
||||||
|
|
||||||
bool openProject(const QString& path);
|
|
||||||
|
|
||||||
bool openEditor(ProjectModel::SongGroupNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::SoundGroupNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::SoundMacroNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::ADSRNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::CurveNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::KeymapNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::LayersNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::SampleNode* node, bool appendNav = true);
|
|
||||||
bool openEditor(ProjectModel::INode* node, bool appendNav = true);
|
|
||||||
void closeEditor();
|
|
||||||
|
|
||||||
ProjectModel::INode* getEditorNode() const;
|
|
||||||
EditorWidget* getEditorWidget() const;
|
|
||||||
amuse::ObjToken<amuse::Voice> startEditorVoice(uint8_t key, uint8_t vel);
|
|
||||||
amuse::ObjToken<amuse::Voice> startSFX(amuse::GroupId groupId, amuse::SFXId sfxId);
|
|
||||||
amuse::ObjToken<amuse::Sequencer> startSong(amuse::GroupId groupId, amuse::SongId songId,
|
|
||||||
const unsigned char* arrData);
|
|
||||||
void pushUndoCommand(EditorUndoCommand* cmd);
|
|
||||||
void updateFocus();
|
|
||||||
void aboutToDeleteNode(ProjectModel::INode* node);
|
|
||||||
bool askAboutSave();
|
|
||||||
void closeEvent(QCloseEvent* ev);
|
|
||||||
void showEvent(QShowEvent* ev);
|
|
||||||
|
|
||||||
QString getGroupName(ProjectModel::GroupNode* group) const;
|
|
||||||
ProjectModel::GroupNode* getSelectedGroupNode() const;
|
|
||||||
QString getSelectedGroupName() const;
|
|
||||||
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
|
|
||||||
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
|
|
||||||
|
|
||||||
ProjectModel* projectModel() const { return m_projectModel; }
|
|
||||||
UIMessenger& uiMessenger() { return m_mainMessenger; }
|
|
||||||
|
|
||||||
void setItemEditFlags(AmuseItemEditFlags flags);
|
|
||||||
void setItemNewEnabled(bool enabled);
|
|
||||||
AmuseItemEditFlags outlineEditFlags();
|
|
||||||
bool isUiDisabled() const { return m_uiDisabled; }
|
|
||||||
void findUsages(ProjectModel::INode* node);
|
|
||||||
|
|
||||||
|
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||||
|
const QModelIndex& index);
|
||||||
public slots:
|
public slots:
|
||||||
void newAction();
|
void doExportGroup();
|
||||||
void _newAction(const QString& path);
|
void doFindUsages();
|
||||||
void openAction();
|
void doCut();
|
||||||
void _openAction(const QString& path);
|
void doCopy();
|
||||||
void openRecentFileAction();
|
void doPaste();
|
||||||
void clearRecentFilesAction();
|
void doDuplicate();
|
||||||
void saveAction();
|
void doDelete();
|
||||||
void revertAction();
|
void doRename();
|
||||||
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);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow {
|
||||||
|
friend class MIDIReader;
|
||||||
|
friend class ProjectModel;
|
||||||
|
friend class GroupNodeUndoCommand;
|
||||||
|
friend class TreeDelegate;
|
||||||
|
Q_OBJECT
|
||||||
|
Ui::MainWindow m_ui;
|
||||||
|
QAction* m_goBack;
|
||||||
|
QAction* m_goForward;
|
||||||
|
QLinkedList<ProjectModel::INode*> m_navList;
|
||||||
|
QLinkedList<ProjectModel::INode*>::iterator m_navIt;
|
||||||
|
QAction* m_clearRecentFileAct;
|
||||||
|
QAction* m_recentFileActs[MaxRecentFiles];
|
||||||
|
TreeDelegate m_treeDelegate;
|
||||||
|
UIMessenger m_mainMessenger;
|
||||||
|
ProjectModel* m_projectModel = nullptr;
|
||||||
|
QWidget* m_faceSvg;
|
||||||
|
SongGroupEditor* m_songGroupEditor = nullptr;
|
||||||
|
SoundGroupEditor* m_soundGroupEditor = nullptr;
|
||||||
|
SoundMacroEditor* m_soundMacroEditor = nullptr;
|
||||||
|
ADSREditor* m_adsrEditor = nullptr;
|
||||||
|
CurveEditor* m_curveEditor = nullptr;
|
||||||
|
KeymapEditor* m_keymapEditor = nullptr;
|
||||||
|
LayersEditor* m_layersEditor = nullptr;
|
||||||
|
SampleEditor* m_sampleEditor = nullptr;
|
||||||
|
StudioSetupWidget* m_studioSetup = nullptr;
|
||||||
|
QFileDialog m_openDirectoryDialog;
|
||||||
|
QFileDialog m_openFileDialog;
|
||||||
|
QFileDialog m_newFileDialog;
|
||||||
|
|
||||||
|
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
|
||||||
|
std::unique_ptr<VoiceAllocator> m_voxAllocator;
|
||||||
|
std::unique_ptr<amuse::Engine> m_engine;
|
||||||
|
amuse::ObjToken<amuse::Voice> m_lastSound;
|
||||||
|
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
|
||||||
|
int m_velocity = 90;
|
||||||
|
float m_pitch = 0.f;
|
||||||
|
int8_t m_ctrlVals[128] = {};
|
||||||
|
float m_auxAVol = 0.f;
|
||||||
|
float m_auxBVol = 0.f;
|
||||||
|
bool m_uiDisabled = false;
|
||||||
|
bool m_clipboardAmuseData = false;
|
||||||
|
|
||||||
|
QUndoStack* m_undoStack;
|
||||||
|
|
||||||
|
QMetaObject::Connection m_cutConn;
|
||||||
|
QMetaObject::Connection m_copyConn;
|
||||||
|
QMetaObject::Connection m_pasteConn;
|
||||||
|
QMetaObject::Connection m_deleteConn;
|
||||||
|
QMetaObject::Connection m_canEditConn;
|
||||||
|
|
||||||
|
BackgroundTask* m_backgroundTask = nullptr;
|
||||||
|
QProgressDialog* m_backgroundDialog = nullptr;
|
||||||
|
QThread m_backgroundThread;
|
||||||
|
|
||||||
|
uint64_t m_timerFireCount = 0;
|
||||||
|
int m_peakVoices = 0;
|
||||||
|
|
||||||
|
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
|
||||||
|
|
||||||
|
void updateWindowTitle();
|
||||||
|
void updateRecentFileActions();
|
||||||
|
void updateNavigationButtons();
|
||||||
|
bool setProjectPath(const QString& path);
|
||||||
|
void refreshAudioIO();
|
||||||
|
void refreshMIDIIO();
|
||||||
|
void timerEvent(QTimerEvent* ev);
|
||||||
|
void setSustain(bool sustain);
|
||||||
|
void keyPressEvent(QKeyEvent* ev);
|
||||||
|
void keyReleaseEvent(QKeyEvent* ev);
|
||||||
|
|
||||||
|
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
|
||||||
|
std::function<void(BackgroundTask&)>&& task);
|
||||||
|
|
||||||
|
bool _setEditor(EditorWidget* widget, bool appendNav = true);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget* parent = Q_NULLPTR);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
bool openProject(const QString& path);
|
||||||
|
|
||||||
|
bool openEditor(ProjectModel::SongGroupNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::SoundGroupNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::SoundMacroNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::ADSRNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::CurveNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::KeymapNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::LayersNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::SampleNode* node, bool appendNav = true);
|
||||||
|
bool openEditor(ProjectModel::INode* node, bool appendNav = true);
|
||||||
|
void closeEditor();
|
||||||
|
|
||||||
|
ProjectModel::INode* getEditorNode() const;
|
||||||
|
EditorWidget* getEditorWidget() const;
|
||||||
|
amuse::ObjToken<amuse::Voice> startEditorVoice(uint8_t key, uint8_t vel);
|
||||||
|
amuse::ObjToken<amuse::Voice> startSFX(amuse::GroupId groupId, amuse::SFXId sfxId);
|
||||||
|
amuse::ObjToken<amuse::Sequencer> startSong(amuse::GroupId groupId, amuse::SongId songId,
|
||||||
|
const unsigned char* arrData);
|
||||||
|
void pushUndoCommand(EditorUndoCommand* cmd);
|
||||||
|
void updateFocus();
|
||||||
|
void aboutToDeleteNode(ProjectModel::INode* node);
|
||||||
|
bool askAboutSave();
|
||||||
|
void closeEvent(QCloseEvent* ev);
|
||||||
|
void showEvent(QShowEvent* ev);
|
||||||
|
|
||||||
|
QString getGroupName(ProjectModel::GroupNode* group) const;
|
||||||
|
ProjectModel::GroupNode* getSelectedGroupNode() const;
|
||||||
|
QString getSelectedGroupName() const;
|
||||||
|
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
|
||||||
|
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
|
||||||
|
|
||||||
|
ProjectModel* projectModel() const { return m_projectModel; }
|
||||||
|
UIMessenger& uiMessenger() { return m_mainMessenger; }
|
||||||
|
|
||||||
|
void setItemEditFlags(AmuseItemEditFlags flags);
|
||||||
|
void setItemNewEnabled(bool enabled);
|
||||||
|
AmuseItemEditFlags outlineEditFlags();
|
||||||
|
bool isUiDisabled() const { return m_uiDisabled; }
|
||||||
|
void findUsages(ProjectModel::INode* node);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void newAction();
|
||||||
|
void _newAction(const QString& path);
|
||||||
|
void openAction();
|
||||||
|
void _openAction(const QString& path);
|
||||||
|
void openRecentFileAction();
|
||||||
|
void clearRecentFilesAction();
|
||||||
|
void saveAction();
|
||||||
|
void revertAction();
|
||||||
|
void reloadSampleDataAction();
|
||||||
|
void importAction();
|
||||||
|
void _importAction(const QString& path);
|
||||||
|
void importSongsAction();
|
||||||
|
void _importSongsAction(const QString& path);
|
||||||
|
void exportAction();
|
||||||
|
void importHeadersAction();
|
||||||
|
void _importHeadersAction(const QString& path);
|
||||||
|
void exportHeadersAction();
|
||||||
|
void _exportHeadersAction(const QString& path);
|
||||||
|
|
||||||
|
void newSubprojectAction();
|
||||||
|
void newSFXGroupAction();
|
||||||
|
void newSongGroupAction();
|
||||||
|
void newSoundMacroAction();
|
||||||
|
void newADSRAction();
|
||||||
|
void newCurveAction();
|
||||||
|
void newKeymapAction();
|
||||||
|
void newLayersAction();
|
||||||
|
|
||||||
|
void goForward();
|
||||||
|
void goBack();
|
||||||
|
|
||||||
|
void aboutToShowAudioIOMenu();
|
||||||
|
void aboutToShowMIDIIOMenu();
|
||||||
|
|
||||||
|
void setAudioIO();
|
||||||
|
void setMIDIIO(bool checked);
|
||||||
|
|
||||||
|
void aboutAmuseAction();
|
||||||
|
void aboutQtAction();
|
||||||
|
|
||||||
|
void notePressed(int key);
|
||||||
|
void noteReleased();
|
||||||
|
void velocityChanged(int vel);
|
||||||
|
void modulationChanged(int mod);
|
||||||
|
void pitchChanged(int pitch);
|
||||||
|
void killSounds();
|
||||||
|
void fxPressed();
|
||||||
|
void volumeChanged(int vol);
|
||||||
|
void auxAChanged(int vol);
|
||||||
|
void auxBChanged(int vol);
|
||||||
|
|
||||||
|
void outlineCutAction();
|
||||||
|
void outlineCopyAction();
|
||||||
|
void outlinePasteAction();
|
||||||
|
void outlineDeleteAction();
|
||||||
|
|
||||||
|
void onFocusChanged(QWidget* old, QWidget* now);
|
||||||
|
void onClipboardChanged();
|
||||||
|
void outlineItemActivated(const QModelIndex& index);
|
||||||
|
void onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
|
||||||
|
void onTextSelect();
|
||||||
|
void onTextDelete();
|
||||||
|
void cleanChanged(bool clean);
|
||||||
|
|
||||||
|
void studioSetupHidden();
|
||||||
|
void studioSetupShown();
|
||||||
|
|
||||||
|
void onBackgroundTaskFinished(int id);
|
||||||
|
|
||||||
|
QMessageBox::StandardButton msgInformation(const QString& title, const QString& text,
|
||||||
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||||
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
|
int msgQuestion(const QString& title, const QString& text, const QString& button0Text,
|
||||||
|
const QString& button1Text = QString(), const QString& button2Text = QString(),
|
||||||
|
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
|
||||||
|
QMessageBox::StandardButton
|
||||||
|
msgQuestion(const QString& title, const QString& text,
|
||||||
|
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||||
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
|
QMessageBox::StandardButton msgWarning(const QString& title, const QString& text,
|
||||||
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||||
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
|
QMessageBox::StandardButton msgCritical(const QString& title, const QString& text,
|
||||||
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
|
||||||
|
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||||
|
};
|
||||||
|
|
|
@ -3,285 +3,155 @@
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include "amuse/Common.hpp"
|
#include "amuse/Common.hpp"
|
||||||
|
|
||||||
static const uint32_t BasicMacro[] =
|
static const uint32_t BasicMacro[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF,
|
||||||
0x38000000, 0xC07A1000,
|
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07010001, 0x0000FFFF,
|
|
||||||
0x11000000, 0x00000000,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t Looped[] =
|
static const uint32_t Looped[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x38000000,
|
||||||
0x38000000, 0xC07A1000,
|
0xE8030000, 0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07010001, 0x0000FFFF,
|
|
||||||
0x3000D08A, 0x00000000,
|
|
||||||
0x38000000, 0xE8030000,
|
|
||||||
0x0F000000, 0x0001E803,
|
|
||||||
0x07000001, 0x0000E803,
|
|
||||||
0x11000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t LoopRelease[] =
|
static const uint32_t LoopRelease[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001FA00, 0x07010000, 0x0000FFFF,
|
||||||
0x38000000, 0xC07A1000,
|
0x3000D08A, 0x00000000, 0x0F500000, 0x00013200, 0x07000000, 0x00003200, 0x38000000, 0xE8030000,
|
||||||
0x10000000, 0x00000000,
|
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 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[] =
|
static const uint32_t LoopSoftRelease[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000, 0x0000FFFF,
|
||||||
0x38000000, 0xC07A1000,
|
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
|
||||||
0x10000000, 0x00000000,
|
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 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[] =
|
static const uint32_t LoopSoftReleaseNoClick[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000,
|
||||||
0x38000000, 0xC07A1000,
|
0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
|
||||||
0x147F0000, 0x00010100,
|
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
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[] =
|
static const uint32_t LoopADSR[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
|
||||||
0x38000000, 0xC07A1000,
|
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x12000000, 0x00000000, 0x07000001, 0x0000E803,
|
||||||
0x0C000000, 0x00000000,
|
0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 0x00000000,
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x1C000F01, 0x0001C800,
|
|
||||||
0x07010001, 0x0000FFFF,
|
|
||||||
0x3000D08A, 0x00000000,
|
|
||||||
0x12000000, 0x00000000,
|
|
||||||
0x07000001, 0x0000E803,
|
|
||||||
0x38000000, 0xE8030000,
|
|
||||||
0x07000001, 0x0000E803,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t LoopADSRSoftRelease[] =
|
static const uint32_t LoopADSRSoftRelease[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
|
||||||
0x38000000, 0xC07A1000,
|
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00017800, 0x07000001, 0x00007800,
|
||||||
0x0C000000, 0x00000000,
|
0x12000000, 0x00000000, 0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 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[] =
|
static const uint32_t LoopHold[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010001, 0x0000FFFF,
|
||||||
0x38000000, 0xC07A1000,
|
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000001, 0x00006400, 0x38000000, 0xE8030000,
|
||||||
0x10000000, 0x00000000,
|
0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 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[] =
|
static const uint32_t OneShot[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000001,
|
||||||
0x38000000, 0xC07A1000,
|
0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07000001, 0x0000FFFF,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t OneShotFixedNote[] =
|
static const uint32_t OneShotFixedNote[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x193C0000, 0x00010000, 0x10000000, 0x00000000,
|
||||||
0x38000000, 0xC07A1000,
|
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x193C0000, 0x00010000,
|
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07000001, 0x0000FFFF,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t OneShotNoClick[] =
|
static const uint32_t OneShotNoClick[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000,
|
||||||
0x38000000, 0xC07A1000,
|
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x147F0000, 0x00010100,
|
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07000001, 0x0000FFFF,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t OneShotFixedNoClick[] =
|
static const uint32_t OneShotFixedNoClick[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x193C0000, 0x00010000, 0x10000000,
|
||||||
0x38000000, 0xC07A1000,
|
0x00000000, 0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x147F0000, 0x00010100,
|
|
||||||
0x193C0000, 0x00010000,
|
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07000001, 0x0000FFFF,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t Bubbles[] =
|
static const uint32_t Bubbles[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x0D600000, 0x00010000, 0x10000000, 0x00000000, 0x1D08E803,
|
||||||
0x38000000, 0xC07A1000,
|
0x00010000, 0x1E0544FD, 0x00010000, 0x0F000000, 0x0001F401, 0x07000000, 0x0000F401,
|
||||||
0x0D600000, 0x00010000,
|
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x1D08E803, 0x00010000,
|
|
||||||
0x1E0544FD, 0x00010000,
|
|
||||||
0x0F000000, 0x0001F401,
|
|
||||||
0x07000000, 0x0000F401,
|
|
||||||
0x11000000, 0x00000000,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t DownTrigger[] =
|
static const uint32_t DownTrigger[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000000, 0x00001200, 0x11000000, 0x00000000,
|
||||||
0x38000000, 0xC07A1000,
|
0x07000000, 0x00000100, 0x18FE0000, 0x00010000, 0x05000000, 0x01000C00, 0x0F000000, 0x00016400,
|
||||||
0x10000000, 0x00000000,
|
0x07000001, 0x00006400, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x07000000, 0x00001200,
|
|
||||||
0x11000000, 0x00000000,
|
|
||||||
0x07000000, 0x00000100,
|
|
||||||
0x18FE0000, 0x00010000,
|
|
||||||
0x05000000, 0x01000C00,
|
|
||||||
0x0F000000, 0x00016400,
|
|
||||||
0x07000001, 0x00006400,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t LongFadeInAndStop[] =
|
static const uint32_t LongFadeInAndStop[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x147F0000, 0x0001E803, 0x10000000, 0x00000000, 0x07000001,
|
||||||
0x38000000, 0xC07A1000,
|
0x0000E803, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x147F0000, 0x0001E803,
|
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07000001, 0x0000E803,
|
|
||||||
0x11000000, 0x00000000,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t FadeInAndStop[] =
|
static const uint32_t FadeInAndStop[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x147F0000, 0x0001C800, 0x10000000, 0x00000000, 0x07000001,
|
||||||
0x38000000, 0xC07A1000,
|
0x0000C800, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x147F0000, 0x0001C800,
|
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x07000001, 0x0000C800,
|
|
||||||
0x11000000, 0x00000000,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t RandomTrigger[] =
|
static const uint32_t RandomTrigger[] = {
|
||||||
{
|
0x38000000, 0xC07A1000, 0x0F000000, 0x0001F401, 0x195A0000, 0x00010000, 0x10000000,
|
||||||
0x38000000, 0xC07A1000,
|
0x00000000, 0x1750005A, 0x01000000, 0x07000001, 0x00002300, 0x05000000, 0x03001400,
|
||||||
0x0F000000, 0x0001F401,
|
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
0x195A0000, 0x00010000,
|
|
||||||
0x10000000, 0x00000000,
|
|
||||||
0x1750005A, 0x01000000,
|
|
||||||
0x07000001, 0x00002300,
|
|
||||||
0x05000000, 0x03001400,
|
|
||||||
0x11000000, 0x00000000,
|
|
||||||
0x31000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t SimplePlaySample[] =
|
static const uint32_t SimplePlaySample[] = {
|
||||||
{
|
0x10000000,
|
||||||
0x10000000, 0x00000000,
|
0x00000000,
|
||||||
0x00000000, 0x00000000,
|
0x00000000,
|
||||||
|
0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const SoundMacroTemplateEntry Entries[] =
|
static const SoundMacroTemplateEntry Entries[] = {
|
||||||
{
|
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Basic Macro"), sizeof(BasicMacro), BasicMacro},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Basic Macro"), sizeof(BasicMacro), BasicMacro},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Looped"), sizeof(Looped), Looped},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Looped"), sizeof(Looped), Looped},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Release"), sizeof(LoopRelease), LoopRelease},
|
{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"), sizeof(LoopSoftRelease), LoopSoftRelease},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release No Click"), sizeof(LoopSoftReleaseNoClick), LoopSoftReleaseNoClick},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release No Click"), sizeof(LoopSoftReleaseNoClick),
|
||||||
|
LoopSoftReleaseNoClick},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR"), sizeof(LoopADSR), LoopADSR},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR"), sizeof(LoopADSR), LoopADSR},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR Soft Release"), sizeof(LoopADSRSoftRelease), LoopADSRSoftRelease},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR Soft Release"), sizeof(LoopADSRSoftRelease),
|
||||||
|
LoopADSRSoftRelease},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Hold"), sizeof(LoopHold), LoopHold},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Hold"), sizeof(LoopHold), LoopHold},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot"), sizeof(OneShot), OneShot},
|
{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 Fixed Note"), sizeof(OneShotFixedNote), OneShotFixedNote},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot No Click"), sizeof(OneShotNoClick), OneShotNoClick},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot No Click"), sizeof(OneShotNoClick), OneShotNoClick},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed No Click"), sizeof(OneShotFixedNoClick), OneShotFixedNoClick},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed No Click"), sizeof(OneShotFixedNoClick),
|
||||||
|
OneShotFixedNoClick},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Bubbles"), sizeof(Bubbles), Bubbles},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Bubbles"), sizeof(Bubbles), Bubbles},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Down Trigger"), sizeof(DownTrigger), DownTrigger},
|
{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", "Long Fade in and Stop"), sizeof(LongFadeInAndStop), LongFadeInAndStop},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Fade in and Stop"), sizeof(FadeInAndStop), FadeInAndStop},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Fade in and Stop"), sizeof(FadeInAndStop), FadeInAndStop},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Random Trigger"), sizeof(RandomTrigger), RandomTrigger},
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Random Trigger"), sizeof(RandomTrigger), RandomTrigger},
|
||||||
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Simple Play Sample"), sizeof(SimplePlaySample), SimplePlaySample}
|
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Simple Play Sample"), sizeof(SimplePlaySample), SimplePlaySample}};
|
||||||
};
|
|
||||||
|
|
||||||
const SoundMacroTemplateEntry* NewSoundMacroDialog::getSelectedTemplate() const
|
const SoundMacroTemplateEntry* NewSoundMacroDialog::getSelectedTemplate() const {
|
||||||
{
|
return &Entries[m_combo.currentIndex()];
|
||||||
return &Entries[m_combo.currentIndex()];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NewSoundMacroDialog::NewSoundMacroDialog(const QString& groupName, QWidget* parent)
|
NewSoundMacroDialog::NewSoundMacroDialog(const QString& groupName, QWidget* parent)
|
||||||
: QDialog(parent),
|
: QDialog(parent)
|
||||||
m_le(QString::fromStdString(amuse::SoundMacroId::CurNameDB->generateDefaultName(amuse::NameDB::Type::SoundMacro))),
|
, m_le(QString::fromStdString(amuse::SoundMacroId::CurNameDB->generateDefaultName(amuse::NameDB::Type::SoundMacro)))
|
||||||
m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal)
|
, m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal) {
|
||||||
{
|
setWindowTitle(tr("New Sound Macro"));
|
||||||
setWindowTitle(tr("New Sound Macro"));
|
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (const auto& ent : Entries)
|
for (const auto& ent : Entries)
|
||||||
m_combo.addItem(tr(ent.m_name), idx++);
|
m_combo.addItem(tr(ent.m_name), idx++);
|
||||||
m_combo.setCurrentIndex(0);
|
m_combo.setCurrentIndex(0);
|
||||||
|
|
||||||
QObject::connect(&m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
QObject::connect(&m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
QObject::connect(&m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
QObject::connect(&m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout;
|
QVBoxLayout* layout = new QVBoxLayout;
|
||||||
|
|
||||||
layout->addWidget(new QLabel(tr("What should the new macro in %1 be named?").arg(groupName)));
|
layout->addWidget(new QLabel(tr("What should the new macro in %1 be named?").arg(groupName)));
|
||||||
layout->addWidget(&m_le);
|
layout->addWidget(&m_le);
|
||||||
layout->addWidget(new QLabel(tr("Sound Macro Template")));
|
layout->addWidget(new QLabel(tr("Sound Macro Template")));
|
||||||
layout->addWidget(&m_combo);
|
layout->addWidget(&m_combo);
|
||||||
layout->addWidget(&m_buttonBox);
|
layout->addWidget(&m_buttonBox);
|
||||||
|
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,20 @@
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
|
|
||||||
struct SoundMacroTemplateEntry
|
struct SoundMacroTemplateEntry {
|
||||||
{
|
const char* m_name;
|
||||||
const char* m_name;
|
size_t m_length;
|
||||||
size_t m_length;
|
const uint32_t* m_data;
|
||||||
const uint32_t* m_data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NewSoundMacroDialog : public QDialog
|
class NewSoundMacroDialog : public QDialog {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QLineEdit m_le;
|
||||||
QLineEdit m_le;
|
QComboBox m_combo;
|
||||||
QComboBox m_combo;
|
QDialogButtonBox m_buttonBox;
|
||||||
QDialogButtonBox m_buttonBox;
|
|
||||||
public:
|
public:
|
||||||
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
|
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
|
||||||
QString getName() const { return m_le.text(); }
|
QString getName() const { return m_le.text(); }
|
||||||
const SoundMacroTemplateEntry* getSelectedTemplate() const;
|
const SoundMacroTemplateEntry* getSelectedTemplate() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,498 +17,451 @@
|
||||||
class ProjectModel;
|
class ProjectModel;
|
||||||
class EditorUndoCommand;
|
class EditorUndoCommand;
|
||||||
|
|
||||||
enum AmuseItemEditFlags
|
enum AmuseItemEditFlags {
|
||||||
{
|
AmuseItemNone = 0,
|
||||||
AmuseItemNone = 0,
|
AmuseItemCut = 1,
|
||||||
AmuseItemCut = 1,
|
AmuseItemCopy = 2,
|
||||||
AmuseItemCopy = 2,
|
AmuseItemPaste = 4,
|
||||||
AmuseItemPaste = 4,
|
AmuseItemDelete = 8,
|
||||||
AmuseItemDelete = 8,
|
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
|
||||||
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
|
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
|
||||||
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class OutlineFilterProxyModel : public QSortFilterProxyModel
|
class OutlineFilterProxyModel : public QSortFilterProxyModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QRegExp m_usageKey;
|
||||||
QRegExp m_usageKey;
|
|
||||||
public:
|
public:
|
||||||
explicit OutlineFilterProxyModel(ProjectModel* source);
|
explicit OutlineFilterProxyModel(ProjectModel* source);
|
||||||
public slots:
|
public slots:
|
||||||
void setFilterRegExp(const QString &pattern);
|
void setFilterRegExp(const QString& pattern);
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullItemProxyModel : public QIdentityProxyModel
|
class NullItemProxyModel : public QIdentityProxyModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit NullItemProxyModel(ProjectModel* source);
|
explicit NullItemProxyModel(ProjectModel* source);
|
||||||
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
|
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
|
||||||
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
|
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
|
||||||
int rowCount(const QModelIndex& parent) const;
|
int rowCount(const QModelIndex& parent) const;
|
||||||
QModelIndex index(int row, int column, const QModelIndex& parent) const;
|
QModelIndex index(int row, int column, const QModelIndex& parent) const;
|
||||||
QVariant data(const QModelIndex& proxyIndex, int role) const;
|
QVariant data(const QModelIndex& proxyIndex, int role) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PageObjectProxyModel : public QIdentityProxyModel
|
class PageObjectProxyModel : public QIdentityProxyModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit PageObjectProxyModel(ProjectModel* source);
|
explicit PageObjectProxyModel(ProjectModel* source);
|
||||||
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
|
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
|
||||||
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
|
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
|
||||||
QModelIndex parent(const QModelIndex& child) const;
|
QModelIndex parent(const QModelIndex& child) const;
|
||||||
int rowCount(const QModelIndex& parent) const;
|
int rowCount(const QModelIndex& parent) const;
|
||||||
QModelIndex index(int row, int column, const QModelIndex& parent) const;
|
QModelIndex index(int row, int column, const QModelIndex& parent) const;
|
||||||
QVariant data(const QModelIndex& proxyIndex, int role) const;
|
QVariant data(const QModelIndex& proxyIndex, int role) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const;
|
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProjectModel : public QAbstractItemModel
|
class ProjectModel : public QAbstractItemModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
enum class ImportMode
|
enum class ImportMode { Original, WAVs, Both };
|
||||||
{
|
|
||||||
Original,
|
|
||||||
WAVs,
|
|
||||||
Both
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NameUndoRegistry
|
struct NameUndoRegistry {
|
||||||
{
|
std::unordered_map<amuse::SongId, std::string> m_songIDs;
|
||||||
std::unordered_map<amuse::SongId, std::string> m_songIDs;
|
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
|
||||||
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
|
void registerSongName(amuse::SongId id) const;
|
||||||
void registerSongName(amuse::SongId id) const;
|
void unregisterSongName(amuse::SongId id);
|
||||||
void unregisterSongName(amuse::SongId id);
|
void registerSFXName(amuse::SongId id) const;
|
||||||
void registerSFXName(amuse::SongId id) const;
|
void unregisterSFXName(amuse::SongId id);
|
||||||
void unregisterSFXName(amuse::SongId id);
|
void clear() {
|
||||||
void clear()
|
m_songIDs.clear();
|
||||||
{
|
m_sfxIDs.clear();
|
||||||
m_songIDs.clear();
|
}
|
||||||
m_sfxIDs.clear();
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
OutlineFilterProxyModel m_outlineProxy;
|
OutlineFilterProxyModel m_outlineProxy;
|
||||||
NullItemProxyModel m_nullProxy;
|
NullItemProxyModel m_nullProxy;
|
||||||
PageObjectProxyModel m_pageObjectProxy;
|
PageObjectProxyModel m_pageObjectProxy;
|
||||||
|
|
||||||
amuse::ProjectDatabase m_projectDatabase;
|
amuse::ProjectDatabase m_projectDatabase;
|
||||||
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>> m_groups;
|
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>> m_groups;
|
||||||
|
|
||||||
struct Song
|
struct Song {
|
||||||
{
|
QString m_path;
|
||||||
QString m_path;
|
int m_refCount = 0;
|
||||||
int m_refCount = 0;
|
};
|
||||||
};
|
std::unordered_map<amuse::SongId, Song> m_midiFiles;
|
||||||
std::unordered_map<amuse::SongId, Song> m_midiFiles;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class INode : public amuse::IObj
|
class INode : public amuse::IObj {
|
||||||
{
|
friend class ProjectModel;
|
||||||
friend class ProjectModel;
|
virtual void _sortChildren();
|
||||||
virtual void _sortChildren();
|
|
||||||
public:
|
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
Null,
|
|
||||||
Root,
|
|
||||||
Group, // Top-level group
|
|
||||||
SongGroup,
|
|
||||||
SoundGroup,
|
|
||||||
Collection, // Classified object collection, one of the following:
|
|
||||||
SoundMacro,
|
|
||||||
ADSR,
|
|
||||||
Curve,
|
|
||||||
Keymap,
|
|
||||||
Layer,
|
|
||||||
Sample
|
|
||||||
};
|
|
||||||
protected:
|
|
||||||
QString m_name;
|
|
||||||
INode* m_parent = nullptr;
|
|
||||||
int m_row = -1;
|
|
||||||
std::vector<amuse::IObjToken<INode>> m_children;
|
|
||||||
amuse::IObjToken<INode> m_nullChild;
|
|
||||||
public:
|
|
||||||
virtual ~INode() = default;
|
|
||||||
INode(const QString& name);
|
|
||||||
INode(INode* parent) : m_parent(parent), m_row(0)
|
|
||||||
{
|
|
||||||
/* ONLY USED BY NULL NODE! */
|
|
||||||
}
|
|
||||||
|
|
||||||
int childCount() const { return int(m_children.size()); }
|
public:
|
||||||
INode* child(int row) const
|
enum class Type {
|
||||||
{
|
Null,
|
||||||
if (row == m_children.size())
|
Root,
|
||||||
return nullChild();
|
Group, // Top-level group
|
||||||
return m_children[row].get();
|
SongGroup,
|
||||||
}
|
SoundGroup,
|
||||||
INode* nullChild() const { return m_nullChild.get(); }
|
Collection, // Classified object collection, one of the following:
|
||||||
INode* parent() const { return m_parent; }
|
SoundMacro,
|
||||||
int row() const { return m_row; }
|
ADSR,
|
||||||
|
Curve,
|
||||||
void reindexRows(int row)
|
Keymap,
|
||||||
{
|
Layer,
|
||||||
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
|
Sample
|
||||||
(*it)->m_row = row++;
|
|
||||||
m_nullChild->m_row = row;
|
|
||||||
}
|
|
||||||
|
|
||||||
void insertChild(amuse::ObjToken<INode> n)
|
|
||||||
{
|
|
||||||
assert(n->m_parent == nullptr && "Inserting already-parented node");
|
|
||||||
n->m_parent = this;
|
|
||||||
int row = hypotheticalIndex(n->name());
|
|
||||||
m_children.insert(m_children.begin() + row, n.get());
|
|
||||||
reindexRows(row);
|
|
||||||
}
|
|
||||||
amuse::ObjToken<INode> removeChild(INode* n)
|
|
||||||
{
|
|
||||||
amuse::ObjToken<INode> ret = n;
|
|
||||||
int row = ret->row();
|
|
||||||
assert(ret.get() == m_children.at(row).get() && "Removing non-child from node");
|
|
||||||
m_children.erase(m_children.begin() + row);
|
|
||||||
reindexRows(row);
|
|
||||||
ret->m_parent = nullptr;
|
|
||||||
ret->m_row = -1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reserve(size_t sz) { m_children.reserve(sz); }
|
|
||||||
template<class T, class... _Args>
|
|
||||||
T& makeChild(_Args&&... args)
|
|
||||||
{
|
|
||||||
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
|
|
||||||
insertChild(tok.get());
|
|
||||||
return static_cast<T&>(*tok);
|
|
||||||
}
|
|
||||||
template<class T, class... _Args>
|
|
||||||
T& _appendChild(_Args&&... args)
|
|
||||||
{
|
|
||||||
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
|
|
||||||
tok->m_parent = this;
|
|
||||||
tok->m_row = m_children.size();
|
|
||||||
m_children.push_back(tok.get());
|
|
||||||
m_nullChild->m_row = m_children.size();
|
|
||||||
return static_cast<T&>(*tok);
|
|
||||||
}
|
|
||||||
|
|
||||||
INode* findChild(const QString& name) const
|
|
||||||
{
|
|
||||||
int idx = hypotheticalIndex(name);
|
|
||||||
if (idx >= m_children.size())
|
|
||||||
return nullptr;
|
|
||||||
INode* ret = m_children[idx].get();
|
|
||||||
if (ret->name() == name)
|
|
||||||
return ret;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool depthTraverse(const std::function<bool(INode* node)>& func)
|
|
||||||
{
|
|
||||||
for (auto& n : m_children)
|
|
||||||
if (!n->depthTraverse(func))
|
|
||||||
break;
|
|
||||||
return func(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oneLevelTraverse(const std::function<bool(INode* node)>& func)
|
|
||||||
{
|
|
||||||
for (auto& n : m_children)
|
|
||||||
if (!func(n.get()))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString& name() const { return m_name; }
|
|
||||||
virtual int hypotheticalIndex(const QString& name) const;
|
|
||||||
|
|
||||||
virtual amuse::NameDB* getNameDb() const { return nullptr; }
|
|
||||||
|
|
||||||
virtual Type type() const = 0;
|
|
||||||
virtual QString text() const = 0;
|
|
||||||
virtual QIcon icon() const = 0;
|
|
||||||
virtual Qt::ItemFlags flags() const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; }
|
|
||||||
virtual AmuseItemEditFlags editFlags() const { return AmuseItemNone; }
|
|
||||||
|
|
||||||
virtual void registerNames(const NameUndoRegistry& registry) const {}
|
|
||||||
virtual void unregisterNames(NameUndoRegistry& registry) const {}
|
|
||||||
};
|
};
|
||||||
struct NullNode final : INode
|
|
||||||
{
|
|
||||||
NullNode(INode* parent) : INode(parent) {}
|
|
||||||
|
|
||||||
Type type() const { return Type::Null; }
|
protected:
|
||||||
QString text() const { return {}; }
|
QString m_name;
|
||||||
QIcon icon() const { return {}; }
|
INode* m_parent = nullptr;
|
||||||
};
|
int m_row = -1;
|
||||||
struct RootNode final : INode
|
std::vector<amuse::IObjToken<INode>> m_children;
|
||||||
{
|
amuse::IObjToken<INode> m_nullChild;
|
||||||
RootNode() : INode(QStringLiteral("<root>")) {}
|
|
||||||
|
|
||||||
Type type() const { return Type::Root; }
|
public:
|
||||||
QString text() const { return {}; }
|
virtual ~INode() = default;
|
||||||
QIcon icon() const { return {}; }
|
INode(const QString& name);
|
||||||
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
|
INode(INode* parent) : m_parent(parent), m_row(0) { /* ONLY USED BY NULL NODE! */
|
||||||
};
|
}
|
||||||
struct CollectionNode;
|
|
||||||
struct BasePoolObjectNode;
|
|
||||||
struct GroupNode final : INode
|
|
||||||
{
|
|
||||||
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
|
|
||||||
GroupNode(const QString& name) : INode(name) {}
|
|
||||||
GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
|
|
||||||
: INode(it->first), m_it(it) {}
|
|
||||||
|
|
||||||
int hypotheticalIndex(const QString& name) const;
|
int childCount() const { return int(m_children.size()); }
|
||||||
void _sortChildren();
|
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; }
|
||||||
|
|
||||||
static QIcon Icon;
|
void reindexRows(int row) {
|
||||||
Type type() const { return Type::Group; }
|
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
|
||||||
QString text() const { return m_name; }
|
(*it)->m_row = row++;
|
||||||
QIcon icon() const { return Icon; }
|
m_nullChild->m_row = row;
|
||||||
AmuseItemEditFlags editFlags() const { return AmuseItemNoCut; }
|
}
|
||||||
|
|
||||||
CollectionNode* getCollectionOfType(Type tp) const;
|
void insertChild(amuse::ObjToken<INode> n) {
|
||||||
amuse::AudioGroupDatabase* getAudioGroup() const { return m_it->second.get(); }
|
assert(n->m_parent == nullptr && "Inserting already-parented node");
|
||||||
BasePoolObjectNode* pageObjectNodeOfId(amuse::ObjectId id) const;
|
n->m_parent = this;
|
||||||
};
|
int row = hypotheticalIndex(n->name());
|
||||||
struct SongGroupNode final : INode
|
m_children.insert(m_children.begin() + row, n.get());
|
||||||
{
|
reindexRows(row);
|
||||||
amuse::GroupId m_id;
|
}
|
||||||
amuse::ObjToken<amuse::SongGroupIndex> m_index;
|
amuse::ObjToken<INode> removeChild(INode* n) {
|
||||||
SongGroupNode(const QString& name, amuse::ObjToken<amuse::SongGroupIndex> index)
|
amuse::ObjToken<INode> ret = n;
|
||||||
: INode(name), m_index(index) {}
|
int row = ret->row();
|
||||||
SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
|
assert(ret.get() == m_children.at(row).get() && "Removing non-child from node");
|
||||||
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
|
m_children.erase(m_children.begin() + row);
|
||||||
|
reindexRows(row);
|
||||||
|
ret->m_parent = nullptr;
|
||||||
|
ret->m_row = -1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static QIcon Icon;
|
void reserve(size_t sz) { m_children.reserve(sz); }
|
||||||
Type type() const { return Type::SongGroup; }
|
template <class T, class... _Args>
|
||||||
QString text() const { return m_name; }
|
T& makeChild(_Args&&... args) {
|
||||||
QIcon icon() const { return Icon; }
|
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
|
||||||
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void registerNames(const NameUndoRegistry& registry) const
|
bool depthTraverse(const std::function<bool(INode* node)>& func) {
|
||||||
{
|
for (auto& n : m_children)
|
||||||
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
if (!n->depthTraverse(func))
|
||||||
for (auto& p : m_index->m_midiSetups)
|
break;
|
||||||
registry.registerSongName(p.first);
|
return func(this);
|
||||||
}
|
}
|
||||||
void unregisterNames(NameUndoRegistry& registry) const
|
|
||||||
{
|
|
||||||
amuse::GroupId::CurNameDB->remove(m_id);
|
|
||||||
for (auto& p : m_index->m_midiSetups)
|
|
||||||
registry.unregisterSongName(p.first);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct SoundGroupNode final : INode
|
|
||||||
{
|
|
||||||
amuse::GroupId m_id;
|
|
||||||
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
|
|
||||||
SoundGroupNode(const QString& name, amuse::ObjToken<amuse::SFXGroupIndex> index)
|
|
||||||
: INode(name), m_index(index) {}
|
|
||||||
SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
|
|
||||||
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
|
|
||||||
|
|
||||||
static QIcon Icon;
|
bool oneLevelTraverse(const std::function<bool(INode* node)>& func) {
|
||||||
Type type() const { return Type::SoundGroup; }
|
for (auto& n : m_children)
|
||||||
QString text() const { return m_name; }
|
if (!func(n.get()))
|
||||||
QIcon icon() const { return Icon; }
|
return false;
|
||||||
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
|
const QString& name() const { return m_name; }
|
||||||
|
virtual int hypotheticalIndex(const QString& name) const;
|
||||||
|
|
||||||
void registerNames(const NameUndoRegistry& registry) const
|
virtual amuse::NameDB* getNameDb() const { return nullptr; }
|
||||||
{
|
|
||||||
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
|
||||||
for (auto& p : m_index->m_sfxEntries)
|
|
||||||
registry.registerSFXName(p.first);
|
|
||||||
}
|
|
||||||
void unregisterNames(NameUndoRegistry& registry) const
|
|
||||||
{
|
|
||||||
amuse::GroupId::CurNameDB->remove(m_id);
|
|
||||||
for (auto& p : m_index->m_sfxEntries)
|
|
||||||
registry.unregisterSFXName(p.first);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct CollectionNode final : INode
|
|
||||||
{
|
|
||||||
QIcon m_icon;
|
|
||||||
Type m_collectionType;
|
|
||||||
CollectionNode(const QString& name, const QIcon& icon, Type collectionType)
|
|
||||||
: INode(name), m_icon(icon), m_collectionType(collectionType) {}
|
|
||||||
|
|
||||||
Type type() const { return Type::Collection; }
|
virtual Type type() const = 0;
|
||||||
QString text() const { return m_name; }
|
virtual QString text() const = 0;
|
||||||
QIcon icon() const { return m_icon; }
|
virtual QIcon icon() const = 0;
|
||||||
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
|
virtual Qt::ItemFlags flags() const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; }
|
||||||
|
virtual AmuseItemEditFlags editFlags() const { return AmuseItemNone; }
|
||||||
|
|
||||||
Type collectionType() const { return m_collectionType; }
|
virtual void registerNames(const NameUndoRegistry& registry) const {}
|
||||||
int indexOfId(amuse::ObjectId id) const;
|
virtual void unregisterNames(NameUndoRegistry& registry) const {}
|
||||||
amuse::ObjectId idOfIndex(int idx) const;
|
};
|
||||||
BasePoolObjectNode* nodeOfIndex(int idx) const;
|
struct NullNode final : INode {
|
||||||
BasePoolObjectNode* nodeOfId(amuse::ObjectId id) const;
|
NullNode(INode* parent) : INode(parent) {}
|
||||||
};
|
|
||||||
struct BasePoolObjectNode : INode
|
|
||||||
{
|
|
||||||
amuse::ObjectId m_id;
|
|
||||||
BasePoolObjectNode(const QString& name) : INode(name) {}
|
|
||||||
BasePoolObjectNode(amuse::ObjectId id, const QString& name)
|
|
||||||
: INode(name), m_id(id) {}
|
|
||||||
amuse::ObjectId id() const { return m_id; }
|
|
||||||
QString text() const { return m_name; }
|
|
||||||
QIcon icon() const { return {}; }
|
|
||||||
};
|
|
||||||
template <class ID, class T, INode::Type TP>
|
|
||||||
struct PoolObjectNode final : BasePoolObjectNode
|
|
||||||
{
|
|
||||||
amuse::ObjToken<T> m_obj;
|
|
||||||
PoolObjectNode(const QString& name, amuse::ObjToken<T> obj) : BasePoolObjectNode(name), m_obj(obj) {}
|
|
||||||
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
|
|
||||||
: BasePoolObjectNode(id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
|
|
||||||
|
|
||||||
Type type() const { return TP; }
|
Type type() const { return Type::Null; }
|
||||||
AmuseItemEditFlags editFlags() const { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
|
QString text() const { return {}; }
|
||||||
|
QIcon icon() const { return {}; }
|
||||||
|
};
|
||||||
|
struct RootNode final : INode {
|
||||||
|
RootNode() : INode(QStringLiteral("<root>")) {}
|
||||||
|
|
||||||
void registerNames(const NameUndoRegistry& registry) const
|
Type type() const { return Type::Root; }
|
||||||
{
|
QString text() const { return {}; }
|
||||||
ID::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
QIcon icon() const { return {}; }
|
||||||
}
|
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
|
||||||
void unregisterNames(NameUndoRegistry& registry) const
|
};
|
||||||
{
|
struct CollectionNode;
|
||||||
ID::CurNameDB->remove(m_id);
|
struct BasePoolObjectNode;
|
||||||
}
|
struct GroupNode final : INode {
|
||||||
amuse::NameDB* getNameDb() const
|
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
|
||||||
{
|
GroupNode(const QString& name) : INode(name) {}
|
||||||
return ID::CurNameDB;
|
GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
|
||||||
}
|
: INode(it->first), m_it(it) {}
|
||||||
};
|
|
||||||
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;
|
int hypotheticalIndex(const QString& name) const;
|
||||||
|
void _sortChildren();
|
||||||
|
|
||||||
bool m_needsReset = false;
|
static QIcon Icon;
|
||||||
void _buildGroupNodeCollections(GroupNode& gn);
|
Type type() const { return Type::Group; }
|
||||||
void _buildGroupNode(GroupNode& gn, amuse::AudioGroup& group);
|
QString text() const { return m_name; }
|
||||||
void _resetModelData();
|
QIcon icon() const { return Icon; }
|
||||||
void _resetSongRefCount();
|
AmuseItemEditFlags editFlags() const { return AmuseItemNoCut; }
|
||||||
QString MakeDedupedSubprojectName(const QString& origName);
|
|
||||||
static QString MakeDedupedName(const QString& origName, amuse::NameDB* db);
|
CollectionNode* getCollectionOfType(Type tp) const;
|
||||||
|
amuse::AudioGroupDatabase* getAudioGroup() const { return m_it->second.get(); }
|
||||||
|
BasePoolObjectNode* pageObjectNodeOfId(amuse::ObjectId id) const;
|
||||||
|
};
|
||||||
|
struct SongGroupNode final : INode {
|
||||||
|
amuse::GroupId m_id;
|
||||||
|
amuse::ObjToken<amuse::SongGroupIndex> m_index;
|
||||||
|
SongGroupNode(const QString& name, amuse::ObjToken<amuse::SongGroupIndex> index) : INode(name), m_index(index) {}
|
||||||
|
SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
|
||||||
|
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
|
||||||
|
|
||||||
|
static QIcon Icon;
|
||||||
|
Type type() const { return Type::SongGroup; }
|
||||||
|
QString text() const { return m_name; }
|
||||||
|
QIcon icon() const { return Icon; }
|
||||||
|
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
|
||||||
|
|
||||||
|
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
|
||||||
|
|
||||||
|
void registerNames(const NameUndoRegistry& registry) const {
|
||||||
|
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
||||||
|
for (auto& p : m_index->m_midiSetups)
|
||||||
|
registry.registerSongName(p.first);
|
||||||
|
}
|
||||||
|
void unregisterNames(NameUndoRegistry& registry) const {
|
||||||
|
amuse::GroupId::CurNameDB->remove(m_id);
|
||||||
|
for (auto& p : m_index->m_midiSetups)
|
||||||
|
registry.unregisterSongName(p.first);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct SoundGroupNode final : INode {
|
||||||
|
amuse::GroupId m_id;
|
||||||
|
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
|
||||||
|
SoundGroupNode(const QString& name, amuse::ObjToken<amuse::SFXGroupIndex> index) : INode(name), m_index(index) {}
|
||||||
|
SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
|
||||||
|
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
|
||||||
|
|
||||||
|
static QIcon Icon;
|
||||||
|
Type type() const { return Type::SoundGroup; }
|
||||||
|
QString text() const { return m_name; }
|
||||||
|
QIcon icon() const { return Icon; }
|
||||||
|
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
|
||||||
|
|
||||||
|
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
|
||||||
|
|
||||||
|
void registerNames(const NameUndoRegistry& registry) const {
|
||||||
|
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
||||||
|
for (auto& p : m_index->m_sfxEntries)
|
||||||
|
registry.registerSFXName(p.first);
|
||||||
|
}
|
||||||
|
void unregisterNames(NameUndoRegistry& registry) const {
|
||||||
|
amuse::GroupId::CurNameDB->remove(m_id);
|
||||||
|
for (auto& p : m_index->m_sfxEntries)
|
||||||
|
registry.unregisterSFXName(p.first);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct CollectionNode final : INode {
|
||||||
|
QIcon m_icon;
|
||||||
|
Type m_collectionType;
|
||||||
|
CollectionNode(const QString& name, const QIcon& icon, Type collectionType)
|
||||||
|
: INode(name), m_icon(icon), m_collectionType(collectionType) {}
|
||||||
|
|
||||||
|
Type type() const { return Type::Collection; }
|
||||||
|
QString text() const { return m_name; }
|
||||||
|
QIcon icon() const { return m_icon; }
|
||||||
|
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
|
||||||
|
|
||||||
|
Type collectionType() const { return m_collectionType; }
|
||||||
|
int indexOfId(amuse::ObjectId id) const;
|
||||||
|
amuse::ObjectId idOfIndex(int idx) const;
|
||||||
|
BasePoolObjectNode* nodeOfIndex(int idx) const;
|
||||||
|
BasePoolObjectNode* nodeOfId(amuse::ObjectId id) const;
|
||||||
|
};
|
||||||
|
struct BasePoolObjectNode : INode {
|
||||||
|
amuse::ObjectId m_id;
|
||||||
|
BasePoolObjectNode(const QString& name) : INode(name) {}
|
||||||
|
BasePoolObjectNode(amuse::ObjectId id, const QString& name) : INode(name), m_id(id) {}
|
||||||
|
amuse::ObjectId id() const { return m_id; }
|
||||||
|
QString text() const { return m_name; }
|
||||||
|
QIcon icon() const { return {}; }
|
||||||
|
};
|
||||||
|
template <class ID, class T, INode::Type TP>
|
||||||
|
struct PoolObjectNode final : BasePoolObjectNode {
|
||||||
|
amuse::ObjToken<T> m_obj;
|
||||||
|
PoolObjectNode(const QString& name, amuse::ObjToken<T> obj) : BasePoolObjectNode(name), m_obj(obj) {}
|
||||||
|
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
|
||||||
|
: BasePoolObjectNode(id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
|
||||||
|
|
||||||
|
Type type() const { return TP; }
|
||||||
|
AmuseItemEditFlags editFlags() const { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
|
||||||
|
|
||||||
|
void registerNames(const NameUndoRegistry& registry) const {
|
||||||
|
ID::CurNameDB->registerPair(text().toUtf8().data(), m_id);
|
||||||
|
}
|
||||||
|
void unregisterNames(NameUndoRegistry& registry) const { ID::CurNameDB->remove(m_id); }
|
||||||
|
amuse::NameDB* getNameDb() const { return ID::CurNameDB; }
|
||||||
|
};
|
||||||
|
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
|
||||||
|
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
|
||||||
|
using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
|
||||||
|
using KeymapNode = PoolObjectNode<amuse::KeymapId, std::array<amuse::Keymap, 128>, INode::Type::Keymap>;
|
||||||
|
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
|
||||||
|
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
|
||||||
|
|
||||||
|
amuse::ObjToken<RootNode> m_root;
|
||||||
|
|
||||||
|
bool m_needsReset = false;
|
||||||
|
void _buildGroupNodeCollections(GroupNode& gn);
|
||||||
|
void _buildGroupNode(GroupNode& gn, amuse::AudioGroup& group);
|
||||||
|
void _resetModelData();
|
||||||
|
void _resetSongRefCount();
|
||||||
|
QString MakeDedupedSubprojectName(const QString& origName);
|
||||||
|
static QString MakeDedupedName(const QString& origName, amuse::NameDB* db);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
|
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
|
||||||
|
|
||||||
bool clearProjectData();
|
bool clearProjectData();
|
||||||
bool openGroupData(const QString& groupName, UIMessenger& messenger);
|
bool openGroupData(const QString& groupName, UIMessenger& messenger);
|
||||||
void openSongsData();
|
void openSongsData();
|
||||||
void importSongsData(const QString& path);
|
void importSongsData(const QString& path);
|
||||||
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
|
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
|
||||||
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
|
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data, ImportMode mode,
|
||||||
ImportMode mode, UIMessenger& messenger);
|
UIMessenger& messenger);
|
||||||
void saveSongsIndex();
|
void saveSongsIndex();
|
||||||
bool saveToFile(UIMessenger& messenger);
|
bool saveToFile(UIMessenger& messenger);
|
||||||
QStringList getGroupList() const;
|
QStringList getGroupList() const;
|
||||||
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
|
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
|
||||||
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
|
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
|
||||||
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
|
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
|
||||||
|
|
||||||
void updateNodeNames();
|
void updateNodeNames();
|
||||||
bool ensureModelData();
|
bool ensureModelData();
|
||||||
|
|
||||||
QModelIndex proxyCreateIndex(int arow, int acolumn, void *adata) const;
|
QModelIndex proxyCreateIndex(int arow, int acolumn, void* adata) const;
|
||||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
|
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
|
||||||
QModelIndex index(INode* node) const;
|
QModelIndex index(INode* node) const;
|
||||||
QModelIndex parent(const QModelIndex& child) const;
|
QModelIndex parent(const QModelIndex& child) const;
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
INode* node(const QModelIndex& index) const;
|
INode* node(const QModelIndex& index) const;
|
||||||
GroupNode* getGroupNode(INode* node) const;
|
GroupNode* getGroupNode(INode* node) const;
|
||||||
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
|
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
|
||||||
RootNode* rootNode() const { return m_root.get(); }
|
RootNode* rootNode() const { return m_root.get(); }
|
||||||
|
|
||||||
void _postAddNode(INode* n, const NameUndoRegistry& registry);
|
void _postAddNode(INode* n, const NameUndoRegistry& registry);
|
||||||
void _preDelNode(INode* n, NameUndoRegistry& registry);
|
void _preDelNode(INode* n, NameUndoRegistry& registry);
|
||||||
void _addNode(GroupNode* node, std::unique_ptr<amuse::AudioGroupDatabase>&& data, const 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);
|
std::unique_ptr<amuse::AudioGroupDatabase> _delNode(GroupNode* node, NameUndoRegistry& registry);
|
||||||
GroupNode* newSubproject(const QString& name);
|
GroupNode* newSubproject(const QString& name);
|
||||||
template <class NT, class T>
|
template <class NT, class T>
|
||||||
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
|
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
|
||||||
template <class NT, class T>
|
template <class NT, class T>
|
||||||
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
|
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
|
||||||
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||||
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||||
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name);
|
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name);
|
||||||
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||||
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||||
SongGroupNode* newSongGroup(GroupNode* group, const QString& name);
|
SongGroupNode* newSongGroup(GroupNode* group, const QString& name);
|
||||||
template <class NT, class T>
|
template <class NT, class T>
|
||||||
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
|
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
|
||||||
template <class NT, class T>
|
template <class NT, class T>
|
||||||
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
|
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
|
||||||
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||||
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||||
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name,
|
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name, const SoundMacroTemplateEntry* templ = nullptr);
|
||||||
const SoundMacroTemplateEntry* templ = nullptr);
|
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||||
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||||
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
ADSRNode* newADSR(GroupNode* group, const QString& name);
|
||||||
ADSRNode* newADSR(GroupNode* group, const QString& name);
|
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||||
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||||
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
CurveNode* newCurve(GroupNode* group, const QString& name);
|
||||||
CurveNode* newCurve(GroupNode* group, const QString& name);
|
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||||
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||||
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
KeymapNode* newKeymap(GroupNode* group, const QString& name);
|
||||||
KeymapNode* newKeymap(GroupNode* group, const QString& name);
|
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
||||||
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
|
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
||||||
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
|
LayersNode* newLayers(GroupNode* group, const QString& name);
|
||||||
LayersNode* newLayers(GroupNode* group, const QString& name);
|
void _renameNode(INode* node, const QString& name);
|
||||||
void _renameNode(INode* node, const QString& name);
|
|
||||||
|
|
||||||
template <class NT>
|
template <class NT>
|
||||||
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
|
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
|
||||||
template <class NT>
|
template <class NT>
|
||||||
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
|
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
|
||||||
|
|
||||||
QStringList mimeTypes() const;
|
QStringList mimeTypes() const;
|
||||||
QMimeData* mimeData(const QModelIndexList& indexes) const;
|
QMimeData* mimeData(const QModelIndexList& indexes) const;
|
||||||
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
|
||||||
|
|
||||||
void cut(const QModelIndex& index);
|
void cut(const QModelIndex& index);
|
||||||
void copy(const QModelIndex& index);
|
void copy(const QModelIndex& index);
|
||||||
void paste(const QModelIndex& index);
|
void paste(const QModelIndex& index);
|
||||||
QModelIndex duplicate(const QModelIndex& index);
|
QModelIndex duplicate(const QModelIndex& index);
|
||||||
void del(const QModelIndex& index);
|
void del(const QModelIndex& index);
|
||||||
|
|
||||||
const QDir& dir() const { return m_dir; }
|
const QDir& dir() const { return m_dir; }
|
||||||
QString path() const { return m_dir.path(); }
|
QString path() const { return m_dir.path(); }
|
||||||
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
|
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
|
||||||
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
|
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
|
||||||
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
|
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
|
||||||
|
|
||||||
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
|
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
|
||||||
QString getMIDIPathOfSong(amuse::SongId id) const;
|
QString getMIDIPathOfSong(amuse::SongId id) const;
|
||||||
void setMIDIPathOfSong(amuse::SongId id, const QString& path);
|
void setMIDIPathOfSong(amuse::SongId id, const QString& path);
|
||||||
std::pair<amuse::SongId, std::string> bootstrapSongId();
|
std::pair<amuse::SongId, std::string> bootstrapSongId();
|
||||||
void allocateSongId(amuse::SongId id, std::string_view name);
|
void allocateSongId(amuse::SongId id, std::string_view name);
|
||||||
void deallocateSongId(amuse::SongId oldId);
|
void deallocateSongId(amuse::SongId oldId);
|
||||||
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
|
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
|
||||||
|
|
||||||
void setIdDatabases(INode* context) const;
|
void setIdDatabases(INode* context) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,106 +9,99 @@
|
||||||
|
|
||||||
class SampleEditor;
|
class SampleEditor;
|
||||||
|
|
||||||
class SampleView : public QWidget
|
class SampleView : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SampleControls;
|
||||||
friend class SampleControls;
|
qreal m_baseSamplesPerPx = 100.0;
|
||||||
qreal m_baseSamplesPerPx = 100.0;
|
qreal m_samplesPerPx = 100.0;
|
||||||
qreal m_samplesPerPx = 100.0;
|
qreal m_zoomFactor = 1.0;
|
||||||
qreal m_zoomFactor = 1.0;
|
amuse::ObjToken<ProjectModel::SampleNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::SampleNode> m_node;
|
amuse::ObjToken<amuse::SampleEntryData> m_sample;
|
||||||
amuse::ObjToken<amuse::SampleEntryData> m_sample;
|
amuse::ObjToken<amuse::SoundMacro> m_playbackMacro;
|
||||||
amuse::ObjToken<amuse::SoundMacro> m_playbackMacro;
|
const unsigned char* m_sampleData = nullptr;
|
||||||
const unsigned char* m_sampleData = nullptr;
|
qreal m_curSamplePos = 0.0;
|
||||||
qreal m_curSamplePos = 0.0;
|
int16_t m_prev1 = 0;
|
||||||
int16_t m_prev1 = 0;
|
int16_t m_prev2 = 0;
|
||||||
int16_t m_prev2 = 0;
|
QFont m_rulerFont;
|
||||||
QFont m_rulerFont;
|
int m_displaySamplePos = -1;
|
||||||
int m_displaySamplePos = -1;
|
enum class DragState { None, Start, End };
|
||||||
enum class DragState
|
DragState m_dragState = DragState::None;
|
||||||
{
|
void seekToSample(qreal sample);
|
||||||
None,
|
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> iterateSampleInterval(qreal interval);
|
||||||
Start,
|
void calculateSamplesPerPx();
|
||||||
End
|
SampleEditor* getEditor() const;
|
||||||
};
|
|
||||||
DragState m_dragState = DragState::None;
|
|
||||||
void seekToSample(qreal sample);
|
|
||||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> iterateSampleInterval(qreal interval);
|
|
||||||
void calculateSamplesPerPx();
|
|
||||||
SampleEditor* getEditor() const;
|
|
||||||
public:
|
|
||||||
explicit SampleView(QWidget* parent = Q_NULLPTR);
|
|
||||||
bool loadData(ProjectModel::SampleNode* node);
|
|
||||||
void unloadData();
|
|
||||||
ProjectModel::INode* currentNode() const;
|
|
||||||
amuse::SampleEntryData* entryData() const;
|
|
||||||
const amuse::SoundMacro* soundMacro() const;
|
|
||||||
void setSamplePos(int pos);
|
|
||||||
void updateSampleRange(int oldSamp, int newSamp);
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent* ev);
|
public:
|
||||||
void resetZoom();
|
explicit SampleView(QWidget* parent = Q_NULLPTR);
|
||||||
void setZoom(int zVal);
|
bool loadData(ProjectModel::SampleNode* node);
|
||||||
void showEvent(QShowEvent* ev);
|
void unloadData();
|
||||||
void mousePressEvent(QMouseEvent* ev);
|
ProjectModel::INode* currentNode() const;
|
||||||
void mouseReleaseEvent(QMouseEvent* ev);
|
amuse::SampleEntryData* entryData() const;
|
||||||
void mouseMoveEvent(QMouseEvent* ev);
|
const amuse::SoundMacro* soundMacro() const;
|
||||||
void wheelEvent(QWheelEvent* ev);
|
void setSamplePos(int pos);
|
||||||
|
void updateSampleRange(int oldSamp, int newSamp);
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent* ev);
|
||||||
|
void resetZoom();
|
||||||
|
void setZoom(int zVal);
|
||||||
|
void showEvent(QShowEvent* ev);
|
||||||
|
void mousePressEvent(QMouseEvent* ev);
|
||||||
|
void mouseReleaseEvent(QMouseEvent* ev);
|
||||||
|
void mouseMoveEvent(QMouseEvent* ev);
|
||||||
|
void wheelEvent(QWheelEvent* ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SampleControls : public QFrame
|
class SampleControls : public QFrame {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QString m_path;
|
||||||
QString m_path;
|
QSlider* m_zoomSlider;
|
||||||
QSlider* m_zoomSlider;
|
QCheckBox* m_loopCheck;
|
||||||
QCheckBox* m_loopCheck;
|
QSpinBox* m_loopStart;
|
||||||
QSpinBox* m_loopStart;
|
QSpinBox* m_loopEnd;
|
||||||
QSpinBox* m_loopEnd;
|
QSpinBox* m_basePitch;
|
||||||
QSpinBox* m_basePitch;
|
QPushButton* m_makeOtherVersion;
|
||||||
QPushButton* m_makeOtherVersion;
|
QMetaObject::Connection m_makeOtherConn;
|
||||||
QMetaObject::Connection m_makeOtherConn;
|
QPushButton* m_showInBrowser;
|
||||||
QPushButton* m_showInBrowser;
|
bool m_enableUpdate = true;
|
||||||
bool m_enableUpdate = true;
|
bool m_enableFileWrite = true;
|
||||||
bool m_enableFileWrite = true;
|
|
||||||
public:
|
public:
|
||||||
explicit SampleControls(QWidget* parent = Q_NULLPTR);
|
explicit SampleControls(QWidget* parent = Q_NULLPTR);
|
||||||
void doFileWrite();
|
void doFileWrite();
|
||||||
void setFileWrite(bool w);
|
void setFileWrite(bool w);
|
||||||
void updateFileState();
|
void updateFileState();
|
||||||
void setLoopStartSample(int sample) { m_loopStart->setValue(sample); }
|
void setLoopStartSample(int sample) { m_loopStart->setValue(sample); }
|
||||||
void setLoopEndSample(int sample) { m_loopEnd->setValue(sample); }
|
void setLoopEndSample(int sample) { m_loopEnd->setValue(sample); }
|
||||||
void loadData(bool reset);
|
void loadData(bool reset);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
public slots:
|
public slots:
|
||||||
void zoomSliderChanged(int val);
|
void zoomSliderChanged(int val);
|
||||||
void loopStateChanged(int state);
|
void loopStateChanged(int state);
|
||||||
void startValueChanged(int val);
|
void startValueChanged(int val);
|
||||||
void endValueChanged(int val);
|
void endValueChanged(int val);
|
||||||
void pitchValueChanged(int val);
|
void pitchValueChanged(int val);
|
||||||
void makeWAVVersion();
|
void makeWAVVersion();
|
||||||
void makeCompressedVersion();
|
void makeCompressedVersion();
|
||||||
void showInBrowser();
|
void showInBrowser();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SampleEditor : public EditorWidget
|
class SampleEditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SampleView;
|
||||||
friend class SampleView;
|
friend class SampleControls;
|
||||||
friend class SampleControls;
|
friend class SampLoopUndoCommand;
|
||||||
friend class SampLoopUndoCommand;
|
friend class SampPitchUndoCommand;
|
||||||
friend class SampPitchUndoCommand;
|
QScrollArea* m_scrollArea;
|
||||||
QScrollArea* m_scrollArea;
|
SampleView* m_sampleView;
|
||||||
SampleView* m_sampleView;
|
SampleControls* m_controls;
|
||||||
SampleControls* m_controls;
|
|
||||||
public:
|
public:
|
||||||
explicit SampleEditor(QWidget* parent = Q_NULLPTR);
|
explicit SampleEditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::SampleNode* node);
|
bool loadData(ProjectModel::SampleNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
const amuse::SoundMacro* soundMacro() const;
|
const amuse::SoundMacro* soundMacro() const;
|
||||||
void setSamplePos(int pos);
|
void setSamplePos(int pos);
|
||||||
|
|
||||||
void resizeEvent(QResizeEvent* ev);
|
void resizeEvent(QResizeEvent* ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,284 +16,274 @@
|
||||||
|
|
||||||
class SetupTableView;
|
class SetupTableView;
|
||||||
|
|
||||||
class PageObjectDelegate : public BaseObjectDelegate
|
class PageObjectDelegate : public BaseObjectDelegate {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
protected:
|
protected:
|
||||||
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
|
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
|
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
|
||||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||||
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
||||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
||||||
private slots:
|
private slots:
|
||||||
void objIndexChanged();
|
void objIndexChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
class MIDIFileFieldWidget : public QWidget
|
class MIDIFileFieldWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QLineEdit m_le;
|
||||||
QLineEdit m_le;
|
QPushButton m_button;
|
||||||
QPushButton m_button;
|
QFileDialog m_dialog;
|
||||||
QFileDialog m_dialog;
|
|
||||||
public:
|
public:
|
||||||
explicit MIDIFileFieldWidget(QWidget* parent = Q_NULLPTR);
|
explicit MIDIFileFieldWidget(QWidget* parent = Q_NULLPTR);
|
||||||
QString path() const { return m_le.text(); }
|
QString path() const { return m_le.text(); }
|
||||||
void setPath(const QString& path) { m_le.setText(path); }
|
void setPath(const QString& path) { m_le.setText(path); }
|
||||||
public slots:
|
public slots:
|
||||||
void buttonPressed();
|
void buttonPressed();
|
||||||
void fileDialogOpened(const QString& path);
|
void fileDialogOpened(const QString& path);
|
||||||
signals:
|
signals:
|
||||||
void pathChanged();
|
void pathChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
class MIDIFileDelegate : public QStyledItemDelegate
|
class MIDIFileDelegate : public QStyledItemDelegate {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QFileDialog m_fileDialogMid, m_fileDialogSng;
|
||||||
QFileDialog m_fileDialogMid, m_fileDialogSng;
|
std::vector<uint8_t> m_exportData;
|
||||||
std::vector<uint8_t> m_exportData;
|
|
||||||
public:
|
public:
|
||||||
explicit MIDIFileDelegate(SetupTableView* parent = Q_NULLPTR);
|
explicit MIDIFileDelegate(SetupTableView* parent = Q_NULLPTR);
|
||||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||||
void destroyEditor(QWidget *editor, const QModelIndex &index) const;
|
void destroyEditor(QWidget* editor, const QModelIndex& index) const;
|
||||||
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
||||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
||||||
bool editorEvent(QEvent *event, QAbstractItemModel *model,
|
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||||
const QStyleOptionViewItem &option, const QModelIndex &index);
|
const QModelIndex& index);
|
||||||
private slots:
|
private slots:
|
||||||
void doExportMIDI();
|
void doExportMIDI();
|
||||||
void _doExportMIDI(const QString& path);
|
void _doExportMIDI(const QString& path);
|
||||||
void doExportSNG();
|
void doExportSNG();
|
||||||
void _doExportSNG(const QString& path);
|
void _doExportSNG(const QString& path);
|
||||||
public slots:
|
public slots:
|
||||||
void pathChanged();
|
void pathChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
class PageModel : public QAbstractTableModel
|
class PageModel : public QAbstractTableModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SongGroupEditor;
|
||||||
friend class SongGroupEditor;
|
friend class PageObjectDelegate;
|
||||||
friend class PageObjectDelegate;
|
friend class PageTableView;
|
||||||
friend class PageTableView;
|
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
struct Iterator {
|
||||||
struct Iterator
|
using ItTp = std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>::iterator;
|
||||||
{
|
ItTp m_it;
|
||||||
using ItTp = std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>::iterator;
|
Iterator(ItTp it) : m_it(it) {}
|
||||||
ItTp m_it;
|
ItTp::pointer operator->() { return m_it.operator->(); }
|
||||||
Iterator(ItTp it) : m_it(it) {}
|
bool operator<(const Iterator& other) const { return m_it->first < other.m_it->first; }
|
||||||
ItTp::pointer operator->() { return m_it.operator->(); }
|
bool operator<(uint8_t other) const { return m_it->first < other; }
|
||||||
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::vector<Iterator> m_sorted;
|
std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>& _getMap() const;
|
||||||
bool m_drum;
|
void _buildSortedList();
|
||||||
std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>& _getMap() const;
|
QModelIndex _indexOfProgram(uint8_t prog) const;
|
||||||
void _buildSortedList();
|
int _hypotheticalIndexOfProgram(uint8_t prog) const;
|
||||||
QModelIndex _indexOfProgram(uint8_t prog) const;
|
|
||||||
int _hypotheticalIndexOfProgram(uint8_t prog) const;
|
|
||||||
public:
|
public:
|
||||||
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
|
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
|
||||||
void loadData(ProjectModel::SongGroupNode* node);
|
void loadData(ProjectModel::SongGroupNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
|
|
||||||
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
|
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
|
||||||
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
|
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetupListModel : public QAbstractTableModel
|
class SetupListModel : public QAbstractTableModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SongGroupEditor;
|
||||||
friend class SongGroupEditor;
|
friend class MIDIFileDelegate;
|
||||||
friend class MIDIFileDelegate;
|
friend class SetupTableView;
|
||||||
friend class SetupTableView;
|
friend class SetupModel;
|
||||||
friend class SetupModel;
|
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
struct Iterator {
|
||||||
struct Iterator
|
using ItTp = std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>::iterator;
|
||||||
{
|
ItTp m_it;
|
||||||
using ItTp = std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>::iterator;
|
Iterator(ItTp it) : m_it(it) {}
|
||||||
ItTp m_it;
|
ItTp::pointer operator->() { return m_it.operator->(); }
|
||||||
Iterator(ItTp it) : m_it(it) {}
|
bool operator<(const Iterator& other) const {
|
||||||
ItTp::pointer operator->() { return m_it.operator->(); }
|
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||||
bool operator<(const Iterator& other) const
|
amuse::SongId::CurNameDB->resolveNameFromId(other.m_it->first);
|
||||||
{
|
}
|
||||||
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
|
bool operator<(amuse::SongId other) const {
|
||||||
amuse::SongId::CurNameDB->resolveNameFromId(other.m_it->first);
|
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||||
}
|
amuse::SongId::CurNameDB->resolveNameFromId(other);
|
||||||
bool operator<(amuse::SongId other) const
|
}
|
||||||
{
|
bool operator<(const std::string& name) const {
|
||||||
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
|
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) < name;
|
||||||
amuse::SongId::CurNameDB->resolveNameFromId(other);
|
}
|
||||||
}
|
};
|
||||||
bool operator<(const std::string& name) const
|
std::vector<Iterator> m_sorted;
|
||||||
{
|
std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& _getMap() const;
|
||||||
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) < name;
|
void _buildSortedList();
|
||||||
}
|
QModelIndex _indexOfSong(amuse::SongId id) const;
|
||||||
};
|
int _hypotheticalIndexOfSong(const std::string& songName) const;
|
||||||
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:
|
public:
|
||||||
explicit SetupListModel(QObject* parent = Q_NULLPTR);
|
explicit SetupListModel(QObject* parent = Q_NULLPTR);
|
||||||
void loadData(ProjectModel::SongGroupNode* node);
|
void loadData(ProjectModel::SongGroupNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
|
|
||||||
int _insertRow(
|
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>>& data);
|
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>> _removeRow(amuse::SongId id);
|
||||||
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>
|
|
||||||
_removeRow(amuse::SongId id);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetupModel : public QAbstractTableModel
|
class SetupModel : public QAbstractTableModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SongGroupEditor;
|
||||||
friend class SongGroupEditor;
|
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
|
||||||
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
|
|
||||||
public:
|
|
||||||
explicit SetupModel(QObject* parent = Q_NULLPTR);
|
|
||||||
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
|
|
||||||
void unloadData();
|
|
||||||
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
public:
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
explicit SetupModel(QObject* parent = Q_NULLPTR);
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
void unloadData();
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
|
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PageTableView : public QTableView
|
class PageTableView : public QTableView {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
PageObjectDelegate m_poDelegate;
|
||||||
PageObjectDelegate m_poDelegate;
|
RangedValueFactory<0, 127> m_127Factory;
|
||||||
RangedValueFactory<0, 127> m_127Factory;
|
RangedValueFactory<1, 128> m_128Factory;
|
||||||
RangedValueFactory<1, 128> m_128Factory;
|
RangedValueFactory<0, 255> m_255Factory;
|
||||||
RangedValueFactory<0, 255> m_255Factory;
|
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
|
||||||
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
|
|
||||||
public:
|
public:
|
||||||
explicit PageTableView(QWidget* parent = Q_NULLPTR);
|
explicit PageTableView(QWidget* parent = Q_NULLPTR);
|
||||||
void setModel(QAbstractItemModel* model);
|
void setModel(QAbstractItemModel* model);
|
||||||
void deleteSelection();
|
void deleteSelection();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetupTableView : public QSplitter
|
class SetupTableView : public QSplitter {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SongGroupEditor;
|
||||||
friend class SongGroupEditor;
|
friend class SetupRowUndoCommand;
|
||||||
friend class SetupRowUndoCommand;
|
QTableView* m_listView;
|
||||||
QTableView* m_listView;
|
QTableView* m_tableView;
|
||||||
QTableView* m_tableView;
|
MIDIFileDelegate m_midiDelegate;
|
||||||
MIDIFileDelegate m_midiDelegate;
|
RangedValueFactory<0, 127> m_127Factory;
|
||||||
RangedValueFactory<0, 127> m_127Factory;
|
RangedValueFactory<1, 128> m_128Factory;
|
||||||
RangedValueFactory<1, 128> m_128Factory;
|
QStyledItemDelegate m_127Delegate, m_128Delegate;
|
||||||
QStyledItemDelegate m_127Delegate, m_128Delegate;
|
|
||||||
public:
|
public:
|
||||||
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
|
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
|
||||||
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
|
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
|
||||||
void deleteSelection();
|
void deleteSelection();
|
||||||
void showEvent(QShowEvent* event);
|
void showEvent(QShowEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ColoredTabBarStyle : public QProxyStyle
|
class ColoredTabBarStyle : public QProxyStyle {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
using QProxyStyle::QProxyStyle;
|
using QProxyStyle::QProxyStyle;
|
||||||
void drawControl(QStyle::ControlElement element, const QStyleOption *option,
|
void drawControl(QStyle::ControlElement element, const QStyleOption* option, QPainter* painter,
|
||||||
QPainter *painter, const QWidget *widget = nullptr) const;
|
const QWidget* widget = nullptr) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ColoredTabBar : public QTabBar
|
class ColoredTabBar : public QTabBar {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
|
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ColoredTabWidget : public QTabWidget
|
class ColoredTabWidget : public QTabWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
ColoredTabBar m_tabBar;
|
||||||
ColoredTabBar m_tabBar;
|
|
||||||
public:
|
public:
|
||||||
explicit ColoredTabWidget(QWidget* parent = Q_NULLPTR);
|
explicit ColoredTabWidget(QWidget* parent = Q_NULLPTR);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MIDIPlayerWidget : public QWidget
|
class MIDIPlayerWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QAction m_playAction;
|
||||||
QAction m_playAction;
|
QToolButton m_button;
|
||||||
QToolButton m_button;
|
QModelIndex m_index;
|
||||||
QModelIndex m_index;
|
amuse::GroupId m_groupId;
|
||||||
amuse::GroupId m_groupId;
|
amuse::SongId m_songId;
|
||||||
amuse::SongId m_songId;
|
QString m_path;
|
||||||
QString m_path;
|
std::vector<uint8_t> m_arrData;
|
||||||
std::vector<uint8_t> m_arrData;
|
amuse::ObjToken<amuse::Sequencer> m_seq;
|
||||||
amuse::ObjToken<amuse::Sequencer> m_seq;
|
|
||||||
public:
|
public:
|
||||||
explicit MIDIPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SongId id,
|
explicit MIDIPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SongId id, const QString& path,
|
||||||
const QString& path, QWidget* parent = Q_NULLPTR);
|
QWidget* parent = Q_NULLPTR);
|
||||||
~MIDIPlayerWidget();
|
~MIDIPlayerWidget();
|
||||||
amuse::SongId songId() const { return m_songId; }
|
amuse::SongId songId() const { return m_songId; }
|
||||||
amuse::Sequencer* sequencer() const { return m_seq.get(); }
|
amuse::Sequencer* sequencer() const { return m_seq.get(); }
|
||||||
void stopped();
|
void stopped();
|
||||||
void resizeEvent(QResizeEvent* event);
|
void resizeEvent(QResizeEvent* event);
|
||||||
void mouseDoubleClickEvent(QMouseEvent* event);
|
void mouseDoubleClickEvent(QMouseEvent* event);
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
public slots:
|
public slots:
|
||||||
void clicked();
|
void clicked();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SongGroupEditor : public EditorWidget
|
class SongGroupEditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SetupModel;
|
||||||
friend class SetupModel;
|
PageModel m_normPages;
|
||||||
PageModel m_normPages;
|
PageModel m_drumPages;
|
||||||
PageModel m_drumPages;
|
SetupListModel m_setupList;
|
||||||
SetupListModel m_setupList;
|
SetupModel m_setup;
|
||||||
SetupModel m_setup;
|
PageTableView* m_normTable;
|
||||||
PageTableView* m_normTable;
|
PageTableView* m_drumTable;
|
||||||
PageTableView* m_drumTable;
|
SetupTableView* m_setupTable;
|
||||||
SetupTableView* m_setupTable;
|
ColoredTabWidget m_tabs;
|
||||||
ColoredTabWidget m_tabs;
|
AddRemoveButtons m_addRemoveButtons;
|
||||||
AddRemoveButtons m_addRemoveButtons;
|
|
||||||
public:
|
public:
|
||||||
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
|
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::SongGroupNode* node);
|
bool loadData(ProjectModel::SongGroupNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
void setEditorEnabled(bool en) {}
|
void setEditorEnabled(bool en) {}
|
||||||
void resizeEvent(QResizeEvent* ev);
|
void resizeEvent(QResizeEvent* ev);
|
||||||
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
|
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
|
||||||
AmuseItemEditFlags itemEditFlags() const;
|
AmuseItemEditFlags itemEditFlags() const;
|
||||||
private slots:
|
private slots:
|
||||||
void doAdd();
|
void doAdd();
|
||||||
void doSelectionChanged();
|
void doSelectionChanged();
|
||||||
void doSetupSelectionChanged();
|
void doSetupSelectionChanged();
|
||||||
void currentTabChanged(int idx);
|
void currentTabChanged(int idx);
|
||||||
void normRowsInserted(const QModelIndex& parent, int first, int last);
|
void normRowsInserted(const QModelIndex& parent, int first, int last);
|
||||||
void normRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
void normRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||||
void drumRowsInserted(const QModelIndex& parent, int first, int last);
|
void drumRowsInserted(const QModelIndex& parent, int first, int last);
|
||||||
void drumRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
void drumRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||||
void setupRowsInserted(const QModelIndex& parent, int first, int last);
|
void setupRowsInserted(const QModelIndex& parent, int first, int last);
|
||||||
void setupRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
void setupRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||||
void setupRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
|
void setupRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
|
||||||
void setupModelAboutToBeReset();
|
void setupModelAboutToBeReset();
|
||||||
void setupDataChanged();
|
void setupDataChanged();
|
||||||
void itemDeleteAction();
|
void itemDeleteAction();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,127 +5,121 @@
|
||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
#include "amuse/Voice.hpp"
|
#include "amuse/Voice.hpp"
|
||||||
|
|
||||||
class SFXObjectDelegate : public BaseObjectDelegate
|
class SFXObjectDelegate : public BaseObjectDelegate {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
protected:
|
protected:
|
||||||
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
|
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
|
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
|
||||||
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||||
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
void setEditorData(QWidget* editor, const QModelIndex& index) const;
|
||||||
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
|
||||||
private slots:
|
private slots:
|
||||||
void objIndexChanged();
|
void objIndexChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SFXModel : public QAbstractTableModel
|
class SFXModel : public QAbstractTableModel {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SoundGroupEditor;
|
||||||
friend class SoundGroupEditor;
|
friend class SFXObjectDelegate;
|
||||||
friend class SFXObjectDelegate;
|
friend class SFXTableView;
|
||||||
friend class SFXTableView;
|
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
|
struct Iterator {
|
||||||
struct Iterator
|
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
|
||||||
{
|
ItTp m_it;
|
||||||
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
|
Iterator(ItTp it) : m_it(it) {}
|
||||||
ItTp m_it;
|
ItTp::pointer operator->() const { return m_it.operator->(); }
|
||||||
Iterator(ItTp it) : m_it(it) {}
|
bool operator<(const Iterator& other) const {
|
||||||
ItTp::pointer operator->() const { return m_it.operator->(); }
|
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||||
bool operator<(const Iterator& other) const
|
amuse::SFXId::CurNameDB->resolveNameFromId(other.m_it->first);
|
||||||
{
|
}
|
||||||
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
|
bool operator<(amuse::SongId other) const {
|
||||||
amuse::SFXId::CurNameDB->resolveNameFromId(other.m_it->first);
|
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||||
}
|
amuse::SFXId::CurNameDB->resolveNameFromId(other);
|
||||||
bool operator<(amuse::SongId other) const
|
}
|
||||||
{
|
bool operator<(const std::string& name) const {
|
||||||
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
|
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) < name;
|
||||||
amuse::SFXId::CurNameDB->resolveNameFromId(other);
|
}
|
||||||
}
|
};
|
||||||
bool operator<(const std::string& name) const
|
std::vector<Iterator> m_sorted;
|
||||||
{
|
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& _getMap() const;
|
||||||
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) < name;
|
void _buildSortedList();
|
||||||
}
|
QModelIndex _indexOfSFX(amuse::SFXId sfx) const;
|
||||||
};
|
int _hypotheticalIndexOfSFX(const std::string& sfxName) const;
|
||||||
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:
|
public:
|
||||||
explicit SFXModel(QObject* parent = Q_NULLPTR);
|
explicit SFXModel(QObject* parent = Q_NULLPTR);
|
||||||
void loadData(ProjectModel::SoundGroupNode* node);
|
void loadData(ProjectModel::SoundGroupNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
|
|
||||||
int _insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data);
|
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);
|
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> _removeRow(amuse::SFXId sfx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SFXTableView : public QTableView
|
class SFXTableView : public QTableView {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
SFXObjectDelegate m_sfxDelegate;
|
||||||
SFXObjectDelegate m_sfxDelegate;
|
RangedValueFactory<0, 127> m_127Factory;
|
||||||
RangedValueFactory<0, 127> m_127Factory;
|
RangedValueFactory<0, 255> m_255Factory;
|
||||||
RangedValueFactory<0, 255> m_255Factory;
|
QStyledItemDelegate m_127Delegate, m_255Delegate;
|
||||||
QStyledItemDelegate m_127Delegate, m_255Delegate;
|
|
||||||
public:
|
public:
|
||||||
explicit SFXTableView(QWidget* parent = Q_NULLPTR);
|
explicit SFXTableView(QWidget* parent = Q_NULLPTR);
|
||||||
void setModel(QAbstractItemModel* model);
|
void setModel(QAbstractItemModel* model);
|
||||||
void deleteSelection();
|
void deleteSelection();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SFXPlayerWidget : public QWidget
|
class SFXPlayerWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QAction m_playAction;
|
||||||
QAction m_playAction;
|
QToolButton m_button;
|
||||||
QToolButton m_button;
|
QModelIndex m_index;
|
||||||
QModelIndex m_index;
|
amuse::GroupId m_groupId;
|
||||||
amuse::GroupId m_groupId;
|
amuse::SFXId m_sfxId;
|
||||||
amuse::SFXId m_sfxId;
|
amuse::ObjToken<amuse::Voice> m_vox;
|
||||||
amuse::ObjToken<amuse::Voice> m_vox;
|
|
||||||
public:
|
public:
|
||||||
explicit SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id,
|
explicit SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id, QWidget* parent = Q_NULLPTR);
|
||||||
QWidget* parent = Q_NULLPTR);
|
~SFXPlayerWidget();
|
||||||
~SFXPlayerWidget();
|
amuse::SongId sfxId() const { return m_sfxId; }
|
||||||
amuse::SongId sfxId() const { return m_sfxId; }
|
amuse::Voice* voice() const { return m_vox.get(); }
|
||||||
amuse::Voice* voice() const { return m_vox.get(); }
|
void stopped();
|
||||||
void stopped();
|
void resizeEvent(QResizeEvent* event);
|
||||||
void resizeEvent(QResizeEvent* event);
|
void mouseDoubleClickEvent(QMouseEvent* event);
|
||||||
void mouseDoubleClickEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
|
||||||
public slots:
|
public slots:
|
||||||
void clicked();
|
void clicked();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SoundGroupEditor : public EditorWidget
|
class SoundGroupEditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
SFXModel m_sfxs;
|
||||||
SFXModel m_sfxs;
|
SFXTableView* m_sfxTable;
|
||||||
SFXTableView* m_sfxTable;
|
AddRemoveButtons m_addRemoveButtons;
|
||||||
AddRemoveButtons m_addRemoveButtons;
|
|
||||||
public:
|
public:
|
||||||
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
|
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::SoundGroupNode* node);
|
bool loadData(ProjectModel::SoundGroupNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
void setEditorEnabled(bool en) {}
|
void setEditorEnabled(bool en) {}
|
||||||
void resizeEvent(QResizeEvent* ev);
|
void resizeEvent(QResizeEvent* ev);
|
||||||
QTableView* getSFXListView() const { return m_sfxTable; }
|
QTableView* getSFXListView() const { return m_sfxTable; }
|
||||||
AmuseItemEditFlags itemEditFlags() const;
|
AmuseItemEditFlags itemEditFlags() const;
|
||||||
private slots:
|
private slots:
|
||||||
void rowsInserted(const QModelIndex &parent, int first, int last);
|
void rowsInserted(const QModelIndex& parent, int first, int last);
|
||||||
void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
|
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
||||||
void doAdd();
|
void doAdd();
|
||||||
void doSelectionChanged();
|
void doSelectionChanged();
|
||||||
void sfxDataChanged();
|
void sfxDataChanged();
|
||||||
void itemDeleteAction();
|
void itemDeleteAction();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,173 +16,170 @@ class SoundMacroEditor;
|
||||||
class SoundMacroListing;
|
class SoundMacroListing;
|
||||||
class CatalogueItem;
|
class CatalogueItem;
|
||||||
|
|
||||||
class TargetButton : public QPushButton
|
class TargetButton : public QPushButton {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit TargetButton(QWidget* parent = Q_NULLPTR);
|
explicit TargetButton(QWidget* parent = Q_NULLPTR);
|
||||||
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
|
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
|
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
|
||||||
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
|
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldSoundMacroStep : public QWidget
|
class FieldSoundMacroStep : public QWidget {
|
||||||
{
|
friend class CommandWidget;
|
||||||
friend class CommandWidget;
|
Q_OBJECT
|
||||||
Q_OBJECT
|
FieldProjectNode* m_macroField;
|
||||||
FieldProjectNode* m_macroField;
|
FieldSpinBox m_spinBox;
|
||||||
FieldSpinBox m_spinBox;
|
TargetButton m_targetButton;
|
||||||
TargetButton m_targetButton;
|
SoundMacroEditor* getEditor() const;
|
||||||
SoundMacroEditor* getEditor() const;
|
SoundMacroListing* getListing() const;
|
||||||
SoundMacroListing* getListing() const;
|
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(int);
|
void valueChanged(int);
|
||||||
public slots:
|
public slots:
|
||||||
void targetPressed();
|
void targetPressed();
|
||||||
void updateMacroField();
|
void updateMacroField();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
||||||
~FieldSoundMacroStep();
|
~FieldSoundMacroStep();
|
||||||
void setIndex(int index);
|
void setIndex(int index);
|
||||||
void cancel();
|
void cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandWidget : public QWidget
|
class CommandWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SoundMacroListing;
|
||||||
friend class SoundMacroListing;
|
QFont m_numberFont;
|
||||||
QFont m_numberFont;
|
QLabel m_titleLabel;
|
||||||
QLabel m_titleLabel;
|
ListingDeleteButton m_deleteButton;
|
||||||
ListingDeleteButton m_deleteButton;
|
QStaticText m_numberText;
|
||||||
QStaticText m_numberText;
|
int m_index = -1;
|
||||||
int m_index = -1;
|
amuse::SoundMacro::ICmd* m_cmd;
|
||||||
amuse::SoundMacro::ICmd* m_cmd;
|
const amuse::SoundMacro::CmdIntrospection* m_introspection;
|
||||||
const amuse::SoundMacro::CmdIntrospection* m_introspection;
|
FieldSoundMacroStep* m_stepField = nullptr;
|
||||||
FieldSoundMacroStep* m_stepField = nullptr;
|
void setIndex(int index);
|
||||||
void setIndex(int index);
|
SoundMacroListing* getParent() const;
|
||||||
SoundMacroListing* getParent() const;
|
|
||||||
private slots:
|
private slots:
|
||||||
void boolChanged(int);
|
void boolChanged(int);
|
||||||
void numChanged(int);
|
void numChanged(int);
|
||||||
void nodeChanged(int);
|
void nodeChanged(int);
|
||||||
void deleteClicked();
|
void deleteClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
|
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
|
||||||
CommandWidget(QWidget* parent, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
CommandWidget(QWidget* parent, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
||||||
void paintEvent(QPaintEvent* event);
|
void paintEvent(QPaintEvent* event);
|
||||||
QString getText() const { return m_titleLabel.text(); }
|
QString getText() const { return m_titleLabel.text(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandWidgetContainer : public QWidget
|
class CommandWidgetContainer : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SoundMacroListing;
|
||||||
friend class SoundMacroListing;
|
CommandWidget* m_commandWidget;
|
||||||
CommandWidget* m_commandWidget;
|
QPropertyAnimation* m_animation = nullptr;
|
||||||
QPropertyAnimation* m_animation = nullptr;
|
void animateOpen();
|
||||||
void animateOpen();
|
void animateClosed();
|
||||||
void animateClosed();
|
void snapOpen();
|
||||||
void snapOpen();
|
void snapClosed();
|
||||||
void snapClosed();
|
|
||||||
private slots:
|
private slots:
|
||||||
void animationDestroyed();
|
void animationDestroyed();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <class..._Args>
|
template <class... _Args>
|
||||||
CommandWidgetContainer(QWidget* parent, _Args&&... args);
|
CommandWidgetContainer(QWidget* parent, _Args&&... args);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SoundMacroListing : public QWidget
|
class SoundMacroListing : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class CommandWidget;
|
||||||
friend class CommandWidget;
|
friend class SoundMacroEditor;
|
||||||
friend class SoundMacroEditor;
|
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
|
||||||
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
|
QVBoxLayout* m_layout;
|
||||||
QVBoxLayout* m_layout;
|
QLayoutItem* m_dragItem = nullptr;
|
||||||
QLayoutItem* m_dragItem = nullptr;
|
int m_origIdx = -1;
|
||||||
int m_origIdx = -1;
|
int m_dragOpenIdx = -1;
|
||||||
int m_dragOpenIdx = -1;
|
CommandWidgetContainer* m_prevDragOpen = nullptr;
|
||||||
CommandWidgetContainer* m_prevDragOpen = nullptr;
|
int m_autoscrollTimer = -1;
|
||||||
int m_autoscrollTimer = -1;
|
int m_autoscrollDelta = 0;
|
||||||
int m_autoscrollDelta = 0;
|
QWidget* m_autoscrollSource = nullptr;
|
||||||
QWidget* m_autoscrollSource = nullptr;
|
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
|
||||||
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
|
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
|
||||||
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
|
void stopAutoscroll();
|
||||||
void stopAutoscroll();
|
bool beginDrag(CommandWidget* widget);
|
||||||
bool beginDrag(CommandWidget* widget);
|
void endDrag();
|
||||||
void endDrag();
|
void cancelDrag();
|
||||||
void cancelDrag();
|
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||||
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
void moveDrag(CommandWidget* widget, 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);
|
||||||
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
|
void insertDragout();
|
||||||
void insertDragout();
|
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
|
||||||
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
|
void deleteCommand(int index);
|
||||||
void deleteCommand(int index);
|
void reindex();
|
||||||
void reindex();
|
void clear();
|
||||||
void clear();
|
|
||||||
public:
|
public:
|
||||||
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
|
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::SoundMacroNode* node);
|
bool loadData(ProjectModel::SoundMacroNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
ProjectModel::INode* currentNode() const;
|
ProjectModel::INode* currentNode() const;
|
||||||
void timerEvent(QTimerEvent* event);
|
void timerEvent(QTimerEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CatalogueItem : public QWidget
|
class CatalogueItem : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
amuse::SoundMacro::CmdOp m_op;
|
||||||
amuse::SoundMacro::CmdOp m_op;
|
QLabel m_iconLab;
|
||||||
QLabel m_iconLab;
|
QLabel m_label;
|
||||||
QLabel m_label;
|
|
||||||
public:
|
public:
|
||||||
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name,
|
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name, const QString& doc,
|
||||||
const QString& doc, QWidget* parent = Q_NULLPTR);
|
QWidget* parent = Q_NULLPTR);
|
||||||
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
|
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
|
||||||
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
|
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
|
||||||
QString getText() const { return m_label.text(); }
|
QString getText() const { return m_label.text(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SoundMacroCatalogue : public QTreeWidget
|
class SoundMacroCatalogue : public QTreeWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
|
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
void mouseMoveEvent(QMouseEvent* event);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SoundMacroEditor : public EditorWidget
|
class SoundMacroEditor : public EditorWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class SoundMacroCatalogue;
|
||||||
friend class SoundMacroCatalogue;
|
friend class FieldSoundMacroStep;
|
||||||
friend class FieldSoundMacroStep;
|
QSplitter* m_splitter;
|
||||||
QSplitter* m_splitter;
|
SoundMacroListing* m_listing;
|
||||||
SoundMacroListing* m_listing;
|
SoundMacroCatalogue* m_catalogue;
|
||||||
SoundMacroCatalogue* m_catalogue;
|
CommandWidget* m_draggedCmd = nullptr;
|
||||||
CommandWidget* m_draggedCmd = nullptr;
|
CatalogueItem* m_draggedItem = nullptr;
|
||||||
CatalogueItem* m_draggedItem = nullptr;
|
FieldSoundMacroStep* m_targetField = nullptr;
|
||||||
FieldSoundMacroStep* m_targetField = nullptr;
|
QPoint m_draggedPt;
|
||||||
QPoint m_draggedPt;
|
int m_dragInsertIdx = -1;
|
||||||
int m_dragInsertIdx = -1;
|
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
||||||
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
||||||
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
void beginStepTarget(FieldSoundMacroStep* stepField);
|
||||||
void beginStepTarget(FieldSoundMacroStep* stepField);
|
void endStepTarget();
|
||||||
void endStepTarget();
|
|
||||||
public:
|
|
||||||
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
|
|
||||||
bool loadData(ProjectModel::SoundMacroNode* node);
|
|
||||||
void unloadData();
|
|
||||||
ProjectModel::INode* currentNode() const;
|
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent* event);
|
public:
|
||||||
void mouseReleaseEvent(QMouseEvent* event);
|
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
|
||||||
void mouseMoveEvent(QMouseEvent* event);
|
bool loadData(ProjectModel::SoundMacroNode* node);
|
||||||
void keyPressEvent(QKeyEvent* event);
|
void unloadData();
|
||||||
|
ProjectModel::INode* currentNode() const;
|
||||||
|
|
||||||
|
void mousePressEvent(QMouseEvent* event);
|
||||||
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
|
void keyPressEvent(QKeyEvent* event);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
|
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,113 +1,97 @@
|
||||||
#include "StatusBarWidget.hpp"
|
#include "StatusBarWidget.hpp"
|
||||||
|
|
||||||
FXButton::FXButton(QWidget* parent)
|
FXButton::FXButton(QWidget* parent) : QPushButton(parent) {
|
||||||
: QPushButton(parent)
|
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
|
||||||
{
|
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
|
||||||
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
|
|
||||||
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusBarWidget::StatusBarWidget(QWidget* parent)
|
StatusBarWidget::StatusBarWidget(QWidget* parent)
|
||||||
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal),
|
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal), m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal) {
|
||||||
m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal)
|
addWidget(&m_normalMessage);
|
||||||
{
|
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
|
||||||
addWidget(&m_normalMessage);
|
m_killButton.setVisible(false);
|
||||||
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
|
m_killButton.setToolTip(tr("Immediately kill active voices"));
|
||||||
m_killButton.setVisible(false);
|
m_voiceCount.setVisible(false);
|
||||||
m_killButton.setToolTip(tr("Immediately kill active voices"));
|
m_volumeIcons[0] = QIcon(QStringLiteral(":/icons/IconVolume0.svg"));
|
||||||
m_voiceCount.setVisible(false);
|
m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1.svg"));
|
||||||
m_volumeIcons[0] = QIcon(QStringLiteral(":/icons/IconVolume0.svg"));
|
m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2.svg"));
|
||||||
m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1.svg"));
|
m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3.svg"));
|
||||||
m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2.svg"));
|
m_aIcon.setFixedSize(16, 16);
|
||||||
m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3.svg"));
|
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
|
||||||
m_aIcon.setFixedSize(16, 16);
|
QString aTip = tr("Aux A send level for all voices");
|
||||||
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
|
m_aIcon.setToolTip(aTip);
|
||||||
QString aTip = tr("Aux A send level for all voices");
|
m_aSlider.setRange(0, 100);
|
||||||
m_aIcon.setToolTip(aTip);
|
m_aSlider.setFixedWidth(100);
|
||||||
m_aSlider.setRange(0, 100);
|
m_aSlider.setToolTip(aTip);
|
||||||
m_aSlider.setFixedWidth(100);
|
m_bIcon.setFixedSize(16, 16);
|
||||||
m_aSlider.setToolTip(aTip);
|
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
|
||||||
m_bIcon.setFixedSize(16, 16);
|
QString bTip = tr("Aux B send level for all voices");
|
||||||
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
|
m_bIcon.setToolTip(bTip);
|
||||||
QString bTip = tr("Aux B send level for all voices");
|
m_bSlider.setRange(0, 100);
|
||||||
m_bIcon.setToolTip(bTip);
|
m_bSlider.setFixedWidth(100);
|
||||||
m_bSlider.setRange(0, 100);
|
m_bSlider.setToolTip(bTip);
|
||||||
m_bSlider.setFixedWidth(100);
|
m_volumeIcon.setFixedSize(16, 16);
|
||||||
m_bSlider.setToolTip(bTip);
|
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
|
||||||
m_volumeIcon.setFixedSize(16, 16);
|
QString volTip = tr("Master volume level");
|
||||||
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
|
m_volumeIcon.setToolTip(volTip);
|
||||||
QString volTip = tr("Master volume level");
|
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
|
||||||
m_volumeIcon.setToolTip(volTip);
|
m_volumeSlider.setRange(0, 100);
|
||||||
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
|
m_volumeSlider.setFixedWidth(100);
|
||||||
m_volumeSlider.setRange(0, 100);
|
m_volumeSlider.setToolTip(volTip);
|
||||||
m_volumeSlider.setFixedWidth(100);
|
addPermanentWidget(&m_voiceCount);
|
||||||
m_volumeSlider.setToolTip(volTip);
|
addPermanentWidget(&m_killButton);
|
||||||
addPermanentWidget(&m_voiceCount);
|
addPermanentWidget(&m_fxButton);
|
||||||
addPermanentWidget(&m_killButton);
|
addPermanentWidget(&m_aIcon);
|
||||||
addPermanentWidget(&m_fxButton);
|
addPermanentWidget(&m_aSlider);
|
||||||
addPermanentWidget(&m_aIcon);
|
addPermanentWidget(&m_bIcon);
|
||||||
addPermanentWidget(&m_aSlider);
|
addPermanentWidget(&m_bSlider);
|
||||||
addPermanentWidget(&m_bIcon);
|
addPermanentWidget(&m_volumeIcon);
|
||||||
addPermanentWidget(&m_bSlider);
|
addPermanentWidget(&m_volumeSlider);
|
||||||
addPermanentWidget(&m_volumeIcon);
|
|
||||||
addPermanentWidget(&m_volumeSlider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarWidget::setVoiceCount(int voices)
|
void StatusBarWidget::setVoiceCount(int voices) {
|
||||||
{
|
if (voices != m_cachedVoiceCount) {
|
||||||
if (voices != m_cachedVoiceCount)
|
m_voiceCount.setText(QString::number(voices));
|
||||||
{
|
m_cachedVoiceCount = voices;
|
||||||
m_voiceCount.setText(QString::number(voices));
|
setKillVisible(voices != 0);
|
||||||
m_cachedVoiceCount = voices;
|
}
|
||||||
setKillVisible(voices != 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarWidget::volumeChanged(int vol)
|
void StatusBarWidget::volumeChanged(int vol) {
|
||||||
{
|
int idx = int(std::round(vol * (3.f / 100.f)));
|
||||||
int idx = int(std::round(vol * (3.f / 100.f)));
|
if (idx != m_lastVolIdx) {
|
||||||
if (idx != m_lastVolIdx)
|
m_lastVolIdx = idx;
|
||||||
{
|
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
|
||||||
m_lastVolIdx = idx;
|
}
|
||||||
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarFocus::setMessage(const QString& message)
|
void StatusBarFocus::setMessage(const QString& message) {
|
||||||
{
|
m_message = message;
|
||||||
m_message = message;
|
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
|
||||||
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
|
if (widget->m_curFocus == this) {
|
||||||
{
|
if (m_message.isEmpty())
|
||||||
if (widget->m_curFocus == this)
|
widget->clearMessage();
|
||||||
{
|
else
|
||||||
if (m_message.isEmpty())
|
widget->showMessage(m_message);
|
||||||
widget->clearMessage();
|
|
||||||
else
|
|
||||||
widget->showMessage(m_message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarFocus::enter()
|
void StatusBarFocus::enter() {
|
||||||
{
|
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
|
||||||
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
|
widget->m_curFocus = this;
|
||||||
{
|
if (m_message.isEmpty())
|
||||||
widget->m_curFocus = this;
|
widget->clearMessage();
|
||||||
if (m_message.isEmpty())
|
else
|
||||||
widget->clearMessage();
|
widget->showMessage(m_message);
|
||||||
else
|
}
|
||||||
widget->showMessage(m_message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarFocus::exit()
|
void StatusBarFocus::exit() {
|
||||||
{
|
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
|
||||||
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
|
if (widget->m_curFocus == this) {
|
||||||
{
|
widget->clearMessage();
|
||||||
if (widget->m_curFocus == this)
|
widget->m_curFocus = nullptr;
|
||||||
{
|
|
||||||
widget->clearMessage();
|
|
||||||
widget->m_curFocus = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,67 +9,72 @@
|
||||||
|
|
||||||
class StatusBarFocus;
|
class StatusBarFocus;
|
||||||
|
|
||||||
class FXButton : public QPushButton
|
class FXButton : public QPushButton {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit FXButton(QWidget* parent = Q_NULLPTR);
|
explicit FXButton(QWidget* parent = Q_NULLPTR);
|
||||||
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
|
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
|
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
|
||||||
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
|
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatusBarWidget : public QStatusBar
|
class StatusBarWidget : public QStatusBar {
|
||||||
{
|
friend class StatusBarFocus;
|
||||||
friend class StatusBarFocus;
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QLabel m_normalMessage;
|
||||||
QLabel m_normalMessage;
|
QPushButton m_killButton;
|
||||||
QPushButton m_killButton;
|
FXButton m_fxButton;
|
||||||
FXButton m_fxButton;
|
QIcon m_volumeIcons[4];
|
||||||
QIcon m_volumeIcons[4];
|
QLabel m_volumeIcon;
|
||||||
QLabel m_volumeIcon;
|
QSlider m_volumeSlider;
|
||||||
QSlider m_volumeSlider;
|
QLabel m_aIcon;
|
||||||
QLabel m_aIcon;
|
QSlider m_aSlider;
|
||||||
QSlider m_aSlider;
|
QLabel m_bIcon;
|
||||||
QLabel m_bIcon;
|
QSlider m_bSlider;
|
||||||
QSlider m_bSlider;
|
int m_lastVolIdx = 0;
|
||||||
int m_lastVolIdx = 0;
|
QLabel m_voiceCount;
|
||||||
QLabel m_voiceCount;
|
int m_cachedVoiceCount = -1;
|
||||||
int m_cachedVoiceCount = -1;
|
StatusBarFocus* m_curFocus = nullptr;
|
||||||
StatusBarFocus* m_curFocus = nullptr;
|
void setKillVisible(bool vis) {
|
||||||
void setKillVisible(bool vis) { m_killButton.setVisible(vis); m_voiceCount.setVisible(vis); }
|
m_killButton.setVisible(vis);
|
||||||
|
m_voiceCount.setVisible(vis);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
|
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
|
||||||
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
|
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
|
||||||
void setVoiceCount(int voices);
|
void setVoiceCount(int voices);
|
||||||
void connectKillClicked(const QObject* receiver, const char* method)
|
void connectKillClicked(const QObject* receiver, const char* method) {
|
||||||
{ connect(&m_killButton, SIGNAL(clicked(bool)), receiver, method); }
|
connect(&m_killButton, SIGNAL(clicked(bool)), receiver, method);
|
||||||
void connectFXPressed(const QObject* receiver, const char* method)
|
}
|
||||||
{ connect(&m_fxButton, SIGNAL(pressed()), receiver, method); }
|
void connectFXPressed(const QObject* receiver, const char* method) {
|
||||||
void setFXDown(bool down) { m_fxButton.setDown(down); }
|
connect(&m_fxButton, SIGNAL(pressed()), receiver, method);
|
||||||
void connectVolumeSlider(const QObject* receiver, const char* method)
|
}
|
||||||
{ connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method); }
|
void setFXDown(bool down) { m_fxButton.setDown(down); }
|
||||||
void connectASlider(const QObject* receiver, const char* method)
|
void connectVolumeSlider(const QObject* receiver, const char* method) {
|
||||||
{ connect(&m_aSlider, SIGNAL(valueChanged(int)), receiver, method); }
|
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method);
|
||||||
void connectBSlider(const QObject* receiver, const char* method)
|
}
|
||||||
{ connect(&m_bSlider, SIGNAL(valueChanged(int)), receiver, method); }
|
void connectASlider(const QObject* receiver, const char* method) {
|
||||||
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
|
connect(&m_aSlider, SIGNAL(valueChanged(int)), receiver, method);
|
||||||
|
}
|
||||||
|
void connectBSlider(const QObject* receiver, const char* method) {
|
||||||
|
connect(&m_bSlider, SIGNAL(valueChanged(int)), receiver, method);
|
||||||
|
}
|
||||||
|
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void volumeChanged(int vol);
|
void volumeChanged(int vol);
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatusBarFocus : public QObject
|
class StatusBarFocus : public QObject {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
QString m_message;
|
||||||
QString m_message;
|
|
||||||
public:
|
public:
|
||||||
explicit StatusBarFocus(StatusBarWidget* statusWidget)
|
explicit StatusBarFocus(StatusBarWidget* statusWidget) : QObject(statusWidget) {}
|
||||||
: QObject(statusWidget) {}
|
~StatusBarFocus() { exit(); }
|
||||||
~StatusBarFocus() { exit(); }
|
void setMessage(const QString& message);
|
||||||
void setMessage(const QString& message);
|
void enter();
|
||||||
void enter();
|
void exit();
|
||||||
void exit();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,190 +14,181 @@
|
||||||
|
|
||||||
class EffectListing;
|
class EffectListing;
|
||||||
|
|
||||||
struct EffectIntrospection
|
struct EffectIntrospection {
|
||||||
{
|
struct Field {
|
||||||
struct Field
|
enum class Type { Invalid, UInt32, UInt32x8, Float };
|
||||||
{
|
Type m_tp;
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
UInt32,
|
|
||||||
UInt32x8,
|
|
||||||
Float
|
|
||||||
};
|
|
||||||
Type m_tp;
|
|
||||||
std::string_view m_name;
|
|
||||||
float m_min, m_max, m_default;
|
|
||||||
};
|
|
||||||
amuse::EffectType m_tp;
|
|
||||||
std::string_view m_name;
|
std::string_view m_name;
|
||||||
std::string_view m_description;
|
float m_min, m_max, m_default;
|
||||||
Field m_fields[7];
|
};
|
||||||
|
amuse::EffectType m_tp;
|
||||||
|
std::string_view m_name;
|
||||||
|
std::string_view m_description;
|
||||||
|
Field m_fields[7];
|
||||||
};
|
};
|
||||||
|
|
||||||
class Uint32X8Popup : public QFrame
|
class Uint32X8Popup : public QFrame {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
FieldSlider* m_sliders[8];
|
||||||
FieldSlider* m_sliders[8];
|
|
||||||
public:
|
public:
|
||||||
explicit Uint32X8Popup(int min, int max, QWidget* parent = Q_NULLPTR);
|
explicit Uint32X8Popup(int min, int max, QWidget* parent = Q_NULLPTR);
|
||||||
void setValue(int chanIdx, int val);
|
void setValue(int chanIdx, int val);
|
||||||
private slots:
|
private slots:
|
||||||
void doValueChanged(int val);
|
void doValueChanged(int val);
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(int chanIdx, int val);
|
void valueChanged(int chanIdx, int val);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Uint32X8Button : public QPushButton
|
class Uint32X8Button : public QPushButton {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
Uint32X8Popup* m_popup;
|
||||||
Uint32X8Popup* m_popup;
|
|
||||||
public:
|
public:
|
||||||
explicit Uint32X8Button(int min, int max, QWidget* parent = Q_NULLPTR);
|
explicit Uint32X8Button(int min, int max, QWidget* parent = Q_NULLPTR);
|
||||||
void paintEvent(QPaintEvent* event);
|
void paintEvent(QPaintEvent* event);
|
||||||
Uint32X8Popup* popup() const { return m_popup; }
|
Uint32X8Popup* popup() const { return m_popup; }
|
||||||
QStyleOptionComboBox comboStyleOption() const;
|
QStyleOptionComboBox comboStyleOption() const;
|
||||||
private slots:
|
private slots:
|
||||||
void onPressed();
|
void onPressed();
|
||||||
};
|
};
|
||||||
|
|
||||||
class EffectWidget : public QWidget
|
class EffectWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class EffectListing;
|
||||||
friend class EffectListing;
|
QFont m_numberFont;
|
||||||
QFont m_numberFont;
|
QLabel m_titleLabel;
|
||||||
QLabel m_titleLabel;
|
ListingDeleteButton m_deleteButton;
|
||||||
ListingDeleteButton m_deleteButton;
|
QStaticText m_numberText;
|
||||||
QStaticText m_numberText;
|
int m_index = -1;
|
||||||
int m_index = -1;
|
amuse::EffectBaseTypeless* m_effect;
|
||||||
amuse::EffectBaseTypeless* m_effect;
|
const EffectIntrospection* m_introspection;
|
||||||
const EffectIntrospection* m_introspection;
|
void setIndex(int index);
|
||||||
void setIndex(int index);
|
|
||||||
private slots:
|
private slots:
|
||||||
void numChanged(int);
|
void numChanged(int);
|
||||||
void numChanged(double);
|
void numChanged(double);
|
||||||
void chanNumChanged(int, int);
|
void chanNumChanged(int, int);
|
||||||
void deleteClicked();
|
void deleteClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type);
|
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect, amuse::EffectType type);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EffectListing* getParent() const;
|
EffectListing* getParent() const;
|
||||||
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect);
|
explicit EffectWidget(QWidget* parent, amuse::EffectBaseTypeless* effect);
|
||||||
explicit EffectWidget(QWidget* parent, amuse::EffectType op);
|
explicit EffectWidget(QWidget* parent, amuse::EffectType op);
|
||||||
void paintEvent(QPaintEvent* event);
|
void paintEvent(QPaintEvent* event);
|
||||||
QString getText() const { return m_titleLabel.text(); }
|
QString getText() const { return m_titleLabel.text(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class EffectWidgetContainer : public QWidget
|
class EffectWidgetContainer : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class EffectListing;
|
||||||
friend class EffectListing;
|
EffectWidget* m_effectWidget;
|
||||||
EffectWidget* m_effectWidget;
|
QPropertyAnimation* m_animation = nullptr;
|
||||||
QPropertyAnimation* m_animation = nullptr;
|
void animateOpen();
|
||||||
void animateOpen();
|
void animateClosed();
|
||||||
void animateClosed();
|
void snapOpen();
|
||||||
void snapOpen();
|
void snapClosed();
|
||||||
void snapClosed();
|
|
||||||
private slots:
|
private slots:
|
||||||
void animationDestroyed();
|
void animationDestroyed();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <class..._Args>
|
template <class... _Args>
|
||||||
EffectWidgetContainer(QWidget* parent, _Args&&... args);
|
EffectWidgetContainer(QWidget* parent, _Args&&... args);
|
||||||
};
|
};
|
||||||
|
|
||||||
class EffectListing : public QWidget
|
class EffectListing : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class EffectWidget;
|
||||||
friend class EffectWidget;
|
friend class StudioSetupWidget;
|
||||||
friend class StudioSetupWidget;
|
amuse::Submix* m_submix = nullptr;
|
||||||
amuse::Submix* m_submix = nullptr;
|
QVBoxLayout* m_layout;
|
||||||
QVBoxLayout* m_layout;
|
QLayoutItem* m_dragItem = nullptr;
|
||||||
QLayoutItem* m_dragItem = nullptr;
|
int m_origIdx = -1;
|
||||||
int m_origIdx = -1;
|
int m_dragOpenIdx = -1;
|
||||||
int m_dragOpenIdx = -1;
|
EffectWidgetContainer* m_prevDragOpen = nullptr;
|
||||||
EffectWidgetContainer* m_prevDragOpen = nullptr;
|
int m_autoscrollTimer = -1;
|
||||||
int m_autoscrollTimer = -1;
|
int m_autoscrollDelta = 0;
|
||||||
int m_autoscrollDelta = 0;
|
QWidget* m_autoscrollSource = nullptr;
|
||||||
QWidget* m_autoscrollSource = nullptr;
|
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
|
||||||
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
|
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
|
||||||
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
|
void stopAutoscroll();
|
||||||
void stopAutoscroll();
|
bool beginDrag(EffectWidget* widget);
|
||||||
bool beginDrag(EffectWidget* widget);
|
void endDrag();
|
||||||
void endDrag();
|
void cancelDrag();
|
||||||
void cancelDrag();
|
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
||||||
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
|
void moveDrag(EffectWidget* widget, 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);
|
||||||
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
|
void insertDragout();
|
||||||
void insertDragout();
|
void insert(amuse::EffectType type, const QString& text);
|
||||||
void insert(amuse::EffectType type, const QString& text);
|
void deleteEffect(int index);
|
||||||
void deleteEffect(int index);
|
void reindex();
|
||||||
void reindex();
|
void clear();
|
||||||
void clear();
|
|
||||||
public:
|
public:
|
||||||
explicit EffectListing(QWidget* parent = Q_NULLPTR);
|
explicit EffectListing(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(amuse::Submix* submix);
|
bool loadData(amuse::Submix* submix);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
void timerEvent(QTimerEvent* event);
|
void timerEvent(QTimerEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class EffectCatalogueItem : public QWidget
|
class EffectCatalogueItem : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
amuse::EffectType m_type;
|
||||||
amuse::EffectType m_type;
|
QLabel m_iconLab;
|
||||||
QLabel m_iconLab;
|
QLabel m_label;
|
||||||
QLabel m_label;
|
|
||||||
public:
|
public:
|
||||||
explicit EffectCatalogueItem(amuse::EffectType type, const QString& name,
|
explicit EffectCatalogueItem(amuse::EffectType type, const QString& name, const QString& doc,
|
||||||
const QString& doc, QWidget* parent = Q_NULLPTR);
|
QWidget* parent = Q_NULLPTR);
|
||||||
explicit EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent = Q_NULLPTR);
|
explicit EffectCatalogueItem(const EffectCatalogueItem& other, QWidget* parent = Q_NULLPTR);
|
||||||
amuse::EffectType getType() const { return m_type; }
|
amuse::EffectType getType() const { return m_type; }
|
||||||
QString getText() const { return m_label.text(); }
|
QString getText() const { return m_label.text(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class EffectCatalogue : public QTreeWidget
|
class EffectCatalogue : public QTreeWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
explicit EffectCatalogue(QWidget* parent = Q_NULLPTR);
|
explicit EffectCatalogue(QWidget* parent = Q_NULLPTR);
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
void mouseMoveEvent(QMouseEvent* event);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
class StudioSetupWidget : public QWidget
|
class StudioSetupWidget : public QWidget {
|
||||||
{
|
Q_OBJECT
|
||||||
Q_OBJECT
|
friend class EffectCatalogue;
|
||||||
friend class EffectCatalogue;
|
QSplitter* m_splitter;
|
||||||
QSplitter* m_splitter;
|
QTabWidget* m_tabs;
|
||||||
QTabWidget* m_tabs;
|
EffectListing* m_listing[2];
|
||||||
EffectListing* m_listing[2];
|
EffectCatalogue* m_catalogue;
|
||||||
EffectCatalogue* m_catalogue;
|
EffectWidget* m_draggedCmd = nullptr;
|
||||||
EffectWidget* m_draggedCmd = nullptr;
|
EffectCatalogueItem* m_draggedItem = nullptr;
|
||||||
EffectCatalogueItem* m_draggedItem = nullptr;
|
QPoint m_draggedPt;
|
||||||
QPoint m_draggedPt;
|
int m_dragInsertIdx = -1;
|
||||||
int m_dragInsertIdx = -1;
|
EffectListing* getCurrentListing() const;
|
||||||
EffectListing* getCurrentListing() const;
|
void beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
||||||
void beginCommandDrag(EffectWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
void beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
||||||
void beginCatalogueDrag(EffectCatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
|
||||||
public:
|
public:
|
||||||
explicit StudioSetupWidget(QWidget* parent = Q_NULLPTR);
|
explicit StudioSetupWidget(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(amuse::Studio* studio);
|
bool loadData(amuse::Studio* studio);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
void mouseMoveEvent(QMouseEvent* event);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
void keyPressEvent(QKeyEvent* event);
|
void keyPressEvent(QKeyEvent* event);
|
||||||
|
|
||||||
void hideEvent(QHideEvent* event);
|
void hideEvent(QHideEvent* event);
|
||||||
void showEvent(QShowEvent* event);
|
void showEvent(QShowEvent* event);
|
||||||
void updateWindowPosition();
|
void updateWindowPosition();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
|
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void hidden();
|
void hidden();
|
||||||
void shown();
|
void shown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
149
Editor/main.cpp
149
Editor/main.cpp
|
@ -17,106 +17,103 @@ void MacOSSetDarkAppearance();
|
||||||
|
|
||||||
extern "C" const uint8_t MAINICON_QT[];
|
extern "C" const uint8_t MAINICON_QT[];
|
||||||
|
|
||||||
static QIcon MakeAppIcon()
|
static QIcon MakeAppIcon() {
|
||||||
{
|
QIcon ret;
|
||||||
QIcon ret;
|
|
||||||
|
|
||||||
const uint8_t* ptr = MAINICON_QT;
|
const uint8_t* ptr = MAINICON_QT;
|
||||||
for (int i = 0; i < 6; ++i)
|
for (int i = 0; i < 6; ++i) {
|
||||||
{
|
uint32_t size = *reinterpret_cast<const uint32_t*>(ptr);
|
||||||
uint32_t size = *reinterpret_cast<const uint32_t*>(ptr);
|
ptr += 4;
|
||||||
ptr += 4;
|
|
||||||
|
|
||||||
QPixmap pm;
|
QPixmap pm;
|
||||||
pm.loadFromData(ptr, size);
|
pm.loadFromData(ptr, size);
|
||||||
ret.addPixmap(pm);
|
ret.addPixmap(pm);
|
||||||
ptr += size;
|
ptr += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is for adapting the get*Name methods */
|
/* This is for adapting the get*Name methods */
|
||||||
class BooInterface : public boo::IApplication
|
class BooInterface : public boo::IApplication {
|
||||||
{
|
std::vector<boo::SystemString> m_args;
|
||||||
std::vector<boo::SystemString> m_args;
|
void _deletedWindow(boo::IWindow* window) {}
|
||||||
void _deletedWindow(boo::IWindow* window) {}
|
|
||||||
public:
|
public:
|
||||||
EPlatformType getPlatformType() const { return EPlatformType::Qt; }
|
EPlatformType getPlatformType() const { return EPlatformType::Qt; }
|
||||||
|
|
||||||
int run() { return 0; }
|
int run() { return 0; }
|
||||||
boo::SystemStringView getUniqueName() const { return _SYS_STR("amuse-gui"sv); }
|
boo::SystemStringView getUniqueName() const { return _SYS_STR("amuse-gui"sv); }
|
||||||
boo::SystemStringView getFriendlyName() const { return _SYS_STR("Amuse"sv); }
|
boo::SystemStringView getFriendlyName() const { return _SYS_STR("Amuse"sv); }
|
||||||
boo::SystemStringView getProcessName() const { return _SYS_STR("amuse-gui"sv); }
|
boo::SystemStringView getProcessName() const { return _SYS_STR("amuse-gui"sv); }
|
||||||
const std::vector<boo::SystemString>& getArgs() const { return m_args; }
|
const std::vector<boo::SystemString>& getArgs() const { return m_args; }
|
||||||
|
|
||||||
/* Constructors/initializers for sub-objects */
|
/* Constructors/initializers for sub-objects */
|
||||||
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) { return {}; }
|
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
MainWindow* g_MainWindow = nullptr;
|
MainWindow* g_MainWindow = nullptr;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[]) {
|
||||||
{
|
QApplication::setAttribute(Qt::AA_Use96Dpi);
|
||||||
QApplication::setAttribute(Qt::AA_Use96Dpi);
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
#endif
|
#endif
|
||||||
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
|
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
QApplication::setWindowIcon(MakeAppIcon());
|
QApplication::setWindowIcon(MakeAppIcon());
|
||||||
|
|
||||||
a.setOrganizationName("AxioDL");
|
a.setOrganizationName("AxioDL");
|
||||||
a.setApplicationName("Amuse");
|
a.setApplicationName("Amuse");
|
||||||
|
|
||||||
QPalette darkPalette;
|
QPalette darkPalette;
|
||||||
darkPalette.setColor(QPalette::Window, QColor(53,53,53));
|
darkPalette.setColor(QPalette::Window, QColor(53, 53, 53));
|
||||||
darkPalette.setColor(QPalette::WindowText, Qt::white);
|
darkPalette.setColor(QPalette::WindowText, Qt::white);
|
||||||
darkPalette.setColor(QPalette::Base, QColor(42,42,42));
|
darkPalette.setColor(QPalette::Base, QColor(42, 42, 42));
|
||||||
darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25,25,25,53));
|
darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25, 25, 25, 53));
|
||||||
darkPalette.setColor(QPalette::AlternateBase, QColor(53,53,53));
|
darkPalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
|
||||||
darkPalette.setColor(QPalette::ToolTipBase, QColor(42,42,42));
|
darkPalette.setColor(QPalette::ToolTipBase, QColor(42, 42, 42));
|
||||||
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
|
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
|
||||||
darkPalette.setColor(QPalette::Text, Qt::white);
|
darkPalette.setColor(QPalette::Text, Qt::white);
|
||||||
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255,255,255,120));
|
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::Disabled, QPalette::Light, QColor(0, 0, 0, 0));
|
||||||
darkPalette.setColor(QPalette::Button, QColor(53,53,53));
|
darkPalette.setColor(QPalette::Button, QColor(53, 53, 53));
|
||||||
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53,53,53,53));
|
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53, 53, 53, 53));
|
||||||
darkPalette.setColor(QPalette::ButtonText, Qt::white);
|
darkPalette.setColor(QPalette::ButtonText, Qt::white);
|
||||||
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255,255,255,120));
|
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255, 255, 255, 120));
|
||||||
darkPalette.setColor(QPalette::BrightText, Qt::red);
|
darkPalette.setColor(QPalette::BrightText, Qt::red);
|
||||||
darkPalette.setColor(QPalette::Link, QColor(42,130,218));
|
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||||
darkPalette.setColor(QPalette::Highlight, 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::Disabled, QPalette::Highlight, QColor(42, 130, 218, 53));
|
||||||
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
|
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
|
||||||
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255,255,255,120));
|
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255, 255, 255, 120));
|
||||||
a.setPalette(darkPalette);
|
a.setPalette(darkPalette);
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
MacOSSetDarkAppearance();
|
MacOSSetDarkAppearance();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
logvisor::RegisterConsoleLogger();
|
logvisor::RegisterConsoleLogger();
|
||||||
logvisor::RegisterStandardExceptions();
|
logvisor::RegisterStandardExceptions();
|
||||||
|
|
||||||
BooInterface booApp;
|
BooInterface booApp;
|
||||||
boo::APP = &booApp;
|
boo::APP = &booApp;
|
||||||
|
|
||||||
Q_INIT_RESOURCE(translation_res);
|
Q_INIT_RESOURCE(translation_res);
|
||||||
QTranslator translator;
|
QTranslator translator;
|
||||||
if (translator.load(QLocale(), QLatin1String("lang"), QLatin1String("_"), QLatin1String(":/translations")))
|
if (translator.load(QLocale(), QLatin1String("lang"), QLatin1String("_"), QLatin1String(":/translations")))
|
||||||
a.installTranslator(&translator);
|
a.installTranslator(&translator);
|
||||||
|
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
g_MainWindow = &w;
|
g_MainWindow = &w;
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.process(a);
|
parser.process(a);
|
||||||
QStringList args = parser.positionalArguments();
|
QStringList args = parser.positionalArguments();
|
||||||
if (!args.empty())
|
if (!args.empty())
|
||||||
w.openProject(args.back());
|
w.openProject(args.back());
|
||||||
|
|
||||||
return a.exec();
|
return a.exec();
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,8 +3,7 @@
|
||||||
#include <Shellapi.h>
|
#include <Shellapi.h>
|
||||||
#include <Shlwapi.h>
|
#include <Shlwapi.h>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
static const wchar_t* const GMNames[128] = {L"Acoustic Grand Piano",
|
static const wchar_t* const GMNames[128] = {L"Acoustic Grand Piano",
|
||||||
L"Bright Acoustic Piano",
|
L"Bright Acoustic Piano",
|
||||||
|
@ -154,434 +153,379 @@ static const wchar_t* const GMPercNames[128] = {
|
||||||
L"Hi Wood Block", L"Low Wood Block", L"Mute Cuica", L"Open Cuica", L"Mute Triangle",
|
L"Hi Wood Block", L"Low Wood Block", L"Mute Cuica", L"Open Cuica", L"Mute Triangle",
|
||||||
L"Open Triangle"};
|
L"Open Triangle"};
|
||||||
|
|
||||||
bool AudioGroupDataCollection::loadProj()
|
bool AudioGroupDataCollection::loadProj() {
|
||||||
{
|
std::wstring path = m_path + L"\\proj";
|
||||||
std::wstring path = m_path + L"\\proj";
|
athena::io::FileReader r(path, 1024 * 32, false);
|
||||||
athena::io::FileReader r(path, 1024 * 32, false);
|
if (r.hasError())
|
||||||
if (r.hasError())
|
return false;
|
||||||
return false;
|
std::vector<uint8_t>& ret = m_projData;
|
||||||
std::vector<uint8_t>& ret = m_projData;
|
size_t len = r.length();
|
||||||
size_t len = r.length();
|
ret.resize(len);
|
||||||
ret.resize(len);
|
r.readUBytesToBuf(ret.data(), len);
|
||||||
r.readUBytesToBuf(ret.data(), len);
|
return ret.size() != 0;
|
||||||
return ret.size() != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroupDataCollection::loadPool()
|
bool AudioGroupDataCollection::loadPool() {
|
||||||
{
|
std::wstring path = m_path + L"\\pool";
|
||||||
std::wstring path = m_path + L"\\pool";
|
athena::io::FileReader r(path, 1024 * 32, false);
|
||||||
athena::io::FileReader r(path, 1024 * 32, false);
|
if (r.hasError())
|
||||||
if (r.hasError())
|
return false;
|
||||||
return false;
|
std::vector<uint8_t>& ret = m_poolData;
|
||||||
std::vector<uint8_t>& ret = m_poolData;
|
size_t len = r.length();
|
||||||
size_t len = r.length();
|
ret.resize(len);
|
||||||
ret.resize(len);
|
r.readUBytesToBuf(ret.data(), len);
|
||||||
r.readUBytesToBuf(ret.data(), len);
|
return ret.size() != 0;
|
||||||
return ret.size() != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroupDataCollection::loadSdir()
|
bool AudioGroupDataCollection::loadSdir() {
|
||||||
{
|
std::wstring path = m_path + L"\\sdir";
|
||||||
std::wstring path = m_path + L"\\sdir";
|
athena::io::FileReader r(path, 1024 * 32, false);
|
||||||
athena::io::FileReader r(path, 1024 * 32, false);
|
if (r.hasError())
|
||||||
if (r.hasError())
|
return false;
|
||||||
return false;
|
std::vector<uint8_t>& ret = m_sdirData;
|
||||||
std::vector<uint8_t>& ret = m_sdirData;
|
size_t len = r.length();
|
||||||
size_t len = r.length();
|
ret.resize(len);
|
||||||
ret.resize(len);
|
r.readUBytesToBuf(ret.data(), len);
|
||||||
r.readUBytesToBuf(ret.data(), len);
|
return ret.size() != 0;
|
||||||
return ret.size() != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroupDataCollection::loadSamp()
|
bool AudioGroupDataCollection::loadSamp() {
|
||||||
{
|
std::wstring path = m_path + L"\\samp";
|
||||||
std::wstring path = m_path + L"\\samp";
|
athena::io::FileReader r(path, 1024 * 32, false);
|
||||||
athena::io::FileReader r(path, 1024 * 32, false);
|
if (r.hasError())
|
||||||
if (r.hasError())
|
return false;
|
||||||
return false;
|
std::vector<uint8_t>& ret = m_sampData;
|
||||||
std::vector<uint8_t>& ret = m_sampData;
|
size_t len = r.length();
|
||||||
size_t len = r.length();
|
ret.resize(len);
|
||||||
ret.resize(len);
|
r.readUBytesToBuf(ret.data(), len);
|
||||||
r.readUBytesToBuf(ret.data(), len);
|
return ret.size() != 0;
|
||||||
return ret.size() != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroupDataCollection::loadMeta()
|
bool AudioGroupDataCollection::loadMeta() {
|
||||||
{
|
std::wstring path = m_path + L"\\meta";
|
||||||
std::wstring path = m_path + L"\\meta";
|
athena::io::FileReader r(path, 1024 * 32, false);
|
||||||
athena::io::FileReader r(path, 1024 * 32, false);
|
if (r.hasError())
|
||||||
if (r.hasError())
|
return false;
|
||||||
return false;
|
std::experimental::optional<MetaData>& ret = m_metaData;
|
||||||
std::experimental::optional<MetaData>& ret = m_metaData;
|
ret.emplace(r);
|
||||||
ret.emplace(r);
|
return ret.operator bool();
|
||||||
return ret.operator bool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioGroupDataCollection::AudioGroupDataCollection(std::wstring_view path, std::wstring_view name)
|
AudioGroupDataCollection::AudioGroupDataCollection(std::wstring_view path, std::wstring_view name)
|
||||||
: m_path(path), m_name(name)
|
: m_path(path), m_name(name) {}
|
||||||
{
|
|
||||||
|
bool AudioGroupDataCollection::_attemptLoad() {
|
||||||
|
if (m_metaData && m_loadedData && m_loadedGroup)
|
||||||
|
return true;
|
||||||
|
if (!loadProj())
|
||||||
|
return false;
|
||||||
|
if (!loadPool())
|
||||||
|
return false;
|
||||||
|
if (!loadSdir())
|
||||||
|
return false;
|
||||||
|
if (!loadSamp())
|
||||||
|
return false;
|
||||||
|
if (!loadMeta())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _indexData();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroupDataCollection::_attemptLoad()
|
bool AudioGroupDataCollection::_indexData() {
|
||||||
{
|
switch (m_metaData->fmt) {
|
||||||
if (m_metaData && m_loadedData && m_loadedGroup)
|
case amuse::DataFormat::GCN:
|
||||||
return true;
|
default:
|
||||||
if (!loadProj())
|
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(), m_sdirData.data(),
|
||||||
return false;
|
m_sdirData.size(), m_sampData.data(), m_sampData.size(), amuse::GCNDataTag{});
|
||||||
if (!loadPool())
|
break;
|
||||||
return false;
|
case amuse::DataFormat::N64:
|
||||||
if (!loadSdir())
|
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(), m_sdirData.data(),
|
||||||
return false;
|
m_sdirData.size(), m_sampData.data(), m_sampData.size(), m_metaData->absOffs,
|
||||||
if (!loadSamp())
|
amuse::N64DataTag{});
|
||||||
return false;
|
break;
|
||||||
if (!loadMeta())
|
case amuse::DataFormat::PC:
|
||||||
return false;
|
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(), m_sdirData.data(),
|
||||||
|
m_sdirData.size(), m_sampData.data(), m_sampData.size(), m_metaData->absOffs,
|
||||||
|
amuse::PCDataTag{});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return _indexData();
|
return m_loadedData.operator bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroupDataCollection::_indexData()
|
void AudioGroupDataCollection::addToEngine(amuse::Engine& engine) {
|
||||||
{
|
m_loadedGroup = engine.addAudioGroup(*m_loadedData);
|
||||||
switch (m_metaData->fmt)
|
m_groupTokens.clear();
|
||||||
|
if (m_loadedGroup) {
|
||||||
|
m_groupTokens.reserve(m_loadedGroup->getProj().songGroups().size() + m_loadedGroup->getProj().sfxGroups().size());
|
||||||
|
|
||||||
{
|
{
|
||||||
case amuse::DataFormat::GCN:
|
const auto& songGroups = m_loadedGroup->getProj().songGroups();
|
||||||
default:
|
std::map<int, const amuse::SongGroupIndex*> sortGroups;
|
||||||
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
|
for (const auto& pair : songGroups)
|
||||||
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
|
sortGroups[pair.first] = &pair.second;
|
||||||
amuse::GCNDataTag{});
|
for (const auto& pair : sortGroups)
|
||||||
break;
|
m_groupTokens.emplace_back(pair.first, pair.second);
|
||||||
case amuse::DataFormat::N64:
|
|
||||||
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
|
|
||||||
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
|
|
||||||
m_metaData->absOffs, amuse::N64DataTag{});
|
|
||||||
break;
|
|
||||||
case amuse::DataFormat::PC:
|
|
||||||
m_loadedData.emplace(m_projData.data(), m_projData.size(), m_poolData.data(), m_poolData.size(),
|
|
||||||
m_sdirData.data(), m_sdirData.size(), m_sampData.data(), m_sampData.size(),
|
|
||||||
m_metaData->absOffs, amuse::PCDataTag{});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_loadedData.operator bool();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGroupDataCollection::addToEngine(amuse::Engine& engine)
|
|
||||||
{
|
|
||||||
m_loadedGroup = engine.addAudioGroup(*m_loadedData);
|
|
||||||
m_groupTokens.clear();
|
|
||||||
if (m_loadedGroup)
|
|
||||||
{
|
{
|
||||||
m_groupTokens.reserve(m_loadedGroup->getProj().songGroups().size() +
|
const auto& sfxGroups = m_loadedGroup->getProj().sfxGroups();
|
||||||
m_loadedGroup->getProj().sfxGroups().size());
|
std::map<int, const amuse::SFXGroupIndex*> sortGroups;
|
||||||
|
for (const auto& pair : sfxGroups)
|
||||||
{
|
sortGroups[pair.first] = &pair.second;
|
||||||
const auto& songGroups = m_loadedGroup->getProj().songGroups();
|
for (const auto& pair : sortGroups)
|
||||||
std::map<int, const amuse::SongGroupIndex*> sortGroups;
|
m_groupTokens.emplace_back(pair.first, pair.second);
|
||||||
for (const auto& pair : songGroups)
|
|
||||||
sortGroups[pair.first] = &pair.second;
|
|
||||||
for (const auto& pair : sortGroups)
|
|
||||||
m_groupTokens.emplace_back(pair.first, pair.second);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const auto& sfxGroups = m_loadedGroup->getProj().sfxGroups();
|
|
||||||
std::map<int, const amuse::SFXGroupIndex*> sortGroups;
|
|
||||||
for (const auto& pair : sfxGroups)
|
|
||||||
sortGroups[pair.first] = &pair.second;
|
|
||||||
for (const auto& pair : sortGroups)
|
|
||||||
m_groupTokens.emplace_back(pair.first, pair.second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupDataCollection::removeFromEngine(amuse::Engine& engine) const { engine.removeAudioGroup(*m_loadedData); }
|
void AudioGroupDataCollection::removeFromEngine(amuse::Engine& engine) const { engine.removeAudioGroup(*m_loadedData); }
|
||||||
|
|
||||||
AudioGroupCollection::AudioGroupCollection(std::wstring_view path, std::wstring_view name)
|
AudioGroupCollection::AudioGroupCollection(std::wstring_view path, std::wstring_view name)
|
||||||
: m_path(path), m_name(name)
|
: m_path(path), m_name(name) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGroupCollection::addCollection(
|
void AudioGroupCollection::addCollection(
|
||||||
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
|
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection) {
|
||||||
{
|
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection) {
|
||||||
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection)
|
std::wstring collectionPath = m_path + L'\\' + pair.first;
|
||||||
{
|
|
||||||
std::wstring collectionPath = m_path + L'\\' + pair.first;
|
|
||||||
|
|
||||||
amuse::IntrusiveAudioGroupData& dataIn = pair.second;
|
amuse::IntrusiveAudioGroupData& dataIn = pair.second;
|
||||||
auto search = m_groups.find(pair.first);
|
auto search = m_groups.find(pair.first);
|
||||||
if (search == m_groups.end())
|
if (search == m_groups.end()) {
|
||||||
{
|
search =
|
||||||
search =
|
m_groups.emplace(pair.first, std::make_unique<AudioGroupDataCollection>(collectionPath, pair.first)).first;
|
||||||
m_groups.emplace(pair.first, std::make_unique<AudioGroupDataCollection>(collectionPath, pair.first))
|
|
||||||
.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioGroupDataCollection& dataCollection = *search->second;
|
|
||||||
dataCollection.m_projData.resize(dataIn.getProjSize());
|
|
||||||
memmove(dataCollection.m_projData.data(), dataIn.getProj(), dataIn.getProjSize());
|
|
||||||
|
|
||||||
dataCollection.m_poolData.resize(dataIn.getPoolSize());
|
|
||||||
memmove(dataCollection.m_poolData.data(), dataIn.getPool(), dataIn.getPoolSize());
|
|
||||||
|
|
||||||
dataCollection.m_sdirData.resize(dataIn.getSdirSize());
|
|
||||||
memmove(dataCollection.m_sdirData.data(), dataIn.getSdir(), dataIn.getSdirSize());
|
|
||||||
|
|
||||||
dataCollection.m_sampData.resize(dataIn.getSampSize());
|
|
||||||
memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize());
|
|
||||||
|
|
||||||
dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true);
|
|
||||||
dataCollection._indexData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioGroupDataCollection& dataCollection = *search->second;
|
||||||
|
dataCollection.m_projData.resize(dataIn.getProjSize());
|
||||||
|
memmove(dataCollection.m_projData.data(), dataIn.getProj(), dataIn.getProjSize());
|
||||||
|
|
||||||
|
dataCollection.m_poolData.resize(dataIn.getPoolSize());
|
||||||
|
memmove(dataCollection.m_poolData.data(), dataIn.getPool(), dataIn.getPoolSize());
|
||||||
|
|
||||||
|
dataCollection.m_sdirData.resize(dataIn.getSdirSize());
|
||||||
|
memmove(dataCollection.m_sdirData.data(), dataIn.getSdir(), dataIn.getSdirSize());
|
||||||
|
|
||||||
|
dataCollection.m_sampData.resize(dataIn.getSampSize());
|
||||||
|
memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize());
|
||||||
|
|
||||||
|
dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true);
|
||||||
|
dataCollection._indexData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupCollection::update(AudioGroupFilePresenter& presenter)
|
void AudioGroupCollection::update(AudioGroupFilePresenter& presenter) {
|
||||||
{
|
std::wstring path = m_path + L"\\*";
|
||||||
std::wstring path = m_path + L"\\*";
|
|
||||||
|
|
||||||
WIN32_FIND_DATAW d;
|
WIN32_FIND_DATAW d;
|
||||||
HANDLE dir = FindFirstFileW(path.c_str(), &d);
|
HANDLE dir = FindFirstFileW(path.c_str(), &d);
|
||||||
if (dir == INVALID_HANDLE_VALUE)
|
if (dir == INVALID_HANDLE_VALUE)
|
||||||
return;
|
return;
|
||||||
do
|
do {
|
||||||
{
|
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
|
||||||
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
{
|
std::wstring nameStr(d.cFileName);
|
||||||
std::wstring nameStr(d.cFileName);
|
auto search = m_groups.find(nameStr);
|
||||||
auto search = m_groups.find(nameStr);
|
if (search == m_groups.end()) {
|
||||||
if (search == m_groups.end())
|
search =
|
||||||
{
|
m_groups.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
|
||||||
search =
|
.first;
|
||||||
m_groups
|
search->second->_attemptLoad();
|
||||||
.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
|
}
|
||||||
.first;
|
}
|
||||||
search->second->_attemptLoad();
|
} while (FindNextFileW(dir, &d));
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (FindNextFileW(dir, &d));
|
|
||||||
|
|
||||||
FindClose(dir);
|
FindClose(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupFilePresenter::update()
|
void AudioGroupFilePresenter::update() {
|
||||||
{
|
std::wstring path = m_backend.getUserDir() + L"\\*";
|
||||||
std::wstring path = m_backend.getUserDir() + L"\\*";
|
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>& theMap = m_audioGroupCollections;
|
||||||
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>& theMap = m_audioGroupCollections;
|
|
||||||
|
|
||||||
WIN32_FIND_DATAW d;
|
WIN32_FIND_DATAW d;
|
||||||
HANDLE dir = FindFirstFileW(path.c_str(), &d);
|
HANDLE dir = FindFirstFileW(path.c_str(), &d);
|
||||||
if (dir == INVALID_HANDLE_VALUE)
|
if (dir == INVALID_HANDLE_VALUE)
|
||||||
return;
|
return;
|
||||||
do
|
do {
|
||||||
{
|
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
|
||||||
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
{
|
std::wstring nameStr(d.cFileName);
|
||||||
std::wstring nameStr(d.cFileName);
|
auto search = theMap.find(nameStr);
|
||||||
auto search = theMap.find(nameStr);
|
if (search == theMap.end()) {
|
||||||
if (search == theMap.end())
|
search = theMap
|
||||||
{
|
.emplace(nameStr,
|
||||||
search = theMap
|
std::make_unique<AudioGroupCollection>(m_backend.getUserDir() + L'\\' + nameStr, nameStr))
|
||||||
.emplace(nameStr, std::make_unique<AudioGroupCollection>(
|
.first;
|
||||||
m_backend.getUserDir() + L'\\' + nameStr, nameStr))
|
search->second->update(*this);
|
||||||
.first;
|
}
|
||||||
search->second->update(*this);
|
}
|
||||||
}
|
} while (FindNextFileW(dir, &d));
|
||||||
}
|
|
||||||
} while (FindNextFileW(dir, &d));
|
|
||||||
|
|
||||||
FindClose(dir);
|
FindClose(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupFilePresenter::addCollection(
|
void AudioGroupFilePresenter::addCollection(
|
||||||
std::wstring_view name, std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
|
std::wstring_view name, std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection) {
|
||||||
{
|
std::wstring path = m_backend.getUserDir() + L'\\' + name;
|
||||||
std::wstring path = m_backend.getUserDir() + L'\\' + name;
|
AudioGroupCollection& insert =
|
||||||
AudioGroupCollection& insert =
|
*m_audioGroupCollections.emplace(name, std::make_unique<AudioGroupCollection>(path, name)).first->second;
|
||||||
*m_audioGroupCollections.emplace(name, std::make_unique<AudioGroupCollection>(path, name)).first->second;
|
CreateDirectory(insert.m_path.c_str(), nullptr);
|
||||||
CreateDirectory(insert.m_path.c_str(), nullptr);
|
insert.addCollection(std::move(collection));
|
||||||
insert.addCollection(std::move(collection));
|
|
||||||
|
|
||||||
for (std::pair<const std::wstring, std::unique_ptr<AudioGroupDataCollection>>& pair : insert.m_groups)
|
for (std::pair<const std::wstring, std::unique_ptr<AudioGroupDataCollection>>& pair : insert.m_groups) {
|
||||||
{
|
std::wstring collectionPath = insert.m_path + L'\\' + pair.first;
|
||||||
std::wstring collectionPath = insert.m_path + L'\\' + pair.first;
|
CreateDirectory(collectionPath.c_str(), nullptr);
|
||||||
CreateDirectory(collectionPath.c_str(), nullptr);
|
|
||||||
|
|
||||||
FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb");
|
FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb");
|
||||||
if (fp)
|
if (fp) {
|
||||||
{
|
fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), fp);
|
||||||
fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), fp);
|
fclose(fp);
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fp = _wfopen((collectionPath + L"\\pool").c_str(), L"wb");
|
|
||||||
if (fp)
|
|
||||||
{
|
|
||||||
fwrite(pair.second->m_poolData.data(), 1, pair.second->m_poolData.size(), fp);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fp = _wfopen((collectionPath + L"\\sdir").c_str(), L"wb");
|
|
||||||
if (fp)
|
|
||||||
{
|
|
||||||
fwrite(pair.second->m_sdirData.data(), 1, pair.second->m_sdirData.size(), fp);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fp = _wfopen((collectionPath + L"\\samp").c_str(), L"wb");
|
|
||||||
if (fp)
|
|
||||||
{
|
|
||||||
fwrite(pair.second->m_sampData.data(), 1, pair.second->m_sampData.size(), fp);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
fp = _wfopen((collectionPath + L"\\meta").c_str(), L"wb");
|
|
||||||
if (fp)
|
|
||||||
{
|
|
||||||
fwrite(&*pair.second->m_metaData, 1, sizeof(*pair.second->m_metaData), fp);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGroupFilePresenter::removeCollection(unsigned idx)
|
fp = _wfopen((collectionPath + L"\\pool").c_str(), L"wb");
|
||||||
{
|
if (fp) {
|
||||||
if (idx < m_iteratorVec.size())
|
fwrite(pair.second->m_poolData.data(), 1, pair.second->m_poolData.size(), fp);
|
||||||
{
|
fclose(fp);
|
||||||
CollectionIterator& it = m_iteratorVec[idx];
|
|
||||||
std::wstring collectionPath = it->second->m_path + L'\0';
|
|
||||||
SHFILEOPSTRUCT op = {};
|
|
||||||
op.wFunc = FO_DELETE;
|
|
||||||
op.pFrom = collectionPath.c_str();
|
|
||||||
op.fFlags = FOF_NO_UI;
|
|
||||||
SHFileOperation(&op);
|
|
||||||
m_audioGroupCollections.erase(it);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx)
|
fp = _wfopen((collectionPath + L"\\sdir").c_str(), L"wb");
|
||||||
{
|
if (fp) {
|
||||||
TVINSERTSTRUCT ins = {};
|
fwrite(pair.second->m_sdirData.data(), 1, pair.second->m_sdirData.size(), fp);
|
||||||
ins.item.mask = TVIF_TEXT | TVIF_PARAM;
|
fclose(fp);
|
||||||
ins.hParent = colHandle;
|
|
||||||
ins.hInsertAfter = TVI_LAST;
|
|
||||||
|
|
||||||
m_iteratorVec.clear();
|
|
||||||
m_iteratorVec.reserve(m_groups.size());
|
|
||||||
for (auto it = m_groups.begin(); it != m_groups.end(); ++it)
|
|
||||||
{
|
|
||||||
ins.item.pszText = LPWSTR(it->first.c_str());
|
|
||||||
ins.item.lParam = LPARAM(0x80000000 | (parentIdx << 16) | m_iteratorVec.size());
|
|
||||||
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
|
|
||||||
if (editor.m_selCollectionIdx == parentIdx && editor.m_selFileIdx == m_iteratorVec.size())
|
|
||||||
editor.m_deferredCollectionSel = item;
|
|
||||||
m_iteratorVec.push_back(it);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGroupFilePresenter::populateCollectionColumn(VSTEditor& editor)
|
fp = _wfopen((collectionPath + L"\\samp").c_str(), L"wb");
|
||||||
{
|
if (fp) {
|
||||||
TreeView_DeleteAllItems(editor.m_collectionTree);
|
fwrite(pair.second->m_sampData.data(), 1, pair.second->m_sampData.size(), fp);
|
||||||
TVINSERTSTRUCT ins = {};
|
fclose(fp);
|
||||||
ins.hParent = TVI_ROOT;
|
|
||||||
ins.hInsertAfter = TVI_LAST;
|
|
||||||
ins.item.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
|
|
||||||
|
|
||||||
m_iteratorVec.clear();
|
|
||||||
m_iteratorVec.reserve(m_audioGroupCollections.size());
|
|
||||||
for (auto it = m_audioGroupCollections.begin(); it != m_audioGroupCollections.end(); ++it)
|
|
||||||
{
|
|
||||||
ins.item.cChildren = it->second->m_groups.size() ? 1 : 0;
|
|
||||||
ins.item.pszText = LPWSTR(it->first.c_str());
|
|
||||||
ins.item.lParam = LPARAM(m_iteratorVec.size() << 16);
|
|
||||||
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
|
|
||||||
it->second->populateFiles(editor, item, m_iteratorVec.size());
|
|
||||||
if (editor.m_selCollectionIdx == m_iteratorVec.size() && editor.m_selFileIdx == -1)
|
|
||||||
editor.m_deferredCollectionSel = item;
|
|
||||||
m_iteratorVec.push_back(it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fp = _wfopen((collectionPath + L"\\meta").c_str(), L"wb");
|
||||||
|
if (fp) {
|
||||||
|
fwrite(&*pair.second->m_metaData, 1, sizeof(*pair.second->m_metaData), fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupFilePresenter::populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx)
|
void AudioGroupFilePresenter::removeCollection(unsigned idx) {
|
||||||
{
|
if (idx < m_iteratorVec.size()) {
|
||||||
LVITEM item = {};
|
CollectionIterator& it = m_iteratorVec[idx];
|
||||||
item.mask = LVIF_TEXT;
|
std::wstring collectionPath = it->second->m_path + L'\0';
|
||||||
|
SHFILEOPSTRUCT op = {};
|
||||||
|
op.wFunc = FO_DELETE;
|
||||||
|
op.pFrom = collectionPath.c_str();
|
||||||
|
op.fFlags = FOF_NO_UI;
|
||||||
|
SHFileOperation(&op);
|
||||||
|
m_audioGroupCollections.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListView_DeleteAllItems(editor.m_groupListView);
|
void AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx) {
|
||||||
ListView_DeleteAllItems(editor.m_pageListView);
|
TVINSERTSTRUCT ins = {};
|
||||||
if (collectionIdx < m_iteratorVec.size())
|
ins.item.mask = TVIF_TEXT | TVIF_PARAM;
|
||||||
{
|
ins.hParent = colHandle;
|
||||||
CollectionIterator& it = m_iteratorVec[collectionIdx];
|
ins.hInsertAfter = TVI_LAST;
|
||||||
if (fileIdx < it->second->m_iteratorVec.size())
|
|
||||||
{
|
m_iteratorVec.clear();
|
||||||
size_t idx = 0;
|
m_iteratorVec.reserve(m_groups.size());
|
||||||
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
for (auto it = m_groups.begin(); it != m_groups.end(); ++it) {
|
||||||
for (AudioGroupDataCollection::GroupToken& gtok : git->second->m_groupTokens)
|
ins.item.pszText = LPWSTR(it->first.c_str());
|
||||||
{
|
ins.item.lParam = LPARAM(0x80000000 | (parentIdx << 16) | m_iteratorVec.size());
|
||||||
wchar_t name[256];
|
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
|
||||||
wnsprintf(name, 256, L"%d (%s)", gtok.m_groupId, gtok.m_song ? L"Song" : L"SFX");
|
if (editor.m_selCollectionIdx == parentIdx && editor.m_selFileIdx == m_iteratorVec.size())
|
||||||
item.pszText = name;
|
editor.m_deferredCollectionSel = item;
|
||||||
item.iItem = idx++;
|
m_iteratorVec.push_back(it);
|
||||||
ListView_InsertItem(editor.m_groupListView, &item);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioGroupFilePresenter::populateCollectionColumn(VSTEditor& editor) {
|
||||||
|
TreeView_DeleteAllItems(editor.m_collectionTree);
|
||||||
|
TVINSERTSTRUCT ins = {};
|
||||||
|
ins.hParent = TVI_ROOT;
|
||||||
|
ins.hInsertAfter = TVI_LAST;
|
||||||
|
ins.item.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
|
||||||
|
|
||||||
|
m_iteratorVec.clear();
|
||||||
|
m_iteratorVec.reserve(m_audioGroupCollections.size());
|
||||||
|
for (auto it = m_audioGroupCollections.begin(); it != m_audioGroupCollections.end(); ++it) {
|
||||||
|
ins.item.cChildren = it->second->m_groups.size() ? 1 : 0;
|
||||||
|
ins.item.pszText = LPWSTR(it->first.c_str());
|
||||||
|
ins.item.lParam = LPARAM(m_iteratorVec.size() << 16);
|
||||||
|
HTREEITEM item = TreeView_InsertItem(editor.m_collectionTree, &ins);
|
||||||
|
it->second->populateFiles(editor, item, m_iteratorVec.size());
|
||||||
|
if (editor.m_selCollectionIdx == m_iteratorVec.size() && editor.m_selFileIdx == -1)
|
||||||
|
editor.m_deferredCollectionSel = item;
|
||||||
|
m_iteratorVec.push_back(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioGroupFilePresenter::populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx) {
|
||||||
|
LVITEM item = {};
|
||||||
|
item.mask = LVIF_TEXT;
|
||||||
|
|
||||||
|
ListView_DeleteAllItems(editor.m_groupListView);
|
||||||
|
ListView_DeleteAllItems(editor.m_pageListView);
|
||||||
|
if (collectionIdx < m_iteratorVec.size()) {
|
||||||
|
CollectionIterator& it = m_iteratorVec[collectionIdx];
|
||||||
|
if (fileIdx < it->second->m_iteratorVec.size()) {
|
||||||
|
size_t idx = 0;
|
||||||
|
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
||||||
|
for (AudioGroupDataCollection::GroupToken& gtok : git->second->m_groupTokens) {
|
||||||
|
wchar_t name[256];
|
||||||
|
wnsprintf(name, 256, L"%d (%s)", gtok.m_groupId, gtok.m_song ? L"Song" : L"SFX");
|
||||||
|
item.pszText = name;
|
||||||
|
item.iItem = idx++;
|
||||||
|
ListView_InsertItem(editor.m_groupListView, &item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx) {
|
||||||
|
LVITEM item = {};
|
||||||
|
item.mask = LVIF_TEXT | LVIF_PARAM;
|
||||||
|
|
||||||
|
ListView_DeleteAllItems(editor.m_pageListView);
|
||||||
|
if (collectionIdx < m_iteratorVec.size()) {
|
||||||
|
CollectionIterator& it = m_iteratorVec[collectionIdx];
|
||||||
|
if (fileIdx < it->second->m_iteratorVec.size()) {
|
||||||
|
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
||||||
|
if (groupIdx < git->second->m_groupTokens.size()) {
|
||||||
|
AudioGroupDataCollection::GroupToken& groupTok = git->second->m_groupTokens[groupIdx];
|
||||||
|
if (groupTok.m_song) {
|
||||||
|
std::map<uint8_t, const amuse::SongGroupIndex::PageEntry*> sortPages;
|
||||||
|
for (auto& pair : groupTok.m_song->m_normPages)
|
||||||
|
sortPages[pair.first] = pair.second;
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
for (auto& pair : sortPages) {
|
||||||
|
wchar_t name[256];
|
||||||
|
wnsprintf(name, 256, L"%d (%s)", pair.first, GMNames[pair.first] ? GMNames[pair.first] : L"???");
|
||||||
|
item.pszText = name;
|
||||||
|
item.iItem = idx++;
|
||||||
|
item.lParam = pair.first;
|
||||||
|
ListView_InsertItem(editor.m_pageListView, &item);
|
||||||
|
}
|
||||||
|
|
||||||
|
sortPages.clear();
|
||||||
|
for (auto& pair : groupTok.m_song->m_drumPages)
|
||||||
|
sortPages[pair.first] = pair.second;
|
||||||
|
|
||||||
|
for (auto& pair : sortPages) {
|
||||||
|
wchar_t name[256];
|
||||||
|
wnsprintf(name, 256, L"%d (%s)", pair.first, GMPercNames[pair.first] ? GMPercNames[pair.first] : L"???");
|
||||||
|
item.pszText = name;
|
||||||
|
item.iItem = idx++;
|
||||||
|
item.lParam = 0x80000000 | pair.first;
|
||||||
|
ListView_InsertItem(editor.m_pageListView, &item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} // namespace amuse
|
||||||
void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx)
|
|
||||||
{
|
|
||||||
LVITEM item = {};
|
|
||||||
item.mask = LVIF_TEXT | LVIF_PARAM;
|
|
||||||
|
|
||||||
ListView_DeleteAllItems(editor.m_pageListView);
|
|
||||||
if (collectionIdx < m_iteratorVec.size())
|
|
||||||
{
|
|
||||||
CollectionIterator& it = m_iteratorVec[collectionIdx];
|
|
||||||
if (fileIdx < it->second->m_iteratorVec.size())
|
|
||||||
{
|
|
||||||
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
|
||||||
if (groupIdx < git->second->m_groupTokens.size())
|
|
||||||
{
|
|
||||||
AudioGroupDataCollection::GroupToken& groupTok = git->second->m_groupTokens[groupIdx];
|
|
||||||
if (groupTok.m_song)
|
|
||||||
{
|
|
||||||
std::map<uint8_t, const amuse::SongGroupIndex::PageEntry*> sortPages;
|
|
||||||
for (auto& pair : groupTok.m_song->m_normPages)
|
|
||||||
sortPages[pair.first] = pair.second;
|
|
||||||
|
|
||||||
size_t idx = 0;
|
|
||||||
for (auto& pair : sortPages)
|
|
||||||
{
|
|
||||||
wchar_t name[256];
|
|
||||||
wnsprintf(name, 256, L"%d (%s)", pair.first,
|
|
||||||
GMNames[pair.first] ? GMNames[pair.first] : L"???");
|
|
||||||
item.pszText = name;
|
|
||||||
item.iItem = idx++;
|
|
||||||
item.lParam = pair.first;
|
|
||||||
ListView_InsertItem(editor.m_pageListView, &item);
|
|
||||||
}
|
|
||||||
|
|
||||||
sortPages.clear();
|
|
||||||
for (auto& pair : groupTok.m_song->m_drumPages)
|
|
||||||
sortPages[pair.first] = pair.second;
|
|
||||||
|
|
||||||
for (auto& pair : sortPages)
|
|
||||||
{
|
|
||||||
wchar_t name[256];
|
|
||||||
wnsprintf(name, 256, L"%d (%s)", pair.first,
|
|
||||||
GMPercNames[pair.first] ? GMPercNames[pair.first] : L"???");
|
|
||||||
item.pszText = name;
|
|
||||||
item.iItem = idx++;
|
|
||||||
item.lParam = 0x80000000 | pair.first;
|
|
||||||
ListView_InsertItem(editor.m_pageListView, &item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,105 +10,93 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <CommCtrl.h>
|
#include <CommCtrl.h>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class VSTBackend;
|
class VSTBackend;
|
||||||
class VSTEditor;
|
class VSTEditor;
|
||||||
class AudioGroupFilePresenter;
|
class AudioGroupFilePresenter;
|
||||||
|
|
||||||
struct AudioGroupDataCollection
|
struct AudioGroupDataCollection {
|
||||||
{
|
std::wstring m_path;
|
||||||
std::wstring m_path;
|
std::wstring m_name;
|
||||||
std::wstring m_name;
|
|
||||||
|
|
||||||
std::vector<uint8_t> m_projData;
|
std::vector<uint8_t> m_projData;
|
||||||
std::vector<uint8_t> m_poolData;
|
std::vector<uint8_t> m_poolData;
|
||||||
std::vector<uint8_t> m_sdirData;
|
std::vector<uint8_t> m_sdirData;
|
||||||
std::vector<uint8_t> m_sampData;
|
std::vector<uint8_t> m_sampData;
|
||||||
|
|
||||||
struct MetaData
|
struct MetaData {
|
||||||
{
|
amuse::DataFormat fmt;
|
||||||
amuse::DataFormat fmt;
|
uint32_t absOffs;
|
||||||
uint32_t absOffs;
|
uint32_t active;
|
||||||
uint32_t active;
|
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
||||||
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {}
|
||||||
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
|
MetaData(athena::io::FileReader& r)
|
||||||
{
|
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
|
||||||
}
|
};
|
||||||
MetaData(athena::io::FileReader& r)
|
std::experimental::optional<MetaData> m_metaData;
|
||||||
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::experimental::optional<MetaData> m_metaData;
|
|
||||||
|
|
||||||
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
|
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
|
||||||
const amuse::AudioGroup* m_loadedGroup;
|
const amuse::AudioGroup* m_loadedGroup;
|
||||||
struct GroupToken
|
struct GroupToken {
|
||||||
{
|
int m_groupId;
|
||||||
int m_groupId;
|
const amuse::SongGroupIndex* m_song = nullptr;
|
||||||
const amuse::SongGroupIndex* m_song = nullptr;
|
const amuse::SFXGroupIndex* m_sfx = nullptr;
|
||||||
const amuse::SFXGroupIndex* m_sfx = nullptr;
|
GroupToken(int id, const amuse::SongGroupIndex* song) : m_groupId(id), m_song(song) {}
|
||||||
GroupToken(int id, const amuse::SongGroupIndex* song) : m_groupId(id), m_song(song) {}
|
GroupToken(int id, const amuse::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {}
|
||||||
GroupToken(int id, const amuse::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {}
|
};
|
||||||
};
|
std::vector<GroupToken> m_groupTokens;
|
||||||
std::vector<GroupToken> m_groupTokens;
|
|
||||||
|
|
||||||
bool loadProj();
|
bool loadProj();
|
||||||
bool loadPool();
|
bool loadPool();
|
||||||
bool loadSdir();
|
bool loadSdir();
|
||||||
bool loadSamp();
|
bool loadSamp();
|
||||||
bool loadMeta();
|
bool loadMeta();
|
||||||
|
|
||||||
AudioGroupDataCollection(std::wstring_view path, std::wstring_view name);
|
AudioGroupDataCollection(std::wstring_view path, std::wstring_view name);
|
||||||
bool isDataComplete() const
|
bool isDataComplete() const {
|
||||||
{
|
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
|
||||||
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
|
}
|
||||||
}
|
bool _attemptLoad();
|
||||||
bool _attemptLoad();
|
bool _indexData();
|
||||||
bool _indexData();
|
|
||||||
|
|
||||||
void addToEngine(amuse::Engine& engine);
|
void addToEngine(amuse::Engine& engine);
|
||||||
void removeFromEngine(amuse::Engine& engine) const;
|
void removeFromEngine(amuse::Engine& engine) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioGroupCollection
|
struct AudioGroupCollection {
|
||||||
{
|
using GroupIterator = std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>>::iterator;
|
||||||
using GroupIterator = std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>>::iterator;
|
std::wstring m_path;
|
||||||
std::wstring m_path;
|
std::wstring m_name;
|
||||||
std::wstring m_name;
|
|
||||||
|
|
||||||
std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
||||||
std::vector<GroupIterator> m_iteratorVec;
|
std::vector<GroupIterator> m_iteratorVec;
|
||||||
|
|
||||||
AudioGroupCollection(std::wstring_view path, std::wstring_view name);
|
AudioGroupCollection(std::wstring_view path, std::wstring_view name);
|
||||||
void addCollection(std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
|
void addCollection(std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||||
void update(AudioGroupFilePresenter& presenter);
|
void update(AudioGroupFilePresenter& presenter);
|
||||||
void populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx);
|
void populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioGroupFilePresenter
|
class AudioGroupFilePresenter {
|
||||||
{
|
friend class VSTBackend;
|
||||||
friend class VSTBackend;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using CollectionIterator = std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>::iterator;
|
using CollectionIterator = std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>::iterator;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VSTBackend& m_backend;
|
VSTBackend& m_backend;
|
||||||
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
|
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
|
||||||
std::vector<CollectionIterator> m_iteratorVec;
|
std::vector<CollectionIterator> m_iteratorVec;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {}
|
AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {}
|
||||||
void update();
|
void update();
|
||||||
void populateCollectionColumn(VSTEditor& editor);
|
void populateCollectionColumn(VSTEditor& editor);
|
||||||
void populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx);
|
void populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx);
|
||||||
void populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx);
|
void populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx);
|
||||||
void addCollection(std::wstring_view name,
|
void addCollection(std::wstring_view name,
|
||||||
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
|
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||||
void removeCollection(unsigned idx);
|
void removeCollection(unsigned idx);
|
||||||
VSTBackend& getBackend() { return m_backend; }
|
VSTBackend& getBackend() { return m_backend; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@
|
||||||
#define CONTROL_GROUP 2000
|
#define CONTROL_GROUP 2000
|
||||||
#define CONTROL_RADIOBUTTONLIST 2
|
#define CONTROL_RADIOBUTTONLIST 2
|
||||||
#define CONTROL_RADIOBUTTON1 1
|
#define CONTROL_RADIOBUTTON1 1
|
||||||
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST,
|
#define CONTROL_RADIOBUTTON2 \
|
||||||
// because it is a child control under CONTROL_RADIOBUTTONLIST
|
2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST,
|
||||||
|
// because it is a child control under CONTROL_RADIOBUTTONLIST
|
||||||
|
|
||||||
// IDs for the Task Dialog Buttons
|
// IDs for the Task Dialog Buttons
|
||||||
#define IDC_BASICFILEOPEN 100
|
#define IDC_BASICFILEOPEN 100
|
||||||
|
@ -33,201 +34,179 @@ HWND ghMainWnd = 0;
|
||||||
HINSTANCE ghAppInst = 0;
|
HINSTANCE ghAppInst = 0;
|
||||||
RECT winRect;
|
RECT winRect;
|
||||||
|
|
||||||
class CDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents
|
class CDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
// IUnknown methods
|
// IUnknown methods
|
||||||
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
|
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
|
||||||
{
|
static const QITAB qit[] = {
|
||||||
static const QITAB qit[] = {
|
QITABENT(CDialogEventHandler, IFileDialogEvents),
|
||||||
QITABENT(CDialogEventHandler, IFileDialogEvents),
|
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
|
||||||
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
|
{0},
|
||||||
{0},
|
};
|
||||||
};
|
return QISearch(this, qit, riid, ppv);
|
||||||
return QISearch(this, qit, riid, ppv);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
|
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
|
||||||
|
|
||||||
IFACEMETHODIMP_(ULONG) Release()
|
IFACEMETHODIMP_(ULONG) Release() {
|
||||||
{
|
long cRef = InterlockedDecrement(&_cRef);
|
||||||
long cRef = InterlockedDecrement(&_cRef);
|
if (!cRef)
|
||||||
if (!cRef)
|
delete this;
|
||||||
delete this;
|
return cRef;
|
||||||
return cRef;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// IFileDialogEvents methods
|
// IFileDialogEvents methods
|
||||||
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
|
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
|
||||||
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
|
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
|
||||||
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
|
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
|
||||||
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
|
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
|
||||||
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
|
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
|
||||||
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; };
|
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; };
|
||||||
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd);
|
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd);
|
||||||
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
|
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
|
||||||
|
|
||||||
// IFileDialogControlEvents methods
|
// IFileDialogControlEvents methods
|
||||||
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem);
|
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem);
|
||||||
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
|
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
|
||||||
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) { return S_OK; };
|
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) { return S_OK; };
|
||||||
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) { return S_OK; };
|
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) { return S_OK; };
|
||||||
|
|
||||||
CDialogEventHandler() : _cRef(1){};
|
CDialogEventHandler() : _cRef(1){};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~CDialogEventHandler(){};
|
~CDialogEventHandler(){};
|
||||||
long _cRef;
|
long _cRef;
|
||||||
};
|
};
|
||||||
|
|
||||||
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv);
|
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv);
|
||||||
|
|
||||||
std::wstring openDB()
|
std::wstring openDB() {
|
||||||
{
|
std::wstring ret;
|
||||||
std::wstring ret;
|
CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
|
||||||
|
|
||||||
// Cocreate the file open dialog object
|
// Cocreate the file open dialog object
|
||||||
IFileDialog* pfd = NULL;
|
IFileDialog* pfd = NULL;
|
||||||
|
|
||||||
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
|
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Stuff needed for later
|
||||||
// Stuff needed for later
|
const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}};
|
||||||
const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}};
|
|
||||||
|
|
||||||
// Create event handling
|
// Create event handling
|
||||||
IFileDialogEvents* pfde = NULL;
|
IFileDialogEvents* pfde = NULL;
|
||||||
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
|
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Hook the event handler
|
||||||
// Hook the event handler
|
DWORD dwCookie;
|
||||||
DWORD dwCookie;
|
|
||||||
|
|
||||||
hr = pfd->Advise(pfde, &dwCookie);
|
hr = pfd->Advise(pfde, &dwCookie);
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Set options for the dialog
|
||||||
// Set options for the dialog
|
DWORD dwFlags;
|
||||||
DWORD dwFlags;
|
|
||||||
|
|
||||||
// Get options first so we do not override
|
// Get options first so we do not override
|
||||||
hr = pfd->GetOptions(&dwFlags);
|
hr = pfd->GetOptions(&dwFlags);
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Get shell items only
|
||||||
// Get shell items only
|
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
|
||||||
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Types of files to display (not default)
|
||||||
// Types of files to display (not default)
|
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
|
||||||
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Set default file type to display
|
||||||
// Set default file type to display
|
// hr = pfd->SetDefaultExtension(L"sqlite");
|
||||||
// hr = pfd->SetDefaultExtension(L"sqlite");
|
|
||||||
|
|
||||||
// if (SUCCEEDED(hr))
|
// if (SUCCEEDED(hr))
|
||||||
//{
|
//{
|
||||||
// Show dialog
|
// Show dialog
|
||||||
hr = pfd->Show(NULL);
|
hr = pfd->Show(NULL);
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Get the result once the user clicks on open
|
||||||
// Get the result once the user clicks on open
|
IShellItem* result;
|
||||||
IShellItem* result;
|
|
||||||
|
|
||||||
hr = pfd->GetResult(&result);
|
hr = pfd->GetResult(&result);
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
// Print out the file name
|
||||||
// Print out the file name
|
PWSTR fName = NULL;
|
||||||
PWSTR fName = NULL;
|
|
||||||
|
|
||||||
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
|
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr)) {
|
||||||
{
|
ret.assign(fName);
|
||||||
ret.assign(fName);
|
CoTaskMemFree(fName);
|
||||||
CoTaskMemFree(fName);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
result->Release();
|
result->Release();
|
||||||
}
|
|
||||||
}
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pfd->Unadvise(dwCookie);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pfde->Release();
|
pfd->Unadvise(dwCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
pfd->Release();
|
pfde->Release();
|
||||||
return ret;
|
}
|
||||||
|
|
||||||
|
pfd->Release();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv)
|
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv) {
|
||||||
{
|
*ppv = NULL;
|
||||||
*ppv = NULL;
|
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
|
||||||
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
|
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
|
||||||
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
|
if (SUCCEEDED(hr)) {
|
||||||
if (SUCCEEDED(hr))
|
hr = pDialogEventHandler->QueryInterface(riid, ppv);
|
||||||
{
|
pDialogEventHandler->Release();
|
||||||
hr = pDialogEventHandler->QueryInterface(riid, ppv);
|
}
|
||||||
pDialogEventHandler->Release();
|
return hr;
|
||||||
}
|
|
||||||
return hr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog* pfd)
|
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog* pfd) {
|
||||||
{
|
IFileSaveDialog* pfsd;
|
||||||
IFileSaveDialog* pfsd;
|
HRESULT hr = pfd->QueryInterface(&pfsd);
|
||||||
HRESULT hr = pfd->QueryInterface(&pfsd);
|
if (SUCCEEDED(hr)) {
|
||||||
if (SUCCEEDED(hr))
|
UINT uIndex;
|
||||||
{
|
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
|
||||||
UINT uIndex;
|
if (SUCCEEDED(hr)) {
|
||||||
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
|
IPropertyDescriptionList* pdl = NULL;
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
IPropertyDescriptionList* pdl = NULL;
|
|
||||||
}
|
|
||||||
pfsd->Release();
|
|
||||||
}
|
}
|
||||||
return hr;
|
pfsd->Release();
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IFileDialogControlEvents
|
// IFileDialogControlEvents
|
||||||
// This method gets called when an dialog control item selection happens (radio-button selection. etc).
|
// This method gets called when an dialog control item selection happens (radio-button selection. etc).
|
||||||
// For sample sake, let's react to this event by changing the dialog title.
|
// For sample sake, let's react to this event by changing the dialog title.
|
||||||
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem)
|
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem) {
|
||||||
{
|
IFileDialog* pfd = NULL;
|
||||||
IFileDialog* pfd = NULL;
|
HRESULT hr = pfdc->QueryInterface(&pfd);
|
||||||
HRESULT hr = pfdc->QueryInterface(&pfd);
|
if (SUCCEEDED(hr)) {
|
||||||
if (SUCCEEDED(hr))
|
if (dwIDCtl == CONTROL_RADIOBUTTONLIST) {
|
||||||
{
|
switch (dwIDItem) {
|
||||||
if (dwIDCtl == CONTROL_RADIOBUTTONLIST)
|
case CONTROL_RADIOBUTTON1:
|
||||||
{
|
hr = pfd->SetTitle(L"Longhorn Dialog");
|
||||||
switch (dwIDItem)
|
break;
|
||||||
{
|
|
||||||
case CONTROL_RADIOBUTTON1:
|
|
||||||
hr = pfd->SetTitle(L"Longhorn Dialog");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONTROL_RADIOBUTTON2:
|
case CONTROL_RADIOBUTTON2:
|
||||||
hr = pfd->SetTitle(L"Vista Dialog");
|
hr = pfd->SetTitle(L"Vista Dialog");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
pfd->Release();
|
|
||||||
}
|
}
|
||||||
return hr;
|
pfd->Release();
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,3 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
std::wstring openDB();
|
std::wstring openDB();
|
||||||
|
|
||||||
|
|
|
@ -6,389 +6,343 @@
|
||||||
#undef min
|
#undef min
|
||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
|
struct VSTVoiceEngine : boo::BaseAudioVoiceEngine {
|
||||||
{
|
std::vector<float> m_interleavedBuf;
|
||||||
std::vector<float> m_interleavedBuf;
|
float** m_outputData = nullptr;
|
||||||
float** m_outputData = nullptr;
|
size_t m_renderFrames = 0;
|
||||||
size_t m_renderFrames = 0;
|
size_t m_curBufFrame = 0;
|
||||||
size_t m_curBufFrame = 0;
|
|
||||||
|
|
||||||
boo::AudioChannelSet _getAvailableSet() { return boo::AudioChannelSet::Stereo; }
|
boo::AudioChannelSet _getAvailableSet() { return boo::AudioChannelSet::Stereo; }
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const { return {}; }
|
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const { return {}; }
|
||||||
|
|
||||||
boo::ReceiveFunctor* m_midiReceiver = nullptr;
|
boo::ReceiveFunctor* m_midiReceiver = nullptr;
|
||||||
|
|
||||||
struct MIDIIn : public boo::IMIDIIn
|
struct MIDIIn : public boo::IMIDIIn {
|
||||||
{
|
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) : IMIDIIn(virt, std::move(receiver)) {}
|
||||||
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) : IMIDIIn(virt, std::move(receiver)) {}
|
|
||||||
|
|
||||||
std::string description() const { return "VST MIDI"; }
|
std::string description() const { return "VST MIDI"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver)
|
std::unique_ptr<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) {
|
||||||
{
|
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
|
||||||
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
|
m_midiReceiver = &ret->m_receiver;
|
||||||
m_midiReceiver = &ret->m_receiver;
|
return ret;
|
||||||
return ret;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<boo::IMIDIOut> newVirtualMIDIOut() { return {}; }
|
std::unique_ptr<boo::IMIDIOut> newVirtualMIDIOut() { return {}; }
|
||||||
|
|
||||||
std::unique_ptr<boo::IMIDIInOut> newVirtualMIDIInOut(boo::ReceiveFunctor&& receiver) { return {}; }
|
std::unique_ptr<boo::IMIDIInOut> newVirtualMIDIInOut(boo::ReceiveFunctor&& receiver) { return {}; }
|
||||||
|
|
||||||
std::unique_ptr<boo::IMIDIIn> newRealMIDIIn(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
|
std::unique_ptr<boo::IMIDIIn> newRealMIDIIn(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
|
||||||
|
|
||||||
std::unique_ptr<boo::IMIDIOut> newRealMIDIOut(const char* name) { return {}; }
|
std::unique_ptr<boo::IMIDIOut> newRealMIDIOut(const char* name) { return {}; }
|
||||||
|
|
||||||
std::unique_ptr<boo::IMIDIInOut> newRealMIDIInOut(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
|
std::unique_ptr<boo::IMIDIInOut> newRealMIDIInOut(const char* name, boo::ReceiveFunctor&& receiver) { return {}; }
|
||||||
|
|
||||||
bool useMIDILock() const { return false; }
|
bool useMIDILock() const { return false; }
|
||||||
|
|
||||||
VSTVoiceEngine()
|
VSTVoiceEngine() {
|
||||||
{
|
m_mixInfo.m_periodFrames = 1024;
|
||||||
m_mixInfo.m_periodFrames = 1024;
|
m_mixInfo.m_sampleRate = 44100.0;
|
||||||
m_mixInfo.m_sampleRate = 44100.0;
|
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
|
||||||
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
|
m_mixInfo.m_bitsPerSample = 32;
|
||||||
m_mixInfo.m_bitsPerSample = 32;
|
_buildAudioRenderClient();
|
||||||
_buildAudioRenderClient();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void _buildAudioRenderClient()
|
void _buildAudioRenderClient() {
|
||||||
{
|
m_mixInfo.m_channels = _getAvailableSet();
|
||||||
m_mixInfo.m_channels = _getAvailableSet();
|
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
|
||||||
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
|
|
||||||
|
|
||||||
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
|
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
|
||||||
m_curBufFrame = m_5msFrames;
|
m_curBufFrame = m_5msFrames;
|
||||||
m_mixInfo.m_periodFrames = m_5msFrames;
|
m_mixInfo.m_periodFrames = m_5msFrames;
|
||||||
m_interleavedBuf.resize(m_5msFrames * 2);
|
m_interleavedBuf.resize(m_5msFrames * 2);
|
||||||
|
|
||||||
boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap;
|
boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap;
|
||||||
chMapOut.m_channelCount = 2;
|
chMapOut.m_channelCount = 2;
|
||||||
chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft;
|
chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft;
|
||||||
chMapOut.m_channels[1] = boo::AudioChannel::FrontRight;
|
chMapOut.m_channels[1] = boo::AudioChannel::FrontRight;
|
||||||
|
|
||||||
while (chMapOut.m_channelCount < chCount)
|
while (chMapOut.m_channelCount < chCount)
|
||||||
chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown;
|
chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames)
|
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) {
|
||||||
{
|
m_mixInfo.m_periodFrames = periodFrames;
|
||||||
m_mixInfo.m_periodFrames = periodFrames;
|
m_mixInfo.m_sampleRate = sampleRate;
|
||||||
m_mixInfo.m_sampleRate = sampleRate;
|
_buildAudioRenderClient();
|
||||||
_buildAudioRenderClient();
|
|
||||||
|
|
||||||
for (boo::AudioVoice* vox : m_activeVoices)
|
for (boo::AudioVoice* vox : m_activeVoices)
|
||||||
vox->_resetSampleRate(vox->m_sampleRateIn);
|
vox->_resetSampleRate(vox->m_sampleRateIn);
|
||||||
for (boo::AudioSubmix* smx : m_activeSubmixes)
|
for (boo::AudioSubmix* smx : m_activeSubmixes)
|
||||||
smx->_resetOutputSampleRate();
|
smx->_resetOutputSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pumpAndMixVoices()
|
void pumpAndMixVoices() {
|
||||||
{
|
for (size_t f = 0; f < m_renderFrames;) {
|
||||||
for (size_t f = 0; f < m_renderFrames;)
|
if (m_curBufFrame == m_5msFrames) {
|
||||||
{
|
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data());
|
||||||
if (m_curBufFrame == m_5msFrames)
|
m_curBufFrame = 0;
|
||||||
{
|
}
|
||||||
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data());
|
|
||||||
m_curBufFrame = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t remRenderFrames = std::min(m_renderFrames - f, m_5msFrames - m_curBufFrame);
|
size_t remRenderFrames = std::min(m_renderFrames - f, m_5msFrames - m_curBufFrame);
|
||||||
if (remRenderFrames)
|
if (remRenderFrames) {
|
||||||
{
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
for (size_t i = 0; i < 2; ++i)
|
float* bufOut = m_outputData[i];
|
||||||
{
|
for (size_t lf = 0; lf < remRenderFrames; ++lf)
|
||||||
float* bufOut = m_outputData[i];
|
bufOut[f + lf] = m_interleavedBuf[(m_curBufFrame + lf) * 2 + i];
|
||||||
for (size_t lf = 0; lf < remRenderFrames; ++lf)
|
|
||||||
bufOut[f + lf] = m_interleavedBuf[(m_curBufFrame + lf) * 2 + i];
|
|
||||||
}
|
|
||||||
m_curBufFrame += remRenderFrames;
|
|
||||||
f += remRenderFrames;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
m_curBufFrame += remRenderFrames;
|
||||||
|
f += remRenderFrames;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double getCurrentSampleRate() const { return m_mixInfo.m_sampleRate; }
|
double getCurrentSampleRate() const { return m_mixInfo.m_sampleRate; }
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
#define kBackendID CCONST('a', 'm', 'u', 's')
|
#define kBackendID CCONST('a', 'm', 'u', 's')
|
||||||
|
|
||||||
static logvisor::Module Log("amuse::AudioUnitBackend");
|
static logvisor::Module Log("amuse::AudioUnitBackend");
|
||||||
|
|
||||||
VSTBackend::VSTBackend(audioMasterCallback cb) : AudioEffectX(cb, 0, 0), m_filePresenter(*this), m_editor(*this)
|
VSTBackend::VSTBackend(audioMasterCallback cb) : AudioEffectX(cb, 0, 0), m_filePresenter(*this), m_editor(*this) {
|
||||||
{
|
isSynth();
|
||||||
isSynth();
|
setUniqueID(kBackendID);
|
||||||
setUniqueID(kBackendID);
|
setNumInputs(0);
|
||||||
setNumInputs(0);
|
setNumOutputs(2);
|
||||||
setNumOutputs(2);
|
setEditor(&m_editor);
|
||||||
setEditor(&m_editor);
|
sizeWindow(600, 420);
|
||||||
sizeWindow(600, 420);
|
programsAreChunks();
|
||||||
programsAreChunks();
|
|
||||||
|
|
||||||
m_booBackend = std::make_unique<VSTVoiceEngine>();
|
m_booBackend = std::make_unique<VSTVoiceEngine>();
|
||||||
m_voxAlloc.emplace(*m_booBackend);
|
m_voxAlloc.emplace(*m_booBackend);
|
||||||
m_engine.emplace(*m_voxAlloc);
|
m_engine.emplace(*m_voxAlloc);
|
||||||
|
|
||||||
WCHAR path[MAX_PATH];
|
WCHAR path[MAX_PATH];
|
||||||
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path)))
|
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path))) {
|
||||||
{
|
m_userDir = std::wstring(path) + L"\\Amuse";
|
||||||
m_userDir = std::wstring(path) + L"\\Amuse";
|
CreateDirectory(m_userDir.c_str(), nullptr);
|
||||||
CreateDirectory(m_userDir.c_str(), nullptr);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_filePresenter.update();
|
m_filePresenter.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
VSTBackend::~VSTBackend() { editor = nullptr; }
|
VSTBackend::~VSTBackend() { editor = nullptr; }
|
||||||
|
|
||||||
AEffEditor* VSTBackend::getEditor() { return &m_editor; }
|
AEffEditor* VSTBackend::getEditor() { return &m_editor; }
|
||||||
|
|
||||||
VstInt32 VSTBackend::processEvents(VstEvents* events)
|
VstInt32 VSTBackend::processEvents(VstEvents* events) {
|
||||||
{
|
std::unique_lock<std::mutex> lk(m_lock);
|
||||||
std::unique_lock<std::mutex> lk(m_lock);
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
|
||||||
|
|
||||||
/* Handle group load request */
|
/* Handle group load request */
|
||||||
if (m_curGroup != m_reqGroup)
|
if (m_curGroup != m_reqGroup) {
|
||||||
{
|
m_curGroup = m_reqGroup;
|
||||||
m_curGroup = m_reqGroup;
|
if (m_curSeq)
|
||||||
if (m_curSeq)
|
m_curSeq->kill();
|
||||||
m_curSeq->kill();
|
m_curSeq = m_engine->seqPlay(m_reqGroup, -1, nullptr);
|
||||||
m_curSeq = m_engine->seqPlay(m_reqGroup, -1, nullptr);
|
m_editor.reselectPage();
|
||||||
m_editor.reselectPage();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (engine.m_midiReceiver)
|
if (engine.m_midiReceiver) {
|
||||||
{
|
for (VstInt32 i = 0; i < events->numEvents; ++i) {
|
||||||
for (VstInt32 i = 0; i < events->numEvents; ++i)
|
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
|
||||||
{
|
if (evt->type == kVstMidiType) {
|
||||||
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
|
if (m_routeChannel != -1) {
|
||||||
if (evt->type == kVstMidiType)
|
evt->midiData[0] &= ~0xf;
|
||||||
{
|
evt->midiData[0] |= m_routeChannel & 0xf;
|
||||||
if (m_routeChannel != -1)
|
|
||||||
{
|
|
||||||
evt->midiData[0] &= ~0xf;
|
|
||||||
evt->midiData[0] |= m_routeChannel & 0xf;
|
|
||||||
}
|
|
||||||
(*engine.m_midiReceiver)(
|
|
||||||
std::vector<uint8_t>(std::cbegin(evt->midiData), std::cbegin(evt->midiData) + evt->byteSize),
|
|
||||||
(m_curFrame + evt->deltaFrames) / sampleRate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
(*engine.m_midiReceiver)(
|
||||||
|
std::vector<uint8_t>(std::cbegin(evt->midiData), std::cbegin(evt->midiData) + evt->byteSize),
|
||||||
|
(m_curFrame + evt->deltaFrames) / sampleRate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTBackend::processReplacing(float**, float** outputs, VstInt32 sampleFrames)
|
void VSTBackend::processReplacing(float**, float** outputs, VstInt32 sampleFrames) {
|
||||||
{
|
std::unique_lock<std::mutex> lk(m_lock);
|
||||||
std::unique_lock<std::mutex> lk(m_lock);
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
|
||||||
|
|
||||||
/* Output buffers */
|
/* Output buffers */
|
||||||
engine.m_renderFrames = sampleFrames;
|
engine.m_renderFrames = sampleFrames;
|
||||||
engine.m_outputData = outputs;
|
engine.m_outputData = outputs;
|
||||||
m_engine->pumpEngine();
|
m_engine->pumpEngine();
|
||||||
|
|
||||||
m_curFrame += sampleFrames;
|
m_curFrame += sampleFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
VstInt32 VSTBackend::canDo(char* text)
|
VstInt32 VSTBackend::canDo(char* text) {
|
||||||
{
|
VstInt32 returnCode = 0;
|
||||||
VstInt32 returnCode = 0;
|
|
||||||
|
|
||||||
if (!strcmp(text, "receiveVstEvents"))
|
if (!strcmp(text, "receiveVstEvents"))
|
||||||
returnCode = 1;
|
returnCode = 1;
|
||||||
else if (!strcmp(text, "receiveVstMidiEvent"))
|
else if (!strcmp(text, "receiveVstMidiEvent"))
|
||||||
returnCode = 1;
|
returnCode = 1;
|
||||||
|
|
||||||
return returnCode;
|
return returnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
VstPlugCategory VSTBackend::getPlugCategory() { return kPlugCategSynth; }
|
VstPlugCategory VSTBackend::getPlugCategory() { return kPlugCategSynth; }
|
||||||
|
|
||||||
bool VSTBackend::getEffectName(char* text)
|
bool VSTBackend::getEffectName(char* text) {
|
||||||
{
|
strcpy(text, "Amuse");
|
||||||
strcpy(text, "Amuse");
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VSTBackend::getProductString(char* text)
|
bool VSTBackend::getProductString(char* text) {
|
||||||
{
|
strcpy(text, "Amuse");
|
||||||
strcpy(text, "Amuse");
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VSTBackend::getVendorString(char* text)
|
bool VSTBackend::getVendorString(char* text) {
|
||||||
{
|
strcpy(text, "AxioDL");
|
||||||
strcpy(text, "AxioDL");
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VSTBackend::getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text)
|
bool VSTBackend::getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text) {
|
||||||
{
|
strcpy(text, "Sampler");
|
||||||
strcpy(text, "Sampler");
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* properties)
|
bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* properties) {
|
||||||
{
|
bool returnCode = false;
|
||||||
bool returnCode = false;
|
if (index == 0) {
|
||||||
if (index == 0)
|
strcpy(properties->label, "Amuse Out");
|
||||||
{
|
properties->flags = kVstPinIsStereo | kVstPinIsActive;
|
||||||
strcpy(properties->label, "Amuse Out");
|
properties->arrangementType = kSpeakerArrStereo;
|
||||||
properties->flags = kVstPinIsStereo | kVstPinIsActive;
|
returnCode = true;
|
||||||
properties->arrangementType = kSpeakerArrStereo;
|
}
|
||||||
returnCode = true;
|
return returnCode;
|
||||||
}
|
|
||||||
return returnCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VstInt32 VSTBackend::getNumMidiInputChannels() { return 1; }
|
VstInt32 VSTBackend::getNumMidiInputChannels() { return 1; }
|
||||||
|
|
||||||
void VSTBackend::setSampleRate(float sampleRate)
|
void VSTBackend::setSampleRate(float sampleRate) {
|
||||||
{
|
AudioEffectX::setSampleRate(sampleRate);
|
||||||
AudioEffectX::setSampleRate(sampleRate);
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
engine._rebuildAudioRenderClient(sampleRate, engine.mixInfo().m_periodFrames);
|
||||||
engine._rebuildAudioRenderClient(sampleRate, engine.mixInfo().m_periodFrames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTBackend::setBlockSize(VstInt32 blockSize)
|
void VSTBackend::setBlockSize(VstInt32 blockSize) {
|
||||||
{
|
AudioEffectX::setBlockSize(blockSize);
|
||||||
AudioEffectX::setBlockSize(blockSize);
|
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
||||||
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
|
engine._rebuildAudioRenderClient(engine.mixInfo().m_sampleRate, blockSize);
|
||||||
engine._rebuildAudioRenderClient(engine.mixInfo().m_sampleRate, blockSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTBackend::loadGroupFile(int collectionIdx, int fileIdx)
|
void VSTBackend::loadGroupFile(int collectionIdx, int fileIdx) {
|
||||||
{
|
std::unique_lock<std::mutex> lk(m_lock);
|
||||||
std::unique_lock<std::mutex> lk(m_lock);
|
|
||||||
|
|
||||||
if (m_curSeq)
|
if (m_curSeq) {
|
||||||
{
|
m_curSeq->kill();
|
||||||
|
m_curSeq.reset();
|
||||||
|
m_curGroup = -1;
|
||||||
|
m_reqGroup = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collectionIdx < m_filePresenter.m_iteratorVec.size()) {
|
||||||
|
AudioGroupFilePresenter::CollectionIterator& it = m_filePresenter.m_iteratorVec[collectionIdx];
|
||||||
|
if (fileIdx < it->second->m_iteratorVec.size()) {
|
||||||
|
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
||||||
|
if (m_curData)
|
||||||
|
m_curData->removeFromEngine(*m_engine);
|
||||||
|
git->second->addToEngine(*m_engine);
|
||||||
|
m_curData = git->second.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTBackend::setGroup(int groupIdx, bool immediate) {
|
||||||
|
std::unique_lock<std::mutex> lk(m_lock);
|
||||||
|
|
||||||
|
if (!m_curData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (groupIdx < m_curData->m_groupTokens.size()) {
|
||||||
|
const AudioGroupDataCollection::GroupToken& groupTok = m_curData->m_groupTokens[groupIdx];
|
||||||
|
m_reqGroup = groupTok.m_groupId;
|
||||||
|
if (immediate) {
|
||||||
|
if (m_curSeq)
|
||||||
m_curSeq->kill();
|
m_curSeq->kill();
|
||||||
m_curSeq.reset();
|
m_curSeq = m_engine->seqPlay(groupTok.m_groupId, -1, nullptr);
|
||||||
m_curGroup = -1;
|
|
||||||
m_reqGroup = -1;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (collectionIdx < m_filePresenter.m_iteratorVec.size())
|
void VSTBackend::_setNormalProgram(int programNo) {
|
||||||
{
|
if (!m_curSeq)
|
||||||
AudioGroupFilePresenter::CollectionIterator& it = m_filePresenter.m_iteratorVec[collectionIdx];
|
return;
|
||||||
if (fileIdx < it->second->m_iteratorVec.size())
|
m_curSeq->setChanProgram(0, programNo);
|
||||||
{
|
m_routeChannel = 0;
|
||||||
AudioGroupCollection::GroupIterator& git = it->second->m_iteratorVec[fileIdx];
|
}
|
||||||
if (m_curData)
|
|
||||||
m_curData->removeFromEngine(*m_engine);
|
void VSTBackend::setNormalProgram(int programNo) {
|
||||||
git->second->addToEngine(*m_engine);
|
std::unique_lock<std::mutex> lk(m_lock);
|
||||||
m_curData = git->second.get();
|
_setNormalProgram(programNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VSTBackend::_setDrumProgram(int programNo) {
|
||||||
|
if (!m_curSeq)
|
||||||
|
return;
|
||||||
|
m_curSeq->setChanProgram(9, programNo);
|
||||||
|
m_routeChannel = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTBackend::setDrumProgram(int programNo) {
|
||||||
|
std::unique_lock<std::mutex> lk(m_lock);
|
||||||
|
_setDrumProgram(programNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
VstInt32 VSTBackend::getChunk(void** data, bool) {
|
||||||
|
size_t allocSz = 14;
|
||||||
|
if (m_curData)
|
||||||
|
allocSz += (m_curData->m_path.size() - m_userDir.size() - 1) * 2;
|
||||||
|
|
||||||
|
uint8_t* buf = new uint8_t[allocSz];
|
||||||
|
if (m_curData)
|
||||||
|
memmove(buf, m_curData->m_path.data() + m_userDir.size() + 1, allocSz - 12);
|
||||||
|
else
|
||||||
|
*reinterpret_cast<wchar_t*>(buf) = L'\0';
|
||||||
|
uint32_t* intVals = reinterpret_cast<uint32_t*>(buf + allocSz - 12);
|
||||||
|
intVals[0] = 0;
|
||||||
|
intVals[1] = m_editor.m_selGroupIdx;
|
||||||
|
intVals[2] = m_editor.m_selPageIdx;
|
||||||
|
*data = buf;
|
||||||
|
return allocSz;
|
||||||
|
}
|
||||||
|
|
||||||
|
VstInt32 VSTBackend::setChunk(void* data, VstInt32 byteSize, bool) {
|
||||||
|
if (byteSize < 14)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wchar_t* path = reinterpret_cast<wchar_t*>(data);
|
||||||
|
uint32_t* intVals = reinterpret_cast<uint32_t*>(path + wcslen(path) + 1);
|
||||||
|
std::wstring targetPath = m_userDir + L'\\' + path;
|
||||||
|
uint32_t groupIdx = intVals[1];
|
||||||
|
uint32_t pageIdx = intVals[2];
|
||||||
|
|
||||||
|
size_t colIdx = 0;
|
||||||
|
for (auto& collection : m_filePresenter.m_audioGroupCollections) {
|
||||||
|
size_t fileIdx = 0;
|
||||||
|
for (auto& file : collection.second->m_groups) {
|
||||||
|
if (!file.second->m_path.compare(targetPath)) {
|
||||||
|
m_editor.selectCollection(LPARAM(0x80000000 | (colIdx << 16) | fileIdx));
|
||||||
|
m_editor.selectGroup(groupIdx);
|
||||||
|
m_editor.selectPage(pageIdx);
|
||||||
|
m_editor._reselectColumns();
|
||||||
|
}
|
||||||
|
++fileIdx;
|
||||||
}
|
}
|
||||||
|
++colIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
} // namespace amuse
|
||||||
void VSTBackend::setGroup(int groupIdx, bool immediate)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(m_lock);
|
|
||||||
|
|
||||||
if (!m_curData)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (groupIdx < m_curData->m_groupTokens.size())
|
|
||||||
{
|
|
||||||
const AudioGroupDataCollection::GroupToken& groupTok = m_curData->m_groupTokens[groupIdx];
|
|
||||||
m_reqGroup = groupTok.m_groupId;
|
|
||||||
if (immediate)
|
|
||||||
{
|
|
||||||
if (m_curSeq)
|
|
||||||
m_curSeq->kill();
|
|
||||||
m_curSeq = m_engine->seqPlay(groupTok.m_groupId, -1, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTBackend::_setNormalProgram(int programNo)
|
|
||||||
{
|
|
||||||
if (!m_curSeq)
|
|
||||||
return;
|
|
||||||
m_curSeq->setChanProgram(0, programNo);
|
|
||||||
m_routeChannel = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTBackend::setNormalProgram(int programNo)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(m_lock);
|
|
||||||
_setNormalProgram(programNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTBackend::_setDrumProgram(int programNo)
|
|
||||||
{
|
|
||||||
if (!m_curSeq)
|
|
||||||
return;
|
|
||||||
m_curSeq->setChanProgram(9, programNo);
|
|
||||||
m_routeChannel = 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTBackend::setDrumProgram(int programNo)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(m_lock);
|
|
||||||
_setDrumProgram(programNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
VstInt32 VSTBackend::getChunk(void** data, bool)
|
|
||||||
{
|
|
||||||
size_t allocSz = 14;
|
|
||||||
if (m_curData)
|
|
||||||
allocSz += (m_curData->m_path.size() - m_userDir.size() - 1) * 2;
|
|
||||||
|
|
||||||
uint8_t* buf = new uint8_t[allocSz];
|
|
||||||
if (m_curData)
|
|
||||||
memmove(buf, m_curData->m_path.data() + m_userDir.size() + 1, allocSz - 12);
|
|
||||||
else
|
|
||||||
*reinterpret_cast<wchar_t*>(buf) = L'\0';
|
|
||||||
uint32_t* intVals = reinterpret_cast<uint32_t*>(buf + allocSz - 12);
|
|
||||||
intVals[0] = 0;
|
|
||||||
intVals[1] = m_editor.m_selGroupIdx;
|
|
||||||
intVals[2] = m_editor.m_selPageIdx;
|
|
||||||
*data = buf;
|
|
||||||
return allocSz;
|
|
||||||
}
|
|
||||||
|
|
||||||
VstInt32 VSTBackend::setChunk(void* data, VstInt32 byteSize, bool)
|
|
||||||
{
|
|
||||||
if (byteSize < 14)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
wchar_t* path = reinterpret_cast<wchar_t*>(data);
|
|
||||||
uint32_t* intVals = reinterpret_cast<uint32_t*>(path + wcslen(path) + 1);
|
|
||||||
std::wstring targetPath = m_userDir + L'\\' + path;
|
|
||||||
uint32_t groupIdx = intVals[1];
|
|
||||||
uint32_t pageIdx = intVals[2];
|
|
||||||
|
|
||||||
size_t colIdx = 0;
|
|
||||||
for (auto& collection : m_filePresenter.m_audioGroupCollections)
|
|
||||||
{
|
|
||||||
size_t fileIdx = 0;
|
|
||||||
for (auto& file : collection.second->m_groups)
|
|
||||||
{
|
|
||||||
if (!file.second->m_path.compare(targetPath))
|
|
||||||
{
|
|
||||||
m_editor.selectCollection(LPARAM(0x80000000 | (colIdx << 16) | fileIdx));
|
|
||||||
m_editor.selectGroup(groupIdx);
|
|
||||||
m_editor.selectPage(pageIdx);
|
|
||||||
m_editor._reselectColumns();
|
|
||||||
}
|
|
||||||
++fileIdx;
|
|
||||||
}
|
|
||||||
++colIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioEffect* createEffectInstance(audioMasterCallback audioMaster) { return new amuse::VSTBackend(audioMaster); }
|
AudioEffect* createEffectInstance(audioMasterCallback audioMaster) { return new amuse::VSTBackend(audioMaster); }
|
||||||
|
|
|
@ -12,64 +12,60 @@
|
||||||
#include "amuse/IBackendVoiceAllocator.hpp"
|
#include "amuse/IBackendVoiceAllocator.hpp"
|
||||||
#include "AudioGroupFilePresenter.hpp"
|
#include "AudioGroupFilePresenter.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class VSTBackend;
|
class VSTBackend;
|
||||||
|
|
||||||
/** Backend voice allocator implementation for AudioUnit mixer */
|
/** Backend voice allocator implementation for AudioUnit mixer */
|
||||||
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator
|
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
|
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Actual plugin implementation class */
|
/** Actual plugin implementation class */
|
||||||
class VSTBackend : public AudioEffectX
|
class VSTBackend : public AudioEffectX {
|
||||||
{
|
std::mutex m_lock;
|
||||||
std::mutex m_lock;
|
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
std::experimental::optional<amuse::VSTBackendVoiceAllocator> m_voxAlloc;
|
||||||
std::experimental::optional<amuse::VSTBackendVoiceAllocator> m_voxAlloc;
|
std::experimental::optional<amuse::Engine> m_engine;
|
||||||
std::experimental::optional<amuse::Engine> m_engine;
|
std::shared_ptr<amuse::Sequencer> m_curSeq;
|
||||||
std::shared_ptr<amuse::Sequencer> m_curSeq;
|
int m_reqGroup = -1;
|
||||||
int m_reqGroup = -1;
|
int m_curGroup = -1;
|
||||||
int m_curGroup = -1;
|
const AudioGroupDataCollection* m_curData = nullptr;
|
||||||
const AudioGroupDataCollection* m_curData = nullptr;
|
size_t m_curFrame = 0;
|
||||||
size_t m_curFrame = 0;
|
std::wstring m_userDir;
|
||||||
std::wstring m_userDir;
|
int m_routeChannel = -1;
|
||||||
int m_routeChannel = -1;
|
AudioGroupFilePresenter m_filePresenter;
|
||||||
AudioGroupFilePresenter m_filePresenter;
|
VSTEditor m_editor;
|
||||||
VSTEditor m_editor;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VSTBackend(audioMasterCallback cb);
|
VSTBackend(audioMasterCallback cb);
|
||||||
~VSTBackend();
|
~VSTBackend();
|
||||||
AEffEditor* getEditor();
|
AEffEditor* getEditor();
|
||||||
VstInt32 processEvents(VstEvents* events);
|
VstInt32 processEvents(VstEvents* events);
|
||||||
void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);
|
void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);
|
||||||
VstInt32 canDo(char* text);
|
VstInt32 canDo(char* text);
|
||||||
VstPlugCategory getPlugCategory();
|
VstPlugCategory getPlugCategory();
|
||||||
bool getEffectName(char* text);
|
bool getEffectName(char* text);
|
||||||
bool getProductString(char* text);
|
bool getProductString(char* text);
|
||||||
bool getVendorString(char* text);
|
bool getVendorString(char* text);
|
||||||
bool getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text);
|
bool getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text);
|
||||||
bool getOutputProperties(VstInt32 index, VstPinProperties* properties);
|
bool getOutputProperties(VstInt32 index, VstPinProperties* properties);
|
||||||
VstInt32 getNumMidiInputChannels();
|
VstInt32 getNumMidiInputChannels();
|
||||||
void setSampleRate(float sampleRate);
|
void setSampleRate(float sampleRate);
|
||||||
void setBlockSize(VstInt32 blockSize);
|
void setBlockSize(VstInt32 blockSize);
|
||||||
|
|
||||||
amuse::Engine& getAmuseEngine() { return *m_engine; }
|
amuse::Engine& getAmuseEngine() { return *m_engine; }
|
||||||
std::wstring_view getUserDir() const { return m_userDir; }
|
std::wstring_view getUserDir() const { return m_userDir; }
|
||||||
AudioGroupFilePresenter& getFilePresenter() { return m_filePresenter; }
|
AudioGroupFilePresenter& getFilePresenter() { return m_filePresenter; }
|
||||||
|
|
||||||
void loadGroupFile(int collectionIdx, int fileIdx);
|
void loadGroupFile(int collectionIdx, int fileIdx);
|
||||||
void setGroup(int groupIdx, bool immediate);
|
void setGroup(int groupIdx, bool immediate);
|
||||||
void _setNormalProgram(int programNo);
|
void _setNormalProgram(int programNo);
|
||||||
void setNormalProgram(int programNo);
|
void setNormalProgram(int programNo);
|
||||||
void _setDrumProgram(int programNo);
|
void _setDrumProgram(int programNo);
|
||||||
void setDrumProgram(int programNo);
|
void setDrumProgram(int programNo);
|
||||||
|
|
||||||
VstInt32 getChunk(void** data, bool isPreset);
|
VstInt32 getChunk(void** data, bool isPreset);
|
||||||
VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset);
|
VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -13,381 +13,345 @@ extern void* hInstance;
|
||||||
static WNDPROC OriginalListViewProc = 0;
|
static WNDPROC OriginalListViewProc = 0;
|
||||||
static HBRUSH gGreyBorderBrush;
|
static HBRUSH gGreyBorderBrush;
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
VSTEditor::VSTEditor(VSTBackend& backend) : AEffEditor(&backend), m_backend(backend) {}
|
VSTEditor::VSTEditor(VSTBackend& backend) : AEffEditor(&backend), m_backend(backend) {}
|
||||||
|
|
||||||
bool VSTEditor::getRect(ERect** rect)
|
bool VSTEditor::getRect(ERect** rect) {
|
||||||
{
|
*rect = &m_windowRect;
|
||||||
*rect = &m_windowRect;
|
return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||||
{
|
VSTEditor& editor = *reinterpret_cast<VSTEditor*>(GetWindowLongPtrW(hwnd, 0));
|
||||||
VSTEditor& editor = *reinterpret_cast<VSTEditor*>(GetWindowLongPtrW(hwnd, 0));
|
switch (uMsg) {
|
||||||
switch (uMsg)
|
case WM_NOTIFY: {
|
||||||
{
|
NMHDR& itemAct = *reinterpret_cast<LPNMHDR>(lParam);
|
||||||
case WM_NOTIFY:
|
switch (itemAct.code) {
|
||||||
{
|
case HDN_BEGINTRACK:
|
||||||
NMHDR& itemAct = *reinterpret_cast<LPNMHDR>(lParam);
|
return TRUE;
|
||||||
switch (itemAct.code)
|
case NM_CLICK: {
|
||||||
{
|
NMITEMACTIVATE& itemAct = *reinterpret_cast<LPNMITEMACTIVATE>(lParam);
|
||||||
case HDN_BEGINTRACK:
|
if (itemAct.hdr.hwndFrom == editor.m_groupListView)
|
||||||
return TRUE;
|
editor.selectGroup(itemAct.iItem);
|
||||||
case NM_CLICK:
|
else if (itemAct.hdr.hwndFrom == editor.m_pageListView)
|
||||||
{
|
editor.selectPage(itemAct.iItem);
|
||||||
NMITEMACTIVATE& itemAct = *reinterpret_cast<LPNMITEMACTIVATE>(lParam);
|
return 0;
|
||||||
if (itemAct.hdr.hwndFrom == editor.m_groupListView)
|
|
||||||
editor.selectGroup(itemAct.iItem);
|
|
||||||
else if (itemAct.hdr.hwndFrom == editor.m_pageListView)
|
|
||||||
editor.selectPage(itemAct.iItem);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TVN_SELCHANGED:
|
|
||||||
{
|
|
||||||
if (editor.m_deferredCollectionSel)
|
|
||||||
return 0;
|
|
||||||
NMTREEVIEW& itemAct = *reinterpret_cast<LPNMTREEVIEW>(lParam);
|
|
||||||
if (itemAct.hdr.hwndFrom == editor.m_collectionTree)
|
|
||||||
editor.selectCollection(itemAct.itemNew.lParam);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case TVN_GETDISPINFO:
|
|
||||||
{
|
|
||||||
NMTVDISPINFO& treeDispInfo = *reinterpret_cast<LPNMTVDISPINFO>(lParam);
|
|
||||||
if (treeDispInfo.item.mask & TVIF_CHILDREN)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case WM_COMMAND:
|
case TVN_SELCHANGED: {
|
||||||
{
|
if (editor.m_deferredCollectionSel)
|
||||||
switch (HIWORD(wParam))
|
return 0;
|
||||||
{
|
NMTREEVIEW& itemAct = *reinterpret_cast<LPNMTREEVIEW>(lParam);
|
||||||
case BN_CLICKED:
|
if (itemAct.hdr.hwndFrom == editor.m_collectionTree)
|
||||||
{
|
editor.selectCollection(itemAct.itemNew.lParam);
|
||||||
HWND button = HWND(lParam);
|
return 0;
|
||||||
if (button == editor.m_collectionAdd)
|
|
||||||
editor.addAction();
|
|
||||||
else if (button == editor.m_collectionRemove)
|
|
||||||
editor.removeAction();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case WM_ERASEBKGND:
|
case TVN_GETDISPINFO: {
|
||||||
{
|
NMTVDISPINFO& treeDispInfo = *reinterpret_cast<LPNMTVDISPINFO>(lParam);
|
||||||
RECT rect;
|
if (treeDispInfo.item.mask & TVIF_CHILDREN) {}
|
||||||
GetClientRect(hwnd, &rect);
|
return 0;
|
||||||
FillRect(HDC(wParam), &rect, gGreyBorderBrush);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
case WM_COMMAND: {
|
||||||
|
switch (HIWORD(wParam)) {
|
||||||
|
case BN_CLICKED: {
|
||||||
|
HWND button = HWND(lParam);
|
||||||
|
if (button == editor.m_collectionAdd)
|
||||||
|
editor.addAction();
|
||||||
|
else if (button == editor.m_collectionRemove)
|
||||||
|
editor.removeAction();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case WM_ERASEBKGND: {
|
||||||
|
RECT rect;
|
||||||
|
GetClientRect(hwnd, &rect);
|
||||||
|
FillRect(HDC(wParam), &rect, gGreyBorderBrush);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||||
{
|
switch (uMsg) {
|
||||||
switch (uMsg)
|
case WM_SETCURSOR:
|
||||||
{
|
return TRUE;
|
||||||
case WM_SETCURSOR:
|
case WM_LBUTTONDBLCLK:
|
||||||
return TRUE;
|
return 0;
|
||||||
case WM_LBUTTONDBLCLK:
|
case WM_PAINT: {
|
||||||
return 0;
|
PAINTSTRUCT ps;
|
||||||
case WM_PAINT:
|
HDC dc = BeginPaint(hwnd, &ps);
|
||||||
{
|
RECT rect;
|
||||||
PAINTSTRUCT ps;
|
GetClientRect(hwnd, &rect);
|
||||||
HDC dc = BeginPaint(hwnd, &ps);
|
|
||||||
RECT rect;
|
|
||||||
GetClientRect(hwnd, &rect);
|
|
||||||
|
|
||||||
TRIVERTEX verts[] = {{rect.left, rect.top, 0x6000, 0x6000, 0x7000, 0xff00},
|
TRIVERTEX verts[] = {{rect.left, rect.top, 0x6000, 0x6000, 0x7000, 0xff00},
|
||||||
{rect.right, rect.bottom, 0x2000, 0x2000, 0x2800, 0xff00}};
|
{rect.right, rect.bottom, 0x2000, 0x2000, 0x2800, 0xff00}};
|
||||||
GRADIENT_RECT grect = {0, 1};
|
GRADIENT_RECT grect = {0, 1};
|
||||||
GradientFill(dc, verts, 2, &grect, 1, GRADIENT_FILL_RECT_V);
|
GradientFill(dc, verts, 2, &grect, 1, GRADIENT_FILL_RECT_V);
|
||||||
|
|
||||||
SetTextColor(dc, RGB(255, 255, 255));
|
SetTextColor(dc, RGB(255, 255, 255));
|
||||||
SetBkMode(dc, TRANSPARENT);
|
SetBkMode(dc, TRANSPARENT);
|
||||||
SelectObject(dc, GetStockObject(ANSI_VAR_FONT));
|
SelectObject(dc, GetStockObject(ANSI_VAR_FONT));
|
||||||
rect.left += 6;
|
rect.left += 6;
|
||||||
|
|
||||||
LPWSTR str = LPWSTR(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
LPWSTR str = LPWSTR(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
||||||
DrawText(dc, str, -1, &rect, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
|
DrawText(dc, str, -1, &rect, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
|
||||||
|
|
||||||
EndPaint(hwnd, &ps);
|
EndPaint(hwnd, &ps);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallWindowProc(OriginalListViewProc, hwnd, uMsg, wParam, lParam);
|
return CallWindowProc(OriginalListViewProc, hwnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTEditor::_reselectColumns()
|
void VSTEditor::_reselectColumns() {
|
||||||
{
|
if (m_deferredCollectionSel) {
|
||||||
if (m_deferredCollectionSel)
|
TreeView_SelectItem(m_collectionTree, m_deferredCollectionSel);
|
||||||
{
|
m_deferredCollectionSel = 0;
|
||||||
TreeView_SelectItem(m_collectionTree, m_deferredCollectionSel);
|
}
|
||||||
m_deferredCollectionSel = 0;
|
if (m_selGroupIdx != -1)
|
||||||
}
|
ListView_SetItemState(m_groupListView, m_selGroupIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
|
||||||
if (m_selGroupIdx != -1)
|
if (m_selPageIdx != -1)
|
||||||
ListView_SetItemState(m_groupListView, m_selGroupIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
|
ListView_SetItemState(m_pageListView, m_selPageIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
|
||||||
if (m_selPageIdx != -1)
|
|
||||||
ListView_SetItemState(m_pageListView, m_selPageIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VSTEditor::open(void* ptr)
|
bool VSTEditor::open(void* ptr) {
|
||||||
{
|
AEffEditor::open(ptr);
|
||||||
AEffEditor::open(ptr);
|
HWND hostView = HWND(ptr);
|
||||||
HWND hostView = HWND(ptr);
|
gGreyBorderBrush = CreateSolidBrush(RGB(100, 100, 100));
|
||||||
gGreyBorderBrush = CreateSolidBrush(RGB(100, 100, 100));
|
|
||||||
|
|
||||||
WNDCLASSW notifyCls = {CS_HREDRAW | CS_VREDRAW,
|
WNDCLASSW notifyCls = {CS_HREDRAW | CS_VREDRAW,
|
||||||
WindowProc,
|
WindowProc,
|
||||||
0,
|
0,
|
||||||
8,
|
8,
|
||||||
HINSTANCE(hInstance),
|
HINSTANCE(hInstance),
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
L"VSTNotify"};
|
L"VSTNotify"};
|
||||||
RegisterClassW(¬ifyCls);
|
RegisterClassW(¬ifyCls);
|
||||||
|
|
||||||
m_rootView = CreateWindowW(L"VSTNotify", L"", WS_CHILD, 0, 0, m_windowRect.right, m_windowRect.bottom, hostView,
|
m_rootView = CreateWindowW(L"VSTNotify", L"", WS_CHILD, 0, 0, m_windowRect.right, m_windowRect.bottom, hostView,
|
||||||
nullptr, HINSTANCE(hInstance), nullptr);
|
nullptr, HINSTANCE(hInstance), nullptr);
|
||||||
SetWindowLongPtrW(m_rootView, 0, LONG_PTR(this));
|
SetWindowLongPtrW(m_rootView, 0, LONG_PTR(this));
|
||||||
ShowWindow(m_rootView, SW_SHOW);
|
ShowWindow(m_rootView, SW_SHOW);
|
||||||
|
|
||||||
TVINSERTSTRUCT treeItem = {};
|
TVINSERTSTRUCT treeItem = {};
|
||||||
treeItem.hParent = TVI_ROOT;
|
treeItem.hParent = TVI_ROOT;
|
||||||
treeItem.hInsertAfter = TVI_LAST;
|
treeItem.hInsertAfter = TVI_LAST;
|
||||||
|
|
||||||
treeItem.item.mask = TVIF_CHILDREN | TVIF_TEXT;
|
treeItem.item.mask = TVIF_CHILDREN | TVIF_TEXT;
|
||||||
treeItem.item.cChildren = 1;
|
treeItem.item.cChildren = 1;
|
||||||
treeItem.item.pszText = L"Root A";
|
treeItem.item.pszText = L"Root A";
|
||||||
|
|
||||||
LVCOLUMN column = {};
|
LVCOLUMN column = {};
|
||||||
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
|
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
|
||||||
column.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH;
|
column.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH;
|
||||||
column.cx = 199;
|
column.cx = 199;
|
||||||
|
|
||||||
m_collectionTree =
|
m_collectionTree =
|
||||||
CreateWindowW(WC_TREEVIEW, L"",
|
CreateWindowW(WC_TREEVIEW, L"",
|
||||||
WS_CHILD | WS_CLIPSIBLINGS | TVS_SHOWSELALWAYS | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS,
|
WS_CHILD | WS_CLIPSIBLINGS | TVS_SHOWSELALWAYS | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, 1,
|
||||||
1, 25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
|
25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
|
||||||
TreeView_SetBkColor(m_collectionTree, RGB(64, 64, 64));
|
TreeView_SetBkColor(m_collectionTree, RGB(64, 64, 64));
|
||||||
TreeView_SetTextColor(m_collectionTree, RGB(255, 255, 255));
|
TreeView_SetTextColor(m_collectionTree, RGB(255, 255, 255));
|
||||||
HTREEITEM rootItemA = TreeView_InsertItem(m_collectionTree, &treeItem);
|
HTREEITEM rootItemA = TreeView_InsertItem(m_collectionTree, &treeItem);
|
||||||
treeItem.item.pszText = L"Root B";
|
treeItem.item.pszText = L"Root B";
|
||||||
HTREEITEM rootItemB = TreeView_InsertItem(m_collectionTree, &treeItem);
|
HTREEITEM rootItemB = TreeView_InsertItem(m_collectionTree, &treeItem);
|
||||||
treeItem.hParent = rootItemA;
|
treeItem.hParent = rootItemA;
|
||||||
treeItem.item.cChildren = 0;
|
treeItem.item.cChildren = 0;
|
||||||
treeItem.item.pszText = L"Child A";
|
treeItem.item.pszText = L"Child A";
|
||||||
TreeView_InsertItem(m_collectionTree, &treeItem);
|
TreeView_InsertItem(m_collectionTree, &treeItem);
|
||||||
treeItem.item.pszText = L"Child B";
|
treeItem.item.pszText = L"Child B";
|
||||||
TreeView_InsertItem(m_collectionTree, &treeItem);
|
TreeView_InsertItem(m_collectionTree, &treeItem);
|
||||||
treeItem.hParent = rootItemB;
|
treeItem.hParent = rootItemB;
|
||||||
treeItem.item.pszText = L"Child A";
|
treeItem.item.pszText = L"Child A";
|
||||||
TreeView_InsertItem(m_collectionTree, &treeItem);
|
TreeView_InsertItem(m_collectionTree, &treeItem);
|
||||||
treeItem.item.pszText = L"Child B";
|
treeItem.item.pszText = L"Child B";
|
||||||
TreeView_InsertItem(m_collectionTree, &treeItem);
|
TreeView_InsertItem(m_collectionTree, &treeItem);
|
||||||
ShowWindow(m_collectionTree, SW_SHOW);
|
ShowWindow(m_collectionTree, SW_SHOW);
|
||||||
|
|
||||||
HWND cHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 1, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
|
HWND cHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 1, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
|
||||||
SetWindowLongPtrW(cHeader, GWLP_USERDATA, LONG_PTR(L"Collection"));
|
SetWindowLongPtrW(cHeader, GWLP_USERDATA, LONG_PTR(L"Collection"));
|
||||||
OriginalListViewProc = WNDPROC(SetWindowLongPtr(cHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
|
OriginalListViewProc = WNDPROC(SetWindowLongPtr(cHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
|
||||||
ShowWindow(cHeader, SW_SHOW);
|
ShowWindow(cHeader, SW_SHOW);
|
||||||
|
|
||||||
HWND gHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 201, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
|
HWND gHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 201, 1, 199, 24, m_rootView, nullptr, nullptr, nullptr);
|
||||||
SetWindowLongPtrW(gHeader, GWLP_USERDATA, LONG_PTR(L"Group"));
|
SetWindowLongPtrW(gHeader, GWLP_USERDATA, LONG_PTR(L"Group"));
|
||||||
OriginalListViewProc = WNDPROC(SetWindowLongPtr(gHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
|
OriginalListViewProc = WNDPROC(SetWindowLongPtr(gHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
|
||||||
ShowWindow(gHeader, SW_SHOW);
|
ShowWindow(gHeader, SW_SHOW);
|
||||||
|
|
||||||
HWND pHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 401, 1, 198, 24, m_rootView, nullptr, nullptr, nullptr);
|
HWND pHeader = CreateWindowW(WC_HEADER, L"", WS_CHILD, 401, 1, 198, 24, m_rootView, nullptr, nullptr, nullptr);
|
||||||
SetWindowLongPtrW(pHeader, GWLP_USERDATA, LONG_PTR(L"Page"));
|
SetWindowLongPtrW(pHeader, GWLP_USERDATA, LONG_PTR(L"Page"));
|
||||||
OriginalListViewProc = WNDPROC(SetWindowLongPtr(pHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
|
OriginalListViewProc = WNDPROC(SetWindowLongPtr(pHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
|
||||||
ShowWindow(pHeader, SW_SHOW);
|
ShowWindow(pHeader, SW_SHOW);
|
||||||
|
|
||||||
m_collectionAdd =
|
m_collectionAdd =
|
||||||
CreateWindowW(WC_BUTTON, L"+", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 1,
|
CreateWindowW(WC_BUTTON, L"+", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 1,
|
||||||
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
|
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
|
||||||
SetWindowFont(m_collectionAdd, GetStockObject(ANSI_FIXED_FONT), FALSE);
|
SetWindowFont(m_collectionAdd, GetStockObject(ANSI_FIXED_FONT), FALSE);
|
||||||
Button_Enable(m_collectionAdd, TRUE);
|
Button_Enable(m_collectionAdd, TRUE);
|
||||||
SetWindowPos(m_collectionAdd, HWND_TOP, 1, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
|
SetWindowPos(m_collectionAdd, HWND_TOP, 1, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
|
||||||
|
|
||||||
m_collectionRemove =
|
m_collectionRemove =
|
||||||
CreateWindowW(WC_BUTTON, L"-", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 26,
|
CreateWindowW(WC_BUTTON, L"-", WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON, 26,
|
||||||
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
|
m_windowRect.bottom - m_windowRect.top - 25, 25, 24, m_rootView, nullptr, nullptr, nullptr);
|
||||||
SetWindowFont(m_collectionRemove, GetStockObject(ANSI_FIXED_FONT), FALSE);
|
SetWindowFont(m_collectionRemove, GetStockObject(ANSI_FIXED_FONT), FALSE);
|
||||||
Button_Enable(m_collectionRemove, FALSE);
|
Button_Enable(m_collectionRemove, FALSE);
|
||||||
SetWindowPos(m_collectionRemove, HWND_TOP, 26, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
|
SetWindowPos(m_collectionRemove, HWND_TOP, 26, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
|
||||||
|
|
||||||
m_groupListView =
|
m_groupListView =
|
||||||
CreateWindowW(WC_LISTVIEW, L"",
|
CreateWindowW(WC_LISTVIEW, L"",
|
||||||
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
|
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
|
||||||
201, 25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
|
201, 25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
|
||||||
column.pszText = L"Group";
|
column.pszText = L"Group";
|
||||||
HWND header = ListView_GetHeader(m_groupListView);
|
HWND header = ListView_GetHeader(m_groupListView);
|
||||||
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
|
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
|
||||||
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
|
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
|
||||||
ListView_SetBkColor(m_groupListView, RGB(64, 64, 64));
|
ListView_SetBkColor(m_groupListView, RGB(64, 64, 64));
|
||||||
ListView_SetTextBkColor(m_groupListView, CLR_NONE);
|
ListView_SetTextBkColor(m_groupListView, CLR_NONE);
|
||||||
ListView_SetTextColor(m_groupListView, RGB(255, 255, 255));
|
ListView_SetTextColor(m_groupListView, RGB(255, 255, 255));
|
||||||
ListView_InsertColumn(m_groupListView, 0, &column);
|
ListView_InsertColumn(m_groupListView, 0, &column);
|
||||||
ShowWindow(m_groupListView, SW_SHOW);
|
ShowWindow(m_groupListView, SW_SHOW);
|
||||||
|
|
||||||
m_pageListView =
|
m_pageListView =
|
||||||
CreateWindowW(WC_LISTVIEW, L"",
|
CreateWindowW(WC_LISTVIEW, L"",
|
||||||
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
|
WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER,
|
||||||
401, 25, 198, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
|
401, 25, 198, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
|
||||||
column.pszText = L"Page";
|
column.pszText = L"Page";
|
||||||
column.cx = 198 - GetSystemMetrics(SM_CXVSCROLL);
|
column.cx = 198 - GetSystemMetrics(SM_CXVSCROLL);
|
||||||
header = ListView_GetHeader(m_pageListView);
|
header = ListView_GetHeader(m_pageListView);
|
||||||
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
|
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
|
||||||
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
|
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
|
||||||
ListView_SetBkColor(m_pageListView, RGB(64, 64, 64));
|
ListView_SetBkColor(m_pageListView, RGB(64, 64, 64));
|
||||||
ListView_SetTextBkColor(m_pageListView, CLR_NONE);
|
ListView_SetTextBkColor(m_pageListView, CLR_NONE);
|
||||||
ListView_SetTextColor(m_pageListView, RGB(255, 255, 255));
|
ListView_SetTextColor(m_pageListView, RGB(255, 255, 255));
|
||||||
ListView_InsertColumn(m_pageListView, 0, &column);
|
ListView_InsertColumn(m_pageListView, 0, &column);
|
||||||
ShowWindow(m_pageListView, SW_SHOW);
|
ShowWindow(m_pageListView, SW_SHOW);
|
||||||
|
|
||||||
|
update();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTEditor::close() {
|
||||||
|
AEffEditor::close();
|
||||||
|
UnregisterClassW(L"VSTNotify", HINSTANCE(hInstance));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTEditor::update() {
|
||||||
|
m_backend.getFilePresenter().populateCollectionColumn(*this);
|
||||||
|
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
|
||||||
|
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
|
||||||
|
m_backend.setGroup(m_selGroupIdx, true);
|
||||||
|
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
|
||||||
|
selectPage(m_selPageIdx);
|
||||||
|
_reselectColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTEditor::addAction() {
|
||||||
|
std::wstring path = openDB();
|
||||||
|
if (path.size()) {
|
||||||
|
amuse::ContainerRegistry::Type containerType;
|
||||||
|
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>> data =
|
||||||
|
amuse::ContainerRegistry::LoadContainer(path.c_str(), containerType);
|
||||||
|
if (data.empty()) {
|
||||||
|
wchar_t msg[512];
|
||||||
|
SNPrintf(msg, 512, L"Unable to load Audio Groups from %s", path.c_str());
|
||||||
|
MessageBoxW(nullptr, msg, L"Invalid Data File", MB_OK | MB_ICONERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemString name(amuse::ContainerRegistry::TypeToName(containerType));
|
||||||
|
if (containerType == amuse::ContainerRegistry::Type::Raw4) {
|
||||||
|
size_t dotpos = path.rfind(L'.');
|
||||||
|
if (dotpos != std::string::npos)
|
||||||
|
name.assign(path.cbegin(), path.cbegin() + dotpos);
|
||||||
|
size_t slashpos = name.rfind(L'\\');
|
||||||
|
size_t fslashpos = name.rfind(L'/');
|
||||||
|
if (slashpos == std::string::npos)
|
||||||
|
slashpos = fslashpos;
|
||||||
|
else if (fslashpos != std::string::npos)
|
||||||
|
slashpos = std::max(slashpos, fslashpos);
|
||||||
|
if (slashpos != std::string::npos)
|
||||||
|
name.assign(name.cbegin() + slashpos + 1, name.cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_backend.getFilePresenter().addCollection(name, std::move(data));
|
||||||
update();
|
update();
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTEditor::close()
|
void VSTEditor::removeAction() {
|
||||||
{
|
if (m_selCollectionIdx == -1)
|
||||||
AEffEditor::close();
|
return;
|
||||||
UnregisterClassW(L"VSTNotify", HINSTANCE(hInstance));
|
m_backend.getFilePresenter().removeCollection(m_selCollectionIdx);
|
||||||
|
m_backend.getFilePresenter().populateCollectionColumn(*this);
|
||||||
|
m_selCollectionIdx = -1;
|
||||||
|
m_selFileIdx = -1;
|
||||||
|
m_selGroupIdx = -1;
|
||||||
|
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
|
||||||
|
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
|
||||||
|
Button_Enable(m_collectionRemove, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTEditor::update()
|
void VSTEditor::selectCollection(LPARAM idx) {
|
||||||
{
|
if (0x80000000 & idx) {
|
||||||
m_backend.getFilePresenter().populateCollectionColumn(*this);
|
/* Sub-item */
|
||||||
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
|
int rootIdx = (idx >> 16) & 0x7fff;
|
||||||
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
|
int subIdx = idx & 0xffff;
|
||||||
m_backend.setGroup(m_selGroupIdx, true);
|
|
||||||
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
|
|
||||||
selectPage(m_selPageIdx);
|
|
||||||
_reselectColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTEditor::addAction()
|
|
||||||
{
|
|
||||||
std::wstring path = openDB();
|
|
||||||
if (path.size())
|
|
||||||
{
|
|
||||||
amuse::ContainerRegistry::Type containerType;
|
|
||||||
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>> data =
|
|
||||||
amuse::ContainerRegistry::LoadContainer(path.c_str(), containerType);
|
|
||||||
if (data.empty())
|
|
||||||
{
|
|
||||||
wchar_t msg[512];
|
|
||||||
SNPrintf(msg, 512, L"Unable to load Audio Groups from %s", path.c_str());
|
|
||||||
MessageBoxW(nullptr, msg, L"Invalid Data File", MB_OK | MB_ICONERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemString name(amuse::ContainerRegistry::TypeToName(containerType));
|
|
||||||
if (containerType == amuse::ContainerRegistry::Type::Raw4)
|
|
||||||
{
|
|
||||||
size_t dotpos = path.rfind(L'.');
|
|
||||||
if (dotpos != std::string::npos)
|
|
||||||
name.assign(path.cbegin(), path.cbegin() + dotpos);
|
|
||||||
size_t slashpos = name.rfind(L'\\');
|
|
||||||
size_t fslashpos = name.rfind(L'/');
|
|
||||||
if (slashpos == std::string::npos)
|
|
||||||
slashpos = fslashpos;
|
|
||||||
else if (fslashpos != std::string::npos)
|
|
||||||
slashpos = std::max(slashpos, fslashpos);
|
|
||||||
if (slashpos != std::string::npos)
|
|
||||||
name.assign(name.cbegin() + slashpos + 1, name.cend());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_backend.getFilePresenter().addCollection(name, std::move(data));
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTEditor::removeAction()
|
|
||||||
{
|
|
||||||
if (m_selCollectionIdx == -1)
|
|
||||||
return;
|
|
||||||
m_backend.getFilePresenter().removeCollection(m_selCollectionIdx);
|
|
||||||
m_backend.getFilePresenter().populateCollectionColumn(*this);
|
|
||||||
m_selCollectionIdx = -1;
|
|
||||||
m_selFileIdx = -1;
|
|
||||||
m_selGroupIdx = -1;
|
|
||||||
m_backend.getFilePresenter().populateGroupColumn(*this, m_selCollectionIdx, m_selFileIdx);
|
|
||||||
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
|
|
||||||
Button_Enable(m_collectionRemove, FALSE);
|
Button_Enable(m_collectionRemove, FALSE);
|
||||||
|
m_selCollectionIdx = rootIdx;
|
||||||
|
m_selFileIdx = subIdx;
|
||||||
|
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
|
||||||
|
m_backend.getFilePresenter().populateGroupColumn(*this, rootIdx, subIdx);
|
||||||
|
} else {
|
||||||
|
/* Root-item */
|
||||||
|
int rootIdx = (idx >> 16) & 0x7fff;
|
||||||
|
m_selCollectionIdx = rootIdx;
|
||||||
|
Button_Enable(m_collectionRemove, TRUE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTEditor::selectCollection(LPARAM idx)
|
void VSTEditor::selectGroup(int idx) {
|
||||||
{
|
m_selGroupIdx = idx;
|
||||||
if (0x80000000 & idx)
|
m_backend.setGroup(m_selGroupIdx, false);
|
||||||
{
|
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
|
||||||
/* Sub-item */
|
m_lastLParam = -1;
|
||||||
int rootIdx = (idx >> 16) & 0x7fff;
|
}
|
||||||
int subIdx = idx & 0xffff;
|
|
||||||
Button_Enable(m_collectionRemove, FALSE);
|
void VSTEditor::selectPage(int idx) {
|
||||||
m_selCollectionIdx = rootIdx;
|
m_selPageIdx = idx;
|
||||||
m_selFileIdx = subIdx;
|
LV_ITEM item = {};
|
||||||
m_backend.loadGroupFile(m_selCollectionIdx, m_selFileIdx);
|
item.mask = LVIF_PARAM;
|
||||||
m_backend.getFilePresenter().populateGroupColumn(*this, rootIdx, subIdx);
|
item.iItem = idx;
|
||||||
}
|
ListView_GetItem(m_pageListView, &item);
|
||||||
|
m_lastLParam = item.lParam;
|
||||||
|
if (item.lParam & 0x80000000)
|
||||||
|
selectDrumPage(item.lParam & 0x7fffffff);
|
||||||
|
else
|
||||||
|
selectNormalPage(item.lParam & 0x7fffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSTEditor::reselectPage() {
|
||||||
|
if (m_lastLParam != -1) {
|
||||||
|
if (m_lastLParam & 0x80000000)
|
||||||
|
m_backend._setDrumProgram(m_lastLParam & 0x7fffffff);
|
||||||
else
|
else
|
||||||
{
|
m_backend._setNormalProgram(m_lastLParam & 0x7fffffff);
|
||||||
/* Root-item */
|
}
|
||||||
int rootIdx = (idx >> 16) & 0x7fff;
|
|
||||||
m_selCollectionIdx = rootIdx;
|
|
||||||
Button_Enable(m_collectionRemove, TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTEditor::selectGroup(int idx)
|
|
||||||
{
|
|
||||||
m_selGroupIdx = idx;
|
|
||||||
m_backend.setGroup(m_selGroupIdx, false);
|
|
||||||
m_backend.getFilePresenter().populatePageColumn(*this, m_selCollectionIdx, m_selFileIdx, m_selGroupIdx);
|
|
||||||
m_lastLParam = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTEditor::selectPage(int idx)
|
|
||||||
{
|
|
||||||
m_selPageIdx = idx;
|
|
||||||
LV_ITEM item = {};
|
|
||||||
item.mask = LVIF_PARAM;
|
|
||||||
item.iItem = idx;
|
|
||||||
ListView_GetItem(m_pageListView, &item);
|
|
||||||
m_lastLParam = item.lParam;
|
|
||||||
if (item.lParam & 0x80000000)
|
|
||||||
selectDrumPage(item.lParam & 0x7fffffff);
|
|
||||||
else
|
|
||||||
selectNormalPage(item.lParam & 0x7fffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VSTEditor::reselectPage()
|
|
||||||
{
|
|
||||||
if (m_lastLParam != -1)
|
|
||||||
{
|
|
||||||
if (m_lastLParam & 0x80000000)
|
|
||||||
m_backend._setDrumProgram(m_lastLParam & 0x7fffffff);
|
|
||||||
else
|
|
||||||
m_backend._setNormalProgram(m_lastLParam & 0x7fffffff);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTEditor::selectNormalPage(int idx) { m_backend.setNormalProgram(idx); }
|
void VSTEditor::selectNormalPage(int idx) { m_backend.setNormalProgram(idx); }
|
||||||
|
|
||||||
void VSTEditor::selectDrumPage(int idx) { m_backend.setDrumProgram(idx); }
|
void VSTEditor::selectDrumPage(int idx) { m_backend.setDrumProgram(idx); }
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
|
@ -6,57 +6,54 @@
|
||||||
|
|
||||||
#include "aeffeditor.h"
|
#include "aeffeditor.h"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class VSTBackend;
|
class VSTBackend;
|
||||||
|
|
||||||
/** Editor UI class */
|
/** Editor UI class */
|
||||||
class VSTEditor : public AEffEditor
|
class VSTEditor : public AEffEditor {
|
||||||
{
|
friend class VSTBackend;
|
||||||
friend class VSTBackend;
|
friend class AudioGroupFilePresenter;
|
||||||
friend class AudioGroupFilePresenter;
|
friend struct AudioGroupCollection;
|
||||||
friend struct AudioGroupCollection;
|
|
||||||
|
|
||||||
VSTBackend& m_backend;
|
VSTBackend& m_backend;
|
||||||
ERect m_windowRect = {0, 0, 420, 600};
|
ERect m_windowRect = {0, 0, 420, 600};
|
||||||
|
|
||||||
HWND m_rootView = 0;
|
HWND m_rootView = 0;
|
||||||
HWND m_collectionTree = 0;
|
HWND m_collectionTree = 0;
|
||||||
HWND m_collectionAdd = 0;
|
HWND m_collectionAdd = 0;
|
||||||
HWND m_collectionRemove = 0;
|
HWND m_collectionRemove = 0;
|
||||||
HWND m_groupListView = 0;
|
HWND m_groupListView = 0;
|
||||||
HWND m_pageListView = 0;
|
HWND m_pageListView = 0;
|
||||||
|
|
||||||
int m_selCollectionIdx = -1;
|
int m_selCollectionIdx = -1;
|
||||||
int m_selFileIdx = -1;
|
int m_selFileIdx = -1;
|
||||||
int m_selGroupIdx = -1;
|
int m_selGroupIdx = -1;
|
||||||
int m_selPageIdx = -1;
|
int m_selPageIdx = -1;
|
||||||
int m_lastLParam = -1;
|
int m_lastLParam = -1;
|
||||||
|
|
||||||
HTREEITEM m_deferredCollectionSel = 0;
|
HTREEITEM m_deferredCollectionSel = 0;
|
||||||
|
|
||||||
static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
|
static LRESULT CALLBACK WindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
|
||||||
static LRESULT CALLBACK ColHeaderWindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
|
static LRESULT CALLBACK ColHeaderWindowProc(_In_ HWND hwnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
|
||||||
|
|
||||||
void _reselectColumns();
|
void _reselectColumns();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VSTEditor(VSTBackend& backend);
|
VSTEditor(VSTBackend& backend);
|
||||||
|
|
||||||
bool getRect(ERect** rect);
|
bool getRect(ERect** rect);
|
||||||
bool open(void* ptr);
|
bool open(void* ptr);
|
||||||
void close();
|
void close();
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
void addAction();
|
void addAction();
|
||||||
void removeAction();
|
void removeAction();
|
||||||
|
|
||||||
void selectCollection(LPARAM idx);
|
void selectCollection(LPARAM idx);
|
||||||
void selectGroup(int idx);
|
void selectGroup(int idx);
|
||||||
void selectPage(int idx);
|
void selectPage(int idx);
|
||||||
void reselectPage();
|
void reselectPage();
|
||||||
void selectNormalPage(int idx);
|
void selectNormalPage(int idx);
|
||||||
void selectDrumPage(int idx);
|
void selectDrumPage(int idx);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -7,130 +7,111 @@
|
||||||
|
|
||||||
static logvisor::Module Log("amuseconv");
|
static logvisor::Module Log("amuseconv");
|
||||||
|
|
||||||
enum ConvType
|
enum ConvType { ConvN64, ConvGCN, ConvPC };
|
||||||
{
|
|
||||||
ConvN64,
|
|
||||||
ConvGCN,
|
|
||||||
ConvPC
|
|
||||||
};
|
|
||||||
|
|
||||||
static void ReportConvType(ConvType tp)
|
static void ReportConvType(ConvType tp) {
|
||||||
{
|
switch (tp) {
|
||||||
switch (tp)
|
case ConvN64:
|
||||||
{
|
Log.report(logvisor::Info, _SYS_STR("using N64 format"));
|
||||||
case ConvN64:
|
break;
|
||||||
Log.report(logvisor::Info, _SYS_STR("using N64 format"));
|
case ConvPC:
|
||||||
break;
|
Log.report(logvisor::Info, _SYS_STR("using PC format"));
|
||||||
case ConvPC:
|
break;
|
||||||
Log.report(logvisor::Info, _SYS_STR("using PC format"));
|
case ConvGCN:
|
||||||
break;
|
default:
|
||||||
case ConvGCN:
|
Log.report(logvisor::Info, _SYS_STR("using GameCube format"));
|
||||||
default:
|
break;
|
||||||
Log.report(logvisor::Info, _SYS_STR("using GameCube format"));
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
static bool BuildAudioGroup(amuse::SystemStringView groupBase, amuse::SystemStringView targetPath) { return true; }
|
||||||
|
|
||||||
|
static bool ExtractAudioGroup(amuse::SystemStringView inPath, amuse::SystemStringView targetPath) {
|
||||||
|
amuse::ContainerRegistry::Type type;
|
||||||
|
auto groups = amuse::ContainerRegistry::LoadContainer(inPath.data(), type);
|
||||||
|
|
||||||
|
if (groups.size()) {
|
||||||
|
Log.report(logvisor::Info, _SYS_STR("Found '%s'"), amuse::ContainerRegistry::TypeToName(type));
|
||||||
|
|
||||||
|
amuse::Mkdir(targetPath.data(), 0755);
|
||||||
|
Log.report(logvisor::Info, _SYS_STR("Established directory at %s"), targetPath.data());
|
||||||
|
|
||||||
|
for (auto& group : groups) {
|
||||||
|
Log.report(logvisor::Info, _SYS_STR("Extracting %s"), group.first.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool BuildAudioGroup(amuse::SystemStringView groupBase, amuse::SystemStringView targetPath)
|
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
|
||||||
{
|
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _SYS_STR("/midifiles");
|
||||||
return true;
|
bool madeDir = false;
|
||||||
}
|
for (auto& pair : songs) {
|
||||||
|
if (!madeDir) {
|
||||||
static bool ExtractAudioGroup(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
|
amuse::Mkdir(targetPath.data(), 0755);
|
||||||
{
|
amuse::Mkdir(songsDir.c_str(), 0755);
|
||||||
amuse::ContainerRegistry::Type type;
|
madeDir = true;
|
||||||
auto groups = amuse::ContainerRegistry::LoadContainer(inPath.data(), type);
|
|
||||||
|
|
||||||
if (groups.size())
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Info, _SYS_STR("Found '%s'"), amuse::ContainerRegistry::TypeToName(type));
|
|
||||||
|
|
||||||
amuse::Mkdir(targetPath.data(), 0755);
|
|
||||||
Log.report(logvisor::Info, _SYS_STR("Established directory at %s"), targetPath.data());
|
|
||||||
|
|
||||||
for (auto& group : groups)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Info, _SYS_STR("Extracting %s"), group.first.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
|
amuse::SystemString songPath = songsDir + _SYS_STR('/') + pair.first + _SYS_STR(".mid");
|
||||||
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _SYS_STR("/midifiles");
|
FILE* fp = amuse::FOpen(songPath.c_str(), _SYS_STR("wb"));
|
||||||
bool madeDir = false;
|
if (fp) {
|
||||||
for (auto& pair : songs)
|
Log.report(logvisor::Info, _SYS_STR("Extracting %s"), pair.first.c_str());
|
||||||
{
|
int extractedVersion;
|
||||||
if (!madeDir)
|
bool isBig;
|
||||||
{
|
std::vector<uint8_t> mid = amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedVersion, isBig);
|
||||||
amuse::Mkdir(targetPath.data(), 0755);
|
fwrite(mid.data(), 1, mid.size(), fp);
|
||||||
amuse::Mkdir(songsDir.c_str(), 0755);
|
fclose(fp);
|
||||||
madeDir = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
amuse::SystemString songPath = songsDir + _SYS_STR('/') + pair.first + _SYS_STR(".mid");
|
|
||||||
FILE* fp = amuse::FOpen(songPath.c_str(), _SYS_STR("wb"));
|
|
||||||
if (fp)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Info, _SYS_STR("Extracting %s"), pair.first.c_str());
|
|
||||||
int extractedVersion;
|
|
||||||
bool isBig;
|
|
||||||
std::vector<uint8_t> mid =
|
|
||||||
amuse::SongConverter::SongToMIDI(pair.second.m_data.get(), extractedVersion, isBig);
|
|
||||||
fwrite(mid.data(), 1, mid.size(), fp);
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool BuildSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath, int version, bool big)
|
static bool BuildSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath, int version, bool big) {
|
||||||
{
|
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
|
||||||
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
|
if (!fp)
|
||||||
if (!fp)
|
return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
fseek(fp, 0, SEEK_END);
|
||||||
long sz = ftell(fp);
|
long sz = ftell(fp);
|
||||||
fseek(fp, 0, SEEK_SET);
|
fseek(fp, 0, SEEK_SET);
|
||||||
std::vector<uint8_t> data(sz, 0);
|
std::vector<uint8_t> data(sz, 0);
|
||||||
fread(&data[0], 1, sz, fp);
|
fread(&data[0], 1, sz, fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
|
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
|
||||||
if (out.empty())
|
if (out.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
|
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
|
||||||
fwrite(out.data(), 1, out.size(), fp);
|
fwrite(out.data(), 1, out.size(), fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
|
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath) {
|
||||||
{
|
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
|
||||||
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
|
if (!fp)
|
||||||
if (!fp)
|
return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
fseek(fp, 0, SEEK_END);
|
||||||
long sz = ftell(fp);
|
long sz = ftell(fp);
|
||||||
fseek(fp, 0, SEEK_SET);
|
fseek(fp, 0, SEEK_SET);
|
||||||
std::vector<uint8_t> data(sz, 0);
|
std::vector<uint8_t> data(sz, 0);
|
||||||
fread(&data[0], 1, sz, fp);
|
fread(&data[0], 1, sz, fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
int extractedVersion;
|
int extractedVersion;
|
||||||
bool isBig;
|
bool isBig;
|
||||||
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
|
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
|
||||||
if (out.empty())
|
if (out.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
|
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
|
||||||
fwrite(out.data(), 1, out.size(), fp);
|
fwrite(out.data(), 1, out.size(), fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -139,77 +120,64 @@ int wmain(int argc, const amuse::SystemChar** argv)
|
||||||
int main(int argc, const amuse::SystemChar** argv)
|
int main(int argc, const amuse::SystemChar** argv)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
logvisor::RegisterConsoleLogger();
|
logvisor::RegisterConsoleLogger();
|
||||||
|
|
||||||
if (argc < 3)
|
|
||||||
{
|
|
||||||
printf("Usage: amuseconv <in-file> <out-file> [n64|pc|gcn]\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConvType type = ConvGCN;
|
|
||||||
if (argc >= 4)
|
|
||||||
{
|
|
||||||
if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("n64")))
|
|
||||||
type = ConvN64;
|
|
||||||
else if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("gcn")))
|
|
||||||
type = ConvGCN;
|
|
||||||
else if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("pc")))
|
|
||||||
type = ConvPC;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, _SYS_STR("unrecognized format: %s"), argv[3]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool good = false;
|
|
||||||
FILE* fin = amuse::FOpen(argv[1], _SYS_STR("rb"));
|
|
||||||
if (fin)
|
|
||||||
{
|
|
||||||
fclose(fin);
|
|
||||||
amuse::SystemString barePath(argv[1]);
|
|
||||||
size_t dotPos = barePath.rfind(_SYS_STR('.'));
|
|
||||||
const amuse::SystemChar* dot = barePath.c_str() + dotPos;
|
|
||||||
if (dotPos != amuse::SystemString::npos)
|
|
||||||
{
|
|
||||||
if (!amuse::CompareCaseInsensitive(dot, _SYS_STR(".mid")) || !amuse::CompareCaseInsensitive(dot, _SYS_STR(".midi")))
|
|
||||||
{
|
|
||||||
ReportConvType(type);
|
|
||||||
good = BuildSNG(barePath, argv[2], 1, true);
|
|
||||||
}
|
|
||||||
else if (!amuse::CompareCaseInsensitive(dot, _SYS_STR(".son")) || !amuse::CompareCaseInsensitive(dot, _SYS_STR(".sng")))
|
|
||||||
{
|
|
||||||
good = ExtractSNG(argv[1], argv[2]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
good = ExtractAudioGroup(argv[1], argv[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
amuse::Sstat theStat;
|
|
||||||
if (!amuse::Stat(argv[1], &theStat) && S_ISDIR(theStat.st_mode))
|
|
||||||
{
|
|
||||||
amuse::SystemString projectPath(argv[1]);
|
|
||||||
projectPath += _SYS_STR("/project.yaml");
|
|
||||||
fin = amuse::FOpen(projectPath.c_str(), _SYS_STR("rb"));
|
|
||||||
if (fin)
|
|
||||||
{
|
|
||||||
fclose(fin);
|
|
||||||
ReportConvType(type);
|
|
||||||
good = BuildAudioGroup(argv[1], argv[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!good)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, _SYS_STR("unable to convert %s to %s"), argv[1], argv[2]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
printf("Usage: amuseconv <in-file> <out-file> [n64|pc|gcn]\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvType type = ConvGCN;
|
||||||
|
if (argc >= 4) {
|
||||||
|
if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("n64")))
|
||||||
|
type = ConvN64;
|
||||||
|
else if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("gcn")))
|
||||||
|
type = ConvGCN;
|
||||||
|
else if (!amuse::CompareCaseInsensitive(argv[3], _SYS_STR("pc")))
|
||||||
|
type = ConvPC;
|
||||||
|
else {
|
||||||
|
Log.report(logvisor::Error, _SYS_STR("unrecognized format: %s"), argv[3]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool good = false;
|
||||||
|
FILE* fin = amuse::FOpen(argv[1], _SYS_STR("rb"));
|
||||||
|
if (fin) {
|
||||||
|
fclose(fin);
|
||||||
|
amuse::SystemString barePath(argv[1]);
|
||||||
|
size_t dotPos = barePath.rfind(_SYS_STR('.'));
|
||||||
|
const amuse::SystemChar* dot = barePath.c_str() + dotPos;
|
||||||
|
if (dotPos != amuse::SystemString::npos) {
|
||||||
|
if (!amuse::CompareCaseInsensitive(dot, _SYS_STR(".mid")) ||
|
||||||
|
!amuse::CompareCaseInsensitive(dot, _SYS_STR(".midi"))) {
|
||||||
|
ReportConvType(type);
|
||||||
|
good = BuildSNG(barePath, argv[2], 1, true);
|
||||||
|
} else if (!amuse::CompareCaseInsensitive(dot, _SYS_STR(".son")) ||
|
||||||
|
!amuse::CompareCaseInsensitive(dot, _SYS_STR(".sng"))) {
|
||||||
|
good = ExtractSNG(argv[1], argv[2]);
|
||||||
|
} else {
|
||||||
|
good = ExtractAudioGroup(argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
amuse::Sstat theStat;
|
||||||
|
if (!amuse::Stat(argv[1], &theStat) && S_ISDIR(theStat.st_mode)) {
|
||||||
|
amuse::SystemString projectPath(argv[1]);
|
||||||
|
projectPath += _SYS_STR("/project.yaml");
|
||||||
|
fin = amuse::FOpen(projectPath.c_str(), _SYS_STR("rb"));
|
||||||
|
if (fin) {
|
||||||
|
fclose(fin);
|
||||||
|
ReportConvType(type);
|
||||||
|
good = BuildAudioGroup(argv[1], argv[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!good) {
|
||||||
|
Log.report(logvisor::Error, _SYS_STR("unable to convert %s to %s"), argv[1], argv[2]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
1791
driver/amuseplay.cpp
1791
driver/amuseplay.cpp
File diff suppressed because it is too large
Load Diff
|
@ -21,16 +21,15 @@ static logvisor::Module Log("amuserender");
|
||||||
__attribute__((__format__(__printf__, 3, 4)))
|
__attribute__((__format__(__printf__, 3, 4)))
|
||||||
#endif
|
#endif
|
||||||
static inline void
|
static inline void
|
||||||
SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...)
|
SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...) {
|
||||||
{
|
va_list va;
|
||||||
va_list va;
|
va_start(va, format);
|
||||||
va_start(va, format);
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
_vsnwprintf(str, maxlen, format, va);
|
_vsnwprintf(str, maxlen, format, va);
|
||||||
#else
|
#else
|
||||||
vsnprintf(str, maxlen, format, va);
|
vsnprintf(str, maxlen, format, va);
|
||||||
#endif
|
#endif
|
||||||
va_end(va);
|
va_end(va);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -39,49 +38,44 @@ SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
static void abortHandler(int signum)
|
static void abortHandler(int signum) {
|
||||||
{
|
unsigned int i;
|
||||||
unsigned int i;
|
void* stack[100];
|
||||||
void* stack[100];
|
unsigned short frames;
|
||||||
unsigned short frames;
|
SYMBOL_INFO* symbol;
|
||||||
SYMBOL_INFO* symbol;
|
HANDLE process;
|
||||||
HANDLE process;
|
|
||||||
|
|
||||||
process = GetCurrentProcess();
|
process = GetCurrentProcess();
|
||||||
SymInitialize(process, NULL, TRUE);
|
SymInitialize(process, NULL, TRUE);
|
||||||
frames = CaptureStackBackTrace(0, 100, stack, NULL);
|
frames = CaptureStackBackTrace(0, 100, stack, NULL);
|
||||||
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
|
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
|
||||||
symbol->MaxNameLen = 255;
|
symbol->MaxNameLen = 255;
|
||||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||||
|
|
||||||
for (i = 0; i < frames; i++)
|
for (i = 0; i < frames; i++) {
|
||||||
{
|
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
||||||
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
|
|
||||||
|
|
||||||
printf("%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
|
printf("%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
|
||||||
|
|
||||||
DWORD dwDisplacement;
|
DWORD dwDisplacement;
|
||||||
IMAGEHLP_LINE64 line;
|
IMAGEHLP_LINE64 line;
|
||||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||||
|
|
||||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line))
|
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
|
||||||
{
|
// SymGetLineFromAddr64 returned success
|
||||||
// SymGetLineFromAddr64 returned success
|
printf(" LINE %d\n", line.LineNumber);
|
||||||
printf(" LINE %d\n", line.LineNumber);
|
} else {
|
||||||
}
|
printf("\n");
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
free(symbol);
|
free(symbol);
|
||||||
|
|
||||||
// If you caught one of the above signals, it is likely you just
|
// If you caught one of the above signals, it is likely you just
|
||||||
// want to quit your program right now.
|
// want to quit your program right now.
|
||||||
system("PAUSE");
|
system("PAUSE");
|
||||||
exit(signum);
|
exit(signum);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -95,456 +89,378 @@ int wmain(int argc, const boo::SystemChar** argv)
|
||||||
int main(int argc, const boo::SystemChar** argv)
|
int main(int argc, const boo::SystemChar** argv)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
logvisor::RegisterConsoleLogger();
|
logvisor::RegisterConsoleLogger();
|
||||||
|
|
||||||
std::vector<boo::SystemString> m_args;
|
std::vector<boo::SystemString> m_args;
|
||||||
m_args.reserve(argc);
|
m_args.reserve(argc);
|
||||||
double rate = NativeSampleRate;
|
double rate = NativeSampleRate;
|
||||||
int chCount = 2;
|
int chCount = 2;
|
||||||
double volume = 1.0;
|
double volume = 1.0;
|
||||||
for (int i = 1; i < argc; ++i)
|
for (int i = 1; i < argc; ++i) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
if (!wcsncmp(argv[i], L"-r", 2))
|
if (!wcsncmp(argv[i], L"-r", 2)) {
|
||||||
{
|
if (argv[i][2])
|
||||||
if (argv[i][2])
|
rate = wcstod(&argv[i][2], nullptr);
|
||||||
rate = wcstod(&argv[i][2], nullptr);
|
else if (argc > (i + 1)) {
|
||||||
else if (argc > (i + 1))
|
rate = wcstod(argv[i + 1], nullptr);
|
||||||
{
|
++i;
|
||||||
rate = wcstod(argv[i + 1], nullptr);
|
}
|
||||||
++i;
|
} else if (!wcsncmp(argv[i], L"-c", 2)) {
|
||||||
}
|
if (argv[i][2])
|
||||||
}
|
chCount = wcstoul(&argv[i][2], nullptr, 0);
|
||||||
else if (!wcsncmp(argv[i], L"-c", 2))
|
else if (argc > (i + 1)) {
|
||||||
{
|
chCount = wcstoul(argv[i + 1], nullptr, 0);
|
||||||
if (argv[i][2])
|
++i;
|
||||||
chCount = wcstoul(&argv[i][2], nullptr, 0);
|
}
|
||||||
else if (argc > (i + 1))
|
} else if (!wcsncmp(argv[i], L"-v", 2)) {
|
||||||
{
|
if (argv[i][2])
|
||||||
chCount = wcstoul(argv[i + 1], nullptr, 0);
|
volume = wcstod(&argv[i][2], nullptr);
|
||||||
++i;
|
else if (argc > (i + 1)) {
|
||||||
}
|
volume = wcstod(argv[i + 1], nullptr);
|
||||||
}
|
++i;
|
||||||
else if (!wcsncmp(argv[i], L"-v", 2))
|
}
|
||||||
{
|
} else
|
||||||
if (argv[i][2])
|
m_args.push_back(argv[i]);
|
||||||
volume = wcstod(&argv[i][2], nullptr);
|
|
||||||
else if (argc > (i + 1))
|
|
||||||
{
|
|
||||||
volume = wcstod(argv[i + 1], nullptr);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_args.push_back(argv[i]);
|
|
||||||
#else
|
#else
|
||||||
if (!strncmp(argv[i], "-r", 2))
|
if (!strncmp(argv[i], "-r", 2)) {
|
||||||
{
|
if (argv[i][2])
|
||||||
if (argv[i][2])
|
rate = strtod(&argv[i][2], nullptr);
|
||||||
rate = strtod(&argv[i][2], nullptr);
|
else if (argc > (i + 1)) {
|
||||||
else if (argc > (i + 1))
|
rate = strtod(argv[i + 1], nullptr);
|
||||||
{
|
++i;
|
||||||
rate = strtod(argv[i + 1], nullptr);
|
}
|
||||||
++i;
|
} else if (!strncmp(argv[i], "-c", 2)) {
|
||||||
}
|
if (argv[i][2])
|
||||||
}
|
chCount = strtoul(&argv[i][2], nullptr, 0);
|
||||||
else if (!strncmp(argv[i], "-c", 2))
|
else if (argc > (i + 1)) {
|
||||||
{
|
chCount = strtoul(argv[i + 1], nullptr, 0);
|
||||||
if (argv[i][2])
|
++i;
|
||||||
chCount = strtoul(&argv[i][2], nullptr, 0);
|
}
|
||||||
else if (argc > (i + 1))
|
} else if (!strncmp(argv[i], "-v", 2)) {
|
||||||
{
|
if (argv[i][2])
|
||||||
chCount = strtoul(argv[i + 1], nullptr, 0);
|
volume = strtod(&argv[i][2], nullptr);
|
||||||
++i;
|
else if (argc > (i + 1)) {
|
||||||
}
|
volume = strtod(argv[i + 1], nullptr);
|
||||||
}
|
++i;
|
||||||
else if (!strncmp(argv[i], "-v", 2))
|
}
|
||||||
{
|
} else
|
||||||
if (argv[i][2])
|
m_args.push_back(argv[i]);
|
||||||
volume = strtod(&argv[i][2], nullptr);
|
|
||||||
else if (argc > (i + 1))
|
|
||||||
{
|
|
||||||
volume = strtod(argv[i + 1], nullptr);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_args.push_back(argv[i]);
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load data */
|
||||||
|
if (m_args.size() < 1) {
|
||||||
|
Log.report(logvisor::Error,
|
||||||
|
"Usage: amuserender <group-file> [<songs-file>] [-r <sample-rate>] [-c <channel-count>] [-v <volume "
|
||||||
|
"0.0-1.0>]");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
amuse::ContainerRegistry::Type cType = amuse::ContainerRegistry::DetectContainerType(m_args[0].c_str());
|
||||||
|
if (cType == amuse::ContainerRegistry::Type::Invalid) {
|
||||||
|
Log.report(logvisor::Error, "invalid/no data at path argument");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Log.report(logvisor::Info, _SYS_STR("Found '%s' Audio Group data"), amuse::ContainerRegistry::TypeToName(cType));
|
||||||
|
|
||||||
|
std::vector<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>> data =
|
||||||
|
amuse::ContainerRegistry::LoadContainer(m_args[0].c_str());
|
||||||
|
if (data.empty()) {
|
||||||
|
Log.report(logvisor::Error, "invalid/no data at path argument");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_groupId = -1;
|
||||||
|
int m_setupId = -1;
|
||||||
|
const amuse::SystemString* m_groupName = nullptr;
|
||||||
|
const amuse::SystemString* m_songName = nullptr;
|
||||||
|
amuse::ContainerRegistry::SongData* m_arrData = nullptr;
|
||||||
|
bool m_sfxGroup = false;
|
||||||
|
|
||||||
|
std::list<amuse::AudioGroupProject> m_projs;
|
||||||
|
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
|
||||||
|
amuse::ObjToken<amuse::SongGroupIndex>>>
|
||||||
|
allSongGroups;
|
||||||
|
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
|
||||||
|
amuse::ObjToken<amuse::SFXGroupIndex>>>
|
||||||
|
allSFXGroups;
|
||||||
|
size_t totalGroups = 0;
|
||||||
|
|
||||||
|
for (auto& grp : data) {
|
||||||
|
/* Load project to assemble group list */
|
||||||
|
m_projs.push_back(amuse::AudioGroupProject::CreateAudioGroupProject(grp.second));
|
||||||
|
amuse::AudioGroupProject& proj = m_projs.back();
|
||||||
|
totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
|
||||||
|
|
||||||
|
for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it)
|
||||||
|
allSongGroups[it->first] = std::make_pair(&grp, it->second);
|
||||||
|
|
||||||
|
for (auto it = proj.sfxGroups().begin(); it != proj.sfxGroups().end(); ++it)
|
||||||
|
allSFXGroups[it->first] = std::make_pair(&grp, it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt loading song */
|
||||||
|
std::vector<std::pair<amuse::SystemString, amuse::ContainerRegistry::SongData>> songs;
|
||||||
|
if (m_args.size() > 1)
|
||||||
|
songs = amuse::ContainerRegistry::LoadSongs(m_args[1].c_str());
|
||||||
|
else
|
||||||
|
songs = amuse::ContainerRegistry::LoadSongs(m_args[0].c_str());
|
||||||
|
|
||||||
|
if (songs.size()) {
|
||||||
|
bool play = true;
|
||||||
|
if (m_args.size() <= 1) {
|
||||||
|
bool prompt = true;
|
||||||
|
while (true) {
|
||||||
|
if (prompt) {
|
||||||
|
printf("Render Song? (Y/N): ");
|
||||||
|
prompt = false;
|
||||||
|
}
|
||||||
|
char userSel;
|
||||||
|
if (scanf("%c", &userSel) <= 0 || userSel == '\n')
|
||||||
|
continue;
|
||||||
|
userSel = tolower(userSel);
|
||||||
|
if (userSel == 'n')
|
||||||
|
play = false;
|
||||||
|
else if (userSel != 'y') {
|
||||||
|
prompt = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load data */
|
if (play) {
|
||||||
if (m_args.size() < 1)
|
/* Get song selection from user */
|
||||||
{
|
if (songs.size() > 1) {
|
||||||
Log.report(logvisor::Error, "Usage: amuserender <group-file> [<songs-file>] [-r <sample-rate>] [-c <channel-count>] [-v <volume 0.0-1.0>]");
|
/* Ask user to specify which song */
|
||||||
return 1;
|
printf("Multiple Songs discovered:\n");
|
||||||
}
|
int idx = 0;
|
||||||
|
for (const auto& pair : songs) {
|
||||||
amuse::ContainerRegistry::Type cType = amuse::ContainerRegistry::DetectContainerType(m_args[0].c_str());
|
const amuse::ContainerRegistry::SongData& sngData = pair.second;
|
||||||
if (cType == amuse::ContainerRegistry::Type::Invalid)
|
int16_t grpId = sngData.m_groupId;
|
||||||
{
|
int16_t setupId = sngData.m_setupId;
|
||||||
Log.report(logvisor::Error, "invalid/no data at path argument");
|
if (sngData.m_groupId == -1 && sngData.m_setupId != -1) {
|
||||||
return 1;
|
for (const auto& pair : allSongGroups) {
|
||||||
}
|
for (const auto& setup : pair.second.second->m_midiSetups) {
|
||||||
Log.report(logvisor::Info, _SYS_STR("Found '%s' Audio Group data"), amuse::ContainerRegistry::TypeToName(cType));
|
if (setup.first == sngData.m_setupId) {
|
||||||
|
grpId = pair.first;
|
||||||
std::vector<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>> data =
|
break;
|
||||||
amuse::ContainerRegistry::LoadContainer(m_args[0].c_str());
|
|
||||||
if (data.empty())
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "invalid/no data at path argument");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int m_groupId = -1;
|
|
||||||
int m_setupId = -1;
|
|
||||||
const amuse::SystemString* m_groupName = nullptr;
|
|
||||||
const amuse::SystemString* m_songName = nullptr;
|
|
||||||
amuse::ContainerRegistry::SongData* m_arrData = nullptr;
|
|
||||||
bool m_sfxGroup = false;
|
|
||||||
|
|
||||||
std::list<amuse::AudioGroupProject> m_projs;
|
|
||||||
std::map<int,
|
|
||||||
std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, amuse::ObjToken<amuse::SongGroupIndex>>>
|
|
||||||
allSongGroups;
|
|
||||||
std::map<int,
|
|
||||||
std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, amuse::ObjToken<amuse::SFXGroupIndex>>>
|
|
||||||
allSFXGroups;
|
|
||||||
size_t totalGroups = 0;
|
|
||||||
|
|
||||||
for (auto& grp : data)
|
|
||||||
{
|
|
||||||
/* Load project to assemble group list */
|
|
||||||
m_projs.push_back(amuse::AudioGroupProject::CreateAudioGroupProject(grp.second));
|
|
||||||
amuse::AudioGroupProject& proj = m_projs.back();
|
|
||||||
totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
|
|
||||||
|
|
||||||
for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it)
|
|
||||||
allSongGroups[it->first] = std::make_pair(&grp, it->second);
|
|
||||||
|
|
||||||
for (auto it = proj.sfxGroups().begin(); it != proj.sfxGroups().end(); ++it)
|
|
||||||
allSFXGroups[it->first] = std::make_pair(&grp, it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attempt loading song */
|
|
||||||
std::vector<std::pair<amuse::SystemString, amuse::ContainerRegistry::SongData>> songs;
|
|
||||||
if (m_args.size() > 1)
|
|
||||||
songs = amuse::ContainerRegistry::LoadSongs(m_args[1].c_str());
|
|
||||||
else
|
|
||||||
songs = amuse::ContainerRegistry::LoadSongs(m_args[0].c_str());
|
|
||||||
|
|
||||||
if (songs.size())
|
|
||||||
{
|
|
||||||
bool play = true;
|
|
||||||
if (m_args.size() <= 1)
|
|
||||||
{
|
|
||||||
bool prompt = true;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (prompt)
|
|
||||||
{
|
|
||||||
printf("Render Song? (Y/N): ");
|
|
||||||
prompt = false;
|
|
||||||
}
|
|
||||||
char userSel;
|
|
||||||
if (scanf("%c", &userSel) <= 0 || userSel == '\n')
|
|
||||||
continue;
|
|
||||||
userSel = tolower(userSel);
|
|
||||||
if (userSel == 'n')
|
|
||||||
play = false;
|
|
||||||
else if (userSel != 'y')
|
|
||||||
{
|
|
||||||
prompt = true;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (grpId != -1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
amuse::Printf(_SYS_STR(" %d %s (Group %d, Setup %d)\n"), idx++, pair.first.c_str(), grpId, setupId);
|
||||||
if (play)
|
|
||||||
{
|
|
||||||
/* Get song selection from user */
|
|
||||||
if (songs.size() > 1)
|
|
||||||
{
|
|
||||||
/* Ask user to specify which song */
|
|
||||||
printf("Multiple Songs discovered:\n");
|
|
||||||
int idx = 0;
|
|
||||||
for (const auto& pair : songs)
|
|
||||||
{
|
|
||||||
const amuse::ContainerRegistry::SongData& sngData = pair.second;
|
|
||||||
int16_t grpId = sngData.m_groupId;
|
|
||||||
int16_t setupId = sngData.m_setupId;
|
|
||||||
if (sngData.m_groupId == -1 && sngData.m_setupId != -1)
|
|
||||||
{
|
|
||||||
for (const auto& pair : allSongGroups)
|
|
||||||
{
|
|
||||||
for (const auto& setup : pair.second.second->m_midiSetups)
|
|
||||||
{
|
|
||||||
if (setup.first == sngData.m_setupId)
|
|
||||||
{
|
|
||||||
grpId = pair.first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (grpId != -1)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
amuse::Printf(_SYS_STR(" %d %s (Group %d, Setup %d)\n"), idx++, pair.first.c_str(), grpId, setupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int userSel = 0;
|
|
||||||
printf("Enter Song Number: ");
|
|
||||||
if (scanf("%d", &userSel) <= 0)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to parse prompt");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userSel < songs.size())
|
|
||||||
{
|
|
||||||
m_arrData = &songs[userSel].second;
|
|
||||||
m_groupId = m_arrData->m_groupId;
|
|
||||||
m_setupId = m_arrData->m_setupId;
|
|
||||||
m_songName = &songs[userSel].first;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to find Song %d", userSel);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (songs.size() == 1)
|
|
||||||
{
|
|
||||||
m_arrData = &songs[0].second;
|
|
||||||
m_groupId = m_arrData->m_groupId;
|
|
||||||
m_setupId = m_arrData->m_setupId;
|
|
||||||
m_songName = &songs[0].first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get group selection via setup search */
|
|
||||||
if (m_groupId == -1 && m_setupId != -1)
|
|
||||||
{
|
|
||||||
for (const auto& pair : allSongGroups)
|
|
||||||
{
|
|
||||||
for (const auto& setup : pair.second.second->m_midiSetups)
|
|
||||||
{
|
|
||||||
if (setup.first == m_setupId)
|
|
||||||
{
|
|
||||||
m_groupId = pair.first;
|
|
||||||
m_groupName = &pair.second.first->first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_groupId != -1)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get group selection via user */
|
|
||||||
if (m_groupId != -1)
|
|
||||||
{
|
|
||||||
auto songSearch = allSongGroups.find(m_groupId);
|
|
||||||
auto sfxSearch = allSFXGroups.find(m_groupId);
|
|
||||||
if (songSearch != allSongGroups.end())
|
|
||||||
{
|
|
||||||
m_sfxGroup = false;
|
|
||||||
m_groupName = &songSearch->second.first->first;
|
|
||||||
}
|
|
||||||
else if (sfxSearch != allSFXGroups.end())
|
|
||||||
{
|
|
||||||
m_sfxGroup = true;
|
|
||||||
m_groupName = &sfxSearch->second.first->first;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to find Group %d", m_groupId);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (totalGroups > 1)
|
|
||||||
{
|
|
||||||
/* Ask user to specify which group in project */
|
|
||||||
printf("Multiple Audio Groups discovered:\n");
|
|
||||||
for (const auto& pair : allSFXGroups)
|
|
||||||
{
|
|
||||||
amuse::Printf(_SYS_STR(" %d %s (SFXGroup) %" PRISize " sfx-entries\n"), pair.first,
|
|
||||||
pair.second.first->first.c_str(), pair.second.second->m_sfxEntries.size());
|
|
||||||
}
|
|
||||||
for (const auto& pair : allSongGroups)
|
|
||||||
{
|
|
||||||
amuse::Printf(_SYS_STR(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize
|
|
||||||
" MIDI-setups\n"),
|
|
||||||
pair.first, pair.second.first->first.c_str(), pair.second.second->m_normPages.size(),
|
|
||||||
pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int userSel = 0;
|
int userSel = 0;
|
||||||
printf("Enter Group Number: ");
|
printf("Enter Song Number: ");
|
||||||
if (scanf("%d", &userSel) <= 0)
|
if (scanf("%d", &userSel) <= 0) {
|
||||||
{
|
Log.report(logvisor::Error, "unable to parse prompt");
|
||||||
Log.report(logvisor::Error, "unable to parse prompt");
|
return 1;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto songSearch = allSongGroups.find(userSel);
|
if (userSel < songs.size()) {
|
||||||
auto sfxSearch = allSFXGroups.find(userSel);
|
m_arrData = &songs[userSel].second;
|
||||||
if (songSearch != allSongGroups.end())
|
m_groupId = m_arrData->m_groupId;
|
||||||
{
|
m_setupId = m_arrData->m_setupId;
|
||||||
m_groupId = userSel;
|
m_songName = &songs[userSel].first;
|
||||||
m_groupName = &songSearch->second.first->first;
|
} else {
|
||||||
m_sfxGroup = false;
|
Log.report(logvisor::Error, "unable to find Song %d", userSel);
|
||||||
}
|
return 1;
|
||||||
else if (sfxSearch != allSFXGroups.end())
|
|
||||||
{
|
|
||||||
m_groupId = userSel;
|
|
||||||
m_groupName = &sfxSearch->second.first->first;
|
|
||||||
m_sfxGroup = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to find Group %d", userSel);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
} else if (songs.size() == 1) {
|
||||||
|
m_arrData = &songs[0].second;
|
||||||
|
m_groupId = m_arrData->m_groupId;
|
||||||
|
m_setupId = m_arrData->m_setupId;
|
||||||
|
m_songName = &songs[0].first;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (totalGroups == 1)
|
}
|
||||||
{
|
|
||||||
/* Load one and only group */
|
|
||||||
if (allSongGroups.size())
|
|
||||||
{
|
|
||||||
const auto& pair = *allSongGroups.cbegin();
|
|
||||||
m_groupId = pair.first;
|
|
||||||
m_groupName = &pair.second.first->first;
|
|
||||||
m_sfxGroup = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& pair = *allSFXGroups.cbegin();
|
|
||||||
m_groupId = pair.first;
|
|
||||||
m_groupName = &pair.second.first->first;
|
|
||||||
m_sfxGroup = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "empty project");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make final group selection */
|
/* Get group selection via setup search */
|
||||||
amuse::IntrusiveAudioGroupData* selData = nullptr;
|
if (m_groupId == -1 && m_setupId != -1) {
|
||||||
amuse::ObjToken<amuse::SongGroupIndex> songIndex;
|
for (const auto& pair : allSongGroups) {
|
||||||
amuse::ObjToken<amuse::SFXGroupIndex> sfxIndex;
|
for (const auto& setup : pair.second.second->m_midiSetups) {
|
||||||
|
if (setup.first == m_setupId) {
|
||||||
|
m_groupId = pair.first;
|
||||||
|
m_groupName = &pair.second.first->first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_groupId != -1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get group selection via user */
|
||||||
|
if (m_groupId != -1) {
|
||||||
auto songSearch = allSongGroups.find(m_groupId);
|
auto songSearch = allSongGroups.find(m_groupId);
|
||||||
if (songSearch != allSongGroups.end())
|
auto sfxSearch = allSFXGroups.find(m_groupId);
|
||||||
{
|
if (songSearch != allSongGroups.end()) {
|
||||||
selData = &songSearch->second.first->second;
|
m_sfxGroup = false;
|
||||||
songIndex = songSearch->second.second;
|
m_groupName = &songSearch->second.first->first;
|
||||||
std::set<int> sortSetups;
|
} else if (sfxSearch != allSFXGroups.end()) {
|
||||||
for (auto& pair : songIndex->m_midiSetups)
|
m_sfxGroup = true;
|
||||||
sortSetups.insert(pair.first);
|
m_groupName = &sfxSearch->second.first->first;
|
||||||
if (m_setupId == -1)
|
} else {
|
||||||
{
|
Log.report(logvisor::Error, "unable to find Group %d", m_groupId);
|
||||||
/* Ask user to specify which group in project */
|
return 1;
|
||||||
printf("Multiple MIDI Setups:\n");
|
|
||||||
for (int setup : sortSetups)
|
|
||||||
printf(" %d\n", setup);
|
|
||||||
int userSel = 0;
|
|
||||||
printf("Enter Setup Number: ");
|
|
||||||
if (scanf("%d", &userSel) <= 0)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to parse prompt");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
m_setupId = userSel;
|
|
||||||
}
|
|
||||||
if (sortSetups.find(m_setupId) == sortSetups.cend())
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to find setup %d", m_setupId);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
} else if (totalGroups > 1) {
|
||||||
{
|
/* Ask user to specify which group in project */
|
||||||
auto sfxSearch = allSFXGroups.find(m_groupId);
|
printf("Multiple Audio Groups discovered:\n");
|
||||||
if (sfxSearch != allSFXGroups.end())
|
for (const auto& pair : allSFXGroups) {
|
||||||
{
|
amuse::Printf(_SYS_STR(" %d %s (SFXGroup) %" PRISize " sfx-entries\n"), pair.first,
|
||||||
selData = &sfxSearch->second.first->second;
|
pair.second.first->first.c_str(), pair.second.second->m_sfxEntries.size());
|
||||||
sfxIndex = sfxSearch->second.second;
|
}
|
||||||
}
|
for (const auto& pair : allSongGroups) {
|
||||||
|
amuse::Printf(_SYS_STR(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize
|
||||||
|
" MIDI-setups\n"),
|
||||||
|
pair.first, pair.second.first->first.c_str(), pair.second.second->m_normPages.size(),
|
||||||
|
pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selData)
|
int userSel = 0;
|
||||||
{
|
printf("Enter Group Number: ");
|
||||||
Log.report(logvisor::Error, "unable to select audio group data");
|
if (scanf("%d", &userSel) <= 0) {
|
||||||
|
Log.report(logvisor::Error, "unable to parse prompt");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto songSearch = allSongGroups.find(userSel);
|
||||||
|
auto sfxSearch = allSFXGroups.find(userSel);
|
||||||
|
if (songSearch != allSongGroups.end()) {
|
||||||
|
m_groupId = userSel;
|
||||||
|
m_groupName = &songSearch->second.first->first;
|
||||||
|
m_sfxGroup = false;
|
||||||
|
} else if (sfxSearch != allSFXGroups.end()) {
|
||||||
|
m_groupId = userSel;
|
||||||
|
m_groupName = &sfxSearch->second.first->first;
|
||||||
|
m_sfxGroup = true;
|
||||||
|
} else {
|
||||||
|
Log.report(logvisor::Error, "unable to find Group %d", userSel);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (totalGroups == 1) {
|
||||||
|
/* Load one and only group */
|
||||||
|
if (allSongGroups.size()) {
|
||||||
|
const auto& pair = *allSongGroups.cbegin();
|
||||||
|
m_groupId = pair.first;
|
||||||
|
m_groupName = &pair.second.first->first;
|
||||||
|
m_sfxGroup = false;
|
||||||
|
} else {
|
||||||
|
const auto& pair = *allSFXGroups.cbegin();
|
||||||
|
m_groupId = pair.first;
|
||||||
|
m_groupName = &pair.second.first->first;
|
||||||
|
m_sfxGroup = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.report(logvisor::Error, "empty project");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make final group selection */
|
||||||
|
amuse::IntrusiveAudioGroupData* selData = nullptr;
|
||||||
|
amuse::ObjToken<amuse::SongGroupIndex> songIndex;
|
||||||
|
amuse::ObjToken<amuse::SFXGroupIndex> sfxIndex;
|
||||||
|
auto songSearch = allSongGroups.find(m_groupId);
|
||||||
|
if (songSearch != allSongGroups.end()) {
|
||||||
|
selData = &songSearch->second.first->second;
|
||||||
|
songIndex = songSearch->second.second;
|
||||||
|
std::set<int> sortSetups;
|
||||||
|
for (auto& pair : songIndex->m_midiSetups)
|
||||||
|
sortSetups.insert(pair.first);
|
||||||
|
if (m_setupId == -1) {
|
||||||
|
/* Ask user to specify which group in project */
|
||||||
|
printf("Multiple MIDI Setups:\n");
|
||||||
|
for (int setup : sortSetups)
|
||||||
|
printf(" %d\n", setup);
|
||||||
|
int userSel = 0;
|
||||||
|
printf("Enter Setup Number: ");
|
||||||
|
if (scanf("%d", &userSel) <= 0) {
|
||||||
|
Log.report(logvisor::Error, "unable to parse prompt");
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
m_setupId = userSel;
|
||||||
}
|
}
|
||||||
|
if (sortSetups.find(m_setupId) == sortSetups.cend()) {
|
||||||
if (m_sfxGroup)
|
Log.report(logvisor::Error, "unable to find setup %d", m_setupId);
|
||||||
{
|
return 1;
|
||||||
Log.report(logvisor::Error, "amuserender is currently only able to render SongGroups");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
/* WAV out path */
|
auto sfxSearch = allSFXGroups.find(m_groupId);
|
||||||
amuse::SystemChar pathOut[1024];
|
if (sfxSearch != allSFXGroups.end()) {
|
||||||
SNPrintf(pathOut, 1024, _SYS_STR("%s-%s.wav"), m_groupName->c_str(), m_songName->c_str());
|
selData = &sfxSearch->second.first->second;
|
||||||
Log.report(logvisor::Info, _SYS_STR("Writing to %s"), pathOut);
|
sfxIndex = sfxSearch->second.second;
|
||||||
|
|
||||||
/* Build voice engine */
|
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewWAVAudioVoiceEngine(pathOut, rate, chCount);
|
|
||||||
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
|
||||||
amuse::Engine engine(booBackend, amuse::AmplitudeMode::PerSample);
|
|
||||||
engine.setVolume(float(amuse::clamp(0.0, volume, 1.0)));
|
|
||||||
|
|
||||||
/* Load group into engine */
|
|
||||||
const amuse::AudioGroup* group = engine.addAudioGroup(*selData);
|
|
||||||
if (!group)
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "unable to add audio group");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Enter playback loop */
|
if (!selData) {
|
||||||
amuse::ObjToken<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get(), false);
|
Log.report(logvisor::Error, "unable to select audio group data");
|
||||||
size_t wroteFrames = 0;
|
return 1;
|
||||||
signal(SIGINT, SIGINTHandler);
|
}
|
||||||
do
|
|
||||||
{
|
|
||||||
voxEngine->pumpAndMixVoices();
|
|
||||||
wroteFrames += voxEngine->get5MsFrames();
|
|
||||||
printf("\rFrame %" PRISize, wroteFrames);
|
|
||||||
fflush(stdout);
|
|
||||||
} while (!g_BreakLoop && (seq->state() == amuse::SequencerState::Playing || seq->getVoiceCount() != 0));
|
|
||||||
|
|
||||||
printf("\n");
|
if (m_sfxGroup) {
|
||||||
return 0;
|
Log.report(logvisor::Error, "amuserender is currently only able to render SongGroups");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WAV out path */
|
||||||
|
amuse::SystemChar pathOut[1024];
|
||||||
|
SNPrintf(pathOut, 1024, _SYS_STR("%s-%s.wav"), m_groupName->c_str(), m_songName->c_str());
|
||||||
|
Log.report(logvisor::Info, _SYS_STR("Writing to %s"), pathOut);
|
||||||
|
|
||||||
|
/* Build voice engine */
|
||||||
|
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewWAVAudioVoiceEngine(pathOut, rate, chCount);
|
||||||
|
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
||||||
|
amuse::Engine engine(booBackend, amuse::AmplitudeMode::PerSample);
|
||||||
|
engine.setVolume(float(amuse::clamp(0.0, volume, 1.0)));
|
||||||
|
|
||||||
|
/* Load group into engine */
|
||||||
|
const amuse::AudioGroup* group = engine.addAudioGroup(*selData);
|
||||||
|
if (!group) {
|
||||||
|
Log.report(logvisor::Error, "unable to add audio group");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enter playback loop */
|
||||||
|
amuse::ObjToken<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get(), false);
|
||||||
|
size_t wroteFrames = 0;
|
||||||
|
signal(SIGINT, SIGINTHandler);
|
||||||
|
do {
|
||||||
|
voxEngine->pumpAndMixVoices();
|
||||||
|
wroteFrames += voxEngine->get5MsFrames();
|
||||||
|
printf("\rFrame %" PRISize, wroteFrames);
|
||||||
|
fflush(stdout);
|
||||||
|
} while (!g_BreakLoop && (seq->state() == amuse::SequencerState::Playing || seq->getVoiceCount() != 0));
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
|
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) {
|
||||||
{
|
signal(SIGABRT, abortHandler);
|
||||||
signal(SIGABRT, abortHandler);
|
signal(SIGSEGV, abortHandler);
|
||||||
signal(SIGSEGV, abortHandler);
|
signal(SIGILL, abortHandler);
|
||||||
signal(SIGILL, abortHandler);
|
signal(SIGFPE, abortHandler);
|
||||||
signal(SIGFPE, abortHandler);
|
|
||||||
|
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
const boo::SystemChar** argv;
|
const boo::SystemChar** argv;
|
||||||
if (lpCmdLine[0])
|
if (lpCmdLine[0])
|
||||||
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
|
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
|
||||||
static boo::SystemChar selfPath[1024];
|
static boo::SystemChar selfPath[1024];
|
||||||
GetModuleFileNameW(nullptr, selfPath, 1024);
|
GetModuleFileNameW(nullptr, selfPath, 1024);
|
||||||
static const boo::SystemChar* booArgv[32] = {};
|
static const boo::SystemChar* booArgv[32] = {};
|
||||||
booArgv[0] = selfPath;
|
booArgv[0] = selfPath;
|
||||||
for (int i=0 ; i<argc ; ++i)
|
for (int i = 0; i < argc; ++i)
|
||||||
booArgv[i+1] = argv[i];
|
booArgv[i + 1] = argv[i];
|
||||||
|
|
||||||
logvisor::CreateWin32Console();
|
logvisor::CreateWin32Console();
|
||||||
SetConsoleOutputCP(65001);
|
SetConsoleOutputCP(65001);
|
||||||
return wmain(argc + 1, booArgv);
|
return wmain(argc + 1, booArgv);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,113 +5,103 @@
|
||||||
#include "AudioGroupSampleDirectory.hpp"
|
#include "AudioGroupSampleDirectory.hpp"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class AudioGroupData;
|
class AudioGroupData;
|
||||||
class ProjectDatabase;
|
class ProjectDatabase;
|
||||||
|
|
||||||
/** Runtime audio group index container */
|
/** Runtime audio group index container */
|
||||||
class AudioGroup
|
class AudioGroup {
|
||||||
{
|
friend class AudioGroupSampleDirectory;
|
||||||
friend class AudioGroupSampleDirectory;
|
|
||||||
protected:
|
protected:
|
||||||
AudioGroupProject m_proj;
|
AudioGroupProject m_proj;
|
||||||
AudioGroupPool m_pool;
|
AudioGroupPool m_pool;
|
||||||
AudioGroupSampleDirectory m_sdir;
|
AudioGroupSampleDirectory m_sdir;
|
||||||
const unsigned char* m_samp = nullptr;
|
const unsigned char* m_samp = nullptr;
|
||||||
SystemString m_groupPath; /* Typically only set by editor */
|
SystemString m_groupPath; /* Typically only set by editor */
|
||||||
bool m_valid;
|
bool m_valid;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SystemString getSampleBasePath(SampleId sfxId) const;
|
SystemString getSampleBasePath(SampleId sfxId) const;
|
||||||
operator bool() const { return m_valid; }
|
operator bool() const { return m_valid; }
|
||||||
AudioGroup() = default;
|
AudioGroup() = default;
|
||||||
explicit AudioGroup(const AudioGroupData& data) { assign(data); }
|
explicit AudioGroup(const AudioGroupData& data) { assign(data); }
|
||||||
explicit AudioGroup(SystemStringView groupPath) { assign(groupPath); }
|
explicit AudioGroup(SystemStringView groupPath) { assign(groupPath); }
|
||||||
explicit AudioGroup(const AudioGroup& data, SystemStringView groupPath) { assign(data, groupPath); }
|
explicit AudioGroup(const AudioGroup& data, SystemStringView groupPath) { assign(data, groupPath); }
|
||||||
|
|
||||||
void assign(const AudioGroupData& data);
|
void assign(const AudioGroupData& data);
|
||||||
void assign(SystemStringView groupPath);
|
void assign(SystemStringView groupPath);
|
||||||
void assign(const AudioGroup& data, SystemStringView groupPath);
|
void assign(const AudioGroup& data, SystemStringView groupPath);
|
||||||
void setGroupPath(SystemStringView groupPath) { m_groupPath = groupPath; }
|
void setGroupPath(SystemStringView groupPath) { m_groupPath = groupPath; }
|
||||||
|
|
||||||
const SampleEntry* getSample(SampleId sfxId) const;
|
const SampleEntry* getSample(SampleId sfxId) const;
|
||||||
std::pair<ObjToken<SampleEntryData>, const unsigned char*>
|
std::pair<ObjToken<SampleEntryData>, const unsigned char*> getSampleData(SampleId sfxId,
|
||||||
getSampleData(SampleId sfxId, const SampleEntry* sample) const;
|
const SampleEntry* sample) const;
|
||||||
SampleFileState getSampleFileState(SampleId sfxId,
|
SampleFileState getSampleFileState(SampleId sfxId, const SampleEntry* sample, SystemString* pathOut = nullptr) const;
|
||||||
const SampleEntry* sample, SystemString* pathOut = nullptr) const;
|
void patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const;
|
||||||
void patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const;
|
void makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const;
|
||||||
void makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const;
|
void makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const;
|
||||||
void makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const;
|
const AudioGroupProject& getProj() const { return m_proj; }
|
||||||
const AudioGroupProject& getProj() const { return m_proj; }
|
const AudioGroupPool& getPool() const { return m_pool; }
|
||||||
const AudioGroupPool& getPool() const { return m_pool; }
|
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
|
||||||
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
|
AudioGroupProject& getProj() { return m_proj; }
|
||||||
AudioGroupProject& getProj() { return m_proj; }
|
AudioGroupPool& getPool() { return m_pool; }
|
||||||
AudioGroupPool& getPool() { return m_pool; }
|
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
|
||||||
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
|
|
||||||
|
|
||||||
virtual void setIdDatabases() const {}
|
virtual void setIdDatabases() const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioGroupDatabase final : public AudioGroup
|
class AudioGroupDatabase final : public AudioGroup {
|
||||||
{
|
NameDB m_soundMacroDb;
|
||||||
NameDB m_soundMacroDb;
|
NameDB m_sampleDb;
|
||||||
NameDB m_sampleDb;
|
NameDB m_tableDb;
|
||||||
NameDB m_tableDb;
|
NameDB m_keymapDb;
|
||||||
NameDB m_keymapDb;
|
NameDB m_layersDb;
|
||||||
NameDB m_layersDb;
|
|
||||||
|
|
||||||
void _recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
|
void _recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
|
||||||
std::unordered_set<SoundMacroId>& renamedIds);
|
std::unordered_set<SoundMacroId>& renamedIds);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioGroupDatabase() = default;
|
AudioGroupDatabase() = default;
|
||||||
explicit AudioGroupDatabase(const AudioGroupData& data)
|
explicit AudioGroupDatabase(const AudioGroupData& data) {
|
||||||
{
|
setIdDatabases();
|
||||||
setIdDatabases();
|
assign(data);
|
||||||
assign(data);
|
}
|
||||||
}
|
explicit AudioGroupDatabase(SystemStringView groupPath) {
|
||||||
explicit AudioGroupDatabase(SystemStringView groupPath)
|
setIdDatabases();
|
||||||
{
|
assign(groupPath);
|
||||||
setIdDatabases();
|
}
|
||||||
assign(groupPath);
|
explicit AudioGroupDatabase(const AudioGroupDatabase& data, SystemStringView groupPath) {
|
||||||
}
|
setIdDatabases();
|
||||||
explicit AudioGroupDatabase(const AudioGroupDatabase& data, SystemStringView groupPath)
|
assign(data, groupPath);
|
||||||
{
|
}
|
||||||
setIdDatabases();
|
|
||||||
assign(data, groupPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setIdDatabases() const
|
void setIdDatabases() const {
|
||||||
{
|
SoundMacroId::CurNameDB = const_cast<NameDB*>(&m_soundMacroDb);
|
||||||
SoundMacroId::CurNameDB = const_cast<NameDB*>(&m_soundMacroDb);
|
SampleId::CurNameDB = const_cast<NameDB*>(&m_sampleDb);
|
||||||
SampleId::CurNameDB = const_cast<NameDB*>(&m_sampleDb);
|
TableId::CurNameDB = const_cast<NameDB*>(&m_tableDb);
|
||||||
TableId::CurNameDB = const_cast<NameDB*>(&m_tableDb);
|
KeymapId::CurNameDB = const_cast<NameDB*>(&m_keymapDb);
|
||||||
KeymapId::CurNameDB = const_cast<NameDB*>(&m_keymapDb);
|
LayersId::CurNameDB = const_cast<NameDB*>(&m_layersDb);
|
||||||
LayersId::CurNameDB = const_cast<NameDB*>(&m_layersDb);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void renameSample(SampleId id, std::string_view str);
|
void renameSample(SampleId id, std::string_view str);
|
||||||
void deleteSample(SampleId id);
|
void deleteSample(SampleId id);
|
||||||
void copySampleInto(const SystemString& basePath, const SystemString& newBasePath);
|
void copySampleInto(const SystemString& basePath, const SystemString& newBasePath);
|
||||||
|
|
||||||
void importCHeader(std::string_view header);
|
void importCHeader(std::string_view header);
|
||||||
std::string exportCHeader(std::string_view projectName, std::string_view groupName) const;
|
std::string exportCHeader(std::string_view projectName, std::string_view groupName) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProjectDatabase
|
class ProjectDatabase {
|
||||||
{
|
NameDB m_songDb;
|
||||||
NameDB m_songDb;
|
NameDB m_sfxDb;
|
||||||
NameDB m_sfxDb;
|
NameDB m_groupDb;
|
||||||
NameDB m_groupDb;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setIdDatabases() const
|
void setIdDatabases() const {
|
||||||
{
|
SongId::CurNameDB = const_cast<NameDB*>(&m_songDb);
|
||||||
SongId::CurNameDB = const_cast<NameDB*>(&m_songDb);
|
SFXId::CurNameDB = const_cast<NameDB*>(&m_sfxDb);
|
||||||
SFXId::CurNameDB = const_cast<NameDB*>(&m_sfxDb);
|
GroupId::CurNameDB = const_cast<NameDB*>(&m_groupDb);
|
||||||
GroupId::CurNameDB = const_cast<NameDB*>(&m_groupDb);
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -2,123 +2,111 @@
|
||||||
|
|
||||||
#include "Common.hpp"
|
#include "Common.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
/** Simple pointer-container of the four Audio Group chunks */
|
/** Simple pointer-container of the four Audio Group chunks */
|
||||||
class AudioGroupData
|
class AudioGroupData {
|
||||||
{
|
friend class Engine;
|
||||||
friend class Engine;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned char* m_proj;
|
unsigned char* m_proj;
|
||||||
size_t m_projSz;
|
size_t m_projSz;
|
||||||
unsigned char* m_pool;
|
unsigned char* m_pool;
|
||||||
size_t m_poolSz;
|
size_t m_poolSz;
|
||||||
unsigned char* m_sdir;
|
unsigned char* m_sdir;
|
||||||
size_t m_sdirSz;
|
size_t m_sdirSz;
|
||||||
unsigned char* m_samp;
|
unsigned char* m_samp;
|
||||||
size_t m_sampSz;
|
size_t m_sampSz;
|
||||||
|
|
||||||
DataFormat m_fmt;
|
DataFormat m_fmt;
|
||||||
bool m_absOffs;
|
bool m_absOffs;
|
||||||
|
|
||||||
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
||||||
size_t sdirSz, unsigned char* samp, size_t sampSz, DataFormat fmt, bool absOffs)
|
size_t sdirSz, unsigned char* samp, size_t sampSz, DataFormat fmt, bool absOffs)
|
||||||
: m_proj(proj)
|
: m_proj(proj)
|
||||||
, m_projSz(projSz)
|
, m_projSz(projSz)
|
||||||
, m_pool(pool)
|
, m_pool(pool)
|
||||||
, m_poolSz(poolSz)
|
, m_poolSz(poolSz)
|
||||||
, m_sdir(sdir)
|
, m_sdir(sdir)
|
||||||
, m_sdirSz(sdirSz)
|
, m_sdirSz(sdirSz)
|
||||||
, m_samp(samp)
|
, m_samp(samp)
|
||||||
, m_sampSz(sampSz)
|
, m_sampSz(sampSz)
|
||||||
, m_fmt(fmt)
|
, m_fmt(fmt)
|
||||||
, m_absOffs(absOffs)
|
, m_absOffs(absOffs) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
||||||
size_t sdirSz, unsigned char* samp, size_t sampSz, GCNDataTag)
|
size_t sdirSz, unsigned char* samp, size_t sampSz, GCNDataTag)
|
||||||
: m_proj(proj)
|
: m_proj(proj)
|
||||||
, m_projSz(projSz)
|
, m_projSz(projSz)
|
||||||
, m_pool(pool)
|
, m_pool(pool)
|
||||||
, m_poolSz(poolSz)
|
, m_poolSz(poolSz)
|
||||||
, m_sdir(sdir)
|
, m_sdir(sdir)
|
||||||
, m_sdirSz(sdirSz)
|
, m_sdirSz(sdirSz)
|
||||||
, m_samp(samp)
|
, m_samp(samp)
|
||||||
, m_sampSz(sampSz)
|
, m_sampSz(sampSz)
|
||||||
, m_fmt(DataFormat::GCN)
|
, m_fmt(DataFormat::GCN)
|
||||||
, m_absOffs(true)
|
, m_absOffs(true) {}
|
||||||
{
|
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
||||||
}
|
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, N64DataTag)
|
||||||
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
: m_proj(proj)
|
||||||
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, N64DataTag)
|
, m_projSz(projSz)
|
||||||
: m_proj(proj)
|
, m_pool(pool)
|
||||||
, m_projSz(projSz)
|
, m_poolSz(poolSz)
|
||||||
, m_pool(pool)
|
, m_sdir(sdir)
|
||||||
, m_poolSz(poolSz)
|
, m_sdirSz(sdirSz)
|
||||||
, m_sdir(sdir)
|
, m_samp(samp)
|
||||||
, m_sdirSz(sdirSz)
|
, m_sampSz(sampSz)
|
||||||
, m_samp(samp)
|
, m_fmt(DataFormat::N64)
|
||||||
, m_sampSz(sampSz)
|
, m_absOffs(absOffs) {}
|
||||||
, m_fmt(DataFormat::N64)
|
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
||||||
, m_absOffs(absOffs)
|
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, PCDataTag)
|
||||||
{
|
: m_proj(proj)
|
||||||
}
|
, m_projSz(projSz)
|
||||||
AudioGroupData(unsigned char* proj, size_t projSz, unsigned char* pool, size_t poolSz, unsigned char* sdir,
|
, m_pool(pool)
|
||||||
size_t sdirSz, unsigned char* samp, size_t sampSz, bool absOffs, PCDataTag)
|
, m_poolSz(poolSz)
|
||||||
: m_proj(proj)
|
, m_sdir(sdir)
|
||||||
, m_projSz(projSz)
|
, m_sdirSz(sdirSz)
|
||||||
, m_pool(pool)
|
, m_samp(samp)
|
||||||
, m_poolSz(poolSz)
|
, m_sampSz(sampSz)
|
||||||
, m_sdir(sdir)
|
, m_fmt(DataFormat::PC)
|
||||||
, m_sdirSz(sdirSz)
|
, m_absOffs(absOffs) {}
|
||||||
, m_samp(samp)
|
|
||||||
, m_sampSz(sampSz)
|
|
||||||
, m_fmt(DataFormat::PC)
|
|
||||||
, m_absOffs(absOffs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned char* getProj() const { return m_proj; }
|
const unsigned char* getProj() const { return m_proj; }
|
||||||
const unsigned char* getPool() const { return m_pool; }
|
const unsigned char* getPool() const { return m_pool; }
|
||||||
const unsigned char* getSdir() const { return m_sdir; }
|
const unsigned char* getSdir() const { return m_sdir; }
|
||||||
const unsigned char* getSamp() const { return m_samp; }
|
const unsigned char* getSamp() const { return m_samp; }
|
||||||
|
|
||||||
unsigned char* getProj() { return m_proj; }
|
unsigned char* getProj() { return m_proj; }
|
||||||
unsigned char* getPool() { return m_pool; }
|
unsigned char* getPool() { return m_pool; }
|
||||||
unsigned char* getSdir() { return m_sdir; }
|
unsigned char* getSdir() { return m_sdir; }
|
||||||
unsigned char* getSamp() { return m_samp; }
|
unsigned char* getSamp() { return m_samp; }
|
||||||
|
|
||||||
size_t getProjSize() const { return m_projSz; }
|
size_t getProjSize() const { return m_projSz; }
|
||||||
size_t getPoolSize() const { return m_poolSz; }
|
size_t getPoolSize() const { return m_poolSz; }
|
||||||
size_t getSdirSize() const { return m_sdirSz; }
|
size_t getSdirSize() const { return m_sdirSz; }
|
||||||
size_t getSampSize() const { return m_sampSz; }
|
size_t getSampSize() const { return m_sampSz; }
|
||||||
|
|
||||||
operator bool() const { return m_proj != nullptr && m_pool != nullptr && m_sdir != nullptr && m_samp != nullptr; }
|
operator bool() const { return m_proj != nullptr && m_pool != nullptr && m_sdir != nullptr && m_samp != nullptr; }
|
||||||
|
|
||||||
DataFormat getDataFormat() const { return m_fmt; }
|
DataFormat getDataFormat() const { return m_fmt; }
|
||||||
bool getAbsoluteProjOffsets() const { return m_absOffs; }
|
bool getAbsoluteProjOffsets() const { return m_absOffs; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A buffer-owning version of AudioGroupData */
|
/** A buffer-owning version of AudioGroupData */
|
||||||
class IntrusiveAudioGroupData : public AudioGroupData
|
class IntrusiveAudioGroupData : public AudioGroupData {
|
||||||
{
|
bool m_owns = true;
|
||||||
bool m_owns = true;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using AudioGroupData::AudioGroupData;
|
using AudioGroupData::AudioGroupData;
|
||||||
~IntrusiveAudioGroupData();
|
~IntrusiveAudioGroupData();
|
||||||
|
|
||||||
IntrusiveAudioGroupData(const IntrusiveAudioGroupData&) = delete;
|
IntrusiveAudioGroupData(const IntrusiveAudioGroupData&) = delete;
|
||||||
IntrusiveAudioGroupData& operator=(const IntrusiveAudioGroupData&) = delete;
|
IntrusiveAudioGroupData& operator=(const IntrusiveAudioGroupData&) = delete;
|
||||||
|
|
||||||
IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept;
|
IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept;
|
||||||
IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other) noexcept;
|
IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other) noexcept;
|
||||||
|
|
||||||
void dangleOwnership() { m_owns = false; }
|
void dangleOwnership() { m_owns = false; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,221 +7,205 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class AudioGroupData;
|
class AudioGroupData;
|
||||||
class AudioGroupPool;
|
class AudioGroupPool;
|
||||||
class AudioGroupSampleDirectory;
|
class AudioGroupSampleDirectory;
|
||||||
|
|
||||||
enum class GroupType : atUint16
|
enum class GroupType : atUint16 { Song, SFX };
|
||||||
{
|
|
||||||
Song,
|
|
||||||
SFX
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Header at top of project file */
|
/** Header at top of project file */
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) GroupHeader : BigDNA {
|
||||||
GroupHeader : BigDNA
|
AT_DECL_DNA
|
||||||
{
|
Value<atUint32, DNAEn> groupEndOff;
|
||||||
AT_DECL_DNA
|
GroupIdDNA<DNAEn> groupId;
|
||||||
Value<atUint32, DNAEn> groupEndOff;
|
Value<GroupType, DNAEn> type;
|
||||||
GroupIdDNA<DNAEn> groupId;
|
Value<atUint32, DNAEn> soundMacroIdsOff;
|
||||||
Value<GroupType, DNAEn> type;
|
Value<atUint32, DNAEn> samplIdsOff;
|
||||||
Value<atUint32, DNAEn> soundMacroIdsOff;
|
Value<atUint32, DNAEn> tableIdsOff;
|
||||||
Value<atUint32, DNAEn> samplIdsOff;
|
Value<atUint32, DNAEn> keymapIdsOff;
|
||||||
Value<atUint32, DNAEn> tableIdsOff;
|
Value<atUint32, DNAEn> layerIdsOff;
|
||||||
Value<atUint32, DNAEn> keymapIdsOff;
|
Value<atUint32, DNAEn> pageTableOff;
|
||||||
Value<atUint32, DNAEn> layerIdsOff;
|
Value<atUint32, DNAEn> drumTableOff;
|
||||||
Value<atUint32, DNAEn> pageTableOff;
|
Value<atUint32, DNAEn> midiSetupsOff;
|
||||||
Value<atUint32, DNAEn> drumTableOff;
|
|
||||||
Value<atUint32, DNAEn> midiSetupsOff;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Common index members of SongGroups and SFXGroups */
|
/** Common index members of SongGroups and SFXGroups */
|
||||||
struct AudioGroupIndex {};
|
struct AudioGroupIndex {};
|
||||||
|
|
||||||
/** Root index of SongGroup */
|
/** Root index of SongGroup */
|
||||||
struct SongGroupIndex : AudioGroupIndex
|
struct SongGroupIndex : AudioGroupIndex {
|
||||||
{
|
/** Maps GM program numbers to sound entities */
|
||||||
/** Maps GM program numbers to sound entities */
|
template <athena::Endian DNAEn>
|
||||||
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) PageEntryDNA : BigDNA {
|
||||||
|
AT_DECL_DNA_YAML
|
||||||
|
PageObjectIdDNA<DNAEn> objId;
|
||||||
|
Value<atUint8> priority;
|
||||||
|
Value<atUint8> maxVoices;
|
||||||
|
Value<atUint8> programNo;
|
||||||
|
Seek<1, athena::Current> pad;
|
||||||
|
};
|
||||||
|
template <athena::Endian DNAEn>
|
||||||
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1PageEntryDNA : BigDNA {
|
||||||
|
AT_DECL_DNA
|
||||||
|
PageObjectIdDNA<DNAEn> objId;
|
||||||
|
Value<atUint8> priority;
|
||||||
|
Value<atUint8> maxVoices;
|
||||||
|
Value<atUint8> unk;
|
||||||
|
Value<atUint8> programNo;
|
||||||
|
Seek<2, athena::Current> pad;
|
||||||
|
};
|
||||||
|
struct PageEntry : BigDNA {
|
||||||
|
AT_DECL_DNA_YAML
|
||||||
|
PageObjectIdDNA<athena::Big> objId;
|
||||||
|
Value<atUint8> priority = 0;
|
||||||
|
Value<atUint8> maxVoices = 255;
|
||||||
|
|
||||||
|
PageEntry() = default;
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
PageEntry(const PageEntryDNA<DNAE>& in) : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
PageEntry(const MusyX1PageEntryDNA<DNAE>& in)
|
||||||
|
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
PageEntryDNA<DNAEn> toDNA(uint8_t programNo) const {
|
||||||
PageEntryDNA : BigDNA
|
PageEntryDNA<DNAEn> ret;
|
||||||
{
|
ret.objId = objId;
|
||||||
AT_DECL_DNA_YAML
|
ret.priority = priority;
|
||||||
PageObjectIdDNA<DNAEn> objId;
|
ret.maxVoices = maxVoices;
|
||||||
Value<atUint8> priority;
|
ret.programNo = programNo;
|
||||||
Value<atUint8> maxVoices;
|
return ret;
|
||||||
Value<atUint8> programNo;
|
}
|
||||||
Seek<1, athena::Current> pad;
|
};
|
||||||
};
|
std::unordered_map<uint8_t, PageEntry> m_normPages;
|
||||||
template <athena::Endian DNAEn>
|
std::unordered_map<uint8_t, PageEntry> m_drumPages;
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
|
||||||
MusyX1PageEntryDNA : BigDNA
|
|
||||||
{
|
|
||||||
AT_DECL_DNA
|
|
||||||
PageObjectIdDNA<DNAEn> objId;
|
|
||||||
Value<atUint8> priority;
|
|
||||||
Value<atUint8> maxVoices;
|
|
||||||
Value<atUint8> unk;
|
|
||||||
Value<atUint8> programNo;
|
|
||||||
Seek<2, athena::Current> pad;
|
|
||||||
};
|
|
||||||
struct PageEntry : BigDNA
|
|
||||||
{
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
PageObjectIdDNA<athena::Big> objId;
|
|
||||||
Value<atUint8> priority = 0;
|
|
||||||
Value<atUint8> maxVoices = 255;
|
|
||||||
|
|
||||||
PageEntry() = default;
|
/** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */
|
||||||
|
struct MusyX1MIDISetup : BigDNA {
|
||||||
|
AT_DECL_DNA_YAML
|
||||||
|
Value<atUint8> programNo;
|
||||||
|
Value<atUint8> volume;
|
||||||
|
Value<atUint8> panning;
|
||||||
|
Value<atUint8> reverb;
|
||||||
|
Value<atUint8> chorus;
|
||||||
|
Seek<3, athena::Current> pad;
|
||||||
|
};
|
||||||
|
struct MIDISetup : BigDNA {
|
||||||
|
AT_DECL_DNA_YAML
|
||||||
|
Value<atUint8> programNo = 0;
|
||||||
|
Value<atUint8> volume = 127;
|
||||||
|
Value<atUint8> panning = 64;
|
||||||
|
Value<atUint8> reverb = 0;
|
||||||
|
Value<atUint8> chorus = 0;
|
||||||
|
MIDISetup() = default;
|
||||||
|
MIDISetup(const MusyX1MIDISetup& setup)
|
||||||
|
: programNo(setup.programNo)
|
||||||
|
, volume(setup.volume)
|
||||||
|
, panning(setup.panning)
|
||||||
|
, reverb(setup.reverb)
|
||||||
|
, chorus(setup.chorus) {}
|
||||||
|
};
|
||||||
|
std::unordered_map<SongId, std::array<MIDISetup, 16>> m_midiSetups;
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
void toYAML(athena::io::YAMLDocWriter& w) const;
|
||||||
PageEntry(const PageEntryDNA<DNAE>& in)
|
void fromYAML(athena::io::YAMLDocReader& r);
|
||||||
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
PageEntry(const MusyX1PageEntryDNA<DNAE>& in)
|
|
||||||
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
|
||||||
PageEntryDNA<DNAEn> toDNA(uint8_t programNo) const
|
|
||||||
{
|
|
||||||
PageEntryDNA<DNAEn> ret;
|
|
||||||
ret.objId = objId;
|
|
||||||
ret.priority = priority;
|
|
||||||
ret.maxVoices = maxVoices;
|
|
||||||
ret.programNo = programNo;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::unordered_map<uint8_t, PageEntry> m_normPages;
|
|
||||||
std::unordered_map<uint8_t, PageEntry> m_drumPages;
|
|
||||||
|
|
||||||
/** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */
|
|
||||||
struct MusyX1MIDISetup : BigDNA
|
|
||||||
{
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint8> programNo;
|
|
||||||
Value<atUint8> volume;
|
|
||||||
Value<atUint8> panning;
|
|
||||||
Value<atUint8> reverb;
|
|
||||||
Value<atUint8> chorus;
|
|
||||||
Seek<3, athena::Current> pad;
|
|
||||||
};
|
|
||||||
struct MIDISetup : BigDNA
|
|
||||||
{
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
Value<atUint8> programNo = 0;
|
|
||||||
Value<atUint8> volume = 127;
|
|
||||||
Value<atUint8> panning = 64;
|
|
||||||
Value<atUint8> reverb = 0;
|
|
||||||
Value<atUint8> chorus = 0;
|
|
||||||
MIDISetup() = default;
|
|
||||||
MIDISetup(const MusyX1MIDISetup& setup)
|
|
||||||
: programNo(setup.programNo), volume(setup.volume), panning(setup.panning),
|
|
||||||
reverb(setup.reverb), chorus(setup.chorus) {}
|
|
||||||
};
|
|
||||||
std::unordered_map<SongId, std::array<MIDISetup, 16>> m_midiSetups;
|
|
||||||
|
|
||||||
void toYAML(athena::io::YAMLDocWriter& w) const;
|
|
||||||
void fromYAML(athena::io::YAMLDocReader& r);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Root index of SFXGroup */
|
/** Root index of SFXGroup */
|
||||||
struct SFXGroupIndex : AudioGroupIndex
|
struct SFXGroupIndex : AudioGroupIndex {
|
||||||
{
|
/** Maps game-side SFX define IDs to sound entities */
|
||||||
/** Maps game-side SFX define IDs to sound entities */
|
template <athena::Endian DNAEn>
|
||||||
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) SFXEntryDNA : BigDNA {
|
||||||
|
AT_DECL_DNA
|
||||||
|
SFXIdDNA<DNAEn> sfxId;
|
||||||
|
PageObjectIdDNA<DNAEn> objId;
|
||||||
|
Value<atUint8> priority;
|
||||||
|
Value<atUint8> maxVoices;
|
||||||
|
Value<atUint8> defVel;
|
||||||
|
Value<atUint8> panning;
|
||||||
|
Value<atUint8> defKey;
|
||||||
|
Seek<1, athena::Current> pad;
|
||||||
|
};
|
||||||
|
struct SFXEntry : BigDNA {
|
||||||
|
AT_DECL_DNA_YAML
|
||||||
|
PageObjectIdDNA<athena::Big> objId;
|
||||||
|
Value<atUint8> priority = 0;
|
||||||
|
Value<atUint8> maxVoices = 255;
|
||||||
|
Value<atUint8> defVel = 127;
|
||||||
|
Value<atUint8> panning = 64;
|
||||||
|
Value<atUint8> defKey = 60;
|
||||||
|
|
||||||
|
SFXEntry() = default;
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
SFXEntry(const SFXEntryDNA<DNAE>& in)
|
||||||
|
: objId(in.objId.id)
|
||||||
|
, priority(in.priority)
|
||||||
|
, maxVoices(in.maxVoices)
|
||||||
|
, defVel(in.defVel)
|
||||||
|
, panning(in.panning)
|
||||||
|
, defKey(in.defKey) {}
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
SFXEntryDNA<DNAEn> toDNA(SFXId id) const {
|
||||||
SFXEntryDNA : BigDNA
|
SFXEntryDNA<DNAEn> ret;
|
||||||
{
|
ret.sfxId.id = id;
|
||||||
AT_DECL_DNA
|
ret.objId = objId;
|
||||||
SFXIdDNA<DNAEn> sfxId;
|
ret.priority = priority;
|
||||||
PageObjectIdDNA<DNAEn> objId;
|
ret.maxVoices = maxVoices;
|
||||||
Value<atUint8> priority;
|
ret.defVel = defVel;
|
||||||
Value<atUint8> maxVoices;
|
ret.panning = panning;
|
||||||
Value<atUint8> defVel;
|
ret.defKey = defKey;
|
||||||
Value<atUint8> panning;
|
return ret;
|
||||||
Value<atUint8> defKey;
|
}
|
||||||
Seek<1, athena::Current> pad;
|
};
|
||||||
};
|
std::unordered_map<SFXId, SFXEntry> m_sfxEntries;
|
||||||
struct SFXEntry : BigDNA
|
|
||||||
{
|
|
||||||
AT_DECL_DNA_YAML
|
|
||||||
PageObjectIdDNA<athena::Big> objId;
|
|
||||||
Value<atUint8> priority = 0;
|
|
||||||
Value<atUint8> maxVoices = 255;
|
|
||||||
Value<atUint8> defVel = 127;
|
|
||||||
Value<atUint8> panning = 64;
|
|
||||||
Value<atUint8> defKey = 60;
|
|
||||||
|
|
||||||
SFXEntry() = default;
|
SFXGroupIndex() = default;
|
||||||
|
SFXGroupIndex(const SFXGroupIndex& other);
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
void toYAML(athena::io::YAMLDocWriter& w) const;
|
||||||
SFXEntry(const SFXEntryDNA<DNAE>& in)
|
void fromYAML(athena::io::YAMLDocReader& r);
|
||||||
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices),
|
|
||||||
defVel(in.defVel), panning(in.panning), defKey(in.defKey) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
|
||||||
SFXEntryDNA<DNAEn> toDNA(SFXId id) const
|
|
||||||
{
|
|
||||||
SFXEntryDNA<DNAEn> ret;
|
|
||||||
ret.sfxId.id = id;
|
|
||||||
ret.objId = objId;
|
|
||||||
ret.priority = priority;
|
|
||||||
ret.maxVoices = maxVoices;
|
|
||||||
ret.defVel = defVel;
|
|
||||||
ret.panning = panning;
|
|
||||||
ret.defKey = defKey;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::unordered_map<SFXId, SFXEntry> m_sfxEntries;
|
|
||||||
|
|
||||||
SFXGroupIndex() = default;
|
|
||||||
SFXGroupIndex(const SFXGroupIndex& other);
|
|
||||||
|
|
||||||
void toYAML(athena::io::YAMLDocWriter& w) const;
|
|
||||||
void fromYAML(athena::io::YAMLDocReader& r);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Collection of SongGroup and SFXGroup indexes */
|
/** Collection of SongGroup and SFXGroup indexes */
|
||||||
class AudioGroupProject
|
class AudioGroupProject {
|
||||||
{
|
std::unordered_map<GroupId, ObjToken<SongGroupIndex>> m_songGroups;
|
||||||
std::unordered_map<GroupId, ObjToken<SongGroupIndex>> m_songGroups;
|
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>> m_sfxGroups;
|
||||||
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>> m_sfxGroups;
|
|
||||||
|
|
||||||
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
|
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
|
||||||
template <athena::Endian DNAE>
|
template <athena::Endian DNAE>
|
||||||
static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
|
static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
|
||||||
|
|
||||||
|
static void BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag);
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs);
|
||||||
|
|
||||||
static void BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag);
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs);
|
|
||||||
public:
|
public:
|
||||||
AudioGroupProject() = default;
|
AudioGroupProject() = default;
|
||||||
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
|
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
|
||||||
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
|
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
|
||||||
static AudioGroupProject CreateAudioGroupProject(const AudioGroupProject& oldProj);
|
static AudioGroupProject CreateAudioGroupProject(const AudioGroupProject& oldProj);
|
||||||
static void BootstrapObjectIDs(const AudioGroupData& data);
|
static void BootstrapObjectIDs(const AudioGroupData& data);
|
||||||
|
|
||||||
const SongGroupIndex* getSongGroupIndex(int groupId) const;
|
const SongGroupIndex* getSongGroupIndex(int groupId) const;
|
||||||
const SFXGroupIndex* getSFXGroupIndex(int groupId) const;
|
const SFXGroupIndex* getSFXGroupIndex(int groupId) const;
|
||||||
|
|
||||||
const std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() const { return m_songGroups; }
|
const std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() const { return m_songGroups; }
|
||||||
const std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() const { return m_sfxGroups; }
|
const std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() const { return m_sfxGroups; }
|
||||||
std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() { return m_songGroups; }
|
std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() { return m_songGroups; }
|
||||||
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
|
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
|
||||||
|
|
||||||
std::vector<uint8_t> toYAML() const;
|
std::vector<uint8_t> toYAML() const;
|
||||||
std::vector<uint8_t> toGCNData(const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const;
|
std::vector<uint8_t> toGCNData(const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const;
|
||||||
|
|
||||||
AudioGroupProject(const AudioGroupProject&) = delete;
|
AudioGroupProject(const AudioGroupProject&) = delete;
|
||||||
AudioGroupProject& operator=(const AudioGroupProject&) = delete;
|
AudioGroupProject& operator=(const AudioGroupProject&) = delete;
|
||||||
AudioGroupProject(AudioGroupProject&&) = default;
|
AudioGroupProject(AudioGroupProject&&) = default;
|
||||||
AudioGroupProject& operator=(AudioGroupProject&&) = default;
|
AudioGroupProject& operator=(AudioGroupProject&&) = default;
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -4,381 +4,351 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "Common.hpp"
|
#include "Common.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class AudioGroupData;
|
class AudioGroupData;
|
||||||
class AudioGroupDatabase;
|
class AudioGroupDatabase;
|
||||||
|
|
||||||
struct DSPADPCMHeader : BigDNA
|
struct DSPADPCMHeader : BigDNA {
|
||||||
{
|
AT_DECL_DNA
|
||||||
AT_DECL_DNA
|
Value<atUint32> x0_num_samples;
|
||||||
Value<atUint32> x0_num_samples;
|
Value<atUint32> x4_num_nibbles;
|
||||||
Value<atUint32> x4_num_nibbles;
|
Value<atUint32> x8_sample_rate;
|
||||||
Value<atUint32> x8_sample_rate;
|
Value<atUint16> xc_loop_flag = 0;
|
||||||
Value<atUint16> xc_loop_flag = 0;
|
Value<atUint16> xe_format = 0; /* 0 for ADPCM */
|
||||||
Value<atUint16> xe_format = 0; /* 0 for ADPCM */
|
Value<atUint32> x10_loop_start_nibble = 0;
|
||||||
Value<atUint32> x10_loop_start_nibble = 0;
|
Value<atUint32> x14_loop_end_nibble = 0;
|
||||||
Value<atUint32> x14_loop_end_nibble = 0;
|
Value<atUint32> x18_ca = 0;
|
||||||
Value<atUint32> x18_ca = 0;
|
Value<atInt16> x1c_coef[8][2];
|
||||||
Value<atInt16> x1c_coef[8][2];
|
Value<atInt16> x3c_gain = 0;
|
||||||
Value<atInt16> x3c_gain = 0;
|
Value<atInt16> x3e_ps = 0;
|
||||||
Value<atInt16> x3e_ps = 0;
|
Value<atInt16> x40_hist1 = 0;
|
||||||
Value<atInt16> x40_hist1 = 0;
|
Value<atInt16> x42_hist2 = 0;
|
||||||
Value<atInt16> x42_hist2 = 0;
|
Value<atInt16> x44_loop_ps = 0;
|
||||||
Value<atInt16> x44_loop_ps = 0;
|
Value<atInt16> x46_loop_hist1 = 0;
|
||||||
Value<atInt16> x46_loop_hist1 = 0;
|
Value<atInt16> x48_loop_hist2 = 0;
|
||||||
Value<atInt16> x48_loop_hist2 = 0;
|
Value<atUint8> m_pitch = 0; // Stash this in the padding
|
||||||
Value<atUint8> m_pitch = 0; // Stash this in the padding
|
Seek<21, athena::Current> pad;
|
||||||
Seek<21, athena::Current> pad;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VADPCMHeader : BigDNA
|
struct VADPCMHeader : BigDNA {
|
||||||
{
|
AT_DECL_DNA
|
||||||
AT_DECL_DNA
|
Value<uint32_t> m_pitchSampleRate;
|
||||||
Value<uint32_t> m_pitchSampleRate;
|
Value<uint32_t> m_numSamples;
|
||||||
Value<uint32_t> m_numSamples;
|
Value<uint32_t> m_loopStartSample;
|
||||||
Value<uint32_t> m_loopStartSample;
|
Value<uint32_t> m_loopLengthSamples;
|
||||||
Value<uint32_t> m_loopLengthSamples;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WAVFormatChunk : LittleDNA
|
struct WAVFormatChunk : LittleDNA {
|
||||||
{
|
AT_DECL_DNA
|
||||||
AT_DECL_DNA
|
Value<atUint16> sampleFmt = 1;
|
||||||
Value<atUint16> sampleFmt = 1;
|
Value<atUint16> numChannels = 1;
|
||||||
Value<atUint16> numChannels = 1;
|
Value<atUint32> sampleRate;
|
||||||
Value<atUint32> sampleRate;
|
Value<atUint32> byteRate; // sampleRate * numChannels * bitsPerSample/8
|
||||||
Value<atUint32> byteRate; // sampleRate * numChannels * bitsPerSample/8
|
Value<atUint16> blockAlign = 2; // numChannels * bitsPerSample/8
|
||||||
Value<atUint16> blockAlign = 2; // numChannels * bitsPerSample/8
|
Value<atUint16> bitsPerSample = 16;
|
||||||
Value<atUint16> bitsPerSample = 16;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WAVSampleChunk : LittleDNA
|
struct WAVSampleChunk : LittleDNA {
|
||||||
{
|
AT_DECL_DNA
|
||||||
AT_DECL_DNA
|
Value<atUint32> smplManufacturer = 0;
|
||||||
Value<atUint32> smplManufacturer = 0;
|
Value<atUint32> smplProduct = 0;
|
||||||
Value<atUint32> smplProduct = 0;
|
Value<atUint32> smplPeriod; // 1 / sampleRate in nanoseconds
|
||||||
Value<atUint32> smplPeriod; // 1 / sampleRate in nanoseconds
|
Value<atUint32> midiNote; // native MIDI note of sample
|
||||||
Value<atUint32> midiNote; // native MIDI note of sample
|
Value<atUint32> midiPitchFrac = 0;
|
||||||
Value<atUint32> midiPitchFrac = 0;
|
Value<atUint32> smpteFormat = 0;
|
||||||
Value<atUint32> smpteFormat = 0;
|
Value<atUint32> smpteOffset = 0;
|
||||||
Value<atUint32> smpteOffset = 0;
|
Value<atUint32> numSampleLoops = 0;
|
||||||
Value<atUint32> numSampleLoops = 0;
|
Value<atUint32> additionalDataSize = 0;
|
||||||
Value<atUint32> additionalDataSize = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WAVSampleLoop : LittleDNA
|
struct WAVSampleLoop : LittleDNA {
|
||||||
{
|
AT_DECL_DNA
|
||||||
AT_DECL_DNA
|
Value<atUint32> cuePointId = 0;
|
||||||
Value<atUint32> cuePointId = 0;
|
Value<atUint32> loopType = 0; // 0: forward loop
|
||||||
Value<atUint32> loopType = 0; // 0: forward loop
|
Value<atUint32> start;
|
||||||
Value<atUint32> start;
|
Value<atUint32> end;
|
||||||
Value<atUint32> end;
|
Value<atUint32> fraction = 0;
|
||||||
Value<atUint32> fraction = 0;
|
Value<atUint32> playCount = 0;
|
||||||
Value<atUint32> playCount = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WAVHeader : LittleDNA
|
struct WAVHeader : LittleDNA {
|
||||||
{
|
AT_DECL_DNA
|
||||||
AT_DECL_DNA
|
Value<atUint32> riffMagic = SBIG('RIFF');
|
||||||
Value<atUint32> riffMagic = SBIG('RIFF');
|
Value<atUint32> wavChuckSize; // everything - 8
|
||||||
Value<atUint32> wavChuckSize; // everything - 8
|
Value<atUint32> wavMagic = SBIG('WAVE');
|
||||||
Value<atUint32> wavMagic = SBIG('WAVE');
|
|
||||||
|
|
||||||
Value<atUint32> fmtMagic = SBIG('fmt ');
|
Value<atUint32> fmtMagic = SBIG('fmt ');
|
||||||
Value<atUint32> fmtChunkSize = 16;
|
Value<atUint32> fmtChunkSize = 16;
|
||||||
WAVFormatChunk fmtChunk;
|
WAVFormatChunk fmtChunk;
|
||||||
|
|
||||||
Value<atUint32> smplMagic = SBIG('smpl');
|
Value<atUint32> smplMagic = SBIG('smpl');
|
||||||
Value<atUint32> smplChunkSize = 36; // 36 + numSampleLoops*24
|
Value<atUint32> smplChunkSize = 36; // 36 + numSampleLoops*24
|
||||||
WAVSampleChunk smplChunk;
|
WAVSampleChunk smplChunk;
|
||||||
|
|
||||||
Value<atUint32> dataMagic = SBIG('data');
|
Value<atUint32> dataMagic = SBIG('data');
|
||||||
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
|
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WAVHeaderLoop : LittleDNA
|
struct WAVHeaderLoop : LittleDNA {
|
||||||
{
|
AT_DECL_DNA
|
||||||
AT_DECL_DNA
|
Value<atUint32> riffMagic = SBIG('RIFF');
|
||||||
Value<atUint32> riffMagic = SBIG('RIFF');
|
Value<atUint32> wavChuckSize; // everything - 8
|
||||||
Value<atUint32> wavChuckSize; // everything - 8
|
Value<atUint32> wavMagic = SBIG('WAVE');
|
||||||
Value<atUint32> wavMagic = SBIG('WAVE');
|
|
||||||
|
|
||||||
Value<atUint32> fmtMagic = SBIG('fmt ');
|
Value<atUint32> fmtMagic = SBIG('fmt ');
|
||||||
Value<atUint32> fmtChunkSize = 16;
|
Value<atUint32> fmtChunkSize = 16;
|
||||||
WAVFormatChunk fmtChunk;
|
WAVFormatChunk fmtChunk;
|
||||||
|
|
||||||
Value<atUint32> smplMagic = SBIG('smpl');
|
Value<atUint32> smplMagic = SBIG('smpl');
|
||||||
Value<atUint32> smplChunkSize = 60; // 36 + numSampleLoops*24
|
Value<atUint32> smplChunkSize = 60; // 36 + numSampleLoops*24
|
||||||
WAVSampleChunk smplChunk;
|
WAVSampleChunk smplChunk;
|
||||||
WAVSampleLoop sampleLoop;
|
WAVSampleLoop sampleLoop;
|
||||||
|
|
||||||
Value<atUint32> dataMagic = SBIG('data');
|
Value<atUint32> dataMagic = SBIG('data');
|
||||||
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
|
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SampleFormat : uint8_t
|
enum class SampleFormat : uint8_t {
|
||||||
{
|
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
|
||||||
DSP, /**< GCN DSP-ucode ADPCM (very common for GameCube games) */
|
DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude appropriately)
|
||||||
DSP_DRUM, /**< GCN DSP-ucode ADPCM (seems to be set into drum samples for expanding their amplitude appropriately) */
|
*/
|
||||||
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
|
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
|
||||||
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
|
N64, /**< 2-stage VADPCM coding with SAMP-embedded codebooks */
|
||||||
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
|
PCM_PC /**< Little-endian PCM found in PC Rogue Squadron (actually enum 0 which conflicts with DSP-ADPCM) */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SampleFileState
|
enum class SampleFileState {
|
||||||
{
|
NoData,
|
||||||
NoData,
|
MemoryOnlyWAV,
|
||||||
MemoryOnlyWAV,
|
MemoryOnlyCompressed,
|
||||||
MemoryOnlyCompressed,
|
WAVRecent,
|
||||||
WAVRecent,
|
CompressedRecent,
|
||||||
CompressedRecent,
|
CompressedNoWAV,
|
||||||
CompressedNoWAV,
|
WAVNoCompressed
|
||||||
WAVNoCompressed
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Indexes individual samples in SAMP chunk */
|
/** Indexes individual samples in SAMP chunk */
|
||||||
class AudioGroupSampleDirectory
|
class AudioGroupSampleDirectory {
|
||||||
{
|
friend class AudioGroup;
|
||||||
friend class AudioGroup;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
union ADPCMParms {
|
union ADPCMParms {
|
||||||
struct DSPParms
|
struct DSPParms {
|
||||||
{
|
uint16_t m_bytesPerFrame;
|
||||||
uint16_t m_bytesPerFrame;
|
uint8_t m_ps;
|
||||||
uint8_t m_ps;
|
uint8_t m_lps;
|
||||||
uint8_t m_lps;
|
int16_t m_hist2;
|
||||||
int16_t m_hist2;
|
int16_t m_hist1;
|
||||||
int16_t m_hist1;
|
int16_t m_coefs[8][2];
|
||||||
int16_t m_coefs[8][2];
|
} dsp;
|
||||||
} dsp;
|
struct VADPCMParms {
|
||||||
struct VADPCMParms
|
int16_t m_coefs[8][2][8];
|
||||||
{
|
} vadpcm;
|
||||||
int16_t m_coefs[8][2][8];
|
void swapBigDSP();
|
||||||
} vadpcm;
|
void swapBigVADPCM();
|
||||||
void swapBigDSP();
|
};
|
||||||
void swapBigVADPCM();
|
|
||||||
};
|
template <athena::Endian DNAEn>
|
||||||
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) EntryDNA : BigDNA {
|
||||||
|
AT_DECL_DNA
|
||||||
|
SampleIdDNA<DNAEn> m_sfxId;
|
||||||
|
Seek<2, athena::Current> pad;
|
||||||
|
Value<atUint32, DNAEn> m_sampleOff;
|
||||||
|
Value<atUint32, DNAEn> m_unk;
|
||||||
|
Value<atUint8, DNAEn> m_pitch;
|
||||||
|
Seek<1, athena::Current> pad2;
|
||||||
|
Value<atUint16, DNAEn> m_sampleRate;
|
||||||
|
Value<atUint32, DNAEn> m_numSamples; // Top 8 bits is SampleFormat
|
||||||
|
Value<atUint32, DNAEn> m_loopStartSample;
|
||||||
|
Value<atUint32, DNAEn> m_loopLengthSamples;
|
||||||
|
Value<atUint32, DNAEn> m_adpcmParmOffset;
|
||||||
|
|
||||||
|
void _setLoopStartSample(atUint32 sample) {
|
||||||
|
m_loopLengthSamples += m_loopStartSample - sample;
|
||||||
|
m_loopStartSample = sample;
|
||||||
|
}
|
||||||
|
void setLoopEndSample(atUint32 sample) { m_loopLengthSamples = sample + 1 - m_loopStartSample; }
|
||||||
|
};
|
||||||
|
template <athena::Endian DNAEn>
|
||||||
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1SdirEntry : BigDNA {
|
||||||
|
AT_DECL_DNA
|
||||||
|
SampleIdDNA<DNAEn> m_sfxId;
|
||||||
|
Seek<2, athena::Current> pad;
|
||||||
|
Value<atUint32, DNAEn> m_sampleOff;
|
||||||
|
Value<atUint32, DNAEn> m_pitchSampleRate;
|
||||||
|
Value<atUint32, DNAEn> m_numSamples;
|
||||||
|
Value<atUint32, DNAEn> m_loopStartSample;
|
||||||
|
Value<atUint32, DNAEn> m_loopLengthSamples;
|
||||||
|
};
|
||||||
|
template <athena::Endian DNAEn>
|
||||||
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) MusyX1AbsSdirEntry : BigDNA {
|
||||||
|
AT_DECL_DNA
|
||||||
|
SampleIdDNA<DNAEn> m_sfxId;
|
||||||
|
Seek<2, athena::Current> pad;
|
||||||
|
Value<uint32_t, DNAEn> m_sampleOff;
|
||||||
|
Value<uint32_t, DNAEn> m_unk;
|
||||||
|
Value<uint32_t, DNAEn> m_pitchSampleRate;
|
||||||
|
Value<uint32_t, DNAEn> m_numSamples;
|
||||||
|
Value<uint32_t, DNAEn> m_loopStartSample;
|
||||||
|
Value<uint32_t, DNAEn> m_loopLengthSamples;
|
||||||
|
};
|
||||||
|
struct EntryData {
|
||||||
|
atUint32 m_sampleOff = 0;
|
||||||
|
atUint32 m_unk = 0;
|
||||||
|
atUint8 m_pitch = 0;
|
||||||
|
atUint16 m_sampleRate = 0;
|
||||||
|
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
|
||||||
|
atUint32 m_loopStartSample = 0;
|
||||||
|
atUint32 m_loopLengthSamples = 0;
|
||||||
|
atUint32 m_adpcmParmOffset = 0;
|
||||||
|
|
||||||
|
/* Stored out-of-band in a platform-dependent way */
|
||||||
|
ADPCMParms m_ADPCMParms;
|
||||||
|
|
||||||
|
/* In-memory storage of an individual sample. Editors use this structure
|
||||||
|
* to override the loaded sample with a file-backed version without repacking
|
||||||
|
* the sample data into a SAMP block. */
|
||||||
|
time_t m_looseModTime = 0;
|
||||||
|
std::unique_ptr<uint8_t[]> m_looseData;
|
||||||
|
|
||||||
|
/* Use middle C when pitch is (impossibly low) default */
|
||||||
|
atUint8 getPitch() const { return m_pitch == 0 ? atUint8(60) : m_pitch; }
|
||||||
|
atUint32 getNumSamples() const { return m_numSamples & 0xffffff; }
|
||||||
|
SampleFormat getSampleFormat() const { return SampleFormat(m_numSamples >> 24); }
|
||||||
|
bool isFormatDSP() const {
|
||||||
|
SampleFormat fmt = getSampleFormat();
|
||||||
|
return fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLooped() const { return m_loopLengthSamples != 0 && m_loopStartSample != 0xffffffff; }
|
||||||
|
|
||||||
|
void _setLoopStartSample(atUint32 sample) {
|
||||||
|
m_loopLengthSamples += m_loopStartSample - sample;
|
||||||
|
m_loopStartSample = sample;
|
||||||
|
}
|
||||||
|
void setLoopStartSample(atUint32 sample);
|
||||||
|
atUint32 getLoopStartSample() const { return m_loopStartSample; }
|
||||||
|
void setLoopEndSample(atUint32 sample) { m_loopLengthSamples = sample + 1 - m_loopStartSample; }
|
||||||
|
atUint32 getLoopEndSample() const { return m_loopStartSample + m_loopLengthSamples - 1; }
|
||||||
|
|
||||||
|
EntryData() = default;
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
EntryData(const EntryDNA<DNAE>& in)
|
||||||
|
: m_sampleOff(in.m_sampleOff)
|
||||||
|
, m_unk(in.m_unk)
|
||||||
|
, m_pitch(in.m_pitch)
|
||||||
|
, m_sampleRate(in.m_sampleRate)
|
||||||
|
, m_numSamples(in.m_numSamples)
|
||||||
|
, m_loopStartSample(in.m_loopStartSample)
|
||||||
|
, m_loopLengthSamples(in.m_loopLengthSamples)
|
||||||
|
, m_adpcmParmOffset(in.m_adpcmParmOffset) {}
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
EntryData(const MusyX1SdirEntry<DNAE>& in)
|
||||||
|
: m_sampleOff(in.m_sampleOff)
|
||||||
|
, m_unk(0)
|
||||||
|
, m_pitch(in.m_pitchSampleRate >> 24)
|
||||||
|
, m_sampleRate(in.m_pitchSampleRate & 0xffff)
|
||||||
|
, m_numSamples(in.m_numSamples)
|
||||||
|
, m_loopStartSample(in.m_loopStartSample)
|
||||||
|
, m_loopLengthSamples(in.m_loopLengthSamples)
|
||||||
|
, m_adpcmParmOffset(0) {}
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
EntryData(const MusyX1AbsSdirEntry<DNAE>& in)
|
||||||
|
: m_sampleOff(in.m_sampleOff)
|
||||||
|
, m_unk(in.m_unk)
|
||||||
|
, m_pitch(in.m_pitchSampleRate >> 24)
|
||||||
|
, m_sampleRate(in.m_pitchSampleRate & 0xffff)
|
||||||
|
, m_numSamples(in.m_numSamples)
|
||||||
|
, m_loopStartSample(in.m_loopStartSample)
|
||||||
|
, m_loopLengthSamples(in.m_loopLengthSamples)
|
||||||
|
, m_adpcmParmOffset(0) {}
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
EntryDNA<DNAEn> toDNA(SFXId id) const {
|
||||||
EntryDNA : BigDNA
|
EntryDNA<DNAEn> ret;
|
||||||
{
|
ret.m_sfxId.id = id;
|
||||||
AT_DECL_DNA
|
ret.m_sampleOff = m_sampleOff;
|
||||||
SampleIdDNA<DNAEn> m_sfxId;
|
ret.m_unk = m_unk;
|
||||||
Seek<2, athena::Current> pad;
|
ret.m_pitch = m_pitch;
|
||||||
Value<atUint32, DNAEn> m_sampleOff;
|
ret.m_sampleRate = m_sampleRate;
|
||||||
Value<atUint32, DNAEn> m_unk;
|
ret.m_numSamples = m_numSamples;
|
||||||
Value<atUint8, DNAEn> m_pitch;
|
ret.m_loopStartSample = m_loopStartSample;
|
||||||
Seek<1, athena::Current> pad2;
|
ret.m_loopLengthSamples = m_loopLengthSamples;
|
||||||
Value<atUint16, DNAEn> m_sampleRate;
|
ret.m_adpcmParmOffset = m_adpcmParmOffset;
|
||||||
Value<atUint32, DNAEn> m_numSamples; // Top 8 bits is SampleFormat
|
return ret;
|
||||||
Value<atUint32, DNAEn> m_loopStartSample;
|
}
|
||||||
Value<atUint32, DNAEn> m_loopLengthSamples;
|
|
||||||
Value<atUint32, DNAEn> m_adpcmParmOffset;
|
void loadLooseDSP(SystemStringView dspPath);
|
||||||
|
void loadLooseVADPCM(SystemStringView vadpcmPath);
|
||||||
|
void loadLooseWAV(SystemStringView wavPath);
|
||||||
|
|
||||||
|
void patchMetadataDSP(SystemStringView dspPath);
|
||||||
|
void patchMetadataVADPCM(SystemStringView vadpcmPath);
|
||||||
|
void patchMetadataWAV(SystemStringView wavPath);
|
||||||
|
};
|
||||||
|
/* This double-wrapper allows Voices to keep a strong reference on
|
||||||
|
* a single instance of loaded loose data without being unexpectedly
|
||||||
|
* clobbered */
|
||||||
|
struct Entry {
|
||||||
|
ObjToken<EntryData> m_data;
|
||||||
|
|
||||||
|
Entry() : m_data(MakeObj<EntryData>()) {}
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
Entry(const EntryDNA<DNAE>& in) : m_data(MakeObj<EntryData>(in)) {}
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
Entry(const MusyX1SdirEntry<DNAE>& in) : m_data(MakeObj<EntryData>(in)) {}
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
Entry(const MusyX1AbsSdirEntry<DNAE>& in) : m_data(MakeObj<EntryData>(in)) {}
|
||||||
|
|
||||||
void _setLoopStartSample(atUint32 sample)
|
|
||||||
{
|
|
||||||
m_loopLengthSamples += m_loopStartSample - sample;
|
|
||||||
m_loopStartSample = sample;
|
|
||||||
}
|
|
||||||
void setLoopEndSample(atUint32 sample)
|
|
||||||
{
|
|
||||||
m_loopLengthSamples = sample + 1 - m_loopStartSample;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
EntryDNA<DNAEn> toDNA(SFXId id) const {
|
||||||
MusyX1SdirEntry : BigDNA
|
return m_data->toDNA<DNAEn>(id);
|
||||||
{
|
}
|
||||||
AT_DECL_DNA
|
|
||||||
SampleIdDNA<DNAEn> m_sfxId;
|
|
||||||
Seek<2, athena::Current> pad;
|
|
||||||
Value<atUint32, DNAEn> m_sampleOff;
|
|
||||||
Value<atUint32, DNAEn> m_pitchSampleRate;
|
|
||||||
Value<atUint32, DNAEn> m_numSamples;
|
|
||||||
Value<atUint32, DNAEn> m_loopStartSample;
|
|
||||||
Value<atUint32, DNAEn> m_loopLengthSamples;
|
|
||||||
};
|
|
||||||
template <athena::Endian DNAEn>
|
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
|
||||||
MusyX1AbsSdirEntry : BigDNA
|
|
||||||
{
|
|
||||||
AT_DECL_DNA
|
|
||||||
SampleIdDNA<DNAEn> m_sfxId;
|
|
||||||
Seek<2, athena::Current> pad;
|
|
||||||
Value<uint32_t, DNAEn> m_sampleOff;
|
|
||||||
Value<uint32_t, DNAEn> m_unk;
|
|
||||||
Value<uint32_t, DNAEn> m_pitchSampleRate;
|
|
||||||
Value<uint32_t, DNAEn> m_numSamples;
|
|
||||||
Value<uint32_t, DNAEn> m_loopStartSample;
|
|
||||||
Value<uint32_t, DNAEn> m_loopLengthSamples;
|
|
||||||
};
|
|
||||||
struct EntryData
|
|
||||||
{
|
|
||||||
atUint32 m_sampleOff = 0;
|
|
||||||
atUint32 m_unk = 0;
|
|
||||||
atUint8 m_pitch = 0;
|
|
||||||
atUint16 m_sampleRate = 0;
|
|
||||||
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
|
|
||||||
atUint32 m_loopStartSample = 0;
|
|
||||||
atUint32 m_loopLengthSamples = 0;
|
|
||||||
atUint32 m_adpcmParmOffset = 0;
|
|
||||||
|
|
||||||
/* Stored out-of-band in a platform-dependent way */
|
void loadLooseData(SystemStringView basePath);
|
||||||
ADPCMParms m_ADPCMParms;
|
SampleFileState getFileState(SystemStringView basePath, SystemString* pathOut = nullptr) const;
|
||||||
|
void patchSampleMetadata(SystemStringView basePath) const;
|
||||||
/* In-memory storage of an individual sample. Editors use this structure
|
};
|
||||||
* to override the loaded sample with a file-backed version without repacking
|
|
||||||
* the sample data into a SAMP block. */
|
|
||||||
time_t m_looseModTime = 0;
|
|
||||||
std::unique_ptr<uint8_t[]> m_looseData;
|
|
||||||
|
|
||||||
/* Use middle C when pitch is (impossibly low) default */
|
|
||||||
atUint8 getPitch() const { return m_pitch == 0 ? atUint8(60) : m_pitch; }
|
|
||||||
atUint32 getNumSamples() const { return m_numSamples & 0xffffff; }
|
|
||||||
SampleFormat getSampleFormat() const { return SampleFormat(m_numSamples >> 24); }
|
|
||||||
bool isFormatDSP() const
|
|
||||||
{
|
|
||||||
SampleFormat fmt = getSampleFormat();
|
|
||||||
return fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isLooped() const { return m_loopLengthSamples != 0 && m_loopStartSample != 0xffffffff; }
|
|
||||||
|
|
||||||
void _setLoopStartSample(atUint32 sample)
|
|
||||||
{
|
|
||||||
m_loopLengthSamples += m_loopStartSample - sample;
|
|
||||||
m_loopStartSample = sample;
|
|
||||||
}
|
|
||||||
void setLoopStartSample(atUint32 sample);
|
|
||||||
atUint32 getLoopStartSample() const
|
|
||||||
{
|
|
||||||
return m_loopStartSample;
|
|
||||||
}
|
|
||||||
void setLoopEndSample(atUint32 sample)
|
|
||||||
{
|
|
||||||
m_loopLengthSamples = sample + 1 - m_loopStartSample;
|
|
||||||
}
|
|
||||||
atUint32 getLoopEndSample() const
|
|
||||||
{
|
|
||||||
return m_loopStartSample + m_loopLengthSamples - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntryData() = default;
|
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
EntryData(const EntryDNA<DNAE>& in)
|
|
||||||
: m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitch),
|
|
||||||
m_sampleRate(in.m_sampleRate), m_numSamples(in.m_numSamples),
|
|
||||||
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
|
|
||||||
m_adpcmParmOffset(in.m_adpcmParmOffset) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
EntryData(const MusyX1SdirEntry<DNAE>& in)
|
|
||||||
: m_sampleOff(in.m_sampleOff), m_unk(0), m_pitch(in.m_pitchSampleRate >> 24),
|
|
||||||
m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
|
|
||||||
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
|
|
||||||
m_adpcmParmOffset(0) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
EntryData(const MusyX1AbsSdirEntry<DNAE>& in)
|
|
||||||
: m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitchSampleRate >> 24),
|
|
||||||
m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
|
|
||||||
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
|
|
||||||
m_adpcmParmOffset(0) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
|
||||||
EntryDNA<DNAEn> toDNA(SFXId id) const
|
|
||||||
{
|
|
||||||
EntryDNA<DNAEn> ret;
|
|
||||||
ret.m_sfxId.id = id;
|
|
||||||
ret.m_sampleOff = m_sampleOff;
|
|
||||||
ret.m_unk = m_unk;
|
|
||||||
ret.m_pitch = m_pitch;
|
|
||||||
ret.m_sampleRate = m_sampleRate;
|
|
||||||
ret.m_numSamples = m_numSamples;
|
|
||||||
ret.m_loopStartSample = m_loopStartSample;
|
|
||||||
ret.m_loopLengthSamples = m_loopLengthSamples;
|
|
||||||
ret.m_adpcmParmOffset = m_adpcmParmOffset;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadLooseDSP(SystemStringView dspPath);
|
|
||||||
void loadLooseVADPCM(SystemStringView vadpcmPath);
|
|
||||||
void loadLooseWAV(SystemStringView wavPath);
|
|
||||||
|
|
||||||
void patchMetadataDSP(SystemStringView dspPath);
|
|
||||||
void patchMetadataVADPCM(SystemStringView vadpcmPath);
|
|
||||||
void patchMetadataWAV(SystemStringView wavPath);
|
|
||||||
};
|
|
||||||
/* This double-wrapper allows Voices to keep a strong reference on
|
|
||||||
* a single instance of loaded loose data without being unexpectedly
|
|
||||||
* clobbered */
|
|
||||||
struct Entry
|
|
||||||
{
|
|
||||||
ObjToken<EntryData> m_data;
|
|
||||||
|
|
||||||
Entry()
|
|
||||||
: m_data(MakeObj<EntryData>()) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
Entry(const EntryDNA<DNAE>& in)
|
|
||||||
: m_data(MakeObj<EntryData>(in)) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
Entry(const MusyX1SdirEntry<DNAE>& in)
|
|
||||||
: m_data(MakeObj<EntryData>(in)) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
Entry(const MusyX1AbsSdirEntry<DNAE>& in)
|
|
||||||
: m_data(MakeObj<EntryData>(in)) {}
|
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
|
||||||
EntryDNA<DNAEn> toDNA(SFXId id) const
|
|
||||||
{
|
|
||||||
return m_data->toDNA<DNAEn>(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadLooseData(SystemStringView basePath);
|
|
||||||
SampleFileState getFileState(SystemStringView basePath, SystemString* pathOut = nullptr) const;
|
|
||||||
void patchSampleMetadata(SystemStringView basePath) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
|
std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
|
||||||
static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
|
static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
|
||||||
const unsigned char* samp);
|
const unsigned char* samp);
|
||||||
static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
|
static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
|
||||||
const unsigned char* samp, bool compressWAV = false);
|
const unsigned char* samp, bool compressWAV = false);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioGroupSampleDirectory() = default;
|
AudioGroupSampleDirectory() = default;
|
||||||
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
|
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
|
||||||
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
|
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
|
||||||
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
|
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
|
||||||
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
|
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
|
||||||
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
|
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
|
||||||
|
|
||||||
const std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() const { return m_entries; }
|
const std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() const { return m_entries; }
|
||||||
std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() { return m_entries; }
|
std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() { return m_entries; }
|
||||||
|
|
||||||
void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
|
void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
|
||||||
void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const;
|
void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const;
|
||||||
void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
|
void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
|
||||||
void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const;
|
void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const;
|
||||||
|
|
||||||
void reloadSampleData(SystemStringView groupPath);
|
void reloadSampleData(SystemStringView groupPath);
|
||||||
|
|
||||||
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> toGCNData(const AudioGroupDatabase& group) const;
|
std::pair<std::vector<uint8_t>, std::vector<uint8_t>> toGCNData(const AudioGroupDatabase& group) const;
|
||||||
|
|
||||||
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
|
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
|
||||||
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
|
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
|
||||||
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
|
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
|
||||||
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
|
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
using SampleEntry = AudioGroupSampleDirectory::Entry;
|
using SampleEntry = AudioGroupSampleDirectory::Entry;
|
||||||
using SampleEntryData = AudioGroupSampleDirectory::EntryData;
|
using SampleEntryData = AudioGroupSampleDirectory::EntryData;
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -10,136 +10,130 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
/** Backend voice implementation for boo mixer */
|
/** Backend voice implementation for boo mixer */
|
||||||
class BooBackendVoice : public IBackendVoice
|
class BooBackendVoice : public IBackendVoice {
|
||||||
{
|
friend class BooBackendVoiceAllocator;
|
||||||
friend class BooBackendVoiceAllocator;
|
Voice& m_clientVox;
|
||||||
Voice& m_clientVox;
|
struct VoiceCallback : boo::IAudioVoiceCallback {
|
||||||
struct VoiceCallback : boo::IAudioVoiceCallback
|
BooBackendVoice& m_parent;
|
||||||
{
|
void preSupplyAudio(boo::IAudioVoice& voice, double dt);
|
||||||
BooBackendVoice& m_parent;
|
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
|
||||||
void preSupplyAudio(boo::IAudioVoice& voice, double dt);
|
void routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out);
|
||||||
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
|
void routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out);
|
||||||
void routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out);
|
void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out);
|
||||||
void routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out);
|
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
|
||||||
void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out);
|
} m_cb;
|
||||||
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
|
boo::ObjToken<boo::IAudioVoice> m_booVoice;
|
||||||
} m_cb;
|
|
||||||
boo::ObjToken<boo::IAudioVoice> m_booVoice;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch);
|
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch);
|
||||||
void resetSampleRate(double sampleRate);
|
void resetSampleRate(double sampleRate);
|
||||||
|
|
||||||
void resetChannelLevels();
|
void resetChannelLevels();
|
||||||
void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew);
|
void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew);
|
||||||
void setPitchRatio(double ratio, bool slew);
|
void setPitchRatio(double ratio, bool slew);
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Backend submix implementation for boo mixer */
|
/** Backend submix implementation for boo mixer */
|
||||||
class BooBackendSubmix : public IBackendSubmix
|
class BooBackendSubmix : public IBackendSubmix {
|
||||||
{
|
friend class BooBackendVoiceAllocator;
|
||||||
friend class BooBackendVoiceAllocator;
|
friend class BooBackendVoice;
|
||||||
friend class BooBackendVoice;
|
Submix& m_clientSmx;
|
||||||
Submix& m_clientSmx;
|
struct SubmixCallback : boo::IAudioSubmixCallback {
|
||||||
struct SubmixCallback : boo::IAudioSubmixCallback
|
BooBackendSubmix& m_parent;
|
||||||
{
|
bool canApplyEffect() const;
|
||||||
BooBackendSubmix& m_parent;
|
void applyEffect(int16_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
|
||||||
bool canApplyEffect() const;
|
void applyEffect(int32_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
|
||||||
void applyEffect(int16_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
|
void applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
|
||||||
void applyEffect(int32_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
|
void resetOutputSampleRate(double sampleRate);
|
||||||
void applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
|
SubmixCallback(BooBackendSubmix& parent) : m_parent(parent) {}
|
||||||
void resetOutputSampleRate(double sampleRate);
|
} m_cb;
|
||||||
SubmixCallback(BooBackendSubmix& parent) : m_parent(parent) {}
|
boo::ObjToken<boo::IAudioSubmix> m_booSubmix;
|
||||||
} m_cb;
|
|
||||||
boo::ObjToken<boo::IAudioSubmix> m_booSubmix;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId);
|
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId);
|
||||||
void setSendLevel(IBackendSubmix* submix, float level, bool slew);
|
void setSendLevel(IBackendSubmix* submix, float level, bool slew);
|
||||||
double getSampleRate() const;
|
double getSampleRate() const;
|
||||||
SubmixFormat getSampleFormat() const;
|
SubmixFormat getSampleFormat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Backend MIDI event reader for controlling sequencer with external hardware / software */
|
/** Backend MIDI event reader for controlling sequencer with external hardware / software */
|
||||||
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
|
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader {
|
||||||
{
|
friend class BooBackendVoiceAllocator;
|
||||||
friend class BooBackendVoiceAllocator;
|
|
||||||
protected:
|
|
||||||
Engine& m_engine;
|
|
||||||
std::unordered_map<std::string, std::unique_ptr<boo::IMIDIIn>> m_midiIns;
|
|
||||||
std::unique_ptr<boo::IMIDIIn> m_virtualIn;
|
|
||||||
boo::MIDIDecoder m_decoder;
|
|
||||||
|
|
||||||
bool m_useLock;
|
protected:
|
||||||
std::list<std::pair<double, std::vector<uint8_t>>> m_queue;
|
Engine& m_engine;
|
||||||
std::mutex m_midiMutex;
|
std::unordered_map<std::string, std::unique_ptr<boo::IMIDIIn>> m_midiIns;
|
||||||
void _MIDIReceive(std::vector<uint8_t>&& bytes, double time);
|
std::unique_ptr<boo::IMIDIIn> m_virtualIn;
|
||||||
|
boo::MIDIDecoder m_decoder;
|
||||||
|
|
||||||
|
bool m_useLock;
|
||||||
|
std::list<std::pair<double, std::vector<uint8_t>>> m_queue;
|
||||||
|
std::mutex m_midiMutex;
|
||||||
|
void _MIDIReceive(std::vector<uint8_t>&& bytes, double time);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~BooBackendMIDIReader();
|
~BooBackendMIDIReader();
|
||||||
BooBackendMIDIReader(Engine& engine, bool useLock);
|
BooBackendMIDIReader(Engine& engine, bool useLock);
|
||||||
|
|
||||||
void addMIDIIn(const char* name);
|
void addMIDIIn(const char* name);
|
||||||
void removeMIDIIn(const char* name);
|
void removeMIDIIn(const char* name);
|
||||||
bool hasMIDIIn(const char* name) const;
|
bool hasMIDIIn(const char* name) const;
|
||||||
void setVirtualIn(bool v);
|
void setVirtualIn(bool v);
|
||||||
bool hasVirtualIn() const;
|
bool hasVirtualIn() const;
|
||||||
|
|
||||||
void pumpReader(double dt);
|
void pumpReader(double dt);
|
||||||
|
|
||||||
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
|
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
|
||||||
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
|
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
|
||||||
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
|
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
|
||||||
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
|
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
|
||||||
void programChange(uint8_t chan, uint8_t program);
|
void programChange(uint8_t chan, uint8_t program);
|
||||||
void channelPressure(uint8_t chan, uint8_t pressure);
|
void channelPressure(uint8_t chan, uint8_t pressure);
|
||||||
void pitchBend(uint8_t chan, int16_t pitch);
|
void pitchBend(uint8_t chan, int16_t pitch);
|
||||||
|
|
||||||
void allSoundOff(uint8_t chan);
|
void allSoundOff(uint8_t chan);
|
||||||
void resetAllControllers(uint8_t chan);
|
void resetAllControllers(uint8_t chan);
|
||||||
void localControl(uint8_t chan, bool on);
|
void localControl(uint8_t chan, bool on);
|
||||||
void allNotesOff(uint8_t chan);
|
void allNotesOff(uint8_t chan);
|
||||||
void omniMode(uint8_t chan, bool on);
|
void omniMode(uint8_t chan, bool on);
|
||||||
void polyMode(uint8_t chan, bool on);
|
void polyMode(uint8_t chan, bool on);
|
||||||
|
|
||||||
void sysex(const void* data, size_t len);
|
void sysex(const void* data, size_t len);
|
||||||
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
|
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
|
||||||
void songPositionPointer(uint16_t pointer);
|
void songPositionPointer(uint16_t pointer);
|
||||||
void songSelect(uint8_t song);
|
void songSelect(uint8_t song);
|
||||||
void tuneRequest();
|
void tuneRequest();
|
||||||
|
|
||||||
void startSeq();
|
void startSeq();
|
||||||
void continueSeq();
|
void continueSeq();
|
||||||
void stopSeq();
|
void stopSeq();
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Backend voice allocator implementation for boo mixer */
|
/** Backend voice allocator implementation for boo mixer */
|
||||||
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback
|
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback {
|
||||||
{
|
friend class BooBackendMIDIReader;
|
||||||
friend class BooBackendMIDIReader;
|
|
||||||
protected:
|
protected:
|
||||||
boo::IAudioVoiceEngine& m_booEngine;
|
boo::IAudioVoiceEngine& m_booEngine;
|
||||||
Engine* m_cbInterface = nullptr;
|
Engine* m_cbInterface = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
||||||
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
||||||
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId);
|
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId);
|
||||||
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices();
|
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices();
|
||||||
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine);
|
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine);
|
||||||
void setCallbackInterface(Engine* engine);
|
void setCallbackInterface(Engine* engine);
|
||||||
AudioChannelSet getAvailableSet();
|
AudioChannelSet getAvailableSet();
|
||||||
void setVolume(float vol);
|
void setVolume(float vol);
|
||||||
void on5MsInterval(boo::IAudioVoiceEngine& engine, double dt);
|
void on5MsInterval(boo::IAudioVoiceEngine& engine, double dt);
|
||||||
void onPumpCycleComplete(boo::IAudioVoiceEngine& engine);
|
void onPumpCycleComplete(boo::IAudioVoiceEngine& engine);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,7 @@
|
||||||
|
|
||||||
constexpr float NativeSampleRate = 32000.0f;
|
constexpr float NativeSampleRate = 32000.0f;
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
struct NameDB;
|
struct NameDB;
|
||||||
|
|
||||||
using BigDNA = athena::io::DNA<athena::Big>;
|
using BigDNA = athena::io::DNA<athena::Big>;
|
||||||
|
@ -46,48 +45,45 @@ using LittleDNAV = athena::io::DNAVYaml<athena::Little>;
|
||||||
|
|
||||||
/** Common ID structure statically tagging
|
/** Common ID structure statically tagging
|
||||||
* SoundMacros, Tables, Keymaps, Layers, Samples, SFX, Songs */
|
* SoundMacros, Tables, Keymaps, Layers, Samples, SFX, Songs */
|
||||||
struct ObjectId
|
struct ObjectId {
|
||||||
{
|
uint16_t id = 0xffff;
|
||||||
uint16_t id = 0xffff;
|
operator uint16_t() const { return id; }
|
||||||
operator uint16_t() const { return id; }
|
ObjectId() = default;
|
||||||
ObjectId() = default;
|
ObjectId(uint16_t idIn) : id(idIn) {}
|
||||||
ObjectId(uint16_t idIn) : id(idIn) {}
|
ObjectId& operator=(uint16_t idIn) {
|
||||||
ObjectId& operator=(uint16_t idIn) { id = idIn; return *this; }
|
id = idIn;
|
||||||
static thread_local NameDB* CurNameDB;
|
return *this;
|
||||||
|
}
|
||||||
|
static thread_local NameDB* CurNameDB;
|
||||||
};
|
};
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) ObjectIdDNA : BigDNA {
|
||||||
ObjectIdDNA : BigDNA
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
{
|
void _read(athena::io::YAMLDocReader& r);
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
void _write(athena::io::YAMLDocWriter& w);
|
||||||
void _read(athena::io::YAMLDocReader& r);
|
ObjectId id;
|
||||||
void _write(athena::io::YAMLDocWriter& w);
|
ObjectIdDNA() = default;
|
||||||
ObjectId id;
|
ObjectIdDNA(ObjectId idIn) : id(idIn) {}
|
||||||
ObjectIdDNA() = default;
|
operator ObjectId() const { return id; }
|
||||||
ObjectIdDNA(ObjectId idIn) : id(idIn) {}
|
|
||||||
operator ObjectId() const { return id; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DECL_ID_TYPE(type) \
|
#define DECL_ID_TYPE(type) \
|
||||||
struct type : ObjectId \
|
struct type : ObjectId { \
|
||||||
{ \
|
using ObjectId::ObjectId; \
|
||||||
using ObjectId::ObjectId; \
|
type() = default; \
|
||||||
type() = default; \
|
type(const ObjectId& id) : ObjectId(id) {} \
|
||||||
type(const ObjectId& id) : ObjectId(id) {} \
|
static thread_local NameDB* CurNameDB; \
|
||||||
static thread_local NameDB* CurNameDB; \
|
}; \
|
||||||
}; \
|
template <athena::Endian DNAEn> \
|
||||||
template <athena::Endian DNAEn> \
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) type##DNA : BigDNA { \
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) \
|
AT_DECL_EXPLICIT_DNA_YAML \
|
||||||
type##DNA : BigDNA \
|
void _read(athena::io::YAMLDocReader& r); \
|
||||||
{ \
|
void _write(athena::io::YAMLDocWriter& w); \
|
||||||
AT_DECL_EXPLICIT_DNA_YAML \
|
type id; \
|
||||||
void _read(athena::io::YAMLDocReader& r); \
|
type##DNA() = default; \
|
||||||
void _write(athena::io::YAMLDocWriter& w); \
|
type##DNA(type idIn) : id(idIn) {} \
|
||||||
type id; \
|
operator type() const { return id; } \
|
||||||
type##DNA() = default; \
|
};
|
||||||
type##DNA(type idIn) : id(idIn) {} \
|
|
||||||
operator type() const { return id; } \
|
|
||||||
};
|
|
||||||
DECL_ID_TYPE(SoundMacroId)
|
DECL_ID_TYPE(SoundMacroId)
|
||||||
DECL_ID_TYPE(SampleId)
|
DECL_ID_TYPE(SampleId)
|
||||||
DECL_ID_TYPE(TableId)
|
DECL_ID_TYPE(TableId)
|
||||||
|
@ -101,160 +97,204 @@ DECL_ID_TYPE(GroupId)
|
||||||
* referenced by a song group's page object. When the upper bit is set,
|
* referenced by a song group's page object. When the upper bit is set,
|
||||||
* this indicates a layer type. */
|
* this indicates a layer type. */
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) PageObjectIdDNA : BigDNA {
|
||||||
PageObjectIdDNA : BigDNA
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
{
|
void _read(athena::io::YAMLDocReader& r);
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
void _write(athena::io::YAMLDocWriter& w);
|
||||||
void _read(athena::io::YAMLDocReader& r);
|
ObjectId id;
|
||||||
void _write(athena::io::YAMLDocWriter& w);
|
PageObjectIdDNA() = default;
|
||||||
ObjectId id;
|
PageObjectIdDNA(ObjectId idIn) : id(idIn) {}
|
||||||
PageObjectIdDNA() = default;
|
operator ObjectId() const { return id; }
|
||||||
PageObjectIdDNA(ObjectId idIn) : id(idIn) {}
|
|
||||||
operator ObjectId() const { return id; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundMacroStep
|
struct SoundMacroStep {
|
||||||
{
|
uint16_t step = 0;
|
||||||
uint16_t step = 0;
|
operator uint16_t() const { return step; }
|
||||||
operator uint16_t() const { return step; }
|
SoundMacroStep() = default;
|
||||||
SoundMacroStep() = default;
|
SoundMacroStep(uint16_t idIn) : step(idIn) {}
|
||||||
SoundMacroStep(uint16_t idIn) : step(idIn) {}
|
SoundMacroStep& operator=(uint16_t idIn) {
|
||||||
SoundMacroStep& operator=(uint16_t idIn) { step = idIn; return *this; }
|
step = idIn;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <athena::Endian DNAEn>
|
template <athena::Endian DNAEn>
|
||||||
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) SoundMacroStepDNA : BigDNA {
|
||||||
SoundMacroStepDNA : BigDNA
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
{
|
SoundMacroStep step;
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
SoundMacroStepDNA() = default;
|
||||||
SoundMacroStep step;
|
SoundMacroStepDNA(SoundMacroStep idIn) : step(idIn) {}
|
||||||
SoundMacroStepDNA() = default;
|
operator SoundMacroStep() const { return step; }
|
||||||
SoundMacroStepDNA(SoundMacroStep idIn) : step(idIn) {}
|
|
||||||
operator SoundMacroStep() const { return step; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LittleUInt24 : LittleDNA
|
struct LittleUInt24 : LittleDNA {
|
||||||
{
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
atUint32 val;
|
||||||
atUint32 val;
|
operator uint32_t() const { return val; }
|
||||||
operator uint32_t() const { return val; }
|
LittleUInt24() = default;
|
||||||
LittleUInt24() = default;
|
LittleUInt24(uint32_t valIn) : val(valIn) {}
|
||||||
LittleUInt24(uint32_t valIn) : val(valIn) {}
|
LittleUInt24& operator=(uint32_t valIn) {
|
||||||
LittleUInt24& operator=(uint32_t valIn) { val = valIn; return *this; }
|
val = valIn;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class IObj
|
class IObj {
|
||||||
{
|
std::atomic_int m_refCount = {0};
|
||||||
std::atomic_int m_refCount = {0};
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~IObj() = default;
|
virtual ~IObj() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void increment() { m_refCount++; }
|
void increment() { m_refCount++; }
|
||||||
void decrement()
|
void decrement() {
|
||||||
{
|
if (m_refCount.fetch_sub(1) == 1)
|
||||||
if (m_refCount.fetch_sub(1) == 1)
|
delete this;
|
||||||
delete this;
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class SubCls>
|
template <class SubCls>
|
||||||
class ObjWrapper : public IObj
|
class ObjWrapper : public IObj {
|
||||||
{
|
SubCls m_obj;
|
||||||
SubCls m_obj;
|
|
||||||
public:
|
public:
|
||||||
template <class... _Args>
|
template <class... _Args>
|
||||||
ObjWrapper(_Args&&... args) : m_obj(std::forward<_Args>(args)...) {}
|
ObjWrapper(_Args&&... args) : m_obj(std::forward<_Args>(args)...) {}
|
||||||
SubCls* get() { return &m_obj; }
|
SubCls* get() { return &m_obj; }
|
||||||
const SubCls* get() const { return &m_obj; }
|
const SubCls* get() const { return &m_obj; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class SubCls>
|
template <class SubCls>
|
||||||
class ObjTokenBase
|
class ObjTokenBase {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
IObj* m_obj = nullptr;
|
IObj* m_obj = nullptr;
|
||||||
ObjTokenBase(IObj* obj) : m_obj(obj) { if (m_obj) m_obj->increment(); }
|
ObjTokenBase(IObj* obj) : m_obj(obj) {
|
||||||
|
if (m_obj)
|
||||||
|
m_obj->increment();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjTokenBase() = default;
|
ObjTokenBase() = default;
|
||||||
ObjTokenBase(const ObjTokenBase& other) : m_obj(other.m_obj) { if (m_obj) m_obj->increment(); }
|
ObjTokenBase(const ObjTokenBase& other) : m_obj(other.m_obj) {
|
||||||
ObjTokenBase(ObjTokenBase&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; }
|
if (m_obj)
|
||||||
ObjTokenBase& operator=(const ObjTokenBase& other)
|
m_obj->increment();
|
||||||
{ if (m_obj) m_obj->decrement(); m_obj = other.m_obj; if (m_obj) m_obj->increment(); return *this; }
|
}
|
||||||
ObjTokenBase& operator=(ObjTokenBase&& other)
|
ObjTokenBase(ObjTokenBase&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; }
|
||||||
{ if (m_obj) m_obj->decrement(); m_obj = other.m_obj; other.m_obj = nullptr; return *this; }
|
ObjTokenBase& operator=(const ObjTokenBase& other) {
|
||||||
~ObjTokenBase() { if (m_obj) m_obj->decrement(); }
|
if (m_obj)
|
||||||
bool operator==(const ObjTokenBase& other) const { return m_obj == other.m_obj; }
|
m_obj->decrement();
|
||||||
bool operator!=(const ObjTokenBase& other) const { return m_obj != other.m_obj; }
|
m_obj = other.m_obj;
|
||||||
bool operator<(const ObjTokenBase& other) const { return m_obj < other.m_obj; }
|
if (m_obj)
|
||||||
bool operator>(const ObjTokenBase& other) const { return m_obj > other.m_obj; }
|
m_obj->increment();
|
||||||
operator bool() const { return m_obj != nullptr; }
|
return *this;
|
||||||
void reset() { if (m_obj) m_obj->decrement(); m_obj = nullptr; }
|
}
|
||||||
|
ObjTokenBase& operator=(ObjTokenBase&& other) {
|
||||||
|
if (m_obj)
|
||||||
|
m_obj->decrement();
|
||||||
|
m_obj = other.m_obj;
|
||||||
|
other.m_obj = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~ObjTokenBase() {
|
||||||
|
if (m_obj)
|
||||||
|
m_obj->decrement();
|
||||||
|
}
|
||||||
|
bool operator==(const ObjTokenBase& other) const { return m_obj == other.m_obj; }
|
||||||
|
bool operator!=(const ObjTokenBase& other) const { return m_obj != other.m_obj; }
|
||||||
|
bool operator<(const ObjTokenBase& other) const { return m_obj < other.m_obj; }
|
||||||
|
bool operator>(const ObjTokenBase& other) const { return m_obj > other.m_obj; }
|
||||||
|
operator bool() const { return m_obj != nullptr; }
|
||||||
|
void reset() {
|
||||||
|
if (m_obj)
|
||||||
|
m_obj->decrement();
|
||||||
|
m_obj = nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class SubCls, class Enable = void>
|
template <class SubCls, class Enable = void>
|
||||||
class ObjToken : public ObjTokenBase<SubCls>
|
class ObjToken : public ObjTokenBase<SubCls> {
|
||||||
{
|
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
||||||
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
IObj* const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
||||||
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
|
||||||
public:
|
public:
|
||||||
using ObjTokenBase<SubCls>::ObjTokenBase;
|
using ObjTokenBase<SubCls>::ObjTokenBase;
|
||||||
ObjToken() = default;
|
ObjToken() = default;
|
||||||
ObjToken(ObjWrapper<SubCls>* obj) : ObjTokenBase<SubCls>(obj) {}
|
ObjToken(ObjWrapper<SubCls>* obj) : ObjTokenBase<SubCls>(obj) {}
|
||||||
ObjToken& operator=(ObjWrapper<SubCls>* obj)
|
ObjToken& operator=(ObjWrapper<SubCls>* obj) {
|
||||||
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); return *this; }
|
if (_obj())
|
||||||
SubCls* get() const { return static_cast<ObjWrapper<SubCls>*>(_obj())->get(); }
|
_obj()->decrement();
|
||||||
SubCls* operator->() const { return get(); }
|
_obj() = obj;
|
||||||
SubCls& operator*() const { return *get(); }
|
if (_obj())
|
||||||
|
_obj()->increment();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
SubCls* get() const { return static_cast<ObjWrapper<SubCls>*>(_obj())->get(); }
|
||||||
|
SubCls* operator->() const { return get(); }
|
||||||
|
SubCls& operator*() const { return *get(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class SubCls>
|
template <class SubCls>
|
||||||
class ObjToken<SubCls, typename std::enable_if_t<std::is_base_of_v<IObj, SubCls>>> : public ObjTokenBase<SubCls>
|
class ObjToken<SubCls, typename std::enable_if_t<std::is_base_of_v<IObj, SubCls>>> : public ObjTokenBase<SubCls> {
|
||||||
{
|
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
||||||
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
IObj* const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
||||||
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
|
||||||
public:
|
public:
|
||||||
using ObjTokenBase<SubCls>::ObjTokenBase;
|
using ObjTokenBase<SubCls>::ObjTokenBase;
|
||||||
ObjToken() = default;
|
ObjToken() = default;
|
||||||
ObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
|
ObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
|
||||||
ObjToken& operator=(IObj* obj)
|
ObjToken& operator=(IObj* obj) {
|
||||||
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); return *this; }
|
if (_obj())
|
||||||
SubCls* get() const { return static_cast<SubCls*>(_obj()); }
|
_obj()->decrement();
|
||||||
SubCls* operator->() const { return get(); }
|
_obj() = obj;
|
||||||
SubCls& operator*() const { return *get(); }
|
if (_obj())
|
||||||
template <class T>
|
_obj()->increment();
|
||||||
T* cast() const { return static_cast<T*>(_obj()); }
|
return *this;
|
||||||
|
}
|
||||||
|
SubCls* get() const { return static_cast<SubCls*>(_obj()); }
|
||||||
|
SubCls* operator->() const { return get(); }
|
||||||
|
SubCls& operator*() const { return *get(); }
|
||||||
|
template <class T>
|
||||||
|
T* cast() const {
|
||||||
|
return static_cast<T*>(_obj());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ONLY USE WITH CLASSES DERIVED FROM IOBJ!
|
/* ONLY USE WITH CLASSES DERIVED FROM IOBJ!
|
||||||
* Bypasses type_traits tests for incomplete type definitions. */
|
* Bypasses type_traits tests for incomplete type definitions. */
|
||||||
template<class SubCls>
|
template <class SubCls>
|
||||||
class IObjToken : public ObjTokenBase<SubCls>
|
class IObjToken : public ObjTokenBase<SubCls> {
|
||||||
{
|
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
||||||
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
IObj* const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
||||||
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
|
||||||
public:
|
public:
|
||||||
using ObjTokenBase<SubCls>::ObjTokenBase;
|
using ObjTokenBase<SubCls>::ObjTokenBase;
|
||||||
IObjToken() = default;
|
IObjToken() = default;
|
||||||
IObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
|
IObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
|
||||||
IObjToken& operator=(IObj* obj)
|
IObjToken& operator=(IObj* obj) {
|
||||||
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); return *this; }
|
if (_obj())
|
||||||
SubCls* get() const { return static_cast<SubCls*>(_obj()); }
|
_obj()->decrement();
|
||||||
SubCls* operator->() const { return get(); }
|
_obj() = obj;
|
||||||
SubCls& operator*() const { return *get(); }
|
if (_obj())
|
||||||
template <class T>
|
_obj()->increment();
|
||||||
T* cast() const { return static_cast<T*>(_obj()); }
|
return *this;
|
||||||
|
}
|
||||||
|
SubCls* get() const { return static_cast<SubCls*>(_obj()); }
|
||||||
|
SubCls* operator->() const { return get(); }
|
||||||
|
SubCls& operator*() const { return *get(); }
|
||||||
|
template <class T>
|
||||||
|
T* cast() const {
|
||||||
|
return static_cast<T*>(_obj());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class Tp, class... _Args>
|
template <class Tp, class... _Args>
|
||||||
inline typename std::enable_if_t<std::is_base_of_v<IObj, Tp>, ObjToken<Tp>> MakeObj(_Args&&... args)
|
inline typename std::enable_if_t<std::is_base_of_v<IObj, Tp>, ObjToken<Tp>> MakeObj(_Args&&... args) {
|
||||||
{
|
return new Tp(std::forward<_Args>(args)...);
|
||||||
return new Tp(std::forward<_Args>(args)...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Tp, class... _Args>
|
template <class Tp, class... _Args>
|
||||||
inline typename std::enable_if_t<!std::is_base_of_v<IObj, Tp>, ObjToken<Tp>> MakeObj(_Args&&... args)
|
inline typename std::enable_if_t<!std::is_base_of_v<IObj, Tp>, ObjToken<Tp>> MakeObj(_Args&&... args) {
|
||||||
{
|
return new ObjWrapper<Tp>(std::forward<_Args>(args)...);
|
||||||
return new ObjWrapper<Tp>(std::forward<_Args>(args)...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef PRISize
|
#ifndef PRISize
|
||||||
|
@ -287,13 +327,12 @@ static inline int Mkdir(const char* path, mode_t mode) { return mkdir(path, mode
|
||||||
static inline int Stat(const char* path, Sstat* statout) { return stat(path, statout); }
|
static inline int Stat(const char* path, Sstat* statout) { return stat(path, statout); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath)
|
static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
//return _wrename(oldpath, newpath);
|
// return _wrename(oldpath, newpath);
|
||||||
return MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
|
return MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
|
||||||
#else
|
#else
|
||||||
return rename(oldpath, newpath);
|
return rename(oldpath, newpath);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,40 +340,34 @@ static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath)
|
||||||
static inline int CompareCaseInsensitive(const char* a, const char* b) { return _stricmp(a, b); }
|
static inline int CompareCaseInsensitive(const char* a, const char* b) { return _stricmp(a, b); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline int CompareCaseInsensitive(const SystemChar* a, const SystemChar* b)
|
static inline int CompareCaseInsensitive(const SystemChar* a, const SystemChar* b) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return _wcsicmp(a, b);
|
return _wcsicmp(a, b);
|
||||||
#else
|
#else
|
||||||
return strcasecmp(a, b);
|
return strcasecmp(a, b);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T clamp(T a, T val, T b)
|
static inline T clamp(T a, T val, T b) {
|
||||||
{
|
return std::max<T>(a, std::min<T>(b, val));
|
||||||
return std::max<T>(a, std::min<T>(b, val));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T ClampFull(float in)
|
inline T ClampFull(float in) {
|
||||||
{
|
if (std::is_floating_point<T>()) {
|
||||||
if (std::is_floating_point<T>())
|
return in;
|
||||||
{
|
} else {
|
||||||
return in;
|
constexpr T MAX = std::numeric_limits<T>::max();
|
||||||
}
|
constexpr T MIN = std::numeric_limits<T>::min();
|
||||||
else
|
|
||||||
{
|
|
||||||
constexpr T MAX = std::numeric_limits<T>::max();
|
|
||||||
constexpr T MIN = std::numeric_limits<T>::min();
|
|
||||||
|
|
||||||
if (in < MIN)
|
if (in < MIN)
|
||||||
return MIN;
|
return MIN;
|
||||||
else if (in > MAX)
|
else if (in > MAX)
|
||||||
return MAX;
|
return MAX;
|
||||||
else
|
else
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef M_PIF
|
#ifndef M_PIF
|
||||||
|
@ -345,94 +378,86 @@ inline T ClampFull(float in)
|
||||||
__attribute__((__format__(__printf__, 1, 2)))
|
__attribute__((__format__(__printf__, 1, 2)))
|
||||||
#endif
|
#endif
|
||||||
static inline void
|
static inline void
|
||||||
Printf(const SystemChar* fmt, ...)
|
Printf(const SystemChar* fmt, ...) {
|
||||||
{
|
va_list args;
|
||||||
va_list args;
|
va_start(args, fmt);
|
||||||
va_start(args, fmt);
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
vwprintf(fmt, args);
|
vwprintf(fmt, args);
|
||||||
#else
|
#else
|
||||||
vprintf(fmt, args);
|
vprintf(fmt, args);
|
||||||
#endif
|
#endif
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __GNUC__
|
#if __GNUC__
|
||||||
__attribute__((__format__(__printf__, 3, 4)))
|
__attribute__((__format__(__printf__, 3, 4)))
|
||||||
#endif
|
#endif
|
||||||
static inline void
|
static inline void
|
||||||
SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...)
|
SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...) {
|
||||||
{
|
va_list va;
|
||||||
va_list va;
|
va_start(va, format);
|
||||||
va_start(va, format);
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
_vsnwprintf(str, maxlen, format, va);
|
_vsnwprintf(str, maxlen, format, va);
|
||||||
#else
|
#else
|
||||||
vsnprintf(str, maxlen, format, va);
|
vsnprintf(str, maxlen, format, va);
|
||||||
#endif
|
#endif
|
||||||
va_end(va);
|
va_end(va);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const SystemChar* StrRChr(const SystemChar* str, SystemChar ch)
|
static inline const SystemChar* StrRChr(const SystemChar* str, SystemChar ch) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return wcsrchr(str, ch);
|
return wcsrchr(str, ch);
|
||||||
#else
|
#else
|
||||||
return strrchr(str, ch);
|
return strrchr(str, ch);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline SystemChar* StrRChr(SystemChar* str, SystemChar ch)
|
static inline SystemChar* StrRChr(SystemChar* str, SystemChar ch) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return wcsrchr(str, ch);
|
return wcsrchr(str, ch);
|
||||||
#else
|
#else
|
||||||
return strrchr(str, ch);
|
return strrchr(str, ch);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int FSeek(FILE* fp, int64_t offset, int whence)
|
static inline int FSeek(FILE* fp, int64_t offset, int whence) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return _fseeki64(fp, offset, whence);
|
return _fseeki64(fp, offset, whence);
|
||||||
#elif __APPLE__ || __FreeBSD__
|
#elif __APPLE__ || __FreeBSD__
|
||||||
return fseeko(fp, offset, whence);
|
return fseeko(fp, offset, whence);
|
||||||
#else
|
#else
|
||||||
return fseeko64(fp, offset, whence);
|
return fseeko64(fp, offset, whence);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int64_t FTell(FILE* fp)
|
static inline int64_t FTell(FILE* fp) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return _ftelli64(fp);
|
return _ftelli64(fp);
|
||||||
#elif __APPLE__ || __FreeBSD__
|
#elif __APPLE__ || __FreeBSD__
|
||||||
return ftello(fp);
|
return ftello(fp);
|
||||||
#else
|
#else
|
||||||
return ftello64(fp);
|
return ftello64(fp);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline FILE* FOpen(const SystemChar* path, const SystemChar* mode)
|
static inline FILE* FOpen(const SystemChar* path, const SystemChar* mode) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
FILE* fp = _wfopen(path, mode);
|
FILE* fp = _wfopen(path, mode);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#else
|
#else
|
||||||
FILE* fp = fopen(path, mode);
|
FILE* fp = fopen(path, mode);
|
||||||
if (!fp)
|
if (!fp)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void Unlink(const SystemChar* file)
|
static inline void Unlink(const SystemChar* file) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
_wunlink(file);
|
_wunlink(file);
|
||||||
#else
|
#else
|
||||||
unlink(file);
|
unlink(file);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,43 +469,40 @@ bool Copy(const SystemChar* from, const SystemChar* to);
|
||||||
|
|
||||||
/* Type-sensitive byte swappers */
|
/* Type-sensitive byte swappers */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T bswap16(T val)
|
static inline T bswap16(T val) {
|
||||||
{
|
|
||||||
#if __GNUC__
|
#if __GNUC__
|
||||||
return __builtin_bswap16(val);
|
return __builtin_bswap16(val);
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
return _byteswap_ushort(val);
|
return _byteswap_ushort(val);
|
||||||
#else
|
#else
|
||||||
return (val = (val << 8) | ((val >> 8) & 0xFF));
|
return (val = (val << 8) | ((val >> 8) & 0xFF));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T bswap32(T val)
|
static inline T bswap32(T val) {
|
||||||
{
|
|
||||||
#if __GNUC__
|
#if __GNUC__
|
||||||
return __builtin_bswap32(val);
|
return __builtin_bswap32(val);
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
return _byteswap_ulong(val);
|
return _byteswap_ulong(val);
|
||||||
#else
|
#else
|
||||||
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
|
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
|
||||||
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
|
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
|
||||||
return val;
|
return val;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T bswap64(T val)
|
static inline T bswap64(T val) {
|
||||||
{
|
|
||||||
#if __GNUC__
|
#if __GNUC__
|
||||||
return __builtin_bswap64(val);
|
return __builtin_bswap64(val);
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
return _byteswap_uint64(val);
|
return _byteswap_uint64(val);
|
||||||
#else
|
#else
|
||||||
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
|
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
|
||||||
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
|
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
|
||||||
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
|
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
|
||||||
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
|
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,15 +513,13 @@ static inline int32_t SBig(int32_t val) { return bswap32(val); }
|
||||||
static inline uint32_t SBig(uint32_t val) { return bswap32(val); }
|
static inline uint32_t SBig(uint32_t val) { return bswap32(val); }
|
||||||
static inline int64_t SBig(int64_t val) { return bswap64(val); }
|
static inline int64_t SBig(int64_t val) { return bswap64(val); }
|
||||||
static inline uint64_t SBig(uint64_t val) { return bswap64(val); }
|
static inline uint64_t SBig(uint64_t val) { return bswap64(val); }
|
||||||
static inline float SBig(float val)
|
static inline float SBig(float val) {
|
||||||
{
|
int32_t ival = bswap32(*((int32_t*)(&val)));
|
||||||
int32_t ival = bswap32(*((int32_t*)(&val)));
|
return *((float*)(&ival));
|
||||||
return *((float*)(&ival));
|
|
||||||
}
|
}
|
||||||
static inline double SBig(double val)
|
static inline double SBig(double val) {
|
||||||
{
|
int64_t ival = bswap64(*((int64_t*)(&val)));
|
||||||
int64_t ival = bswap64(*((int64_t*)(&val)));
|
return *((double*)(&ival));
|
||||||
return *((double*)(&ival));
|
|
||||||
}
|
}
|
||||||
#ifndef SBIG
|
#ifndef SBIG
|
||||||
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
||||||
|
@ -523,15 +543,13 @@ static inline int32_t SLittle(int32_t val) { return bswap32(val); }
|
||||||
static inline uint32_t SLittle(uint32_t val) { return bswap32(val); }
|
static inline uint32_t SLittle(uint32_t val) { return bswap32(val); }
|
||||||
static inline int64_t SLittle(int64_t val) { return bswap64(val); }
|
static inline int64_t SLittle(int64_t val) { return bswap64(val); }
|
||||||
static inline uint64_t SLittle(uint64_t val) { return bswap64(val); }
|
static inline uint64_t SLittle(uint64_t val) { return bswap64(val); }
|
||||||
static inline float SLittle(float val)
|
static inline float SLittle(float val) {
|
||||||
{
|
int32_t ival = bswap32(*((int32_t*)(&val)));
|
||||||
int32_t ival = bswap32(*((int32_t*)(&val)));
|
return *((float*)(&ival));
|
||||||
return *((float*)(&ival));
|
|
||||||
}
|
}
|
||||||
static inline double SLittle(double val)
|
static inline double SLittle(double val) {
|
||||||
{
|
int64_t ival = bswap64(*((int64_t*)(&val)));
|
||||||
int64_t ival = bswap64(*((int64_t*)(&val)));
|
return *((double*)(&ival));
|
||||||
return *((double*)(&ival));
|
|
||||||
}
|
}
|
||||||
#ifndef SLITTLE
|
#ifndef SLITTLE
|
||||||
#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
||||||
|
@ -551,83 +569,66 @@ static inline double SBig(double val) { return val; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Versioned data format to interpret */
|
/** Versioned data format to interpret */
|
||||||
enum class DataFormat
|
enum class DataFormat { GCN, N64, PC };
|
||||||
{
|
|
||||||
GCN,
|
|
||||||
N64,
|
|
||||||
PC
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Meta-type for selecting GameCube (MusyX 2.0) data formats */
|
/** Meta-type for selecting GameCube (MusyX 2.0) data formats */
|
||||||
struct GCNDataTag
|
struct GCNDataTag {};
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Meta-type for selecting N64 (MusyX 1.0) data formats */
|
/** Meta-type for selecting N64 (MusyX 1.0) data formats */
|
||||||
struct N64DataTag
|
struct N64DataTag {};
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Meta-type for selecting PC (MusyX 1.0) data formats */
|
/** Meta-type for selecting PC (MusyX 1.0) data formats */
|
||||||
struct PCDataTag
|
struct PCDataTag {};
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
static std::vector<std::pair<typename T::key_type,
|
static std::vector<std::pair<typename T::key_type, std::reference_wrapper<typename T::mapped_type>>>
|
||||||
std::reference_wrapper<typename T::mapped_type>>> SortUnorderedMap(T& um)
|
SortUnorderedMap(T& um) {
|
||||||
{
|
std::vector<std::pair<typename T::key_type, std::reference_wrapper<typename T::mapped_type>>> ret;
|
||||||
std::vector<std::pair<typename T::key_type, std::reference_wrapper<typename T::mapped_type>>> ret;
|
ret.reserve(um.size());
|
||||||
ret.reserve(um.size());
|
for (auto& p : um)
|
||||||
for (auto& p : um)
|
ret.emplace_back(p.first, p.second);
|
||||||
ret.emplace_back(p.first, p.second);
|
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
|
||||||
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
static std::vector<std::pair<typename T::key_type,
|
static std::vector<std::pair<typename T::key_type, std::reference_wrapper<const typename T::mapped_type>>>
|
||||||
std::reference_wrapper<const typename T::mapped_type>>> SortUnorderedMap(const T& um)
|
SortUnorderedMap(const T& um) {
|
||||||
{
|
std::vector<std::pair<typename T::key_type, std::reference_wrapper<const typename T::mapped_type>>> ret;
|
||||||
std::vector<std::pair<typename T::key_type, std::reference_wrapper<const typename T::mapped_type>>> ret;
|
ret.reserve(um.size());
|
||||||
ret.reserve(um.size());
|
for (const auto& p : um)
|
||||||
for (const auto& p : um)
|
ret.emplace_back(p.first, p.second);
|
||||||
ret.emplace_back(p.first, p.second);
|
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
|
||||||
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
static std::vector<typename T::key_type> SortUnorderedSet(T& us)
|
static std::vector<typename T::key_type> SortUnorderedSet(T& us) {
|
||||||
{
|
std::vector<typename T::key_type> ret;
|
||||||
std::vector<typename T::key_type> ret;
|
ret.reserve(us.size());
|
||||||
ret.reserve(us.size());
|
for (auto& p : us)
|
||||||
for (auto& p : us)
|
ret.emplace_back(p);
|
||||||
ret.emplace_back(p);
|
std::sort(ret.begin(), ret.end());
|
||||||
std::sort(ret.begin(), ret.end());
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
static std::vector<typename T::key_type> SortUnorderedSet(const T& us)
|
static std::vector<typename T::key_type> SortUnorderedSet(const T& us) {
|
||||||
{
|
std::vector<typename T::key_type> ret;
|
||||||
std::vector<typename T::key_type> ret;
|
ret.reserve(us.size());
|
||||||
ret.reserve(us.size());
|
for (const auto& p : us)
|
||||||
for (const auto& p : us)
|
ret.emplace_back(p);
|
||||||
ret.emplace_back(p);
|
std::sort(ret.begin(), ret.end());
|
||||||
std::sort(ret.begin(), ret.end());
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // namespace amuse
|
||||||
|
|
||||||
namespace std
|
namespace std {
|
||||||
{
|
#define DECL_ID_HASH(type) \
|
||||||
#define DECL_ID_HASH(type) \
|
template <> \
|
||||||
template<> \
|
struct hash<amuse::type> { \
|
||||||
struct hash<amuse::type> \
|
size_t operator()(const amuse::type& val) const noexcept { return val.id; } \
|
||||||
{ \
|
};
|
||||||
size_t operator()(const amuse::type& val) const noexcept { return val.id; } \
|
|
||||||
};
|
|
||||||
DECL_ID_HASH(ObjectId)
|
DECL_ID_HASH(ObjectId)
|
||||||
DECL_ID_HASH(SoundMacroId)
|
DECL_ID_HASH(SoundMacroId)
|
||||||
DECL_ID_HASH(SampleId)
|
DECL_ID_HASH(SampleId)
|
||||||
|
@ -638,40 +639,26 @@ DECL_ID_HASH(SongId)
|
||||||
DECL_ID_HASH(SFXId)
|
DECL_ID_HASH(SFXId)
|
||||||
DECL_ID_HASH(GroupId)
|
DECL_ID_HASH(GroupId)
|
||||||
|
|
||||||
template<class T>
|
template <class T>
|
||||||
struct hash<amuse::ObjToken<T>>
|
struct hash<amuse::ObjToken<T>> {
|
||||||
{
|
size_t operator()(const amuse::ObjToken<T>& val) const noexcept { return hash<T*>()(val.get()); }
|
||||||
size_t operator()(const amuse::ObjToken<T>& val) const noexcept { return hash<T*>()(val.get()); }
|
|
||||||
};
|
};
|
||||||
}
|
} // namespace std
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
struct NameDB {
|
||||||
struct NameDB
|
enum class Type { SoundMacro, Table, Keymap, Layer, Song, SFX, Group, Sample };
|
||||||
{
|
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
SoundMacro,
|
|
||||||
Table,
|
|
||||||
Keymap,
|
|
||||||
Layer,
|
|
||||||
Song,
|
|
||||||
SFX,
|
|
||||||
Group,
|
|
||||||
Sample
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<std::string, ObjectId> m_stringToId;
|
std::unordered_map<std::string, ObjectId> m_stringToId;
|
||||||
std::unordered_map<ObjectId, std::string> m_idToString;
|
std::unordered_map<ObjectId, std::string> m_idToString;
|
||||||
|
|
||||||
ObjectId generateId(Type tp) const;
|
ObjectId generateId(Type tp) const;
|
||||||
static std::string generateName(ObjectId id, Type tp);
|
static std::string generateName(ObjectId id, Type tp);
|
||||||
std::string generateDefaultName(Type tp) const;
|
std::string generateDefaultName(Type tp) const;
|
||||||
std::string_view registerPair(std::string_view str, ObjectId id);
|
std::string_view registerPair(std::string_view str, ObjectId id);
|
||||||
std::string_view resolveNameFromId(ObjectId id) const;
|
std::string_view resolveNameFromId(ObjectId id) const;
|
||||||
ObjectId resolveIdFromName(std::string_view str) const;
|
ObjectId resolveIdFromName(std::string_view str) const;
|
||||||
void remove(ObjectId id);
|
void remove(ObjectId id);
|
||||||
void rename(ObjectId id, std::string_view str);
|
void rename(ObjectId id, std::string_view str);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -6,41 +6,34 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
class ContainerRegistry
|
class ContainerRegistry {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
enum class Type
|
enum class Type {
|
||||||
{
|
Invalid = -1,
|
||||||
Invalid = -1,
|
Raw4 = 0,
|
||||||
Raw4 = 0,
|
MetroidPrime,
|
||||||
MetroidPrime,
|
MetroidPrime2,
|
||||||
MetroidPrime2,
|
RogueSquadronPC,
|
||||||
RogueSquadronPC,
|
RogueSquadronN64,
|
||||||
RogueSquadronN64,
|
Factor5N64Rev,
|
||||||
Factor5N64Rev,
|
RogueSquadron2,
|
||||||
RogueSquadron2,
|
RogueSquadron3
|
||||||
RogueSquadron3
|
};
|
||||||
};
|
struct SongData {
|
||||||
struct SongData
|
std::unique_ptr<uint8_t[]> m_data;
|
||||||
{
|
size_t m_size;
|
||||||
std::unique_ptr<uint8_t[]> m_data;
|
int16_t m_groupId;
|
||||||
size_t m_size;
|
int16_t m_setupId;
|
||||||
int16_t m_groupId;
|
SongData(std::unique_ptr<uint8_t[]>&& data, size_t size, int16_t groupId, int16_t setupId)
|
||||||
int16_t m_setupId;
|
: m_data(std::move(data)), m_size(size), m_groupId(groupId), m_setupId(setupId) {}
|
||||||
SongData(std::unique_ptr<uint8_t[]>&& data, size_t size, int16_t groupId, int16_t setupId)
|
};
|
||||||
: m_data(std::move(data)), m_size(size), m_groupId(groupId), m_setupId(setupId)
|
static const SystemChar* TypeToName(Type tp);
|
||||||
{
|
static Type DetectContainerType(const SystemChar* path);
|
||||||
}
|
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path);
|
||||||
};
|
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path,
|
||||||
static const SystemChar* TypeToName(Type tp);
|
Type& typeOut);
|
||||||
static Type DetectContainerType(const SystemChar* path);
|
static std::vector<std::pair<SystemString, SongData>> LoadSongs(const SystemChar* path);
|
||||||
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path);
|
|
||||||
static std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path,
|
|
||||||
Type& typeOut);
|
|
||||||
static std::vector<std::pair<SystemString, SongData>> LoadSongs(const SystemChar* path);
|
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -3,36 +3,30 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
|
|
||||||
static inline int16_t DSPSampClamp(int32_t val)
|
static inline int16_t DSPSampClamp(int32_t val) {
|
||||||
{
|
if (val < -32768)
|
||||||
if (val < -32768) val = -32768;
|
val = -32768;
|
||||||
else if (val > 32767) val = 32767;
|
else if (val > 32767)
|
||||||
return val;
|
val = 32767;
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
|
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
|
||||||
unsigned lastSample);
|
unsigned lastSample);
|
||||||
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
|
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
|
||||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
int16_t* prev2, unsigned lastSample);
|
||||||
unsigned lastSample);
|
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
|
||||||
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
|
int16_t* prev2, unsigned lastSample);
|
||||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
|
||||||
unsigned lastSample);
|
|
||||||
|
|
||||||
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
|
||||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
int16_t* prev2, unsigned firstSample, unsigned lastSample);
|
||||||
unsigned firstSample, unsigned lastSample);
|
|
||||||
|
|
||||||
unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
|
unsigned DSPDecompressFrameStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
|
||||||
unsigned lastSample);
|
unsigned lastSample);
|
||||||
|
|
||||||
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in,
|
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
|
||||||
unsigned firstSample, unsigned lastSample);
|
unsigned firstSample, unsigned lastSample);
|
||||||
|
|
||||||
void DSPCorrelateCoefs(const short* source, int samples, short coefsOut[8][2]);
|
void DSPCorrelateCoefs(const short* source, int samples, short coefsOut[8][2]);
|
||||||
|
|
||||||
void DSPEncodeFrame(short pcmInOut[16], int sampleCount, unsigned char adpcmOut[8], const short coefsIn[8][2]);
|
void DSPEncodeFrame(short pcmInOut[16], int sampleCount, unsigned char adpcmOut[8], const short coefsIn[8][2]);
|
||||||
|
|
||||||
|
|
|
@ -3,64 +3,51 @@
|
||||||
#include "Common.hpp"
|
#include "Common.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
struct CaseInsensitiveCompare
|
struct CaseInsensitiveCompare {
|
||||||
{
|
bool operator()(std::string_view lhs, std::string_view rhs) const {
|
||||||
bool operator()(std::string_view lhs, std::string_view rhs) const
|
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
if (_stricmp(lhs.data(), rhs.data()) < 0)
|
if (_stricmp(lhs.data(), rhs.data()) < 0)
|
||||||
#else
|
#else
|
||||||
if (strcasecmp(lhs.data(), rhs.data()) < 0)
|
if (strcasecmp(lhs.data(), rhs.data()) < 0)
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const
|
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const {
|
||||||
{
|
if (_wcsicmp(lhs.data(), rhs.data()) < 0)
|
||||||
if (_wcsicmp(lhs.data(), rhs.data()) < 0)
|
return true;
|
||||||
return true;
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirectoryEnumerator
|
class DirectoryEnumerator {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
enum class Mode
|
enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted };
|
||||||
{
|
struct Entry {
|
||||||
Native,
|
SystemString m_path;
|
||||||
DirsSorted,
|
SystemString m_name;
|
||||||
FilesSorted,
|
size_t m_fileSz;
|
||||||
DirsThenFilesSorted
|
bool m_isDir;
|
||||||
};
|
|
||||||
struct Entry
|
|
||||||
{
|
|
||||||
SystemString m_path;
|
|
||||||
SystemString m_name;
|
|
||||||
size_t m_fileSz;
|
|
||||||
bool m_isDir;
|
|
||||||
|
|
||||||
Entry(const SystemString& path, const SystemChar* name, size_t sz, bool isDir)
|
Entry(const SystemString& path, const SystemChar* name, size_t sz, bool isDir)
|
||||||
: m_path(path), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
|
: m_path(path), m_name(name), m_fileSz(sz), m_isDir(isDir) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Entry> m_entries;
|
std::vector<Entry> m_entries;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
|
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
|
||||||
bool reverse = false, bool noHidden = false);
|
bool reverse = false, bool noHidden = false);
|
||||||
|
|
||||||
operator bool() const { return m_entries.size() != 0; }
|
operator bool() const { return m_entries.size() != 0; }
|
||||||
size_t size() const { return m_entries.size(); }
|
size_t size() const { return m_entries.size(); }
|
||||||
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
|
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
|
||||||
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
|
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -3,33 +3,21 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
struct ChannelMap;
|
struct ChannelMap;
|
||||||
|
|
||||||
enum class EffectType
|
enum class EffectType { Invalid, ReverbStd, ReverbHi, Delay, Chorus, EffectTypeMAX };
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
ReverbStd,
|
|
||||||
ReverbHi,
|
|
||||||
Delay,
|
|
||||||
Chorus,
|
|
||||||
EffectTypeMAX
|
|
||||||
};
|
|
||||||
|
|
||||||
class EffectBaseTypeless
|
class EffectBaseTypeless {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
virtual ~EffectBaseTypeless() = default;
|
virtual ~EffectBaseTypeless() = default;
|
||||||
virtual void resetOutputSampleRate(double sampleRate) = 0;
|
virtual void resetOutputSampleRate(double sampleRate) = 0;
|
||||||
virtual EffectType Isa() const = 0;
|
virtual EffectType Isa() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectBase : public EffectBaseTypeless
|
class EffectBase : public EffectBaseTypeless {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
virtual void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) = 0;
|
virtual void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) = 0;
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -4,121 +4,111 @@
|
||||||
#include "Common.hpp"
|
#include "Common.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectChorusImp;
|
class EffectChorusImp;
|
||||||
|
|
||||||
#define AMUSE_CHORUS_NUM_BLOCKS 3
|
#define AMUSE_CHORUS_NUM_BLOCKS 3
|
||||||
|
|
||||||
/** Parameters needed to create EffectChorus */
|
/** Parameters needed to create EffectChorus */
|
||||||
struct EffectChorusInfo
|
struct EffectChorusInfo {
|
||||||
{
|
uint32_t baseDelay = 5; /**< [5, 15] minimum value (in ms) for computed delay */
|
||||||
uint32_t baseDelay = 5; /**< [5, 15] minimum value (in ms) for computed delay */
|
uint32_t variation = 0; /**< [0, 5] time error (in ms) to set delay within */
|
||||||
uint32_t variation = 0; /**< [0, 5] time error (in ms) to set delay within */
|
uint32_t period = 500; /**< [500, 10000] time (in ms) of one delay-shift cycle */
|
||||||
uint32_t period = 500; /**< [500, 10000] time (in ms) of one delay-shift cycle */
|
|
||||||
|
|
||||||
EffectChorusInfo() = default;
|
EffectChorusInfo() = default;
|
||||||
EffectChorusInfo(uint32_t baseDelay, uint32_t variation, uint32_t period)
|
EffectChorusInfo(uint32_t baseDelay, uint32_t variation, uint32_t period)
|
||||||
: baseDelay(baseDelay), variation(variation), period(period) {}
|
: baseDelay(baseDelay), variation(variation), period(period) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Mixes the audio back into itself after continuously-varying delay */
|
/** Mixes the audio back into itself after continuously-varying delay */
|
||||||
class EffectChorus
|
class EffectChorus {
|
||||||
{
|
uint32_t x90_baseDelay; /**< [5, 15] minimum value (in ms) for computed delay */
|
||||||
uint32_t x90_baseDelay; /**< [5, 15] minimum value (in ms) for computed delay */
|
uint32_t x94_variation; /**< [0, 5] time error (in ms) to set delay within */
|
||||||
uint32_t x94_variation; /**< [0, 5] time error (in ms) to set delay within */
|
uint32_t x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
|
||||||
uint32_t x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
|
bool m_dirty = true; /**< needs update of internal parameter data */
|
||||||
bool m_dirty = true; /**< needs update of internal parameter data */
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
friend class EffectChorusImp;
|
friend class EffectChorusImp;
|
||||||
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
|
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using ImpType = EffectChorusImp<T>;
|
using ImpType = EffectChorusImp<T>;
|
||||||
|
|
||||||
void setBaseDelay(uint32_t baseDelay)
|
void setBaseDelay(uint32_t baseDelay) {
|
||||||
{
|
baseDelay = clamp(5u, baseDelay, 15u);
|
||||||
baseDelay = clamp(5u, baseDelay, 15u);
|
x90_baseDelay = baseDelay;
|
||||||
x90_baseDelay = baseDelay;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
uint32_t getBaseDelay() const { return x90_baseDelay; }
|
||||||
uint32_t getBaseDelay() const { return x90_baseDelay; }
|
|
||||||
|
|
||||||
void setVariation(uint32_t variation)
|
void setVariation(uint32_t variation) {
|
||||||
{
|
variation = clamp(0u, variation, 5u);
|
||||||
variation = clamp(0u, variation, 5u);
|
x94_variation = variation;
|
||||||
x94_variation = variation;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
uint32_t getVariation() const { return x94_variation; }
|
||||||
uint32_t getVariation() const { return x94_variation; }
|
|
||||||
|
|
||||||
void setPeriod(uint32_t period)
|
void setPeriod(uint32_t period) {
|
||||||
{
|
period = clamp(500u, period, 10000u);
|
||||||
period = clamp(500u, period, 10000u);
|
x98_period = period;
|
||||||
x98_period = period;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
uint32_t getPeriod() const { return x98_period; }
|
||||||
uint32_t getPeriod() const { return x98_period; }
|
|
||||||
|
|
||||||
void updateParams(const EffectChorusInfo& info)
|
void updateParams(const EffectChorusInfo& info) {
|
||||||
{
|
setBaseDelay(info.baseDelay);
|
||||||
setBaseDelay(info.baseDelay);
|
setVariation(info.variation);
|
||||||
setVariation(info.variation);
|
setPeriod(info.period);
|
||||||
setPeriod(info.period);
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Type-specific implementation of chorus effect */
|
/** Type-specific implementation of chorus effect */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectChorusImp : public EffectBase<T>, public EffectChorus
|
class EffectChorusImp : public EffectBase<T>, public EffectChorus {
|
||||||
{
|
T* x0_lastChans[8][AMUSE_CHORUS_NUM_BLOCKS] = {}; /**< Evenly-allocated pointer-table for each channel's delay */
|
||||||
T* x0_lastChans[8][AMUSE_CHORUS_NUM_BLOCKS] = {}; /**< Evenly-allocated pointer-table for each channel's delay */
|
|
||||||
|
|
||||||
uint8_t x24_currentLast = 1; /**< Last 5ms block-idx to be processed */
|
uint8_t x24_currentLast = 1; /**< Last 5ms block-idx to be processed */
|
||||||
T x28_oldChans[8][4] = {}; /**< Unprocessed history of previous 4 samples */
|
T x28_oldChans[8][4] = {}; /**< Unprocessed history of previous 4 samples */
|
||||||
|
|
||||||
uint32_t x58_currentPosLo = 0; /**< 16.7 fixed-point low-part of sample index */
|
uint32_t x58_currentPosLo = 0; /**< 16.7 fixed-point low-part of sample index */
|
||||||
uint32_t x5c_currentPosHi = 0; /**< 16.7 fixed-point high-part of sample index */
|
uint32_t x5c_currentPosHi = 0; /**< 16.7 fixed-point high-part of sample index */
|
||||||
|
|
||||||
int32_t x60_pitchOffset; /**< packed 16.16 fixed-point value of pitchHi and pitchLo quantities */
|
int32_t x60_pitchOffset; /**< packed 16.16 fixed-point value of pitchHi and pitchLo quantities */
|
||||||
uint32_t x64_pitchOffsetPeriodCount; /**< trigger value for flipping SRC state */
|
uint32_t x64_pitchOffsetPeriodCount; /**< trigger value for flipping SRC state */
|
||||||
uint32_t x68_pitchOffsetPeriod; /**< intermediate block window quantity for calculating SRC state */
|
uint32_t x68_pitchOffsetPeriod; /**< intermediate block window quantity for calculating SRC state */
|
||||||
|
|
||||||
struct SrcInfo
|
struct SrcInfo {
|
||||||
{
|
T* x6c_dest; /**< selected channel's live buffer */
|
||||||
T* x6c_dest; /**< selected channel's live buffer */
|
T* x70_smpBase; /**< selected channel's delay buffer */
|
||||||
T* x70_smpBase; /**< selected channel's delay buffer */
|
T* x74_old; /**< selected channel's 4-sample history buffer */
|
||||||
T* x74_old; /**< selected channel's 4-sample history buffer */
|
uint32_t x78_posLo; /**< 16.7 fixed-point low-part of sample index */
|
||||||
uint32_t x78_posLo; /**< 16.7 fixed-point low-part of sample index */
|
uint32_t x7c_posHi; /**< 16.7 fixed-point high-part of sample index */
|
||||||
uint32_t x7c_posHi; /**< 16.7 fixed-point high-part of sample index */
|
uint32_t x80_pitchLo; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
|
||||||
uint32_t x80_pitchLo; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
|
uint32_t x84_pitchHi; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
|
||||||
uint32_t x84_pitchHi; /**< 16.7 fixed-point low-part of sample-rate conversion differential */
|
uint32_t x88_trigger; /**< total count of samples per channel across all blocks */
|
||||||
uint32_t x88_trigger; /**< total count of samples per channel across all blocks */
|
uint32_t x8c_target = 0; /**< value to reset to when trigger hit */
|
||||||
uint32_t x8c_target = 0; /**< value to reset to when trigger hit */
|
|
||||||
|
|
||||||
void doSrc1(size_t blockSamples, size_t chanCount);
|
void doSrc1(size_t blockSamples, size_t chanCount);
|
||||||
void doSrc2(size_t blockSamples, size_t chanCount);
|
void doSrc2(size_t blockSamples, size_t chanCount);
|
||||||
} x6c_src;
|
} x6c_src;
|
||||||
|
|
||||||
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
||||||
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
||||||
|
|
||||||
void _setup(double sampleRate);
|
void _setup(double sampleRate);
|
||||||
void _update();
|
void _update();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~EffectChorusImp();
|
~EffectChorusImp();
|
||||||
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
|
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
|
||||||
EffectChorusImp(const EffectChorusInfo& info, double sampleRate)
|
EffectChorusImp(const EffectChorusInfo& info, double sampleRate)
|
||||||
: EffectChorusImp(info.baseDelay, info.variation, info.period, sampleRate) {}
|
: EffectChorusImp(info.baseDelay, info.variation, info.period, sampleRate) {}
|
||||||
|
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||||
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
||||||
|
|
||||||
EffectType Isa() const { return EffectType::Chorus; }
|
EffectType Isa() const { return EffectType::Chorus; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -6,138 +6,122 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectDelayImp;
|
class EffectDelayImp;
|
||||||
|
|
||||||
/** Parameters needed to create EffectDelay */
|
/** Parameters needed to create EffectDelay */
|
||||||
struct EffectDelayInfo
|
struct EffectDelayInfo {
|
||||||
{
|
uint32_t delay[8]; /**< [10, 5000] time in ms of each channel's delay */
|
||||||
uint32_t delay[8]; /**< [10, 5000] time in ms of each channel's delay */
|
uint32_t feedback[8] = {}; /**< [0, 100] percent to mix delayed signal with input signal */
|
||||||
uint32_t feedback[8] = {}; /**< [0, 100] percent to mix delayed signal with input signal */
|
uint32_t output[8] = {}; /**< [0, 100] total output percent */
|
||||||
uint32_t output[8] = {}; /**< [0, 100] total output percent */
|
|
||||||
|
|
||||||
static uint32_t lerp(uint32_t v0, uint32_t v1, float t) { return (1.f - t) * v0 + t * v1; }
|
static uint32_t lerp(uint32_t v0, uint32_t v1, float t) { return (1.f - t) * v0 + t * v1; }
|
||||||
|
|
||||||
static void Interp3To8(uint32_t arr[8], uint32_t L, uint32_t R, uint32_t S)
|
static void Interp3To8(uint32_t arr[8], uint32_t L, uint32_t R, uint32_t S) {
|
||||||
{
|
arr[int(AudioChannel::FrontLeft)] = L;
|
||||||
arr[int(AudioChannel::FrontLeft)] = L;
|
arr[int(AudioChannel::FrontRight)] = R;
|
||||||
arr[int(AudioChannel::FrontRight)] = R;
|
arr[int(AudioChannel::RearLeft)] = lerp(L, S, 0.75f);
|
||||||
arr[int(AudioChannel::RearLeft)] = lerp(L, S, 0.75f);
|
arr[int(AudioChannel::RearRight)] = lerp(R, S, 0.75f);
|
||||||
arr[int(AudioChannel::RearRight)] = lerp(R, S, 0.75f);
|
arr[int(AudioChannel::FrontCenter)] = lerp(L, R, 0.5f);
|
||||||
arr[int(AudioChannel::FrontCenter)] = lerp(L, R, 0.5f);
|
arr[int(AudioChannel::LFE)] = arr[int(AudioChannel::FrontCenter)];
|
||||||
arr[int(AudioChannel::LFE)] = arr[int(AudioChannel::FrontCenter)];
|
arr[int(AudioChannel::SideLeft)] = lerp(L, S, 0.5f);
|
||||||
arr[int(AudioChannel::SideLeft)] = lerp(L, S, 0.5f);
|
arr[int(AudioChannel::SideRight)] = lerp(R, S, 0.5f);
|
||||||
arr[int(AudioChannel::SideRight)] = lerp(R, S, 0.5f);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
EffectDelayInfo() { std::fill_n(delay, 8, 10); }
|
EffectDelayInfo() { std::fill_n(delay, 8, 10); }
|
||||||
EffectDelayInfo(uint32_t delayL, uint32_t delayR, uint32_t delayS,
|
EffectDelayInfo(uint32_t delayL, uint32_t delayR, uint32_t delayS, uint32_t feedbackL, uint32_t feedbackR,
|
||||||
uint32_t feedbackL, uint32_t feedbackR, uint32_t feedbackS,
|
uint32_t feedbackS, uint32_t outputL, uint32_t outputR, uint32_t outputS) {
|
||||||
uint32_t outputL, uint32_t outputR, uint32_t outputS)
|
Interp3To8(delay, delayL, delayR, delayS);
|
||||||
{
|
Interp3To8(feedback, feedbackL, feedbackR, feedbackS);
|
||||||
Interp3To8(delay, delayL, delayR, delayS);
|
Interp3To8(output, outputL, outputR, outputS);
|
||||||
Interp3To8(feedback, feedbackL, feedbackR, feedbackS);
|
}
|
||||||
Interp3To8(output, outputL, outputR, outputS);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Mixes the audio back into itself after specified delay */
|
/** Mixes the audio back into itself after specified delay */
|
||||||
class EffectDelay
|
class EffectDelay {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t x3c_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
|
uint32_t x3c_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
|
||||||
uint32_t x48_feedback[8]; /**< [0, 100] percent to mix delayed signal with input signal */
|
uint32_t x48_feedback[8]; /**< [0, 100] percent to mix delayed signal with input signal */
|
||||||
uint32_t x54_output[8]; /**< [0, 100] total output percent */
|
uint32_t x54_output[8]; /**< [0, 100] total output percent */
|
||||||
bool m_dirty = true; /**< needs update of internal parameter data */
|
bool m_dirty = true; /**< needs update of internal parameter data */
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using ImpType = EffectDelayImp<T>;
|
using ImpType = EffectDelayImp<T>;
|
||||||
|
|
||||||
void setDelay(uint32_t delay)
|
void setDelay(uint32_t delay) {
|
||||||
{
|
delay = clamp(10u, delay, 5000u);
|
||||||
delay = clamp(10u, delay, 5000u);
|
for (int i = 0; i < 8; ++i)
|
||||||
for (int i = 0; i < 8; ++i)
|
x3c_delay[i] = delay;
|
||||||
x3c_delay[i] = delay;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
void setChanDelay(int chanIdx, uint32_t delay) {
|
||||||
void setChanDelay(int chanIdx, uint32_t delay)
|
delay = clamp(10u, delay, 5000u);
|
||||||
{
|
x3c_delay[chanIdx] = delay;
|
||||||
delay = clamp(10u, delay, 5000u);
|
m_dirty = true;
|
||||||
x3c_delay[chanIdx] = delay;
|
}
|
||||||
m_dirty = true;
|
uint32_t getChanDelay(int chanIdx) const { return x3c_delay[chanIdx]; }
|
||||||
}
|
|
||||||
uint32_t getChanDelay(int chanIdx) const { return x3c_delay[chanIdx]; }
|
|
||||||
|
|
||||||
void setFeedback(uint32_t feedback)
|
void setFeedback(uint32_t feedback) {
|
||||||
{
|
feedback = clamp(0u, feedback, 100u);
|
||||||
feedback = clamp(0u, feedback, 100u);
|
for (int i = 0; i < 8; ++i)
|
||||||
for (int i = 0; i < 8; ++i)
|
x48_feedback[i] = feedback;
|
||||||
x48_feedback[i] = feedback;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void setChanFeedback(int chanIdx, uint32_t feedback)
|
void setChanFeedback(int chanIdx, uint32_t feedback) {
|
||||||
{
|
feedback = clamp(0u, feedback, 100u);
|
||||||
feedback = clamp(0u, feedback, 100u);
|
x48_feedback[chanIdx] = feedback;
|
||||||
x48_feedback[chanIdx] = feedback;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
uint32_t getChanFeedback(int chanIdx) const { return x48_feedback[chanIdx]; }
|
||||||
uint32_t getChanFeedback(int chanIdx) const { return x48_feedback[chanIdx]; }
|
|
||||||
|
|
||||||
void setOutput(uint32_t output)
|
void setOutput(uint32_t output) {
|
||||||
{
|
output = clamp(0u, output, 100u);
|
||||||
output = clamp(0u, output, 100u);
|
for (int i = 0; i < 8; ++i)
|
||||||
for (int i = 0; i < 8; ++i)
|
x54_output[i] = output;
|
||||||
x54_output[i] = output;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void setChanOutput(int chanIdx, uint32_t output)
|
void setChanOutput(int chanIdx, uint32_t output) {
|
||||||
{
|
output = clamp(0u, output, 100u);
|
||||||
output = clamp(0u, output, 100u);
|
x54_output[chanIdx] = output;
|
||||||
x54_output[chanIdx] = output;
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
uint32_t getChanOutput(int chanIdx) const { return x54_output[chanIdx]; }
|
||||||
uint32_t getChanOutput(int chanIdx) const { return x54_output[chanIdx]; }
|
|
||||||
|
|
||||||
void setParams(const EffectDelayInfo& info)
|
void setParams(const EffectDelayInfo& info) {
|
||||||
{
|
for (int i = 0; i < 8; ++i) {
|
||||||
for (int i = 0; i < 8; ++i)
|
x3c_delay[i] = clamp(10u, info.delay[i], 5000u);
|
||||||
{
|
x48_feedback[i] = clamp(0u, info.feedback[i], 100u);
|
||||||
x3c_delay[i] = clamp(10u, info.delay[i], 5000u);
|
x54_output[i] = clamp(0u, info.output[i], 100u);
|
||||||
x48_feedback[i] = clamp(0u, info.feedback[i], 100u);
|
|
||||||
x54_output[i] = clamp(0u, info.output[i], 100u);
|
|
||||||
}
|
|
||||||
m_dirty = true;
|
|
||||||
}
|
}
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Type-specific implementation of delay effect */
|
/** Type-specific implementation of delay effect */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectDelayImp : public EffectBase<T>, public EffectDelay
|
class EffectDelayImp : public EffectBase<T>, public EffectDelay {
|
||||||
{
|
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
|
||||||
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
|
uint32_t xc_currentPos[8]; /**< per-channel block-index */
|
||||||
uint32_t xc_currentPos[8]; /**< per-channel block-index */
|
uint32_t x18_currentFeedback[8]; /**< [0, 128] feedback attenuator */
|
||||||
uint32_t x18_currentFeedback[8]; /**< [0, 128] feedback attenuator */
|
uint32_t x24_currentOutput[8]; /**< [0, 128] total attenuator */
|
||||||
uint32_t x24_currentOutput[8]; /**< [0, 128] total attenuator */
|
|
||||||
|
|
||||||
std::unique_ptr<T[]> x30_chanLines[8]; /**< delay-line buffers for each channel */
|
std::unique_ptr<T[]> x30_chanLines[8]; /**< delay-line buffers for each channel */
|
||||||
|
|
||||||
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
||||||
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
||||||
void _setup(double sampleRate);
|
void _setup(double sampleRate);
|
||||||
void _update();
|
void _update();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
|
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
|
||||||
EffectDelayImp(const EffectDelayInfo& info, double sampleRate);
|
EffectDelayImp(const EffectDelayInfo& info, double sampleRate);
|
||||||
|
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||||
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
||||||
|
|
||||||
EffectType Isa() const { return EffectType::Delay; }
|
EffectType Isa() const { return EffectType::Delay; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -4,49 +4,45 @@
|
||||||
#include "amuse/Common.hpp"
|
#include "amuse/Common.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
/** Parameters needed to create EffectReverbStd */
|
/** Parameters needed to create EffectReverbStd */
|
||||||
struct EffectReverbStdInfo
|
struct EffectReverbStdInfo {
|
||||||
{
|
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
|
||||||
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
|
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
||||||
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
|
||||||
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
|
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
||||||
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
||||||
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
|
||||||
|
|
||||||
EffectReverbStdInfo() = default;
|
EffectReverbStdInfo() = default;
|
||||||
EffectReverbStdInfo(float coloration, float mix, float time, float damping, float preDelay)
|
EffectReverbStdInfo(float coloration, float mix, float time, float damping, float preDelay)
|
||||||
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay) {}
|
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Parameters needed to create EffectReverbHi */
|
/** Parameters needed to create EffectReverbHi */
|
||||||
struct EffectReverbHiInfo
|
struct EffectReverbHiInfo {
|
||||||
{
|
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
|
||||||
float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
|
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
||||||
float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
|
||||||
float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */
|
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
||||||
float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
||||||
float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
float crosstalk = 0.f; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
|
||||||
float crosstalk = 0.f; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
|
|
||||||
|
|
||||||
EffectReverbHiInfo() = default;
|
EffectReverbHiInfo() = default;
|
||||||
EffectReverbHiInfo(float coloration, float mix, float time, float damping, float preDelay, float crosstalk)
|
EffectReverbHiInfo(float coloration, float mix, float time, float damping, float preDelay, float crosstalk)
|
||||||
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay), crosstalk(crosstalk) {}
|
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay), crosstalk(crosstalk) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Delay state for one 'tap' of the reverb effect */
|
/** Delay state for one 'tap' of the reverb effect */
|
||||||
struct ReverbDelayLine
|
struct ReverbDelayLine {
|
||||||
{
|
int32_t x0_inPoint = 0;
|
||||||
int32_t x0_inPoint = 0;
|
int32_t x4_outPoint = 0;
|
||||||
int32_t x4_outPoint = 0;
|
int32_t x8_length = 0;
|
||||||
int32_t x8_length = 0;
|
std::unique_ptr<float[]> xc_inputs;
|
||||||
std::unique_ptr<float[]> xc_inputs;
|
float x10_lastInput = 0.f;
|
||||||
float x10_lastInput = 0.f;
|
|
||||||
|
|
||||||
void allocate(int32_t delay);
|
void allocate(int32_t delay);
|
||||||
void setdelay(int32_t delay);
|
void setdelay(int32_t delay);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -56,166 +52,153 @@ template <typename T>
|
||||||
class EffectReverbHiImp;
|
class EffectReverbHiImp;
|
||||||
|
|
||||||
/** Reverb effect with configurable reflection filtering */
|
/** Reverb effect with configurable reflection filtering */
|
||||||
class EffectReverbStd
|
class EffectReverbStd {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a
|
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a
|
||||||
room */
|
room */
|
||||||
float x144_x1cc_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
float x144_x1cc_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
||||||
float x148_x1d0_time; /**< [0.01, 10.0] time in seconds for reflection decay */
|
float x148_x1d0_time; /**< [0.01, 10.0] time in seconds for reflection decay */
|
||||||
float x14c_x1d4_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
float x14c_x1d4_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
||||||
float x150_x1d8_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
float x150_x1d8_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
||||||
bool m_dirty = true; /**< needs update of internal parameter data */
|
bool m_dirty = true; /**< needs update of internal parameter data */
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
friend class EffectReverbStdImp;
|
friend class EffectReverbStdImp;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
friend class EffectReverbHiImp;
|
friend class EffectReverbHiImp;
|
||||||
EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay);
|
EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using ImpType = EffectReverbStdImp<T>;
|
using ImpType = EffectReverbStdImp<T>;
|
||||||
|
|
||||||
void setColoration(float coloration)
|
void setColoration(float coloration) {
|
||||||
{
|
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
|
||||||
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
float getColoration() const { return x140_x1c8_coloration; }
|
||||||
float getColoration() const { return x140_x1c8_coloration; }
|
|
||||||
|
|
||||||
void setMix(float mix)
|
void setMix(float mix) {
|
||||||
{
|
x144_x1cc_mix = clamp(0.f, mix, 1.f);
|
||||||
x144_x1cc_mix = clamp(0.f, mix, 1.f);
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
float getMix() const { return x144_x1cc_mix; }
|
||||||
float getMix() const { return x144_x1cc_mix; }
|
|
||||||
|
|
||||||
void setTime(float time)
|
void setTime(float time) {
|
||||||
{
|
x148_x1d0_time = clamp(0.01f, time, 10.f);
|
||||||
x148_x1d0_time = clamp(0.01f, time, 10.f);
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
float getTime() const { return x148_x1d0_time; }
|
||||||
float getTime() const { return x148_x1d0_time; }
|
|
||||||
|
|
||||||
void setDamping(float damping)
|
void setDamping(float damping) {
|
||||||
{
|
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
|
||||||
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
float getDamping() const { return x14c_x1d4_damping; }
|
||||||
float getDamping() const { return x14c_x1d4_damping; }
|
|
||||||
|
|
||||||
void setPreDelay(float preDelay)
|
void setPreDelay(float preDelay) {
|
||||||
{
|
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
|
||||||
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
float getPreDelay() const { return x150_x1d8_preDelay; }
|
||||||
float getPreDelay() const { return x150_x1d8_preDelay; }
|
|
||||||
|
|
||||||
void setParams(const EffectReverbStdInfo& info)
|
void setParams(const EffectReverbStdInfo& info) {
|
||||||
{
|
setColoration(info.coloration);
|
||||||
setColoration(info.coloration);
|
setMix(info.mix);
|
||||||
setMix(info.mix);
|
setTime(info.time);
|
||||||
setTime(info.time);
|
setDamping(info.damping);
|
||||||
setDamping(info.damping);
|
setPreDelay(info.preDelay);
|
||||||
setPreDelay(info.preDelay);
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Reverb effect with configurable reflection filtering, adds per-channel low-pass and crosstalk */
|
/** Reverb effect with configurable reflection filtering, adds per-channel low-pass and crosstalk */
|
||||||
class EffectReverbHi : public EffectReverbStd
|
class EffectReverbHi : public EffectReverbStd {
|
||||||
{
|
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
|
||||||
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
friend class EffectReverbHiImp;
|
friend class EffectReverbHiImp;
|
||||||
EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
|
EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using ImpType = EffectReverbHiImp<T>;
|
using ImpType = EffectReverbHiImp<T>;
|
||||||
|
|
||||||
void setCrosstalk(float crosstalk)
|
void setCrosstalk(float crosstalk) {
|
||||||
{
|
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
|
||||||
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
|
m_dirty = true;
|
||||||
m_dirty = true;
|
}
|
||||||
}
|
float getCrosstalk() const { return x1dc_crosstalk; }
|
||||||
float getCrosstalk() const { return x1dc_crosstalk; }
|
|
||||||
|
|
||||||
void setParams(const EffectReverbHiInfo& info)
|
void setParams(const EffectReverbHiInfo& info) {
|
||||||
{
|
setColoration(info.coloration);
|
||||||
setColoration(info.coloration);
|
setMix(info.mix);
|
||||||
setMix(info.mix);
|
setTime(info.time);
|
||||||
setTime(info.time);
|
setDamping(info.damping);
|
||||||
setDamping(info.damping);
|
setPreDelay(info.preDelay);
|
||||||
setPreDelay(info.preDelay);
|
setCrosstalk(info.crosstalk);
|
||||||
setCrosstalk(info.crosstalk);
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Standard-quality 2-stage reverb */
|
/** Standard-quality 2-stage reverb */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectReverbStdImp : public EffectBase<T>, public EffectReverbStd
|
class EffectReverbStdImp : public EffectBase<T>, public EffectReverbStd {
|
||||||
{
|
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
|
||||||
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
|
ReverbDelayLine x78_C[8][2] = {}; /**< Comb delay lines */
|
||||||
ReverbDelayLine x78_C[8][2] = {}; /**< Comb delay lines */
|
float xf0_allPassCoef = 0.f; /**< All-pass mix coefficient */
|
||||||
float xf0_allPassCoef = 0.f; /**< All-pass mix coefficient */
|
float xf4_combCoef[8][2] = {}; /**< Comb mix coefficients */
|
||||||
float xf4_combCoef[8][2] = {}; /**< Comb mix coefficients */
|
float x10c_lpLastout[8] = {}; /**< Last low-pass results */
|
||||||
float x10c_lpLastout[8] = {}; /**< Last low-pass results */
|
float x118_level = 0.f; /**< Internal wet/dry mix factor */
|
||||||
float x118_level = 0.f; /**< Internal wet/dry mix factor */
|
float x11c_damping = 0.f; /**< Low-pass damping */
|
||||||
float x11c_damping = 0.f; /**< Low-pass damping */
|
int32_t x120_preDelayTime = 0; /**< Sample count of pre-delay */
|
||||||
int32_t x120_preDelayTime = 0; /**< Sample count of pre-delay */
|
std::unique_ptr<float[]> x124_preDelayLine[8]; /**< Dedicated pre-delay buffers */
|
||||||
std::unique_ptr<float[]> x124_preDelayLine[8]; /**< Dedicated pre-delay buffers */
|
float* x130_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
||||||
float* x130_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
|
||||||
|
|
||||||
double m_sampleRate; /**< copy of sample rate */
|
double m_sampleRate; /**< copy of sample rate */
|
||||||
void _setup(double sampleRate);
|
void _setup(double sampleRate);
|
||||||
void _update();
|
void _update();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay, double sampleRate);
|
EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay, double sampleRate);
|
||||||
EffectReverbStdImp(const EffectReverbStdInfo& info, double sampleRate)
|
EffectReverbStdImp(const EffectReverbStdInfo& info, double sampleRate)
|
||||||
: EffectReverbStdImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, sampleRate) {}
|
: EffectReverbStdImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, sampleRate) {}
|
||||||
|
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||||
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
||||||
|
|
||||||
EffectType Isa() const { return EffectType::ReverbStd; }
|
EffectType Isa() const { return EffectType::ReverbStd; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** High-quality 3-stage reverb with per-channel low-pass and crosstalk */
|
/** High-quality 3-stage reverb with per-channel low-pass and crosstalk */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi
|
class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi {
|
||||||
{
|
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
|
||||||
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
|
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
|
||||||
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
|
ReverbDelayLine xb4_C[8][3] = {}; /**< Comb delay lines */
|
||||||
ReverbDelayLine xb4_C[8][3] = {}; /**< Comb delay lines */
|
float x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
|
||||||
float x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
|
float x16c_combCoef[8][3] = {}; /**< Comb mix coefficients */
|
||||||
float x16c_combCoef[8][3] = {}; /**< Comb mix coefficients */
|
float x190_lpLastout[8] = {}; /**< Last low-pass results */
|
||||||
float x190_lpLastout[8] = {}; /**< Last low-pass results */
|
float x19c_level = 0.f; /**< Internal wet/dry mix factor */
|
||||||
float x19c_level = 0.f; /**< Internal wet/dry mix factor */
|
float x1a0_damping = 0.f; /**< Low-pass damping */
|
||||||
float x1a0_damping = 0.f; /**< Low-pass damping */
|
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
|
||||||
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
|
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
|
||||||
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
|
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
||||||
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
float x1a8_internalCrosstalk = 0.f;
|
||||||
float x1a8_internalCrosstalk = 0.f;
|
|
||||||
|
|
||||||
double m_sampleRate; /**< copy of sample rate */
|
double m_sampleRate; /**< copy of sample rate */
|
||||||
void _setup(double sampleRate);
|
void _setup(double sampleRate);
|
||||||
void _update();
|
void _update();
|
||||||
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
|
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
|
||||||
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
|
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay, float crosstalk,
|
EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay, float crosstalk,
|
||||||
double sampleRate);
|
double sampleRate);
|
||||||
EffectReverbHiImp(const EffectReverbHiInfo& info, double sampleRate)
|
EffectReverbHiImp(const EffectReverbHiInfo& info, double sampleRate)
|
||||||
: EffectReverbHiImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, info.crosstalk, sampleRate) {}
|
: EffectReverbHiImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, info.crosstalk, sampleRate) {}
|
||||||
|
|
||||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||||
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
||||||
|
|
||||||
EffectType Isa() const { return EffectType::ReverbHi; }
|
EffectType Isa() const { return EffectType::ReverbHi; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -7,60 +7,58 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class Listener;
|
class Listener;
|
||||||
|
|
||||||
using Vector3f = float[3];
|
using Vector3f = float[3];
|
||||||
|
|
||||||
static inline float Dot(const Vector3f& a, const Vector3f& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
|
static inline float Dot(const Vector3f& a, const Vector3f& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
|
||||||
|
|
||||||
static inline float Length(const Vector3f& a)
|
static inline float Length(const Vector3f& a) {
|
||||||
{
|
if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON)
|
||||||
if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON)
|
return 0.f;
|
||||||
return 0.f;
|
return std::sqrt(Dot(a, a));
|
||||||
return std::sqrt(Dot(a, a));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline float Normalize(Vector3f& out)
|
static inline float Normalize(Vector3f& out) {
|
||||||
{
|
float dist = Length(out);
|
||||||
float dist = Length(out);
|
if (dist == 0.f)
|
||||||
if (dist == 0.f)
|
return 0.f;
|
||||||
return 0.f;
|
out[0] /= dist;
|
||||||
out[0] /= dist;
|
out[1] /= dist;
|
||||||
out[1] /= dist;
|
out[2] /= dist;
|
||||||
out[2] /= dist;
|
return dist;
|
||||||
return dist;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Voice wrapper with positional-3D level control */
|
/** Voice wrapper with positional-3D level control */
|
||||||
class Emitter : public Entity
|
class Emitter : public Entity {
|
||||||
{
|
ObjToken<Voice> m_vox;
|
||||||
ObjToken<Voice> m_vox;
|
Vector3f m_pos = {};
|
||||||
Vector3f m_pos = {};
|
Vector3f m_dir = {};
|
||||||
Vector3f m_dir = {};
|
float m_maxDist;
|
||||||
float m_maxDist;
|
float m_maxVol = 1.f;
|
||||||
float m_maxVol = 1.f;
|
float m_minVol;
|
||||||
float m_minVol;
|
float m_falloff;
|
||||||
float m_falloff;
|
bool m_doppler;
|
||||||
bool m_doppler;
|
bool m_dirty = true;
|
||||||
bool m_dirty = true;
|
Voice::VolumeCache m_attCache;
|
||||||
Voice::VolumeCache m_attCache;
|
|
||||||
|
|
||||||
friend class Engine;
|
friend class Engine;
|
||||||
void _destroy();
|
void _destroy();
|
||||||
float _attenuationCurve(float dist) const;
|
float _attenuationCurve(float dist) const;
|
||||||
void _update();
|
void _update();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Emitter();
|
~Emitter();
|
||||||
Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox,
|
Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox, float maxDist, float minVol, float falloff,
|
||||||
float maxDist, float minVol, float falloff, bool doppler);
|
bool doppler);
|
||||||
|
|
||||||
void setVectors(const float* pos, const float* dir);
|
void setVectors(const float* pos, const float* dir);
|
||||||
void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); m_dirty = true; }
|
void setMaxVol(float maxVol) {
|
||||||
|
m_maxVol = clamp(0.f, maxVol, 1.f);
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
ObjToken<Voice> getVoice() const { return m_vox; }
|
ObjToken<Voice> getVoice() const { return m_vox; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,7 @@
|
||||||
#include "Studio.hpp"
|
#include "Studio.hpp"
|
||||||
#include "IBackendVoiceAllocator.hpp"
|
#include "IBackendVoiceAllocator.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class IBackendVoiceAllocator;
|
class IBackendVoiceAllocator;
|
||||||
class Voice;
|
class Voice;
|
||||||
class Submix;
|
class Submix;
|
||||||
|
@ -22,175 +21,162 @@ class AudioGroup;
|
||||||
class AudioGroupData;
|
class AudioGroupData;
|
||||||
class IMIDIReader;
|
class IMIDIReader;
|
||||||
|
|
||||||
enum class AmplitudeMode
|
enum class AmplitudeMode {
|
||||||
{
|
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
|
||||||
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
|
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
|
||||||
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Main audio playback system for a single audio output */
|
/** Main audio playback system for a single audio output */
|
||||||
class Engine
|
class Engine {
|
||||||
{
|
friend class Voice;
|
||||||
friend class Voice;
|
friend class Emitter;
|
||||||
friend class Emitter;
|
friend class Sequencer;
|
||||||
friend class Sequencer;
|
friend class Studio;
|
||||||
friend class Studio;
|
friend struct Sequencer::ChannelState;
|
||||||
friend struct Sequencer::ChannelState;
|
|
||||||
|
|
||||||
IBackendVoiceAllocator& m_backend;
|
IBackendVoiceAllocator& m_backend;
|
||||||
AmplitudeMode m_ampMode;
|
AmplitudeMode m_ampMode;
|
||||||
std::unique_ptr<IMIDIReader> m_midiReader;
|
std::unique_ptr<IMIDIReader> m_midiReader;
|
||||||
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
|
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
|
||||||
std::list<ObjToken<Voice>> m_activeVoices;
|
std::list<ObjToken<Voice>> m_activeVoices;
|
||||||
std::list<ObjToken<Emitter>> m_activeEmitters;
|
std::list<ObjToken<Emitter>> m_activeEmitters;
|
||||||
std::list<ObjToken<Listener>> m_activeListeners;
|
std::list<ObjToken<Listener>> m_activeListeners;
|
||||||
std::list<ObjToken<Sequencer>> m_activeSequencers;
|
std::list<ObjToken<Sequencer>> m_activeSequencers;
|
||||||
bool m_defaultStudioReady = false;
|
bool m_defaultStudioReady = false;
|
||||||
ObjToken<Studio> m_defaultStudio;
|
ObjToken<Studio> m_defaultStudio;
|
||||||
std::unordered_map<SFXId, std::tuple<AudioGroup*, GroupId, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
|
std::unordered_map<SFXId, std::tuple<AudioGroup*, GroupId, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
|
||||||
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
||||||
int m_nextVid = 0;
|
int m_nextVid = 0;
|
||||||
float m_masterVolume = 1.f;
|
float m_masterVolume = 1.f;
|
||||||
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
|
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
|
||||||
|
|
||||||
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
|
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
|
||||||
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(GroupId groupId) const;
|
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(GroupId groupId) const;
|
||||||
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(GroupId groupId) const;
|
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(GroupId groupId) const;
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator _allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
|
std::list<ObjToken<Voice>>::iterator _allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
|
||||||
bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
|
bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
|
||||||
std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, GroupId groupId,
|
std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, GroupId groupId, SongId setupId,
|
||||||
SongId setupId, ObjToken<Studio> studio);
|
ObjToken<Studio> studio);
|
||||||
ObjToken<Studio> _allocateStudio(bool mainOut);
|
ObjToken<Studio> _allocateStudio(bool mainOut);
|
||||||
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
|
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
|
||||||
std::list<ObjToken<Sequencer>>::iterator
|
std::list<ObjToken<Sequencer>>::iterator _destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
|
||||||
_destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
|
void _bringOutYourDead();
|
||||||
void _bringOutYourDead();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Engine();
|
~Engine();
|
||||||
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
|
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
|
||||||
|
|
||||||
/** Access voice backend of engine */
|
/** Access voice backend of engine */
|
||||||
IBackendVoiceAllocator& getBackend() { return m_backend; }
|
IBackendVoiceAllocator& getBackend() { return m_backend; }
|
||||||
|
|
||||||
/** Access MIDI reader */
|
/** Access MIDI reader */
|
||||||
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
|
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
|
||||||
|
|
||||||
/** Add audio group data pointers to engine; must remain resident! */
|
/** Add audio group data pointers to engine; must remain resident! */
|
||||||
const AudioGroup* addAudioGroup(const AudioGroupData& data);
|
const AudioGroup* addAudioGroup(const AudioGroupData& data);
|
||||||
|
|
||||||
/** Remove audio group from engine */
|
/** Remove audio group from engine */
|
||||||
void removeAudioGroup(const AudioGroupData& data);
|
void removeAudioGroup(const AudioGroupData& data);
|
||||||
|
|
||||||
/** Access engine's default studio */
|
/** Access engine's default studio */
|
||||||
ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
|
ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
|
||||||
|
|
||||||
/** Create new Studio within engine */
|
/** Create new Studio within engine */
|
||||||
ObjToken<Studio> addStudio(bool mainOut);
|
ObjToken<Studio> addStudio(bool mainOut);
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups */
|
/** Start soundFX playing from loaded audio groups */
|
||||||
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx);
|
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx);
|
||||||
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan)
|
ObjToken<Voice> fxStart(SFXId sfxId, float vol, float pan) { return fxStart(sfxId, vol, pan, m_defaultStudio); }
|
||||||
{
|
|
||||||
return fxStart(sfxId, vol, pan, m_defaultStudio);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Start soundFX playing from explicit group data (for editor use) */
|
/** Start soundFX playing from explicit group data (for editor use) */
|
||||||
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan, ObjToken<Studio> smx);
|
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan,
|
||||||
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan)
|
ObjToken<Studio> smx);
|
||||||
{
|
ObjToken<Voice> fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan) {
|
||||||
return fxStart(group, groupId, sfxId, vol, pan, m_defaultStudio);
|
return fxStart(group, groupId, sfxId, vol, pan, m_defaultStudio);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start SoundMacro node playing directly (for editor use) */
|
/** Start SoundMacro node playing directly (for editor use) */
|
||||||
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
|
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod,
|
||||||
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
|
ObjToken<Studio> smx);
|
||||||
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
|
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod) {
|
||||||
uint8_t vel, uint8_t mod)
|
return macroStart(group, id, key, vel, mod, m_defaultStudio);
|
||||||
{
|
}
|
||||||
return macroStart(group, id, key, vel, mod, m_defaultStudio);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Start SoundMacro object playing directly (for editor use) */
|
/** Start SoundMacro object playing directly (for editor use) */
|
||||||
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
|
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod,
|
||||||
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
|
ObjToken<Studio> smx);
|
||||||
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
|
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod) {
|
||||||
uint8_t vel, uint8_t mod)
|
return macroStart(group, macro, key, vel, mod, m_defaultStudio);
|
||||||
{
|
}
|
||||||
return macroStart(group, macro, key, vel, mod, m_defaultStudio);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Start PageObject node playing directly (for editor use) */
|
/** Start PageObject node playing directly (for editor use) */
|
||||||
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
|
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod,
|
||||||
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
|
ObjToken<Studio> smx);
|
||||||
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
|
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod) {
|
||||||
uint8_t vel, uint8_t mod)
|
return pageObjectStart(group, id, key, vel, mod, m_defaultStudio);
|
||||||
{
|
}
|
||||||
return pageObjectStart(group, id, key, vel, mod, m_defaultStudio);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||||
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
|
||||||
SFXId sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
|
float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
|
||||||
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
|
||||||
SFXId sfxId, float minVol, float maxVol, bool doppler)
|
float minVol, float maxVol, bool doppler) {
|
||||||
{
|
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
|
||||||
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Build listener and add to engine's listener list */
|
/** Build listener and add to engine's listener list */
|
||||||
ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
|
ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
|
||||||
float frontDiff, float backDiff, float soundSpeed, float volume);
|
float frontDiff, float backDiff, float soundSpeed, float volume);
|
||||||
|
|
||||||
/** Remove listener from engine's listener list */
|
/** Remove listener from engine's listener list */
|
||||||
void removeListener(Listener* listener);
|
void removeListener(Listener* listener);
|
||||||
|
|
||||||
/** Start song playing from loaded audio groups */
|
/** Start song playing from loaded audio groups */
|
||||||
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop, ObjToken<Studio> smx);
|
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop,
|
||||||
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop = true)
|
ObjToken<Studio> smx);
|
||||||
{
|
ObjToken<Sequencer> seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop = true) {
|
||||||
return seqPlay(groupId, songId, arrData, loop, m_defaultStudio);
|
return seqPlay(groupId, songId, arrData, loop, m_defaultStudio);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start song playing from explicit group data (for editor use) */
|
/** Start song playing from explicit group data (for editor use) */
|
||||||
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData, bool loop, ObjToken<Studio> smx);
|
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData,
|
||||||
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData, bool loop = true)
|
bool loop, ObjToken<Studio> smx);
|
||||||
{
|
ObjToken<Sequencer> seqPlay(const AudioGroup* group, GroupId groupId, SongId songId, const unsigned char* arrData,
|
||||||
return seqPlay(group, groupId, songId, arrData, loop, m_defaultStudio);
|
bool loop = true) {
|
||||||
}
|
return seqPlay(group, groupId, songId, arrData, loop, m_defaultStudio);
|
||||||
|
}
|
||||||
|
|
||||||
/** Set total volume of engine */
|
/** Set total volume of engine */
|
||||||
void setVolume(float vol);
|
void setVolume(float vol);
|
||||||
|
|
||||||
/** Find voice from VoiceId */
|
/** Find voice from VoiceId */
|
||||||
ObjToken<Voice> findVoice(int vid);
|
ObjToken<Voice> findVoice(int vid);
|
||||||
|
|
||||||
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
||||||
void killKeygroup(uint8_t kg, bool now);
|
void killKeygroup(uint8_t kg, bool now);
|
||||||
|
|
||||||
/** Send all voices using `macroId` the message `val` */
|
/** Send all voices using `macroId` the message `val` */
|
||||||
void sendMacroMessage(ObjectId macroId, int32_t val);
|
void sendMacroMessage(ObjectId macroId, int32_t val);
|
||||||
|
|
||||||
/** Obtain next random number from engine's PRNG */
|
/** Obtain next random number from engine's PRNG */
|
||||||
uint32_t nextRandom() { return m_random(); }
|
uint32_t nextRandom() { return m_random(); }
|
||||||
|
|
||||||
/** Obtain list of active voices */
|
/** Obtain list of active voices */
|
||||||
std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
|
std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
|
||||||
|
|
||||||
/** Obtain total active voice count (including child voices) */
|
/** Obtain total active voice count (including child voices) */
|
||||||
size_t getNumTotalActiveVoices() const;
|
size_t getNumTotalActiveVoices() const;
|
||||||
|
|
||||||
/** Obtain list of active sequencers */
|
/** Obtain list of active sequencers */
|
||||||
std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
|
std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
|
||||||
|
|
||||||
/** All mixing occurs in virtual 5ms intervals;
|
/** All mixing occurs in virtual 5ms intervals;
|
||||||
* this is called at the start of each interval for all mixable entities */
|
* this is called at the start of each interval for all mixable entities */
|
||||||
void _on5MsInterval(IBackendVoiceAllocator& engine, double dt);
|
void _on5MsInterval(IBackendVoiceAllocator& engine, double dt);
|
||||||
|
|
||||||
/** When a pumping cycle is complete this is called to allow the client to
|
/** When a pumping cycle is complete this is called to allow the client to
|
||||||
* perform periodic cleanup tasks */
|
* perform periodic cleanup tasks */
|
||||||
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
|
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -5,47 +5,40 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include "Common.hpp"
|
#include "Common.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class Engine;
|
class Engine;
|
||||||
class AudioGroup;
|
class AudioGroup;
|
||||||
|
|
||||||
/** Common 'engine child' class */
|
/** Common 'engine child' class */
|
||||||
class Entity : public IObj
|
class Entity : public IObj {
|
||||||
{
|
/* Only the Engine will manage Entity lifetimes,
|
||||||
/* Only the Engine will manage Entity lifetimes,
|
* but shared_ptrs are issued to the client so it can safely track state */
|
||||||
* but shared_ptrs are issued to the client so it can safely track state */
|
friend class Engine;
|
||||||
friend class Engine;
|
friend struct SoundMacroState;
|
||||||
friend struct SoundMacroState;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool m_destroyed = false;
|
bool m_destroyed = false;
|
||||||
void _destroy()
|
void _destroy() {
|
||||||
{
|
assert(!m_destroyed);
|
||||||
assert(!m_destroyed);
|
m_destroyed = true;
|
||||||
m_destroyed = true;
|
}
|
||||||
}
|
Engine& m_engine;
|
||||||
Engine& m_engine;
|
const AudioGroup& m_audioGroup;
|
||||||
const AudioGroup& m_audioGroup;
|
GroupId m_groupId;
|
||||||
GroupId m_groupId;
|
ObjectId m_objectId; /* if applicable */
|
||||||
ObjectId m_objectId; /* if applicable */
|
|
||||||
public:
|
public:
|
||||||
Entity(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid = ObjectId())
|
Entity(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid = ObjectId())
|
||||||
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid)
|
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid) {}
|
||||||
{
|
~Entity() {
|
||||||
}
|
/* Ensure proper destruction procedure followed */
|
||||||
~Entity()
|
assert(m_destroyed);
|
||||||
{
|
}
|
||||||
/* Ensure proper destruction procedure followed */
|
|
||||||
assert(m_destroyed);
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine& getEngine() { return m_engine; }
|
Engine& getEngine() { return m_engine; }
|
||||||
const AudioGroup& getAudioGroup() const { return m_audioGroup; }
|
const AudioGroup& getAudioGroup() const { return m_audioGroup; }
|
||||||
GroupId getGroupId() const { return m_groupId; }
|
GroupId getGroupId() const { return m_groupId; }
|
||||||
ObjectId getObjectId() const { return m_objectId; }
|
ObjectId getObjectId() const { return m_objectId; }
|
||||||
bool isDestroyed() const { return m_destroyed; }
|
bool isDestroyed() const { return m_destroyed; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -2,42 +2,32 @@
|
||||||
|
|
||||||
#include "AudioGroupPool.hpp"
|
#include "AudioGroupPool.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class Voice;
|
class Voice;
|
||||||
|
|
||||||
/** Per-sample state tracker for ADSR envelope data */
|
/** Per-sample state tracker for ADSR envelope data */
|
||||||
class Envelope
|
class Envelope {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
enum class State
|
enum class State { Attack, Decay, Sustain, Release, Complete };
|
||||||
{
|
|
||||||
Attack,
|
|
||||||
Decay,
|
|
||||||
Sustain,
|
|
||||||
Release,
|
|
||||||
Complete
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
State m_phase = State::Attack; /**< Current envelope state */
|
State m_phase = State::Attack; /**< Current envelope state */
|
||||||
double m_attackTime = 0.01; /**< Time of attack in seconds */
|
double m_attackTime = 0.01; /**< Time of attack in seconds */
|
||||||
double m_decayTime = 0.0; /**< Time of decay in seconds */
|
double m_decayTime = 0.0; /**< Time of decay in seconds */
|
||||||
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
|
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
|
||||||
double m_releaseTime = 0.01; /**< Time of release in seconds */
|
double m_releaseTime = 0.01; /**< Time of release in seconds */
|
||||||
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
|
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
|
||||||
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
|
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
|
||||||
bool m_adsrSet = false;
|
bool m_adsrSet = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void reset(const ADSR* adsr);
|
void reset(const ADSR* adsr);
|
||||||
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
|
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
|
||||||
void keyOff(const Voice& vox);
|
void keyOff(const Voice& vox);
|
||||||
void keyOff();
|
void keyOff();
|
||||||
float advance(double dt, const Voice& vox);
|
float advance(double dt, const Voice& vox);
|
||||||
float advance(double dt);
|
float advance(double dt);
|
||||||
bool isComplete() const { return m_phase == State::Complete; }
|
bool isComplete() const { return m_phase == State::Complete; }
|
||||||
bool isAdsrSet() const { return m_adsrSet; }
|
bool isAdsrSet() const { return m_adsrSet; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -2,32 +2,24 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class IBackendVoice;
|
class IBackendVoice;
|
||||||
class Voice;
|
class Voice;
|
||||||
|
|
||||||
enum class SubmixFormat
|
enum class SubmixFormat { Int16, Int32, Float };
|
||||||
{
|
|
||||||
Int16,
|
|
||||||
Int32,
|
|
||||||
Float
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Client-implemented submix instance */
|
/** Client-implemented submix instance */
|
||||||
class IBackendSubmix
|
class IBackendSubmix {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
virtual ~IBackendSubmix() = default;
|
virtual ~IBackendSubmix() = default;
|
||||||
|
|
||||||
/** Set send level for submix (AudioChannel enum for array index) */
|
/** Set send level for submix (AudioChannel enum for array index) */
|
||||||
virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew) = 0;
|
virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew) = 0;
|
||||||
|
|
||||||
/** Amuse gets fixed sample rate of submix this way */
|
/** Amuse gets fixed sample rate of submix this way */
|
||||||
virtual double getSampleRate() const = 0;
|
virtual double getSampleRate() const = 0;
|
||||||
|
|
||||||
/** Amuse gets fixed sample format of submix this way */
|
/** Amuse gets fixed sample format of submix this way */
|
||||||
virtual SubmixFormat getSampleFormat() const = 0;
|
virtual SubmixFormat getSampleFormat() const = 0;
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -1,53 +1,48 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class IBackendSubmix;
|
class IBackendSubmix;
|
||||||
|
|
||||||
/** Same channel enums from boo, used for matrix coefficient table index */
|
/** Same channel enums from boo, used for matrix coefficient table index */
|
||||||
enum class AudioChannel
|
enum class AudioChannel {
|
||||||
{
|
FrontLeft,
|
||||||
FrontLeft,
|
FrontRight,
|
||||||
FrontRight,
|
RearLeft,
|
||||||
RearLeft,
|
RearRight,
|
||||||
RearRight,
|
FrontCenter,
|
||||||
FrontCenter,
|
LFE,
|
||||||
LFE,
|
SideLeft,
|
||||||
SideLeft,
|
SideRight,
|
||||||
SideRight,
|
Unknown = 0xff
|
||||||
Unknown = 0xff
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Same structure from boo, used to represent interleaved speaker layout */
|
/** Same structure from boo, used to represent interleaved speaker layout */
|
||||||
struct ChannelMap
|
struct ChannelMap {
|
||||||
{
|
unsigned m_channelCount = 0;
|
||||||
unsigned m_channelCount = 0;
|
AudioChannel m_channels[8] = {};
|
||||||
AudioChannel m_channels[8] = {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Client-implemented voice instance */
|
/** Client-implemented voice instance */
|
||||||
class IBackendVoice
|
class IBackendVoice {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
virtual ~IBackendVoice() = default;
|
virtual ~IBackendVoice() = default;
|
||||||
|
|
||||||
/** Set new sample rate into platform voice (may result in artifacts while playing) */
|
/** Set new sample rate into platform voice (may result in artifacts while playing) */
|
||||||
virtual void resetSampleRate(double sampleRate) = 0;
|
virtual void resetSampleRate(double sampleRate) = 0;
|
||||||
|
|
||||||
/** Reset channel-gains to silence and unbind all submixes */
|
/** Reset channel-gains to silence and unbind all submixes */
|
||||||
virtual void resetChannelLevels() = 0;
|
virtual void resetChannelLevels() = 0;
|
||||||
|
|
||||||
/** Set channel-gains for audio source (AudioChannel enum for array index) */
|
/** Set channel-gains for audio source (AudioChannel enum for array index) */
|
||||||
virtual void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew) = 0;
|
virtual void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew) = 0;
|
||||||
|
|
||||||
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
|
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
|
||||||
virtual void setPitchRatio(double ratio, bool slew) = 0;
|
virtual void setPitchRatio(double ratio, bool slew) = 0;
|
||||||
|
|
||||||
/** Instructs platform to begin consuming sample data; invoking callback as needed */
|
/** Instructs platform to begin consuming sample data; invoking callback as needed */
|
||||||
virtual void start() = 0;
|
virtual void start() = 0;
|
||||||
|
|
||||||
/** Instructs platform to stop consuming sample data */
|
/** Instructs platform to stop consuming sample data */
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class IBackendVoice;
|
class IBackendVoice;
|
||||||
class IBackendSubmix;
|
class IBackendSubmix;
|
||||||
class Voice;
|
class Voice;
|
||||||
|
@ -13,49 +12,39 @@ class Submix;
|
||||||
class Engine;
|
class Engine;
|
||||||
|
|
||||||
/** Same enum as boo for describing speaker-configuration */
|
/** Same enum as boo for describing speaker-configuration */
|
||||||
enum class AudioChannelSet
|
enum class AudioChannelSet { Stereo, Quad, Surround51, Surround71, Unknown = 0xff };
|
||||||
{
|
|
||||||
Stereo,
|
|
||||||
Quad,
|
|
||||||
Surround51,
|
|
||||||
Surround71,
|
|
||||||
Unknown = 0xff
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Handle to MIDI-reader, implementation opaque */
|
/** Handle to MIDI-reader, implementation opaque */
|
||||||
class IMIDIReader
|
class IMIDIReader {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
virtual ~IMIDIReader() = default;
|
virtual ~IMIDIReader() = default;
|
||||||
virtual void pumpReader(double dt) = 0;
|
virtual void pumpReader(double dt) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Client-implemented voice allocator */
|
/** Client-implemented voice allocator */
|
||||||
class IBackendVoiceAllocator
|
class IBackendVoiceAllocator {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
virtual ~IBackendVoiceAllocator() = default;
|
virtual ~IBackendVoiceAllocator() = default;
|
||||||
|
|
||||||
/** Amuse obtains a new voice from the platform this way */
|
/** Amuse obtains a new voice from the platform this way */
|
||||||
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) = 0;
|
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) = 0;
|
||||||
|
|
||||||
/** Amuse obtains a new submix from the platform this way */
|
/** Amuse obtains a new submix from the platform this way */
|
||||||
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId) = 0;
|
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId) = 0;
|
||||||
|
|
||||||
/** Amuse obtains a list of all MIDI devices this way */
|
/** Amuse obtains a list of all MIDI devices this way */
|
||||||
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
|
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
|
||||||
|
|
||||||
/** Amuse obtains an interactive MIDI-in connection from the OS this way */
|
/** Amuse obtains an interactive MIDI-in connection from the OS this way */
|
||||||
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) = 0;
|
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) = 0;
|
||||||
|
|
||||||
/** Amuse obtains speaker-configuration from the platform this way */
|
/** Amuse obtains speaker-configuration from the platform this way */
|
||||||
virtual AudioChannelSet getAvailableSet() = 0;
|
virtual AudioChannelSet getAvailableSet() = 0;
|
||||||
|
|
||||||
/** Set volume of main mix out */
|
/** Set volume of main mix out */
|
||||||
virtual void setVolume(float vol) = 0;
|
virtual void setVolume(float vol) = 0;
|
||||||
|
|
||||||
/** Amuse registers for key callback events from the mixing engine this way */
|
/** Amuse registers for key callback events from the mixing engine this way */
|
||||||
virtual void setCallbackInterface(Engine* engine) = 0;
|
virtual void setCallbackInterface(Engine* engine) = 0;
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,28 @@
|
||||||
|
|
||||||
#include "amuse/Emitter.hpp"
|
#include "amuse/Emitter.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
class Listener {
|
||||||
class Listener
|
friend class Emitter;
|
||||||
{
|
friend class Engine;
|
||||||
friend class Emitter;
|
Vector3f m_pos = {};
|
||||||
friend class Engine;
|
Vector3f m_dir = {};
|
||||||
Vector3f m_pos = {};
|
Vector3f m_heading = {};
|
||||||
Vector3f m_dir = {};
|
Vector3f m_up = {};
|
||||||
Vector3f m_heading = {};
|
Vector3f m_right = {};
|
||||||
Vector3f m_up = {};
|
float m_volume;
|
||||||
Vector3f m_right = {};
|
float m_frontDiff;
|
||||||
float m_volume;
|
float m_backDiff;
|
||||||
float m_frontDiff;
|
float m_soundSpeed;
|
||||||
float m_backDiff;
|
bool m_dirty = true;
|
||||||
float m_soundSpeed;
|
|
||||||
bool m_dirty = true;
|
|
||||||
public:
|
|
||||||
Listener(float volume, float frontDiff, float backDiff, float soundSpeed)
|
|
||||||
: m_volume(clamp(0.f, volume, 1.f)), m_frontDiff(frontDiff), m_backDiff(backDiff), m_soundSpeed(soundSpeed) {}
|
|
||||||
void setVectors(const float* pos, const float* dir, const float* heading, const float* up);
|
|
||||||
void setVolume(float vol) { m_volume = clamp(0.f, vol, 1.f); m_dirty = true; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
Listener(float volume, float frontDiff, float backDiff, float soundSpeed)
|
||||||
|
: m_volume(clamp(0.f, volume, 1.f)), m_frontDiff(frontDiff), m_backDiff(backDiff), m_soundSpeed(soundSpeed) {}
|
||||||
|
void setVectors(const float* pos, const float* dir, const float* heading, const float* up);
|
||||||
|
void setVolume(float vol) {
|
||||||
|
m_volume = clamp(0.f, vol, 1.f);
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace amuse
|
||||||
|
|
|
@ -2,18 +2,15 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
static inline int16_t N64MusyXSampClamp(int32_t val)
|
static inline int16_t N64MusyXSampClamp(int32_t val) {
|
||||||
{
|
if (val < -32768)
|
||||||
if (val < -32768) val = -32768;
|
val = -32768;
|
||||||
else if (val > 32767) val = 32767;
|
else if (val > 32767)
|
||||||
return val;
|
val = 32767;
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in,
|
unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], unsigned lastSample);
|
||||||
const int16_t coefs[8][2][8],
|
|
||||||
unsigned lastSample);
|
|
||||||
|
|
||||||
unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8],
|
||||||
const int16_t coefs[8][2][8],
|
|
||||||
unsigned firstSample, unsigned lastSample);
|
unsigned firstSample, unsigned lastSample);
|
||||||
|
|
||||||
|
|
|
@ -10,158 +10,153 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
/** State of sequencer over lifetime */
|
/** State of sequencer over lifetime */
|
||||||
enum class SequencerState
|
enum class SequencerState {
|
||||||
{
|
Playing, /**< Sequencer actively playing arrangement */
|
||||||
Playing, /**< Sequencer actively playing arrangement */
|
Interactive, /**< Interactive sequencer for live MIDI message processing, will not automatically die */
|
||||||
Interactive, /**< Interactive sequencer for live MIDI message processing, will not automatically die */
|
Dead /**< Set when arrangement complete and `dieOnEnd` was set, or manually with die() */
|
||||||
Dead /**< Set when arrangement complete and `dieOnEnd` was set, or manually with die() */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Multi-voice lifetime manager and polyphonic parameter tracking */
|
/** Multi-voice lifetime manager and polyphonic parameter tracking */
|
||||||
class Sequencer : public Entity
|
class Sequencer : public Entity {
|
||||||
{
|
friend class Engine;
|
||||||
friend class Engine;
|
const SongGroupIndex* m_songGroup = nullptr; /**< Quick access to song group project index */
|
||||||
const SongGroupIndex* m_songGroup = nullptr; /**< Quick access to song group project index */
|
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */
|
||||||
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */
|
const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */
|
||||||
const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */
|
std::vector<const SFXGroupIndex::SFXEntry*> m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */
|
||||||
std::vector<const SFXGroupIndex::SFXEntry*> m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */
|
ObjToken<Studio> m_studio; /**< Studio this sequencer outputs to */
|
||||||
ObjToken<Studio> m_studio; /**< Studio this sequencer outputs to */
|
|
||||||
|
|
||||||
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
|
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
|
||||||
SongState m_songState; /**< State of current arrangement playback */
|
SongState m_songState; /**< State of current arrangement playback */
|
||||||
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
|
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
|
||||||
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
|
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
|
||||||
|
|
||||||
float m_curVol = 1.f; /**< Current volume of sequencer */
|
float m_curVol = 1.f; /**< Current volume of sequencer */
|
||||||
float m_volFadeTime = 0.f;
|
float m_volFadeTime = 0.f;
|
||||||
float m_volFadeTarget = 0.f;
|
float m_volFadeTarget = 0.f;
|
||||||
float m_volFadeStart = 0.f;
|
float m_volFadeStart = 0.f;
|
||||||
float m_stopFadeTime = 0.f;
|
float m_stopFadeTime = 0.f;
|
||||||
float m_stopFadeBeginVol = 0.f;
|
float m_stopFadeBeginVol = 0.f;
|
||||||
|
|
||||||
/** State of a single MIDI channel */
|
/** State of a single MIDI channel */
|
||||||
struct ChannelState
|
struct ChannelState {
|
||||||
{
|
Sequencer* m_parent = nullptr;
|
||||||
Sequencer* m_parent = nullptr;
|
uint8_t m_chanId;
|
||||||
uint8_t m_chanId;
|
const SongGroupIndex::MIDISetup* m_setup = nullptr; /* Channel defaults to program 0 if null */
|
||||||
const SongGroupIndex::MIDISetup* m_setup = nullptr; /* Channel defaults to program 0 if null */
|
const SongGroupIndex::PageEntry* m_page = nullptr;
|
||||||
const SongGroupIndex::PageEntry* m_page = nullptr;
|
~ChannelState();
|
||||||
~ChannelState();
|
ChannelState() = default;
|
||||||
ChannelState() = default;
|
ChannelState(Sequencer& parent, uint8_t chanId);
|
||||||
ChannelState(Sequencer& parent, uint8_t chanId);
|
operator bool() const { return m_parent != nullptr; }
|
||||||
operator bool() const { return m_parent != nullptr; }
|
|
||||||
|
|
||||||
/** Voices corresponding to currently-pressed keys in channel */
|
/** Voices corresponding to currently-pressed keys in channel */
|
||||||
std::unordered_map<uint8_t, ObjToken<Voice>> m_chanVoxs;
|
std::unordered_map<uint8_t, ObjToken<Voice>> m_chanVoxs;
|
||||||
std::unordered_set<ObjToken<Voice>> m_keyoffVoxs;
|
std::unordered_set<ObjToken<Voice>> m_keyoffVoxs;
|
||||||
ObjToken<Voice> m_lastVoice;
|
ObjToken<Voice> m_lastVoice;
|
||||||
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
|
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
|
||||||
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
|
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
|
||||||
int8_t m_pitchWheelRange = -1; /**< Pitch wheel range settable by RPN 0 */
|
int8_t m_pitchWheelRange = -1; /**< Pitch wheel range settable by RPN 0 */
|
||||||
int8_t m_curProgram = 0; /**< MIDI program number */
|
int8_t m_curProgram = 0; /**< MIDI program number */
|
||||||
float m_curVol = 1.f; /**< Current volume of channel */
|
float m_curVol = 1.f; /**< Current volume of channel */
|
||||||
float m_curPan = 0.f; /**< Current panning of channel */
|
float m_curPan = 0.f; /**< Current panning of channel */
|
||||||
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
|
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
|
||||||
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for channel */
|
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for channel */
|
||||||
|
|
||||||
void _bringOutYourDead();
|
|
||||||
size_t getVoiceCount() const;
|
|
||||||
ObjToken<Voice> keyOn(uint8_t note, uint8_t velocity);
|
|
||||||
void keyOff(uint8_t note, uint8_t velocity);
|
|
||||||
void setCtrlValue(uint8_t ctrl, int8_t val);
|
|
||||||
bool programChange(int8_t prog);
|
|
||||||
void nextProgram();
|
|
||||||
void prevProgram();
|
|
||||||
void setPitchWheel(float pitchWheel);
|
|
||||||
void setVolume(float vol);
|
|
||||||
void setPan(float pan);
|
|
||||||
void allOff();
|
|
||||||
void killKeygroup(uint8_t kg, bool now);
|
|
||||||
ObjToken<Voice> findVoice(int vid);
|
|
||||||
void sendMacroMessage(ObjectId macroId, int32_t val);
|
|
||||||
};
|
|
||||||
std::array<ChannelState, 16> m_chanStates; /**< Lazily-allocated channel states */
|
|
||||||
|
|
||||||
void _bringOutYourDead();
|
void _bringOutYourDead();
|
||||||
void _destroy();
|
size_t getVoiceCount() const;
|
||||||
|
ObjToken<Voice> keyOn(uint8_t note, uint8_t velocity);
|
||||||
|
void keyOff(uint8_t note, uint8_t velocity);
|
||||||
|
void setCtrlValue(uint8_t ctrl, int8_t val);
|
||||||
|
bool programChange(int8_t prog);
|
||||||
|
void nextProgram();
|
||||||
|
void prevProgram();
|
||||||
|
void setPitchWheel(float pitchWheel);
|
||||||
|
void setVolume(float vol);
|
||||||
|
void setPan(float pan);
|
||||||
|
void allOff();
|
||||||
|
void killKeygroup(uint8_t kg, bool now);
|
||||||
|
ObjToken<Voice> findVoice(int vid);
|
||||||
|
void sendMacroMessage(ObjectId macroId, int32_t val);
|
||||||
|
};
|
||||||
|
std::array<ChannelState, 16> m_chanStates; /**< Lazily-allocated channel states */
|
||||||
|
|
||||||
|
void _bringOutYourDead();
|
||||||
|
void _destroy();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Sequencer();
|
~Sequencer();
|
||||||
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup, SongId setupId,
|
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup, SongId setupId,
|
||||||
ObjToken<Studio> studio);
|
ObjToken<Studio> studio);
|
||||||
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup,
|
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup,
|
||||||
ObjToken<Studio> studio);
|
ObjToken<Studio> studio);
|
||||||
|
|
||||||
/** Advance current song data (if any) */
|
/** Advance current song data (if any) */
|
||||||
void advance(double dt);
|
void advance(double dt);
|
||||||
|
|
||||||
/** Obtain pointer to Sequencer's Submix */
|
/** Obtain pointer to Sequencer's Submix */
|
||||||
ObjToken<Studio> getStudio() { return m_studio; }
|
ObjToken<Studio> getStudio() { return m_studio; }
|
||||||
|
|
||||||
/** Get current state of sequencer */
|
/** Get current state of sequencer */
|
||||||
SequencerState state() const { return m_state; }
|
SequencerState state() const { return m_state; }
|
||||||
|
|
||||||
/** Get number of active voices */
|
/** Get number of active voices */
|
||||||
size_t getVoiceCount() const;
|
size_t getVoiceCount() const;
|
||||||
|
|
||||||
/** Register key press with voice set */
|
/** Register key press with voice set */
|
||||||
ObjToken<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
|
ObjToken<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
|
||||||
|
|
||||||
/** Register key release with voice set */
|
/** Register key release with voice set */
|
||||||
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
|
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
|
||||||
|
|
||||||
/** Set MIDI control value [0,127] for all voices */
|
/** Set MIDI control value [0,127] for all voices */
|
||||||
void setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val);
|
void setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val);
|
||||||
|
|
||||||
/** Set pitchwheel value for use with voice controllers */
|
/** Set pitchwheel value for use with voice controllers */
|
||||||
void setPitchWheel(uint8_t chan, float pitchWheel);
|
void setPitchWheel(uint8_t chan, float pitchWheel);
|
||||||
|
|
||||||
/** Send keyoffs to all active notes, silence immediately if `now` set */
|
/** Send keyoffs to all active notes, silence immediately if `now` set */
|
||||||
void allOff(bool now = false);
|
void allOff(bool now = false);
|
||||||
|
|
||||||
/** Send keyoffs to all active notes on specified channel, silence immediately if `now` set */
|
/** Send keyoffs to all active notes on specified channel, silence immediately if `now` set */
|
||||||
void allOff(uint8_t chan, bool now = false);
|
void allOff(uint8_t chan, bool now = false);
|
||||||
|
|
||||||
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
||||||
void killKeygroup(uint8_t kg, bool now);
|
void killKeygroup(uint8_t kg, bool now);
|
||||||
|
|
||||||
/** Find voice instance contained within Sequencer */
|
/** Find voice instance contained within Sequencer */
|
||||||
ObjToken<Voice> findVoice(int vid);
|
ObjToken<Voice> findVoice(int vid);
|
||||||
|
|
||||||
/** Send all voices using `macroId` the message `val` */
|
/** Send all voices using `macroId` the message `val` */
|
||||||
void sendMacroMessage(ObjectId macroId, int32_t val);
|
void sendMacroMessage(ObjectId macroId, int32_t val);
|
||||||
|
|
||||||
/** Set tempo of sequencer and all voices in ticks per second */
|
/** Set tempo of sequencer and all voices in ticks per second */
|
||||||
void setTempo(uint8_t chan, double ticksPerSec);
|
void setTempo(uint8_t chan, double ticksPerSec);
|
||||||
void setTempo(double ticksPerSec);
|
void setTempo(double ticksPerSec);
|
||||||
|
|
||||||
/** Play MIDI arrangement */
|
/** Play MIDI arrangement */
|
||||||
void playSong(const unsigned char* arrData, bool loop = true, bool dieOnEnd = true);
|
void playSong(const unsigned char* arrData, bool loop = true, bool dieOnEnd = true);
|
||||||
|
|
||||||
/** Stop current MIDI arrangement */
|
/** Stop current MIDI arrangement */
|
||||||
void stopSong(float fadeTime = 0.f, bool now = false);
|
void stopSong(float fadeTime = 0.f, bool now = false);
|
||||||
|
|
||||||
/** Set total volume of sequencer */
|
/** Set total volume of sequencer */
|
||||||
void setVolume(float vol, float fadeTime = 0.f);
|
void setVolume(float vol, float fadeTime = 0.f);
|
||||||
|
|
||||||
/** Get current program number of channel */
|
/** Get current program number of channel */
|
||||||
int8_t getChanProgram(int8_t chanId) const;
|
int8_t getChanProgram(int8_t chanId) const;
|
||||||
|
|
||||||
/** Set current program number of channel */
|
/** Set current program number of channel */
|
||||||
bool setChanProgram(int8_t chanId, int8_t prog);
|
bool setChanProgram(int8_t chanId, int8_t prog);
|
||||||
|
|
||||||
/** Advance to next program in channel */
|
/** Advance to next program in channel */
|
||||||
void nextChanProgram(int8_t chanId);
|
void nextChanProgram(int8_t chanId);
|
||||||
|
|
||||||
/** Advance to prev program in channel */
|
/** Advance to prev program in channel */
|
||||||
void prevChanProgram(int8_t chanId);
|
void prevChanProgram(int8_t chanId);
|
||||||
|
|
||||||
/** Manually kill sequencer for deferred release from engine */
|
/** Manually kill sequencer for deferred release from engine */
|
||||||
void kill() { m_state = SequencerState::Dead; }
|
void kill() { m_state = SequencerState::Dead; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,11 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
class SongConverter
|
class SongConverter {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
static std::vector<uint8_t> SongToMIDI(const unsigned char* data, int& versionOut, bool& isBig);
|
static std::vector<uint8_t> SongToMIDI(const unsigned char* data, int& versionOut, bool& isBig);
|
||||||
static std::vector<uint8_t> MIDIToSong(const std::vector<uint8_t>& data, int version, bool big);
|
static std::vector<uint8_t> MIDIToSong(const std::vector<uint8_t>& data, int version, bool big);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -6,133 +6,121 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class Sequencer;
|
class Sequencer;
|
||||||
|
|
||||||
enum class SongPlayState
|
enum class SongPlayState { Stopped, Playing };
|
||||||
{
|
|
||||||
Stopped,
|
|
||||||
Playing
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Real-time state of Song execution */
|
/** Real-time state of Song execution */
|
||||||
class SongState
|
class SongState {
|
||||||
{
|
friend class Voice;
|
||||||
friend class Voice;
|
friend class SongConverter;
|
||||||
friend class SongConverter;
|
|
||||||
|
|
||||||
/** Song header */
|
/** Song header */
|
||||||
struct Header
|
struct Header {
|
||||||
{
|
uint32_t m_trackIdxOff;
|
||||||
uint32_t m_trackIdxOff;
|
uint32_t m_regionIdxOff;
|
||||||
uint32_t m_regionIdxOff;
|
uint32_t m_chanMapOff;
|
||||||
uint32_t m_chanMapOff;
|
uint32_t m_tempoTableOff;
|
||||||
uint32_t m_tempoTableOff;
|
uint32_t m_initialTempo; /* Top bit indicates per-channel looping */
|
||||||
uint32_t m_initialTempo; /* Top bit indicates per-channel looping */
|
uint32_t m_loopStartTicks[16];
|
||||||
uint32_t m_loopStartTicks[16];
|
uint32_t m_chanMapOff2;
|
||||||
uint32_t m_chanMapOff2;
|
void swapToBig();
|
||||||
void swapToBig();
|
void swapFromBig();
|
||||||
void swapFromBig();
|
Header& operator=(const Header& other);
|
||||||
Header& operator=(const Header& other);
|
Header(const Header& other) { *this = other; }
|
||||||
Header(const Header& other) { *this = other; }
|
Header() = default;
|
||||||
Header() = default;
|
} m_header;
|
||||||
} m_header;
|
|
||||||
|
|
||||||
/** Track region ('clip' in an NLA representation) */
|
/** Track region ('clip' in an NLA representation) */
|
||||||
struct TrackRegion
|
struct TrackRegion {
|
||||||
{
|
uint32_t m_startTick;
|
||||||
uint32_t m_startTick;
|
uint8_t m_progNum;
|
||||||
uint8_t m_progNum;
|
uint8_t m_unk1;
|
||||||
uint8_t m_unk1;
|
uint16_t m_unk2;
|
||||||
uint16_t m_unk2;
|
int16_t m_regionIndex; /* -1 to terminate song, -2 to loop to previous region */
|
||||||
int16_t m_regionIndex; /* -1 to terminate song, -2 to loop to previous region */
|
int16_t m_loopToRegion;
|
||||||
int16_t m_loopToRegion;
|
bool indexDone(bool bigEndian, bool loop) const;
|
||||||
bool indexDone(bool bigEndian, bool loop) const;
|
bool indexValid(bool bigEndian) const;
|
||||||
bool indexValid(bool bigEndian) const;
|
int indexLoop(bool bigEndian) const;
|
||||||
int indexLoop(bool bigEndian) const;
|
};
|
||||||
|
|
||||||
|
/** Tempo change entry */
|
||||||
|
struct TempoChange {
|
||||||
|
uint32_t m_tick; /**< Relative song ticks from previous tempo change */
|
||||||
|
uint32_t m_tempo; /**< Tempo value in beats-per-minute (at 384 ticks per quarter-note) */
|
||||||
|
void swapBig();
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
|
||||||
|
int m_sngVersion; /**< Detected song revision, 1 has RLE-compressed delta-times */
|
||||||
|
bool m_bigEndian; /**< True if loaded song is big-endian data */
|
||||||
|
|
||||||
|
/** State of a single track within arrangement */
|
||||||
|
struct Track {
|
||||||
|
struct Header {
|
||||||
|
uint32_t m_type;
|
||||||
|
uint32_t m_pitchOff;
|
||||||
|
uint32_t m_modOff;
|
||||||
|
void swapBig();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Tempo change entry */
|
SongState* m_parent = nullptr;
|
||||||
struct TempoChange
|
uint8_t m_midiChan = 0xff; /**< MIDI channel number of song channel */
|
||||||
{
|
const TrackRegion* m_initRegion = nullptr; /**< Pointer to first track region */
|
||||||
uint32_t m_tick; /**< Relative song ticks from previous tempo change */
|
const TrackRegion* m_curRegion = nullptr; /**< Pointer to currently-playing track region */
|
||||||
uint32_t m_tempo; /**< Tempo value in beats-per-minute (at 384 ticks per quarter-note) */
|
const TrackRegion* m_nextRegion = nullptr; /**< Pointer to next-queued track region */
|
||||||
void swapBig();
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
|
double m_remDt = 0.0; /**< Remaining dt for keeping remainder between cycles */
|
||||||
int m_sngVersion; /**< Detected song revision, 1 has RLE-compressed delta-times */
|
uint32_t m_curTick = 0; /**< Current playback position for this track */
|
||||||
bool m_bigEndian; /**< True if loaded song is big-endian data */
|
uint32_t m_loopStartTick = 0; /**< Tick to loop back to */
|
||||||
|
/** Current pointer to tempo control, iterated over playback */
|
||||||
|
const TempoChange* m_tempoPtr;
|
||||||
|
uint32_t m_tempo; /**< Current tempo (beats per minute) */
|
||||||
|
|
||||||
/** State of a single track within arrangement */
|
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
|
||||||
struct Track
|
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
|
||||||
{
|
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
|
||||||
struct Header
|
int32_t m_pitchVal = 0; /**< Accumulated value of pitch */
|
||||||
{
|
uint32_t m_nextPitchTick = 0; /**< Upcoming position of pitch wheel change */
|
||||||
uint32_t m_type;
|
int32_t m_nextPitchDelta = 0; /**< Upcoming delta value of pitch */
|
||||||
uint32_t m_pitchOff;
|
int32_t m_modVal = 0; /**< Accumulated value of mod */
|
||||||
uint32_t m_modOff;
|
uint32_t m_nextModTick = 0; /**< Upcoming position of mod wheel change */
|
||||||
void swapBig();
|
int32_t m_nextModDelta = 0; /**< Upcoming delta value of mod */
|
||||||
};
|
std::array<int, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
|
||||||
|
|
||||||
SongState* m_parent = nullptr;
|
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
|
||||||
uint8_t m_midiChan = 0xff; /**< MIDI channel number of song channel */
|
int32_t m_lastN64EventTick =
|
||||||
const TrackRegion* m_initRegion = nullptr; /**< Pointer to first track region */
|
0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */
|
||||||
const TrackRegion* m_curRegion = nullptr; /**< Pointer to currently-playing track region */
|
|
||||||
const TrackRegion* m_nextRegion = nullptr; /**< Pointer to next-queued track region */
|
|
||||||
|
|
||||||
double m_remDt = 0.0; /**< Remaining dt for keeping remainder between cycles */
|
Track() = default;
|
||||||
uint32_t m_curTick = 0; /**< Current playback position for this track */
|
Track(SongState& parent, uint8_t midiChan, uint32_t loopStart, const TrackRegion* regions, uint32_t tempo);
|
||||||
uint32_t m_loopStartTick = 0; /**< Tick to loop back to */
|
operator bool() const { return m_parent != nullptr; }
|
||||||
/** Current pointer to tempo control, iterated over playback */
|
void setRegion(const TrackRegion* region);
|
||||||
const TempoChange* m_tempoPtr;
|
void advanceRegion();
|
||||||
uint32_t m_tempo; /**< Current tempo (beats per minute) */
|
bool advance(Sequencer& seq, double dt);
|
||||||
|
void resetTempo();
|
||||||
|
};
|
||||||
|
std::array<Track, 64> m_tracks;
|
||||||
|
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
|
||||||
|
|
||||||
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
|
SongPlayState m_songState = SongPlayState::Playing; /**< High-level state of Song playback */
|
||||||
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
|
bool m_loop = true; /**< Enable looping */
|
||||||
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
|
|
||||||
int32_t m_pitchVal = 0; /**< Accumulated value of pitch */
|
|
||||||
uint32_t m_nextPitchTick = 0; /**< Upcoming position of pitch wheel change */
|
|
||||||
int32_t m_nextPitchDelta = 0; /**< Upcoming delta value of pitch */
|
|
||||||
int32_t m_modVal = 0; /**< Accumulated value of mod */
|
|
||||||
uint32_t m_nextModTick = 0; /**< Upcoming position of mod wheel change */
|
|
||||||
int32_t m_nextModDelta = 0; /**< Upcoming delta value of mod */
|
|
||||||
std::array<int, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
|
|
||||||
|
|
||||||
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
|
|
||||||
int32_t m_lastN64EventTick =
|
|
||||||
0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */
|
|
||||||
|
|
||||||
Track() = default;
|
|
||||||
Track(SongState& parent, uint8_t midiChan, uint32_t loopStart, const TrackRegion* regions, uint32_t tempo);
|
|
||||||
operator bool() const { return m_parent != nullptr; }
|
|
||||||
void setRegion(const TrackRegion* region);
|
|
||||||
void advanceRegion();
|
|
||||||
bool advance(Sequencer& seq, double dt);
|
|
||||||
void resetTempo();
|
|
||||||
};
|
|
||||||
std::array<Track, 64> m_tracks;
|
|
||||||
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
|
|
||||||
|
|
||||||
SongPlayState m_songState = SongPlayState::Playing; /**< High-level state of Song playback */
|
|
||||||
bool m_loop = true; /**< Enable looping */
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Determine SNG version
|
/** Determine SNG version
|
||||||
* @param isBig returns true if big-endian SNG
|
* @param isBig returns true if big-endian SNG
|
||||||
* @return 0 for initial version, 1 for delta-time revision, -1 for non-SNG */
|
* @return 0 for initial version, 1 for delta-time revision, -1 for non-SNG */
|
||||||
static int DetectVersion(const unsigned char* ptr, bool& isBig);
|
static int DetectVersion(const unsigned char* ptr, bool& isBig);
|
||||||
|
|
||||||
/** initialize state for Song data at `ptr` */
|
/** initialize state for Song data at `ptr` */
|
||||||
bool initialize(const unsigned char* ptr, bool loop);
|
bool initialize(const unsigned char* ptr, bool loop);
|
||||||
|
|
||||||
uint32_t getInitialTempo() const { return m_header.m_initialTempo & 0x7fffffff; }
|
uint32_t getInitialTempo() const { return m_header.m_initialTempo & 0x7fffffff; }
|
||||||
|
|
||||||
/** advances `dt` seconds worth of commands in the Song
|
/** advances `dt` seconds worth of commands in the Song
|
||||||
* @return `true` if END reached
|
* @return `true` if END reached
|
||||||
*/
|
*/
|
||||||
bool advance(Sequencer& seq, double dt);
|
bool advance(Sequencer& seq, double dt);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -11,134 +11,114 @@
|
||||||
#undef SendMessage
|
#undef SendMessage
|
||||||
#undef GetMessage
|
#undef GetMessage
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class Voice;
|
class Voice;
|
||||||
|
|
||||||
/** Real-time state of SoundMacro execution */
|
/** Real-time state of SoundMacro execution */
|
||||||
struct SoundMacroState
|
struct SoundMacroState {
|
||||||
{
|
/** 'program counter' stack for the active SoundMacro */
|
||||||
/** 'program counter' stack for the active SoundMacro */
|
std::vector<std::tuple<ObjectId, const SoundMacro*, int>> m_pc;
|
||||||
std::vector<std::tuple<ObjectId, const SoundMacro*, int>> m_pc;
|
void _setPC(int pc) { std::get<2>(m_pc.back()) = std::get<1>(m_pc.back())->assertPC(pc); }
|
||||||
void _setPC(int pc)
|
|
||||||
{
|
|
||||||
std::get<2>(m_pc.back()) = std::get<1>(m_pc.back())->assertPC(pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
double m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
|
double m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
|
||||||
uint8_t m_initVel; /**< Velocity played for this macro invocation */
|
uint8_t m_initVel; /**< Velocity played for this macro invocation */
|
||||||
uint8_t m_initMod; /**< Modulation played for this macro invocation */
|
uint8_t m_initMod; /**< Modulation played for this macro invocation */
|
||||||
uint8_t m_initKey; /**< Key played for this macro invocation */
|
uint8_t m_initKey; /**< Key played for this macro invocation */
|
||||||
uint8_t m_curVel; /**< Current velocity played for this macro invocation */
|
uint8_t m_curVel; /**< Current velocity played for this macro invocation */
|
||||||
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
|
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
|
||||||
uint32_t m_curPitch; /**< Current key played for this macro invocation (in cents) */
|
uint32_t m_curPitch; /**< Current key played for this macro invocation (in cents) */
|
||||||
|
|
||||||
double m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
|
double m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
|
||||||
bool m_keyoff; /**< keyoff message has been received */
|
bool m_keyoff; /**< keyoff message has been received */
|
||||||
bool m_sampleEnd; /**< sample has finished playback */
|
bool m_sampleEnd; /**< sample has finished playback */
|
||||||
|
|
||||||
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
|
bool m_inWait = false; /**< set when timer/keyoff/sampleend wait active */
|
||||||
bool m_indefiniteWait = false; /**< set when timer wait is indefinite (keyoff/sampleend only) */
|
bool m_indefiniteWait = false; /**< set when timer wait is indefinite (keyoff/sampleend only) */
|
||||||
bool m_keyoffWait = false; /**< set when active wait is a keyoff wait */
|
bool m_keyoffWait = false; /**< set when active wait is a keyoff wait */
|
||||||
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
|
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
|
||||||
double m_waitCountdown; /**< countdown timer for active wait */
|
double m_waitCountdown; /**< countdown timer for active wait */
|
||||||
|
|
||||||
int m_loopCountdown = -1; /**< countdown for current loop */
|
int m_loopCountdown = -1; /**< countdown for current loop */
|
||||||
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
|
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
|
||||||
|
|
||||||
bool m_useAdsrControllers; /**< when set, use the following controllers for envelope times */
|
bool m_useAdsrControllers; /**< when set, use the following controllers for envelope times */
|
||||||
uint8_t m_midiAttack; /**< Attack MIDI controller */
|
uint8_t m_midiAttack; /**< Attack MIDI controller */
|
||||||
uint8_t m_midiDecay; /**< Decay MIDI controller */
|
uint8_t m_midiDecay; /**< Decay MIDI controller */
|
||||||
uint8_t m_midiSustain; /**< Sustain MIDI controller */
|
uint8_t m_midiSustain; /**< Sustain MIDI controller */
|
||||||
uint8_t m_midiRelease; /**< Release MIDI controller */
|
uint8_t m_midiRelease; /**< Release MIDI controller */
|
||||||
|
|
||||||
SoundMacro::CmdPortamento::PortState m_portamentoMode =
|
SoundMacro::CmdPortamento::PortState m_portamentoMode =
|
||||||
SoundMacro::CmdPortamento::PortState::MIDIControlled; /**< (0: Off, 1: On, 2: MIDI specified) */
|
SoundMacro::CmdPortamento::PortState::MIDIControlled; /**< (0: Off, 1: On, 2: MIDI specified) */
|
||||||
SoundMacro::CmdPortamento::PortType m_portamentoType =
|
SoundMacro::CmdPortamento::PortType m_portamentoType =
|
||||||
SoundMacro::CmdPortamento::PortType::LastPressed; /**< (0: New key pressed while old key pressed, 1: Always) */
|
SoundMacro::CmdPortamento::PortType::LastPressed; /**< (0: New key pressed while old key pressed, 1: Always) */
|
||||||
float m_portamentoTime = 0.5f; /**< portamento transition time, 0.f will perform legato */
|
float m_portamentoTime = 0.5f; /**< portamento transition time, 0.f will perform legato */
|
||||||
|
|
||||||
/** Used to build a multi-component formula for overriding controllers */
|
/** Used to build a multi-component formula for overriding controllers */
|
||||||
struct Evaluator
|
struct Evaluator {
|
||||||
{
|
enum class Combine : uint8_t { Set, Add, Mult };
|
||||||
enum class Combine : uint8_t
|
enum class VarType : uint8_t { Ctrl, Var };
|
||||||
{
|
|
||||||
Set,
|
|
||||||
Add,
|
|
||||||
Mult
|
|
||||||
};
|
|
||||||
enum class VarType : uint8_t
|
|
||||||
{
|
|
||||||
Ctrl,
|
|
||||||
Var
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Represents one term of the formula assembled via *_SELECT commands */
|
/** Represents one term of the formula assembled via *_SELECT commands */
|
||||||
struct Component
|
struct Component {
|
||||||
{
|
uint8_t m_midiCtrl;
|
||||||
uint8_t m_midiCtrl;
|
float m_scale;
|
||||||
float m_scale;
|
Combine m_combine;
|
||||||
Combine m_combine;
|
VarType m_varType;
|
||||||
VarType m_varType;
|
|
||||||
|
|
||||||
Component(uint8_t midiCtrl, float scale, Combine combine, VarType varType)
|
Component(uint8_t midiCtrl, float scale, Combine combine, VarType varType)
|
||||||
: m_midiCtrl(midiCtrl), m_scale(scale), m_combine(combine), m_varType(varType)
|
: m_midiCtrl(midiCtrl), m_scale(scale), m_combine(combine), m_varType(varType) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::vector<Component> m_comps; /**< Components built up by the macro */
|
|
||||||
|
|
||||||
/** Combine additional component(s) to formula */
|
|
||||||
void addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType);
|
|
||||||
|
|
||||||
/** Calculate value */
|
|
||||||
float evaluate(double time, const Voice& vox, const SoundMacroState& st) const;
|
|
||||||
|
|
||||||
/** Determine if able to use */
|
|
||||||
operator bool() const { return m_comps.size() != 0; }
|
|
||||||
};
|
};
|
||||||
|
std::vector<Component> m_comps; /**< Components built up by the macro */
|
||||||
|
|
||||||
Evaluator m_volumeSel;
|
/** Combine additional component(s) to formula */
|
||||||
Evaluator m_panSel;
|
void addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType);
|
||||||
Evaluator m_pitchWheelSel;
|
|
||||||
Evaluator m_modWheelSel;
|
|
||||||
Evaluator m_pedalSel;
|
|
||||||
Evaluator m_portamentoSel;
|
|
||||||
Evaluator m_reverbSel;
|
|
||||||
Evaluator m_preAuxASel;
|
|
||||||
Evaluator m_preAuxBSel;
|
|
||||||
Evaluator m_auxAFxSel[3];
|
|
||||||
Evaluator m_auxBFxSel[3];
|
|
||||||
Evaluator m_postAuxB;
|
|
||||||
Evaluator m_spanSel;
|
|
||||||
Evaluator m_dopplerSel;
|
|
||||||
Evaluator m_tremoloSel;
|
|
||||||
|
|
||||||
int32_t m_variables[32]; /**< 32-bit variables set with relevant commands */
|
/** Calculate value */
|
||||||
|
float evaluate(double time, const Voice& vox, const SoundMacroState& st) const;
|
||||||
|
|
||||||
/** Event registration data for TRAP_EVENT */
|
/** Determine if able to use */
|
||||||
struct EventTrap
|
operator bool() const { return m_comps.size() != 0; }
|
||||||
{
|
};
|
||||||
ObjectId macroId = 0xffff;
|
|
||||||
uint16_t macroStep;
|
Evaluator m_volumeSel;
|
||||||
};
|
Evaluator m_panSel;
|
||||||
|
Evaluator m_pitchWheelSel;
|
||||||
|
Evaluator m_modWheelSel;
|
||||||
|
Evaluator m_pedalSel;
|
||||||
|
Evaluator m_portamentoSel;
|
||||||
|
Evaluator m_reverbSel;
|
||||||
|
Evaluator m_preAuxASel;
|
||||||
|
Evaluator m_preAuxBSel;
|
||||||
|
Evaluator m_auxAFxSel[3];
|
||||||
|
Evaluator m_auxBFxSel[3];
|
||||||
|
Evaluator m_postAuxB;
|
||||||
|
Evaluator m_spanSel;
|
||||||
|
Evaluator m_dopplerSel;
|
||||||
|
Evaluator m_tremoloSel;
|
||||||
|
|
||||||
|
int32_t m_variables[32]; /**< 32-bit variables set with relevant commands */
|
||||||
|
|
||||||
|
/** Event registration data for TRAP_EVENT */
|
||||||
|
struct EventTrap {
|
||||||
|
ObjectId macroId = 0xffff;
|
||||||
|
uint16_t macroStep;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** initialize state for SoundMacro data at `ptr` */
|
/** initialize state for SoundMacro data at `ptr` */
|
||||||
void initialize(ObjectId id, const SoundMacro* macro, int step);
|
void initialize(ObjectId id, const SoundMacro* macro, int step);
|
||||||
void initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec,
|
void initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
|
uint8_t midiMod);
|
||||||
|
|
||||||
/** advances `dt` seconds worth of commands in the SoundMacro
|
/** advances `dt` seconds worth of commands in the SoundMacro
|
||||||
* @return `true` if END reached
|
* @return `true` if END reached
|
||||||
*/
|
*/
|
||||||
bool advance(Voice& vox, double dt);
|
bool advance(Voice& vox, double dt);
|
||||||
|
|
||||||
/** keyoff event */
|
/** keyoff event */
|
||||||
void keyoffNotify(Voice& vox);
|
void keyoffNotify(Voice& vox);
|
||||||
|
|
||||||
/** sample end event */
|
/** sample end event */
|
||||||
void sampleEndNotify(Voice& vox);
|
void sampleEndNotify(Voice& vox);
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -6,50 +6,44 @@
|
||||||
#include "Submix.hpp"
|
#include "Submix.hpp"
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
struct StudioSend;
|
struct StudioSend;
|
||||||
|
|
||||||
class Studio
|
class Studio {
|
||||||
{
|
friend class Engine;
|
||||||
friend class Engine;
|
Engine& m_engine;
|
||||||
Engine& m_engine;
|
Submix m_master;
|
||||||
Submix m_master;
|
Submix m_auxA;
|
||||||
Submix m_auxA;
|
Submix m_auxB;
|
||||||
Submix m_auxB;
|
|
||||||
|
|
||||||
std::list<StudioSend> m_studiosOut;
|
std::list<StudioSend> m_studiosOut;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
bool _cyclicCheck(Studio* leaf);
|
bool _cyclicCheck(Studio* leaf);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Studio(Engine& engine, bool mainOut);
|
Studio(Engine& engine, bool mainOut);
|
||||||
|
|
||||||
/** Register a target Studio to send this Studio's mixing busses */
|
/** Register a target Studio to send this Studio's mixing busses */
|
||||||
void addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB);
|
void addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB);
|
||||||
|
|
||||||
/** Advise submixes of changing sample rate */
|
/** Advise submixes of changing sample rate */
|
||||||
void resetOutputSampleRate(double sampleRate);
|
void resetOutputSampleRate(double sampleRate);
|
||||||
|
|
||||||
Submix& getMaster() { return m_master; }
|
Submix& getMaster() { return m_master; }
|
||||||
Submix& getAuxA() { return m_auxA; }
|
Submix& getAuxA() { return m_auxA; }
|
||||||
Submix& getAuxB() { return m_auxB; }
|
Submix& getAuxB() { return m_auxB; }
|
||||||
|
|
||||||
Engine& getEngine() { return m_engine; }
|
Engine& getEngine() { return m_engine; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StudioSend
|
struct StudioSend {
|
||||||
{
|
ObjToken<Studio> m_targetStudio;
|
||||||
ObjToken<Studio> m_targetStudio;
|
float m_dryLevel;
|
||||||
float m_dryLevel;
|
float m_auxALevel;
|
||||||
float m_auxALevel;
|
float m_auxBLevel;
|
||||||
float m_auxBLevel;
|
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
|
||||||
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
|
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB) {}
|
||||||
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -10,103 +10,93 @@
|
||||||
#include "EffectReverb.hpp"
|
#include "EffectReverb.hpp"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class IBackendSubmix;
|
class IBackendSubmix;
|
||||||
class Sequencer;
|
class Sequencer;
|
||||||
|
|
||||||
/** Intermediate mix of voices for applying auxiliary effects */
|
/** Intermediate mix of voices for applying auxiliary effects */
|
||||||
class Submix
|
class Submix {
|
||||||
{
|
friend class Engine;
|
||||||
friend class Engine;
|
friend class Voice;
|
||||||
friend class Voice;
|
friend class Sequencer;
|
||||||
friend class Sequencer;
|
Engine& m_root;
|
||||||
Engine& m_root;
|
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
|
||||||
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
|
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
|
||||||
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Submix(Engine& engine);
|
Submix(Engine& engine);
|
||||||
|
|
||||||
/** Construct new effect */
|
/** Construct new effect */
|
||||||
template <class T, class... Args>
|
template <class T, class... Args>
|
||||||
std::unique_ptr<EffectBaseTypeless> _makeEffect(Args... args)
|
std::unique_ptr<EffectBaseTypeless> _makeEffect(Args... args) {
|
||||||
{
|
switch (m_backendSubmix->getSampleFormat()) {
|
||||||
switch (m_backendSubmix->getSampleFormat())
|
case SubmixFormat::Int16: {
|
||||||
{
|
using ImpType = typename T::template ImpType<int16_t>;
|
||||||
case SubmixFormat::Int16:
|
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
|
||||||
{
|
|
||||||
using ImpType = typename T::template ImpType<int16_t>;
|
|
||||||
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
|
|
||||||
}
|
|
||||||
case SubmixFormat::Int32:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
using ImpType = typename T::template ImpType<int32_t>;
|
|
||||||
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
|
|
||||||
}
|
|
||||||
case SubmixFormat::Float:
|
|
||||||
{
|
|
||||||
using ImpType = typename T::template ImpType<float>;
|
|
||||||
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
case SubmixFormat::Int32:
|
||||||
/** Add new effect to effect stack and assume ownership */
|
default: {
|
||||||
template <class T, class... Args>
|
using ImpType = typename T::template ImpType<int32_t>;
|
||||||
T& makeEffect(Args... args)
|
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
|
||||||
{
|
|
||||||
m_effectStack.push_back(_makeEffect<T>(args...));
|
|
||||||
return static_cast<typename T::template ImpType<float>&>(*m_effectStack.back());
|
|
||||||
}
|
}
|
||||||
|
case SubmixFormat::Float: {
|
||||||
|
using ImpType = typename T::template ImpType<float>;
|
||||||
|
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Add new chorus effect to effect stack and assume ownership */
|
/** Add new effect to effect stack and assume ownership */
|
||||||
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
|
template <class T, class... Args>
|
||||||
|
T& makeEffect(Args... args) {
|
||||||
|
m_effectStack.push_back(_makeEffect<T>(args...));
|
||||||
|
return static_cast<typename T::template ImpType<float>&>(*m_effectStack.back());
|
||||||
|
}
|
||||||
|
|
||||||
/** Add new chorus effect to effect stack and assume ownership */
|
/** Add new chorus effect to effect stack and assume ownership */
|
||||||
EffectChorus& makeChorus(const EffectChorusInfo& info);
|
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
|
||||||
|
|
||||||
/** Add new delay effect to effect stack and assume ownership */
|
/** Add new chorus effect to effect stack and assume ownership */
|
||||||
EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
|
EffectChorus& makeChorus(const EffectChorusInfo& info);
|
||||||
|
|
||||||
/** Add new delay effect to effect stack and assume ownership */
|
/** Add new delay effect to effect stack and assume ownership */
|
||||||
EffectDelay& makeDelay(const EffectDelayInfo& info);
|
EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
|
||||||
|
|
||||||
/** Add new standard-quality reverb effect to effect stack and assume ownership */
|
/** Add new delay effect to effect stack and assume ownership */
|
||||||
EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay);
|
EffectDelay& makeDelay(const EffectDelayInfo& info);
|
||||||
|
|
||||||
/** Add new standard-quality reverb effect to effect stack and assume ownership */
|
/** Add new standard-quality reverb effect to effect stack and assume ownership */
|
||||||
EffectReverbStd& makeReverbStd(const EffectReverbStdInfo& info);
|
EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay);
|
||||||
|
|
||||||
/** Add new high-quality reverb effect to effect stack and assume ownership */
|
/** Add new standard-quality reverb effect to effect stack and assume ownership */
|
||||||
EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay,
|
EffectReverbStd& makeReverbStd(const EffectReverbStdInfo& info);
|
||||||
float crosstalk);
|
|
||||||
|
|
||||||
/** Add new high-quality reverb effect to effect stack and assume ownership */
|
/** Add new high-quality reverb effect to effect stack and assume ownership */
|
||||||
EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info);
|
EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
|
||||||
|
|
||||||
/** Remove and deallocate all effects from effect stack */
|
/** Add new high-quality reverb effect to effect stack and assume ownership */
|
||||||
void clearEffects() { m_effectStack.clear(); }
|
EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info);
|
||||||
|
|
||||||
/** Returns true when an effect callback is bound */
|
/** Remove and deallocate all effects from effect stack */
|
||||||
bool canApplyEffect() const { return m_effectStack.size() != 0; }
|
void clearEffects() { m_effectStack.clear(); }
|
||||||
|
|
||||||
/** in/out transformation entry for audio effect */
|
/** Returns true when an effect callback is bound */
|
||||||
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
bool canApplyEffect() const { return m_effectStack.size() != 0; }
|
||||||
|
|
||||||
/** in/out transformation entry for audio effect */
|
/** in/out transformation entry for audio effect */
|
||||||
void applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
||||||
|
|
||||||
/** in/out transformation entry for audio effect */
|
/** in/out transformation entry for audio effect */
|
||||||
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
void applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
||||||
|
|
||||||
/** advice effects of changing sample rate */
|
/** in/out transformation entry for audio effect */
|
||||||
void resetOutputSampleRate(double sampleRate);
|
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
||||||
|
|
||||||
Engine& getEngine() { return m_root; }
|
/** advice effects of changing sample rate */
|
||||||
|
void resetOutputSampleRate(double sampleRate);
|
||||||
|
|
||||||
std::vector<std::unique_ptr<EffectBaseTypeless>>& getEffectStack() { return m_effectStack; }
|
Engine& getEngine() { return m_root; }
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<EffectBaseTypeless>>& getEffectStack() { return m_effectStack; }
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -12,383 +12,371 @@
|
||||||
#include "Envelope.hpp"
|
#include "Envelope.hpp"
|
||||||
#include "Studio.hpp"
|
#include "Studio.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
class IBackendVoice;
|
class IBackendVoice;
|
||||||
struct Keymap;
|
struct Keymap;
|
||||||
struct LayerMapping;
|
struct LayerMapping;
|
||||||
|
|
||||||
/** State of voice over lifetime */
|
/** State of voice over lifetime */
|
||||||
enum class VoiceState
|
enum class VoiceState {
|
||||||
{
|
Playing, /**< SoundMacro actively executing, not in KeyOff */
|
||||||
Playing, /**< SoundMacro actively executing, not in KeyOff */
|
KeyOff, /**< KeyOff event issued, macro beginning fade-out */
|
||||||
KeyOff, /**< KeyOff event issued, macro beginning fade-out */
|
Dead /**< Default state, causes Engine to remove voice at end of pump cycle */
|
||||||
Dead /**< Default state, causes Engine to remove voice at end of pump cycle */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Individual source of audio */
|
/** Individual source of audio */
|
||||||
class Voice : public Entity
|
class Voice : public Entity {
|
||||||
{
|
friend class Engine;
|
||||||
friend class Engine;
|
friend class Sequencer;
|
||||||
friend class Sequencer;
|
friend struct SoundMacroState;
|
||||||
friend struct SoundMacroState;
|
friend class Envelope;
|
||||||
friend class Envelope;
|
friend class Emitter;
|
||||||
friend class Emitter;
|
friend struct SoundMacro::CmdScaleVolume;
|
||||||
friend struct SoundMacro::CmdScaleVolume;
|
friend struct SoundMacro::CmdKeyOff;
|
||||||
friend struct SoundMacro::CmdKeyOff;
|
friend struct SoundMacro::CmdScaleVolumeDLS;
|
||||||
friend struct SoundMacro::CmdScaleVolumeDLS;
|
friend struct SoundMacro::CmdReturn;
|
||||||
friend struct SoundMacro::CmdReturn;
|
friend struct SoundMacro::CmdGoSub;
|
||||||
friend struct SoundMacro::CmdGoSub;
|
friend struct SoundMacro::CmdTrapEvent;
|
||||||
friend struct SoundMacro::CmdTrapEvent;
|
friend struct SoundMacro::CmdUntrapEvent;
|
||||||
friend struct SoundMacro::CmdUntrapEvent;
|
friend struct SoundMacro::CmdGetMessage;
|
||||||
friend struct SoundMacro::CmdGetMessage;
|
friend struct SoundMacro::CmdModeSelect;
|
||||||
friend struct SoundMacro::CmdModeSelect;
|
|
||||||
|
|
||||||
struct VolumeCache
|
struct VolumeCache {
|
||||||
{
|
bool m_curDLS = false; /**< Current DLS status of cached lookup */
|
||||||
bool m_curDLS = false; /**< Current DLS status of cached lookup */
|
float m_curVolLUTKey = -1.f; /**< Current input level cached out of LUT */
|
||||||
float m_curVolLUTKey = -1.f; /**< Current input level cached out of LUT */
|
float m_curVolLUTVal = 0.f; /**< Current output level cached out of LUT */
|
||||||
float m_curVolLUTVal = 0.f; /**< Current output level cached out of LUT */
|
float getVolume(float vol, bool dls);
|
||||||
float getVolume(float vol, bool dls);
|
float getCachedVolume() const { return m_curVolLUTVal; }
|
||||||
float getCachedVolume() const { return m_curVolLUTVal; }
|
};
|
||||||
};
|
|
||||||
|
|
||||||
struct Panning
|
struct Panning {
|
||||||
{
|
double m_time; /**< time of PANNING command */
|
||||||
double m_time; /**< time of PANNING command */
|
double m_dur; /**< requested duration of PANNING command */
|
||||||
double m_dur; /**< requested duration of PANNING command */
|
uint8_t m_pos; /**< initial pan value of PANNING command */
|
||||||
uint8_t m_pos; /**< initial pan value of PANNING command */
|
int8_t m_width; /**< delta pan value to target of PANNING command */
|
||||||
int8_t m_width; /**< delta pan value to target of PANNING command */
|
};
|
||||||
};
|
|
||||||
|
|
||||||
void _setObjectId(ObjectId id) { m_objectId = id; }
|
void _setObjectId(ObjectId id) { m_objectId = id; }
|
||||||
|
|
||||||
int m_vid; /**< VoiceID of this voice instance */
|
int m_vid; /**< VoiceID of this voice instance */
|
||||||
bool m_emitter; /**< Voice is part of an Emitter */
|
bool m_emitter; /**< Voice is part of an Emitter */
|
||||||
ObjToken<Studio> m_studio; /**< Studio this voice outputs to */
|
ObjToken<Studio> m_studio; /**< Studio this voice outputs to */
|
||||||
IObjToken<Sequencer> m_sequencer; /**< Strong reference to parent sequencer to retain ctrl vals */
|
IObjToken<Sequencer> m_sequencer; /**< Strong reference to parent sequencer to retain ctrl vals */
|
||||||
|
|
||||||
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
|
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
|
||||||
SoundMacroState m_state; /**< State container for SoundMacro playback */
|
SoundMacroState m_state; /**< State container for SoundMacro playback */
|
||||||
SoundMacroState::EventTrap m_keyoffTrap; /**< Trap for keyoff (SoundMacro overrides default envelope behavior) */
|
SoundMacroState::EventTrap m_keyoffTrap; /**< Trap for keyoff (SoundMacro overrides default envelope behavior) */
|
||||||
SoundMacroState::EventTrap m_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */
|
SoundMacroState::EventTrap m_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */
|
||||||
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
|
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
|
||||||
int32_t m_latestMessage = 0; /**< Latest message received on voice */
|
int32_t m_latestMessage = 0; /**< Latest message received on voice */
|
||||||
std::list<ObjToken<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
|
std::list<ObjToken<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
|
||||||
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
||||||
|
|
||||||
ObjToken<SampleEntryData> m_curSample; /**< Current sample entry playing */
|
ObjToken<SampleEntryData> m_curSample; /**< Current sample entry playing */
|
||||||
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
|
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
|
||||||
SampleFormat m_curFormat; /**< Current sample format playing */
|
SampleFormat m_curFormat; /**< Current sample format playing */
|
||||||
uint32_t m_curSamplePos = 0; /**< Current sample position */
|
uint32_t m_curSamplePos = 0; /**< Current sample position */
|
||||||
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
||||||
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
||||||
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
|
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
|
||||||
double m_dopplerRatio = 1.0; /**< Current ratio to mix with chromatic pitch for doppler effects */
|
double m_dopplerRatio = 1.0; /**< Current ratio to mix with chromatic pitch for doppler effects */
|
||||||
double m_sampleRate = NativeSampleRate; /**< Current sample rate computed from relative sample key or SETPITCH */
|
double m_sampleRate = NativeSampleRate; /**< Current sample rate computed from relative sample key or SETPITCH */
|
||||||
double m_voiceTime = 0.0; /**< Current seconds of voice playback (per-sample resolution) */
|
double m_voiceTime = 0.0; /**< Current seconds of voice playback (per-sample resolution) */
|
||||||
uint64_t m_voiceSamples = 0; /**< Count of samples processed over voice's lifetime */
|
uint64_t m_voiceSamples = 0; /**< Count of samples processed over voice's lifetime */
|
||||||
float m_lastLevel = 0.f; /**< Last computed level ([0,1] mapped to [-10,0] clamped decibels) */
|
float m_lastLevel = 0.f; /**< Last computed level ([0,1] mapped to [-10,0] clamped decibels) */
|
||||||
float m_nextLevel = 0.f; /**< Next computed level used for lerp-mode amplitude */
|
float m_nextLevel = 0.f; /**< Next computed level used for lerp-mode amplitude */
|
||||||
VolumeCache m_nextLevelCache;
|
VolumeCache m_nextLevelCache;
|
||||||
VolumeCache m_lerpedCache;
|
VolumeCache m_lerpedCache;
|
||||||
|
|
||||||
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
|
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
|
||||||
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
|
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
|
||||||
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
||||||
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
|
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
|
||||||
|
|
||||||
float m_targetUserVol = 1.f; /**< Target user volume of voice (slewed to prevent audible aliasing) */
|
float m_targetUserVol = 1.f; /**< Target user volume of voice (slewed to prevent audible aliasing) */
|
||||||
float m_curUserVol = 1.f; /**< Current user volume of voice */
|
float m_curUserVol = 1.f; /**< Current user volume of voice */
|
||||||
float m_curVol = 1.f; /**< Current volume of voice */
|
float m_curVol = 1.f; /**< Current volume of voice */
|
||||||
float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
|
float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
|
||||||
float m_curAuxBVol = 0.f; /**< Current AuxB volume of voice */
|
float m_curAuxBVol = 0.f; /**< Current AuxB volume of voice */
|
||||||
float m_curPan = 0.f; /**< Current pan of voice */
|
float m_curPan = 0.f; /**< Current pan of voice */
|
||||||
float m_curSpan = -1.f; /**< Current surround pan of voice */
|
float m_curSpan = -1.f; /**< Current surround pan of voice */
|
||||||
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
|
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
|
||||||
int32_t m_pitchWheelUp = 200; /**< Up range for pitchwheel control in cents */
|
int32_t m_pitchWheelUp = 200; /**< Up range for pitchwheel control in cents */
|
||||||
int32_t m_pitchWheelDown = 200; /**< Down range for pitchwheel control in cents */
|
int32_t m_pitchWheelDown = 200; /**< Down range for pitchwheel control in cents */
|
||||||
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
|
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
|
||||||
int32_t m_curPitch; /**< Current base pitch in cents */
|
int32_t m_curPitch; /**< Current base pitch in cents */
|
||||||
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
|
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
|
||||||
bool m_needsSlew = false; /**< next _setTotalPitch will be slewed */
|
bool m_needsSlew = false; /**< next _setTotalPitch will be slewed */
|
||||||
bool m_dlsVol = false; /**< Use DLS LUT for resolving voice volume */
|
bool m_dlsVol = false; /**< Use DLS LUT for resolving voice volume */
|
||||||
|
|
||||||
Envelope m_volAdsr; /**< Volume envelope */
|
Envelope m_volAdsr; /**< Volume envelope */
|
||||||
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
|
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
|
||||||
double m_envelopeDur; /**< requested duration of last ENVELOPE command */
|
double m_envelopeDur; /**< requested duration of last ENVELOPE command */
|
||||||
float m_envelopeStart; /**< initial value for last ENVELOPE command */
|
float m_envelopeStart; /**< initial value for last ENVELOPE command */
|
||||||
float m_envelopeEnd; /**< final value for last ENVELOPE command */
|
float m_envelopeEnd; /**< final value for last ENVELOPE command */
|
||||||
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
|
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
|
||||||
|
|
||||||
bool m_pitchEnv = false; /**< Pitch envelope activated */
|
bool m_pitchEnv = false; /**< Pitch envelope activated */
|
||||||
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
|
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
|
||||||
int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */
|
int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */
|
||||||
|
|
||||||
float m_portamentoTime = -1.f; /**< time since last portamento invocation, -1 for no active portamento-glide */
|
float m_portamentoTime = -1.f; /**< time since last portamento invocation, -1 for no active portamento-glide */
|
||||||
int32_t m_portamentoTarget; /**< destination pitch for latest portamento invocation */
|
int32_t m_portamentoTarget; /**< destination pitch for latest portamento invocation */
|
||||||
|
|
||||||
int32_t m_pitchSweep1 = 0; /**< Current value of PITCHSWEEP1 controller (in cents) */
|
int32_t m_pitchSweep1 = 0; /**< Current value of PITCHSWEEP1 controller (in cents) */
|
||||||
int32_t m_pitchSweep2 = 0; /**< Current value of PITCHSWEEP2 controller (in cents) */
|
int32_t m_pitchSweep2 = 0; /**< Current value of PITCHSWEEP2 controller (in cents) */
|
||||||
int16_t m_pitchSweep1Add = 0; /**< Value to add to PITCHSWEEP1 controller each cycle */
|
int16_t m_pitchSweep1Add = 0; /**< Value to add to PITCHSWEEP1 controller each cycle */
|
||||||
int16_t m_pitchSweep2Add = 0; /**< Value to add to PITCHSWEEP2 controller each cycle */
|
int16_t m_pitchSweep2Add = 0; /**< Value to add to PITCHSWEEP2 controller each cycle */
|
||||||
uint8_t m_pitchSweep1Times = 0; /**< Remaining times to advance PITCHSWEEP1 controller */
|
uint8_t m_pitchSweep1Times = 0; /**< Remaining times to advance PITCHSWEEP1 controller */
|
||||||
uint8_t m_pitchSweep2Times = 0; /**< Remaining times to advance PITCHSWEEP2 controller */
|
uint8_t m_pitchSweep2Times = 0; /**< Remaining times to advance PITCHSWEEP2 controller */
|
||||||
uint8_t m_pitchSweep1It = 0; /**< Current iteration of PITCHSWEEP1 controller */
|
uint8_t m_pitchSweep1It = 0; /**< Current iteration of PITCHSWEEP1 controller */
|
||||||
uint8_t m_pitchSweep2It = 0; /**< Current iteration of PITCHSWEEP2 controller */
|
uint8_t m_pitchSweep2It = 0; /**< Current iteration of PITCHSWEEP2 controller */
|
||||||
|
|
||||||
std::queue<Panning> m_panningQueue; /**< Queue of PANNING commands */
|
std::queue<Panning> m_panningQueue; /**< Queue of PANNING commands */
|
||||||
std::queue<Panning> m_spanningQueue; /**< Queue of SPANNING commands */
|
std::queue<Panning> m_spanningQueue; /**< Queue of SPANNING commands */
|
||||||
|
|
||||||
float m_vibratoTime = -1.f; /**< time since last VIBRATO command, -1 for no active vibrato */
|
float m_vibratoTime = -1.f; /**< time since last VIBRATO command, -1 for no active vibrato */
|
||||||
int32_t m_vibratoLevel = 0; /**< scale of vibrato effect (in cents) */
|
int32_t m_vibratoLevel = 0; /**< scale of vibrato effect (in cents) */
|
||||||
int32_t m_vibratoModLevel = 0; /**< scale of vibrato mod-wheel influence (in cents) */
|
int32_t m_vibratoModLevel = 0; /**< scale of vibrato mod-wheel influence (in cents) */
|
||||||
float m_vibratoPeriod = 0.f; /**< vibrato wave period-time, 0.f will disable vibrato */
|
float m_vibratoPeriod = 0.f; /**< vibrato wave period-time, 0.f will disable vibrato */
|
||||||
bool m_vibratoModWheel = false; /**< vibrato scaled with mod-wheel if set */
|
bool m_vibratoModWheel = false; /**< vibrato scaled with mod-wheel if set */
|
||||||
|
|
||||||
float m_tremoloScale = 0.f; /**< minimum volume factor produced via LFO */
|
float m_tremoloScale = 0.f; /**< minimum volume factor produced via LFO */
|
||||||
float m_tremoloModScale = 0.f; /**< minimum volume factor produced via LFO, scaled via mod wheel */
|
float m_tremoloModScale = 0.f; /**< minimum volume factor produced via LFO, scaled via mod wheel */
|
||||||
|
|
||||||
float m_lfoPeriods[2] = {}; /**< time-periods for LFO1 and LFO2 */
|
float m_lfoPeriods[2] = {}; /**< time-periods for LFO1 and LFO2 */
|
||||||
std::unique_ptr<int8_t[]> m_ctrlValsSelf; /**< Self-owned MIDI Controller values */
|
std::unique_ptr<int8_t[]> m_ctrlValsSelf; /**< Self-owned MIDI Controller values */
|
||||||
int8_t* m_extCtrlVals = nullptr; /**< MIDI Controller values (external storage) */
|
int8_t* m_extCtrlVals = nullptr; /**< MIDI Controller values (external storage) */
|
||||||
|
|
||||||
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
|
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
|
||||||
|
|
||||||
void _destroy();
|
void _destroy();
|
||||||
bool _checkSamplePos(bool& looped);
|
bool _checkSamplePos(bool& looped);
|
||||||
void _doKeyOff();
|
void _doKeyOff();
|
||||||
void _macroKeyOff();
|
void _macroKeyOff();
|
||||||
void _macroSampleEnd();
|
void _macroSampleEnd();
|
||||||
void _procSamplePre(int16_t& samp);
|
void _procSamplePre(int16_t& samp);
|
||||||
VolumeCache m_masterCache;
|
VolumeCache m_masterCache;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T _procSampleMaster(double time, T samp);
|
T _procSampleMaster(double time, T samp);
|
||||||
VolumeCache m_auxACache;
|
VolumeCache m_auxACache;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T _procSampleAuxA(double time, T samp);
|
T _procSampleAuxA(double time, T samp);
|
||||||
VolumeCache m_auxBCache;
|
VolumeCache m_auxBCache;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T _procSampleAuxB(double time, T samp);
|
T _procSampleAuxB(double time, T samp);
|
||||||
void _setTotalPitch(int32_t cents, bool slew);
|
void _setTotalPitch(int32_t cents, bool slew);
|
||||||
bool _isRecursivelyDead();
|
bool _isRecursivelyDead();
|
||||||
void _bringOutYourDead();
|
void _bringOutYourDead();
|
||||||
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
|
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
|
||||||
ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
|
ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
|
||||||
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
|
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
|
std::list<ObjToken<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
|
||||||
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
|
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
|
||||||
|
|
||||||
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
|
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, uint8_t midiKey,
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
||||||
bool _loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey,
|
bool _loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
||||||
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
bool pushPc = false);
|
||||||
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec,
|
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
||||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
uint8_t midiMod, bool pushPc = false);
|
||||||
ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
|
ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
|
||||||
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
||||||
|
|
||||||
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
|
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
|
||||||
void _setPan(float pan);
|
void _setPan(float pan);
|
||||||
void _setSurroundPan(float span);
|
void _setSurroundPan(float span);
|
||||||
void _setChannelCoefs(const float coefs[8]);
|
void _setChannelCoefs(const float coefs[8]);
|
||||||
void _setPitchWheel(float pitchWheel);
|
void _setPitchWheel(float pitchWheel);
|
||||||
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Voice();
|
~Voice();
|
||||||
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken<Studio> studio);
|
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken<Studio> studio);
|
||||||
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter,
|
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter,
|
||||||
ObjToken<Studio> studio);
|
ObjToken<Studio> studio);
|
||||||
|
|
||||||
/** Called before each supplyAudio invocation to prepare voice
|
/** Called before each supplyAudio invocation to prepare voice
|
||||||
* backend for possible parameter updates */
|
* backend for possible parameter updates */
|
||||||
void preSupplyAudio(double dt);
|
void preSupplyAudio(double dt);
|
||||||
|
|
||||||
/** Request specified count of audio frames (samples) from voice,
|
/** Request specified count of audio frames (samples) from voice,
|
||||||
* internally advancing the voice stream */
|
* internally advancing the voice stream */
|
||||||
size_t supplyAudio(size_t frames, int16_t* data);
|
size_t supplyAudio(size_t frames, int16_t* data);
|
||||||
|
|
||||||
/** Called three times after resampling supplyAudio output, voice should
|
/** Called three times after resampling supplyAudio output, voice should
|
||||||
* perform volume processing / send routing for each aux bus and master */
|
* perform volume processing / send routing for each aux bus and master */
|
||||||
void routeAudio(size_t frames, double dt, int busId, int16_t* in, int16_t* out);
|
void routeAudio(size_t frames, double dt, int busId, int16_t* in, int16_t* out);
|
||||||
void routeAudio(size_t frames, double dt, int busId, int32_t* in, int32_t* out);
|
void routeAudio(size_t frames, double dt, int busId, int32_t* in, int32_t* out);
|
||||||
void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
|
void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
|
||||||
|
|
||||||
/** Obtain pointer to Voice's Studio */
|
/** Obtain pointer to Voice's Studio */
|
||||||
ObjToken<Studio> getStudio() { return m_studio; }
|
ObjToken<Studio> getStudio() { return m_studio; }
|
||||||
|
|
||||||
/** Get current state of voice */
|
/** Get current state of voice */
|
||||||
VoiceState state() const { return m_voxState; }
|
VoiceState state() const { return m_voxState; }
|
||||||
|
|
||||||
/** Get VoiceId of this voice (unique to all currently-playing voices) */
|
/** Get VoiceId of this voice (unique to all currently-playing voices) */
|
||||||
int vid() const { return m_vid; }
|
int vid() const { return m_vid; }
|
||||||
|
|
||||||
/** Get max VoiceId of this voice and any contained children */
|
/** Get max VoiceId of this voice and any contained children */
|
||||||
int maxVid() const;
|
int maxVid() const;
|
||||||
|
|
||||||
/** Allocate parallel macro and tie to voice for possible emitter influence */
|
/** Allocate parallel macro and tie to voice for possible emitter influence */
|
||||||
ObjToken<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
|
ObjToken<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
|
||||||
|
|
||||||
/** Load specified SoundMacro Object from within group into voice */
|
/** Load specified SoundMacro Object from within group into voice */
|
||||||
bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
||||||
uint8_t midiMod, bool pushPc = false);
|
uint8_t midiMod, bool pushPc = false);
|
||||||
|
|
||||||
/** Load specified SoundMacro Object into voice */
|
/** Load specified SoundMacro Object into voice */
|
||||||
bool loadMacroObject(const SoundMacro* macro, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
bool loadMacroObject(const SoundMacro* macro, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
||||||
uint8_t midiMod, bool pushPc = false);
|
uint8_t midiMod, bool pushPc = false);
|
||||||
|
|
||||||
/** Load specified song page object (Keymap/Layer) from within group into voice */
|
/** Load specified song page object (Keymap/Layer) from within group into voice */
|
||||||
bool loadPageObject(ObjectId objectId, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
|
bool loadPageObject(ObjectId objectId, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
|
||||||
|
|
||||||
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
||||||
void keyOff();
|
void keyOff();
|
||||||
|
|
||||||
/** Sends numeric message to voice and all siblings */
|
/** Sends numeric message to voice and all siblings */
|
||||||
void message(int32_t val);
|
void message(int32_t val);
|
||||||
|
|
||||||
/** Start playing specified sample from within group, optionally by sample offset */
|
/** Start playing specified sample from within group, optionally by sample offset */
|
||||||
void startSample(SampleId sampId, int32_t offset);
|
void startSample(SampleId sampId, int32_t offset);
|
||||||
|
|
||||||
/** Stop playing current sample */
|
/** Stop playing current sample */
|
||||||
void stopSample();
|
void stopSample();
|
||||||
|
|
||||||
/** Set current voice volume immediately */
|
/** Set current voice volume immediately */
|
||||||
void setVolume(float vol);
|
void setVolume(float vol);
|
||||||
|
|
||||||
/** Set current voice panning immediately */
|
/** Set current voice panning immediately */
|
||||||
void setPan(float pan);
|
void setPan(float pan);
|
||||||
|
|
||||||
/** Set current voice surround-panning immediately */
|
/** Set current voice surround-panning immediately */
|
||||||
void setSurroundPan(float span);
|
void setSurroundPan(float span);
|
||||||
|
|
||||||
/** Set current voice channel coefficients immediately */
|
/** Set current voice channel coefficients immediately */
|
||||||
void setChannelCoefs(const float coefs[8]);
|
void setChannelCoefs(const float coefs[8]);
|
||||||
|
|
||||||
/** Start volume envelope to specified level */
|
/** Start volume envelope to specified level */
|
||||||
void startEnvelope(double dur, float vol, const Curve* envCurve);
|
void startEnvelope(double dur, float vol, const Curve* envCurve);
|
||||||
|
|
||||||
/** Start volume envelope from zero to current level */
|
/** Start volume envelope from zero to current level */
|
||||||
void startFadeIn(double dur, float vol, const Curve* envCurve);
|
void startFadeIn(double dur, float vol, const Curve* envCurve);
|
||||||
|
|
||||||
/** Start pan envelope to specified position */
|
/** Start pan envelope to specified position */
|
||||||
void startPanning(double dur, uint8_t panPos, int8_t panWidth);
|
void startPanning(double dur, uint8_t panPos, int8_t panWidth);
|
||||||
|
|
||||||
/** Start span envelope to specified position */
|
/** Start span envelope to specified position */
|
||||||
void startSpanning(double dur, uint8_t spanPos, int8_t spanWidth);
|
void startSpanning(double dur, uint8_t spanPos, int8_t spanWidth);
|
||||||
|
|
||||||
/** Set voice relative-pitch in cents */
|
/** Set voice relative-pitch in cents */
|
||||||
void setPitchKey(int32_t cents);
|
void setPitchKey(int32_t cents);
|
||||||
|
|
||||||
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
|
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
|
||||||
void setPedal(bool pedal);
|
void setPedal(bool pedal);
|
||||||
|
|
||||||
/** Set doppler factor for voice */
|
/** Set doppler factor for voice */
|
||||||
void setDoppler(float doppler);
|
void setDoppler(float doppler);
|
||||||
|
|
||||||
/** Set vibrato parameters for voice */
|
/** Set vibrato parameters for voice */
|
||||||
void setVibrato(int32_t level, bool modScale, float period);
|
void setVibrato(int32_t level, bool modScale, float period);
|
||||||
|
|
||||||
/** Configure modwheel influence range over vibrato */
|
/** Configure modwheel influence range over vibrato */
|
||||||
void setMod2VibratoRange(int32_t modLevel);
|
void setMod2VibratoRange(int32_t modLevel);
|
||||||
|
|
||||||
/** Setup tremolo parameters for voice */
|
/** Setup tremolo parameters for voice */
|
||||||
void setTremolo(float tremoloScale, float tremoloModScale);
|
void setTremolo(float tremoloScale, float tremoloModScale);
|
||||||
|
|
||||||
/** Setup LFO1 for voice */
|
/** Setup LFO1 for voice */
|
||||||
void setLFO1Period(float period) { m_lfoPeriods[0] = period; }
|
void setLFO1Period(float period) { m_lfoPeriods[0] = period; }
|
||||||
|
|
||||||
/** Setup LFO2 for voice */
|
/** Setup LFO2 for voice */
|
||||||
void setLFO2Period(float period) { m_lfoPeriods[1] = period; }
|
void setLFO2Period(float period) { m_lfoPeriods[1] = period; }
|
||||||
|
|
||||||
/** Setup pitch sweep controller 1 */
|
/** Setup pitch sweep controller 1 */
|
||||||
void setPitchSweep1(uint8_t times, int16_t add);
|
void setPitchSweep1(uint8_t times, int16_t add);
|
||||||
|
|
||||||
/** Setup pitch sweep controller 2 */
|
/** Setup pitch sweep controller 2 */
|
||||||
void setPitchSweep2(uint8_t times, int16_t add);
|
void setPitchSweep2(uint8_t times, int16_t add);
|
||||||
|
|
||||||
/** Set reverb mix for voice */
|
/** Set reverb mix for voice */
|
||||||
void setReverbVol(float rvol);
|
void setReverbVol(float rvol);
|
||||||
|
|
||||||
/** Set AuxB volume for voice */
|
/** Set AuxB volume for voice */
|
||||||
void setAuxBVol(float bvol);
|
void setAuxBVol(float bvol);
|
||||||
|
|
||||||
/** Set envelope for voice */
|
/** Set envelope for voice */
|
||||||
void setAdsr(ObjectId adsrId, bool dls);
|
void setAdsr(ObjectId adsrId, bool dls);
|
||||||
|
|
||||||
/** Set pitch in absolute hertz */
|
/** Set pitch in absolute hertz */
|
||||||
void setPitchFrequency(uint32_t hz, uint16_t fine);
|
void setPitchFrequency(uint32_t hz, uint16_t fine);
|
||||||
|
|
||||||
/** Set pitch envelope */
|
/** Set pitch envelope */
|
||||||
void setPitchAdsr(ObjectId adsrId, int32_t cents);
|
void setPitchAdsr(ObjectId adsrId, int32_t cents);
|
||||||
|
|
||||||
/** Set pitchwheel value for use with controller */
|
/** Set pitchwheel value for use with controller */
|
||||||
void setPitchWheel(float pitchWheel);
|
void setPitchWheel(float pitchWheel);
|
||||||
|
|
||||||
/** Set effective pitch range via the pitchwheel controller */
|
/** Set effective pitch range via the pitchwheel controller */
|
||||||
void setPitchWheelRange(int8_t up, int8_t down);
|
void setPitchWheelRange(int8_t up, int8_t down);
|
||||||
|
|
||||||
/** Set aftertouch */
|
/** Set aftertouch */
|
||||||
void setAftertouch(uint8_t aftertouch);
|
void setAftertouch(uint8_t aftertouch);
|
||||||
|
|
||||||
/** Assign voice to keygroup for coordinated mass-silencing */
|
/** Assign voice to keygroup for coordinated mass-silencing */
|
||||||
void setKeygroup(uint8_t kg) { m_keygroup = kg; }
|
void setKeygroup(uint8_t kg) { m_keygroup = kg; }
|
||||||
|
|
||||||
/** Get note played on voice */
|
/** Get note played on voice */
|
||||||
uint8_t getLastNote() const { return m_state.m_initKey; }
|
uint8_t getLastNote() const { return m_state.m_initKey; }
|
||||||
|
|
||||||
/** Do portamento glide; returns `false` if portamento disabled */
|
/** Do portamento glide; returns `false` if portamento disabled */
|
||||||
bool doPortamento(uint8_t newNote);
|
bool doPortamento(uint8_t newNote);
|
||||||
|
|
||||||
/** Get MIDI Controller value on voice */
|
/** Get MIDI Controller value on voice */
|
||||||
int8_t getCtrlValue(uint8_t ctrl) const
|
int8_t getCtrlValue(uint8_t ctrl) const {
|
||||||
{
|
if (!m_extCtrlVals) {
|
||||||
if (!m_extCtrlVals)
|
if (m_ctrlValsSelf)
|
||||||
{
|
return m_ctrlValsSelf[ctrl];
|
||||||
if (m_ctrlValsSelf)
|
return 0;
|
||||||
return m_ctrlValsSelf[ctrl];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return m_extCtrlVals[ctrl];
|
|
||||||
}
|
}
|
||||||
|
return m_extCtrlVals[ctrl];
|
||||||
|
}
|
||||||
|
|
||||||
/** Set MIDI Controller value on voice */
|
/** Set MIDI Controller value on voice */
|
||||||
void setCtrlValue(uint8_t ctrl, int8_t val)
|
void setCtrlValue(uint8_t ctrl, int8_t val) {
|
||||||
{
|
if (!m_extCtrlVals) {
|
||||||
if (!m_extCtrlVals)
|
std::unique_ptr<int8_t[]>& vals = _ensureCtrlVals();
|
||||||
{
|
vals[ctrl] = val;
|
||||||
std::unique_ptr<int8_t[]>& vals = _ensureCtrlVals();
|
} else
|
||||||
vals[ctrl] = val;
|
m_extCtrlVals[ctrl] = val;
|
||||||
}
|
_notifyCtrlChange(ctrl, val);
|
||||||
else
|
}
|
||||||
m_extCtrlVals[ctrl] = val;
|
|
||||||
_notifyCtrlChange(ctrl, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 'install' external MIDI controller storage */
|
/** 'install' external MIDI controller storage */
|
||||||
void installCtrlValues(int8_t* cvs)
|
void installCtrlValues(int8_t* cvs) {
|
||||||
{
|
m_ctrlValsSelf.reset();
|
||||||
m_ctrlValsSelf.reset();
|
m_extCtrlVals = cvs;
|
||||||
m_extCtrlVals = cvs;
|
for (ObjToken<Voice>& vox : m_childVoices)
|
||||||
for (ObjToken<Voice>& vox : m_childVoices)
|
vox->installCtrlValues(cvs);
|
||||||
vox->installCtrlValues(cvs);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Get MIDI pitch wheel value on voice */
|
/** Get MIDI pitch wheel value on voice */
|
||||||
float getPitchWheel() const { return m_curPitchWheel; }
|
float getPitchWheel() const { return m_curPitchWheel; }
|
||||||
|
|
||||||
/** Get MIDI aftertouch value on voice */
|
/** Get MIDI aftertouch value on voice */
|
||||||
int8_t getAftertouch() const { return m_curAftertouch; }
|
int8_t getAftertouch() const { return m_curAftertouch; }
|
||||||
|
|
||||||
/** Get count of all voices in hierarchy, including this one */
|
/** Get count of all voices in hierarchy, including this one */
|
||||||
size_t getTotalVoices() const;
|
size_t getTotalVoices() const;
|
||||||
|
|
||||||
/** Get latest decoded sample index */
|
/** Get latest decoded sample index */
|
||||||
uint32_t getSamplePos() const { return m_curSamplePos; }
|
uint32_t getSamplePos() const { return m_curSamplePos; }
|
||||||
|
|
||||||
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
|
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
|
||||||
void kill();
|
void kill();
|
||||||
};
|
};
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
float LookupVolume(float vol);
|
float LookupVolume(float vol);
|
||||||
float LookupDLSVolume(float vol);
|
float LookupDLSVolume(float vol);
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include "AudioGroup.hpp"
|
#include "AudioGroup.hpp"
|
||||||
#include "AudioGroupData.hpp"
|
#include "AudioGroupData.hpp"
|
||||||
#include "AudioGroupPool.hpp"
|
#include "AudioGroupPool.hpp"
|
||||||
|
@ -20,4 +19,3 @@
|
||||||
#include "SongState.hpp"
|
#include "SongState.hpp"
|
||||||
#include "Submix.hpp"
|
#include "Submix.hpp"
|
||||||
#include "Voice.hpp"
|
#include "Voice.hpp"
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,9 @@
|
||||||
#undef log2
|
#undef log2
|
||||||
#undef fabs
|
#undef fabs
|
||||||
|
|
||||||
namespace std
|
namespace std {
|
||||||
{
|
using ::exp2;
|
||||||
using ::exp2;
|
using ::fabs;
|
||||||
using ::log2;
|
using ::log2;
|
||||||
using ::fabs;
|
} // namespace std
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -5,372 +5,329 @@
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
void AudioGroup::assign(const AudioGroupData& data)
|
void AudioGroup::assign(const AudioGroupData& data) {
|
||||||
{
|
m_pool = AudioGroupPool::CreateAudioGroupPool(data);
|
||||||
m_pool = AudioGroupPool::CreateAudioGroupPool(data);
|
m_proj = AudioGroupProject::CreateAudioGroupProject(data);
|
||||||
m_proj = AudioGroupProject::CreateAudioGroupProject(data);
|
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data);
|
||||||
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data);
|
m_samp = data.getSamp();
|
||||||
m_samp = data.getSamp();
|
|
||||||
}
|
}
|
||||||
void AudioGroup::assign(SystemStringView groupPath)
|
void AudioGroup::assign(SystemStringView groupPath) {
|
||||||
{
|
/* Reverse order when loading intermediates */
|
||||||
/* Reverse order when loading intermediates */
|
m_groupPath = groupPath;
|
||||||
m_groupPath = groupPath;
|
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
|
||||||
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
|
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
|
||||||
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
|
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
|
||||||
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
|
m_samp = nullptr;
|
||||||
m_samp = nullptr;
|
|
||||||
}
|
}
|
||||||
void AudioGroup::assign(const AudioGroup& data, SystemStringView groupPath)
|
void AudioGroup::assign(const AudioGroup& data, SystemStringView groupPath) {
|
||||||
{
|
/* Reverse order when loading intermediates */
|
||||||
/* Reverse order when loading intermediates */
|
m_groupPath = groupPath;
|
||||||
m_groupPath = groupPath;
|
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
|
||||||
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
|
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
|
||||||
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
|
m_proj = AudioGroupProject::CreateAudioGroupProject(data.getProj());
|
||||||
m_proj = AudioGroupProject::CreateAudioGroupProject(data.getProj());
|
m_samp = nullptr;
|
||||||
m_samp = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SampleEntry* AudioGroup::getSample(SampleId sfxId) const
|
const SampleEntry* AudioGroup::getSample(SampleId sfxId) const {
|
||||||
{
|
auto search = m_sdir.m_entries.find(sfxId);
|
||||||
auto search = m_sdir.m_entries.find(sfxId);
|
if (search == m_sdir.m_entries.cend())
|
||||||
if (search == m_sdir.m_entries.cend())
|
return nullptr;
|
||||||
return nullptr;
|
return search->second.get();
|
||||||
return search->second.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemString AudioGroup::getSampleBasePath(SampleId sfxId) const
|
SystemString AudioGroup::getSampleBasePath(SampleId sfxId) const {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return m_groupPath + _SYS_STR('/') +
|
return m_groupPath + _SYS_STR('/') + athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId));
|
||||||
athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId));
|
|
||||||
#else
|
#else
|
||||||
return m_groupPath + _SYS_STR('/') +
|
return m_groupPath + _SYS_STR('/') + SampleId::CurNameDB->resolveNameFromId(sfxId).data();
|
||||||
SampleId::CurNameDB->resolveNameFromId(sfxId).data();
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ObjToken<SampleEntryData>, const unsigned char*>
|
std::pair<ObjToken<SampleEntryData>, const unsigned char*> AudioGroup::getSampleData(SampleId sfxId,
|
||||||
AudioGroup::getSampleData(SampleId sfxId, const SampleEntry* sample) const
|
const SampleEntry* sample) const {
|
||||||
{
|
if (sample->m_data->m_looseData) {
|
||||||
if (sample->m_data->m_looseData)
|
setIdDatabases();
|
||||||
{
|
SystemString basePath = getSampleBasePath(sfxId);
|
||||||
setIdDatabases();
|
const_cast<SampleEntry*>(sample)->loadLooseData(basePath);
|
||||||
SystemString basePath = getSampleBasePath(sfxId);
|
return {sample->m_data, sample->m_data->m_looseData.get()};
|
||||||
const_cast<SampleEntry*>(sample)->loadLooseData(basePath);
|
}
|
||||||
return {sample->m_data, sample->m_data->m_looseData.get()};
|
return {sample->m_data, m_samp + sample->m_data->m_sampleOff};
|
||||||
}
|
|
||||||
return {sample->m_data, m_samp + sample->m_data->m_sampleOff};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleFileState AudioGroup::getSampleFileState(SampleId sfxId, const SampleEntry* sample, SystemString* pathOut) const
|
SampleFileState AudioGroup::getSampleFileState(SampleId sfxId, const SampleEntry* sample, SystemString* pathOut) const {
|
||||||
{
|
if (sample->m_data->m_looseData) {
|
||||||
if (sample->m_data->m_looseData)
|
setIdDatabases();
|
||||||
{
|
SystemString basePath = getSampleBasePath(sfxId);
|
||||||
setIdDatabases();
|
return sample->getFileState(basePath, pathOut);
|
||||||
SystemString basePath = getSampleBasePath(sfxId);
|
}
|
||||||
return sample->getFileState(basePath, pathOut);
|
if (sample->m_data->isFormatDSP() || sample->m_data->getSampleFormat() == SampleFormat::N64)
|
||||||
}
|
return SampleFileState::MemoryOnlyCompressed;
|
||||||
if (sample->m_data->isFormatDSP() || sample->m_data->getSampleFormat() == SampleFormat::N64)
|
return SampleFileState::MemoryOnlyWAV;
|
||||||
return SampleFileState::MemoryOnlyCompressed;
|
|
||||||
return SampleFileState::MemoryOnlyWAV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroup::patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const
|
void AudioGroup::patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const {
|
||||||
{
|
if (sample->m_data->m_looseData) {
|
||||||
if (sample->m_data->m_looseData)
|
setIdDatabases();
|
||||||
{
|
SystemString basePath = getSampleBasePath(sfxId);
|
||||||
setIdDatabases();
|
sample->patchSampleMetadata(basePath);
|
||||||
SystemString basePath = getSampleBasePath(sfxId);
|
}
|
||||||
sample->patchSampleMetadata(basePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroup::makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const
|
void AudioGroup::makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const {
|
||||||
{
|
if (sample->m_data->m_looseData) {
|
||||||
if (sample->m_data->m_looseData)
|
setIdDatabases();
|
||||||
{
|
m_sdir._extractWAV(sfxId, *sample->m_data, m_groupPath, sample->m_data->m_looseData.get());
|
||||||
setIdDatabases();
|
}
|
||||||
m_sdir._extractWAV(sfxId, *sample->m_data, m_groupPath, sample->m_data->m_looseData.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroup::makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const
|
void AudioGroup::makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const {
|
||||||
{
|
if (sample->m_data->m_looseData) {
|
||||||
if (sample->m_data->m_looseData)
|
setIdDatabases();
|
||||||
{
|
m_sdir._extractCompressed(sfxId, *sample->m_data, m_groupPath, sample->m_data->m_looseData.get(), true);
|
||||||
setIdDatabases();
|
}
|
||||||
m_sdir._extractCompressed(sfxId, *sample->m_data, m_groupPath, sample->m_data->m_looseData.get(), true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupDatabase::renameSample(SampleId id, std::string_view str)
|
void AudioGroupDatabase::renameSample(SampleId id, std::string_view str) {
|
||||||
{
|
SystemString oldBasePath = getSampleBasePath(id);
|
||||||
SystemString oldBasePath = getSampleBasePath(id);
|
SampleId::CurNameDB->rename(id, str);
|
||||||
SampleId::CurNameDB->rename(id, str);
|
SystemString newBasePath = getSampleBasePath(id);
|
||||||
SystemString newBasePath = getSampleBasePath(id);
|
Rename((oldBasePath + _SYS_STR(".wav")).c_str(), (newBasePath + _SYS_STR(".wav")).c_str());
|
||||||
Rename((oldBasePath + _SYS_STR(".wav")).c_str(), (newBasePath + _SYS_STR(".wav")).c_str());
|
Rename((oldBasePath + _SYS_STR(".dsp")).c_str(), (newBasePath + _SYS_STR(".dsp")).c_str());
|
||||||
Rename((oldBasePath + _SYS_STR(".dsp")).c_str(), (newBasePath + _SYS_STR(".dsp")).c_str());
|
Rename((oldBasePath + _SYS_STR(".vadpcm")).c_str(), (newBasePath + _SYS_STR(".vadpcm")).c_str());
|
||||||
Rename((oldBasePath + _SYS_STR(".vadpcm")).c_str(), (newBasePath + _SYS_STR(".vadpcm")).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupDatabase::deleteSample(SampleId id)
|
void AudioGroupDatabase::deleteSample(SampleId id) {
|
||||||
{
|
SystemString basePath = getSampleBasePath(id);
|
||||||
SystemString basePath = getSampleBasePath(id);
|
Unlink((basePath + _SYS_STR(".wav")).c_str());
|
||||||
Unlink((basePath + _SYS_STR(".wav")).c_str());
|
Unlink((basePath + _SYS_STR(".dsp")).c_str());
|
||||||
Unlink((basePath + _SYS_STR(".dsp")).c_str());
|
Unlink((basePath + _SYS_STR(".vadpcm")).c_str());
|
||||||
Unlink((basePath + _SYS_STR(".vadpcm")).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupDatabase::copySampleInto(const SystemString& basePath, const SystemString& newBasePath)
|
void AudioGroupDatabase::copySampleInto(const SystemString& basePath, const SystemString& newBasePath) {
|
||||||
{
|
Copy((basePath + _SYS_STR(".wav")).c_str(), (newBasePath + _SYS_STR(".wav")).c_str());
|
||||||
Copy((basePath + _SYS_STR(".wav")).c_str(), (newBasePath + _SYS_STR(".wav")).c_str());
|
Copy((basePath + _SYS_STR(".dsp")).c_str(), (newBasePath + _SYS_STR(".dsp")).c_str());
|
||||||
Copy((basePath + _SYS_STR(".dsp")).c_str(), (newBasePath + _SYS_STR(".dsp")).c_str());
|
Copy((basePath + _SYS_STR(".vadpcm")).c_str(), (newBasePath + _SYS_STR(".vadpcm")).c_str());
|
||||||
Copy((basePath + _SYS_STR(".vadpcm")).c_str(), (newBasePath + _SYS_STR(".vadpcm")).c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGroupDatabase::_recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
|
void AudioGroupDatabase::_recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
|
||||||
std::unordered_set<SoundMacroId>& renamedIds)
|
std::unordered_set<SoundMacroId>& renamedIds) {
|
||||||
{
|
if (renamedIds.find(id) != renamedIds.cend())
|
||||||
if (renamedIds.find(id) != renamedIds.cend())
|
return;
|
||||||
return;
|
if (const SoundMacro* macro = getPool().soundMacro(id)) {
|
||||||
if (const SoundMacro* macro = getPool().soundMacro(id))
|
if (!strncmp(SoundMacroId::CurNameDB->resolveNameFromId(id).data(), "macro", 5)) {
|
||||||
{
|
std::string macroName("macro"sv);
|
||||||
if (!strncmp(SoundMacroId::CurNameDB->resolveNameFromId(id).data(), "macro", 5))
|
if (macroIdx) {
|
||||||
{
|
char num[16];
|
||||||
std::string macroName("macro"sv);
|
snprintf(num, 16, "%d", macroIdx);
|
||||||
if (macroIdx)
|
macroName += num;
|
||||||
{
|
}
|
||||||
char num[16];
|
macroName += '_';
|
||||||
snprintf(num, 16, "%d", macroIdx);
|
macroName += str;
|
||||||
macroName += num;
|
++macroIdx;
|
||||||
}
|
SoundMacroId::CurNameDB->rename(id, macroName);
|
||||||
macroName += '_';
|
renamedIds.insert(id);
|
||||||
macroName += str;
|
int sampleIdx = 0;
|
||||||
++macroIdx;
|
for (const auto& cmd : macro->m_cmds) {
|
||||||
SoundMacroId::CurNameDB->rename(id, macroName);
|
switch (cmd->Isa()) {
|
||||||
renamedIds.insert(id);
|
case SoundMacro::CmdOp::StartSample: {
|
||||||
int sampleIdx = 0;
|
SoundMacro::CmdStartSample* ss = static_cast<SoundMacro::CmdStartSample*>(cmd.get());
|
||||||
for (const auto& cmd : macro->m_cmds)
|
if (!strncmp(SampleId::CurNameDB->resolveNameFromId(ss->sample.id).data(), "sample", 6)) {
|
||||||
{
|
std::string sampleName("sample"sv);
|
||||||
switch (cmd->Isa())
|
if (sampleIdx) {
|
||||||
{
|
char num[16];
|
||||||
case SoundMacro::CmdOp::StartSample:
|
snprintf(num, 16, "%d", sampleIdx);
|
||||||
{
|
sampleName += num;
|
||||||
SoundMacro::CmdStartSample* ss = static_cast<SoundMacro::CmdStartSample*>(cmd.get());
|
|
||||||
if (!strncmp(SampleId::CurNameDB->resolveNameFromId(ss->sample.id).data(), "sample", 6))
|
|
||||||
{
|
|
||||||
std::string sampleName("sample"sv);
|
|
||||||
if (sampleIdx)
|
|
||||||
{
|
|
||||||
char num[16];
|
|
||||||
snprintf(num, 16, "%d", sampleIdx);
|
|
||||||
sampleName += num;
|
|
||||||
}
|
|
||||||
sampleName += '_';
|
|
||||||
sampleName += macroName;
|
|
||||||
++sampleIdx;
|
|
||||||
renameSample(ss->sample.id, sampleName);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SoundMacro::CmdOp::SplitKey:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitKey*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::SplitVel:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitVel*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::Goto:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdGoto*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::PlayMacro:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdPlayMacro*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::SplitMod:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitMod*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::SplitRnd:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitRnd*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::GoSub:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdGoSub*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::TrapEvent:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdTrapEvent*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
case SoundMacro::CmdOp::SendMessage:
|
|
||||||
_recursiveRenameMacro(static_cast<SoundMacro::CmdSendMessage*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
sampleName += '_';
|
||||||
|
sampleName += macroName;
|
||||||
|
++sampleIdx;
|
||||||
|
renameSample(ss->sample.id, sampleName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
case SoundMacro::CmdOp::SplitKey:
|
||||||
}
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitKey*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
|
break;
|
||||||
static const std::regex DefineGRPEntry(R"(#define\s+GRP(\S+)\s+(\S+))",
|
case SoundMacro::CmdOp::SplitVel:
|
||||||
std::regex::ECMAScript|std::regex::optimize);
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitVel*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
static const std::regex DefineSNGEntry(R"(#define\s+SNG(\S+)\s+(\S+))",
|
break;
|
||||||
std::regex::ECMAScript|std::regex::optimize);
|
case SoundMacro::CmdOp::Goto:
|
||||||
static const std::regex DefineSFXEntry(R"(#define\s+SFX(\S+)\s+(\S+))",
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdGoto*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
std::regex::ECMAScript|std::regex::optimize);
|
break;
|
||||||
|
case SoundMacro::CmdOp::PlayMacro:
|
||||||
void AudioGroupDatabase::importCHeader(std::string_view header)
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdPlayMacro*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
{
|
break;
|
||||||
setIdDatabases();
|
case SoundMacro::CmdOp::SplitMod:
|
||||||
std::match_results<std::string_view::const_iterator> dirMatch;
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitMod*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
auto begin = header.cbegin();
|
break;
|
||||||
auto end = header.cend();
|
case SoundMacro::CmdOp::SplitRnd:
|
||||||
while (std::regex_search(begin, end, dirMatch, DefineGRPEntry))
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdSplitRnd*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
{
|
break;
|
||||||
std::string key = dirMatch[1].str();
|
case SoundMacro::CmdOp::GoSub:
|
||||||
std::string value = dirMatch[2].str();
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdGoSub*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
char* endPtr;
|
break;
|
||||||
amuse::ObjectId id;
|
case SoundMacro::CmdOp::TrapEvent:
|
||||||
id.id = strtoul(value.c_str(), &endPtr, 0);
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdTrapEvent*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
if (endPtr == value.c_str())
|
break;
|
||||||
continue;
|
case SoundMacro::CmdOp::SendMessage:
|
||||||
GroupId::CurNameDB->rename(id, key);
|
_recursiveRenameMacro(static_cast<SoundMacro::CmdSendMessage*>(cmd.get())->macro, str, macroIdx, renamedIds);
|
||||||
begin = dirMatch.suffix().first;
|
break;
|
||||||
}
|
default:
|
||||||
begin = header.cbegin();
|
break;
|
||||||
end = header.cend();
|
|
||||||
while (std::regex_search(begin, end, dirMatch, DefineSNGEntry))
|
|
||||||
{
|
|
||||||
std::string key = dirMatch[1].str();
|
|
||||||
std::string value = dirMatch[2].str();
|
|
||||||
char* endPtr;
|
|
||||||
amuse::ObjectId id;
|
|
||||||
id.id = strtoul(value.c_str(), &endPtr, 0);
|
|
||||||
if (endPtr == value.c_str())
|
|
||||||
continue;
|
|
||||||
SongId::CurNameDB->rename(id, key);
|
|
||||||
begin = dirMatch.suffix().first;
|
|
||||||
}
|
|
||||||
begin = header.cbegin();
|
|
||||||
end = header.cend();
|
|
||||||
std::unordered_set<SoundMacroId> renamedMacroIDs;
|
|
||||||
while (std::regex_search(begin, end, dirMatch, DefineSFXEntry))
|
|
||||||
{
|
|
||||||
std::string key = dirMatch[1].str();
|
|
||||||
std::string value = dirMatch[2].str();
|
|
||||||
char* endPtr;
|
|
||||||
amuse::ObjectId id;
|
|
||||||
id.id = strtoul(value.c_str(), &endPtr, 0);
|
|
||||||
if (endPtr == value.c_str())
|
|
||||||
continue;
|
|
||||||
SFXId::CurNameDB->rename(id, key);
|
|
||||||
int macroIdx = 0;
|
|
||||||
for (auto& sfxGrp : getProj().sfxGroups())
|
|
||||||
{
|
|
||||||
for (auto& sfx : sfxGrp.second->m_sfxEntries)
|
|
||||||
{
|
|
||||||
if (sfx.first == id)
|
|
||||||
{
|
|
||||||
ObjectId sfxObjId = sfx.second.objId;
|
|
||||||
if (sfxObjId == ObjectId() || sfxObjId.id & 0xc000)
|
|
||||||
continue;
|
|
||||||
_recursiveRenameMacro(sfxObjId, key, macroIdx, renamedMacroIDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
begin = dirMatch.suffix().first;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteDefineLine(std::string& ret, std::string_view typeStr, std::string_view name, ObjectId id)
|
static const std::regex DefineGRPEntry(R"(#define\s+GRP(\S+)\s+(\S+))", std::regex::ECMAScript | std::regex::optimize);
|
||||||
{
|
static const std::regex DefineSNGEntry(R"(#define\s+SNG(\S+)\s+(\S+))", std::regex::ECMAScript | std::regex::optimize);
|
||||||
ret += "#define "sv;
|
static const std::regex DefineSFXEntry(R"(#define\s+SFX(\S+)\s+(\S+))", std::regex::ECMAScript | std::regex::optimize);
|
||||||
ret += typeStr;
|
|
||||||
ret += name;
|
void AudioGroupDatabase::importCHeader(std::string_view header) {
|
||||||
ret += ' ';
|
setIdDatabases();
|
||||||
char idStr[16];
|
std::match_results<std::string_view::const_iterator> dirMatch;
|
||||||
snprintf(idStr, 16, "%d", id.id);
|
auto begin = header.cbegin();
|
||||||
ret += idStr;
|
auto end = header.cend();
|
||||||
ret += '\n';
|
while (std::regex_search(begin, end, dirMatch, DefineGRPEntry)) {
|
||||||
|
std::string key = dirMatch[1].str();
|
||||||
|
std::string value = dirMatch[2].str();
|
||||||
|
char* endPtr;
|
||||||
|
amuse::ObjectId id;
|
||||||
|
id.id = strtoul(value.c_str(), &endPtr, 0);
|
||||||
|
if (endPtr == value.c_str())
|
||||||
|
continue;
|
||||||
|
GroupId::CurNameDB->rename(id, key);
|
||||||
|
begin = dirMatch.suffix().first;
|
||||||
|
}
|
||||||
|
begin = header.cbegin();
|
||||||
|
end = header.cend();
|
||||||
|
while (std::regex_search(begin, end, dirMatch, DefineSNGEntry)) {
|
||||||
|
std::string key = dirMatch[1].str();
|
||||||
|
std::string value = dirMatch[2].str();
|
||||||
|
char* endPtr;
|
||||||
|
amuse::ObjectId id;
|
||||||
|
id.id = strtoul(value.c_str(), &endPtr, 0);
|
||||||
|
if (endPtr == value.c_str())
|
||||||
|
continue;
|
||||||
|
SongId::CurNameDB->rename(id, key);
|
||||||
|
begin = dirMatch.suffix().first;
|
||||||
|
}
|
||||||
|
begin = header.cbegin();
|
||||||
|
end = header.cend();
|
||||||
|
std::unordered_set<SoundMacroId> renamedMacroIDs;
|
||||||
|
while (std::regex_search(begin, end, dirMatch, DefineSFXEntry)) {
|
||||||
|
std::string key = dirMatch[1].str();
|
||||||
|
std::string value = dirMatch[2].str();
|
||||||
|
char* endPtr;
|
||||||
|
amuse::ObjectId id;
|
||||||
|
id.id = strtoul(value.c_str(), &endPtr, 0);
|
||||||
|
if (endPtr == value.c_str())
|
||||||
|
continue;
|
||||||
|
SFXId::CurNameDB->rename(id, key);
|
||||||
|
int macroIdx = 0;
|
||||||
|
for (auto& sfxGrp : getProj().sfxGroups()) {
|
||||||
|
for (auto& sfx : sfxGrp.second->m_sfxEntries) {
|
||||||
|
if (sfx.first == id) {
|
||||||
|
ObjectId sfxObjId = sfx.second.objId;
|
||||||
|
if (sfxObjId == ObjectId() || sfxObjId.id & 0xc000)
|
||||||
|
continue;
|
||||||
|
_recursiveRenameMacro(sfxObjId, key, macroIdx, renamedMacroIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
begin = dirMatch.suffix().first;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AudioGroupDatabase::exportCHeader(std::string_view projectName, std::string_view groupName) const
|
static void WriteDefineLine(std::string& ret, std::string_view typeStr, std::string_view name, ObjectId id) {
|
||||||
{
|
ret += "#define "sv;
|
||||||
setIdDatabases();
|
ret += typeStr;
|
||||||
std::string ret;
|
ret += name;
|
||||||
ret += "/* Auto-generated Amuse Defines\n"
|
ret += ' ';
|
||||||
" *\n"
|
char idStr[16];
|
||||||
" * Project: "sv;
|
snprintf(idStr, 16, "%d", id.id);
|
||||||
ret += projectName;
|
ret += idStr;
|
||||||
ret += "\n"
|
ret += '\n';
|
||||||
" * Subproject: "sv;
|
}
|
||||||
ret += groupName;
|
|
||||||
ret += "\n"
|
std::string AudioGroupDatabase::exportCHeader(std::string_view projectName, std::string_view groupName) const {
|
||||||
" * Date: "sv;
|
setIdDatabases();
|
||||||
time_t curTime = time(nullptr);
|
std::string ret;
|
||||||
|
ret +=
|
||||||
|
"/* Auto-generated Amuse Defines\n"
|
||||||
|
" *\n"
|
||||||
|
" * Project: "sv;
|
||||||
|
ret += projectName;
|
||||||
|
ret +=
|
||||||
|
"\n"
|
||||||
|
" * Subproject: "sv;
|
||||||
|
ret += groupName;
|
||||||
|
ret +=
|
||||||
|
"\n"
|
||||||
|
" * Date: "sv;
|
||||||
|
time_t curTime = time(nullptr);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
struct tm curTm;
|
struct tm curTm;
|
||||||
localtime_r(&curTime, &curTm);
|
localtime_r(&curTime, &curTm);
|
||||||
char curTmStr[26];
|
char curTmStr[26];
|
||||||
asctime_r(&curTm, curTmStr);
|
asctime_r(&curTm, curTmStr);
|
||||||
#else
|
#else
|
||||||
struct tm curTm;
|
struct tm curTm;
|
||||||
localtime_s(&curTm, &curTime);
|
localtime_s(&curTm, &curTime);
|
||||||
char curTmStr[26];
|
char curTmStr[26];
|
||||||
asctime_s(curTmStr, &curTm);
|
asctime_s(curTmStr, &curTm);
|
||||||
#endif
|
#endif
|
||||||
if (char* ch = strchr(curTmStr, '\n'))
|
if (char* ch = strchr(curTmStr, '\n'))
|
||||||
*ch = '\0';
|
*ch = '\0';
|
||||||
ret += curTmStr;
|
ret += curTmStr;
|
||||||
ret += "\n"
|
ret +=
|
||||||
" */\n\n\n"sv;
|
"\n"
|
||||||
|
" */\n\n\n"sv;
|
||||||
|
|
||||||
bool addLF = false;
|
bool addLF = false;
|
||||||
for (const auto& sg : SortUnorderedMap(getProj().songGroups()))
|
for (const auto& sg : SortUnorderedMap(getProj().songGroups())) {
|
||||||
{
|
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
|
||||||
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
|
WriteDefineLine(ret, "GRP"sv, name, sg.first);
|
||||||
WriteDefineLine(ret, "GRP"sv, name, sg.first);
|
addLF = true;
|
||||||
addLF = true;
|
}
|
||||||
}
|
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups())) {
|
||||||
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups()))
|
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
|
||||||
{
|
WriteDefineLine(ret, "GRP"sv, name, sg.first);
|
||||||
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
|
addLF = true;
|
||||||
WriteDefineLine(ret, "GRP"sv, name, sg.first);
|
}
|
||||||
addLF = true;
|
|
||||||
|
if (addLF)
|
||||||
|
ret += "\n\n"sv;
|
||||||
|
addLF = false;
|
||||||
|
|
||||||
|
std::unordered_set<amuse::SongId> songIds;
|
||||||
|
for (const auto& sg : getProj().songGroups())
|
||||||
|
for (const auto& song : sg.second->m_midiSetups)
|
||||||
|
songIds.insert(song.first);
|
||||||
|
for (amuse::SongId id : SortUnorderedSet(songIds)) {
|
||||||
|
auto name = amuse::SongId::CurNameDB->resolveNameFromId(id);
|
||||||
|
WriteDefineLine(ret, "SNG"sv, name, id);
|
||||||
|
addLF = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addLF)
|
||||||
|
ret += "\n\n"sv;
|
||||||
|
addLF = false;
|
||||||
|
|
||||||
|
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups())) {
|
||||||
|
for (const auto& sfx : SortUnorderedMap(sg.second.get()->m_sfxEntries)) {
|
||||||
|
auto name = amuse::SFXId::CurNameDB->resolveNameFromId(sfx.first);
|
||||||
|
WriteDefineLine(ret, "SFX"sv, name, sfx.first.id);
|
||||||
|
addLF = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (addLF)
|
if (addLF)
|
||||||
ret += "\n\n"sv;
|
ret += "\n\n"sv;
|
||||||
addLF = false;
|
|
||||||
|
|
||||||
std::unordered_set<amuse::SongId> songIds;
|
return ret;
|
||||||
for (const auto& sg : getProj().songGroups())
|
|
||||||
for (const auto& song : sg.second->m_midiSetups)
|
|
||||||
songIds.insert(song.first);
|
|
||||||
for (amuse::SongId id : SortUnorderedSet(songIds))
|
|
||||||
{
|
|
||||||
auto name = amuse::SongId::CurNameDB->resolveNameFromId(id);
|
|
||||||
WriteDefineLine(ret, "SNG"sv, name, id);
|
|
||||||
addLF = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addLF)
|
|
||||||
ret += "\n\n"sv;
|
|
||||||
addLF = false;
|
|
||||||
|
|
||||||
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups()))
|
|
||||||
{
|
|
||||||
for (const auto& sfx : SortUnorderedMap(sg.second.get()->m_sfxEntries))
|
|
||||||
{
|
|
||||||
auto name = amuse::SFXId::CurNameDB->resolveNameFromId(sfx.first);
|
|
||||||
WriteDefineLine(ret, "SFX"sv, name, sfx.first.id);
|
|
||||||
addLF = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addLF)
|
|
||||||
ret += "\n\n"sv;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // namespace amuse
|
||||||
|
|
|
@ -1,47 +1,41 @@
|
||||||
#include "amuse/AudioGroupData.hpp"
|
#include "amuse/AudioGroupData.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
IntrusiveAudioGroupData::~IntrusiveAudioGroupData()
|
IntrusiveAudioGroupData::~IntrusiveAudioGroupData() {
|
||||||
{
|
if (m_owns) {
|
||||||
if (m_owns)
|
delete[] m_pool;
|
||||||
{
|
delete[] m_proj;
|
||||||
delete[] m_pool;
|
delete[] m_sdir;
|
||||||
delete[] m_proj;
|
delete[] m_samp;
|
||||||
delete[] m_sdir;
|
}
|
||||||
delete[] m_samp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept
|
IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept
|
||||||
: AudioGroupData(other.m_proj, other.m_projSz, other.m_pool, other.m_poolSz, other.m_sdir, other.m_sdirSz, other.m_samp,
|
: AudioGroupData(other.m_proj, other.m_projSz, other.m_pool, other.m_poolSz, other.m_sdir, other.m_sdirSz, other.m_samp,
|
||||||
other.m_sampSz, other.m_fmt, other.m_absOffs)
|
other.m_sampSz, other.m_fmt, other.m_absOffs) {
|
||||||
{
|
m_owns = other.m_owns;
|
||||||
m_owns = other.m_owns;
|
other.m_owns = false;
|
||||||
other.m_owns = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupData&& other) noexcept
|
IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupData&& other) noexcept {
|
||||||
{
|
if (m_owns) {
|
||||||
if (m_owns)
|
delete[] m_pool;
|
||||||
{
|
delete[] m_proj;
|
||||||
delete[] m_pool;
|
delete[] m_sdir;
|
||||||
delete[] m_proj;
|
delete[] m_samp;
|
||||||
delete[] m_sdir;
|
}
|
||||||
delete[] m_samp;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_owns = other.m_owns;
|
m_owns = other.m_owns;
|
||||||
other.m_owns = false;
|
other.m_owns = false;
|
||||||
|
|
||||||
m_proj = other.m_proj;
|
m_proj = other.m_proj;
|
||||||
m_pool = other.m_pool;
|
m_pool = other.m_pool;
|
||||||
m_sdir = other.m_sdir;
|
m_sdir = other.m_sdir;
|
||||||
m_samp = other.m_samp;
|
m_samp = other.m_samp;
|
||||||
m_fmt = other.m_fmt;
|
m_fmt = other.m_fmt;
|
||||||
m_absOffs = other.m_absOffs;
|
m_absOffs = other.m_absOffs;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // namespace amuse
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -3,47 +3,41 @@
|
||||||
#include "amuse/Submix.hpp"
|
#include "amuse/Submix.hpp"
|
||||||
#include "amuse/Engine.hpp"
|
#include "amuse/Engine.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
void BooBackendVoice::VoiceCallback::preSupplyAudio(boo::IAudioVoice&, double dt)
|
void BooBackendVoice::VoiceCallback::preSupplyAudio(boo::IAudioVoice&, double dt) {
|
||||||
{
|
m_parent.m_clientVox.preSupplyAudio(dt);
|
||||||
m_parent.m_clientVox.preSupplyAudio(dt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&, size_t frames, int16_t* data)
|
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&, size_t frames, int16_t* data) {
|
||||||
{
|
return m_parent.m_clientVox.supplyAudio(frames, data);
|
||||||
return m_parent.m_clientVox.supplyAudio(frames, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out)
|
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in,
|
||||||
{
|
int16_t* out) {
|
||||||
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out)
|
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in,
|
||||||
{
|
int32_t* out) {
|
||||||
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out)
|
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, float* in,
|
||||||
{
|
float* out) {
|
||||||
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
BooBackendVoice::BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch)
|
BooBackendVoice::BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch)
|
||||||
: m_clientVox(clientVox), m_cb(*this), m_booVoice(engine.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch))
|
: m_clientVox(clientVox), m_cb(*this), m_booVoice(engine.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch)) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BooBackendVoice::resetSampleRate(double sampleRate) { m_booVoice->resetSampleRate(sampleRate); }
|
void BooBackendVoice::resetSampleRate(double sampleRate) { m_booVoice->resetSampleRate(sampleRate); }
|
||||||
|
|
||||||
void BooBackendVoice::resetChannelLevels() { m_booVoice->resetChannelLevels(); }
|
void BooBackendVoice::resetChannelLevels() { m_booVoice->resetChannelLevels(); }
|
||||||
|
|
||||||
void BooBackendVoice::setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew)
|
void BooBackendVoice::setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew) {
|
||||||
{
|
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
|
||||||
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
|
m_booVoice->setMonoChannelLevels(smx.m_booSubmix.get(), coefs, slew);
|
||||||
m_booVoice->setMonoChannelLevels(smx.m_booSubmix.get(), coefs, slew);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendVoice::setPitchRatio(double ratio, bool slew) { m_booVoice->setPitchRatio(ratio, slew); }
|
void BooBackendVoice::setPitchRatio(double ratio, bool slew) { m_booVoice->setPitchRatio(ratio, slew); }
|
||||||
|
@ -55,37 +49,30 @@ void BooBackendVoice::stop() { m_booVoice->stop(); }
|
||||||
bool BooBackendSubmix::SubmixCallback::canApplyEffect() const { return m_parent.m_clientSmx.canApplyEffect(); }
|
bool BooBackendSubmix::SubmixCallback::canApplyEffect() const { return m_parent.m_clientSmx.canApplyEffect(); }
|
||||||
|
|
||||||
void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, size_t frameCount, const boo::ChannelMap& chanMap,
|
void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, size_t frameCount, const boo::ChannelMap& chanMap,
|
||||||
double) const
|
double) const {
|
||||||
{
|
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
|
||||||
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendSubmix::SubmixCallback::applyEffect(int32_t* audio, size_t frameCount, const boo::ChannelMap& chanMap,
|
void BooBackendSubmix::SubmixCallback::applyEffect(int32_t* audio, size_t frameCount, const boo::ChannelMap& chanMap,
|
||||||
double) const
|
double) const {
|
||||||
{
|
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
|
||||||
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendSubmix::SubmixCallback::applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap,
|
void BooBackendSubmix::SubmixCallback::applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap,
|
||||||
double) const
|
double) const {
|
||||||
{
|
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
|
||||||
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendSubmix::SubmixCallback::resetOutputSampleRate(double sampleRate)
|
void BooBackendSubmix::SubmixCallback::resetOutputSampleRate(double sampleRate) {
|
||||||
{
|
m_parent.m_clientSmx.resetOutputSampleRate(sampleRate);
|
||||||
m_parent.m_clientSmx.resetOutputSampleRate(sampleRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId)
|
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId)
|
||||||
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(mainOut, &m_cb, busId))
|
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(mainOut, &m_cb, busId)) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BooBackendSubmix::setSendLevel(IBackendSubmix* submix, float level, bool slew)
|
void BooBackendSubmix::setSendLevel(IBackendSubmix* submix, float level, bool slew) {
|
||||||
{
|
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
|
||||||
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
|
m_booSubmix->setSendLevel(smx.m_booSubmix.get(), level, slew);
|
||||||
m_booSubmix->setSendLevel(smx.m_booSubmix.get(), level, slew);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double BooBackendSubmix::getSampleRate() const { return m_booSubmix->getSampleRate(); }
|
double BooBackendSubmix::getSampleRate() const { return m_booSubmix->getSampleRate(); }
|
||||||
|
@ -95,67 +82,51 @@ SubmixFormat BooBackendSubmix::getSampleFormat() const { return SubmixFormat(m_b
|
||||||
BooBackendMIDIReader::~BooBackendMIDIReader() {}
|
BooBackendMIDIReader::~BooBackendMIDIReader() {}
|
||||||
|
|
||||||
BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, bool useLock)
|
BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, bool useLock)
|
||||||
: m_engine(engine), m_decoder(*this), m_useLock(useLock)
|
: m_engine(engine), m_decoder(*this), m_useLock(useLock) {
|
||||||
{
|
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(engine.getBackend());
|
||||||
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(engine.getBackend());
|
auto devices = voxAlloc.m_booEngine.enumerateMIDIInputs();
|
||||||
auto devices = voxAlloc.m_booEngine.enumerateMIDIInputs();
|
for (const auto& dev : devices) {
|
||||||
for (const auto& dev : devices)
|
auto midiIn =
|
||||||
{
|
voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(), std::bind(&BooBackendMIDIReader::_MIDIReceive, this,
|
||||||
auto midiIn = voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(),
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
if (midiIn)
|
|
||||||
m_midiIns[dev.first] = std::move(midiIn);
|
|
||||||
}
|
|
||||||
if (voxAlloc.m_booEngine.supportsVirtualMIDIIn())
|
|
||||||
m_virtualIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
|
|
||||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BooBackendMIDIReader::addMIDIIn(const char* name)
|
|
||||||
{
|
|
||||||
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(m_engine.getBackend());
|
|
||||||
auto midiIn = voxAlloc.m_booEngine.newRealMIDIIn(name,
|
|
||||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
if (midiIn)
|
if (midiIn)
|
||||||
m_midiIns[name] = std::move(midiIn);
|
m_midiIns[dev.first] = std::move(midiIn);
|
||||||
|
}
|
||||||
|
if (voxAlloc.m_booEngine.supportsVirtualMIDIIn())
|
||||||
|
m_virtualIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
|
||||||
|
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::removeMIDIIn(const char* name)
|
void BooBackendMIDIReader::addMIDIIn(const char* name) {
|
||||||
{
|
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(m_engine.getBackend());
|
||||||
m_midiIns.erase(name);
|
auto midiIn = voxAlloc.m_booEngine.newRealMIDIIn(
|
||||||
|
name, std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
if (midiIn)
|
||||||
|
m_midiIns[name] = std::move(midiIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BooBackendMIDIReader::hasMIDIIn(const char* name) const
|
void BooBackendMIDIReader::removeMIDIIn(const char* name) { m_midiIns.erase(name); }
|
||||||
{
|
|
||||||
return m_midiIns.find(name) != m_midiIns.cend();
|
bool BooBackendMIDIReader::hasMIDIIn(const char* name) const { return m_midiIns.find(name) != m_midiIns.cend(); }
|
||||||
|
|
||||||
|
void BooBackendMIDIReader::setVirtualIn(bool v) {
|
||||||
|
if (v) {
|
||||||
|
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(m_engine.getBackend());
|
||||||
|
if (voxAlloc.m_booEngine.supportsVirtualMIDIIn())
|
||||||
|
m_virtualIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
|
||||||
|
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
} else {
|
||||||
|
m_virtualIn.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::setVirtualIn(bool v)
|
bool BooBackendMIDIReader::hasVirtualIn() const { return m_virtualIn.operator bool(); }
|
||||||
{
|
|
||||||
if (v)
|
|
||||||
{
|
|
||||||
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(m_engine.getBackend());
|
|
||||||
if (voxAlloc.m_booEngine.supportsVirtualMIDIIn())
|
|
||||||
m_virtualIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
|
|
||||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_virtualIn.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BooBackendMIDIReader::hasVirtualIn() const
|
void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes, double time) {
|
||||||
{
|
std::unique_lock<std::mutex> lk(m_midiMutex, std::defer_lock_t{});
|
||||||
return m_virtualIn.operator bool();
|
if (m_useLock)
|
||||||
}
|
lk.lock();
|
||||||
|
m_queue.emplace_back(time, std::move(bytes));
|
||||||
void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes, double time)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(m_midiMutex, std::defer_lock_t{});
|
|
||||||
if (m_useLock)
|
|
||||||
lk.lock();
|
|
||||||
m_queue.emplace_back(time, std::move(bytes));
|
|
||||||
#if 0
|
#if 0
|
||||||
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
|
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
|
||||||
syslog(LOG_EMERG, "MIDI receive %f\n", time);
|
syslog(LOG_EMERG, "MIDI receive %f\n", time);
|
||||||
|
@ -163,32 +134,29 @@ void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes, double tim
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::pumpReader(double dt)
|
void BooBackendMIDIReader::pumpReader(double dt) {
|
||||||
{
|
dt += 0.001; /* Add 1ms to ensure consumer keeps up with producer */
|
||||||
dt += 0.001; /* Add 1ms to ensure consumer keeps up with producer */
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lk(m_midiMutex, std::defer_lock_t{});
|
std::unique_lock<std::mutex> lk(m_midiMutex, std::defer_lock_t{});
|
||||||
if (m_useLock)
|
if (m_useLock)
|
||||||
lk.lock();
|
lk.lock();
|
||||||
if (m_queue.empty())
|
if (m_queue.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Determine range of buffer updates within this period */
|
/* Determine range of buffer updates within this period */
|
||||||
auto periodEnd = m_queue.cbegin();
|
auto periodEnd = m_queue.cbegin();
|
||||||
double startPt = m_queue.front().first;
|
double startPt = m_queue.front().first;
|
||||||
for (; periodEnd != m_queue.cend(); ++periodEnd)
|
for (; periodEnd != m_queue.cend(); ++periodEnd) {
|
||||||
{
|
double delta = periodEnd->first - startPt;
|
||||||
double delta = periodEnd->first - startPt;
|
if (delta > dt)
|
||||||
if (delta > dt)
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (m_queue.cbegin() == periodEnd)
|
if (m_queue.cbegin() == periodEnd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Dispatch buffers */
|
/* Dispatch buffers */
|
||||||
for (auto it = m_queue.begin(); it != periodEnd;)
|
for (auto it = m_queue.begin(); it != periodEnd;) {
|
||||||
{
|
|
||||||
#if 0
|
#if 0
|
||||||
char str[64];
|
char str[64];
|
||||||
sprintf(str, "MIDI %zu %f ", it->second.size(), it->first);
|
sprintf(str, "MIDI %zu %f ", it->second.size(), it->first);
|
||||||
|
@ -198,15 +166,14 @@ void BooBackendMIDIReader::pumpReader(double dt)
|
||||||
syslog(LOG_EMERG, "%s\n", str);
|
syslog(LOG_EMERG, "%s\n", str);
|
||||||
closelog();
|
closelog();
|
||||||
#endif
|
#endif
|
||||||
m_decoder.receiveBytes(it->second.cbegin(), it->second.cend());
|
m_decoder.receiveBytes(it->second.cbegin(), it->second.cend());
|
||||||
it = m_queue.erase(it);
|
it = m_queue.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
|
void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) {
|
||||||
{
|
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||||
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
seq->keyOff(chan, key, velocity);
|
||||||
seq->keyOff(chan, key, velocity);
|
|
||||||
#if 0
|
#if 0
|
||||||
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
|
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
|
||||||
syslog(LOG_EMERG, "NoteOff %d", key);
|
syslog(LOG_EMERG, "NoteOff %d", key);
|
||||||
|
@ -214,10 +181,9 @@ void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
|
void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) {
|
||||||
{
|
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||||
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
seq->keyOn(chan, key, velocity);
|
||||||
seq->keyOn(chan, key, velocity);
|
|
||||||
#if 0
|
#if 0
|
||||||
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
|
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
|
||||||
syslog(LOG_EMERG, "NoteOn %d", key);
|
syslog(LOG_EMERG, "NoteOn %d", key);
|
||||||
|
@ -227,40 +193,35 @@ void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
|
||||||
|
|
||||||
void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
|
void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
|
||||||
|
|
||||||
void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
|
void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) {
|
||||||
{
|
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||||
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
seq->setCtrlValue(chan, control, value);
|
||||||
seq->setCtrlValue(chan, control, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program)
|
void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program) {
|
||||||
{
|
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||||
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
seq->setChanProgram(chan, program);
|
||||||
seq->setChanProgram(chan, program);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
|
void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
|
||||||
|
|
||||||
void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch)
|
void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch) {
|
||||||
{
|
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||||
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000));
|
||||||
seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::allSoundOff(uint8_t chan)
|
void BooBackendMIDIReader::allSoundOff(uint8_t chan) {
|
||||||
{
|
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||||
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
seq->allOff(chan, true);
|
||||||
seq->allOff(chan, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::resetAllControllers(uint8_t /*chan*/) {}
|
void BooBackendMIDIReader::resetAllControllers(uint8_t /*chan*/) {}
|
||||||
|
|
||||||
void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
|
void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
|
||||||
|
|
||||||
void BooBackendMIDIReader::allNotesOff(uint8_t chan)
|
void BooBackendMIDIReader::allNotesOff(uint8_t chan) {
|
||||||
{
|
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||||
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
|
seq->allOff(chan, false);
|
||||||
seq->allOff(chan, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendMIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
|
void BooBackendMIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||||
|
@ -285,51 +246,40 @@ void BooBackendMIDIReader::stopSeq() {}
|
||||||
|
|
||||||
void BooBackendMIDIReader::reset() {}
|
void BooBackendMIDIReader::reset() {}
|
||||||
|
|
||||||
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : m_booEngine(booEngine) {
|
||||||
: m_booEngine(booEngine)
|
booEngine.setCallbackInterface(this);
|
||||||
{
|
|
||||||
booEngine.setCallbackInterface(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IBackendVoice> BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate,
|
std::unique_ptr<IBackendVoice> BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate,
|
||||||
bool dynamicPitch)
|
bool dynamicPitch) {
|
||||||
{
|
return std::make_unique<BooBackendVoice>(m_booEngine, clientVox, sampleRate, dynamicPitch);
|
||||||
return std::make_unique<BooBackendVoice>(m_booEngine, clientVox, sampleRate, dynamicPitch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx, bool mainOut, int busId)
|
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx, bool mainOut, int busId) {
|
||||||
{
|
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx, mainOut, busId);
|
||||||
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx, mainOut, busId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices()
|
std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices() {
|
||||||
{
|
return m_booEngine.enumerateMIDIInputs();
|
||||||
return m_booEngine.enumerateMIDIInputs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine)
|
std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine) {
|
||||||
{
|
return std::make_unique<BooBackendMIDIReader>(engine, m_booEngine.useMIDILock());
|
||||||
return std::make_unique<BooBackendMIDIReader>(engine, m_booEngine.useMIDILock());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendVoiceAllocator::setCallbackInterface(Engine* engine)
|
void BooBackendVoiceAllocator::setCallbackInterface(Engine* engine) { m_cbInterface = engine; }
|
||||||
{
|
|
||||||
m_cbInterface = engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioChannelSet BooBackendVoiceAllocator::getAvailableSet() { return AudioChannelSet(m_booEngine.getAvailableSet()); }
|
AudioChannelSet BooBackendVoiceAllocator::getAvailableSet() { return AudioChannelSet(m_booEngine.getAvailableSet()); }
|
||||||
|
|
||||||
void BooBackendVoiceAllocator::setVolume(float vol) { m_booEngine.setVolume(vol); }
|
void BooBackendVoiceAllocator::setVolume(float vol) { m_booEngine.setVolume(vol); }
|
||||||
|
|
||||||
void BooBackendVoiceAllocator::on5MsInterval(boo::IAudioVoiceEngine& engine, double dt)
|
void BooBackendVoiceAllocator::on5MsInterval(boo::IAudioVoiceEngine& engine, double dt) {
|
||||||
{
|
if (m_cbInterface)
|
||||||
if (m_cbInterface)
|
m_cbInterface->_on5MsInterval(*this, dt);
|
||||||
m_cbInterface->_on5MsInterval(*this, dt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooBackendVoiceAllocator::onPumpCycleComplete(boo::IAudioVoiceEngine& engine)
|
void BooBackendVoiceAllocator::onPumpCycleComplete(boo::IAudioVoiceEngine& engine) {
|
||||||
{
|
if (m_cbInterface)
|
||||||
if (m_cbInterface)
|
m_cbInterface->_onPumpCycleComplete(*this);
|
||||||
m_cbInterface->_onPumpCycleComplete(*this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // namespace amuse
|
||||||
|
|
719
lib/Common.cpp
719
lib/Common.cpp
|
@ -3,128 +3,121 @@
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
static logvisor::Module Log("amuse");
|
static logvisor::Module Log("amuse");
|
||||||
|
|
||||||
bool Copy(const SystemChar* from, const SystemChar* to)
|
bool Copy(const SystemChar* from, const SystemChar* to) {
|
||||||
{
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
return CopyFileW(from, to, FALSE) != 0;
|
return CopyFileW(from, to, FALSE) != 0;
|
||||||
#else
|
#else
|
||||||
FILE* fi = fopen(from, "rb");
|
FILE* fi = fopen(from, "rb");
|
||||||
if (!fi)
|
if (!fi)
|
||||||
return false;
|
return false;
|
||||||
FILE* fo = fopen(to, "wb");
|
FILE* fo = fopen(to, "wb");
|
||||||
if (!fo)
|
if (!fo) {
|
||||||
{
|
|
||||||
fclose(fi);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[65536]);
|
|
||||||
size_t readSz = 0;
|
|
||||||
while ((readSz = fread(buf.get(), 1, 65536, fi)))
|
|
||||||
fwrite(buf.get(), 1, readSz, fo);
|
|
||||||
fclose(fi);
|
fclose(fi);
|
||||||
fclose(fo);
|
return false;
|
||||||
struct stat theStat;
|
}
|
||||||
if (::stat(from, &theStat))
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[65536]);
|
||||||
return true;
|
size_t readSz = 0;
|
||||||
#if __APPLE__
|
while ((readSz = fread(buf.get(), 1, 65536, fi)))
|
||||||
struct timespec times[] = { theStat.st_atimespec, theStat.st_mtimespec };
|
fwrite(buf.get(), 1, readSz, fo);
|
||||||
#elif __SWITCH__
|
fclose(fi);
|
||||||
struct timespec times[] = { theStat.st_atime, theStat.st_mtime };
|
fclose(fo);
|
||||||
#else
|
struct stat theStat;
|
||||||
struct timespec times[] = { theStat.st_atim, theStat.st_mtim };
|
if (::stat(from, &theStat))
|
||||||
#endif
|
|
||||||
utimensat(AT_FDCWD, to, times, 0);
|
|
||||||
return true;
|
return true;
|
||||||
|
#if __APPLE__
|
||||||
|
struct timespec times[] = {theStat.st_atimespec, theStat.st_mtimespec};
|
||||||
|
#elif __SWITCH__
|
||||||
|
struct timespec times[] = {theStat.st_atime, theStat.st_mtime};
|
||||||
|
#else
|
||||||
|
struct timespec times[] = {theStat.st_atim, theStat.st_mtim};
|
||||||
|
#endif
|
||||||
|
utimensat(AT_FDCWD, to, times, 0);
|
||||||
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEFINE_ID_TYPE(type, typeName) \
|
#define DEFINE_ID_TYPE(type, typeName) \
|
||||||
thread_local NameDB* type::CurNameDB = nullptr; \
|
thread_local NameDB* type::CurNameDB = nullptr; \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader & reader) { \
|
||||||
id = reader.readUint16Little(); \
|
id = reader.readUint16Little(); \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter & writer) { \
|
||||||
writer.writeUint16Little(id); \
|
writer.writeUint16Little(id); \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t & sz) { \
|
||||||
sz += 2; \
|
sz += 2; \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader & reader) { \
|
||||||
_read(reader); \
|
_read(reader); \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter & writer) { \
|
||||||
_write(writer); \
|
_write(writer); \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader & reader) { \
|
||||||
id = reader.readUint16Big(); \
|
id = reader.readUint16Big(); \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter & writer) { \
|
||||||
writer.writeUint16Big(id); \
|
writer.writeUint16Big(id); \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t & sz) { \
|
||||||
sz += 2; \
|
sz += 2; \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader & reader) { \
|
||||||
_read(reader); \
|
_read(reader); \
|
||||||
} \
|
} \
|
||||||
template<> template<> \
|
template <> \
|
||||||
void type##DNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) \
|
template <> \
|
||||||
{ \
|
void type##DNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter & writer) { \
|
||||||
_write(writer); \
|
_write(writer); \
|
||||||
} \
|
} \
|
||||||
template <athena::Endian DNAE> \
|
template <athena::Endian DNAE> \
|
||||||
void type##DNA<DNAE>::_read(athena::io::YAMLDocReader& r) \
|
void type##DNA<DNAE>::_read(athena::io::YAMLDocReader& r) { \
|
||||||
{ \
|
std::string name = r.readString(nullptr); \
|
||||||
std::string name = r.readString(nullptr); \
|
if (!type::CurNameDB) \
|
||||||
if (!type::CurNameDB) \
|
Log.report(logvisor::Fatal, "Unable to resolve " typeName " name %s, no database present", name.c_str()); \
|
||||||
Log.report(logvisor::Fatal, "Unable to resolve " typeName " name %s, no database present", name.c_str()); \
|
if (name.empty()) { \
|
||||||
if (name.empty()) \
|
id.id = 0xffff; \
|
||||||
{ \
|
return; \
|
||||||
id.id = 0xffff; \
|
} \
|
||||||
return; \
|
id = type::CurNameDB->resolveIdFromName(name); \
|
||||||
} \
|
} \
|
||||||
id = type::CurNameDB->resolveIdFromName(name); \
|
template <athena::Endian DNAE> \
|
||||||
} \
|
void type##DNA<DNAE>::_write(athena::io::YAMLDocWriter& w) { \
|
||||||
template <athena::Endian DNAE> \
|
if (!type::CurNameDB) \
|
||||||
void type##DNA<DNAE>::_write(athena::io::YAMLDocWriter& w) \
|
Log.report(logvisor::Fatal, "Unable to resolve " typeName " ID %d, no database present", id.id); \
|
||||||
{ \
|
if (id.id == 0xffff) \
|
||||||
if (!type::CurNameDB) \
|
return; \
|
||||||
Log.report(logvisor::Fatal, "Unable to resolve " typeName " ID %d, no database present", id.id); \
|
std::string_view name = type::CurNameDB->resolveNameFromId(id); \
|
||||||
if (id.id == 0xffff) \
|
if (!name.empty()) \
|
||||||
return; \
|
w.writeString(nullptr, name); \
|
||||||
std::string_view name = type::CurNameDB->resolveNameFromId(id); \
|
} \
|
||||||
if (!name.empty()) \
|
template <athena::Endian DNAE> \
|
||||||
w.writeString(nullptr, name); \
|
const char* type##DNA<DNAE>::DNAType() { \
|
||||||
} \
|
return "amuse::" #type "DNA"; \
|
||||||
template <athena::Endian DNAE> \
|
} \
|
||||||
const char* type##DNA<DNAE>::DNAType() \
|
template struct type##DNA<athena::Big>; \
|
||||||
{ \
|
template struct type##DNA<athena::Little>;
|
||||||
return "amuse::" #type "DNA"; \
|
|
||||||
} \
|
|
||||||
template struct type##DNA<athena::Big>; \
|
|
||||||
template struct type##DNA<athena::Little>;
|
|
||||||
|
|
||||||
DEFINE_ID_TYPE(ObjectId, "object")
|
DEFINE_ID_TYPE(ObjectId, "object")
|
||||||
DEFINE_ID_TYPE(SoundMacroId, "SoundMacro")
|
DEFINE_ID_TYPE(SoundMacroId, "SoundMacro")
|
||||||
|
@ -136,336 +129,300 @@ DEFINE_ID_TYPE(SongId, "song")
|
||||||
DEFINE_ID_TYPE(SFXId, "sfx")
|
DEFINE_ID_TYPE(SFXId, "sfx")
|
||||||
DEFINE_ID_TYPE(GroupId, "group")
|
DEFINE_ID_TYPE(GroupId, "group")
|
||||||
|
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
|
||||||
id = reader.readUint16Little();
|
id = reader.readUint16Little();
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
|
||||||
writer.writeUint16Little(id);
|
writer.writeUint16Little(id);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
|
||||||
sz += 2;
|
sz += 2;
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
|
||||||
_read(reader);
|
_read(reader);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
|
||||||
_write(writer);
|
_write(writer);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
|
||||||
id = reader.readUint16Big();
|
id = reader.readUint16Big();
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
|
||||||
writer.writeUint16Big(id);
|
writer.writeUint16Big(id);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
|
||||||
sz += 2;
|
sz += 2;
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
|
||||||
_read(reader);
|
_read(reader);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
|
template <>
|
||||||
{
|
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
|
||||||
_write(writer);
|
_write(writer);
|
||||||
}
|
}
|
||||||
template <athena::Endian DNAE>
|
template <athena::Endian DNAE>
|
||||||
void PageObjectIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r)
|
void PageObjectIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r) {
|
||||||
{
|
std::string name = r.readString(nullptr);
|
||||||
std::string name = r.readString(nullptr);
|
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
|
||||||
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
|
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers name %s, no database present", name.c_str());
|
||||||
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers name %s, no database present", name.c_str());
|
if (name.empty()) {
|
||||||
if (name.empty())
|
id.id = 0xffff;
|
||||||
{
|
return;
|
||||||
|
}
|
||||||
|
auto search = KeymapId::CurNameDB->m_stringToId.find(name);
|
||||||
|
if (search == KeymapId::CurNameDB->m_stringToId.cend()) {
|
||||||
|
search = LayersId::CurNameDB->m_stringToId.find(name);
|
||||||
|
if (search == LayersId::CurNameDB->m_stringToId.cend()) {
|
||||||
|
search = SoundMacroId::CurNameDB->m_stringToId.find(name);
|
||||||
|
if (search == SoundMacroId::CurNameDB->m_stringToId.cend()) {
|
||||||
|
Log.report(logvisor::Error, "Unable to resolve name %s", name.c_str());
|
||||||
id.id = 0xffff;
|
id.id = 0xffff;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
auto search = KeymapId::CurNameDB->m_stringToId.find(name);
|
}
|
||||||
if (search == KeymapId::CurNameDB->m_stringToId.cend())
|
id = search->second;
|
||||||
{
|
|
||||||
search = LayersId::CurNameDB->m_stringToId.find(name);
|
|
||||||
if (search == LayersId::CurNameDB->m_stringToId.cend())
|
|
||||||
{
|
|
||||||
search = SoundMacroId::CurNameDB->m_stringToId.find(name);
|
|
||||||
if (search == SoundMacroId::CurNameDB->m_stringToId.cend())
|
|
||||||
{
|
|
||||||
Log.report(logvisor::Error, "Unable to resolve name %s", name.c_str());
|
|
||||||
id.id = 0xffff;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id = search->second;
|
|
||||||
}
|
}
|
||||||
template <athena::Endian DNAE>
|
template <athena::Endian DNAE>
|
||||||
void PageObjectIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w)
|
void PageObjectIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w) {
|
||||||
{
|
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
|
||||||
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
|
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers ID %d, no database present", id.id);
|
||||||
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers ID %d, no database present", id.id);
|
if (id.id == 0xffff)
|
||||||
if (id.id == 0xffff)
|
return;
|
||||||
return;
|
if (id.id & 0x8000) {
|
||||||
if (id.id & 0x8000)
|
std::string_view name = LayersId::CurNameDB->resolveNameFromId(id);
|
||||||
{
|
if (!name.empty())
|
||||||
std::string_view name = LayersId::CurNameDB->resolveNameFromId(id);
|
w.writeString(nullptr, name);
|
||||||
if (!name.empty())
|
} else if (id.id & 0x4000) {
|
||||||
w.writeString(nullptr, name);
|
std::string_view name = KeymapId::CurNameDB->resolveNameFromId(id);
|
||||||
}
|
if (!name.empty())
|
||||||
else if (id.id & 0x4000)
|
w.writeString(nullptr, name);
|
||||||
{
|
} else {
|
||||||
std::string_view name = KeymapId::CurNameDB->resolveNameFromId(id);
|
std::string_view name = SoundMacroId::CurNameDB->resolveNameFromId(id);
|
||||||
if (!name.empty())
|
if (!name.empty())
|
||||||
w.writeString(nullptr, name);
|
w.writeString(nullptr, name);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string_view name = SoundMacroId::CurNameDB->resolveNameFromId(id);
|
|
||||||
if (!name.empty())
|
|
||||||
w.writeString(nullptr, name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
template <athena::Endian DNAE>
|
template <athena::Endian DNAE>
|
||||||
const char* PageObjectIdDNA<DNAE>::DNAType()
|
const char* PageObjectIdDNA<DNAE>::DNAType() {
|
||||||
{
|
return "amuse::PageObjectIdDNA";
|
||||||
return "amuse::PageObjectIdDNA";
|
|
||||||
}
|
}
|
||||||
template struct PageObjectIdDNA<athena::Big>;
|
template struct PageObjectIdDNA<athena::Big>;
|
||||||
template struct PageObjectIdDNA<athena::Little>;
|
template struct PageObjectIdDNA<athena::Little>;
|
||||||
|
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
|
||||||
step = reader.readUint16Little();
|
step = reader.readUint16Little();
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
|
||||||
writer.writeUint16Little(step);
|
writer.writeUint16Little(step);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
|
||||||
sz += 2;
|
sz += 2;
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
|
||||||
step = reader.readUint16(nullptr);
|
step = reader.readUint16(nullptr);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
|
||||||
writer.writeUint16(nullptr, step);
|
writer.writeUint16(nullptr, step);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
|
||||||
step = reader.readUint16Big();
|
step = reader.readUint16Big();
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
|
||||||
writer.writeUint16Big(step);
|
writer.writeUint16Big(step);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
|
||||||
sz += 2;
|
sz += 2;
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
|
||||||
step = reader.readUint16(nullptr);
|
step = reader.readUint16(nullptr);
|
||||||
}
|
}
|
||||||
template<> template<>
|
template <>
|
||||||
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
|
template <>
|
||||||
{
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
|
||||||
writer.writeUint16(nullptr, step);
|
writer.writeUint16(nullptr, step);
|
||||||
}
|
}
|
||||||
template <athena::Endian DNAE>
|
template <athena::Endian DNAE>
|
||||||
const char* SoundMacroStepDNA<DNAE>::DNAType()
|
const char* SoundMacroStepDNA<DNAE>::DNAType() {
|
||||||
{
|
return "amuse::SoundMacroStepDNA";
|
||||||
return "amuse::SoundMacroStepDNA";
|
|
||||||
}
|
}
|
||||||
template struct SoundMacroStepDNA<athena::Big>;
|
template struct SoundMacroStepDNA<athena::Big>;
|
||||||
template struct SoundMacroStepDNA<athena::Little>;
|
template struct SoundMacroStepDNA<athena::Little>;
|
||||||
|
|
||||||
ObjectId NameDB::generateId(Type tp) const
|
ObjectId NameDB::generateId(Type tp) const {
|
||||||
{
|
uint16_t maxMatch = 0;
|
||||||
uint16_t maxMatch = 0;
|
if (tp == Type::Layer)
|
||||||
if (tp == Type::Layer)
|
maxMatch = 0x8000;
|
||||||
maxMatch = 0x8000;
|
else if (tp == Type::Keymap)
|
||||||
else if (tp == Type::Keymap)
|
maxMatch = 0x4000;
|
||||||
maxMatch = 0x4000;
|
for (const auto& p : m_idToString)
|
||||||
for (const auto& p : m_idToString)
|
if (p.first >= maxMatch)
|
||||||
if (p.first >= maxMatch)
|
maxMatch = p.first + 1;
|
||||||
maxMatch = p.first + 1;
|
return maxMatch;
|
||||||
return maxMatch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NameDB::generateName(ObjectId id, Type tp)
|
std::string NameDB::generateName(ObjectId id, Type tp) {
|
||||||
{
|
char name[32];
|
||||||
char name[32];
|
switch (tp) {
|
||||||
switch (tp)
|
case Type::SoundMacro:
|
||||||
{
|
snprintf(name, 32, "macro%04X", id.id);
|
||||||
case Type::SoundMacro:
|
break;
|
||||||
snprintf(name, 32, "macro%04X", id.id);
|
case Type::Table:
|
||||||
break;
|
snprintf(name, 32, "table%04X", id.id);
|
||||||
case Type::Table:
|
break;
|
||||||
snprintf(name, 32, "table%04X", id.id);
|
case Type::Keymap:
|
||||||
break;
|
snprintf(name, 32, "keymap%04X", id.id);
|
||||||
case Type::Keymap:
|
break;
|
||||||
snprintf(name, 32, "keymap%04X", id.id);
|
case Type::Layer:
|
||||||
break;
|
snprintf(name, 32, "layers%04X", id.id);
|
||||||
case Type::Layer:
|
break;
|
||||||
snprintf(name, 32, "layers%04X", id.id);
|
case Type::Song:
|
||||||
break;
|
snprintf(name, 32, "song%04X", id.id);
|
||||||
case Type::Song:
|
break;
|
||||||
snprintf(name, 32, "song%04X", id.id);
|
case Type::SFX:
|
||||||
break;
|
snprintf(name, 32, "sfx%04X", id.id);
|
||||||
case Type::SFX:
|
break;
|
||||||
snprintf(name, 32, "sfx%04X", id.id);
|
case Type::Group:
|
||||||
break;
|
snprintf(name, 32, "group%04X", id.id);
|
||||||
case Type::Group:
|
break;
|
||||||
snprintf(name, 32, "group%04X", id.id);
|
case Type::Sample:
|
||||||
break;
|
snprintf(name, 32, "sample%04X", id.id);
|
||||||
case Type::Sample:
|
break;
|
||||||
snprintf(name, 32, "sample%04X", id.id);
|
default:
|
||||||
break;
|
snprintf(name, 32, "obj%04X", id.id);
|
||||||
default:
|
break;
|
||||||
snprintf(name, 32, "obj%04X", id.id);
|
}
|
||||||
break;
|
return name;
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NameDB::generateDefaultName(Type tp) const
|
std::string NameDB::generateDefaultName(Type tp) const { return generateName(generateId(tp), tp); }
|
||||||
{
|
|
||||||
return generateName(generateId(tp), tp);
|
std::string_view NameDB::registerPair(std::string_view str, ObjectId id) {
|
||||||
|
m_stringToId[std::string(str)] = id;
|
||||||
|
return m_idToString.insert(std::make_pair(id, str)).first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view NameDB::registerPair(std::string_view str, ObjectId id)
|
std::string_view NameDB::resolveNameFromId(ObjectId id) const {
|
||||||
{
|
auto search = m_idToString.find(id);
|
||||||
m_stringToId[std::string(str)] = id;
|
if (search == m_idToString.cend()) {
|
||||||
return m_idToString.insert(std::make_pair(id, str)).first->second;
|
Log.report(logvisor::Error, "Unable to resolve ID 0x%04X", id.id);
|
||||||
|
return ""sv;
|
||||||
|
}
|
||||||
|
return search->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view NameDB::resolveNameFromId(ObjectId id) const
|
ObjectId NameDB::resolveIdFromName(std::string_view str) const {
|
||||||
{
|
auto search = m_stringToId.find(std::string(str));
|
||||||
auto search = m_idToString.find(id);
|
if (search == m_stringToId.cend()) {
|
||||||
if (search == m_idToString.cend())
|
Log.report(logvisor::Error, "Unable to resolve name %s", str.data());
|
||||||
{
|
return {};
|
||||||
Log.report(logvisor::Error, "Unable to resolve ID 0x%04X", id.id);
|
}
|
||||||
return ""sv;
|
return search->second;
|
||||||
}
|
|
||||||
return search->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectId NameDB::resolveIdFromName(std::string_view str) const
|
void NameDB::remove(ObjectId id) {
|
||||||
{
|
auto search = m_idToString.find(id);
|
||||||
auto search = m_stringToId.find(std::string(str));
|
if (search == m_idToString.cend())
|
||||||
if (search == m_stringToId.cend())
|
return;
|
||||||
{
|
auto search2 = m_stringToId.find(search->second);
|
||||||
Log.report(logvisor::Error, "Unable to resolve name %s", str.data());
|
if (search2 == m_stringToId.cend())
|
||||||
return {};
|
return;
|
||||||
}
|
m_idToString.erase(search);
|
||||||
return search->second;
|
m_stringToId.erase(search2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NameDB::remove(ObjectId id)
|
void NameDB::rename(ObjectId id, std::string_view str) {
|
||||||
{
|
auto search = m_idToString.find(id);
|
||||||
auto search = m_idToString.find(id);
|
if (search == m_idToString.cend())
|
||||||
if (search == m_idToString.cend())
|
return;
|
||||||
return;
|
if (search->second == str)
|
||||||
auto search2 = m_stringToId.find(search->second);
|
return;
|
||||||
if (search2 == m_stringToId.cend())
|
auto search2 = m_stringToId.find(search->second);
|
||||||
return;
|
if (search2 == m_stringToId.cend())
|
||||||
m_idToString.erase(search);
|
return;
|
||||||
m_stringToId.erase(search2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameDB::rename(ObjectId id, std::string_view str)
|
|
||||||
{
|
|
||||||
auto search = m_idToString.find(id);
|
|
||||||
if (search == m_idToString.cend())
|
|
||||||
return;
|
|
||||||
if (search->second == str)
|
|
||||||
return;
|
|
||||||
auto search2 = m_stringToId.find(search->second);
|
|
||||||
if (search2 == m_stringToId.cend())
|
|
||||||
return;
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
std::swap(m_stringToId[std::string(str)], search2->second);
|
std::swap(m_stringToId[std::string(str)], search2->second);
|
||||||
m_stringToId.erase(search2);
|
m_stringToId.erase(search2);
|
||||||
#else
|
#else
|
||||||
auto nh = m_stringToId.extract(search2);
|
auto nh = m_stringToId.extract(search2);
|
||||||
nh.key() = str;
|
nh.key() = str;
|
||||||
m_stringToId.insert(std::move(nh));
|
m_stringToId.insert(std::move(nh));
|
||||||
#endif
|
#endif
|
||||||
search->second = str;
|
search->second = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
void LittleUInt24::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& reader)
|
void LittleUInt24::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& reader) {
|
||||||
{
|
union {
|
||||||
union
|
atUint32 val;
|
||||||
{
|
char bytes[4];
|
||||||
atUint32 val;
|
} data = {};
|
||||||
char bytes[4];
|
reader.readBytesToBuf(data.bytes, 3);
|
||||||
} data = {};
|
val = SLittle(data.val);
|
||||||
reader.readBytesToBuf(data.bytes, 3);
|
|
||||||
val = SLittle(data.val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
void LittleUInt24::Enumerate<LittleDNA::Write>(athena::io::IStreamWriter& writer)
|
void LittleUInt24::Enumerate<LittleDNA::Write>(athena::io::IStreamWriter& writer) {
|
||||||
{
|
union {
|
||||||
union
|
atUint32 val;
|
||||||
{
|
char bytes[4];
|
||||||
atUint32 val;
|
} data;
|
||||||
char bytes[4];
|
data.val = SLittle(val);
|
||||||
} data;
|
writer.writeBytes(data.bytes, 3);
|
||||||
data.val = SLittle(val);
|
|
||||||
writer.writeBytes(data.bytes, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
void LittleUInt24::Enumerate<LittleDNA::BinarySize>(size_t& sz)
|
void LittleUInt24::Enumerate<LittleDNA::BinarySize>(size_t& sz) {
|
||||||
{
|
sz += 3;
|
||||||
sz += 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
void LittleUInt24::Enumerate<LittleDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
|
void LittleUInt24::Enumerate<LittleDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
|
||||||
{
|
val = reader.readUint32(nullptr);
|
||||||
val = reader.readUint32(nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template <>
|
||||||
void LittleUInt24::Enumerate<LittleDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
|
void LittleUInt24::Enumerate<LittleDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
|
||||||
{
|
writer.writeUint32(nullptr, val);
|
||||||
writer.writeUint32(nullptr, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* LittleUInt24::DNAType()
|
const char* LittleUInt24::DNAType() { return "amuse::LittleUInt24"; }
|
||||||
{
|
|
||||||
return "amuse::LittleUInt24";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
1133
lib/DSPCodec.cpp
1133
lib/DSPCodec.cpp
File diff suppressed because it is too large
Load Diff
|
@ -19,273 +19,251 @@
|
||||||
|
|
||||||
#include "amuse/DirectoryEnumerator.hpp"
|
#include "amuse/DirectoryEnumerator.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
DirectoryEnumerator::DirectoryEnumerator(SystemStringView path, Mode mode, bool sizeSort, bool reverse, bool noHidden)
|
DirectoryEnumerator::DirectoryEnumerator(SystemStringView path, Mode mode, bool sizeSort, bool reverse, bool noHidden) {
|
||||||
{
|
Sstat theStat;
|
||||||
Sstat theStat;
|
if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode))
|
||||||
if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode))
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
SystemString wc(path);
|
SystemString wc(path);
|
||||||
wc += _SYS_STR("/*");
|
wc += _SYS_STR("/*");
|
||||||
WIN32_FIND_DATAW d;
|
WIN32_FIND_DATAW d;
|
||||||
HANDLE dir = FindFirstFileW(wc.c_str(), &d);
|
HANDLE dir = FindFirstFileW(wc.c_str(), &d);
|
||||||
if (dir == INVALID_HANDLE_VALUE)
|
if (dir == INVALID_HANDLE_VALUE)
|
||||||
return;
|
return;
|
||||||
switch (mode)
|
switch (mode) {
|
||||||
{
|
case Mode::Native:
|
||||||
case Mode::Native:
|
do {
|
||||||
do
|
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
||||||
{
|
continue;
|
||||||
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||||
continue;
|
continue;
|
||||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
SystemString fp(path);
|
||||||
continue;
|
fp += _SYS_STR('/');
|
||||||
SystemString fp(path);
|
fp += d.cFileName;
|
||||||
fp += _SYS_STR('/');
|
Sstat st;
|
||||||
fp += d.cFileName;
|
if (Stat(fp.c_str(), &st))
|
||||||
Sstat st;
|
continue;
|
||||||
if (Stat(fp.c_str(), &st))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
size_t sz = 0;
|
size_t sz = 0;
|
||||||
bool isDir = false;
|
bool isDir = false;
|
||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode))
|
||||||
isDir = true;
|
isDir = true;
|
||||||
else if (S_ISREG(st.st_mode))
|
else if (S_ISREG(st.st_mode))
|
||||||
sz = st.st_size;
|
sz = st.st_size;
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m_entries.emplace_back(fp, d.cFileName, sz, isDir);
|
m_entries.emplace_back(fp, d.cFileName, sz, isDir);
|
||||||
} while (FindNextFileW(dir, &d));
|
} while (FindNextFileW(dir, &d));
|
||||||
break;
|
break;
|
||||||
case Mode::DirsThenFilesSorted:
|
case Mode::DirsThenFilesSorted:
|
||||||
case Mode::DirsSorted:
|
case Mode::DirsSorted: {
|
||||||
{
|
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
do {
|
||||||
do
|
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
||||||
{
|
continue;
|
||||||
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||||
continue;
|
continue;
|
||||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
SystemString fp(path);
|
||||||
continue;
|
fp += _SYS_STR('/');
|
||||||
SystemString fp(path);
|
fp += d.cFileName;
|
||||||
fp += _SYS_STR('/');
|
Sstat st;
|
||||||
fp += d.cFileName;
|
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
|
||||||
Sstat st;
|
continue;
|
||||||
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
|
sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, 0, true)));
|
||||||
continue;
|
} while (FindNextFileW(dir, &d));
|
||||||
sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, 0, true)));
|
|
||||||
} while (FindNextFileW(dir, &d));
|
|
||||||
|
|
||||||
m_entries.reserve(sort.size());
|
m_entries.reserve(sort.size());
|
||||||
if (reverse)
|
if (reverse)
|
||||||
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
||||||
m_entries.push_back(std::move(it->second));
|
m_entries.push_back(std::move(it->second));
|
||||||
else
|
else
|
||||||
for (auto& e : sort)
|
for (auto& e : sort)
|
||||||
m_entries.push_back(std::move(e.second));
|
m_entries.push_back(std::move(e.second));
|
||||||
|
|
||||||
if (mode == Mode::DirsSorted)
|
if (mode == Mode::DirsSorted)
|
||||||
break;
|
break;
|
||||||
FindClose(dir);
|
|
||||||
dir = FindFirstFileW(wc.c_str(), &d);
|
|
||||||
}
|
|
||||||
case Mode::FilesSorted:
|
|
||||||
{
|
|
||||||
if (mode == Mode::FilesSorted)
|
|
||||||
m_entries.clear();
|
|
||||||
|
|
||||||
if (sizeSort)
|
|
||||||
{
|
|
||||||
std::multimap<size_t, Entry> sort;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
|
||||||
continue;
|
|
||||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
|
||||||
continue;
|
|
||||||
SystemString fp(path);
|
|
||||||
fp += _SYS_STR('/');
|
|
||||||
fp += d.cFileName;
|
|
||||||
Sstat st;
|
|
||||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
|
||||||
continue;
|
|
||||||
sort.emplace(std::make_pair(st.st_size, Entry(fp, d.cFileName, st.st_size, false)));
|
|
||||||
} while (FindNextFileW(dir, &d));
|
|
||||||
|
|
||||||
m_entries.reserve(sort.size());
|
|
||||||
if (reverse)
|
|
||||||
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
|
||||||
m_entries.push_back(std::move(it->second));
|
|
||||||
else
|
|
||||||
for (auto& e : sort)
|
|
||||||
m_entries.push_back(std::move(e.second));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
|
||||||
continue;
|
|
||||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
|
||||||
continue;
|
|
||||||
SystemString fp(path);
|
|
||||||
fp += _SYS_STR('/');
|
|
||||||
fp += d.cFileName;
|
|
||||||
Sstat st;
|
|
||||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
|
||||||
continue;
|
|
||||||
sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, st.st_size, false)));
|
|
||||||
} while (FindNextFileW(dir, &d));
|
|
||||||
|
|
||||||
m_entries.reserve(sort.size());
|
|
||||||
if (reverse)
|
|
||||||
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
|
||||||
m_entries.push_back(std::move(it->second));
|
|
||||||
else
|
|
||||||
for (auto& e : sort)
|
|
||||||
m_entries.push_back(std::move(e.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FindClose(dir);
|
FindClose(dir);
|
||||||
|
dir = FindFirstFileW(wc.c_str(), &d);
|
||||||
|
}
|
||||||
|
case Mode::FilesSorted: {
|
||||||
|
if (mode == Mode::FilesSorted)
|
||||||
|
m_entries.clear();
|
||||||
|
|
||||||
|
if (sizeSort) {
|
||||||
|
std::multimap<size_t, Entry> sort;
|
||||||
|
do {
|
||||||
|
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
||||||
|
continue;
|
||||||
|
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||||
|
continue;
|
||||||
|
SystemString fp(path);
|
||||||
|
fp += _SYS_STR('/');
|
||||||
|
fp += d.cFileName;
|
||||||
|
Sstat st;
|
||||||
|
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||||
|
continue;
|
||||||
|
sort.emplace(std::make_pair(st.st_size, Entry(fp, d.cFileName, st.st_size, false)));
|
||||||
|
} while (FindNextFileW(dir, &d));
|
||||||
|
|
||||||
|
m_entries.reserve(sort.size());
|
||||||
|
if (reverse)
|
||||||
|
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
||||||
|
m_entries.push_back(std::move(it->second));
|
||||||
|
else
|
||||||
|
for (auto& e : sort)
|
||||||
|
m_entries.push_back(std::move(e.second));
|
||||||
|
} else {
|
||||||
|
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||||
|
do {
|
||||||
|
if (!wcscmp(d.cFileName, _SYS_STR(".")) || !wcscmp(d.cFileName, _SYS_STR("..")))
|
||||||
|
continue;
|
||||||
|
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||||
|
continue;
|
||||||
|
SystemString fp(path);
|
||||||
|
fp += _SYS_STR('/');
|
||||||
|
fp += d.cFileName;
|
||||||
|
Sstat st;
|
||||||
|
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||||
|
continue;
|
||||||
|
sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, st.st_size, false)));
|
||||||
|
} while (FindNextFileW(dir, &d));
|
||||||
|
|
||||||
|
m_entries.reserve(sort.size());
|
||||||
|
if (reverse)
|
||||||
|
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
||||||
|
m_entries.push_back(std::move(it->second));
|
||||||
|
else
|
||||||
|
for (auto& e : sort)
|
||||||
|
m_entries.push_back(std::move(e.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FindClose(dir);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
DIR* dir = opendir(path.data());
|
DIR* dir = opendir(path.data());
|
||||||
if (!dir)
|
if (!dir)
|
||||||
return;
|
return;
|
||||||
const dirent* d;
|
const dirent* d;
|
||||||
switch (mode)
|
switch (mode) {
|
||||||
{
|
case Mode::Native:
|
||||||
case Mode::Native:
|
while ((d = readdir(dir))) {
|
||||||
while ((d = readdir(dir)))
|
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||||
{
|
continue;
|
||||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
if (noHidden && d->d_name[0] == '.')
|
||||||
continue;
|
continue;
|
||||||
if (noHidden && d->d_name[0] == '.')
|
SystemString fp(path);
|
||||||
continue;
|
fp += '/';
|
||||||
SystemString fp(path);
|
fp += d->d_name;
|
||||||
fp += '/';
|
Sstat st;
|
||||||
fp += d->d_name;
|
if (Stat(fp.c_str(), &st))
|
||||||
Sstat st;
|
continue;
|
||||||
if (Stat(fp.c_str(), &st))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
size_t sz = 0;
|
size_t sz = 0;
|
||||||
bool isDir = false;
|
bool isDir = false;
|
||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode))
|
||||||
isDir = true;
|
isDir = true;
|
||||||
else if (S_ISREG(st.st_mode))
|
else if (S_ISREG(st.st_mode))
|
||||||
sz = st.st_size;
|
sz = st.st_size;
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m_entries.emplace_back(fp, d->d_name, sz, isDir);
|
m_entries.emplace_back(fp, d->d_name, sz, isDir);
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Mode::DirsThenFilesSorted:
|
|
||||||
case Mode::DirsSorted:
|
|
||||||
{
|
|
||||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
|
||||||
while ((d = readdir(dir)))
|
|
||||||
{
|
|
||||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
|
||||||
continue;
|
|
||||||
if (noHidden && d->d_name[0] == '.')
|
|
||||||
continue;
|
|
||||||
SystemString fp(path);
|
|
||||||
fp += '/';
|
|
||||||
fp += d->d_name;
|
|
||||||
Sstat st;
|
|
||||||
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
|
|
||||||
continue;
|
|
||||||
sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, 0, true)));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_entries.reserve(sort.size());
|
|
||||||
if (reverse)
|
|
||||||
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
|
||||||
m_entries.push_back(std::move(it->second));
|
|
||||||
else
|
|
||||||
for (auto& e : sort)
|
|
||||||
m_entries.push_back(std::move(e.second));
|
|
||||||
|
|
||||||
if (mode == Mode::DirsSorted)
|
|
||||||
break;
|
|
||||||
rewinddir(dir);
|
|
||||||
}
|
}
|
||||||
case Mode::FilesSorted:
|
break;
|
||||||
{
|
case Mode::DirsThenFilesSorted:
|
||||||
if (mode == Mode::FilesSorted)
|
case Mode::DirsSorted: {
|
||||||
m_entries.clear();
|
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||||
|
while ((d = readdir(dir))) {
|
||||||
if (sizeSort)
|
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||||
{
|
continue;
|
||||||
std::multimap<size_t, Entry> sort;
|
if (noHidden && d->d_name[0] == '.')
|
||||||
while ((d = readdir(dir)))
|
continue;
|
||||||
{
|
SystemString fp(path);
|
||||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
fp += '/';
|
||||||
continue;
|
fp += d->d_name;
|
||||||
if (noHidden && d->d_name[0] == '.')
|
Sstat st;
|
||||||
continue;
|
if (Stat(fp.c_str(), &st) || !S_ISDIR(st.st_mode))
|
||||||
SystemString fp(path);
|
continue;
|
||||||
fp += '/';
|
sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, 0, true)));
|
||||||
fp += d->d_name;
|
|
||||||
Sstat st;
|
|
||||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
|
||||||
continue;
|
|
||||||
sort.emplace(std::make_pair(st.st_size, Entry(fp, d->d_name, st.st_size, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_entries.reserve(sort.size());
|
|
||||||
if (reverse)
|
|
||||||
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
|
||||||
m_entries.push_back(std::move(it->second));
|
|
||||||
else
|
|
||||||
for (auto& e : sort)
|
|
||||||
m_entries.push_back(std::move(e.second));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
|
||||||
while ((d = readdir(dir)))
|
|
||||||
{
|
|
||||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
|
||||||
continue;
|
|
||||||
if (noHidden && d->d_name[0] == '.')
|
|
||||||
continue;
|
|
||||||
SystemString fp(path);
|
|
||||||
fp += '/';
|
|
||||||
fp += d->d_name;
|
|
||||||
Sstat st;
|
|
||||||
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
|
||||||
continue;
|
|
||||||
sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, st.st_size, false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_entries.reserve(sort.size());
|
|
||||||
if (reverse)
|
|
||||||
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
|
||||||
m_entries.push_back(std::move(it->second));
|
|
||||||
else
|
|
||||||
for (auto& e : sort)
|
|
||||||
m_entries.push_back(std::move(e.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_entries.reserve(sort.size());
|
||||||
|
if (reverse)
|
||||||
|
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
||||||
|
m_entries.push_back(std::move(it->second));
|
||||||
|
else
|
||||||
|
for (auto& e : sort)
|
||||||
|
m_entries.push_back(std::move(e.second));
|
||||||
|
|
||||||
|
if (mode == Mode::DirsSorted)
|
||||||
|
break;
|
||||||
|
rewinddir(dir);
|
||||||
|
}
|
||||||
|
case Mode::FilesSorted: {
|
||||||
|
if (mode == Mode::FilesSorted)
|
||||||
|
m_entries.clear();
|
||||||
|
|
||||||
|
if (sizeSort) {
|
||||||
|
std::multimap<size_t, Entry> sort;
|
||||||
|
while ((d = readdir(dir))) {
|
||||||
|
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
if (noHidden && d->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
SystemString fp(path);
|
||||||
|
fp += '/';
|
||||||
|
fp += d->d_name;
|
||||||
|
Sstat st;
|
||||||
|
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||||
|
continue;
|
||||||
|
sort.emplace(std::make_pair(st.st_size, Entry(fp, d->d_name, st.st_size, false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_entries.reserve(sort.size());
|
||||||
|
if (reverse)
|
||||||
|
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
||||||
|
m_entries.push_back(std::move(it->second));
|
||||||
|
else
|
||||||
|
for (auto& e : sort)
|
||||||
|
m_entries.push_back(std::move(e.second));
|
||||||
|
} else {
|
||||||
|
std::map<SystemString, Entry, CaseInsensitiveCompare> sort;
|
||||||
|
while ((d = readdir(dir))) {
|
||||||
|
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||||
|
continue;
|
||||||
|
if (noHidden && d->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
SystemString fp(path);
|
||||||
|
fp += '/';
|
||||||
|
fp += d->d_name;
|
||||||
|
Sstat st;
|
||||||
|
if (Stat(fp.c_str(), &st) || !S_ISREG(st.st_mode))
|
||||||
|
continue;
|
||||||
|
sort.emplace(std::make_pair(d->d_name, Entry(fp, d->d_name, st.st_size, false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_entries.reserve(sort.size());
|
||||||
|
if (reverse)
|
||||||
|
for (auto it = sort.crbegin(); it != sort.crend(); ++it)
|
||||||
|
m_entries.push_back(std::move(it->second));
|
||||||
|
else
|
||||||
|
for (auto& e : sort)
|
||||||
|
m_entries.push_back(std::move(e.second));
|
||||||
}
|
}
|
||||||
closedir(dir);
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
static const float rsmpTab12khz[] =
|
static const float rsmpTab12khz[] =
|
||||||
|
@ -144,230 +143,209 @@ static const float rsmpTab12khz[] =
|
||||||
EffectChorus::EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)
|
EffectChorus::EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)
|
||||||
: x90_baseDelay(clamp(5u, baseDelay, 15u))
|
: x90_baseDelay(clamp(5u, baseDelay, 15u))
|
||||||
, x94_variation(clamp(0u, variation, 5u))
|
, x94_variation(clamp(0u, variation, 5u))
|
||||||
, x98_period(clamp(500u, period, 10000u))
|
, x98_period(clamp(500u, period, 10000u)) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
EffectChorusImp<T>::EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate)
|
EffectChorusImp<T>::EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate)
|
||||||
: EffectChorus(baseDelay, variation, period)
|
: EffectChorus(baseDelay, variation, period) {
|
||||||
{
|
_setup(sampleRate);
|
||||||
_setup(sampleRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectChorusImp<T>::_setup(double sampleRate)
|
void EffectChorusImp<T>::_setup(double sampleRate) {
|
||||||
{
|
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
|
||||||
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
|
m_blockSamples = m_sampsPerMs * 5;
|
||||||
m_blockSamples = m_sampsPerMs * 5;
|
|
||||||
|
|
||||||
delete[] x0_lastChans[0][0];
|
delete[] x0_lastChans[0][0];
|
||||||
|
|
||||||
T* buf = new T[m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS * 8];
|
T* buf = new T[m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS * 8];
|
||||||
memset(buf, 0, m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS * 8 * sizeof(T));
|
memset(buf, 0, m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS * 8 * sizeof(T));
|
||||||
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
||||||
|
|
||||||
for (int c = 0; c < 8; ++c)
|
for (int c = 0; c < 8; ++c)
|
||||||
for (int i = 0; i < AMUSE_CHORUS_NUM_BLOCKS; ++i)
|
for (int i = 0; i < AMUSE_CHORUS_NUM_BLOCKS; ++i)
|
||||||
x0_lastChans[c][i] = buf + chanPitch * c + m_blockSamples * i;
|
x0_lastChans[c][i] = buf + chanPitch * c + m_blockSamples * i;
|
||||||
|
|
||||||
x6c_src.x88_trigger = chanPitch;
|
x6c_src.x88_trigger = chanPitch;
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectChorusImp<T>::_update()
|
void EffectChorusImp<T>::_update() {
|
||||||
{
|
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
||||||
|
size_t fifteenSamps = 15 * m_sampsPerMs;
|
||||||
|
|
||||||
|
x5c_currentPosHi = m_blockSamples * 2 - (x90_baseDelay - 5) * m_sampsPerMs;
|
||||||
|
x58_currentPosLo = 0;
|
||||||
|
uint32_t temp = (x5c_currentPosHi + (x24_currentLast - 1) * m_blockSamples);
|
||||||
|
x5c_currentPosHi = temp % (chanPitch / fifteenSamps * fifteenSamps);
|
||||||
|
|
||||||
|
x68_pitchOffsetPeriod = (x98_period / 5 + 1) & ~1;
|
||||||
|
x64_pitchOffsetPeriodCount = x68_pitchOffsetPeriod / 2;
|
||||||
|
x60_pitchOffset = x94_variation * 2048 / x68_pitchOffsetPeriod;
|
||||||
|
|
||||||
|
m_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
EffectChorusImp<T>::~EffectChorusImp() {
|
||||||
|
delete[] x0_lastChans[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectChorusImp<T>::SrcInfo::doSrc1(size_t blockSamples, size_t chanCount) {
|
||||||
|
float old1 = x74_old[0];
|
||||||
|
float old2 = x74_old[1];
|
||||||
|
float old3 = x74_old[2];
|
||||||
|
float cur = x70_smpBase[x7c_posHi];
|
||||||
|
|
||||||
|
T* dest = x6c_dest;
|
||||||
|
for (size_t i = 0; i < blockSamples; ++i) {
|
||||||
|
const float* selTab = &rsmpTab12khz[x78_posLo >> 23 & 0x1fc];
|
||||||
|
|
||||||
|
uint64_t ovrTest = uint64_t(x78_posLo) + uint64_t(x80_pitchLo);
|
||||||
|
if (ovrTest > UINT32_MAX) {
|
||||||
|
/* overflow */
|
||||||
|
x78_posLo = ovrTest & 0xffffffff;
|
||||||
|
++x7c_posHi;
|
||||||
|
if (x7c_posHi == x88_trigger)
|
||||||
|
x7c_posHi = x8c_target;
|
||||||
|
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
||||||
|
dest += chanCount;
|
||||||
|
old1 = old2;
|
||||||
|
old2 = old3;
|
||||||
|
old3 = cur;
|
||||||
|
cur = x70_smpBase[x7c_posHi];
|
||||||
|
} else {
|
||||||
|
x78_posLo = ovrTest;
|
||||||
|
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
||||||
|
dest += chanCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x74_old[0] = old1;
|
||||||
|
x74_old[1] = old2;
|
||||||
|
x74_old[2] = old3;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectChorusImp<T>::SrcInfo::doSrc2(size_t blockSamples, size_t chanCount) {
|
||||||
|
float old1 = x74_old[0];
|
||||||
|
float old2 = x74_old[1];
|
||||||
|
float old3 = x74_old[2];
|
||||||
|
float cur = x70_smpBase[x7c_posHi];
|
||||||
|
|
||||||
|
T* dest = x6c_dest;
|
||||||
|
for (size_t i = 0; i < blockSamples; ++i) {
|
||||||
|
const float* selTab = &rsmpTab12khz[x78_posLo >> 23 & 0x1fc];
|
||||||
|
++x7c_posHi;
|
||||||
|
|
||||||
|
uint64_t ovrTest = uint64_t(x78_posLo) + uint64_t(x80_pitchLo);
|
||||||
|
if (ovrTest > UINT32_MAX) {
|
||||||
|
/* overflow */
|
||||||
|
x78_posLo = ovrTest & 0xffffffff;
|
||||||
|
|
||||||
|
if (x7c_posHi == x88_trigger)
|
||||||
|
x7c_posHi = x8c_target;
|
||||||
|
|
||||||
|
old1 = old3;
|
||||||
|
old2 = cur;
|
||||||
|
old3 = x70_smpBase[x7c_posHi];
|
||||||
|
|
||||||
|
++x7c_posHi;
|
||||||
|
if (x7c_posHi == x88_trigger)
|
||||||
|
x7c_posHi = x8c_target;
|
||||||
|
|
||||||
|
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
||||||
|
dest += chanCount;
|
||||||
|
|
||||||
|
cur = x70_smpBase[x7c_posHi];
|
||||||
|
} else {
|
||||||
|
x78_posLo = ovrTest;
|
||||||
|
|
||||||
|
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
||||||
|
dest += chanCount;
|
||||||
|
|
||||||
|
old1 = old2;
|
||||||
|
old2 = old3;
|
||||||
|
old3 = cur;
|
||||||
|
|
||||||
|
if (x7c_posHi == x88_trigger)
|
||||||
|
x7c_posHi = x8c_target;
|
||||||
|
|
||||||
|
cur = x70_smpBase[x7c_posHi];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x74_old[0] = old1;
|
||||||
|
x74_old[1] = old2;
|
||||||
|
x74_old[2] = old3;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectChorusImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) {
|
||||||
|
if (m_dirty)
|
||||||
|
_update();
|
||||||
|
|
||||||
|
size_t remFrames = frameCount;
|
||||||
|
for (size_t f = 0; f < frameCount;) {
|
||||||
|
uint8_t next = x24_currentLast + 1;
|
||||||
|
uint8_t buf = next % 3;
|
||||||
|
T* bufs[8] = {
|
||||||
|
x0_lastChans[0][buf], x0_lastChans[1][buf], x0_lastChans[2][buf], x0_lastChans[3][buf],
|
||||||
|
x0_lastChans[4][buf], x0_lastChans[5][buf], x0_lastChans[6][buf], x0_lastChans[7][buf],
|
||||||
|
};
|
||||||
|
|
||||||
|
T* inBuf = audio;
|
||||||
|
for (size_t s = 0; f < frameCount && s < m_blockSamples; ++s, ++f)
|
||||||
|
for (size_t c = 0; c < chanMap.m_channelCount && c < 8; ++c)
|
||||||
|
*bufs[c]++ = *inBuf++;
|
||||||
|
|
||||||
|
x6c_src.x84_pitchHi = (x60_pitchOffset >> 16) + 1;
|
||||||
|
x6c_src.x80_pitchLo = (x60_pitchOffset << 16);
|
||||||
|
|
||||||
|
--x64_pitchOffsetPeriodCount;
|
||||||
|
if (x64_pitchOffsetPeriodCount == 0) {
|
||||||
|
x64_pitchOffsetPeriodCount = x68_pitchOffsetPeriod;
|
||||||
|
x60_pitchOffset = -x60_pitchOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* outBuf = audio;
|
||||||
|
size_t bs = std::min(remFrames, size_t(m_blockSamples));
|
||||||
|
for (size_t c = 0; c < chanMap.m_channelCount && c < 8; ++c) {
|
||||||
|
x6c_src.x7c_posHi = x5c_currentPosHi;
|
||||||
|
x6c_src.x78_posLo = x58_currentPosLo;
|
||||||
|
|
||||||
|
x6c_src.x6c_dest = outBuf++;
|
||||||
|
x6c_src.x70_smpBase = x0_lastChans[c][0];
|
||||||
|
x6c_src.x74_old = x28_oldChans[c];
|
||||||
|
|
||||||
|
switch (x6c_src.x84_pitchHi) {
|
||||||
|
case 0:
|
||||||
|
x6c_src.doSrc1(bs, chanMap.m_channelCount);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
x6c_src.doSrc2(bs, chanMap.m_channelCount);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audio += bs * chanMap.m_channelCount;
|
||||||
|
remFrames -= bs;
|
||||||
|
|
||||||
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
||||||
size_t fifteenSamps = 15 * m_sampsPerMs;
|
size_t fifteenSamps = 15 * m_sampsPerMs;
|
||||||
|
|
||||||
x5c_currentPosHi = m_blockSamples * 2 - (x90_baseDelay - 5) * m_sampsPerMs;
|
x5c_currentPosHi = x6c_src.x7c_posHi % (chanPitch / fifteenSamps * fifteenSamps);
|
||||||
x58_currentPosLo = 0;
|
x58_currentPosLo = x6c_src.x78_posLo;
|
||||||
uint32_t temp = (x5c_currentPosHi + (x24_currentLast - 1) * m_blockSamples);
|
x24_currentLast = buf;
|
||||||
x5c_currentPosHi = temp % (chanPitch / fifteenSamps * fifteenSamps);
|
}
|
||||||
|
|
||||||
x68_pitchOffsetPeriod = (x98_period / 5 + 1) & ~1;
|
|
||||||
x64_pitchOffsetPeriodCount = x68_pitchOffsetPeriod / 2;
|
|
||||||
x60_pitchOffset = x94_variation * 2048 / x68_pitchOffsetPeriod;
|
|
||||||
|
|
||||||
m_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
EffectChorusImp<T>::~EffectChorusImp()
|
|
||||||
{
|
|
||||||
delete[] x0_lastChans[0][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectChorusImp<T>::SrcInfo::doSrc1(size_t blockSamples, size_t chanCount)
|
|
||||||
{
|
|
||||||
float old1 = x74_old[0];
|
|
||||||
float old2 = x74_old[1];
|
|
||||||
float old3 = x74_old[2];
|
|
||||||
float cur = x70_smpBase[x7c_posHi];
|
|
||||||
|
|
||||||
T* dest = x6c_dest;
|
|
||||||
for (size_t i = 0; i < blockSamples; ++i)
|
|
||||||
{
|
|
||||||
const float* selTab = &rsmpTab12khz[x78_posLo >> 23 & 0x1fc];
|
|
||||||
|
|
||||||
uint64_t ovrTest = uint64_t(x78_posLo) + uint64_t(x80_pitchLo);
|
|
||||||
if (ovrTest > UINT32_MAX)
|
|
||||||
{
|
|
||||||
/* overflow */
|
|
||||||
x78_posLo = ovrTest & 0xffffffff;
|
|
||||||
++x7c_posHi;
|
|
||||||
if (x7c_posHi == x88_trigger)
|
|
||||||
x7c_posHi = x8c_target;
|
|
||||||
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
|
||||||
dest += chanCount;
|
|
||||||
old1 = old2;
|
|
||||||
old2 = old3;
|
|
||||||
old3 = cur;
|
|
||||||
cur = x70_smpBase[x7c_posHi];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x78_posLo = ovrTest;
|
|
||||||
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
|
||||||
dest += chanCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x74_old[0] = old1;
|
|
||||||
x74_old[1] = old2;
|
|
||||||
x74_old[2] = old3;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectChorusImp<T>::SrcInfo::doSrc2(size_t blockSamples, size_t chanCount)
|
|
||||||
{
|
|
||||||
float old1 = x74_old[0];
|
|
||||||
float old2 = x74_old[1];
|
|
||||||
float old3 = x74_old[2];
|
|
||||||
float cur = x70_smpBase[x7c_posHi];
|
|
||||||
|
|
||||||
T* dest = x6c_dest;
|
|
||||||
for (size_t i = 0; i < blockSamples; ++i)
|
|
||||||
{
|
|
||||||
const float* selTab = &rsmpTab12khz[x78_posLo >> 23 & 0x1fc];
|
|
||||||
++x7c_posHi;
|
|
||||||
|
|
||||||
uint64_t ovrTest = uint64_t(x78_posLo) + uint64_t(x80_pitchLo);
|
|
||||||
if (ovrTest > UINT32_MAX)
|
|
||||||
{
|
|
||||||
/* overflow */
|
|
||||||
x78_posLo = ovrTest & 0xffffffff;
|
|
||||||
|
|
||||||
if (x7c_posHi == x88_trigger)
|
|
||||||
x7c_posHi = x8c_target;
|
|
||||||
|
|
||||||
old1 = old3;
|
|
||||||
old2 = cur;
|
|
||||||
old3 = x70_smpBase[x7c_posHi];
|
|
||||||
|
|
||||||
++x7c_posHi;
|
|
||||||
if (x7c_posHi == x88_trigger)
|
|
||||||
x7c_posHi = x8c_target;
|
|
||||||
|
|
||||||
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
|
||||||
dest += chanCount;
|
|
||||||
|
|
||||||
cur = x70_smpBase[x7c_posHi];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x78_posLo = ovrTest;
|
|
||||||
|
|
||||||
*dest = ClampFull<T>(selTab[0] * old1 + selTab[1] * old2 + selTab[2] * old3 + selTab[3] * cur);
|
|
||||||
dest += chanCount;
|
|
||||||
|
|
||||||
old1 = old2;
|
|
||||||
old2 = old3;
|
|
||||||
old3 = cur;
|
|
||||||
|
|
||||||
if (x7c_posHi == x88_trigger)
|
|
||||||
x7c_posHi = x8c_target;
|
|
||||||
|
|
||||||
cur = x70_smpBase[x7c_posHi];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x74_old[0] = old1;
|
|
||||||
x74_old[1] = old2;
|
|
||||||
x74_old[2] = old3;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectChorusImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
|
||||||
{
|
|
||||||
if (m_dirty)
|
|
||||||
_update();
|
|
||||||
|
|
||||||
size_t remFrames = frameCount;
|
|
||||||
for (size_t f = 0; f < frameCount;)
|
|
||||||
{
|
|
||||||
uint8_t next = x24_currentLast + 1;
|
|
||||||
uint8_t buf = next % 3;
|
|
||||||
T* bufs[8] = {
|
|
||||||
x0_lastChans[0][buf], x0_lastChans[1][buf], x0_lastChans[2][buf], x0_lastChans[3][buf],
|
|
||||||
x0_lastChans[4][buf], x0_lastChans[5][buf], x0_lastChans[6][buf], x0_lastChans[7][buf],
|
|
||||||
};
|
|
||||||
|
|
||||||
T* inBuf = audio;
|
|
||||||
for (size_t s = 0; f < frameCount && s < m_blockSamples; ++s, ++f)
|
|
||||||
for (size_t c = 0; c < chanMap.m_channelCount && c < 8; ++c)
|
|
||||||
*bufs[c]++ = *inBuf++;
|
|
||||||
|
|
||||||
x6c_src.x84_pitchHi = (x60_pitchOffset >> 16) + 1;
|
|
||||||
x6c_src.x80_pitchLo = (x60_pitchOffset << 16);
|
|
||||||
|
|
||||||
--x64_pitchOffsetPeriodCount;
|
|
||||||
if (x64_pitchOffsetPeriodCount == 0)
|
|
||||||
{
|
|
||||||
x64_pitchOffsetPeriodCount = x68_pitchOffsetPeriod;
|
|
||||||
x60_pitchOffset = -x60_pitchOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* outBuf = audio;
|
|
||||||
size_t bs = std::min(remFrames, size_t(m_blockSamples));
|
|
||||||
for (size_t c = 0; c < chanMap.m_channelCount && c < 8; ++c)
|
|
||||||
{
|
|
||||||
x6c_src.x7c_posHi = x5c_currentPosHi;
|
|
||||||
x6c_src.x78_posLo = x58_currentPosLo;
|
|
||||||
|
|
||||||
x6c_src.x6c_dest = outBuf++;
|
|
||||||
x6c_src.x70_smpBase = x0_lastChans[c][0];
|
|
||||||
x6c_src.x74_old = x28_oldChans[c];
|
|
||||||
|
|
||||||
switch (x6c_src.x84_pitchHi)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
x6c_src.doSrc1(bs, chanMap.m_channelCount);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
x6c_src.doSrc2(bs, chanMap.m_channelCount);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
audio += bs * chanMap.m_channelCount;
|
|
||||||
remFrames -= bs;
|
|
||||||
|
|
||||||
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
|
|
||||||
size_t fifteenSamps = 15 * m_sampsPerMs;
|
|
||||||
|
|
||||||
x5c_currentPosHi = x6c_src.x7c_posHi % (chanPitch / fifteenSamps * fifteenSamps);
|
|
||||||
x58_currentPosLo = x6c_src.x78_posLo;
|
|
||||||
x24_currentLast = buf;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template class EffectChorusImp<int16_t>;
|
template class EffectChorusImp<int16_t>;
|
||||||
template class EffectChorusImp<int32_t>;
|
template class EffectChorusImp<int32_t>;
|
||||||
template class EffectChorusImp<float>;
|
template class EffectChorusImp<float>;
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
|
@ -4,90 +4,78 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate)
|
EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate) {
|
||||||
{
|
initDelay = clamp(10u, initDelay, 5000u);
|
||||||
initDelay = clamp(10u, initDelay, 5000u);
|
initFeedback = clamp(0u, initFeedback, 100u);
|
||||||
initFeedback = clamp(0u, initFeedback, 100u);
|
initOutput = clamp(0u, initOutput, 100u);
|
||||||
initOutput = clamp(0u, initOutput, 100u);
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
for (int i = 0; i < 8; ++i) {
|
||||||
{
|
x3c_delay[i] = initDelay;
|
||||||
x3c_delay[i] = initDelay;
|
x48_feedback[i] = initFeedback;
|
||||||
x48_feedback[i] = initFeedback;
|
x54_output[i] = initOutput;
|
||||||
x54_output[i] = initOutput;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_setup(sampleRate);
|
_setup(sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
EffectDelayImp<T>::EffectDelayImp(const EffectDelayInfo& info, double sampleRate)
|
EffectDelayImp<T>::EffectDelayImp(const EffectDelayInfo& info, double sampleRate) {
|
||||||
{
|
for (int i = 0; i < 8; ++i) {
|
||||||
for (int i = 0; i < 8; ++i)
|
x3c_delay[i] = clamp(10u, info.delay[i], 5000u);
|
||||||
{
|
x48_feedback[i] = clamp(0u, info.feedback[i], 100u);
|
||||||
x3c_delay[i] = clamp(10u, info.delay[i], 5000u);
|
x54_output[i] = clamp(0u, info.output[i], 100u);
|
||||||
x48_feedback[i] = clamp(0u, info.feedback[i], 100u);
|
}
|
||||||
x54_output[i] = clamp(0u, info.output[i], 100u);
|
|
||||||
}
|
|
||||||
|
|
||||||
_setup(sampleRate);
|
_setup(sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectDelayImp<T>::_setup(double sampleRate)
|
void EffectDelayImp<T>::_setup(double sampleRate) {
|
||||||
{
|
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
|
||||||
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
|
m_blockSamples = m_sampsPerMs * 5;
|
||||||
m_blockSamples = m_sampsPerMs * 5;
|
|
||||||
|
|
||||||
|
_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectDelayImp<T>::_update() {
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
x0_currentSize[i] = ((x3c_delay[i] - 5) * m_sampsPerMs + 159) / 160;
|
||||||
|
xc_currentPos[i] = 0;
|
||||||
|
x18_currentFeedback[i] = x48_feedback[i] * 128 / 100;
|
||||||
|
x24_currentOutput[i] = x54_output[i] * 128 / 100;
|
||||||
|
|
||||||
|
x30_chanLines[i].reset(new T[m_blockSamples * x0_currentSize[i]]);
|
||||||
|
memset(x30_chanLines[i].get(), 0, m_blockSamples * x0_currentSize[i] * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectDelayImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) {
|
||||||
|
if (m_dirty)
|
||||||
_update();
|
_update();
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
for (size_t f = 0; f < frameCount;) {
|
||||||
void EffectDelayImp<T>::_update()
|
for (unsigned c = 0; c < chanMap.m_channelCount; ++c) {
|
||||||
{
|
T* chanAud = audio + c;
|
||||||
for (int i = 0; i < 8; ++i)
|
for (unsigned i = 0; i < m_blockSamples && f < frameCount; ++i, ++f) {
|
||||||
{
|
T& liveSamp = chanAud[chanMap.m_channelCount * i];
|
||||||
x0_currentSize[i] = ((x3c_delay[i] - 5) * m_sampsPerMs + 159) / 160;
|
T& samp = x30_chanLines[c][xc_currentPos[c] * m_blockSamples + i];
|
||||||
xc_currentPos[i] = 0;
|
samp = ClampFull<T>(samp * x18_currentFeedback[c] / 128 + liveSamp);
|
||||||
x18_currentFeedback[i] = x48_feedback[i] * 128 / 100;
|
liveSamp = samp * x24_currentOutput[c] / 128;
|
||||||
x24_currentOutput[i] = x54_output[i] * 128 / 100;
|
}
|
||||||
|
xc_currentPos[c] = (xc_currentPos[c] + 1) % x0_currentSize[c];
|
||||||
x30_chanLines[i].reset(new T[m_blockSamples * x0_currentSize[i]]);
|
|
||||||
memset(x30_chanLines[i].get(), 0, m_blockSamples * x0_currentSize[i] * sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectDelayImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
|
||||||
{
|
|
||||||
if (m_dirty)
|
|
||||||
_update();
|
|
||||||
|
|
||||||
for (size_t f = 0; f < frameCount;)
|
|
||||||
{
|
|
||||||
for (unsigned c = 0; c < chanMap.m_channelCount; ++c)
|
|
||||||
{
|
|
||||||
T* chanAud = audio + c;
|
|
||||||
for (unsigned i = 0; i < m_blockSamples && f < frameCount; ++i, ++f)
|
|
||||||
{
|
|
||||||
T& liveSamp = chanAud[chanMap.m_channelCount * i];
|
|
||||||
T& samp = x30_chanLines[c][xc_currentPos[c] * m_blockSamples + i];
|
|
||||||
samp = ClampFull<T>(samp * x18_currentFeedback[c] / 128 + liveSamp);
|
|
||||||
liveSamp = samp * x24_currentOutput[c] / 128;
|
|
||||||
}
|
|
||||||
xc_currentPos[c] = (xc_currentPos[c] + 1) % x0_currentSize[c];
|
|
||||||
}
|
|
||||||
audio += chanMap.m_channelCount * m_blockSamples;
|
|
||||||
}
|
}
|
||||||
|
audio += chanMap.m_channelCount * m_blockSamples;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template class EffectDelayImp<int16_t>;
|
template class EffectDelayImp<int16_t>;
|
||||||
template class EffectDelayImp<int32_t>;
|
template class EffectDelayImp<int32_t>;
|
||||||
template class EffectDelayImp<float>;
|
template class EffectDelayImp<float>;
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
|
|
||||||
|
@ -38,23 +37,21 @@ static const size_t LPTapDelays[] =
|
||||||
|
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
|
|
||||||
void ReverbDelayLine::allocate(int32_t delay)
|
void ReverbDelayLine::allocate(int32_t delay) {
|
||||||
{
|
delay += 2;
|
||||||
delay += 2;
|
x8_length = delay;
|
||||||
x8_length = delay;
|
xc_inputs.reset(new float[delay]);
|
||||||
xc_inputs.reset(new float[delay]);
|
memset(xc_inputs.get(), 0, x8_length * sizeof(float));
|
||||||
memset(xc_inputs.get(), 0, x8_length * sizeof(float));
|
x10_lastInput = 0.f;
|
||||||
x10_lastInput = 0.f;
|
setdelay(delay / 2);
|
||||||
setdelay(delay / 2);
|
x0_inPoint = 0;
|
||||||
x0_inPoint = 0;
|
x4_outPoint = 0;
|
||||||
x4_outPoint = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReverbDelayLine::setdelay(int32_t delay)
|
void ReverbDelayLine::setdelay(int32_t delay) {
|
||||||
{
|
x4_outPoint = x0_inPoint - delay;
|
||||||
x4_outPoint = x0_inPoint - delay;
|
while (x4_outPoint < 0)
|
||||||
while (x4_outPoint < 0)
|
x4_outPoint += x8_length;
|
||||||
x4_outPoint += x8_length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectReverbStd::EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay)
|
EffectReverbStd::EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay)
|
||||||
|
@ -62,301 +59,103 @@ EffectReverbStd::EffectReverbStd(float coloration, float mix, float time, float
|
||||||
, x144_x1cc_mix(clamp(0.f, mix, 1.f))
|
, x144_x1cc_mix(clamp(0.f, mix, 1.f))
|
||||||
, x148_x1d0_time(clamp(0.01f, time, 10.f))
|
, x148_x1d0_time(clamp(0.01f, time, 10.f))
|
||||||
, x14c_x1d4_damping(clamp(0.f, damping, 1.f))
|
, x14c_x1d4_damping(clamp(0.f, damping, 1.f))
|
||||||
, x150_x1d8_preDelay(clamp(0.f, preDelay, 0.1f))
|
, x150_x1d8_preDelay(clamp(0.f, preDelay, 0.1f)) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectReverbHi::EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk)
|
EffectReverbHi::EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk)
|
||||||
: EffectReverbStd(coloration, mix, time, damping, preDelay), x1dc_crosstalk(clamp(0.f, crosstalk, 1.0f))
|
: EffectReverbStd(coloration, mix, time, damping, preDelay), x1dc_crosstalk(clamp(0.f, crosstalk, 1.0f)) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
EffectReverbStdImp<T>::EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay,
|
EffectReverbStdImp<T>::EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay,
|
||||||
double sampleRate)
|
double sampleRate)
|
||||||
: EffectReverbStd(coloration, mix, time, damping, preDelay)
|
: EffectReverbStd(coloration, mix, time, damping, preDelay) {
|
||||||
{
|
_setup(sampleRate);
|
||||||
_setup(sampleRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectReverbStdImp<T>::_setup(double sampleRate)
|
void EffectReverbStdImp<T>::_setup(double sampleRate) {
|
||||||
{
|
m_sampleRate = sampleRate;
|
||||||
m_sampleRate = sampleRate;
|
_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectReverbStdImp<T>::_update() {
|
||||||
|
float timeSamples = x148_x1d0_time * m_sampleRate;
|
||||||
|
double rateRatio = m_sampleRate / NativeSampleRate;
|
||||||
|
for (int c = 0; c < 8; ++c) {
|
||||||
|
for (int t = 0; t < 2; ++t) {
|
||||||
|
ReverbDelayLine& combLine = x78_C[c][t];
|
||||||
|
size_t tapDelay = CTapDelays[t] * rateRatio;
|
||||||
|
combLine.allocate(tapDelay);
|
||||||
|
combLine.setdelay(tapDelay);
|
||||||
|
xf4_combCoef[c][t] = std::pow(10.f, tapDelay * -3.f / timeSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int t = 0; t < 2; ++t) {
|
||||||
|
ReverbDelayLine& allPassLine = x0_AP[c][t];
|
||||||
|
size_t tapDelay = APTapDelays[t] * rateRatio;
|
||||||
|
allPassLine.allocate(tapDelay);
|
||||||
|
allPassLine.setdelay(tapDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xf0_allPassCoef = x140_x1c8_coloration;
|
||||||
|
x118_level = x144_x1cc_mix;
|
||||||
|
x11c_damping = x14c_x1d4_damping;
|
||||||
|
|
||||||
|
if (x11c_damping < 0.05f)
|
||||||
|
x11c_damping = 0.05f;
|
||||||
|
|
||||||
|
x11c_damping = 1.f - (x11c_damping * 0.8f + 0.05);
|
||||||
|
|
||||||
|
if (x150_x1d8_preDelay != 0.f) {
|
||||||
|
x120_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
x124_preDelayLine[i].reset(new float[x120_preDelayTime]);
|
||||||
|
memset(x124_preDelayLine[i].get(), 0, x120_preDelayTime * sizeof(float));
|
||||||
|
x130_preDelayPtr[i] = x124_preDelayLine[i].get();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x120_preDelayTime = 0;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
x124_preDelayLine[i] = nullptr;
|
||||||
|
x130_preDelayPtr[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) {
|
||||||
|
if (m_dirty)
|
||||||
_update();
|
_update();
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
float dampWet = x118_level * 0.6f;
|
||||||
void EffectReverbStdImp<T>::_update()
|
float dampDry = 0.6f - dampWet;
|
||||||
{
|
|
||||||
float timeSamples = x148_x1d0_time * m_sampleRate;
|
|
||||||
double rateRatio = m_sampleRate / NativeSampleRate;
|
|
||||||
for (int c = 0; c < 8; ++c)
|
|
||||||
{
|
|
||||||
for (int t = 0; t < 2; ++t)
|
|
||||||
{
|
|
||||||
ReverbDelayLine& combLine = x78_C[c][t];
|
|
||||||
size_t tapDelay = CTapDelays[t] * rateRatio;
|
|
||||||
combLine.allocate(tapDelay);
|
|
||||||
combLine.setdelay(tapDelay);
|
|
||||||
xf4_combCoef[c][t] = std::pow(10.f, tapDelay * -3.f / timeSamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int t = 0; t < 2; ++t)
|
for (size_t f = 0; f < frameCount; f += 160) {
|
||||||
{
|
for (unsigned c = 0; c < chanMap.m_channelCount; ++c) {
|
||||||
ReverbDelayLine& allPassLine = x0_AP[c][t];
|
float* combCoefs = xf4_combCoef[c];
|
||||||
size_t tapDelay = APTapDelays[t] * rateRatio;
|
float& lpLastOut = x10c_lpLastout[c];
|
||||||
allPassLine.allocate(tapDelay);
|
float* preDelayLine = x124_preDelayLine[c].get();
|
||||||
allPassLine.setdelay(tapDelay);
|
float* preDelayPtr = x130_preDelayPtr[c];
|
||||||
}
|
float* lastPreDelaySamp = &preDelayLine[x120_preDelayTime - 1];
|
||||||
}
|
|
||||||
|
|
||||||
xf0_allPassCoef = x140_x1c8_coloration;
|
ReverbDelayLine* linesC = x78_C[c];
|
||||||
x118_level = x144_x1cc_mix;
|
ReverbDelayLine* linesAP = x0_AP[c];
|
||||||
x11c_damping = x14c_x1d4_damping;
|
|
||||||
|
|
||||||
if (x11c_damping < 0.05f)
|
int procSamples = std::min(size_t(160), frameCount - f);
|
||||||
x11c_damping = 0.05f;
|
for (int s = 0; s < procSamples; ++s) {
|
||||||
|
float sample = audio[s * chanMap.m_channelCount + c];
|
||||||
x11c_damping = 1.f - (x11c_damping * 0.8f + 0.05);
|
|
||||||
|
|
||||||
if (x150_x1d8_preDelay != 0.f)
|
|
||||||
{
|
|
||||||
x120_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
x124_preDelayLine[i].reset(new float[x120_preDelayTime]);
|
|
||||||
memset(x124_preDelayLine[i].get(), 0, x120_preDelayTime * sizeof(float));
|
|
||||||
x130_preDelayPtr[i] = x124_preDelayLine[i].get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x120_preDelayTime = 0;
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
x124_preDelayLine[i] = nullptr;
|
|
||||||
x130_preDelayPtr[i] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
|
||||||
{
|
|
||||||
if (m_dirty)
|
|
||||||
_update();
|
|
||||||
|
|
||||||
float dampWet = x118_level * 0.6f;
|
|
||||||
float dampDry = 0.6f - dampWet;
|
|
||||||
|
|
||||||
for (size_t f = 0; f < frameCount; f += 160)
|
|
||||||
{
|
|
||||||
for (unsigned c = 0; c < chanMap.m_channelCount; ++c)
|
|
||||||
{
|
|
||||||
float* combCoefs = xf4_combCoef[c];
|
|
||||||
float& lpLastOut = x10c_lpLastout[c];
|
|
||||||
float* preDelayLine = x124_preDelayLine[c].get();
|
|
||||||
float* preDelayPtr = x130_preDelayPtr[c];
|
|
||||||
float* lastPreDelaySamp = &preDelayLine[x120_preDelayTime - 1];
|
|
||||||
|
|
||||||
ReverbDelayLine* linesC = x78_C[c];
|
|
||||||
ReverbDelayLine* linesAP = x0_AP[c];
|
|
||||||
|
|
||||||
int procSamples = std::min(size_t(160), frameCount - f);
|
|
||||||
for (int s = 0; s < procSamples; ++s)
|
|
||||||
{
|
|
||||||
float sample = audio[s * chanMap.m_channelCount + c];
|
|
||||||
|
|
||||||
/* Pre-delay stage */
|
|
||||||
float sample2 = sample;
|
|
||||||
if (x120_preDelayTime != 0)
|
|
||||||
{
|
|
||||||
sample2 = *preDelayPtr;
|
|
||||||
*preDelayPtr = sample;
|
|
||||||
preDelayPtr += 1;
|
|
||||||
if (preDelayPtr == lastPreDelaySamp)
|
|
||||||
preDelayPtr = preDelayLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Comb filter stage */
|
|
||||||
linesC[0].xc_inputs[linesC[0].x0_inPoint] = combCoefs[0] * linesC[0].x10_lastInput + sample2;
|
|
||||||
linesC[0].x0_inPoint += 1;
|
|
||||||
|
|
||||||
linesC[1].xc_inputs[linesC[1].x0_inPoint] = combCoefs[1] * linesC[1].x10_lastInput + sample2;
|
|
||||||
linesC[1].x0_inPoint += 1;
|
|
||||||
|
|
||||||
linesC[0].x10_lastInput = linesC[0].xc_inputs[linesC[0].x4_outPoint];
|
|
||||||
linesC[0].x4_outPoint += 1;
|
|
||||||
|
|
||||||
linesC[1].x10_lastInput = linesC[1].xc_inputs[linesC[1].x4_outPoint];
|
|
||||||
linesC[1].x4_outPoint += 1;
|
|
||||||
|
|
||||||
if (linesC[0].x0_inPoint == linesC[0].x8_length)
|
|
||||||
linesC[0].x0_inPoint = 0;
|
|
||||||
|
|
||||||
if (linesC[1].x0_inPoint == linesC[1].x8_length)
|
|
||||||
linesC[1].x0_inPoint = 0;
|
|
||||||
|
|
||||||
if (linesC[0].x4_outPoint == linesC[0].x8_length)
|
|
||||||
linesC[0].x4_outPoint = 0;
|
|
||||||
|
|
||||||
if (linesC[1].x4_outPoint == linesC[1].x8_length)
|
|
||||||
linesC[1].x4_outPoint = 0;
|
|
||||||
|
|
||||||
/* All-pass filter stage */
|
|
||||||
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] =
|
|
||||||
xf0_allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput + linesC[1].x10_lastInput;
|
|
||||||
float lowPass =
|
|
||||||
-(xf0_allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] - linesAP[0].x10_lastInput);
|
|
||||||
linesAP[0].x0_inPoint += 1;
|
|
||||||
|
|
||||||
linesAP[0].x10_lastInput = linesAP[0].xc_inputs[linesAP[0].x4_outPoint];
|
|
||||||
linesAP[0].x4_outPoint += 1;
|
|
||||||
|
|
||||||
if (linesAP[0].x0_inPoint == linesAP[0].x8_length)
|
|
||||||
linesAP[0].x0_inPoint = 0;
|
|
||||||
|
|
||||||
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
|
|
||||||
linesAP[0].x4_outPoint = 0;
|
|
||||||
|
|
||||||
lpLastOut = x11c_damping * lpLastOut + lowPass * 0.3f;
|
|
||||||
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] = xf0_allPassCoef * linesAP[1].x10_lastInput + lpLastOut;
|
|
||||||
float allPass =
|
|
||||||
-(xf0_allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] - linesAP[1].x10_lastInput);
|
|
||||||
linesAP[1].x0_inPoint += 1;
|
|
||||||
|
|
||||||
linesAP[1].x10_lastInput = linesAP[1].xc_inputs[linesAP[1].x4_outPoint];
|
|
||||||
linesAP[1].x4_outPoint += 1;
|
|
||||||
|
|
||||||
if (linesAP[1].x0_inPoint == linesAP[1].x8_length)
|
|
||||||
linesAP[1].x0_inPoint = 0;
|
|
||||||
|
|
||||||
if (linesAP[1].x4_outPoint == linesAP[1].x8_length)
|
|
||||||
linesAP[1].x4_outPoint = 0;
|
|
||||||
|
|
||||||
/* Mix out */
|
|
||||||
audio[s * chanMap.m_channelCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
|
||||||
}
|
|
||||||
x130_preDelayPtr[c] = preDelayPtr;
|
|
||||||
}
|
|
||||||
audio += chanMap.m_channelCount * 160;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
EffectReverbHiImp<T>::EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay,
|
|
||||||
float crosstalk, double sampleRate)
|
|
||||||
: EffectReverbHi(coloration, mix, time, damping, preDelay, crosstalk)
|
|
||||||
{
|
|
||||||
_setup(sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectReverbHiImp<T>::_setup(double sampleRate)
|
|
||||||
{
|
|
||||||
m_sampleRate = sampleRate;
|
|
||||||
_update();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectReverbHiImp<T>::_update()
|
|
||||||
{
|
|
||||||
float timeSamples = x148_x1d0_time * m_sampleRate;
|
|
||||||
double rateRatio = m_sampleRate / NativeSampleRate;
|
|
||||||
for (int c = 0; c < 8; ++c)
|
|
||||||
{
|
|
||||||
for (int t = 0; t < 3; ++t)
|
|
||||||
{
|
|
||||||
ReverbDelayLine& combLine = xb4_C[c][t];
|
|
||||||
size_t tapDelay = CTapDelays[t] * rateRatio;
|
|
||||||
combLine.allocate(tapDelay);
|
|
||||||
combLine.setdelay(tapDelay);
|
|
||||||
x16c_combCoef[c][t] = std::pow(10.f, tapDelay * -3.f / timeSamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int t = 0; t < 2; ++t)
|
|
||||||
{
|
|
||||||
ReverbDelayLine& allPassLine = x0_AP[c][t];
|
|
||||||
size_t tapDelay = APTapDelays[t] * rateRatio;
|
|
||||||
allPassLine.allocate(tapDelay);
|
|
||||||
allPassLine.setdelay(tapDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReverbDelayLine& lpLine = x78_LP[c];
|
|
||||||
size_t tapDelay = LPTapDelays[c] * rateRatio;
|
|
||||||
lpLine.allocate(tapDelay);
|
|
||||||
lpLine.setdelay(tapDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
x168_allPassCoef = x140_x1c8_coloration;
|
|
||||||
x19c_level = x144_x1cc_mix;
|
|
||||||
x1a0_damping = x14c_x1d4_damping;
|
|
||||||
|
|
||||||
if (x1a0_damping < 0.05f)
|
|
||||||
x1a0_damping = 0.05f;
|
|
||||||
|
|
||||||
x1a0_damping = 1.f - (x1a0_damping * 0.8f + 0.05);
|
|
||||||
|
|
||||||
if (x150_x1d8_preDelay != 0.f)
|
|
||||||
{
|
|
||||||
x1a4_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
x1ac_preDelayLine[i].reset(new float[x1a4_preDelayTime]);
|
|
||||||
memset(x1ac_preDelayLine[i].get(), 0, x1a4_preDelayTime * sizeof(float));
|
|
||||||
x1b8_preDelayPtr[i] = x1ac_preDelayLine[i].get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x1a4_preDelayTime = 0;
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
x1ac_preDelayLine[i] = nullptr;
|
|
||||||
x1b8_preDelayPtr[i] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x1a8_internalCrosstalk = x1dc_crosstalk;
|
|
||||||
m_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sampleCount)
|
|
||||||
{
|
|
||||||
float dampWet = x19c_level * 0.6f;
|
|
||||||
float dampDry = 0.6f - dampWet;
|
|
||||||
|
|
||||||
float* combCoefs = x16c_combCoef[c];
|
|
||||||
float& lpLastOut = x190_lpLastout[c];
|
|
||||||
float* preDelayLine = x1ac_preDelayLine[c].get();
|
|
||||||
float* preDelayPtr = x1b8_preDelayPtr[c];
|
|
||||||
float* lastPreDelaySamp = &preDelayLine[x1a4_preDelayTime - 1];
|
|
||||||
|
|
||||||
ReverbDelayLine* linesC = xb4_C[c];
|
|
||||||
ReverbDelayLine* linesAP = x0_AP[c];
|
|
||||||
ReverbDelayLine& lineLP = x78_LP[c];
|
|
||||||
|
|
||||||
float allPassCoef = x168_allPassCoef;
|
|
||||||
float damping = x1a0_damping;
|
|
||||||
int32_t preDelayTime = x1a4_preDelayTime;
|
|
||||||
|
|
||||||
for (int s = 0; s < sampleCount; ++s)
|
|
||||||
{
|
|
||||||
float sample = audio[s * chanCount + c];
|
|
||||||
|
|
||||||
/* Pre-delay stage */
|
/* Pre-delay stage */
|
||||||
float sample2 = sample;
|
float sample2 = sample;
|
||||||
if (preDelayTime != 0)
|
if (x120_preDelayTime != 0) {
|
||||||
{
|
sample2 = *preDelayPtr;
|
||||||
sample2 = *preDelayPtr;
|
*preDelayPtr = sample;
|
||||||
*preDelayPtr = sample;
|
preDelayPtr += 1;
|
||||||
preDelayPtr += 1;
|
if (preDelayPtr == lastPreDelaySamp)
|
||||||
if (preDelayPtr == lastPreDelaySamp)
|
preDelayPtr = preDelayLine;
|
||||||
preDelayPtr = preDelayLine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Comb filter stage */
|
/* Comb filter stage */
|
||||||
|
@ -366,124 +165,279 @@ void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sam
|
||||||
linesC[1].xc_inputs[linesC[1].x0_inPoint] = combCoefs[1] * linesC[1].x10_lastInput + sample2;
|
linesC[1].xc_inputs[linesC[1].x0_inPoint] = combCoefs[1] * linesC[1].x10_lastInput + sample2;
|
||||||
linesC[1].x0_inPoint += 1;
|
linesC[1].x0_inPoint += 1;
|
||||||
|
|
||||||
linesC[2].xc_inputs[linesC[2].x0_inPoint] = combCoefs[2] * linesC[2].x10_lastInput + sample2;
|
|
||||||
linesC[2].x0_inPoint += 1;
|
|
||||||
|
|
||||||
linesC[0].x10_lastInput = linesC[0].xc_inputs[linesC[0].x4_outPoint];
|
linesC[0].x10_lastInput = linesC[0].xc_inputs[linesC[0].x4_outPoint];
|
||||||
linesC[0].x4_outPoint += 1;
|
linesC[0].x4_outPoint += 1;
|
||||||
|
|
||||||
linesC[1].x10_lastInput = linesC[1].xc_inputs[linesC[1].x4_outPoint];
|
linesC[1].x10_lastInput = linesC[1].xc_inputs[linesC[1].x4_outPoint];
|
||||||
linesC[1].x4_outPoint += 1;
|
linesC[1].x4_outPoint += 1;
|
||||||
|
|
||||||
linesC[2].x10_lastInput = linesC[2].xc_inputs[linesC[2].x4_outPoint];
|
|
||||||
linesC[2].x4_outPoint += 1;
|
|
||||||
|
|
||||||
if (linesC[0].x0_inPoint == linesC[0].x8_length)
|
if (linesC[0].x0_inPoint == linesC[0].x8_length)
|
||||||
linesC[0].x0_inPoint = 0;
|
linesC[0].x0_inPoint = 0;
|
||||||
|
|
||||||
if (linesC[1].x0_inPoint == linesC[1].x8_length)
|
if (linesC[1].x0_inPoint == linesC[1].x8_length)
|
||||||
linesC[1].x0_inPoint = 0;
|
linesC[1].x0_inPoint = 0;
|
||||||
|
|
||||||
if (linesC[2].x0_inPoint == linesC[2].x8_length)
|
|
||||||
linesC[2].x0_inPoint = 0;
|
|
||||||
|
|
||||||
if (linesC[0].x4_outPoint == linesC[0].x8_length)
|
if (linesC[0].x4_outPoint == linesC[0].x8_length)
|
||||||
linesC[0].x4_outPoint = 0;
|
linesC[0].x4_outPoint = 0;
|
||||||
|
|
||||||
if (linesC[1].x4_outPoint == linesC[1].x8_length)
|
if (linesC[1].x4_outPoint == linesC[1].x8_length)
|
||||||
linesC[1].x4_outPoint = 0;
|
linesC[1].x4_outPoint = 0;
|
||||||
|
|
||||||
if (linesC[2].x4_outPoint == linesC[2].x8_length)
|
|
||||||
linesC[2].x4_outPoint = 0;
|
|
||||||
|
|
||||||
/* All-pass filter stage */
|
/* All-pass filter stage */
|
||||||
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] = allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput +
|
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] =
|
||||||
linesC[1].x10_lastInput + linesC[2].x10_lastInput;
|
xf0_allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput + linesC[1].x10_lastInput;
|
||||||
|
float lowPass = -(xf0_allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] - linesAP[0].x10_lastInput);
|
||||||
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] =
|
|
||||||
allPassCoef * linesAP[1].x10_lastInput -
|
|
||||||
(allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] - linesAP[0].x10_lastInput);
|
|
||||||
|
|
||||||
float lowPass = -(allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] - linesAP[1].x10_lastInput);
|
|
||||||
linesAP[0].x0_inPoint += 1;
|
linesAP[0].x0_inPoint += 1;
|
||||||
linesAP[1].x0_inPoint += 1;
|
|
||||||
|
|
||||||
if (linesAP[0].x0_inPoint == linesAP[0].x8_length)
|
|
||||||
linesAP[0].x0_inPoint = 0;
|
|
||||||
|
|
||||||
if (linesAP[1].x0_inPoint == linesAP[1].x8_length)
|
|
||||||
linesAP[1].x0_inPoint = 0;
|
|
||||||
|
|
||||||
linesAP[0].x10_lastInput = linesAP[0].xc_inputs[linesAP[0].x4_outPoint];
|
linesAP[0].x10_lastInput = linesAP[0].xc_inputs[linesAP[0].x4_outPoint];
|
||||||
linesAP[0].x4_outPoint += 1;
|
linesAP[0].x4_outPoint += 1;
|
||||||
|
|
||||||
|
if (linesAP[0].x0_inPoint == linesAP[0].x8_length)
|
||||||
|
linesAP[0].x0_inPoint = 0;
|
||||||
|
|
||||||
|
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
|
||||||
|
linesAP[0].x4_outPoint = 0;
|
||||||
|
|
||||||
|
lpLastOut = x11c_damping * lpLastOut + lowPass * 0.3f;
|
||||||
|
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] = xf0_allPassCoef * linesAP[1].x10_lastInput + lpLastOut;
|
||||||
|
float allPass = -(xf0_allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] - linesAP[1].x10_lastInput);
|
||||||
|
linesAP[1].x0_inPoint += 1;
|
||||||
|
|
||||||
linesAP[1].x10_lastInput = linesAP[1].xc_inputs[linesAP[1].x4_outPoint];
|
linesAP[1].x10_lastInput = linesAP[1].xc_inputs[linesAP[1].x4_outPoint];
|
||||||
linesAP[1].x4_outPoint += 1;
|
linesAP[1].x4_outPoint += 1;
|
||||||
|
|
||||||
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
|
if (linesAP[1].x0_inPoint == linesAP[1].x8_length)
|
||||||
linesAP[0].x4_outPoint = 0;
|
linesAP[1].x0_inPoint = 0;
|
||||||
|
|
||||||
if (linesAP[1].x4_outPoint == linesAP[1].x8_length)
|
if (linesAP[1].x4_outPoint == linesAP[1].x8_length)
|
||||||
linesAP[1].x4_outPoint = 0;
|
linesAP[1].x4_outPoint = 0;
|
||||||
|
|
||||||
lpLastOut = damping * lpLastOut + lowPass * 0.3f;
|
|
||||||
lineLP.xc_inputs[lineLP.x0_inPoint] = allPassCoef * lineLP.x10_lastInput + lpLastOut;
|
|
||||||
float allPass = -(allPassCoef * lineLP.xc_inputs[lineLP.x0_inPoint] - lineLP.x10_lastInput);
|
|
||||||
lineLP.x0_inPoint += 1;
|
|
||||||
|
|
||||||
lineLP.x10_lastInput = lineLP.xc_inputs[lineLP.x4_outPoint];
|
|
||||||
lineLP.x4_outPoint += 1;
|
|
||||||
|
|
||||||
if (lineLP.x0_inPoint == lineLP.x8_length)
|
|
||||||
lineLP.x0_inPoint = 0;
|
|
||||||
|
|
||||||
if (lineLP.x4_outPoint == lineLP.x8_length)
|
|
||||||
lineLP.x4_outPoint = 0;
|
|
||||||
|
|
||||||
/* Mix out */
|
/* Mix out */
|
||||||
audio[s * chanCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
audio[s * chanMap.m_channelCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
||||||
|
}
|
||||||
|
x130_preDelayPtr[c] = preDelayPtr;
|
||||||
}
|
}
|
||||||
|
audio += chanMap.m_channelCount * 160;
|
||||||
x1b8_preDelayPtr[c] = preDelayPtr;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectReverbHiImp<T>::_doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount)
|
EffectReverbHiImp<T>::EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay,
|
||||||
{
|
float crosstalk, double sampleRate)
|
||||||
for (int i = 0; i < sampleCount; ++i)
|
: EffectReverbHi(coloration, mix, time, damping, preDelay, crosstalk) {
|
||||||
{
|
_setup(sampleRate);
|
||||||
T* base = &audio[chanCount * i];
|
|
||||||
float allWet = 0;
|
|
||||||
for (int c = 0; c < chanCount; ++c)
|
|
||||||
{
|
|
||||||
allWet += base[c] * wet;
|
|
||||||
base[c] *= dry;
|
|
||||||
}
|
|
||||||
for (int c = 0; c < chanCount; ++c)
|
|
||||||
base[c] = ClampFull<T>(base[c] + allWet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void EffectReverbHiImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
void EffectReverbHiImp<T>::_setup(double sampleRate) {
|
||||||
{
|
m_sampleRate = sampleRate;
|
||||||
if (m_dirty)
|
_update();
|
||||||
_update();
|
}
|
||||||
|
|
||||||
for (size_t f = 0; f < frameCount; f += 160)
|
template <typename T>
|
||||||
{
|
void EffectReverbHiImp<T>::_update() {
|
||||||
size_t blockSamples = std::min(size_t(160), frameCount - f);
|
float timeSamples = x148_x1d0_time * m_sampleRate;
|
||||||
for (unsigned i = 0; i < chanMap.m_channelCount; ++i)
|
double rateRatio = m_sampleRate / NativeSampleRate;
|
||||||
{
|
for (int c = 0; c < 8; ++c) {
|
||||||
if (i == 0 && x1a8_internalCrosstalk != 0.f)
|
for (int t = 0; t < 3; ++t) {
|
||||||
{
|
ReverbDelayLine& combLine = xb4_C[c][t];
|
||||||
float crossWet = x1a8_internalCrosstalk * 0.5;
|
size_t tapDelay = CTapDelays[t] * rateRatio;
|
||||||
_doCrosstalk(audio, crossWet, 1.f - crossWet, chanMap.m_channelCount, blockSamples);
|
combLine.allocate(tapDelay);
|
||||||
}
|
combLine.setdelay(tapDelay);
|
||||||
_handleReverb(audio, i, chanMap.m_channelCount, blockSamples);
|
x16c_combCoef[c][t] = std::pow(10.f, tapDelay * -3.f / timeSamples);
|
||||||
}
|
|
||||||
audio += chanMap.m_channelCount * 160;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int t = 0; t < 2; ++t) {
|
||||||
|
ReverbDelayLine& allPassLine = x0_AP[c][t];
|
||||||
|
size_t tapDelay = APTapDelays[t] * rateRatio;
|
||||||
|
allPassLine.allocate(tapDelay);
|
||||||
|
allPassLine.setdelay(tapDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReverbDelayLine& lpLine = x78_LP[c];
|
||||||
|
size_t tapDelay = LPTapDelays[c] * rateRatio;
|
||||||
|
lpLine.allocate(tapDelay);
|
||||||
|
lpLine.setdelay(tapDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
x168_allPassCoef = x140_x1c8_coloration;
|
||||||
|
x19c_level = x144_x1cc_mix;
|
||||||
|
x1a0_damping = x14c_x1d4_damping;
|
||||||
|
|
||||||
|
if (x1a0_damping < 0.05f)
|
||||||
|
x1a0_damping = 0.05f;
|
||||||
|
|
||||||
|
x1a0_damping = 1.f - (x1a0_damping * 0.8f + 0.05);
|
||||||
|
|
||||||
|
if (x150_x1d8_preDelay != 0.f) {
|
||||||
|
x1a4_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
x1ac_preDelayLine[i].reset(new float[x1a4_preDelayTime]);
|
||||||
|
memset(x1ac_preDelayLine[i].get(), 0, x1a4_preDelayTime * sizeof(float));
|
||||||
|
x1b8_preDelayPtr[i] = x1ac_preDelayLine[i].get();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x1a4_preDelayTime = 0;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
x1ac_preDelayLine[i] = nullptr;
|
||||||
|
x1b8_preDelayPtr[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x1a8_internalCrosstalk = x1dc_crosstalk;
|
||||||
|
m_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sampleCount) {
|
||||||
|
float dampWet = x19c_level * 0.6f;
|
||||||
|
float dampDry = 0.6f - dampWet;
|
||||||
|
|
||||||
|
float* combCoefs = x16c_combCoef[c];
|
||||||
|
float& lpLastOut = x190_lpLastout[c];
|
||||||
|
float* preDelayLine = x1ac_preDelayLine[c].get();
|
||||||
|
float* preDelayPtr = x1b8_preDelayPtr[c];
|
||||||
|
float* lastPreDelaySamp = &preDelayLine[x1a4_preDelayTime - 1];
|
||||||
|
|
||||||
|
ReverbDelayLine* linesC = xb4_C[c];
|
||||||
|
ReverbDelayLine* linesAP = x0_AP[c];
|
||||||
|
ReverbDelayLine& lineLP = x78_LP[c];
|
||||||
|
|
||||||
|
float allPassCoef = x168_allPassCoef;
|
||||||
|
float damping = x1a0_damping;
|
||||||
|
int32_t preDelayTime = x1a4_preDelayTime;
|
||||||
|
|
||||||
|
for (int s = 0; s < sampleCount; ++s) {
|
||||||
|
float sample = audio[s * chanCount + c];
|
||||||
|
|
||||||
|
/* Pre-delay stage */
|
||||||
|
float sample2 = sample;
|
||||||
|
if (preDelayTime != 0) {
|
||||||
|
sample2 = *preDelayPtr;
|
||||||
|
*preDelayPtr = sample;
|
||||||
|
preDelayPtr += 1;
|
||||||
|
if (preDelayPtr == lastPreDelaySamp)
|
||||||
|
preDelayPtr = preDelayLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Comb filter stage */
|
||||||
|
linesC[0].xc_inputs[linesC[0].x0_inPoint] = combCoefs[0] * linesC[0].x10_lastInput + sample2;
|
||||||
|
linesC[0].x0_inPoint += 1;
|
||||||
|
|
||||||
|
linesC[1].xc_inputs[linesC[1].x0_inPoint] = combCoefs[1] * linesC[1].x10_lastInput + sample2;
|
||||||
|
linesC[1].x0_inPoint += 1;
|
||||||
|
|
||||||
|
linesC[2].xc_inputs[linesC[2].x0_inPoint] = combCoefs[2] * linesC[2].x10_lastInput + sample2;
|
||||||
|
linesC[2].x0_inPoint += 1;
|
||||||
|
|
||||||
|
linesC[0].x10_lastInput = linesC[0].xc_inputs[linesC[0].x4_outPoint];
|
||||||
|
linesC[0].x4_outPoint += 1;
|
||||||
|
|
||||||
|
linesC[1].x10_lastInput = linesC[1].xc_inputs[linesC[1].x4_outPoint];
|
||||||
|
linesC[1].x4_outPoint += 1;
|
||||||
|
|
||||||
|
linesC[2].x10_lastInput = linesC[2].xc_inputs[linesC[2].x4_outPoint];
|
||||||
|
linesC[2].x4_outPoint += 1;
|
||||||
|
|
||||||
|
if (linesC[0].x0_inPoint == linesC[0].x8_length)
|
||||||
|
linesC[0].x0_inPoint = 0;
|
||||||
|
|
||||||
|
if (linesC[1].x0_inPoint == linesC[1].x8_length)
|
||||||
|
linesC[1].x0_inPoint = 0;
|
||||||
|
|
||||||
|
if (linesC[2].x0_inPoint == linesC[2].x8_length)
|
||||||
|
linesC[2].x0_inPoint = 0;
|
||||||
|
|
||||||
|
if (linesC[0].x4_outPoint == linesC[0].x8_length)
|
||||||
|
linesC[0].x4_outPoint = 0;
|
||||||
|
|
||||||
|
if (linesC[1].x4_outPoint == linesC[1].x8_length)
|
||||||
|
linesC[1].x4_outPoint = 0;
|
||||||
|
|
||||||
|
if (linesC[2].x4_outPoint == linesC[2].x8_length)
|
||||||
|
linesC[2].x4_outPoint = 0;
|
||||||
|
|
||||||
|
/* All-pass filter stage */
|
||||||
|
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] = allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput +
|
||||||
|
linesC[1].x10_lastInput + linesC[2].x10_lastInput;
|
||||||
|
|
||||||
|
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] =
|
||||||
|
allPassCoef * linesAP[1].x10_lastInput -
|
||||||
|
(allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] - linesAP[0].x10_lastInput);
|
||||||
|
|
||||||
|
float lowPass = -(allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] - linesAP[1].x10_lastInput);
|
||||||
|
linesAP[0].x0_inPoint += 1;
|
||||||
|
linesAP[1].x0_inPoint += 1;
|
||||||
|
|
||||||
|
if (linesAP[0].x0_inPoint == linesAP[0].x8_length)
|
||||||
|
linesAP[0].x0_inPoint = 0;
|
||||||
|
|
||||||
|
if (linesAP[1].x0_inPoint == linesAP[1].x8_length)
|
||||||
|
linesAP[1].x0_inPoint = 0;
|
||||||
|
|
||||||
|
linesAP[0].x10_lastInput = linesAP[0].xc_inputs[linesAP[0].x4_outPoint];
|
||||||
|
linesAP[0].x4_outPoint += 1;
|
||||||
|
|
||||||
|
linesAP[1].x10_lastInput = linesAP[1].xc_inputs[linesAP[1].x4_outPoint];
|
||||||
|
linesAP[1].x4_outPoint += 1;
|
||||||
|
|
||||||
|
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
|
||||||
|
linesAP[0].x4_outPoint = 0;
|
||||||
|
|
||||||
|
if (linesAP[1].x4_outPoint == linesAP[1].x8_length)
|
||||||
|
linesAP[1].x4_outPoint = 0;
|
||||||
|
|
||||||
|
lpLastOut = damping * lpLastOut + lowPass * 0.3f;
|
||||||
|
lineLP.xc_inputs[lineLP.x0_inPoint] = allPassCoef * lineLP.x10_lastInput + lpLastOut;
|
||||||
|
float allPass = -(allPassCoef * lineLP.xc_inputs[lineLP.x0_inPoint] - lineLP.x10_lastInput);
|
||||||
|
lineLP.x0_inPoint += 1;
|
||||||
|
|
||||||
|
lineLP.x10_lastInput = lineLP.xc_inputs[lineLP.x4_outPoint];
|
||||||
|
lineLP.x4_outPoint += 1;
|
||||||
|
|
||||||
|
if (lineLP.x0_inPoint == lineLP.x8_length)
|
||||||
|
lineLP.x0_inPoint = 0;
|
||||||
|
|
||||||
|
if (lineLP.x4_outPoint == lineLP.x8_length)
|
||||||
|
lineLP.x4_outPoint = 0;
|
||||||
|
|
||||||
|
/* Mix out */
|
||||||
|
audio[s * chanCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
x1b8_preDelayPtr[c] = preDelayPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectReverbHiImp<T>::_doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount) {
|
||||||
|
for (int i = 0; i < sampleCount; ++i) {
|
||||||
|
T* base = &audio[chanCount * i];
|
||||||
|
float allWet = 0;
|
||||||
|
for (int c = 0; c < chanCount; ++c) {
|
||||||
|
allWet += base[c] * wet;
|
||||||
|
base[c] *= dry;
|
||||||
|
}
|
||||||
|
for (int c = 0; c < chanCount; ++c)
|
||||||
|
base[c] = ClampFull<T>(base[c] + allWet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void EffectReverbHiImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap) {
|
||||||
|
if (m_dirty)
|
||||||
|
_update();
|
||||||
|
|
||||||
|
for (size_t f = 0; f < frameCount; f += 160) {
|
||||||
|
size_t blockSamples = std::min(size_t(160), frameCount - f);
|
||||||
|
for (unsigned i = 0; i < chanMap.m_channelCount; ++i) {
|
||||||
|
if (i == 0 && x1a8_internalCrosstalk != 0.f) {
|
||||||
|
float crossWet = x1a8_internalCrosstalk * 0.5;
|
||||||
|
_doCrosstalk(audio, crossWet, 1.f - crossWet, chanMap.m_channelCount, blockSamples);
|
||||||
|
}
|
||||||
|
_handleReverb(audio, i, chanMap.m_channelCount, blockSamples);
|
||||||
|
}
|
||||||
|
audio += chanMap.m_channelCount * 160;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template class EffectReverbStdImp<int16_t>;
|
template class EffectReverbStdImp<int16_t>;
|
||||||
|
@ -493,4 +447,4 @@ template class EffectReverbStdImp<float>;
|
||||||
template class EffectReverbHiImp<int16_t>;
|
template class EffectReverbHiImp<int16_t>;
|
||||||
template class EffectReverbHiImp<int32_t>;
|
template class EffectReverbHiImp<int32_t>;
|
||||||
template class EffectReverbHiImp<float>;
|
template class EffectReverbHiImp<float>;
|
||||||
}
|
} // namespace amuse
|
||||||
|
|
213
lib/Emitter.cpp
213
lib/Emitter.cpp
|
@ -3,133 +3,116 @@
|
||||||
#include "amuse/Voice.hpp"
|
#include "amuse/Voice.hpp"
|
||||||
#include "amuse/Engine.hpp"
|
#include "amuse/Engine.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
static void Delta(Vector3f& out, const Vector3f& a, const Vector3f& b)
|
static void Delta(Vector3f& out, const Vector3f& a, const Vector3f& b) {
|
||||||
{
|
out[0] = a[0] - b[0];
|
||||||
out[0] = a[0] - b[0];
|
out[1] = a[1] - b[1];
|
||||||
out[1] = a[1] - b[1];
|
out[2] = a[2] - b[2];
|
||||||
out[2] = a[2] - b[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Emitter::~Emitter() {}
|
Emitter::~Emitter() {}
|
||||||
|
|
||||||
Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox,
|
Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox, float maxDist, float minVol,
|
||||||
float maxDist, float minVol, float falloff, bool doppler)
|
float falloff, bool doppler)
|
||||||
: Entity(engine, group, vox->getGroupId(), vox->getObjectId()), m_vox(vox), m_maxDist(maxDist),
|
: Entity(engine, group, vox->getGroupId(), vox->getObjectId())
|
||||||
m_minVol(clamp(0.f, minVol, 1.f)), m_falloff(clamp(-1.f, falloff, 1.f)), m_doppler(doppler)
|
, m_vox(vox)
|
||||||
{
|
, m_maxDist(maxDist)
|
||||||
|
, m_minVol(clamp(0.f, minVol, 1.f))
|
||||||
|
, m_falloff(clamp(-1.f, falloff, 1.f))
|
||||||
|
, m_doppler(doppler) {}
|
||||||
|
|
||||||
|
void Emitter::_destroy() {
|
||||||
|
Entity::_destroy();
|
||||||
|
m_vox->kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emitter::_destroy()
|
float Emitter::_attenuationCurve(float dist) const {
|
||||||
{
|
if (dist > m_maxDist)
|
||||||
Entity::_destroy();
|
return 0.f;
|
||||||
m_vox->kill();
|
float t = dist / m_maxDist;
|
||||||
|
if (m_falloff >= 0.f) {
|
||||||
|
return 1.f - (m_falloff * t * t + (1.f - m_falloff) * t);
|
||||||
|
} else {
|
||||||
|
float omt = 1.f - t;
|
||||||
|
return 1.f - ((1.f + m_falloff) * t - (1.f - omt * omt) * m_falloff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float Emitter::_attenuationCurve(float dist) const
|
void Emitter::_update() {
|
||||||
{
|
if (!m_dirty) {
|
||||||
if (dist > m_maxDist)
|
/* Ensure that all listeners are also not dirty */
|
||||||
return 0.f;
|
bool dirty = false;
|
||||||
float t = dist / m_maxDist;
|
for (auto& listener : m_engine.m_activeListeners) {
|
||||||
if (m_falloff >= 0.f)
|
if (listener->m_dirty) {
|
||||||
{
|
dirty = true;
|
||||||
return 1.f - (m_falloff * t * t + (1.f - m_falloff) * t);
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
if (!dirty)
|
||||||
{
|
return;
|
||||||
float omt = 1.f - t;
|
}
|
||||||
return 1.f - ((1.f + m_falloff) * t - (1.f - omt * omt) * m_falloff);
|
|
||||||
|
float coefs[8] = {};
|
||||||
|
double avgDopplerRatio = 0.0;
|
||||||
|
|
||||||
|
for (auto& listener : m_engine.m_activeListeners) {
|
||||||
|
Vector3f listenerToEmitter;
|
||||||
|
Delta(listenerToEmitter, m_pos, listener->m_pos);
|
||||||
|
float dist = Length(listenerToEmitter);
|
||||||
|
float panDist = Dot(listenerToEmitter, listener->m_right);
|
||||||
|
float frontPan = clamp(-1.f, panDist / listener->m_frontDiff, 1.f);
|
||||||
|
float backPan = clamp(-1.f, panDist / listener->m_backDiff, 1.f);
|
||||||
|
float spanDist = -Dot(listenerToEmitter, listener->m_heading);
|
||||||
|
float span = clamp(-1.f, spanDist > 0.f ? spanDist / listener->m_backDiff : spanDist / listener->m_frontDiff, 1.f);
|
||||||
|
|
||||||
|
/* Calculate attenuation */
|
||||||
|
float att = _attenuationCurve(dist);
|
||||||
|
att = (m_maxVol - m_minVol) * att + m_minVol;
|
||||||
|
att = m_attCache.getVolume(att, false);
|
||||||
|
if (att > FLT_EPSILON) {
|
||||||
|
/* Apply pan law */
|
||||||
|
float thisCoefs[8] = {};
|
||||||
|
m_vox->_panLaw(thisCoefs, frontPan, backPan, span);
|
||||||
|
|
||||||
|
/* Take maximum coefficient across listeners */
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
coefs[i] = std::max(coefs[i], thisCoefs[i] * att * listener->m_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate doppler */
|
||||||
|
if (m_doppler) {
|
||||||
|
/* Positive values indicate emitter and listener closing in */
|
||||||
|
Vector3f dirDelta;
|
||||||
|
Delta(dirDelta, m_dir, listener->m_dir);
|
||||||
|
Vector3f posDelta;
|
||||||
|
Delta(posDelta, listener->m_pos, m_pos);
|
||||||
|
Normalize(posDelta);
|
||||||
|
float deltaSpeed = Dot(dirDelta, posDelta);
|
||||||
|
if (listener->m_soundSpeed != 0.f)
|
||||||
|
avgDopplerRatio += 1.0 + deltaSpeed / listener->m_soundSpeed;
|
||||||
|
else
|
||||||
|
avgDopplerRatio += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_engine.m_activeListeners.size() != 0) {
|
||||||
|
m_vox->setChannelCoefs(coefs);
|
||||||
|
if (m_doppler) {
|
||||||
|
m_vox->m_dopplerRatio = avgDopplerRatio / float(m_engine.m_activeListeners.size());
|
||||||
|
m_vox->m_pitchDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emitter::_update()
|
void Emitter::setVectors(const float* pos, const float* dir) {
|
||||||
{
|
for (int i = 0; i < 3; ++i) {
|
||||||
if (!m_dirty)
|
m_pos[i] = pos[i];
|
||||||
{
|
m_dir[i] = dir[i];
|
||||||
/* Ensure that all listeners are also not dirty */
|
}
|
||||||
bool dirty = false;
|
m_dirty = true;
|
||||||
for (auto& listener : m_engine.m_activeListeners)
|
|
||||||
{
|
|
||||||
if (listener->m_dirty)
|
|
||||||
{
|
|
||||||
dirty = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!dirty)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float coefs[8] = {};
|
|
||||||
double avgDopplerRatio = 0.0;
|
|
||||||
|
|
||||||
for (auto& listener : m_engine.m_activeListeners)
|
|
||||||
{
|
|
||||||
Vector3f listenerToEmitter;
|
|
||||||
Delta(listenerToEmitter, m_pos, listener->m_pos);
|
|
||||||
float dist = Length(listenerToEmitter);
|
|
||||||
float panDist = Dot(listenerToEmitter, listener->m_right);
|
|
||||||
float frontPan = clamp(-1.f, panDist / listener->m_frontDiff, 1.f);
|
|
||||||
float backPan = clamp(-1.f, panDist / listener->m_backDiff, 1.f);
|
|
||||||
float spanDist = -Dot(listenerToEmitter, listener->m_heading);
|
|
||||||
float span = clamp(-1.f, spanDist > 0.f ? spanDist / listener->m_backDiff :
|
|
||||||
spanDist / listener->m_frontDiff, 1.f);
|
|
||||||
|
|
||||||
/* Calculate attenuation */
|
|
||||||
float att = _attenuationCurve(dist);
|
|
||||||
att = (m_maxVol - m_minVol) * att + m_minVol;
|
|
||||||
att = m_attCache.getVolume(att, false);
|
|
||||||
if (att > FLT_EPSILON)
|
|
||||||
{
|
|
||||||
/* Apply pan law */
|
|
||||||
float thisCoefs[8] = {};
|
|
||||||
m_vox->_panLaw(thisCoefs, frontPan, backPan, span);
|
|
||||||
|
|
||||||
/* Take maximum coefficient across listeners */
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
coefs[i] = std::max(coefs[i], thisCoefs[i] * att * listener->m_volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate doppler */
|
|
||||||
if (m_doppler)
|
|
||||||
{
|
|
||||||
/* Positive values indicate emitter and listener closing in */
|
|
||||||
Vector3f dirDelta;
|
|
||||||
Delta(dirDelta, m_dir, listener->m_dir);
|
|
||||||
Vector3f posDelta;
|
|
||||||
Delta(posDelta, listener->m_pos, m_pos);
|
|
||||||
Normalize(posDelta);
|
|
||||||
float deltaSpeed = Dot(dirDelta, posDelta);
|
|
||||||
if (listener->m_soundSpeed != 0.f)
|
|
||||||
avgDopplerRatio += 1.0 + deltaSpeed / listener->m_soundSpeed;
|
|
||||||
else
|
|
||||||
avgDopplerRatio += 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_engine.m_activeListeners.size() != 0)
|
|
||||||
{
|
|
||||||
m_vox->setChannelCoefs(coefs);
|
|
||||||
if (m_doppler)
|
|
||||||
{
|
|
||||||
m_vox->m_dopplerRatio = avgDopplerRatio / float(m_engine.m_activeListeners.size());
|
|
||||||
m_vox->m_pitchDirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emitter::setVectors(const float* pos, const float* dir)
|
} // namespace amuse
|
||||||
{
|
|
||||||
for (int i=0 ; i<3 ; ++i)
|
|
||||||
{
|
|
||||||
m_pos[i] = pos[i];
|
|
||||||
m_dir[i] = dir[i];
|
|
||||||
}
|
|
||||||
m_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
787
lib/Engine.cpp
787
lib/Engine.cpp
|
@ -8,559 +8,484 @@
|
||||||
#include "amuse/AudioGroup.hpp"
|
#include "amuse/AudioGroup.hpp"
|
||||||
#include "amuse/Common.hpp"
|
#include "amuse/Common.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse {
|
||||||
{
|
|
||||||
|
|
||||||
static const float FullLevels[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f};
|
static const float FullLevels[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f};
|
||||||
|
|
||||||
Engine::~Engine()
|
Engine::~Engine() {
|
||||||
{
|
m_backend.setCallbackInterface(nullptr);
|
||||||
m_backend.setCallbackInterface(nullptr);
|
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
||||||
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
if (!seq->m_destroyed)
|
||||||
if (!seq->m_destroyed)
|
seq->_destroy();
|
||||||
seq->_destroy();
|
for (ObjToken<Emitter>& emitter : m_activeEmitters)
|
||||||
for (ObjToken<Emitter>& emitter : m_activeEmitters)
|
emitter->_destroy();
|
||||||
emitter->_destroy();
|
for (ObjToken<Voice>& vox : m_activeVoices)
|
||||||
for (ObjToken<Voice>& vox : m_activeVoices)
|
vox->_destroy();
|
||||||
vox->_destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine::Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode)
|
Engine::Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode)
|
||||||
: m_backend(backend), m_ampMode(ampMode), m_defaultStudio(_allocateStudio(true))
|
: m_backend(backend), m_ampMode(ampMode), m_defaultStudio(_allocateStudio(true)) {
|
||||||
{
|
m_defaultStudio->getAuxA().makeReverbStd(0.5f, 0.8f, 3.0f, 0.5f, 0.1f);
|
||||||
m_defaultStudio->getAuxA().makeReverbStd(0.5f, 0.8f, 3.0f, 0.5f, 0.1f);
|
m_defaultStudio->getAuxB().makeChorus(15, 0, 500);
|
||||||
m_defaultStudio->getAuxB().makeChorus(15, 0, 500);
|
m_defaultStudioReady = true;
|
||||||
m_defaultStudioReady = true;
|
m_backend.setCallbackInterface(this);
|
||||||
m_backend.setCallbackInterface(this);
|
m_midiReader = backend.allocateMIDIReader(*this);
|
||||||
m_midiReader = backend.allocateMIDIReader(*this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<AudioGroup*, const SongGroupIndex*> Engine::_findSongGroup(GroupId groupId) const
|
std::pair<AudioGroup*, const SongGroupIndex*> Engine::_findSongGroup(GroupId groupId) const {
|
||||||
{
|
for (const auto& pair : m_audioGroups) {
|
||||||
for (const auto& pair : m_audioGroups)
|
const SongGroupIndex* ret = pair.second->getProj().getSongGroupIndex(groupId);
|
||||||
{
|
if (ret)
|
||||||
const SongGroupIndex* ret = pair.second->getProj().getSongGroupIndex(groupId);
|
return {pair.second.get(), ret};
|
||||||
if (ret)
|
}
|
||||||
return {pair.second.get(), ret};
|
return {};
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(GroupId groupId) const
|
std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(GroupId groupId) const {
|
||||||
{
|
for (const auto& pair : m_audioGroups) {
|
||||||
for (const auto& pair : m_audioGroups)
|
const SFXGroupIndex* ret = pair.second->getProj().getSFXGroupIndex(groupId);
|
||||||
{
|
if (ret)
|
||||||
const SFXGroupIndex* ret = pair.second->getProj().getSFXGroupIndex(groupId);
|
return {pair.second.get(), ret};
|
||||||
if (ret)
|
}
|
||||||
return {pair.second.get(), ret};
|
return {};
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, GroupId groupId,
|
std::list<ObjToken<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
|
||||||
double sampleRate, bool dynamicPitch, bool emitter,
|
bool dynamicPitch, bool emitter, ObjToken<Studio> studio) {
|
||||||
ObjToken<Studio> studio)
|
auto it =
|
||||||
{
|
m_activeVoices.emplace(m_activeVoices.end(), MakeObj<Voice>(*this, group, groupId, m_nextVid++, emitter, studio));
|
||||||
auto it =
|
m_activeVoices.back()->m_backendVoice = m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
||||||
m_activeVoices.emplace(m_activeVoices.end(), MakeObj<Voice>(*this, group, groupId, m_nextVid++, emitter, studio));
|
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getMaster().m_backendSubmix.get(), FullLevels, false);
|
||||||
m_activeVoices.back()->m_backendVoice = m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxA().m_backendSubmix.get(), FullLevels, false);
|
||||||
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getMaster().m_backendSubmix.get(), FullLevels, false);
|
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxB().m_backendSubmix.get(), FullLevels, false);
|
||||||
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxA().m_backendSubmix.get(), FullLevels, false);
|
return it;
|
||||||
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxB().m_backendSubmix.get(), FullLevels, false);
|
|
||||||
return it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<ObjToken<Sequencer>>::iterator Engine::_allocateSequencer(const AudioGroup& group, GroupId groupId,
|
std::list<ObjToken<Sequencer>>::iterator Engine::_allocateSequencer(const AudioGroup& group, GroupId groupId,
|
||||||
SongId setupId, ObjToken<Studio> studio)
|
SongId setupId, ObjToken<Studio> studio) {
|
||||||
{
|
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
|
||||||
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
|
if (songGroup) {
|
||||||
if (songGroup)
|
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio);
|
||||||
{
|
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
|
||||||
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio);
|
return it;
|
||||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
|
}
|
||||||
return it;
|
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
|
||||||
|
if (sfxGroup) {
|
||||||
|
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, sfxGroup, studio);
|
||||||
|
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjToken<Studio> Engine::_allocateStudio(bool mainOut) {
|
||||||
|
ObjToken<Studio> ret = MakeObj<Studio>(*this, mainOut);
|
||||||
|
ret->m_master.m_backendSubmix = m_backend.allocateSubmix(ret->m_master, mainOut, 0);
|
||||||
|
ret->m_auxA.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxA, mainOut, 1);
|
||||||
|
ret->m_auxB.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxB, mainOut, 2);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ObjToken<Voice>>::iterator Engine::_destroyVoice(std::list<ObjToken<Voice>>::iterator it) {
|
||||||
|
assert(this == &(*it)->getEngine());
|
||||||
|
if ((*it)->m_destroyed)
|
||||||
|
return m_activeVoices.begin();
|
||||||
|
(*it)->_destroy();
|
||||||
|
return m_activeVoices.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ObjToken<Sequencer>>::iterator Engine::_destroySequencer(std::list<ObjToken<Sequencer>>::iterator it) {
|
||||||
|
assert(this == &(*it)->getEngine());
|
||||||
|
if ((*it)->m_destroyed)
|
||||||
|
return m_activeSequencers.begin();
|
||||||
|
(*it)->_destroy();
|
||||||
|
return m_activeSequencers.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::_bringOutYourDead() {
|
||||||
|
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();) {
|
||||||
|
Emitter* emitter = it->get();
|
||||||
|
if (emitter->getVoice()->_isRecursivelyDead()) {
|
||||||
|
emitter->_destroy();
|
||||||
|
it = m_activeEmitters.erase(it);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
|
++it;
|
||||||
if (sfxGroup)
|
}
|
||||||
{
|
|
||||||
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, sfxGroup, studio);
|
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();) {
|
||||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
|
Voice* vox = it->get();
|
||||||
return it;
|
vox->_bringOutYourDead();
|
||||||
|
if (vox->_isRecursivelyDead()) {
|
||||||
|
it = _destroyVoice(it);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
return {};
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjToken<Studio> Engine::_allocateStudio(bool mainOut)
|
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();) {
|
||||||
{
|
Sequencer* seq = it->get();
|
||||||
ObjToken<Studio> ret = MakeObj<Studio>(*this, mainOut);
|
seq->_bringOutYourDead();
|
||||||
ret->m_master.m_backendSubmix = m_backend.allocateSubmix(ret->m_master, mainOut, 0);
|
if (seq->m_state == SequencerState::Dead) {
|
||||||
ret->m_auxA.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxA, mainOut, 1);
|
it = _destroySequencer(it);
|
||||||
ret->m_auxB.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxB, mainOut, 2);
|
continue;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator Engine::_destroyVoice(std::list<ObjToken<Voice>>::iterator it)
|
|
||||||
{
|
|
||||||
assert(this == &(*it)->getEngine());
|
|
||||||
if ((*it)->m_destroyed)
|
|
||||||
return m_activeVoices.begin();
|
|
||||||
(*it)->_destroy();
|
|
||||||
return m_activeVoices.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<ObjToken<Sequencer>>::iterator
|
|
||||||
Engine::_destroySequencer(std::list<ObjToken<Sequencer>>::iterator it)
|
|
||||||
{
|
|
||||||
assert(this == &(*it)->getEngine());
|
|
||||||
if ((*it)->m_destroyed)
|
|
||||||
return m_activeSequencers.begin();
|
|
||||||
(*it)->_destroy();
|
|
||||||
return m_activeSequencers.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::_bringOutYourDead()
|
|
||||||
{
|
|
||||||
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();)
|
|
||||||
{
|
|
||||||
Emitter* emitter = it->get();
|
|
||||||
if (emitter->getVoice()->_isRecursivelyDead())
|
|
||||||
{
|
|
||||||
emitter->_destroy();
|
|
||||||
it = m_activeEmitters.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
|
|
||||||
{
|
|
||||||
Voice* vox = it->get();
|
|
||||||
vox->_bringOutYourDead();
|
|
||||||
if (vox->_isRecursivelyDead())
|
|
||||||
{
|
|
||||||
it = _destroyVoice(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();)
|
|
||||||
{
|
|
||||||
Sequencer* seq = it->get();
|
|
||||||
seq->_bringOutYourDead();
|
|
||||||
if (seq->m_state == SequencerState::Dead)
|
|
||||||
{
|
|
||||||
it = _destroySequencer(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
|
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt) {
|
||||||
{
|
m_channelSet = engine.getAvailableSet();
|
||||||
m_channelSet = engine.getAvailableSet();
|
if (m_midiReader)
|
||||||
if (m_midiReader)
|
m_midiReader->pumpReader(dt);
|
||||||
m_midiReader->pumpReader(dt);
|
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
||||||
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
seq->advance(dt);
|
||||||
seq->advance(dt);
|
for (ObjToken<Emitter>& emitter : m_activeEmitters)
|
||||||
for (ObjToken<Emitter>& emitter : m_activeEmitters)
|
emitter->_update();
|
||||||
emitter->_update();
|
for (ObjToken<Listener>& listener : m_activeListeners)
|
||||||
for (ObjToken<Listener>& listener : m_activeListeners)
|
listener->m_dirty = false;
|
||||||
listener->m_dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine)
|
void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine) {
|
||||||
{
|
_bringOutYourDead();
|
||||||
_bringOutYourDead();
|
|
||||||
|
|
||||||
/* Determine lowest available free vid */
|
/* Determine lowest available free vid */
|
||||||
int maxVid = -1;
|
int maxVid = -1;
|
||||||
for (ObjToken<Voice>& vox : m_activeVoices)
|
for (ObjToken<Voice>& vox : m_activeVoices)
|
||||||
maxVid = std::max(maxVid, vox->maxVid());
|
maxVid = std::max(maxVid, vox->maxVid());
|
||||||
m_nextVid = maxVid + 1;
|
m_nextVid = maxVid + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp)
|
AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp) {
|
||||||
{
|
AudioGroup* ret = grp.get();
|
||||||
AudioGroup* ret = grp.get();
|
m_audioGroups.emplace(std::make_pair(&data, std::move(grp)));
|
||||||
m_audioGroups.emplace(std::make_pair(&data, std::move(grp)));
|
|
||||||
|
|
||||||
/* setup SFX index for contained objects */
|
/* setup SFX index for contained objects */
|
||||||
for (const auto& grp : ret->getProj().sfxGroups())
|
for (const auto& grp : ret->getProj().sfxGroups()) {
|
||||||
{
|
const SFXGroupIndex& sfxGroup = *grp.second;
|
||||||
const SFXGroupIndex& sfxGroup = *grp.second;
|
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
|
||||||
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
|
for (const auto& ent : sfxGroup.m_sfxEntries)
|
||||||
for (const auto& ent : sfxGroup.m_sfxEntries)
|
m_sfxLookup[ent.first] = std::make_tuple(ret, grp.first, &ent.second);
|
||||||
m_sfxLookup[ent.first] = std::make_tuple(ret, grp.first, &ent.second);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add GameCube audio group data pointers to engine; must remain resident! */
|
/** Add GameCube audio group data pointers to engine; must remain resident! */
|
||||||
const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data)
|
const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data) {
|
||||||
{
|
removeAudioGroup(data);
|
||||||
removeAudioGroup(data);
|
return _addAudioGroup(data, std::make_unique<AudioGroup>(data));
|
||||||
return _addAudioGroup(data, std::make_unique<AudioGroup>(data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove audio group from engine */
|
/** Remove audio group from engine */
|
||||||
void Engine::removeAudioGroup(const AudioGroupData& data)
|
void Engine::removeAudioGroup(const AudioGroupData& data) {
|
||||||
{
|
auto search = m_audioGroups.find(&data);
|
||||||
auto search = m_audioGroups.find(&data);
|
if (search == m_audioGroups.cend())
|
||||||
if (search == m_audioGroups.cend())
|
return;
|
||||||
return;
|
AudioGroup* grp = search->second.get();
|
||||||
AudioGroup* grp = search->second.get();
|
|
||||||
|
|
||||||
/* Destroy runtime entities within group */
|
/* Destroy runtime entities within group */
|
||||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
|
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();) {
|
||||||
{
|
Voice* vox = it->get();
|
||||||
Voice* vox = it->get();
|
if (&vox->getAudioGroup() == grp) {
|
||||||
if (&vox->getAudioGroup() == grp)
|
vox->_destroy();
|
||||||
{
|
it = m_activeVoices.erase(it);
|
||||||
vox->_destroy();
|
continue;
|
||||||
it = m_activeVoices.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();)
|
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();) {
|
||||||
{
|
Emitter* emitter = it->get();
|
||||||
Emitter* emitter = it->get();
|
if (&emitter->getAudioGroup() == grp) {
|
||||||
if (&emitter->getAudioGroup() == grp)
|
emitter->_destroy();
|
||||||
{
|
it = m_activeEmitters.erase(it);
|
||||||
emitter->_destroy();
|
continue;
|
||||||
it = m_activeEmitters.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();)
|
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();) {
|
||||||
{
|
Sequencer* seq = it->get();
|
||||||
Sequencer* seq = it->get();
|
if (&seq->getAudioGroup() == grp) {
|
||||||
if (&seq->getAudioGroup() == grp)
|
seq->_destroy();
|
||||||
{
|
it = m_activeSequencers.erase(it);
|
||||||
seq->_destroy();
|
continue;
|
||||||
it = m_activeSequencers.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
/* teardown SFX index for contained objects */
|
/* teardown SFX index for contained objects */
|
||||||
for (const auto& pair : grp->getProj().sfxGroups())
|
for (const auto& pair : grp->getProj().sfxGroups()) {
|
||||||
{
|
const SFXGroupIndex& sfxGroup = *pair.second;
|
||||||
const SFXGroupIndex& sfxGroup = *pair.second;
|
for (const auto& pair : sfxGroup.m_sfxEntries)
|
||||||
for (const auto& pair : sfxGroup.m_sfxEntries)
|
m_sfxLookup.erase(pair.first);
|
||||||
m_sfxLookup.erase(pair.first);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_audioGroups.erase(search);
|
m_audioGroups.erase(search);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create new Studio within engine */
|
/** Create new Studio within engine */
|
||||||
ObjToken<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); }
|
ObjToken<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); }
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups */
|
/** Start soundFX playing from loaded audio groups */
|
||||||
ObjToken<Voice> Engine::fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx)
|
ObjToken<Voice> Engine::fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx) {
|
||||||
{
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
auto search = m_sfxLookup.find(sfxId);
|
if (search == m_sfxLookup.end())
|
||||||
if (search == m_sfxLookup.end())
|
return {};
|
||||||
return {};
|
|
||||||
|
|
||||||
AudioGroup* grp = std::get<0>(search->second);
|
AudioGroup* grp = std::get<0>(search->second);
|
||||||
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator ret =
|
std::list<ObjToken<Voice>>::iterator ret =
|
||||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
|
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
|
||||||
|
|
||||||
if (!(*ret)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0))
|
if (!(*ret)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0)) {
|
||||||
{
|
_destroyVoice(ret);
|
||||||
_destroyVoice(ret);
|
return {};
|
||||||
return {};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
(*ret)->setVolume(vol);
|
(*ret)->setVolume(vol);
|
||||||
float evalPan = pan != 0.f ? pan : ((entry->panning - 64.f) / 63.f);
|
float evalPan = pan != 0.f ? pan : ((entry->panning - 64.f) / 63.f);
|
||||||
evalPan = clamp(-1.f, evalPan, 1.f);
|
evalPan = clamp(-1.f, evalPan, 1.f);
|
||||||
(*ret)->setPan(evalPan);
|
(*ret)->setPan(evalPan);
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from explicit group data (for editor use) */
|
/** Start soundFX playing from explicit group data (for editor use) */
|
||||||
ObjToken<Voice> Engine::fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan, ObjToken<Studio> smx)
|
ObjToken<Voice> Engine::fxStart(const AudioGroup* group, GroupId groupId, SFXId sfxId, float vol, float pan,
|
||||||
{
|
ObjToken<Studio> smx) {
|
||||||
const SFXGroupIndex* sfxIdx = group->getProj().getSFXGroupIndex(groupId);
|
const SFXGroupIndex* sfxIdx = group->getProj().getSFXGroupIndex(groupId);
|
||||||
if (sfxIdx)
|
if (sfxIdx) {
|
||||||
{
|
auto search = sfxIdx->m_sfxEntries.find(sfxId);
|
||||||
auto search = sfxIdx->m_sfxEntries.find(sfxId);
|
if (search != sfxIdx->m_sfxEntries.cend()) {
|
||||||
if (search != sfxIdx->m_sfxEntries.cend())
|
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, groupId, NativeSampleRate, true, false, smx);
|
||||||
{
|
|
||||||
std::list<ObjToken<Voice>>::iterator ret =
|
|
||||||
_allocateVoice(*group, groupId, NativeSampleRate, true, false, smx);
|
|
||||||
|
|
||||||
auto& entry = search->second;
|
auto& entry = search->second;
|
||||||
if (!(*ret)->loadPageObject(entry.objId, 1000.f, entry.defKey, entry.defVel, 0))
|
if (!(*ret)->loadPageObject(entry.objId, 1000.f, entry.defKey, entry.defVel, 0)) {
|
||||||
{
|
_destroyVoice(ret);
|
||||||
_destroyVoice(ret);
|
return {};
|
||||||
return {};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
(*ret)->setVolume(vol);
|
(*ret)->setVolume(vol);
|
||||||
float evalPan = pan != 0.f ? pan : ((entry.panning - 64.f) / 63.f);
|
float evalPan = pan != 0.f ? pan : ((entry.panning - 64.f) / 63.f);
|
||||||
evalPan = clamp(-1.f, evalPan, 1.f);
|
evalPan = clamp(-1.f, evalPan, 1.f);
|
||||||
(*ret)->setPan(evalPan);
|
(*ret)->setPan(evalPan);
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start SoundMacro node playing directly (for editor use) */
|
/** Start SoundMacro node playing directly (for editor use) */
|
||||||
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel,
|
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod,
|
||||||
uint8_t mod, ObjToken<Studio> smx)
|
ObjToken<Studio> smx) {
|
||||||
{
|
if (!group)
|
||||||
if (!group)
|
return {};
|
||||||
return {};
|
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator ret =
|
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
|
||||||
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
|
|
||||||
|
|
||||||
if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod))
|
if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod)) {
|
||||||
{
|
_destroyVoice(ret);
|
||||||
_destroyVoice(ret);
|
return {};
|
||||||
return {};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
(*ret)->setVolume(1.f);
|
(*ret)->setVolume(1.f);
|
||||||
(*ret)->setPan(0.f);
|
(*ret)->setPan(0.f);
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start SoundMacro object playing directly (for editor use) */
|
/** Start SoundMacro object playing directly (for editor use) */
|
||||||
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
|
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel,
|
||||||
uint8_t vel, uint8_t mod, ObjToken<Studio> smx)
|
uint8_t mod, ObjToken<Studio> smx) {
|
||||||
{
|
if (!group)
|
||||||
if (!group)
|
return {};
|
||||||
return {};
|
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator ret =
|
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
|
||||||
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
|
|
||||||
|
|
||||||
if (!(*ret)->loadMacroObject(macro, 0, 1000.f, key, vel, mod))
|
if (!(*ret)->loadMacroObject(macro, 0, 1000.f, key, vel, mod)) {
|
||||||
{
|
_destroyVoice(ret);
|
||||||
_destroyVoice(ret);
|
return {};
|
||||||
return {};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
(*ret)->setVolume(1.f);
|
(*ret)->setVolume(1.f);
|
||||||
(*ret)->setPan(0.f);
|
(*ret)->setPan(0.f);
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start PageObject node playing directly (for editor use) */
|
/** Start PageObject node playing directly (for editor use) */
|
||||||
ObjToken<Voice> Engine::pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
|
ObjToken<Voice> Engine::pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod,
|
||||||
uint8_t vel, uint8_t mod, ObjToken<Studio> smx)
|
ObjToken<Studio> smx) {
|
||||||
{
|
if (!group)
|
||||||
if (!group)
|
return {};
|
||||||
return {};
|
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator ret =
|
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
|
||||||
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
|
|
||||||
|
|
||||||
if (!(*ret)->loadPageObject(id, 1000.f, key, vel, mod))
|
if (!(*ret)->loadPageObject(id, 1000.f, key, vel, mod)) {
|
||||||
{
|
_destroyVoice(ret);
|
||||||
_destroyVoice(ret);
|
return {};
|
||||||
return {};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||||
ObjToken<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
ObjToken<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
|
||||||
SFXId sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx)
|
float minVol, float maxVol, bool doppler, ObjToken<Studio> smx) {
|
||||||
{
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
auto search = m_sfxLookup.find(sfxId);
|
if (search == m_sfxLookup.end())
|
||||||
if (search == m_sfxLookup.end())
|
return {};
|
||||||
return {};
|
|
||||||
|
|
||||||
AudioGroup* grp = std::get<0>(search->second);
|
AudioGroup* grp = std::get<0>(search->second);
|
||||||
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::list<ObjToken<Voice>>::iterator vox =
|
std::list<ObjToken<Voice>>::iterator vox =
|
||||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
|
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
|
||||||
|
|
||||||
if (!(*vox)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0))
|
if (!(*vox)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0)) {
|
||||||
{
|
_destroyVoice(vox);
|
||||||
_destroyVoice(vox);
|
return {};
|
||||||
return {};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(),
|
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(),
|
||||||
MakeObj<Emitter>(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
|
MakeObj<Emitter>(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
|
||||||
Emitter& ret = *(*emitIt);
|
Emitter& ret = *(*emitIt);
|
||||||
|
|
||||||
ret.getVoice()->setPan(entry->panning);
|
ret.getVoice()->setPan(entry->panning);
|
||||||
ret.setVectors(pos, dir);
|
ret.setVectors(pos, dir);
|
||||||
ret.setMaxVol(maxVol);
|
ret.setMaxVol(maxVol);
|
||||||
|
|
||||||
return *emitIt;
|
return *emitIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Build listener and add to engine's listener list */
|
/** Build listener and add to engine's listener list */
|
||||||
ObjToken<Listener> Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up,
|
ObjToken<Listener> Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up,
|
||||||
float frontDiff, float backDiff, float soundSpeed, float volume)
|
float frontDiff, float backDiff, float soundSpeed, float volume) {
|
||||||
{
|
auto listenerIt =
|
||||||
auto listenerIt = m_activeListeners.emplace(m_activeListeners.end(),
|
m_activeListeners.emplace(m_activeListeners.end(), MakeObj<Listener>(volume, frontDiff, backDiff, soundSpeed));
|
||||||
MakeObj<Listener>(volume, frontDiff, backDiff, soundSpeed));
|
Listener& ret = *(*listenerIt);
|
||||||
Listener& ret = *(*listenerIt);
|
ret.setVectors(pos, dir, heading, up);
|
||||||
ret.setVectors(pos, dir, heading, up);
|
return *listenerIt;
|
||||||
return *listenerIt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove listener from engine's listener list */
|
/** Remove listener from engine's listener list */
|
||||||
void Engine::removeListener(Listener* listener)
|
void Engine::removeListener(Listener* listener) {
|
||||||
{
|
for (auto it = m_activeListeners.begin(); it != m_activeListeners.end(); ++it) {
|
||||||
for (auto it = m_activeListeners.begin() ; it != m_activeListeners.end() ; ++it)
|
if (it->get() == listener) {
|
||||||
{
|
m_activeListeners.erase(it);
|
||||||
if (it->get() == listener)
|
return;
|
||||||
{
|
|
||||||
m_activeListeners.erase(it);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start song playing from loaded audio groups */
|
/** Start song playing from loaded audio groups */
|
||||||
ObjToken<Sequencer> Engine::seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop, ObjToken<Studio> smx)
|
ObjToken<Sequencer> Engine::seqPlay(GroupId groupId, SongId songId, const unsigned char* arrData, bool loop,
|
||||||
{
|
ObjToken<Studio> smx) {
|
||||||
std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId);
|
std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId);
|
||||||
if (songGrp.second)
|
if (songGrp.second) {
|
||||||
{
|
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx);
|
||||||
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx);
|
if (!*ret)
|
||||||
if (!*ret)
|
return {};
|
||||||
return {};
|
|
||||||
|
|
||||||
if (arrData)
|
if (arrData)
|
||||||
(*ret)->playSong(arrData, loop);
|
(*ret)->playSong(arrData, loop);
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<AudioGroup*, const SFXGroupIndex*> sfxGrp = _findSFXGroup(groupId);
|
std::pair<AudioGroup*, const SFXGroupIndex*> sfxGrp = _findSFXGroup(groupId);
|
||||||
if (sfxGrp.second)
|
if (sfxGrp.second) {
|
||||||
{
|
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx);
|
||||||
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx);
|
if (!*ret)
|
||||||
if (!*ret)
|
return {};
|
||||||
return {};
|
return *ret;
|
||||||
return *ret;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjToken<Sequencer> Engine::seqPlay(const AudioGroup* group, GroupId groupId, SongId songId,
|
ObjToken<Sequencer> Engine::seqPlay(const AudioGroup* group, GroupId groupId, SongId songId,
|
||||||
const unsigned char* arrData, bool loop, ObjToken<Studio> smx)
|
const unsigned char* arrData, bool loop, ObjToken<Studio> smx) {
|
||||||
{
|
const SongGroupIndex* sgIdx = group->getProj().getSongGroupIndex(groupId);
|
||||||
const SongGroupIndex* sgIdx = group->getProj().getSongGroupIndex(groupId);
|
if (sgIdx) {
|
||||||
if (sgIdx)
|
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
|
||||||
{
|
if (!*ret)
|
||||||
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
|
return {};
|
||||||
if (!*ret)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
if (arrData)
|
if (arrData)
|
||||||
(*ret)->playSong(arrData, loop);
|
(*ret)->playSong(arrData, loop);
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SFXGroupIndex* sfxIdx = group->getProj().getSFXGroupIndex(groupId);
|
const SFXGroupIndex* sfxIdx = group->getProj().getSFXGroupIndex(groupId);
|
||||||
if (sfxIdx)
|
if (sfxIdx) {
|
||||||
{
|
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
|
||||||
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
|
if (!*ret)
|
||||||
if (!*ret)
|
return {};
|
||||||
return {};
|
return *ret;
|
||||||
return *ret;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set total volume of engine */
|
/** Set total volume of engine */
|
||||||
void Engine::setVolume(float vol)
|
void Engine::setVolume(float vol) { m_masterVolume = vol; }
|
||||||
{
|
|
||||||
m_masterVolume = vol;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Find voice from VoiceId */
|
/** Find voice from VoiceId */
|
||||||
ObjToken<Voice> Engine::findVoice(int vid)
|
ObjToken<Voice> Engine::findVoice(int vid) {
|
||||||
{
|
for (ObjToken<Voice>& vox : m_activeVoices) {
|
||||||
for (ObjToken<Voice>& vox : m_activeVoices)
|
ObjToken<Voice> ret = vox->_findVoice(vid, vox);
|
||||||
{
|
if (ret)
|
||||||
ObjToken<Voice> ret = vox->_findVoice(vid, vox);
|
return ret;
|
||||||
if (ret)
|
}
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
for (ObjToken<Sequencer>& seq : m_activeSequencers) {
|
||||||
{
|
ObjToken<Voice> ret = seq->findVoice(vid);
|
||||||
ObjToken<Voice> ret = seq->findVoice(vid);
|
if (ret)
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `flag` set */
|
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `flag` set */
|
||||||
void Engine::killKeygroup(uint8_t kg, bool now)
|
void Engine::killKeygroup(uint8_t kg, bool now) {
|
||||||
{
|
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();) {
|
||||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
|
Voice* vox = it->get();
|
||||||
{
|
if (vox->m_keygroup == kg) {
|
||||||
Voice* vox = it->get();
|
if (now) {
|
||||||
if (vox->m_keygroup == kg)
|
it = _destroyVoice(it);
|
||||||
{
|
continue;
|
||||||
if (now)
|
}
|
||||||
{
|
vox->keyOff();
|
||||||
it = _destroyVoice(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
vox->keyOff();
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
||||||
seq->killKeygroup(kg, now);
|
seq->killKeygroup(kg, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send all voices using `macroId` the message `val` */
|
/** Send all voices using `macroId` the message `val` */
|
||||||
void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
|
void Engine::sendMacroMessage(ObjectId macroId, int32_t val) {
|
||||||
{
|
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end(); ++it) {
|
||||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end(); ++it)
|
Voice* vox = it->get();
|
||||||
{
|
if (vox->getObjectId() == macroId)
|
||||||
Voice* vox = it->get();
|
vox->message(val);
|
||||||
if (vox->getObjectId() == macroId)
|
}
|
||||||
vox->message(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
for (ObjToken<Sequencer>& seq : m_activeSequencers)
|
||||||
seq->sendMacroMessage(macroId, val);
|
seq->sendMacroMessage(macroId, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Engine::getNumTotalActiveVoices() const
|
size_t Engine::getNumTotalActiveVoices() const {
|
||||||
{
|
size_t ret = 0;
|
||||||
size_t ret = 0;
|
for (const auto& vox : m_activeVoices)
|
||||||
for (const auto& vox : m_activeVoices)
|
ret += vox->getTotalVoices();
|
||||||
ret += vox->getTotalVoices();
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // namespace amuse
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue