New code style refactor

This commit is contained in:
Jack Andersen 2018-12-07 19:20:09 -10:00
parent b4c073c373
commit a7a408cc66
111 changed files with 30851 additions and 37670 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}; };

View File

@ -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;
} }

View File

@ -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

View File

@ -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);
} }

View File

@ -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;
}; };

View File

@ -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);
} }

View File

@ -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();
};

View File

@ -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);
} }

View File

@ -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(); }
};

View File

@ -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);
} }

View File

@ -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

View File

@ -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();
}; };

View File

@ -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());
} }

View File

@ -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

View File

@ -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);
};

View File

@ -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);
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}; };

View File

@ -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;
}
} }
}
} }

View File

@ -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

View File

@ -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();
}; };

View File

@ -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

View File

@ -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);
}
}
}
}
}
}
}

View File

@ -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

View File

@ -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;
} }

View File

@ -3,4 +3,3 @@
#include <string> #include <string>
std::wstring openDB(); std::wstring openDB();

View File

@ -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); }

View File

@ -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

View File

@ -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(&notifyCls); RegisterClassW(&notifyCls);
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

View File

@ -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

View File

@ -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;
} }

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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