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
UseTab: Never
---
@ -8,7 +8,6 @@ DerivePointerAlignment: false
PointerAlignment: Left
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
BreakBeforeBraces: Allman
IndentCaseLabels: false
AllowShortBlocksOnASingleLine: true
AlignOperands: true
@ -16,7 +15,6 @@ AlignTrailingComments: true
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BreakConstructorInitializersBeforeComma: true
BreakStringLiterals: true
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
AllowShortFunctionsOnASingleLine: All
@ -25,6 +23,6 @@ NamespaceIndentation: None
BinPackArguments: true
BinPackParameters: true
SortIncludes: false
AccessModifierOffset: -4
AccessModifierOffset: -2
ConstructorInitializerIndentWidth: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: true

View File

@ -5,50 +5,46 @@
#include <amuse/BooBackend.hpp>
#include <boo/audiodev/IAudioVoiceEngine.hpp>
@interface DataOutlineView : NSOutlineView
{
@interface DataOutlineView : NSOutlineView {
@public
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* deleteMenuItem;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* deleteMenuItem;
}
@end
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
{
AudioGroupFilePresenter* presenter;
@interface SamplesTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
AudioGroupFilePresenter* presenter;
}
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
@end
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate>
{
AudioGroupFilePresenter* presenter;
@interface SFXTableController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
AudioGroupFilePresenter* presenter;
}
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
@end
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient>
{
IBOutlet NSWindow* mainWindow;
IBOutlet NSOutlineView* dataOutline;
IBOutlet NSSearchField* dataSearchField;
IBOutlet NSTableView* sfxTable;
IBOutlet NSTableView* samplesTable;
IBOutlet NSTextView* creditsView;
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient> {
IBOutlet NSWindow* mainWindow;
IBOutlet NSOutlineView* dataOutline;
IBOutlet NSSearchField* dataSearchField;
IBOutlet NSTableView* sfxTable;
IBOutlet NSTableView* samplesTable;
IBOutlet NSTextView* creditsView;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* removeDataMenu;
IBOutlet NSButton* removeDataButton;
IBOutlet NSMenuItem* removeDataMenu;
AudioGroupFilePresenter* groupFilePresenter;
AudioGroupFilePresenter* groupFilePresenter;
SamplesTableController* samplesController;
SFXTableController* sfxController;
SamplesTableController* samplesController;
SFXTableController* sfxController;
@public
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
std::experimental::optional<amuse::Engine> amuseEngine;
std::shared_ptr<amuse::Voice> activeSFXVox;
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
std::experimental::optional<amuse::Engine> amuseEngine;
std::shared_ptr<amuse::Voice> activeSFXVox;
}
- (BOOL)importURL:(NSURL*)url;
- (void)outlineView:(DataOutlineView*)ov selectionChanged:(id)item;
@ -56,4 +52,3 @@
- (void)startSFX:(int)sfxId;
- (void)startSample:(int)sampId;
@end

View File

@ -19,142 +19,127 @@
- (amuse::Engine&)getAmuseEngine;
@end
struct AudioGroupDataCollection
{
std::string m_name;
NSURL* m_proj;
NSURL* m_pool;
NSURL* m_sdir;
NSURL* m_samp;
NSURL* m_meta;
struct AudioGroupDataCollection {
std::string m_name;
NSURL* m_proj;
NSURL* m_pool;
NSURL* m_sdir;
NSURL* m_samp;
NSURL* m_meta;
AudioGroupDataToken* m_token;
AudioGroupDataToken* m_token;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
struct MetaData
{
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
{
}
MetaData(athena::io::FileReader& r)
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
{
}
};
std::experimental::optional<MetaData> m_metaData;
struct MetaData {
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {}
MetaData(athena::io::FileReader& r)
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
};
std::experimental::optional<MetaData> m_metaData;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
std::vector<AudioGroupToken*> m_groupTokens;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
std::vector<AudioGroupToken*> m_groupTokens;
void moveURL(NSURL* oldUrl, NSURL* newUrl);
void moveURL(NSURL* oldUrl, NSURL* newUrl);
bool loadProj(AudioGroupFilePresenter* presenter);
bool loadPool(AudioGroupFilePresenter* presenter);
bool loadSdir(AudioGroupFilePresenter* presenter);
bool loadSamp(AudioGroupFilePresenter* presenter);
bool loadMeta(AudioGroupFilePresenter* presenter);
bool loadProj(AudioGroupFilePresenter* presenter);
bool loadPool(AudioGroupFilePresenter* presenter);
bool loadSdir(AudioGroupFilePresenter* presenter);
bool loadSamp(AudioGroupFilePresenter* presenter);
bool loadMeta(AudioGroupFilePresenter* presenter);
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
bool isDataComplete() const
{
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad(AudioGroupFilePresenter* presenter);
bool _indexData(AudioGroupFilePresenter* presenter);
AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
bool isDataComplete() const {
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad(AudioGroupFilePresenter* presenter);
bool _indexData(AudioGroupFilePresenter* presenter);
void enable(AudioGroupFilePresenter* presenter);
void disable(AudioGroupFilePresenter* presenter);
void enable(AudioGroupFilePresenter* presenter);
void disable(AudioGroupFilePresenter* presenter);
};
struct AudioGroupCollection
{
NSURL* m_url;
struct AudioGroupCollection {
NSURL* m_url;
AudioGroupCollectionToken* m_token;
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
AudioGroupCollectionToken* m_token;
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
AudioGroupCollection(NSURL* url);
void addCollection(AudioGroupFilePresenter* presenter,
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter* presenter);
bool doSearch(std::string_view str);
bool doActiveFilter();
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
AudioGroupCollection(NSURL* url);
void addCollection(AudioGroupFilePresenter* presenter,
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter* presenter);
bool doSearch(std::string_view str);
bool doActiveFilter();
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
};
@interface AudioGroupDataToken : NSObject
{
@interface AudioGroupDataToken : NSObject {
@public
AudioGroupDataCollection* m_collection;
AudioGroupDataCollection* m_collection;
}
- (id)initWithDataCollection:(AudioGroupDataCollection*)collection;
@end
@interface AudioGroupCollectionToken : NSObject
{
@interface AudioGroupCollectionToken : NSObject {
@public
AudioGroupCollection* m_collection;
AudioGroupCollection* m_collection;
}
- (id)initWithCollection:(AudioGroupCollection*)collection;
@end
@interface AudioGroupSFXToken : NSObject
{
@interface AudioGroupSFXToken : NSObject {
@public
NSAttributedString* m_name;
int m_loadId;
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
NSAttributedString* m_name;
int m_loadId;
const amuse::SFXGroupIndex::SFXEntry* m_sfx;
}
- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx;
@end
@interface AudioGroupSampleToken : NSObject
{
@interface AudioGroupSampleToken : NSObject {
@public
NSAttributedString* m_name;
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
NSAttributedString* m_name;
const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>* m_sample;
}
- (id)
initWithName:(NSAttributedString*)name
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry, amuse::AudioGroupSampleDirectory::ADPCMParms>*)
sample;
- (id)initWithName:(NSAttributedString*)name
samp:(const std::pair<amuse::AudioGroupSampleDirectory::Entry,
amuse::AudioGroupSampleDirectory::ADPCMParms>*)sample;
@end
@interface AudioGroupToken : NSObject
{
@interface AudioGroupToken : NSObject {
@public
NSString* m_name;
int m_id;
const amuse::SongGroupIndex* m_song;
const amuse::SFXGroupIndex* m_sfx;
NSString* m_name;
int m_id;
const amuse::SongGroupIndex* m_song;
const amuse::SFXGroupIndex* m_sfx;
}
- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group;
- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group;
@end
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate>
{
@interface AudioGroupFilePresenter : NSObject <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate> {
@public
id<AudioGroupClient> m_audioGroupClient;
NSURL* m_groupURL;
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
NSOutlineView* m_lastOutlineView;
NSString* m_searchStr;
id<AudioGroupClient> m_audioGroupClient;
NSURL* m_groupURL;
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
NSOutlineView* m_lastOutlineView;
NSString* m_searchStr;
std::vector<AudioGroupSFXToken*> m_sfxTableData;
std::vector<AudioGroupSampleToken*> m_sampleTableData;
std::vector<AudioGroupSFXToken*> m_sfxTableData;
std::vector<AudioGroupSampleToken*> m_sampleTableData;
}
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
- (BOOL)addCollectionName:(std::string&&)name
@ -164,4 +149,3 @@ initWithName:(NSAttributedString*)name
- (void)setSearchFilter:(NSString*)str;
- (void)removeSelectedItem;
@end

View File

@ -19,29 +19,26 @@
@class AudioUnitViewController;
namespace amuse
{
namespace amuse {
/** Backend voice allocator implementation for AudioUnit mixer */
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator
{
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator {
public:
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
};
void RegisterAudioUnit();
}
} // namespace amuse
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient>
{
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient> {
@public
AudioUnitViewController* m_viewController;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
AudioGroupFilePresenter* m_filePresenter;
AUAudioUnitBus* m_outBus;
AUAudioUnitBusArray* m_outs;
AudioUnitViewController* m_viewController;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
AudioGroupFilePresenter* m_filePresenter;
AUAudioUnitBus* m_outBus;
AUAudioUnitBusArray* m_outs;
}
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
error:(NSError* __nullable* __nonnull)outError

View File

@ -5,19 +5,16 @@
@class AmuseAudioUnit;
@interface GroupBrowserDelegate : NSObject <NSBrowserDelegate>
{
AmuseAudioUnit* m_audioUnit;
@interface GroupBrowserDelegate : NSObject <NSBrowserDelegate> {
AmuseAudioUnit* m_audioUnit;
}
- (id)initWithAudioUnit:(AmuseAudioUnit*)au;
@end
@interface AudioUnitViewController : AUViewController <AUAudioUnitFactory>
{
@interface AudioUnitViewController : AUViewController <AUAudioUnitFactory> {
@public
AmuseAudioUnit* m_audioUnit;
IBOutlet NSBrowser* m_groupBrowser;
GroupBrowserDelegate* m_groupBrowserDelegate;
AmuseAudioUnit* m_audioUnit;
IBOutlet NSBrowser* m_groupBrowser;
GroupBrowserDelegate* m_groupBrowserDelegate;
}
@end

File diff suppressed because it is too large Load Diff

View File

@ -10,73 +10,71 @@
class ADSREditor;
class ADSRView : public QWidget
{
Q_OBJECT
friend class ADSRControls;
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
std::vector<QStaticText> m_timeTexts;
int m_dragPoint = -1;
uint64_t m_cycleIdx = 0;
ADSREditor* getEditor() const;
public:
explicit ADSRView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
class ADSRView : public QWidget {
Q_OBJECT
friend class ADSRControls;
amuse::ObjToken<ProjectModel::ADSRNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
std::vector<QStaticText> m_timeTexts;
int m_dragPoint = -1;
uint64_t m_cycleIdx = 0;
ADSREditor* getEditor() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseReleaseEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
public:
explicit ADSRView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseReleaseEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
};
class ADSRControls : public QFrame
{
Q_OBJECT
friend class ADSRView;
QDoubleSpinBox* m_attack;
QDoubleSpinBox* m_decay;
QDoubleSpinBox* m_sustain;
QDoubleSpinBox* m_release;
QCheckBox* m_dls;
QLabel* m_velToAttackLab;
QDoubleSpinBox* m_velToAttack;
QLabel* m_keyToDecayLab;
QDoubleSpinBox* m_keyToDecay;
bool m_enableUpdate = true;
ADSREditor* getEditor() const;
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
void setRelease(double release, uint64_t cycleCount);
class ADSRControls : public QFrame {
Q_OBJECT
friend class ADSRView;
QDoubleSpinBox* m_attack;
QDoubleSpinBox* m_decay;
QDoubleSpinBox* m_sustain;
QDoubleSpinBox* m_release;
QCheckBox* m_dls;
QLabel* m_velToAttackLab;
QDoubleSpinBox* m_velToAttack;
QLabel* m_keyToDecayLab;
QDoubleSpinBox* m_keyToDecay;
bool m_enableUpdate = true;
ADSREditor* getEditor() const;
void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
void setRelease(double release, uint64_t cycleCount);
public:
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
explicit ADSRControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
public slots:
void attackChanged(double val);
void decayChanged(double val);
void sustainChanged(double val);
void releaseChanged(double val);
void dlsStateChanged(int state);
void velToAttackChanged(double val);
void keyToDecayChanged(double val);
void attackChanged(double val);
void decayChanged(double val);
void sustainChanged(double val);
void releaseChanged(double val);
void dlsStateChanged(int state);
void velToAttackChanged(double val);
void keyToDecayChanged(double val);
};
class ADSREditor : public EditorWidget
{
Q_OBJECT
friend class ADSRView;
friend class ADSRControls;
ADSRView* m_adsrView;
ADSRControls* m_controls;
class ADSREditor : public EditorWidget {
Q_OBJECT
friend class ADSRView;
friend class ADSRControls;
ADSRView* m_adsrView;
ADSRControls* m_controls;
public:
explicit ADSREditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
explicit ADSREditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::ADSRNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
};

View File

@ -4,105 +4,93 @@
#include <QObject>
#include <QProcess>
boo::SystemString QStringToSysString(const QString& str)
{
boo::SystemString QStringToSysString(const QString& str) {
#ifdef _WIN32
return (wchar_t*)str.utf16();
return (wchar_t*)str.utf16();
#else
return str.toUtf8().toStdString();
return str.toUtf8().toStdString();
#endif
}
QString SysStringToQString(const boo::SystemString& str)
{
QString SysStringToQString(const boo::SystemString& str) {
#ifdef _WIN32
return QString::fromStdWString(str);
return QString::fromStdWString(str);
#else
return QString::fromStdString(str);
return QString::fromStdString(str);
#endif
}
bool MkPath(const QString& path, UIMessenger& messenger)
{
QFileInfo fInfo(path);
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
bool MkPath(const QString& path, UIMessenger& messenger) {
QFileInfo fInfo(path);
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
}
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger)
{
if (!dir.mkpath(file))
{
QString msg = QString(MainWindow::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
messenger.critical(MainWindow::tr("Unable to create directory"), msg);
return false;
}
return true;
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger) {
if (!dir.mkpath(file)) {
QString msg = QString(MainWindow::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
messenger.critical(MainWindow::tr("Unable to create directory"), msg);
return false;
}
return true;
}
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn)
{
const QFileInfo fileInfo(pathIn);
// Mac, Windows support folder or file.
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn) {
const QFileInfo fileInfo(pathIn);
// Mac, Windows support folder or file.
#if defined(Q_OS_WIN)
QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path"));
QString explorer;
for (QString path : paths.split(QStringLiteral(";")))
{
QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe"));
if (finfo.exists())
{
explorer = finfo.filePath();
break;
}
QString paths = QProcessEnvironment::systemEnvironment().value(QStringLiteral("Path"));
QString explorer;
for (QString path : paths.split(QStringLiteral(";"))) {
QFileInfo finfo(QDir(path), QStringLiteral("explorer.exe"));
if (finfo.exists()) {
explorer = finfo.filePath();
break;
}
if (explorer.isEmpty()) {
QMessageBox::warning(parent,
MainWindow::tr("Launching Windows Explorer Failed"),
MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer."));
return;
}
QStringList param;
if (!fileInfo.isDir())
param += QLatin1String("/select,");
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
QProcess::startDetached(explorer, param);
}
if (explorer.isEmpty()) {
QMessageBox::warning(parent, MainWindow::tr("Launching Windows Explorer Failed"),
MainWindow::tr("Could not find explorer.exe in path to launch Windows Explorer."));
return;
}
QStringList param;
if (!fileInfo.isDir())
param += QLatin1String("/select,");
param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
QProcess::startDetached(explorer, param);
#elif defined(Q_OS_MAC)
QStringList scriptArgs;
scriptArgs << QLatin1String("-e")
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
.arg(fileInfo.canonicalFilePath());
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
scriptArgs.clear();
scriptArgs << QLatin1String("-e")
<< QLatin1String("tell application \"Finder\" to activate");
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
QStringList scriptArgs;
scriptArgs << QLatin1String("-e")
<< QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
.arg(fileInfo.canonicalFilePath());
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
scriptArgs.clear();
scriptArgs << QLatin1String("-e") << QLatin1String("tell application \"Finder\" to activate");
QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
#else
// we cannot select a file here, because no file browser really supports it...
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
QProcess browserProc;
const QString browserArgs = QStringLiteral("xdg-open \"%1\"").arg(QFileInfo(folder).path());
browserProc.startDetached(browserArgs);
// we cannot select a file here, because no file browser really supports it...
const QString folder = fileInfo.isDir() ? fileInfo.absoluteFilePath() : fileInfo.filePath();
QProcess browserProc;
const QString browserArgs = QStringLiteral("xdg-open \"%1\"").arg(QFileInfo(folder).path());
browserProc.startDetached(browserArgs);
#endif
}
QString ShowInGraphicalShellString()
{
QString ShowInGraphicalShellString() {
#if defined(Q_OS_WIN)
return MainWindow::tr("Show in Explorer");
return MainWindow::tr("Show in Explorer");
#elif defined(Q_OS_MAC)
return MainWindow::tr("Show in Finder");
return MainWindow::tr("Show in Finder");
#else
return MainWindow::tr("Show in Browser");
return MainWindow::tr("Show in Browser");
#endif
}
QTransform RectToRect(const QRectF& from, const QRectF& to)
{
QPolygonF orig(from);
orig.pop_back();
QPolygonF resize(to);
resize.pop_back();
QTransform ret;
QTransform::quadToQuad(orig, resize, ret);
return ret;
QTransform RectToRect(const QRectF& from, const QRectF& to) {
QPolygonF orig(from);
orig.pop_back();
QPolygonF resize(to);
resize.pop_back();
QTransform ret;
QTransform::quadToQuad(orig, resize, ret);
return ret;
}

View File

@ -8,32 +8,27 @@
class MainWindow;
extern MainWindow* g_MainWindow;
class UIMessenger : public QObject
{
Q_OBJECT
class UIMessenger : public QObject {
Q_OBJECT
public:
using QObject::QObject;
using QObject::QObject;
signals:
QMessageBox::StandardButton information(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
int question(const QString &title,
const QString& text,
const QString& button0Text,
const QString& button1Text = QString(),
const QString& button2Text = QString(),
int defaultButtonNumber = 0,
int escapeButtonNumber = -1);
QMessageBox::StandardButton question(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons =
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton warning(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton critical(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton information(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
int question(const QString& title, const QString& text, const QString& button0Text,
const QString& button1Text = QString(), const QString& button2Text = QString(),
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
QMessageBox::StandardButton
question(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton warning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton critical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
};
boo::SystemString QStringToSysString(const QString& str);
@ -45,22 +40,14 @@ bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger);
void ShowInGraphicalShell(QWidget* parent, const QString& pathIn);
QString ShowInGraphicalShellString();
static QLatin1String StringViewToQString(std::string_view sv)
{
return QLatin1String(sv.data(), int(sv.size()));
}
static QLatin1String StringViewToQString(std::string_view sv) { return QLatin1String(sv.data(), int(sv.size())); }
/* Used for generating transform matrices to map SVG coordinate space */
QTransform RectToRect(const QRectF& from, const QRectF& to);
namespace std
{
template<> struct hash<QString>
{
std::size_t operator()(const QString& s) const noexcept
{
return qHash(s);
}
namespace std {
template <>
struct hash<QString> {
std::size_t operator()(const QString& s) const noexcept { return qHash(s); }
};
}
} // namespace std

View File

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

View File

@ -10,55 +10,54 @@
class CurveEditor;
class CurveView : public QWidget
{
Q_OBJECT
friend class CurveControls;
amuse::ObjToken<ProjectModel::CurveNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
QStaticText m_percentTextsCenter[11];
CurveEditor* getEditor() const;
public:
explicit CurveView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
class CurveView : public QWidget {
Q_OBJECT
friend class CurveControls;
amuse::ObjToken<ProjectModel::CurveNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
QStaticText m_percentTextsCenter[11];
CurveEditor* getEditor() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
public:
explicit CurveView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
};
class CurveControls : public QFrame
{
Q_OBJECT
friend class CurveView;
QLineEdit* m_lineEdit;
QLabel* m_errLabel;
QPushButton* m_setExpr;
QJSEngine m_engine;
CurveEditor* getEditor() const;
class CurveControls : public QFrame {
Q_OBJECT
friend class CurveView;
QLineEdit* m_lineEdit;
QLabel* m_errLabel;
QPushButton* m_setExpr;
QJSEngine m_engine;
CurveEditor* getEditor() const;
public:
explicit CurveControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
void resizeEvent(QResizeEvent* ev);
explicit CurveControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
void resizeEvent(QResizeEvent* ev);
public slots:
void exprCommit();
void exprCommit();
};
class CurveEditor : public EditorWidget
{
Q_OBJECT
friend class CurveView;
friend class CurveControls;
CurveView* m_curveView;
CurveControls* m_controls;
class CurveEditor : public EditorWidget {
Q_OBJECT
friend class CurveView;
friend class CurveControls;
CurveView* m_curveView;
CurveControls* m_controls;
public:
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
};

View File

@ -3,322 +3,273 @@
#include <QStandardItemModel>
#include <QHBoxLayout>
EditorWidget::EditorWidget(QWidget* parent)
: QWidget(parent)
{
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) {}
void EditorUndoCommand::undo() { g_MainWindow->openEditor(m_node.get()); }
void EditorUndoCommand::redo() { g_MainWindow->openEditor(m_node.get()); }
FieldSlider::FieldSlider(QWidget* parent) : QWidget(parent), m_slider(Qt::Horizontal) {
setFixedHeight(22);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(&m_slider);
layout->addWidget(&m_value);
layout->setContentsMargins(QMargins());
setLayout(layout);
m_value.setFixedWidth(42);
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
m_slider.setValue(0);
}
void EditorUndoCommand::undo()
{
g_MainWindow->openEditor(m_node.get());
void FieldSlider::doValueChanged(int value) {
m_value.setText(QString::number(value));
emit valueChanged(value);
}
void EditorUndoCommand::redo()
{
g_MainWindow->openEditor(m_node.get());
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);
}
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);
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 FieldSlider::doValueChanged(int value)
{
m_value.setText(QString::number(value));
emit valueChanged(value);
}
FieldDoubleSlider::FieldDoubleSlider(QWidget* parent)
: QWidget(parent), m_slider(Qt::Horizontal)
{
setFixedHeight(22);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QHBoxLayout* layout = new QHBoxLayout;
layout->addWidget(&m_slider);
layout->addWidget(&m_value);
layout->setContentsMargins(QMargins());
setLayout(layout);
m_value.setFixedWidth(42);
connect(&m_slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
m_slider.setRange(0, 1000);
m_slider.setValue(0);
}
double FieldDoubleSlider::value() const
{
double t = m_slider.value() / 1000.0;
return t * (m_max - m_min) + m_min;
}
void FieldDoubleSlider::setValue(double value)
{
if (value < m_min)
value = m_min;
else if (value > m_max)
value = m_max;
double t = (value - m_min) / (m_max - m_min);
m_slider.setValue(int(t * 1000.0));
doValueChanged(int(t * 1000.0));
}
void FieldDoubleSlider::setRange(double min, double max)
{
m_min = min; m_max = max;
double curValue = value();
if (curValue < min)
setValue(min);
else if (curValue > max)
setValue(max);
}
void FieldDoubleSlider::doValueChanged(int value)
{
double t = value / 1000.0;
t = t * (m_max - m_min) + m_min;
m_value.setText(QString::number(t, 'g', 2));
emit valueChanged(t);
void FieldDoubleSlider::doValueChanged(int value) {
double t = value / 1000.0;
t = t * (m_max - m_min) + m_min;
m_value.setText(QString::number(t, 'g', 2));
emit valueChanged(t);
}
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
: QWidget(parent), m_comboBox(this), m_button(this)
{
: 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.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);
return;
}
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getNullProxy());
m_comboBox.setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
}
void FieldProjectNode::setCollection(ProjectModel::CollectionNode* collection)
{
m_collection = collection;
if (!collection)
{
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
m_button.setDisabled(true);
return;
}
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getNullProxy());
m_comboBox.setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
void FieldProjectNode::_currentIndexChanged(int index) {
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
}
void FieldProjectNode::_currentIndexChanged(int index)
{
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
ProjectModel::BasePoolObjectNode* FieldProjectNode::currentNode() const {
int index = m_comboBox.currentIndex();
if (index == 0)
return nullptr;
else
return m_collection->nodeOfIndex(index - 1);
}
ProjectModel::BasePoolObjectNode* FieldProjectNode::currentNode() const
{
int index = m_comboBox.currentIndex();
if (index == 0)
return nullptr;
else
return m_collection->nodeOfIndex(index - 1);
bool FieldProjectNode::event(QEvent* ev) {
if (ev->type() == QEvent::User) {
showPopup();
return true;
}
return QWidget::event(ev);
}
bool FieldProjectNode::event(QEvent* ev)
{
if (ev->type() == QEvent::User)
{
showPopup();
return true;
}
return QWidget::event(ev);
}
void FieldProjectNode::openCurrent()
{
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
void FieldProjectNode::openCurrent() {
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
}
FieldPageObjectNode::FieldPageObjectNode(ProjectModel::GroupNode* group, QWidget* parent)
: QWidget(parent), m_comboBox(this), m_button(this)
{
: 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.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);
return;
}
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getPageObjectProxy());
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
}
void FieldPageObjectNode::setGroup(ProjectModel::GroupNode* group)
{
m_group = group;
if (!group)
{
m_comboBox.setModel(new QStandardItemModel(0, 1, this));
m_button.setDisabled(true);
return;
}
void FieldPageObjectNode::_currentIndexChanged(int index) {
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
}
ProjectModel::BasePoolObjectNode* FieldPageObjectNode::currentNode() const {
int index = m_comboBox.currentIndex();
if (index == 0) {
return nullptr;
} else {
ProjectModel* model = g_MainWindow->projectModel();
m_comboBox.setModel(model->getPageObjectProxy());
m_comboBox.setRootModelIndex(model->getPageObjectProxy()->mapFromSource(model->index(group)));
return static_cast<ProjectModel::BasePoolObjectNode*>(model->node(model->getPageObjectProxy()->mapToSource(
model->getPageObjectProxy()->index(index, 0, m_comboBox.rootModelIndex()))));
}
}
void FieldPageObjectNode::_currentIndexChanged(int index)
{
m_button.setEnabled(index != 0);
emit currentIndexChanged(index);
bool FieldPageObjectNode::event(QEvent* ev) {
if (ev->type() == QEvent::User) {
showPopup();
return true;
}
return QWidget::event(ev);
}
ProjectModel::BasePoolObjectNode* FieldPageObjectNode::currentNode() const
{
int index = m_comboBox.currentIndex();
if (index == 0)
{
return nullptr;
}
else
{
ProjectModel* model = g_MainWindow->projectModel();
return static_cast<ProjectModel::BasePoolObjectNode*>(
model->node(model->getPageObjectProxy()->mapToSource(
model->getPageObjectProxy()->index(index, 0, m_comboBox.rootModelIndex()))));
}
}
bool FieldPageObjectNode::event(QEvent* ev)
{
if (ev->type() == QEvent::User)
{
showPopup();
return true;
}
return QWidget::event(ev);
}
void FieldPageObjectNode::openCurrent()
{
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
void FieldPageObjectNode::openCurrent() {
if (ProjectModel::BasePoolObjectNode* node = currentNode())
if (!g_MainWindow->isUiDisabled())
g_MainWindow->openEditor(node);
}
AddRemoveButtons::AddRemoveButtons(QWidget* parent)
: QWidget(parent), m_addAction(tr("Add Row")), m_addButton(this),
m_removeAction(tr("Remove Row")), m_removeButton(this)
{
setFixedSize(64, 32);
: QWidget(parent)
, m_addAction(tr("Add Row"))
, m_addButton(this)
, m_removeAction(tr("Remove Row"))
, m_removeButton(this) {
setFixedSize(64, 32);
m_addAction.setIcon(QIcon(QStringLiteral(":/icons/IconAdd.svg")));
m_addButton.setDefaultAction(&m_addAction);
m_addButton.setFixedSize(32, 32);
m_addButton.move(0, 0);
m_addAction.setIcon(QIcon(QStringLiteral(":/icons/IconAdd.svg")));
m_addButton.setDefaultAction(&m_addAction);
m_addButton.setFixedSize(32, 32);
m_addButton.move(0, 0);
m_removeAction.setIcon(QIcon(QStringLiteral(":/icons/IconRemove.svg")));
m_removeButton.setDefaultAction(&m_removeAction);
m_removeButton.setFixedSize(32, 32);
m_removeButton.move(32, 0);
m_removeAction.setEnabled(false);
m_removeAction.setIcon(QIcon(QStringLiteral(":/icons/IconRemove.svg")));
m_removeButton.setDefaultAction(&m_removeAction);
m_removeButton.setFixedSize(32, 32);
m_removeButton.move(32, 0);
m_removeAction.setEnabled(false);
}
static QIcon ListingDeleteIcon;
static QIcon ListingDeleteHoveredIcon;
void ListingDeleteButton::enterEvent(QEvent* event)
{
setIcon(ListingDeleteHoveredIcon);
void ListingDeleteButton::enterEvent(QEvent* event) { setIcon(ListingDeleteHoveredIcon); }
void ListingDeleteButton::leaveEvent(QEvent* event) { setIcon(ListingDeleteIcon); }
ListingDeleteButton::ListingDeleteButton(QWidget* parent) : QPushButton(parent) {
if (ListingDeleteIcon.isNull())
ListingDeleteIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg"));
if (ListingDeleteHoveredIcon.isNull())
ListingDeleteHoveredIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDeleteHovered.svg"));
setVisible(false);
setFixedSize(21, 21);
setFlat(true);
setToolTip(tr("Delete this SoundMacro"));
setIcon(ListingDeleteIcon);
}
void ListingDeleteButton::leaveEvent(QEvent* event)
{
setIcon(ListingDeleteIcon);
}
bool BaseObjectDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index) {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
if (ev->button() == Qt::RightButton) {
ProjectModel::INode* node = getNode(model, index);
ListingDeleteButton::ListingDeleteButton(QWidget* parent)
: QPushButton(parent)
{
if (ListingDeleteIcon.isNull())
ListingDeleteIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg"));
if (ListingDeleteHoveredIcon.isNull())
ListingDeleteHoveredIcon = QIcon(QStringLiteral(":/icons/IconSoundMacroDeleteHovered.svg"));
ContextMenu* menu = new ContextMenu;
setVisible(false);
setFixedSize(21, 21);
setFlat(true);
setToolTip(tr("Delete this SoundMacro"));
setIcon(ListingDeleteIcon);
}
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);
bool BaseObjectDelegate::editorEvent(QEvent *event, QAbstractItemModel* model,
const QStyleOptionViewItem &option, const QModelIndex &index)
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* ev = static_cast<QMouseEvent*>(event);
if (ev->button() == Qt::RightButton)
{
ProjectModel::INode* node = getNode(model, index);
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);
ContextMenu* menu = new ContextMenu;
QAction* openEditorAction = new QAction(tr("Open in Editor"), menu);
openEditorAction->setData(QVariant::fromValue((void*)node));
openEditorAction->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
connect(openEditorAction, SIGNAL(triggered()), this, SLOT(doOpenEditor()));
menu->addAction(openEditorAction);
QAction* findUsagesAction = new QAction(tr("Find Usages"), menu);
findUsagesAction->setData(QVariant::fromValue((void*)node));
findUsagesAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(doFindUsages()));
menu->addAction(findUsagesAction);
menu->popup(ev->globalPos());
}
menu->popup(ev->globalPos());
}
return false;
}
return false;
}
void BaseObjectDelegate::doOpenEditor()
{
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->openEditor(node);
void BaseObjectDelegate::doOpenEditor() {
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->openEditor(node);
}
void BaseObjectDelegate::doFindUsages()
{
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->findUsages(node);
void BaseObjectDelegate::doFindUsages() {
QAction* act = static_cast<QAction*>(sender());
if (ProjectModel::INode* node = reinterpret_cast<ProjectModel::INode*>(act->data().value<void*>()))
g_MainWindow->findUsages(node);
}

View File

@ -15,238 +15,227 @@
#include <QStyledItemDelegate>
#include "ProjectModel.hpp"
class EditorWidget : public QWidget
{
Q_OBJECT
class EditorWidget : public QWidget {
Q_OBJECT
public:
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
virtual bool valid() const { return true; }
virtual void unloadData() {}
virtual ProjectModel::INode* currentNode() const { return nullptr; }
virtual void setEditorEnabled(bool en) { setEnabled(en); }
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
virtual bool valid() const { return true; }
virtual void unloadData() {}
virtual ProjectModel::INode* currentNode() const { return nullptr; }
virtual void setEditorEnabled(bool en) { setEnabled(en); }
virtual AmuseItemEditFlags itemEditFlags() const { return AmuseItemNone; }
public slots:
virtual void itemCutAction() {}
virtual void itemCopyAction() {}
virtual void itemPasteAction() {}
virtual void itemDeleteAction() {}
virtual void itemCutAction() {}
virtual void itemCopyAction() {}
virtual void itemPasteAction() {}
virtual void itemDeleteAction() {}
};
class EditorUndoCommand : public QUndoCommand
{
class EditorUndoCommand : public QUndoCommand {
protected:
amuse::ObjToken<ProjectModel::INode> m_node;
enum class Id
{
SMChangeVal,
SampLoop,
SampPitch,
ADSRAttack,
ADSRDecay,
ADSRSustain,
ADSRAttackAndDecay,
ADSRDecayAndSustain,
ADSRRelease,
ADSRDLS,
ADSRVelToAttack,
ADSRKeyToDecay,
CurveEdit
};
amuse::ObjToken<ProjectModel::INode> m_node;
enum class Id {
SMChangeVal,
SampLoop,
SampPitch,
ADSRAttack,
ADSRDecay,
ADSRSustain,
ADSRAttackAndDecay,
ADSRDecayAndSustain,
ADSRRelease,
ADSRDLS,
ADSRVelToAttack,
ADSRKeyToDecay,
CurveEdit
};
public:
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node,
const QString& text, QUndoCommand* parent = nullptr)
: QUndoCommand(text, parent), m_node(node) {}
void undo();
void redo();
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node, const QString& text, QUndoCommand* parent = nullptr)
: QUndoCommand(text, parent), m_node(node) {}
void undo();
void redo();
};
class FieldSpinBox : public QSpinBox
{
Q_OBJECT
class FieldSpinBox : public QSpinBox {
Q_OBJECT
public:
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR)
: QSpinBox(parent) {}
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR) : QSpinBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
};
class FieldSlider : public QWidget
{
Q_OBJECT
QSlider m_slider;
QLabel m_value;
class FieldSlider : public QWidget {
Q_OBJECT
QSlider m_slider;
QLabel m_value;
public:
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
explicit FieldSlider(QWidget* parent = Q_NULLPTR);
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
int value() const { return m_slider.value(); }
void setValue(int value) { m_slider.setValue(value); doValueChanged(value); }
void setRange(int min, int max) { m_slider.setRange(min, max); }
int value() const { return m_slider.value(); }
void setValue(int value) {
m_slider.setValue(value);
doValueChanged(value);
}
void setRange(int min, int max) { m_slider.setRange(min, max); }
private slots:
void doValueChanged(int value);
void doValueChanged(int value);
signals:
void valueChanged(int value);
void valueChanged(int value);
};
class FieldDoubleSlider : public QWidget
{
Q_OBJECT
QSlider m_slider;
QLabel m_value;
double m_min = 0.0;
double m_max = 1.0;
class FieldDoubleSlider : public QWidget {
Q_OBJECT
QSlider m_slider;
QLabel m_value;
double m_min = 0.0;
double m_max = 1.0;
public:
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
explicit FieldDoubleSlider(QWidget* parent = Q_NULLPTR);
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
double value() const;
void setValue(double value);
void setRange(double min, double max);
double value() const;
void setValue(double value);
void setRange(double min, double max);
private slots:
void doValueChanged(int value);
void doValueChanged(int value);
signals:
void valueChanged(double value);
void valueChanged(double value);
};
class FieldComboBox : public QComboBox
{
Q_OBJECT
class FieldComboBox : public QComboBox {
Q_OBJECT
public:
explicit FieldComboBox(QWidget* parent = Q_NULLPTR)
: QComboBox(parent) {}
explicit FieldComboBox(QWidget* parent = Q_NULLPTR) : QComboBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
};
class FieldProjectNode : public QWidget
{
Q_OBJECT
ProjectModel::CollectionNode* m_collection;
FieldComboBox m_comboBox;
QPushButton m_button;
class FieldProjectNode : public QWidget {
Q_OBJECT
ProjectModel::CollectionNode* m_collection;
FieldComboBox m_comboBox;
QPushButton m_button;
public:
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
void setCollection(ProjectModel::CollectionNode* collection);
ProjectModel::CollectionNode* collection() const { return m_collection; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev);
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
void setCollection(ProjectModel::CollectionNode* collection);
ProjectModel::CollectionNode* collection() const { return m_collection; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev);
private slots:
void _currentIndexChanged(int);
void _currentIndexChanged(int);
public slots:
void openCurrent();
void openCurrent();
signals:
void currentIndexChanged(int);
void currentIndexChanged(int);
};
class FieldPageObjectNode : public QWidget
{
Q_OBJECT
ProjectModel::GroupNode* m_group;
FieldComboBox m_comboBox;
QPushButton m_button;
class FieldPageObjectNode : public QWidget {
Q_OBJECT
ProjectModel::GroupNode* m_group;
FieldComboBox m_comboBox;
QPushButton m_button;
public:
explicit FieldPageObjectNode(ProjectModel::GroupNode* group = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
void setGroup(ProjectModel::GroupNode* group);
ProjectModel::GroupNode* group() const { return m_group; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
QModelIndex rootModelIndex() const { return m_comboBox.rootModelIndex(); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev);
explicit FieldPageObjectNode(ProjectModel::GroupNode* group = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
void setGroup(ProjectModel::GroupNode* group);
ProjectModel::GroupNode* group() const { return m_group; }
int currentIndex() const { return m_comboBox.currentIndex(); }
void setCurrentIndex(int index) { m_comboBox.setCurrentIndex(index); }
QModelIndex rootModelIndex() const { return m_comboBox.rootModelIndex(); }
void showPopup() { m_comboBox.showPopup(); }
ProjectModel::BasePoolObjectNode* currentNode() const;
bool event(QEvent* ev);
private slots:
void _currentIndexChanged(int);
void _currentIndexChanged(int);
public slots:
void openCurrent();
void openCurrent();
signals:
void currentIndexChanged(int);
void currentIndexChanged(int);
};
template <class T>
class EditorFieldNode : public T
{
bool m_deferPopupOpen = true;
class EditorFieldNode : public T {
bool m_deferPopupOpen = true;
public:
using T::T;
bool shouldPopupOpen()
{
bool ret = m_deferPopupOpen;
m_deferPopupOpen = false;
return ret;
}
using T::T;
bool shouldPopupOpen() {
bool ret = m_deferPopupOpen;
m_deferPopupOpen = false;
return ret;
}
};
using EditorFieldProjectNode = EditorFieldNode<FieldProjectNode>;
using EditorFieldPageObjectNode = EditorFieldNode<FieldPageObjectNode>;
template <int MIN, int MAX>
class RangedValueFactory : public QItemEditorFactory
{
class RangedValueFactory : public QItemEditorFactory {
public:
QWidget* createEditor(int userType, QWidget *parent) const
{
QSpinBox* sb = new QSpinBox(parent);
sb->setFrame(false);
sb->setMinimum(MIN);
sb->setMaximum(MAX);
return sb;
}
QWidget* createEditor(int userType, QWidget* parent) const {
QSpinBox* sb = new QSpinBox(parent);
sb->setFrame(false);
sb->setMinimum(MIN);
sb->setMaximum(MAX);
return sb;
}
};
class AddRemoveButtons : public QWidget
{
Q_OBJECT
QAction m_addAction;
QToolButton m_addButton;
QAction m_removeAction;
QToolButton m_removeButton;
class AddRemoveButtons : public QWidget {
Q_OBJECT
QAction m_addAction;
QToolButton m_addButton;
QAction m_removeAction;
QToolButton m_removeButton;
public:
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
QAction* addAction() { return &m_addAction; }
QAction* removeAction() { return &m_removeAction; }
explicit AddRemoveButtons(QWidget* parent = Q_NULLPTR);
QAction* addAction() { return &m_addAction; }
QAction* removeAction() { return &m_removeAction; }
};
class ListingDeleteButton : public QPushButton
{
Q_OBJECT
class ListingDeleteButton : public QPushButton {
Q_OBJECT
public:
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
explicit ListingDeleteButton(QWidget* parent = Q_NULLPTR);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
};
class ContextMenu : public QMenu
{
class ContextMenu : public QMenu {
public:
void hideEvent(QHideEvent* ev)
{
QMenu::hideEvent(ev);
deleteLater();
}
void hideEvent(QHideEvent* ev) {
QMenu::hideEvent(ev);
deleteLater();
}
};
class BaseObjectDelegate : public QStyledItemDelegate
{
Q_OBJECT
class BaseObjectDelegate : public QStyledItemDelegate {
Q_OBJECT
protected:
virtual ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const = 0;
public:
explicit BaseObjectDelegate(QObject* parent = Q_NULLPTR) : QStyledItemDelegate(parent) {}
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
private slots:
void doOpenEditor();
void doFindUsages();
};
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();
};

View File

@ -6,266 +6,183 @@
#include <QApplication>
#include <QScrollBar>
const QString NaturalKeyNames[] =
{
QStringLiteral("C"),
QStringLiteral("D"),
QStringLiteral("E"),
QStringLiteral("F"),
QStringLiteral("G"),
QStringLiteral("A"),
QStringLiteral("B")
};
const QString NaturalKeyNames[] = {QStringLiteral("C"), QStringLiteral("D"), QStringLiteral("E"), QStringLiteral("F"),
QStringLiteral("G"), QStringLiteral("A"), QStringLiteral("B")};
const QString SharpKeyNames[] =
{
QStringLiteral("Cs"),
QStringLiteral("Ds"),
QStringLiteral("Fs"),
QStringLiteral("Gs"),
QStringLiteral("As")
};
const QString SharpKeyNames[] = {QStringLiteral("Cs"), QStringLiteral("Ds"), QStringLiteral("Fs"), QStringLiteral("Gs"),
QStringLiteral("As")};
const QString KeyStrings[] =
{
QStringLiteral("C"),
QStringLiteral("C#"),
QStringLiteral("D"),
QStringLiteral("D#"),
QStringLiteral("E"),
QStringLiteral("F"),
QStringLiteral("F#"),
QStringLiteral("G"),
QStringLiteral("G#"),
QStringLiteral("A"),
QStringLiteral("A#"),
QStringLiteral("B")
};
const QString KeyStrings[] = {QStringLiteral("C"), QStringLiteral("C#"), QStringLiteral("D"), QStringLiteral("D#"),
QStringLiteral("E"), QStringLiteral("F"), QStringLiteral("F#"), QStringLiteral("G"),
QStringLiteral("G#"), QStringLiteral("A"), QStringLiteral("A#"), QStringLiteral("B")};
const int NaturalKeyNumbers[] =
{
0, 2, 4, 5, 7, 9, 11
};
const int NaturalKeyNumbers[] = {0, 2, 4, 5, 7, 9, 11};
const int SharpKeyNumbers[] =
{
1, 3, 6, 8, 10
};
const int SharpKeyNumbers[] = {1, 3, 6, 8, 10};
KeyboardOctave::KeyboardOctave(int octave, const QString& svgPath, QWidget* parent)
: QSvgWidget(svgPath, parent), m_octave(octave)
{
for (int i = 0; i < 7; ++i)
if (renderer()->elementExists(NaturalKeyNames[i]))
m_natural[i] = renderer()->matrixForElement(NaturalKeyNames[i]).
mapRect(renderer()->boundsOnElement(NaturalKeyNames[i]));
: QSvgWidget(svgPath, parent), m_octave(octave) {
for (int i = 0; i < 7; ++i)
if (renderer()->elementExists(NaturalKeyNames[i]))
m_natural[i] =
renderer()->matrixForElement(NaturalKeyNames[i]).mapRect(renderer()->boundsOnElement(NaturalKeyNames[i]));
for (int i = 0; i < 5; ++i)
if (renderer()->elementExists(SharpKeyNames[i]))
m_sharp[i] = renderer()->matrixForElement(SharpKeyNames[i]).
mapRect(renderer()->boundsOnElement(SharpKeyNames[i]));
for (int i = 0; i < 5; ++i)
if (renderer()->elementExists(SharpKeyNames[i]))
m_sharp[i] =
renderer()->matrixForElement(SharpKeyNames[i]).mapRect(renderer()->boundsOnElement(SharpKeyNames[i]));
/* The parent keyboard manages all mouse events */
setAttribute(Qt::WA_TransparentForMouseEvents);
/* The parent keyboard manages all mouse events */
setAttribute(Qt::WA_TransparentForMouseEvents);
}
int KeyboardOctave::getKey(const QPoint& localPos) const
{
QPointF localPoint = m_widgetToSvg.map(localPos);
for (int i = 0; i < 5; ++i)
if (m_sharp[i].contains(localPoint))
return SharpKeyNumbers[i];
for (int i = 0; i < 7; ++i)
if (m_natural[i].contains(localPoint))
return NaturalKeyNumbers[i];
return -1;
int KeyboardOctave::getKey(const QPoint& localPos) const {
QPointF localPoint = m_widgetToSvg.map(localPos);
for (int i = 0; i < 5; ++i)
if (m_sharp[i].contains(localPoint))
return SharpKeyNumbers[i];
for (int i = 0; i < 7; ++i)
if (m_natural[i].contains(localPoint))
return NaturalKeyNumbers[i];
return -1;
}
void KeyboardOctave::resizeEvent(QResizeEvent *event)
{
m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF());
void KeyboardOctave::resizeEvent(QResizeEvent* event) { m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF()); }
KeyboardWidget::KeyboardWidget(QWidget* parent) : QWidget(parent) {
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
for (int i = 0; i < 10; ++i) {
m_widgets[i] = new KeyboardOctave(i, QStringLiteral(":/bg/keyboard.svg"), this);
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[i]);
}
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[10]);
setLayout(layout);
setMouseTracking(true);
}
KeyboardWidget::KeyboardWidget(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
for (int i = 0; i < 10; ++i)
{
m_widgets[i] = new KeyboardOctave(i, QStringLiteral(":/bg/keyboard.svg"), this);
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[i]);
}
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[10]);
setLayout(layout);
setMouseTracking(true);
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const {
for (KeyboardOctave* oct : m_widgets) {
QPoint localPos = oct->mapFromParent(event->pos());
if (oct->rect().contains(localPos))
return {oct->getOctave(), oct->getKey(localPos)};
}
return {-1, -1};
}
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const
{
for (KeyboardOctave* oct : m_widgets)
{
QPoint localPos = oct->mapFromParent(event->pos());
if (oct->rect().contains(localPos))
return {oct->getOctave(), oct->getKey(localPos)};
}
return {-1, -1};
}
void KeyboardWidget::_startKey(int octave, int key) { emit notePressed(octave * 12 + key); }
void KeyboardWidget::_startKey(int octave, int key)
{
emit notePressed(octave * 12 + key);
}
void KeyboardWidget::_stopKey() { emit noteReleased(); }
void KeyboardWidget::_stopKey()
{
emit noteReleased();
}
void KeyboardWidget::_moveOnKey(int octave, int key)
{
if (m_lastOctave != octave || m_lastKey != key)
{
m_lastOctave = octave;
m_lastKey = key;
if (m_statusFocus)
m_statusFocus->setMessage(QStringLiteral("%1%2 (%3)").
arg(KeyStrings[key]).arg(octave - 1).arg(octave * 12 + key));
if (m_holding)
_startKey(octave, key);
}
}
void KeyboardWidget::_pressOnKey(int octave, int key)
{
_moveOnKey(octave, key);
m_holding = true;
_startKey(octave, key);
}
void KeyboardWidget::mouseMoveEvent(QMouseEvent* event)
{
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_moveOnKey(ok.first, ok.second);
}
void KeyboardWidget::mousePressEvent(QMouseEvent* event)
{
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_pressOnKey(ok.first, ok.second);
}
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event)
{
_stopKey();
m_holding = false;
}
void KeyboardWidget::enterEvent(QEvent* event)
{
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->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)
{
if (m_statusFocus)
m_statusFocus->exit();
void KeyboardWidget::_pressOnKey(int octave, int key) {
_moveOnKey(octave, key);
m_holding = true;
_startKey(octave, key);
}
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::mouseMoveEvent(QMouseEvent* event) {
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_moveOnKey(ok.first, ok.second);
}
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);
}
void KeyboardWidget::mousePressEvent(QMouseEvent* event) {
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_pressOnKey(ok.first, ok.second);
}
KeyboardSlider::KeyboardSlider(QWidget* parent)
: QSlider(parent)
{}
void KeyboardSlider::enterEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->enter();
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event) {
_stopKey();
m_holding = false;
}
void KeyboardSlider::leaveEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->exit();
void KeyboardWidget::enterEvent(QEvent* event) {
if (m_statusFocus)
m_statusFocus->enter();
}
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus)
{
m_statusFocus = statusFocus;
void KeyboardWidget::leaveEvent(QEvent* event) {
if (m_statusFocus)
m_statusFocus->exit();
}
void KeyboardWidget::wheelEvent(QWheelEvent* event) {
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
/* Send wheel event directly to the scroll bar */
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
}
}
void KeyboardWidget::showEvent(QShowEvent* event) {
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget())) {
/* Scroll to C1 */
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
}
}
KeyboardSlider::KeyboardSlider(QWidget* parent) : QSlider(parent) {}
void KeyboardSlider::enterEvent(QEvent* event) {
if (m_statusFocus)
m_statusFocus->enter();
}
void KeyboardSlider::leaveEvent(QEvent* event) {
if (m_statusFocus)
m_statusFocus->exit();
}
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus) {
m_statusFocus = statusFocus;
QString str = stringOfValue(value());
m_statusFocus->setMessage(str);
setToolTip(str);
}
void KeyboardSlider::sliderChange(SliderChange change) {
QSlider::sliderChange(change);
if (m_statusFocus && change == QAbstractSlider::SliderValueChange) {
QString str = stringOfValue(value());
m_statusFocus->setMessage(str);
setToolTip(str);
}
}
void KeyboardSlider::sliderChange(SliderChange change)
{
QSlider::sliderChange(change);
if (m_statusFocus && change == QAbstractSlider::SliderValueChange)
{
QString str = stringOfValue(value());
m_statusFocus->setMessage(str);
setToolTip(str);
}
}
VelocitySlider::VelocitySlider(QWidget* parent)
: KeyboardSlider(parent)
{}
QString VelocitySlider::stringOfValue(int value) const
{
return tr("Velocity: %1").arg(value);
}
ModulationSlider::ModulationSlider(QWidget* parent)
: KeyboardSlider(parent)
{}
QString ModulationSlider::stringOfValue(int value) const
{
return tr("Modulation: %1").arg(value);
}
PitchSlider::PitchSlider(QWidget* parent)
: KeyboardSlider(parent)
{}
QString PitchSlider::stringOfValue(int value) const
{
return tr("Pitch: %1").arg(value / 2048.0, 0, 'g', 2);
}
void PitchSlider::mouseReleaseEvent(QMouseEvent *ev)
{
KeyboardSlider::mouseReleaseEvent(ev);
setValue(0);
VelocitySlider::VelocitySlider(QWidget* parent) : KeyboardSlider(parent) {}
QString VelocitySlider::stringOfValue(int value) const { return tr("Velocity: %1").arg(value); }
ModulationSlider::ModulationSlider(QWidget* parent) : KeyboardSlider(parent) {}
QString ModulationSlider::stringOfValue(int value) const { return tr("Modulation: %1").arg(value); }
PitchSlider::PitchSlider(QWidget* parent) : KeyboardSlider(parent) {}
QString PitchSlider::stringOfValue(int value) const { return tr("Pitch: %1").arg(value / 2048.0, 0, 'g', 2); }
void PitchSlider::mouseReleaseEvent(QMouseEvent* ev) {
KeyboardSlider::mouseReleaseEvent(ev);
setValue(0);
}

View File

@ -15,90 +15,87 @@ extern const int SharpKeyNumbers[5];
class KeyboardWidget;
class KeyboardOctave : public QSvgWidget
{
Q_OBJECT
int m_octave;
QRectF m_natural[7];
QRectF m_sharp[5];
QTransform m_widgetToSvg;
class KeyboardOctave : public QSvgWidget {
Q_OBJECT
int m_octave;
QRectF m_natural[7];
QRectF m_sharp[5];
QTransform m_widgetToSvg;
public:
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
int getOctave() const { return m_octave; }
int getKey(const QPoint& localPos) const;
void resizeEvent(QResizeEvent *event);
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
int getOctave() const { return m_octave; }
int getKey(const QPoint& localPos) const;
void resizeEvent(QResizeEvent* event);
};
class KeyboardWidget : public QWidget
{
Q_OBJECT
KeyboardOctave* m_widgets[11];
StatusBarFocus* m_statusFocus = nullptr;
int m_lastOctave = -1;
int m_lastKey = -1;
bool m_holding = false;
class KeyboardWidget : public QWidget {
Q_OBJECT
KeyboardOctave* m_widgets[11];
StatusBarFocus* m_statusFocus = nullptr;
int m_lastOctave = -1;
int m_lastKey = -1;
bool m_holding = false;
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
void _startKey(int octave, int key);
void _stopKey();
void _moveOnKey(int octave, int key);
void _pressOnKey(int octave, int key);
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
void _startKey(int octave, int key);
void _stopKey();
void _moveOnKey(int octave, int key);
void _pressOnKey(int octave, int key);
public:
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void wheelEvent(QWheelEvent *event);
void showEvent(QShowEvent *event);
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void wheelEvent(QWheelEvent* event);
void showEvent(QShowEvent* event);
signals:
void notePressed(int key);
void noteReleased();
void notePressed(int key);
void noteReleased();
};
class KeyboardSlider : public QSlider
{
Q_OBJECT
class KeyboardSlider : public QSlider {
Q_OBJECT
protected:
StatusBarFocus* m_statusFocus = nullptr;
virtual QString stringOfValue(int value) const = 0;
StatusBarFocus* m_statusFocus = nullptr;
virtual QString stringOfValue(int value) const = 0;
public:
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void setStatusFocus(StatusBarFocus* statusFocus);
void sliderChange(SliderChange change);
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void setStatusFocus(StatusBarFocus* statusFocus);
void sliderChange(SliderChange change);
};
class VelocitySlider : public KeyboardSlider
{
Q_OBJECT
QString stringOfValue(int value) const;
public:
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
class VelocitySlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const;
public:
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
};
class ModulationSlider : public KeyboardSlider
{
Q_OBJECT
QString stringOfValue(int value) const;
class ModulationSlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const;
public:
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
};
class PitchSlider : public KeyboardSlider
{
Q_OBJECT
QString stringOfValue(int value) const;
public:
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent *ev);
void wheelEvent(QWheelEvent* ev) { ev->ignore(); }
};
class PitchSlider : public KeyboardSlider {
Q_OBJECT
QString stringOfValue(int value) const;
public:
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* ev);
void wheelEvent(QWheelEvent* ev) { ev->ignore(); }
};

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -7,84 +7,82 @@
#include <QToolButton>
#include <QStyledItemDelegate>
class SoundMacroDelegate : public BaseObjectDelegate
{
Q_OBJECT
class SoundMacroDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
explicit SoundMacroDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
private slots:
void smIndexChanged();
void smIndexChanged();
};
class LayersModel : public QAbstractTableModel
{
Q_OBJECT
friend class LayersEditor;
friend class SoundMacroDelegate;
friend class LayersTableView;
amuse::ObjToken<ProjectModel::LayersNode> m_node;
class LayersModel : public QAbstractTableModel {
Q_OBJECT
friend class LayersEditor;
friend class SoundMacroDelegate;
friend class LayersTableView;
amuse::ObjToken<ProjectModel::LayersNode> m_node;
public:
explicit LayersModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::LayersNode* node);
void unloadData();
explicit LayersModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::LayersNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
Qt::DropActions supportedDropActions() const;
Qt::DropActions supportedDragActions() const;
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent);
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
Qt::DropActions supportedDropActions() const;
Qt::DropActions supportedDragActions() const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
const QModelIndex& destinationParent, int destinationChild);
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent,
int destinationChild);
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
void _insertRow(int row, const amuse::LayerMapping& data);
amuse::LayerMapping _removeRow(int row);
void _insertRow(int row, const amuse::LayerMapping& data);
amuse::LayerMapping _removeRow(int row);
};
class LayersTableView : public QTableView
{
Q_OBJECT
SoundMacroDelegate m_smDelegate;
RangedValueFactory<-128, 127> m_signedFactory;
RangedValueFactory<0, 127> m_unsignedFactory;
QStyledItemDelegate m_signedDelegate, m_unsignedDelegate;
class LayersTableView : public QTableView {
Q_OBJECT
SoundMacroDelegate m_smDelegate;
RangedValueFactory<-128, 127> m_signedFactory;
RangedValueFactory<0, 127> m_unsignedFactory;
QStyledItemDelegate m_signedDelegate, m_unsignedDelegate;
public:
explicit LayersTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
explicit LayersTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
};
class LayersEditor : public EditorWidget
{
Q_OBJECT
friend class LayersModel;
LayersModel m_model;
LayersTableView m_tableView;
AddRemoveButtons m_addRemoveButtons;
class LayersEditor : public EditorWidget {
Q_OBJECT
friend class LayersModel;
LayersModel m_model;
LayersTableView m_tableView;
AddRemoveButtons m_addRemoveButtons;
public:
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::LayersNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void resizeEvent(QResizeEvent* ev);
AmuseItemEditFlags itemEditFlags() const;
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::LayersNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void resizeEvent(QResizeEvent* ev);
AmuseItemEditFlags itemEditFlags() const;
private slots:
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void itemDeleteAction();
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void itemDeleteAction();
};

View File

@ -1,140 +1,118 @@
#include "MIDIReader.hpp"
#include "MainWindow.hpp"
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock)
: amuse::BooBackendMIDIReader(engine, useLock) {}
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock) : amuse::BooBackendMIDIReader(engine, useLock) {}
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
return;
}
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->keyOff(chan, key, velocity);
return;
}
auto keySearch = m_chanVoxs.find(key);
if (keySearch == m_chanVoxs.cend())
return;
auto keySearch = m_chanVoxs.find(key);
if (keySearch == m_chanVoxs.cend())
return;
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
}
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
return;
}
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->keyOn(chan, key, velocity);
return;
}
if (m_lastVoice && m_lastVoice->isDestroyed())
m_lastVoice.reset();
if (m_lastVoice && m_lastVoice->isDestroyed())
m_lastVoice.reset();
/* If portamento is enabled for voice, pre-empt spawning new voices */
if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice)
{
uint8_t lastNote = lastVoice->getLastNote();
if (lastVoice->doPortamento(key))
{
m_chanVoxs.erase(lastNote);
m_chanVoxs[key] = lastVoice;
return;
}
/* If portamento is enabled for voice, pre-empt spawning new voices */
if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice) {
uint8_t lastNote = lastVoice->getLastNote();
if (lastVoice->doPortamento(key)) {
m_chanVoxs.erase(lastNote);
m_chanVoxs[key] = lastVoice;
return;
}
}
/* Ensure keyoff sent first */
auto keySearch = m_chanVoxs.find(key);
if (keySearch != m_chanVoxs.cend())
{
if (keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
keySearch->second->setPedal(false);
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
}
/* Ensure keyoff sent first */
auto keySearch = m_chanVoxs.find(key);
if (keySearch != m_chanVoxs.cend()) {
if (keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
keySearch->second->setPedal(false);
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
}
amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
if (newVox)
{
m_chanVoxs[key] = newVox;
m_lastVoice = newVox;
}
amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
if (newVox) {
m_chanVoxs[key] = newVox;
m_lastVoice = newVox;
}
}
void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
return;
}
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->setCtrlValue(chan, control, value);
return;
}
if (control == 1)
{
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
}
else if (control == 64)
{
g_MainWindow->setSustain(value >= 0x40);
}
else
{
for (auto& v : m_engine.getActiveVoices())
v->setCtrlValue(control, value);
}
g_MainWindow->m_ctrlVals[control] = value;
if (control == 1) {
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
} else if (control == 64) {
g_MainWindow->setSustain(value >= 0x40);
} else {
for (auto& v : m_engine.getActiveVoices())
v->setCtrlValue(control, value);
}
g_MainWindow->m_ctrlVals[control] = value;
}
void MIDIReader::programChange(uint8_t chan, uint8_t program)
{
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setChanProgram(chan, program);
void MIDIReader::programChange(uint8_t chan, uint8_t program) {
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setChanProgram(chan, program);
}
void MIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch)
{
float pWheel = (pitch - 0x2000) / float(0x2000);
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setPitchWheel(chan, pWheel);
else
g_MainWindow->m_ui.pitchSlider->setValue(int(pWheel * 2048.f));
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch) {
float pWheel = (pitch - 0x2000) / float(0x2000);
if (g_MainWindow->m_interactiveSeq)
g_MainWindow->m_interactiveSeq->setPitchWheel(chan, pWheel);
else
g_MainWindow->m_ui.pitchSlider->setValue(int(pWheel * 2048.f));
}
void MIDIReader::allSoundOff(uint8_t chan)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->kill();
return;
}
void MIDIReader::allSoundOff(uint8_t chan) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->kill();
return;
}
for (auto& v : m_engine.getActiveVoices())
v->kill();
for (auto& v : m_engine.getActiveVoices())
v->kill();
}
void MIDIReader::resetAllControllers(uint8_t /*chan*/) {}
void MIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
void MIDIReader::allNotesOff(uint8_t chan)
{
if (g_MainWindow->m_interactiveSeq)
{
g_MainWindow->m_interactiveSeq->kill();
return;
}
void MIDIReader::allNotesOff(uint8_t chan) {
if (g_MainWindow->m_interactiveSeq) {
g_MainWindow->m_interactiveSeq->kill();
return;
}
for (auto& v : m_engine.getActiveVoices())
v->kill();
for (auto& v : m_engine.getActiveVoices())
v->kill();
}
void MIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
@ -159,11 +137,8 @@ void MIDIReader::stopSeq() {}
void MIDIReader::reset() {}
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine)
: amuse::BooBackendVoiceAllocator(booEngine) {}
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine) : amuse::BooBackendVoiceAllocator(booEngine) {}
std::unique_ptr<amuse::IMIDIReader>
VoiceAllocator::allocateMIDIReader(amuse::Engine& engine)
{
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
std::unique_ptr<amuse::IMIDIReader> VoiceAllocator::allocateMIDIReader(amuse::Engine& engine) {
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
}

View File

@ -5,46 +5,44 @@
#include "amuse/Common.hpp"
#include <unordered_set>
class MIDIReader : public amuse::BooBackendMIDIReader
{
std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
amuse::ObjToken<amuse::Voice> m_lastVoice;
class MIDIReader : public amuse::BooBackendMIDIReader {
std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
amuse::ObjToken<amuse::Voice> m_lastVoice;
public:
MIDIReader(amuse::Engine& engine, bool useLock);
MIDIReader(amuse::Engine& engine, bool useLock);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
void programChange(uint8_t chan, uint8_t program);
void channelPressure(uint8_t chan, uint8_t pressure);
void pitchBend(uint8_t chan, int16_t pitch);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
void programChange(uint8_t chan, uint8_t program);
void channelPressure(uint8_t chan, uint8_t pressure);
void pitchBend(uint8_t chan, int16_t pitch);
void allSoundOff(uint8_t chan);
void resetAllControllers(uint8_t chan);
void localControl(uint8_t chan, bool on);
void allNotesOff(uint8_t chan);
void omniMode(uint8_t chan, bool on);
void polyMode(uint8_t chan, bool on);
void allSoundOff(uint8_t chan);
void resetAllControllers(uint8_t chan);
void localControl(uint8_t chan, bool on);
void allNotesOff(uint8_t chan);
void omniMode(uint8_t chan, bool on);
void polyMode(uint8_t chan, bool on);
void sysex(const void* data, size_t len);
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
void songPositionPointer(uint16_t pointer);
void songSelect(uint8_t song);
void tuneRequest();
void sysex(const void* data, size_t len);
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
void songPositionPointer(uint16_t pointer);
void songSelect(uint8_t song);
void tuneRequest();
void startSeq();
void continueSeq();
void stopSeq();
void startSeq();
void continueSeq();
void stopSeq();
void reset();
void reset();
};
class VoiceAllocator : public amuse::BooBackendVoiceAllocator
{
class VoiceAllocator : public amuse::BooBackendVoiceAllocator {
public:
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine);
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
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 SampleEditor;
enum BackgroundTaskId
{
TaskOpen,
TaskImport,
TaskExport,
TaskReloadSamples
};
enum BackgroundTaskId { TaskOpen, TaskImport, TaskExport, TaskReloadSamples };
class BackgroundTask : public QObject {
Q_OBJECT
int m_id;
std::function<void(BackgroundTask&)> m_task;
UIMessenger m_threadMessenger;
bool m_cancelled = false;
class BackgroundTask : public QObject
{
Q_OBJECT
int m_id;
std::function<void(BackgroundTask&)> m_task;
UIMessenger m_threadMessenger;
bool m_cancelled = false;
public:
explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
: m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
bool isCanceled() const { QCoreApplication::processEvents(); return m_cancelled; }
UIMessenger& uiMessenger() { return m_threadMessenger; }
explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
: m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
bool isCanceled() const {
QCoreApplication::processEvents();
return m_cancelled;
}
UIMessenger& uiMessenger() { return m_threadMessenger; }
signals:
void setMinimum(int minimum);
void setMaximum(int maximum);
void setValue(int value);
void setLabelText(const QString& text);
void finished(int id);
void setMinimum(int minimum);
void setMaximum(int maximum);
void setValue(int value);
void setLabelText(const QString& text);
void finished(int id);
public slots:
void run() { m_task(*this); emit finished(m_id); }
void cancel() { m_cancelled = true; }
void run() {
m_task(*this);
emit finished(m_id);
}
void cancel() { m_cancelled = true; }
};
class TreeDelegate : public QStyledItemDelegate
{
Q_OBJECT
MainWindow& m_window;
public:
explicit TreeDelegate(MainWindow& window, QObject* parent = Q_NULLPTR)
: QStyledItemDelegate(parent), m_window(window) {}
bool editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index);
public slots:
void doExportGroup();
void doFindUsages();
void doCut();
void doCopy();
void doPaste();
void doDuplicate();
void doDelete();
void doRename();
};
class MainWindow : public QMainWindow
{
friend class MIDIReader;
friend class ProjectModel;
friend class GroupNodeUndoCommand;
friend class TreeDelegate;
Q_OBJECT
Ui::MainWindow m_ui;
QAction* m_goBack;
QAction* m_goForward;
QLinkedList<ProjectModel::INode*> m_navList;
QLinkedList<ProjectModel::INode*>::iterator m_navIt;
QAction* m_clearRecentFileAct;
QAction* m_recentFileActs[MaxRecentFiles];
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
QWidget* m_faceSvg;
SongGroupEditor* m_songGroupEditor = nullptr;
SoundGroupEditor* m_soundGroupEditor = nullptr;
SoundMacroEditor* m_soundMacroEditor = nullptr;
ADSREditor* m_adsrEditor = nullptr;
CurveEditor* m_curveEditor = nullptr;
KeymapEditor* m_keymapEditor = nullptr;
LayersEditor* m_layersEditor = nullptr;
SampleEditor* m_sampleEditor = nullptr;
StudioSetupWidget* m_studioSetup = nullptr;
QFileDialog m_openDirectoryDialog;
QFileDialog m_openFileDialog;
QFileDialog m_newFileDialog;
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<VoiceAllocator> m_voxAllocator;
std::unique_ptr<amuse::Engine> m_engine;
amuse::ObjToken<amuse::Voice> m_lastSound;
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
int m_velocity = 90;
float m_pitch = 0.f;
int8_t m_ctrlVals[128] = {};
float m_auxAVol = 0.f;
float m_auxBVol = 0.f;
bool m_uiDisabled = false;
bool m_clipboardAmuseData = false;
QUndoStack* m_undoStack;
QMetaObject::Connection m_cutConn;
QMetaObject::Connection m_copyConn;
QMetaObject::Connection m_pasteConn;
QMetaObject::Connection m_deleteConn;
QMetaObject::Connection m_canEditConn;
BackgroundTask* m_backgroundTask = nullptr;
QProgressDialog* m_backgroundDialog = nullptr;
QThread m_backgroundThread;
uint64_t m_timerFireCount = 0;
int m_peakVoices = 0;
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
void updateWindowTitle();
void updateRecentFileActions();
void updateNavigationButtons();
bool setProjectPath(const QString& path);
void refreshAudioIO();
void refreshMIDIIO();
void timerEvent(QTimerEvent* ev);
void setSustain(bool sustain);
void keyPressEvent(QKeyEvent* ev);
void keyReleaseEvent(QKeyEvent* ev);
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task);
bool _setEditor(EditorWidget* widget, bool appendNav = true);
class TreeDelegate : public QStyledItemDelegate {
Q_OBJECT
MainWindow& m_window;
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);
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 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);
void doExportGroup();
void doFindUsages();
void doCut();
void doCopy();
void doPaste();
void doDuplicate();
void doDelete();
void doRename();
};
class MainWindow : public QMainWindow {
friend class MIDIReader;
friend class ProjectModel;
friend class GroupNodeUndoCommand;
friend class TreeDelegate;
Q_OBJECT
Ui::MainWindow m_ui;
QAction* m_goBack;
QAction* m_goForward;
QLinkedList<ProjectModel::INode*> m_navList;
QLinkedList<ProjectModel::INode*>::iterator m_navIt;
QAction* m_clearRecentFileAct;
QAction* m_recentFileActs[MaxRecentFiles];
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
QWidget* m_faceSvg;
SongGroupEditor* m_songGroupEditor = nullptr;
SoundGroupEditor* m_soundGroupEditor = nullptr;
SoundMacroEditor* m_soundMacroEditor = nullptr;
ADSREditor* m_adsrEditor = nullptr;
CurveEditor* m_curveEditor = nullptr;
KeymapEditor* m_keymapEditor = nullptr;
LayersEditor* m_layersEditor = nullptr;
SampleEditor* m_sampleEditor = nullptr;
StudioSetupWidget* m_studioSetup = nullptr;
QFileDialog m_openDirectoryDialog;
QFileDialog m_openFileDialog;
QFileDialog m_newFileDialog;
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<VoiceAllocator> m_voxAllocator;
std::unique_ptr<amuse::Engine> m_engine;
amuse::ObjToken<amuse::Voice> m_lastSound;
amuse::ObjToken<amuse::Sequencer> m_interactiveSeq;
int m_velocity = 90;
float m_pitch = 0.f;
int8_t m_ctrlVals[128] = {};
float m_auxAVol = 0.f;
float m_auxBVol = 0.f;
bool m_uiDisabled = false;
bool m_clipboardAmuseData = false;
QUndoStack* m_undoStack;
QMetaObject::Connection m_cutConn;
QMetaObject::Connection m_copyConn;
QMetaObject::Connection m_pasteConn;
QMetaObject::Connection m_deleteConn;
QMetaObject::Connection m_canEditConn;
BackgroundTask* m_backgroundTask = nullptr;
QProgressDialog* m_backgroundDialog = nullptr;
QThread m_backgroundThread;
uint64_t m_timerFireCount = 0;
int m_peakVoices = 0;
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
void updateWindowTitle();
void updateRecentFileActions();
void updateNavigationButtons();
bool setProjectPath(const QString& path);
void refreshAudioIO();
void refreshMIDIIO();
void timerEvent(QTimerEvent* ev);
void setSustain(bool sustain);
void keyPressEvent(QKeyEvent* ev);
void keyReleaseEvent(QKeyEvent* ev);
void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task);
bool _setEditor(EditorWidget* widget, bool appendNav = true);
public:
explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow();
bool openProject(const QString& path);
bool openEditor(ProjectModel::SongGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundGroupNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SoundMacroNode* node, bool appendNav = true);
bool openEditor(ProjectModel::ADSRNode* node, bool appendNav = true);
bool openEditor(ProjectModel::CurveNode* node, bool appendNav = true);
bool openEditor(ProjectModel::KeymapNode* node, bool appendNav = true);
bool openEditor(ProjectModel::LayersNode* node, bool appendNav = true);
bool openEditor(ProjectModel::SampleNode* node, bool appendNav = true);
bool openEditor(ProjectModel::INode* node, bool appendNav = true);
void closeEditor();
ProjectModel::INode* getEditorNode() const;
EditorWidget* getEditorWidget() const;
amuse::ObjToken<amuse::Voice> startEditorVoice(uint8_t key, uint8_t vel);
amuse::ObjToken<amuse::Voice> startSFX(amuse::GroupId groupId, amuse::SFXId sfxId);
amuse::ObjToken<amuse::Sequencer> startSong(amuse::GroupId groupId, amuse::SongId songId,
const unsigned char* arrData);
void pushUndoCommand(EditorUndoCommand* cmd);
void updateFocus();
void aboutToDeleteNode(ProjectModel::INode* node);
bool askAboutSave();
void closeEvent(QCloseEvent* ev);
void showEvent(QShowEvent* ev);
QString getGroupName(ProjectModel::GroupNode* group) const;
ProjectModel::GroupNode* getSelectedGroupNode() const;
QString getSelectedGroupName() const;
void _recursiveExpandOutline(const QModelIndex& filterIndex) const;
void recursiveExpandAndSelectOutline(const QModelIndex& index) const;
ProjectModel* projectModel() const { return m_projectModel; }
UIMessenger& uiMessenger() { return m_mainMessenger; }
void setItemEditFlags(AmuseItemEditFlags flags);
void setItemNewEnabled(bool enabled);
AmuseItemEditFlags outlineEditFlags();
bool isUiDisabled() const { return m_uiDisabled; }
void findUsages(ProjectModel::INode* node);
public slots:
void newAction();
void _newAction(const QString& path);
void openAction();
void _openAction(const QString& path);
void openRecentFileAction();
void clearRecentFilesAction();
void saveAction();
void revertAction();
void reloadSampleDataAction();
void importAction();
void _importAction(const QString& path);
void importSongsAction();
void _importSongsAction(const QString& path);
void exportAction();
void importHeadersAction();
void _importHeadersAction(const QString& path);
void exportHeadersAction();
void _exportHeadersAction(const QString& path);
void newSubprojectAction();
void newSFXGroupAction();
void newSongGroupAction();
void newSoundMacroAction();
void newADSRAction();
void newCurveAction();
void newKeymapAction();
void newLayersAction();
void goForward();
void goBack();
void aboutToShowAudioIOMenu();
void aboutToShowMIDIIOMenu();
void setAudioIO();
void setMIDIIO(bool checked);
void aboutAmuseAction();
void aboutQtAction();
void notePressed(int key);
void noteReleased();
void velocityChanged(int vel);
void modulationChanged(int mod);
void pitchChanged(int pitch);
void killSounds();
void fxPressed();
void volumeChanged(int vol);
void auxAChanged(int vol);
void auxBChanged(int vol);
void outlineCutAction();
void outlineCopyAction();
void outlinePasteAction();
void outlineDeleteAction();
void onFocusChanged(QWidget* old, QWidget* now);
void onClipboardChanged();
void outlineItemActivated(const QModelIndex& index);
void onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void onTextSelect();
void onTextDelete();
void cleanChanged(bool clean);
void studioSetupHidden();
void studioSetupShown();
void onBackgroundTaskFinished(int id);
QMessageBox::StandardButton msgInformation(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
int msgQuestion(const QString& title, const QString& text, const QString& button0Text,
const QString& button1Text = QString(), const QString& button2Text = QString(),
int defaultButtonNumber = 0, int escapeButtonNumber = -1);
QMessageBox::StandardButton
msgQuestion(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgWarning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgCritical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
};

View File

@ -3,285 +3,155 @@
#include <QLabel>
#include "amuse/Common.hpp"
static const uint32_t BasicMacro[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07010001, 0x0000FFFF,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t BasicMacro[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF,
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t Looped[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000001, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t Looped[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x38000000,
0xE8030000, 0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopRelease[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001FA00,
0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F500000, 0x00013200,
0x07000000, 0x00003200,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000000, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopRelease[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001FA00, 0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000, 0x0F500000, 0x00013200, 0x07000000, 0x00003200, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopSoftRelease[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00016400,
0x07000000, 0x00006400,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000000, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopSoftRelease[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopSoftReleaseNoClick[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x00010100,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010000, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00016400,
0x07000000, 0x00006400,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000000, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopSoftReleaseNoClick[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010000,
0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000000, 0x00006400, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000000, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LoopADSR[] =
{
0x38000000, 0xC07A1000,
0x0C000000, 0x00000000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x12000000, 0x00000000,
0x07000001, 0x0000E803,
0x38000000, 0xE8030000,
0x07000001, 0x0000E803,
0x00000000, 0x00000000,
static const uint32_t LoopADSR[] = {
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x12000000, 0x00000000, 0x07000001, 0x0000E803,
0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 0x00000000,
};
static const uint32_t LoopADSRSoftRelease[] =
{
0x38000000, 0xC07A1000,
0x0C000000, 0x00000000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00017800,
0x07000001, 0x00007800,
0x12000000, 0x00000000,
0x38000000, 0xE8030000,
0x07000001, 0x0000E803,
0x00000000, 0x00000000,
static const uint32_t LoopADSRSoftRelease[] = {
0x38000000, 0xC07A1000, 0x0C000000, 0x00000000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF, 0x3000D08A, 0x00000000, 0x0F600000, 0x00017800, 0x07000001, 0x00007800,
0x12000000, 0x00000000, 0x38000000, 0xE8030000, 0x07000001, 0x0000E803, 0x00000000, 0x00000000,
};
static const uint32_t LoopHold[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x1C000F01, 0x0001C800,
0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000,
0x0F600000, 0x00016400,
0x07000001, 0x00006400,
0x38000000, 0xE8030000,
0x0F000000, 0x0001E803,
0x07000001, 0x0000E803,
0x11000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LoopHold[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x1C000F01, 0x0001C800, 0x07010001, 0x0000FFFF,
0x3000D08A, 0x00000000, 0x0F600000, 0x00016400, 0x07000001, 0x00006400, 0x38000000, 0xE8030000,
0x0F000000, 0x0001E803, 0x07000001, 0x0000E803, 0x11000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShot[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShot[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000001,
0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShotFixedNote[] =
{
0x38000000, 0xC07A1000,
0x193C0000, 0x00010000,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShotFixedNote[] = {
0x38000000, 0xC07A1000, 0x193C0000, 0x00010000, 0x10000000, 0x00000000,
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShotNoClick[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x00010100,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShotNoClick[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x10000000, 0x00000000,
0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t OneShotFixedNoClick[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x00010100,
0x193C0000, 0x00010000,
0x10000000, 0x00000000,
0x07000001, 0x0000FFFF,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t OneShotFixedNoClick[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x00010100, 0x193C0000, 0x00010000, 0x10000000,
0x00000000, 0x07000001, 0x0000FFFF, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t Bubbles[] =
{
0x38000000, 0xC07A1000,
0x0D600000, 0x00010000,
0x10000000, 0x00000000,
0x1D08E803, 0x00010000,
0x1E0544FD, 0x00010000,
0x0F000000, 0x0001F401,
0x07000000, 0x0000F401,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t Bubbles[] = {
0x38000000, 0xC07A1000, 0x0D600000, 0x00010000, 0x10000000, 0x00000000, 0x1D08E803,
0x00010000, 0x1E0544FD, 0x00010000, 0x0F000000, 0x0001F401, 0x07000000, 0x0000F401,
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t DownTrigger[] =
{
0x38000000, 0xC07A1000,
0x10000000, 0x00000000,
0x07000000, 0x00001200,
0x11000000, 0x00000000,
0x07000000, 0x00000100,
0x18FE0000, 0x00010000,
0x05000000, 0x01000C00,
0x0F000000, 0x00016400,
0x07000001, 0x00006400,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t DownTrigger[] = {
0x38000000, 0xC07A1000, 0x10000000, 0x00000000, 0x07000000, 0x00001200, 0x11000000, 0x00000000,
0x07000000, 0x00000100, 0x18FE0000, 0x00010000, 0x05000000, 0x01000C00, 0x0F000000, 0x00016400,
0x07000001, 0x00006400, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t LongFadeInAndStop[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x0001E803,
0x10000000, 0x00000000,
0x07000001, 0x0000E803,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t LongFadeInAndStop[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x0001E803, 0x10000000, 0x00000000, 0x07000001,
0x0000E803, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t FadeInAndStop[] =
{
0x38000000, 0xC07A1000,
0x147F0000, 0x0001C800,
0x10000000, 0x00000000,
0x07000001, 0x0000C800,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t FadeInAndStop[] = {
0x38000000, 0xC07A1000, 0x147F0000, 0x0001C800, 0x10000000, 0x00000000, 0x07000001,
0x0000C800, 0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t RandomTrigger[] =
{
0x38000000, 0xC07A1000,
0x0F000000, 0x0001F401,
0x195A0000, 0x00010000,
0x10000000, 0x00000000,
0x1750005A, 0x01000000,
0x07000001, 0x00002300,
0x05000000, 0x03001400,
0x11000000, 0x00000000,
0x31000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t RandomTrigger[] = {
0x38000000, 0xC07A1000, 0x0F000000, 0x0001F401, 0x195A0000, 0x00010000, 0x10000000,
0x00000000, 0x1750005A, 0x01000000, 0x07000001, 0x00002300, 0x05000000, 0x03001400,
0x11000000, 0x00000000, 0x31000000, 0x00000000, 0x00000000, 0x00000000,
};
static const uint32_t SimplePlaySample[] =
{
0x10000000, 0x00000000,
0x00000000, 0x00000000,
static const uint32_t SimplePlaySample[] = {
0x10000000,
0x00000000,
0x00000000,
0x00000000,
};
static const SoundMacroTemplateEntry Entries[] =
{
static const SoundMacroTemplateEntry Entries[] = {
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Basic Macro"), sizeof(BasicMacro), BasicMacro},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Looped"), sizeof(Looped), Looped},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Release"), sizeof(LoopRelease), LoopRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release"), sizeof(LoopSoftRelease), LoopSoftRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release No Click"), sizeof(LoopSoftReleaseNoClick), LoopSoftReleaseNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Soft Release No Click"), sizeof(LoopSoftReleaseNoClick),
LoopSoftReleaseNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR"), sizeof(LoopADSR), LoopADSR},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR Soft Release"), sizeof(LoopADSRSoftRelease), LoopADSRSoftRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop ADSR Soft Release"), sizeof(LoopADSRSoftRelease),
LoopADSRSoftRelease},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Loop Hold"), sizeof(LoopHold), LoopHold},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot"), sizeof(OneShot), OneShot},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed Note"), sizeof(OneShotFixedNote), OneShotFixedNote},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot No Click"), sizeof(OneShotNoClick), OneShotNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed No Click"), sizeof(OneShotFixedNoClick), OneShotFixedNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "One-Shot Fixed No Click"), sizeof(OneShotFixedNoClick),
OneShotFixedNoClick},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Bubbles"), sizeof(Bubbles), Bubbles},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Down Trigger"), sizeof(DownTrigger), DownTrigger},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Long Fade in and Stop"), sizeof(LongFadeInAndStop), LongFadeInAndStop},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Fade in and Stop"), sizeof(FadeInAndStop), FadeInAndStop},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Random Trigger"), sizeof(RandomTrigger), RandomTrigger},
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Simple Play Sample"), sizeof(SimplePlaySample), SimplePlaySample}
};
{QT_TRANSLATE_NOOP("NewSoundMacroDialog", "Simple Play Sample"), sizeof(SimplePlaySample), SimplePlaySample}};
const SoundMacroTemplateEntry* NewSoundMacroDialog::getSelectedTemplate() const
{
return &Entries[m_combo.currentIndex()];
const SoundMacroTemplateEntry* NewSoundMacroDialog::getSelectedTemplate() const {
return &Entries[m_combo.currentIndex()];
}
NewSoundMacroDialog::NewSoundMacroDialog(const QString& groupName, QWidget* parent)
: QDialog(parent),
m_le(QString::fromStdString(amuse::SoundMacroId::CurNameDB->generateDefaultName(amuse::NameDB::Type::SoundMacro))),
m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal)
{
setWindowTitle(tr("New Sound Macro"));
: QDialog(parent)
, m_le(QString::fromStdString(amuse::SoundMacroId::CurNameDB->generateDefaultName(amuse::NameDB::Type::SoundMacro)))
, m_buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal) {
setWindowTitle(tr("New Sound Macro"));
int idx = 0;
for (const auto& ent : Entries)
m_combo.addItem(tr(ent.m_name), idx++);
m_combo.setCurrentIndex(0);
int idx = 0;
for (const auto& ent : Entries)
m_combo.addItem(tr(ent.m_name), idx++);
m_combo.setCurrentIndex(0);
QObject::connect(&m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
QObject::connect(&m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
QObject::connect(&m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
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(&m_le);
layout->addWidget(new QLabel(tr("Sound Macro Template")));
layout->addWidget(&m_combo);
layout->addWidget(&m_buttonBox);
layout->addWidget(new QLabel(tr("What should the new macro in %1 be named?").arg(groupName)));
layout->addWidget(&m_le);
layout->addWidget(new QLabel(tr("Sound Macro Template")));
layout->addWidget(&m_combo);
layout->addWidget(&m_buttonBox);
setLayout(layout);
setLayout(layout);
}

View File

@ -5,22 +5,20 @@
#include <QComboBox>
#include <QDialogButtonBox>
struct SoundMacroTemplateEntry
{
const char* m_name;
size_t m_length;
const uint32_t* m_data;
struct SoundMacroTemplateEntry {
const char* m_name;
size_t m_length;
const uint32_t* m_data;
};
class NewSoundMacroDialog : public QDialog
{
Q_OBJECT
QLineEdit m_le;
QComboBox m_combo;
QDialogButtonBox m_buttonBox;
class NewSoundMacroDialog : public QDialog {
Q_OBJECT
QLineEdit m_le;
QComboBox m_combo;
QDialogButtonBox m_buttonBox;
public:
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
QString getName() const { return m_le.text(); }
const SoundMacroTemplateEntry* getSelectedTemplate() const;
explicit NewSoundMacroDialog(const QString& groupName, QWidget* parent = Q_NULLPTR);
QString getName() const { return m_le.text(); }
const SoundMacroTemplateEntry* getSelectedTemplate() const;
};

File diff suppressed because it is too large Load Diff

View File

@ -17,498 +17,451 @@
class ProjectModel;
class EditorUndoCommand;
enum AmuseItemEditFlags
{
AmuseItemNone = 0,
AmuseItemCut = 1,
AmuseItemCopy = 2,
AmuseItemPaste = 4,
AmuseItemDelete = 8,
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
enum AmuseItemEditFlags {
AmuseItemNone = 0,
AmuseItemCut = 1,
AmuseItemCopy = 2,
AmuseItemPaste = 4,
AmuseItemDelete = 8,
AmuseItemNoCut = (AmuseItemCopy | AmuseItemPaste | AmuseItemDelete),
AmuseItemAll = (AmuseItemCut | AmuseItemCopy | AmuseItemPaste | AmuseItemDelete)
};
class OutlineFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
QRegExp m_usageKey;
class OutlineFilterProxyModel : public QSortFilterProxyModel {
Q_OBJECT
QRegExp m_usageKey;
public:
explicit OutlineFilterProxyModel(ProjectModel* source);
explicit OutlineFilterProxyModel(ProjectModel* source);
public slots:
void setFilterRegExp(const QString &pattern);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
void setFilterRegExp(const QString& pattern);
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
};
class NullItemProxyModel : public QIdentityProxyModel
{
Q_OBJECT
class NullItemProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit NullItemProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
explicit NullItemProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
};
class PageObjectProxyModel : public QIdentityProxyModel
{
Q_OBJECT
class PageObjectProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit PageObjectProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const;
explicit PageObjectProxyModel(ProjectModel* source);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent) const;
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex, int role) const;
Qt::ItemFlags flags(const QModelIndex& proxyIndex) const;
};
class ProjectModel : public QAbstractItemModel
{
Q_OBJECT
class ProjectModel : public QAbstractItemModel {
Q_OBJECT
public:
enum class ImportMode
{
Original,
WAVs,
Both
};
enum class ImportMode { Original, WAVs, Both };
struct NameUndoRegistry
{
std::unordered_map<amuse::SongId, std::string> m_songIDs;
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
void registerSongName(amuse::SongId id) const;
void unregisterSongName(amuse::SongId id);
void registerSFXName(amuse::SongId id) const;
void unregisterSFXName(amuse::SongId id);
void clear()
{
m_songIDs.clear();
m_sfxIDs.clear();
}
};
struct NameUndoRegistry {
std::unordered_map<amuse::SongId, std::string> m_songIDs;
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
void registerSongName(amuse::SongId id) const;
void unregisterSongName(amuse::SongId id);
void registerSFXName(amuse::SongId id) const;
void unregisterSFXName(amuse::SongId id);
void clear() {
m_songIDs.clear();
m_sfxIDs.clear();
}
};
private:
QDir m_dir;
OutlineFilterProxyModel m_outlineProxy;
NullItemProxyModel m_nullProxy;
PageObjectProxyModel m_pageObjectProxy;
QDir m_dir;
OutlineFilterProxyModel m_outlineProxy;
NullItemProxyModel m_nullProxy;
PageObjectProxyModel m_pageObjectProxy;
amuse::ProjectDatabase m_projectDatabase;
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>> m_groups;
amuse::ProjectDatabase m_projectDatabase;
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>> m_groups;
struct Song
{
QString m_path;
int m_refCount = 0;
};
std::unordered_map<amuse::SongId, Song> m_midiFiles;
struct Song {
QString m_path;
int m_refCount = 0;
};
std::unordered_map<amuse::SongId, Song> m_midiFiles;
public:
class INode : public amuse::IObj
{
friend class ProjectModel;
virtual void _sortChildren();
public:
enum class Type
{
Null,
Root,
Group, // Top-level group
SongGroup,
SoundGroup,
Collection, // Classified object collection, one of the following:
SoundMacro,
ADSR,
Curve,
Keymap,
Layer,
Sample
};
protected:
QString m_name;
INode* m_parent = nullptr;
int m_row = -1;
std::vector<amuse::IObjToken<INode>> m_children;
amuse::IObjToken<INode> m_nullChild;
public:
virtual ~INode() = default;
INode(const QString& name);
INode(INode* parent) : m_parent(parent), m_row(0)
{
/* ONLY USED BY NULL NODE! */
}
class INode : public amuse::IObj {
friend class ProjectModel;
virtual void _sortChildren();
int childCount() const { return int(m_children.size()); }
INode* child(int row) const
{
if (row == m_children.size())
return nullChild();
return m_children[row].get();
}
INode* nullChild() const { return m_nullChild.get(); }
INode* parent() const { return m_parent; }
int row() const { return m_row; }
void reindexRows(int row)
{
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
(*it)->m_row = row++;
m_nullChild->m_row = row;
}
void insertChild(amuse::ObjToken<INode> n)
{
assert(n->m_parent == nullptr && "Inserting already-parented node");
n->m_parent = this;
int row = hypotheticalIndex(n->name());
m_children.insert(m_children.begin() + row, n.get());
reindexRows(row);
}
amuse::ObjToken<INode> removeChild(INode* n)
{
amuse::ObjToken<INode> ret = n;
int row = ret->row();
assert(ret.get() == m_children.at(row).get() && "Removing non-child from node");
m_children.erase(m_children.begin() + row);
reindexRows(row);
ret->m_parent = nullptr;
ret->m_row = -1;
return ret;
}
void reserve(size_t sz) { m_children.reserve(sz); }
template<class T, class... _Args>
T& makeChild(_Args&&... args)
{
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
insertChild(tok.get());
return static_cast<T&>(*tok);
}
template<class T, class... _Args>
T& _appendChild(_Args&&... args)
{
auto tok = amuse::MakeObj<T>(std::forward<_Args>(args)...);
tok->m_parent = this;
tok->m_row = m_children.size();
m_children.push_back(tok.get());
m_nullChild->m_row = m_children.size();
return static_cast<T&>(*tok);
}
INode* findChild(const QString& name) const
{
int idx = hypotheticalIndex(name);
if (idx >= m_children.size())
return nullptr;
INode* ret = m_children[idx].get();
if (ret->name() == name)
return ret;
return nullptr;
}
bool depthTraverse(const std::function<bool(INode* node)>& func)
{
for (auto& n : m_children)
if (!n->depthTraverse(func))
break;
return func(this);
}
bool oneLevelTraverse(const std::function<bool(INode* node)>& func)
{
for (auto& n : m_children)
if (!func(n.get()))
return false;
return true;
}
const QString& name() const { return m_name; }
virtual int hypotheticalIndex(const QString& name) const;
virtual amuse::NameDB* getNameDb() const { return nullptr; }
virtual Type type() const = 0;
virtual QString text() const = 0;
virtual QIcon icon() const = 0;
virtual Qt::ItemFlags flags() const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; }
virtual AmuseItemEditFlags editFlags() const { return AmuseItemNone; }
virtual void registerNames(const NameUndoRegistry& registry) const {}
virtual void unregisterNames(NameUndoRegistry& registry) const {}
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
};
struct NullNode final : INode
{
NullNode(INode* parent) : INode(parent) {}
Type type() const { return Type::Null; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
};
struct RootNode final : INode
{
RootNode() : INode(QStringLiteral("<root>")) {}
protected:
QString m_name;
INode* m_parent = nullptr;
int m_row = -1;
std::vector<amuse::IObjToken<INode>> m_children;
amuse::IObjToken<INode> m_nullChild;
Type type() const { return Type::Root; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
};
struct CollectionNode;
struct BasePoolObjectNode;
struct GroupNode final : INode
{
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
GroupNode(const QString& name) : INode(name) {}
GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
: INode(it->first), m_it(it) {}
public:
virtual ~INode() = default;
INode(const QString& name);
INode(INode* parent) : m_parent(parent), m_row(0) { /* ONLY USED BY NULL NODE! */
}
int hypotheticalIndex(const QString& name) const;
void _sortChildren();
int childCount() const { return int(m_children.size()); }
INode* child(int row) const {
if (row == m_children.size())
return nullChild();
return m_children[row].get();
}
INode* nullChild() const { return m_nullChild.get(); }
INode* parent() const { return m_parent; }
int row() const { return m_row; }
static QIcon Icon;
Type type() const { return Type::Group; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemNoCut; }
void reindexRows(int row) {
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
(*it)->m_row = row++;
m_nullChild->m_row = row;
}
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) {}
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;
}
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; }
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);
}
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
{
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) {}
bool depthTraverse(const std::function<bool(INode* node)>& func) {
for (auto& n : m_children)
if (!n->depthTraverse(func))
break;
return func(this);
}
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; }
bool oneLevelTraverse(const std::function<bool(INode* node)>& func) {
for (auto& n : m_children)
if (!func(n.get()))
return false;
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
{
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) {}
virtual amuse::NameDB* getNameDb() const { return nullptr; }
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; }
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; }
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) {}
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 TP; }
AmuseItemEditFlags editFlags() const { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
Type type() const { return Type::Null; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
};
struct RootNode final : INode {
RootNode() : INode(QStringLiteral("<root>")) {}
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>;
Type type() const { return Type::Root; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
};
struct CollectionNode;
struct BasePoolObjectNode;
struct GroupNode final : INode {
std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator m_it;
GroupNode(const QString& name) : INode(name) {}
GroupNode(std::unordered_map<QString, std::unique_ptr<amuse::AudioGroupDatabase>>::iterator it)
: INode(it->first), m_it(it) {}
amuse::ObjToken<RootNode> m_root;
int hypotheticalIndex(const QString& name) const;
void _sortChildren();
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);
static QIcon Icon;
Type type() const { return Type::Group; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemNoCut; }
CollectionNode* getCollectionOfType(Type tp) const;
amuse::AudioGroupDatabase* getAudioGroup() const { return m_it->second.get(); }
BasePoolObjectNode* pageObjectNodeOfId(amuse::ObjectId id) const;
};
struct SongGroupNode final : INode {
amuse::GroupId m_id;
amuse::ObjToken<amuse::SongGroupIndex> m_index;
SongGroupNode(const QString& name, amuse::ObjToken<amuse::SongGroupIndex> index) : INode(name), m_index(index) {}
SongGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SongGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
void registerNames(const NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_midiSetups)
registry.registerSongName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_midiSetups)
registry.unregisterSongName(p.first);
}
};
struct SoundGroupNode final : INode {
amuse::GroupId m_id;
amuse::ObjToken<amuse::SFXGroupIndex> m_index;
SoundGroupNode(const QString& name, amuse::ObjToken<amuse::SFXGroupIndex> index) : INode(name), m_index(index) {}
SoundGroupNode(amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_id(id), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SoundGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
AmuseItemEditFlags editFlags() const { return AmuseItemAll; }
amuse::NameDB* getNameDb() const { return amuse::GroupId::CurNameDB; }
void registerNames(const NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->registerPair(text().toUtf8().data(), m_id);
for (auto& p : m_index->m_sfxEntries)
registry.registerSFXName(p.first);
}
void unregisterNames(NameUndoRegistry& registry) const {
amuse::GroupId::CurNameDB->remove(m_id);
for (auto& p : m_index->m_sfxEntries)
registry.unregisterSFXName(p.first);
}
};
struct CollectionNode final : INode {
QIcon m_icon;
Type m_collectionType;
CollectionNode(const QString& name, const QIcon& icon, Type collectionType)
: INode(name), m_icon(icon), m_collectionType(collectionType) {}
Type type() const { return Type::Collection; }
QString text() const { return m_name; }
QIcon icon() const { return m_icon; }
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
Type collectionType() const { return m_collectionType; }
int indexOfId(amuse::ObjectId id) const;
amuse::ObjectId idOfIndex(int idx) const;
BasePoolObjectNode* nodeOfIndex(int idx) const;
BasePoolObjectNode* nodeOfId(amuse::ObjectId id) const;
};
struct BasePoolObjectNode : INode {
amuse::ObjectId m_id;
BasePoolObjectNode(const QString& name) : INode(name) {}
BasePoolObjectNode(amuse::ObjectId id, const QString& name) : INode(name), m_id(id) {}
amuse::ObjectId id() const { return m_id; }
QString text() const { return m_name; }
QIcon icon() const { return {}; }
};
template <class ID, class T, INode::Type TP>
struct PoolObjectNode final : BasePoolObjectNode {
amuse::ObjToken<T> m_obj;
PoolObjectNode(const QString& name, amuse::ObjToken<T> obj) : BasePoolObjectNode(name), m_obj(obj) {}
PoolObjectNode(ID id, amuse::ObjToken<T> obj)
: BasePoolObjectNode(id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
Type type() const { return TP; }
AmuseItemEditFlags editFlags() const { return TP == INode::Type::Sample ? AmuseItemNoCut : AmuseItemAll; }
void registerNames(const NameUndoRegistry& registry) const {
ID::CurNameDB->registerPair(text().toUtf8().data(), m_id);
}
void unregisterNames(NameUndoRegistry& registry) const { ID::CurNameDB->remove(m_id); }
amuse::NameDB* getNameDb() const { return ID::CurNameDB; }
};
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
using KeymapNode = PoolObjectNode<amuse::KeymapId, std::array<amuse::Keymap, 128>, INode::Type::Keymap>;
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
amuse::ObjToken<RootNode> m_root;
bool m_needsReset = false;
void _buildGroupNodeCollections(GroupNode& gn);
void _buildGroupNode(GroupNode& gn, amuse::AudioGroup& group);
void _resetModelData();
void _resetSongRefCount();
QString MakeDedupedSubprojectName(const QString& origName);
static QString MakeDedupedName(const QString& origName, amuse::NameDB* db);
public:
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
bool clearProjectData();
bool openGroupData(const QString& groupName, UIMessenger& messenger);
void openSongsData();
void importSongsData(const QString& path);
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger);
void saveSongsIndex();
bool saveToFile(UIMessenger& messenger);
QStringList getGroupList() const;
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
bool clearProjectData();
bool openGroupData(const QString& groupName, UIMessenger& messenger);
void openSongsData();
void importSongsData(const QString& path);
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data, ImportMode mode,
UIMessenger& messenger);
void saveSongsIndex();
bool saveToFile(UIMessenger& messenger);
QStringList getGroupList() const;
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool importHeader(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool exportHeader(const QString& path, const QString& groupName, bool& yesToAll, UIMessenger& messenger) const;
void updateNodeNames();
bool ensureModelData();
void updateNodeNames();
bool ensureModelData();
QModelIndex proxyCreateIndex(int arow, int acolumn, void *adata) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex index(INode* node) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex& index) const;
INode* node(const QModelIndex& index) const;
GroupNode* getGroupNode(INode* node) const;
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
RootNode* rootNode() const { return m_root.get(); }
QModelIndex proxyCreateIndex(int arow, int acolumn, void* adata) const;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex index(INode* node) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex& index) const;
INode* node(const QModelIndex& index) const;
GroupNode* getGroupNode(INode* node) const;
AmuseItemEditFlags editFlags(const QModelIndex& index) const;
RootNode* rootNode() const { return m_root.get(); }
void _postAddNode(INode* n, const NameUndoRegistry& registry);
void _preDelNode(INode* n, NameUndoRegistry& registry);
void _addNode(GroupNode* node, std::unique_ptr<amuse::AudioGroupDatabase>&& data, const NameUndoRegistry& registry);
std::unique_ptr<amuse::AudioGroupDatabase> _delNode(GroupNode* node, NameUndoRegistry& registry);
GroupNode* newSubproject(const QString& name);
template <class NT, class T>
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name);
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SongGroupNode* newSongGroup(GroupNode* group, const QString& name);
template <class NT, class T>
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name,
const SoundMacroTemplateEntry* templ = nullptr);
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
ADSRNode* newADSR(GroupNode* group, const QString& name);
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
CurveNode* newCurve(GroupNode* group, const QString& name);
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
KeymapNode* newKeymap(GroupNode* group, const QString& name);
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
LayersNode* newLayers(GroupNode* group, const QString& name);
void _renameNode(INode* node, const QString& name);
void _postAddNode(INode* n, const NameUndoRegistry& registry);
void _preDelNode(INode* n, NameUndoRegistry& registry);
void _addNode(GroupNode* node, std::unique_ptr<amuse::AudioGroupDatabase>&& data, const NameUndoRegistry& registry);
std::unique_ptr<amuse::AudioGroupDatabase> _delNode(GroupNode* node, NameUndoRegistry& registry);
GroupNode* newSubproject(const QString& name);
template <class NT, class T>
void _addGroupNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delGroupNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundGroupNode* newSoundGroup(GroupNode* group, const QString& name);
void _addNode(SongGroupNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SongGroupNode* node, GroupNode* parent, NameUndoRegistry& registry);
SongGroupNode* newSongGroup(GroupNode* group, const QString& name);
template <class NT, class T>
void _addPoolNode(NT* node, GroupNode* parent, const NameUndoRegistry& registry, T& container);
template <class NT, class T>
void _delPoolNode(NT* node, GroupNode* parent, NameUndoRegistry& registry, T& container);
void _addNode(SoundMacroNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(SoundMacroNode* node, GroupNode* parent, NameUndoRegistry& registry);
SoundMacroNode* newSoundMacro(GroupNode* group, const QString& name, const SoundMacroTemplateEntry* templ = nullptr);
void _addNode(ADSRNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(ADSRNode* node, GroupNode* parent, NameUndoRegistry& registry);
ADSRNode* newADSR(GroupNode* group, const QString& name);
void _addNode(CurveNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(CurveNode* node, GroupNode* parent, NameUndoRegistry& registry);
CurveNode* newCurve(GroupNode* group, const QString& name);
void _addNode(KeymapNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(KeymapNode* node, GroupNode* parent, NameUndoRegistry& registry);
KeymapNode* newKeymap(GroupNode* group, const QString& name);
void _addNode(LayersNode* node, GroupNode* parent, const NameUndoRegistry& registry);
void _delNode(LayersNode* node, GroupNode* parent, NameUndoRegistry& registry);
LayersNode* newLayers(GroupNode* group, const QString& name);
void _renameNode(INode* node, const QString& name);
template <class NT>
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
template <class NT>
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
template <class NT>
EditorUndoCommand* readMimeYAML(athena::io::YAMLDocReader& r, const QString& name, GroupNode* gn);
template <class NT>
void loadMimeData(const QMimeData* data, const QString& mimeType, GroupNode* gn);
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList& indexes) const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList& indexes) const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
void cut(const QModelIndex& index);
void copy(const QModelIndex& index);
void paste(const QModelIndex& index);
QModelIndex duplicate(const QModelIndex& index);
void del(const QModelIndex& index);
void cut(const QModelIndex& index);
void copy(const QModelIndex& index);
void paste(const QModelIndex& index);
QModelIndex duplicate(const QModelIndex& index);
void del(const QModelIndex& index);
const QDir& dir() const { return m_dir; }
QString path() const { return m_dir.path(); }
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
const QDir& dir() const { return m_dir; }
QString path() const { return m_dir.path(); }
OutlineFilterProxyModel* getOutlineProxy() { return &m_outlineProxy; }
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
QString getMIDIPathOfSong(amuse::SongId id) const;
void setMIDIPathOfSong(amuse::SongId id, const QString& path);
std::pair<amuse::SongId, std::string> bootstrapSongId();
void allocateSongId(amuse::SongId id, std::string_view name);
void deallocateSongId(amuse::SongId oldId);
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
QString getMIDIPathOfSong(amuse::SongId id) const;
void setMIDIPathOfSong(amuse::SongId id, const QString& path);
std::pair<amuse::SongId, std::string> bootstrapSongId();
void allocateSongId(amuse::SongId id, std::string_view name);
void deallocateSongId(amuse::SongId oldId);
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
void setIdDatabases(INode* context) const;
void setIdDatabases(INode* context) const;
};

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -16,284 +16,274 @@
class SetupTableView;
class PageObjectDelegate : public BaseObjectDelegate
{
Q_OBJECT
class PageObjectDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
explicit PageObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
private slots:
void objIndexChanged();
void objIndexChanged();
};
class MIDIFileFieldWidget : public QWidget
{
Q_OBJECT
QLineEdit m_le;
QPushButton m_button;
QFileDialog m_dialog;
class MIDIFileFieldWidget : public QWidget {
Q_OBJECT
QLineEdit m_le;
QPushButton m_button;
QFileDialog m_dialog;
public:
explicit MIDIFileFieldWidget(QWidget* parent = Q_NULLPTR);
QString path() const { return m_le.text(); }
void setPath(const QString& path) { m_le.setText(path); }
explicit MIDIFileFieldWidget(QWidget* parent = Q_NULLPTR);
QString path() const { return m_le.text(); }
void setPath(const QString& path) { m_le.setText(path); }
public slots:
void buttonPressed();
void fileDialogOpened(const QString& path);
void buttonPressed();
void fileDialogOpened(const QString& path);
signals:
void pathChanged();
void pathChanged();
};
class MIDIFileDelegate : public QStyledItemDelegate
{
Q_OBJECT
QFileDialog m_fileDialogMid, m_fileDialogSng;
std::vector<uint8_t> m_exportData;
class MIDIFileDelegate : public QStyledItemDelegate {
Q_OBJECT
QFileDialog m_fileDialogMid, m_fileDialogSng;
std::vector<uint8_t> m_exportData;
public:
explicit MIDIFileDelegate(SetupTableView* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void destroyEditor(QWidget *editor, const QModelIndex &index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
bool editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option, const QModelIndex &index);
explicit MIDIFileDelegate(SetupTableView* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void destroyEditor(QWidget* editor, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index);
private slots:
void doExportMIDI();
void _doExportMIDI(const QString& path);
void doExportSNG();
void _doExportSNG(const QString& path);
void doExportMIDI();
void _doExportMIDI(const QString& path);
void doExportSNG();
void _doExportSNG(const QString& path);
public slots:
void pathChanged();
void pathChanged();
};
class PageModel : public QAbstractTableModel
{
Q_OBJECT
friend class SongGroupEditor;
friend class PageObjectDelegate;
friend class PageTableView;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator
{
using ItTp = std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const { return m_it->first < other.m_it->first; }
bool operator<(uint8_t other) const { return m_it->first < other; }
};
std::vector<Iterator> m_sorted;
bool m_drum;
std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfProgram(uint8_t prog) const;
int _hypotheticalIndexOfProgram(uint8_t prog) const;
class PageModel : public QAbstractTableModel {
Q_OBJECT
friend class SongGroupEditor;
friend class PageObjectDelegate;
friend class PageTableView;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator {
using ItTp = std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const { return m_it->first < other.m_it->first; }
bool operator<(uint8_t other) const { return m_it->first < other; }
};
std::vector<Iterator> m_sorted;
bool m_drum;
std::unordered_map<uint8_t, amuse::SongGroupIndex::PageEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfProgram(uint8_t prog) const;
int _hypotheticalIndexOfProgram(uint8_t prog) const;
public:
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
explicit PageModel(bool drum, QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
};
class SetupListModel : public QAbstractTableModel
{
Q_OBJECT
friend class SongGroupEditor;
friend class MIDIFileDelegate;
friend class SetupTableView;
friend class SetupModel;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator
{
using ItTp = std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const
{
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const
{
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const
{
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSong(amuse::SongId id) const;
int _hypotheticalIndexOfSong(const std::string& songName) const;
class SetupListModel : public QAbstractTableModel {
Q_OBJECT
friend class SongGroupEditor;
friend class MIDIFileDelegate;
friend class SetupTableView;
friend class SetupModel;
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
struct Iterator {
using ItTp = std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() { return m_it.operator->(); }
bool operator<(const Iterator& other) const {
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const {
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SongId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const {
return amuse::SongId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSong(amuse::SongId id) const;
int _hypotheticalIndexOfSong(const std::string& songName) const;
public:
explicit SetupListModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
explicit SetupListModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SongGroupNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
int _insertRow(
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data);
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>
_removeRow(amuse::SongId id);
int _insertRow(std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data);
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>> _removeRow(amuse::SongId id);
};
class SetupModel : public QAbstractTableModel
{
Q_OBJECT
friend class SongGroupEditor;
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
public:
explicit SetupModel(QObject* parent = Q_NULLPTR);
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
void unloadData();
class SetupModel : public QAbstractTableModel {
Q_OBJECT
friend class SongGroupEditor;
std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* m_data = nullptr;
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;
public:
explicit SetupModel(QObject* parent = Q_NULLPTR);
void loadData(std::pair<const amuse::SongId, std::array<amuse::SongGroupIndex::MIDISetup, 16>>* data);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
};
class PageTableView : public QTableView
{
Q_OBJECT
PageObjectDelegate m_poDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
class PageTableView : public QTableView {
Q_OBJECT
PageObjectDelegate m_poDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate, m_255Delegate;
public:
explicit PageTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
explicit PageTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
};
class SetupTableView : public QSplitter
{
Q_OBJECT
friend class SongGroupEditor;
friend class SetupRowUndoCommand;
QTableView* m_listView;
QTableView* m_tableView;
MIDIFileDelegate m_midiDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate;
class SetupTableView : public QSplitter {
Q_OBJECT
friend class SongGroupEditor;
friend class SetupRowUndoCommand;
QTableView* m_listView;
QTableView* m_tableView;
MIDIFileDelegate m_midiDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<1, 128> m_128Factory;
QStyledItemDelegate m_127Delegate, m_128Delegate;
public:
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
void deleteSelection();
void showEvent(QShowEvent* event);
explicit SetupTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* list, QAbstractItemModel* table);
void deleteSelection();
void showEvent(QShowEvent* event);
};
class ColoredTabBarStyle : public QProxyStyle
{
class ColoredTabBarStyle : public QProxyStyle {
public:
using QProxyStyle::QProxyStyle;
void drawControl(QStyle::ControlElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget = nullptr) const;
using QProxyStyle::QProxyStyle;
void drawControl(QStyle::ControlElement element, const QStyleOption* option, QPainter* painter,
const QWidget* widget = nullptr) const;
};
class ColoredTabBar : public QTabBar
{
Q_OBJECT
class ColoredTabBar : public QTabBar {
Q_OBJECT
public:
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
};
class ColoredTabWidget : public QTabWidget
{
Q_OBJECT
ColoredTabBar m_tabBar;
class ColoredTabWidget : public QTabWidget {
Q_OBJECT
ColoredTabBar m_tabBar;
public:
explicit ColoredTabWidget(QWidget* parent = Q_NULLPTR);
explicit ColoredTabWidget(QWidget* parent = Q_NULLPTR);
};
class MIDIPlayerWidget : public QWidget
{
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SongId m_songId;
QString m_path;
std::vector<uint8_t> m_arrData;
amuse::ObjToken<amuse::Sequencer> m_seq;
class MIDIPlayerWidget : public QWidget {
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SongId m_songId;
QString m_path;
std::vector<uint8_t> m_arrData;
amuse::ObjToken<amuse::Sequencer> m_seq;
public:
explicit MIDIPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SongId id,
const QString& path, QWidget* parent = Q_NULLPTR);
~MIDIPlayerWidget();
amuse::SongId songId() const { return m_songId; }
amuse::Sequencer* sequencer() const { return m_seq.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
explicit MIDIPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SongId id, const QString& path,
QWidget* parent = Q_NULLPTR);
~MIDIPlayerWidget();
amuse::SongId songId() const { return m_songId; }
amuse::Sequencer* sequencer() const { return m_seq.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
public slots:
void clicked();
void clicked();
};
class SongGroupEditor : public EditorWidget
{
Q_OBJECT
friend class SetupModel;
PageModel m_normPages;
PageModel m_drumPages;
SetupListModel m_setupList;
SetupModel m_setup;
PageTableView* m_normTable;
PageTableView* m_drumTable;
SetupTableView* m_setupTable;
ColoredTabWidget m_tabs;
AddRemoveButtons m_addRemoveButtons;
class SongGroupEditor : public EditorWidget {
Q_OBJECT
friend class SetupModel;
PageModel m_normPages;
PageModel m_drumPages;
SetupListModel m_setupList;
SetupModel m_setup;
PageTableView* m_normTable;
PageTableView* m_drumTable;
SetupTableView* m_setupTable;
ColoredTabWidget m_tabs;
AddRemoveButtons m_addRemoveButtons;
public:
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SongGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
AmuseItemEditFlags itemEditFlags() const;
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SongGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSetupListView() const { return m_setupTable->m_listView; }
AmuseItemEditFlags itemEditFlags() const;
private slots:
void doAdd();
void doSelectionChanged();
void doSetupSelectionChanged();
void currentTabChanged(int idx);
void normRowsInserted(const QModelIndex& parent, int first, int last);
void normRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void drumRowsInserted(const QModelIndex& parent, int first, int last);
void drumRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsInserted(const QModelIndex& parent, int first, int last);
void setupRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
void setupModelAboutToBeReset();
void setupDataChanged();
void itemDeleteAction();
void doAdd();
void doSelectionChanged();
void doSetupSelectionChanged();
void currentTabChanged(int idx);
void normRowsInserted(const QModelIndex& parent, int first, int last);
void normRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void drumRowsInserted(const QModelIndex& parent, int first, int last);
void drumRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsInserted(const QModelIndex& parent, int first, int last);
void setupRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void setupRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
void setupModelAboutToBeReset();
void setupDataChanged();
void itemDeleteAction();
};

File diff suppressed because it is too large Load Diff

View File

@ -5,127 +5,121 @@
#include <QTableView>
#include "amuse/Voice.hpp"
class SFXObjectDelegate : public BaseObjectDelegate
{
Q_OBJECT
class SFXObjectDelegate : public BaseObjectDelegate {
Q_OBJECT
protected:
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
ProjectModel::INode* getNode(const QAbstractItemModel* model, const QModelIndex& index) const;
public:
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
explicit SFXObjectDelegate(QObject* parent = Q_NULLPTR);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
void setEditorData(QWidget* editor, const QModelIndex& index) const;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const;
private slots:
void objIndexChanged();
void objIndexChanged();
};
class SFXModel : public QAbstractTableModel
{
Q_OBJECT
friend class SoundGroupEditor;
friend class SFXObjectDelegate;
friend class SFXTableView;
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
struct Iterator
{
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() const { return m_it.operator->(); }
bool operator<(const Iterator& other) const
{
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const
{
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const
{
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSFX(amuse::SFXId sfx) const;
int _hypotheticalIndexOfSFX(const std::string& sfxName) const;
class SFXModel : public QAbstractTableModel {
Q_OBJECT
friend class SoundGroupEditor;
friend class SFXObjectDelegate;
friend class SFXTableView;
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
struct Iterator {
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
ItTp m_it;
Iterator(ItTp it) : m_it(it) {}
ItTp::pointer operator->() const { return m_it.operator->(); }
bool operator<(const Iterator& other) const {
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other.m_it->first);
}
bool operator<(amuse::SongId other) const {
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
amuse::SFXId::CurNameDB->resolveNameFromId(other);
}
bool operator<(const std::string& name) const {
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) < name;
}
};
std::vector<Iterator> m_sorted;
std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>& _getMap() const;
void _buildSortedList();
QModelIndex _indexOfSFX(amuse::SFXId sfx) const;
int _hypotheticalIndexOfSFX(const std::string& sfxName) const;
public:
explicit SFXModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
explicit SFXModel(QObject* parent = Q_NULLPTR);
void loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
int _insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data);
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> _removeRow(amuse::SFXId sfx);
int _insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data);
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> _removeRow(amuse::SFXId sfx);
};
class SFXTableView : public QTableView
{
Q_OBJECT
SFXObjectDelegate m_sfxDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_255Delegate;
class SFXTableView : public QTableView {
Q_OBJECT
SFXObjectDelegate m_sfxDelegate;
RangedValueFactory<0, 127> m_127Factory;
RangedValueFactory<0, 255> m_255Factory;
QStyledItemDelegate m_127Delegate, m_255Delegate;
public:
explicit SFXTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
explicit SFXTableView(QWidget* parent = Q_NULLPTR);
void setModel(QAbstractItemModel* model);
void deleteSelection();
};
class SFXPlayerWidget : public QWidget
{
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SFXId m_sfxId;
amuse::ObjToken<amuse::Voice> m_vox;
class SFXPlayerWidget : public QWidget {
Q_OBJECT
QAction m_playAction;
QToolButton m_button;
QModelIndex m_index;
amuse::GroupId m_groupId;
amuse::SFXId m_sfxId;
amuse::ObjToken<amuse::Voice> m_vox;
public:
explicit SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id,
QWidget* parent = Q_NULLPTR);
~SFXPlayerWidget();
amuse::SongId sfxId() const { return m_sfxId; }
amuse::Voice* voice() const { return m_vox.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
explicit SFXPlayerWidget(QModelIndex index, amuse::GroupId gid, amuse::SFXId id, QWidget* parent = Q_NULLPTR);
~SFXPlayerWidget();
amuse::SongId sfxId() const { return m_sfxId; }
amuse::Voice* voice() const { return m_vox.get(); }
void stopped();
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
public slots:
void clicked();
void clicked();
};
class SoundGroupEditor : public EditorWidget
{
Q_OBJECT
SFXModel m_sfxs;
SFXTableView* m_sfxTable;
AddRemoveButtons m_addRemoveButtons;
class SoundGroupEditor : public EditorWidget {
Q_OBJECT
SFXModel m_sfxs;
SFXTableView* m_sfxTable;
AddRemoveButtons m_addRemoveButtons;
public:
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSFXListView() const { return m_sfxTable; }
AmuseItemEditFlags itemEditFlags() const;
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundGroupNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void setEditorEnabled(bool en) {}
void resizeEvent(QResizeEvent* ev);
QTableView* getSFXListView() const { return m_sfxTable; }
AmuseItemEditFlags itemEditFlags() const;
private slots:
void rowsInserted(const QModelIndex &parent, int first, int last);
void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
void doAdd();
void doSelectionChanged();
void sfxDataChanged();
void itemDeleteAction();
void rowsInserted(const QModelIndex& parent, int first, int last);
void rowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
void doAdd();
void doSelectionChanged();
void sfxDataChanged();
void itemDeleteAction();
};

File diff suppressed because it is too large Load Diff

View File

@ -16,173 +16,170 @@ class SoundMacroEditor;
class SoundMacroListing;
class CatalogueItem;
class TargetButton : public QPushButton
{
Q_OBJECT
class TargetButton : public QPushButton {
Q_OBJECT
public:
explicit TargetButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
explicit TargetButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
};
class FieldSoundMacroStep : public QWidget
{
friend class CommandWidget;
Q_OBJECT
FieldProjectNode* m_macroField;
FieldSpinBox m_spinBox;
TargetButton m_targetButton;
SoundMacroEditor* getEditor() const;
SoundMacroListing* getListing() const;
class FieldSoundMacroStep : public QWidget {
friend class CommandWidget;
Q_OBJECT
FieldProjectNode* m_macroField;
FieldSpinBox m_spinBox;
TargetButton m_targetButton;
SoundMacroEditor* getEditor() const;
SoundMacroListing* getListing() const;
signals:
void valueChanged(int);
void valueChanged(int);
public slots:
void targetPressed();
void updateMacroField();
void targetPressed();
void updateMacroField();
public:
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
~FieldSoundMacroStep();
void setIndex(int index);
void cancel();
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
~FieldSoundMacroStep();
void setIndex(int index);
void cancel();
};
class CommandWidget : public QWidget
{
Q_OBJECT
friend class SoundMacroListing;
QFont m_numberFont;
QLabel m_titleLabel;
ListingDeleteButton m_deleteButton;
QStaticText m_numberText;
int m_index = -1;
amuse::SoundMacro::ICmd* m_cmd;
const amuse::SoundMacro::CmdIntrospection* m_introspection;
FieldSoundMacroStep* m_stepField = nullptr;
void setIndex(int index);
SoundMacroListing* getParent() const;
class CommandWidget : public QWidget {
Q_OBJECT
friend class SoundMacroListing;
QFont m_numberFont;
QLabel m_titleLabel;
ListingDeleteButton m_deleteButton;
QStaticText m_numberText;
int m_index = -1;
amuse::SoundMacro::ICmd* m_cmd;
const amuse::SoundMacro::CmdIntrospection* m_introspection;
FieldSoundMacroStep* m_stepField = nullptr;
void setIndex(int index);
SoundMacroListing* getParent() const;
private slots:
void boolChanged(int);
void numChanged(int);
void nodeChanged(int);
void deleteClicked();
void boolChanged(int);
void numChanged(int);
void nodeChanged(int);
void deleteClicked();
private:
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
public:
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
CommandWidget(QWidget* parent, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
void paintEvent(QPaintEvent* event);
QString getText() const { return m_titleLabel.text(); }
CommandWidget(QWidget* parent, amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
CommandWidget(QWidget* parent, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
void paintEvent(QPaintEvent* event);
QString getText() const { return m_titleLabel.text(); }
};
class CommandWidgetContainer : public QWidget
{
Q_OBJECT
friend class SoundMacroListing;
CommandWidget* m_commandWidget;
QPropertyAnimation* m_animation = nullptr;
void animateOpen();
void animateClosed();
void snapOpen();
void snapClosed();
class CommandWidgetContainer : public QWidget {
Q_OBJECT
friend class SoundMacroListing;
CommandWidget* m_commandWidget;
QPropertyAnimation* m_animation = nullptr;
void animateOpen();
void animateClosed();
void snapOpen();
void snapClosed();
private slots:
void animationDestroyed();
void animationDestroyed();
public:
template <class..._Args>
CommandWidgetContainer(QWidget* parent, _Args&&... args);
template <class... _Args>
CommandWidgetContainer(QWidget* parent, _Args&&... args);
};
class SoundMacroListing : public QWidget
{
Q_OBJECT
friend class CommandWidget;
friend class SoundMacroEditor;
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
QVBoxLayout* m_layout;
QLayoutItem* m_dragItem = nullptr;
int m_origIdx = -1;
int m_dragOpenIdx = -1;
CommandWidgetContainer* m_prevDragOpen = nullptr;
int m_autoscrollTimer = -1;
int m_autoscrollDelta = 0;
QWidget* m_autoscrollSource = nullptr;
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
void stopAutoscroll();
bool beginDrag(CommandWidget* widget);
void endDrag();
void cancelDrag();
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
void moveDrag(CommandWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
void insertDragout();
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
void deleteCommand(int index);
void reindex();
void clear();
class SoundMacroListing : public QWidget {
Q_OBJECT
friend class CommandWidget;
friend class SoundMacroEditor;
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
QVBoxLayout* m_layout;
QLayoutItem* m_dragItem = nullptr;
int m_origIdx = -1;
int m_dragOpenIdx = -1;
CommandWidgetContainer* m_prevDragOpen = nullptr;
int m_autoscrollTimer = -1;
int m_autoscrollDelta = 0;
QWidget* m_autoscrollSource = nullptr;
QMouseEvent m_autoscrollEvent = {{}, {}, {}, {}, {}};
void startAutoscroll(QWidget* source, QMouseEvent* event, int delta);
void stopAutoscroll();
bool beginDrag(CommandWidget* widget);
void endDrag();
void cancelDrag();
void _moveDrag(int hoverIdx, const QPoint& pt, QWidget* source, QMouseEvent* event);
void moveDrag(CommandWidget* widget, const QPoint& pt, QWidget* source, QMouseEvent* event);
int moveInsertDrag(const QPoint& pt, QWidget* source, QMouseEvent* event);
void insertDragout();
void insert(amuse::SoundMacro::CmdOp op, const QString& text);
void deleteCommand(int index);
void reindex();
void clear();
public:
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void timerEvent(QTimerEvent* event);
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void timerEvent(QTimerEvent* event);
};
class CatalogueItem : public QWidget
{
Q_OBJECT
amuse::SoundMacro::CmdOp m_op;
QLabel m_iconLab;
QLabel m_label;
class CatalogueItem : public QWidget {
Q_OBJECT
amuse::SoundMacro::CmdOp m_op;
QLabel m_iconLab;
QLabel m_label;
public:
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name,
const QString& doc, QWidget* parent = Q_NULLPTR);
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
QString getText() const { return m_label.text(); }
explicit CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name, const QString& doc,
QWidget* parent = Q_NULLPTR);
explicit CatalogueItem(const CatalogueItem& other, QWidget* parent = Q_NULLPTR);
amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; }
QString getText() const { return m_label.text(); }
};
class SoundMacroCatalogue : public QTreeWidget
{
Q_OBJECT
class SoundMacroCatalogue : public QTreeWidget {
Q_OBJECT
public:
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
};
class SoundMacroEditor : public EditorWidget
{
Q_OBJECT
friend class SoundMacroCatalogue;
friend class FieldSoundMacroStep;
QSplitter* m_splitter;
SoundMacroListing* m_listing;
SoundMacroCatalogue* m_catalogue;
CommandWidget* m_draggedCmd = nullptr;
CatalogueItem* m_draggedItem = nullptr;
FieldSoundMacroStep* m_targetField = nullptr;
QPoint m_draggedPt;
int m_dragInsertIdx = -1;
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
void beginStepTarget(FieldSoundMacroStep* stepField);
void endStepTarget();
public:
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
class SoundMacroEditor : public EditorWidget {
Q_OBJECT
friend class SoundMacroCatalogue;
friend class FieldSoundMacroStep;
QSplitter* m_splitter;
SoundMacroListing* m_listing;
SoundMacroCatalogue* m_catalogue;
CommandWidget* m_draggedCmd = nullptr;
CatalogueItem* m_draggedItem = nullptr;
FieldSoundMacroStep* m_targetField = nullptr;
QPoint m_draggedPt;
int m_dragInsertIdx = -1;
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
void beginStepTarget(FieldSoundMacroStep* stepField);
void endStepTarget();
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event);
public:
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SoundMacroNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event);
public slots:
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
void catalogueDoubleClicked(QTreeWidgetItem* item, int column);
};

View File

@ -1,113 +1,97 @@
#include "StatusBarWidget.hpp"
FXButton::FXButton(QWidget* parent)
: QPushButton(parent)
{
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
FXButton::FXButton(QWidget* parent) : QPushButton(parent) {
setIcon(QIcon(QStringLiteral(":/icons/IconFX.svg")));
setToolTip(tr("Access studio setup window for experimenting with audio effects"));
}
StatusBarWidget::StatusBarWidget(QWidget* parent)
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal),
m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal)
{
addWidget(&m_normalMessage);
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
m_killButton.setVisible(false);
m_killButton.setToolTip(tr("Immediately kill active voices"));
m_voiceCount.setVisible(false);
m_volumeIcons[0] = QIcon(QStringLiteral(":/icons/IconVolume0.svg"));
m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1.svg"));
m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2.svg"));
m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3.svg"));
m_aIcon.setFixedSize(16, 16);
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
QString aTip = tr("Aux A send level for all voices");
m_aIcon.setToolTip(aTip);
m_aSlider.setRange(0, 100);
m_aSlider.setFixedWidth(100);
m_aSlider.setToolTip(aTip);
m_bIcon.setFixedSize(16, 16);
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
QString bTip = tr("Aux B send level for all voices");
m_bIcon.setToolTip(bTip);
m_bSlider.setRange(0, 100);
m_bSlider.setFixedWidth(100);
m_bSlider.setToolTip(bTip);
m_volumeIcon.setFixedSize(16, 16);
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
QString volTip = tr("Master volume level");
m_volumeIcon.setToolTip(volTip);
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
m_volumeSlider.setRange(0, 100);
m_volumeSlider.setFixedWidth(100);
m_volumeSlider.setToolTip(volTip);
addPermanentWidget(&m_voiceCount);
addPermanentWidget(&m_killButton);
addPermanentWidget(&m_fxButton);
addPermanentWidget(&m_aIcon);
addPermanentWidget(&m_aSlider);
addPermanentWidget(&m_bIcon);
addPermanentWidget(&m_bSlider);
addPermanentWidget(&m_volumeIcon);
addPermanentWidget(&m_volumeSlider);
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal), m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal) {
addWidget(&m_normalMessage);
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
m_killButton.setVisible(false);
m_killButton.setToolTip(tr("Immediately kill active voices"));
m_voiceCount.setVisible(false);
m_volumeIcons[0] = QIcon(QStringLiteral(":/icons/IconVolume0.svg"));
m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1.svg"));
m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2.svg"));
m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3.svg"));
m_aIcon.setFixedSize(16, 16);
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
QString aTip = tr("Aux A send level for all voices");
m_aIcon.setToolTip(aTip);
m_aSlider.setRange(0, 100);
m_aSlider.setFixedWidth(100);
m_aSlider.setToolTip(aTip);
m_bIcon.setFixedSize(16, 16);
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
QString bTip = tr("Aux B send level for all voices");
m_bIcon.setToolTip(bTip);
m_bSlider.setRange(0, 100);
m_bSlider.setFixedWidth(100);
m_bSlider.setToolTip(bTip);
m_volumeIcon.setFixedSize(16, 16);
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
QString volTip = tr("Master volume level");
m_volumeIcon.setToolTip(volTip);
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
m_volumeSlider.setRange(0, 100);
m_volumeSlider.setFixedWidth(100);
m_volumeSlider.setToolTip(volTip);
addPermanentWidget(&m_voiceCount);
addPermanentWidget(&m_killButton);
addPermanentWidget(&m_fxButton);
addPermanentWidget(&m_aIcon);
addPermanentWidget(&m_aSlider);
addPermanentWidget(&m_bIcon);
addPermanentWidget(&m_bSlider);
addPermanentWidget(&m_volumeIcon);
addPermanentWidget(&m_volumeSlider);
}
void StatusBarWidget::setVoiceCount(int voices)
{
if (voices != m_cachedVoiceCount)
{
m_voiceCount.setText(QString::number(voices));
m_cachedVoiceCount = voices;
setKillVisible(voices != 0);
}
void StatusBarWidget::setVoiceCount(int voices) {
if (voices != m_cachedVoiceCount) {
m_voiceCount.setText(QString::number(voices));
m_cachedVoiceCount = voices;
setKillVisible(voices != 0);
}
}
void StatusBarWidget::volumeChanged(int vol)
{
int idx = int(std::round(vol * (3.f / 100.f)));
if (idx != m_lastVolIdx)
{
m_lastVolIdx = idx;
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
}
void StatusBarWidget::volumeChanged(int vol) {
int idx = int(std::round(vol * (3.f / 100.f)));
if (idx != m_lastVolIdx) {
m_lastVolIdx = idx;
m_volumeIcon.setPixmap(m_volumeIcons[idx].pixmap(16, 16));
}
}
void StatusBarFocus::setMessage(const QString& message)
{
m_message = message;
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
if (widget->m_curFocus == this)
{
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
void StatusBarFocus::setMessage(const QString& message) {
m_message = message;
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
if (widget->m_curFocus == this) {
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
}
}
void StatusBarFocus::enter()
{
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
widget->m_curFocus = this;
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
void StatusBarFocus::enter() {
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
widget->m_curFocus = this;
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
}
void StatusBarFocus::exit()
{
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
if (widget->m_curFocus == this)
{
widget->clearMessage();
widget->m_curFocus = nullptr;
}
void StatusBarFocus::exit() {
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent())) {
if (widget->m_curFocus == this) {
widget->clearMessage();
widget->m_curFocus = nullptr;
}
}
}

View File

@ -9,67 +9,72 @@
class StatusBarFocus;
class FXButton : public QPushButton
{
Q_OBJECT
class FXButton : public QPushButton {
Q_OBJECT
public:
explicit FXButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
explicit FXButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
};
class StatusBarWidget : public QStatusBar
{
friend class StatusBarFocus;
Q_OBJECT
QLabel m_normalMessage;
QPushButton m_killButton;
FXButton m_fxButton;
QIcon m_volumeIcons[4];
QLabel m_volumeIcon;
QSlider m_volumeSlider;
QLabel m_aIcon;
QSlider m_aSlider;
QLabel m_bIcon;
QSlider m_bSlider;
int m_lastVolIdx = 0;
QLabel m_voiceCount;
int m_cachedVoiceCount = -1;
StatusBarFocus* m_curFocus = nullptr;
void setKillVisible(bool vis) { m_killButton.setVisible(vis); m_voiceCount.setVisible(vis); }
class StatusBarWidget : public QStatusBar {
friend class StatusBarFocus;
Q_OBJECT
QLabel m_normalMessage;
QPushButton m_killButton;
FXButton m_fxButton;
QIcon m_volumeIcons[4];
QLabel m_volumeIcon;
QSlider m_volumeSlider;
QLabel m_aIcon;
QSlider m_aSlider;
QLabel m_bIcon;
QSlider m_bSlider;
int m_lastVolIdx = 0;
QLabel m_voiceCount;
int m_cachedVoiceCount = -1;
StatusBarFocus* m_curFocus = nullptr;
void setKillVisible(bool vis) {
m_killButton.setVisible(vis);
m_voiceCount.setVisible(vis);
}
public:
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
void setVoiceCount(int voices);
void connectKillClicked(const QObject* receiver, const char* method)
{ connect(&m_killButton, SIGNAL(clicked(bool)), receiver, method); }
void connectFXPressed(const QObject* receiver, const char* method)
{ connect(&m_fxButton, SIGNAL(pressed()), receiver, method); }
void setFXDown(bool down) { m_fxButton.setDown(down); }
void connectVolumeSlider(const QObject* receiver, const char* method)
{ connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method); }
void connectASlider(const QObject* receiver, const char* method)
{ connect(&m_aSlider, SIGNAL(valueChanged(int)), receiver, method); }
void connectBSlider(const QObject* receiver, const char* method)
{ connect(&m_bSlider, SIGNAL(valueChanged(int)), receiver, method); }
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
void setVoiceCount(int voices);
void connectKillClicked(const QObject* receiver, const char* method) {
connect(&m_killButton, SIGNAL(clicked(bool)), receiver, method);
}
void connectFXPressed(const QObject* receiver, const char* method) {
connect(&m_fxButton, SIGNAL(pressed()), receiver, method);
}
void setFXDown(bool down) { m_fxButton.setDown(down); }
void connectVolumeSlider(const QObject* receiver, const char* method) {
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method);
}
void connectASlider(const QObject* receiver, const char* method) {
connect(&m_aSlider, SIGNAL(valueChanged(int)), receiver, method);
}
void connectBSlider(const QObject* receiver, const char* method) {
connect(&m_bSlider, SIGNAL(valueChanged(int)), receiver, method);
}
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
private slots:
void volumeChanged(int vol);
void volumeChanged(int vol);
};
class StatusBarFocus : public QObject
{
Q_OBJECT
QString m_message;
class StatusBarFocus : public QObject {
Q_OBJECT
QString m_message;
public:
explicit StatusBarFocus(StatusBarWidget* statusWidget)
: QObject(statusWidget) {}
~StatusBarFocus() { exit(); }
void setMessage(const QString& message);
void enter();
void exit();
explicit StatusBarFocus(StatusBarWidget* statusWidget) : QObject(statusWidget) {}
~StatusBarFocus() { exit(); }
void setMessage(const QString& message);
void enter();
void exit();
};

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -17,106 +17,103 @@ void MacOSSetDarkAppearance();
extern "C" const uint8_t MAINICON_QT[];
static QIcon MakeAppIcon()
{
QIcon ret;
static QIcon MakeAppIcon() {
QIcon ret;
const uint8_t* ptr = MAINICON_QT;
for (int i = 0; i < 6; ++i)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(ptr);
ptr += 4;
const uint8_t* ptr = MAINICON_QT;
for (int i = 0; i < 6; ++i) {
uint32_t size = *reinterpret_cast<const uint32_t*>(ptr);
ptr += 4;
QPixmap pm;
pm.loadFromData(ptr, size);
ret.addPixmap(pm);
ptr += size;
}
QPixmap pm;
pm.loadFromData(ptr, size);
ret.addPixmap(pm);
ptr += size;
}
return ret;
return ret;
}
/* This is for adapting the get*Name methods */
class BooInterface : public boo::IApplication
{
std::vector<boo::SystemString> m_args;
void _deletedWindow(boo::IWindow* window) {}
class BooInterface : public boo::IApplication {
std::vector<boo::SystemString> m_args;
void _deletedWindow(boo::IWindow* window) {}
public:
EPlatformType getPlatformType() const { return EPlatformType::Qt; }
EPlatformType getPlatformType() const { return EPlatformType::Qt; }
int run() { return 0; }
boo::SystemStringView getUniqueName() const { return _SYS_STR("amuse-gui"sv); }
boo::SystemStringView getFriendlyName() const { return _SYS_STR("Amuse"sv); }
boo::SystemStringView getProcessName() const { return _SYS_STR("amuse-gui"sv); }
const std::vector<boo::SystemString>& getArgs() const { return m_args; }
int run() { return 0; }
boo::SystemStringView getUniqueName() const { return _SYS_STR("amuse-gui"sv); }
boo::SystemStringView getFriendlyName() const { return _SYS_STR("Amuse"sv); }
boo::SystemStringView getProcessName() const { return _SYS_STR("amuse-gui"sv); }
const std::vector<boo::SystemString>& getArgs() const { return m_args; }
/* Constructors/initializers for sub-objects */
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) { return {}; }
/* Constructors/initializers for sub-objects */
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) { return {}; }
};
MainWindow* g_MainWindow = nullptr;
int main(int argc, char* argv[])
{
QApplication::setAttribute(Qt::AA_Use96Dpi);
int main(int argc, char* argv[]) {
QApplication::setAttribute(Qt::AA_Use96Dpi);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
QApplication a(argc, argv);
QApplication::setWindowIcon(MakeAppIcon());
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
QApplication a(argc, argv);
QApplication::setWindowIcon(MakeAppIcon());
a.setOrganizationName("AxioDL");
a.setApplicationName("Amuse");
a.setOrganizationName("AxioDL");
a.setApplicationName("Amuse");
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, QColor(53,53,53));
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, QColor(42,42,42));
darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25,25,25,53));
darkPalette.setColor(QPalette::AlternateBase, QColor(53,53,53));
darkPalette.setColor(QPalette::ToolTipBase, QColor(42,42,42));
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255,255,255,120));
darkPalette.setColor(QPalette::Disabled, QPalette::Light, QColor(0,0,0,0));
darkPalette.setColor(QPalette::Button, QColor(53,53,53));
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53,53,53,53));
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255,255,255,120));
darkPalette.setColor(QPalette::BrightText, Qt::red);
darkPalette.setColor(QPalette::Link, QColor(42,130,218));
darkPalette.setColor(QPalette::Highlight, QColor(42,130,218));
darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(42,130,218,53));
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255,255,255,120));
a.setPalette(darkPalette);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, QColor(53, 53, 53));
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, QColor(42, 42, 42));
darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(25, 25, 25, 53));
darkPalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
darkPalette.setColor(QPalette::ToolTipBase, QColor(42, 42, 42));
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255, 255, 255, 120));
darkPalette.setColor(QPalette::Disabled, QPalette::Light, QColor(0, 0, 0, 0));
darkPalette.setColor(QPalette::Button, QColor(53, 53, 53));
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53, 53, 53, 53));
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(255, 255, 255, 120));
darkPalette.setColor(QPalette::BrightText, Qt::red);
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(42, 130, 218, 53));
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(255, 255, 255, 120));
a.setPalette(darkPalette);
#ifdef __APPLE__
MacOSSetDarkAppearance();
MacOSSetDarkAppearance();
#endif
logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions();
logvisor::RegisterConsoleLogger();
logvisor::RegisterStandardExceptions();
BooInterface booApp;
boo::APP = &booApp;
BooInterface booApp;
boo::APP = &booApp;
Q_INIT_RESOURCE(translation_res);
QTranslator translator;
if (translator.load(QLocale(), QLatin1String("lang"), QLatin1String("_"), QLatin1String(":/translations")))
a.installTranslator(&translator);
Q_INIT_RESOURCE(translation_res);
QTranslator translator;
if (translator.load(QLocale(), QLatin1String("lang"), QLatin1String("_"), QLatin1String(":/translations")))
a.installTranslator(&translator);
MainWindow w;
g_MainWindow = &w;
w.show();
MainWindow w;
g_MainWindow = &w;
w.show();
QCommandLineParser parser;
parser.process(a);
QStringList args = parser.positionalArguments();
if (!args.empty())
w.openProject(args.back());
QCommandLineParser parser;
parser.process(a);
QStringList args = parser.positionalArguments();
if (!args.empty())
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 <Shlwapi.h>
namespace amuse
{
namespace amuse {
static const wchar_t* const GMNames[128] = {L"Acoustic Grand 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"Open Triangle"};
bool AudioGroupDataCollection::loadProj()
{
std::wstring path = m_path + L"\\proj";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_projData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadProj() {
std::wstring path = m_path + L"\\proj";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_projData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadPool()
{
std::wstring path = m_path + L"\\pool";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_poolData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadPool() {
std::wstring path = m_path + L"\\pool";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_poolData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadSdir()
{
std::wstring path = m_path + L"\\sdir";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sdirData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadSdir() {
std::wstring path = m_path + L"\\sdir";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sdirData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadSamp()
{
std::wstring path = m_path + L"\\samp";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sampData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
bool AudioGroupDataCollection::loadSamp() {
std::wstring path = m_path + L"\\samp";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::vector<uint8_t>& ret = m_sampData;
size_t len = r.length();
ret.resize(len);
r.readUBytesToBuf(ret.data(), len);
return ret.size() != 0;
}
bool AudioGroupDataCollection::loadMeta()
{
std::wstring path = m_path + L"\\meta";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::experimental::optional<MetaData>& ret = m_metaData;
ret.emplace(r);
return ret.operator bool();
bool AudioGroupDataCollection::loadMeta() {
std::wstring path = m_path + L"\\meta";
athena::io::FileReader r(path, 1024 * 32, false);
if (r.hasError())
return false;
std::experimental::optional<MetaData>& ret = m_metaData;
ret.emplace(r);
return ret.operator bool();
}
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()
{
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;
bool AudioGroupDataCollection::_indexData() {
switch (m_metaData->fmt) {
case amuse::DataFormat::GCN:
default:
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(), amuse::GCNDataTag{});
break;
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 _indexData();
return m_loadedData.operator bool();
}
bool AudioGroupDataCollection::_indexData()
{
switch (m_metaData->fmt)
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() + m_loadedGroup->getProj().sfxGroups().size());
{
case amuse::DataFormat::GCN:
default:
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(),
amuse::GCNDataTag{});
break;
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;
const auto& songGroups = m_loadedGroup->getProj().songGroups();
std::map<int, const amuse::SongGroupIndex*> sortGroups;
for (const auto& pair : songGroups)
sortGroups[pair.first] = &pair.second;
for (const auto& pair : sortGroups)
m_groupTokens.emplace_back(pair.first, pair.second);
}
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() +
m_loadedGroup->getProj().sfxGroups().size());
{
const auto& songGroups = m_loadedGroup->getProj().songGroups();
std::map<int, const amuse::SongGroupIndex*> sortGroups;
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);
}
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); }
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(
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
{
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection)
{
std::wstring collectionPath = m_path + L'\\' + pair.first;
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection) {
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection) {
std::wstring collectionPath = m_path + L'\\' + pair.first;
amuse::IntrusiveAudioGroupData& dataIn = pair.second;
auto search = m_groups.find(pair.first);
if (search == m_groups.end())
{
search =
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();
amuse::IntrusiveAudioGroupData& dataIn = pair.second;
auto search = m_groups.find(pair.first);
if (search == m_groups.end()) {
search =
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();
}
}
void AudioGroupCollection::update(AudioGroupFilePresenter& presenter)
{
std::wstring path = m_path + L"\\*";
void AudioGroupCollection::update(AudioGroupFilePresenter& presenter) {
std::wstring path = m_path + L"\\*";
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do
{
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do {
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::wstring nameStr(d.cFileName);
auto search = m_groups.find(nameStr);
if (search == m_groups.end())
{
search =
m_groups
.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
.first;
search->second->_attemptLoad();
}
}
} while (FindNextFileW(dir, &d));
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
std::wstring nameStr(d.cFileName);
auto search = m_groups.find(nameStr);
if (search == m_groups.end()) {
search =
m_groups.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
.first;
search->second->_attemptLoad();
}
}
} while (FindNextFileW(dir, &d));
FindClose(dir);
FindClose(dir);
}
void AudioGroupFilePresenter::update()
{
std::wstring path = m_backend.getUserDir() + L"\\*";
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>& theMap = m_audioGroupCollections;
void AudioGroupFilePresenter::update() {
std::wstring path = m_backend.getUserDir() + L"\\*";
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>& theMap = m_audioGroupCollections;
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do
{
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(path.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
do {
if (!wcscmp(d.cFileName, L".") || !wcscmp(d.cFileName, L".."))
continue;
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::wstring nameStr(d.cFileName);
auto search = theMap.find(nameStr);
if (search == theMap.end())
{
search = theMap
.emplace(nameStr, std::make_unique<AudioGroupCollection>(
m_backend.getUserDir() + L'\\' + nameStr, nameStr))
.first;
search->second->update(*this);
}
}
} while (FindNextFileW(dir, &d));
if (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
std::wstring nameStr(d.cFileName);
auto search = theMap.find(nameStr);
if (search == theMap.end()) {
search = theMap
.emplace(nameStr,
std::make_unique<AudioGroupCollection>(m_backend.getUserDir() + L'\\' + nameStr, nameStr))
.first;
search->second->update(*this);
}
}
} while (FindNextFileW(dir, &d));
FindClose(dir);
FindClose(dir);
}
void AudioGroupFilePresenter::addCollection(
std::wstring_view name, std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
{
std::wstring path = m_backend.getUserDir() + L'\\' + name;
AudioGroupCollection& insert =
*m_audioGroupCollections.emplace(name, std::make_unique<AudioGroupCollection>(path, name)).first->second;
CreateDirectory(insert.m_path.c_str(), nullptr);
insert.addCollection(std::move(collection));
std::wstring_view name, std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection) {
std::wstring path = m_backend.getUserDir() + L'\\' + name;
AudioGroupCollection& insert =
*m_audioGroupCollections.emplace(name, std::make_unique<AudioGroupCollection>(path, name)).first->second;
CreateDirectory(insert.m_path.c_str(), nullptr);
insert.addCollection(std::move(collection));
for (std::pair<const std::wstring, std::unique_ptr<AudioGroupDataCollection>>& pair : insert.m_groups)
{
std::wstring collectionPath = insert.m_path + L'\\' + pair.first;
CreateDirectory(collectionPath.c_str(), nullptr);
for (std::pair<const std::wstring, std::unique_ptr<AudioGroupDataCollection>>& pair : insert.m_groups) {
std::wstring collectionPath = insert.m_path + L'\\' + pair.first;
CreateDirectory(collectionPath.c_str(), nullptr);
FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb");
if (fp)
{
fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), 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);
}
FILE* fp = _wfopen((collectionPath + L"\\proj").c_str(), L"wb");
if (fp) {
fwrite(pair.second->m_projData.data(), 1, pair.second->m_projData.size(), fp);
fclose(fp);
}
}
void AudioGroupFilePresenter::removeCollection(unsigned idx)
{
if (idx < m_iteratorVec.size())
{
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);
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);
}
}
void AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx)
{
TVINSERTSTRUCT ins = {};
ins.item.mask = TVIF_TEXT | TVIF_PARAM;
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);
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);
}
}
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);
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::populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx)
{
LVITEM item = {};
item.mask = LVIF_TEXT;
void AudioGroupFilePresenter::removeCollection(unsigned idx) {
if (idx < m_iteratorVec.size()) {
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);
}
}
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 AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx) {
TVINSERTSTRUCT ins = {};
ins.item.mask = TVIF_TEXT | TVIF_PARAM;
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) {
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);
}
}
}
}
}
}
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

View File

@ -10,105 +10,93 @@
#include <windows.h>
#include <CommCtrl.h>
namespace amuse
{
namespace amuse {
class VSTBackend;
class VSTEditor;
class AudioGroupFilePresenter;
struct AudioGroupDataCollection
{
std::wstring m_path;
std::wstring m_name;
struct AudioGroupDataCollection {
std::wstring m_path;
std::wstring m_name;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
std::vector<uint8_t> m_projData;
std::vector<uint8_t> m_poolData;
std::vector<uint8_t> m_sdirData;
std::vector<uint8_t> m_sampData;
struct MetaData
{
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
{
}
MetaData(athena::io::FileReader& r)
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
{
}
};
std::experimental::optional<MetaData> m_metaData;
struct MetaData {
amuse::DataFormat fmt;
uint32_t absOffs;
uint32_t active;
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {}
MetaData(athena::io::FileReader& r)
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
};
std::experimental::optional<MetaData> m_metaData;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
struct GroupToken
{
int m_groupId;
const amuse::SongGroupIndex* m_song = 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::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {}
};
std::vector<GroupToken> m_groupTokens;
std::experimental::optional<amuse::AudioGroupData> m_loadedData;
const amuse::AudioGroup* m_loadedGroup;
struct GroupToken {
int m_groupId;
const amuse::SongGroupIndex* m_song = 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::SFXGroupIndex* sfx) : m_groupId(id), m_sfx(sfx) {}
};
std::vector<GroupToken> m_groupTokens;
bool loadProj();
bool loadPool();
bool loadSdir();
bool loadSamp();
bool loadMeta();
bool loadProj();
bool loadPool();
bool loadSdir();
bool loadSamp();
bool loadMeta();
AudioGroupDataCollection(std::wstring_view path, std::wstring_view name);
bool isDataComplete() const
{
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad();
bool _indexData();
AudioGroupDataCollection(std::wstring_view path, std::wstring_view name);
bool isDataComplete() const {
return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;
}
bool _attemptLoad();
bool _indexData();
void addToEngine(amuse::Engine& engine);
void removeFromEngine(amuse::Engine& engine) const;
void addToEngine(amuse::Engine& engine);
void removeFromEngine(amuse::Engine& engine) const;
};
struct AudioGroupCollection
{
using GroupIterator = std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>>::iterator;
std::wstring m_path;
std::wstring m_name;
struct AudioGroupCollection {
using GroupIterator = std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>>::iterator;
std::wstring m_path;
std::wstring m_name;
std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<GroupIterator> m_iteratorVec;
std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>> m_groups;
std::vector<GroupIterator> m_iteratorVec;
AudioGroupCollection(std::wstring_view path, std::wstring_view name);
void addCollection(std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter& presenter);
void populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx);
AudioGroupCollection(std::wstring_view path, std::wstring_view name);
void addCollection(std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void update(AudioGroupFilePresenter& presenter);
void populateFiles(VSTEditor& editor, HTREEITEM colHandle, size_t parentIdx);
};
class AudioGroupFilePresenter
{
friend class VSTBackend;
class AudioGroupFilePresenter {
friend class VSTBackend;
public:
using CollectionIterator = std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>::iterator;
using CollectionIterator = std::map<std::wstring, std::unique_ptr<AudioGroupCollection>>::iterator;
private:
VSTBackend& m_backend;
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<CollectionIterator> m_iteratorVec;
VSTBackend& m_backend;
std::map<std::wstring, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
std::vector<CollectionIterator> m_iteratorVec;
public:
AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {}
void update();
void populateCollectionColumn(VSTEditor& editor);
void populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx);
void populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx);
void addCollection(std::wstring_view name,
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void removeCollection(unsigned idx);
VSTBackend& getBackend() { return m_backend; }
AudioGroupFilePresenter(VSTBackend& backend) : m_backend(backend) {}
void update();
void populateCollectionColumn(VSTEditor& editor);
void populateGroupColumn(VSTEditor& editor, int collectionIdx, int fileIdx);
void populatePageColumn(VSTEditor& editor, int collectionIdx, int fileIdx, int groupIdx);
void addCollection(std::wstring_view name,
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
void removeCollection(unsigned idx);
VSTBackend& getBackend() { return m_backend; }
};
}
} // namespace amuse

View File

@ -18,8 +18,9 @@
#define CONTROL_GROUP 2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1 1
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same IDas CONTROL_RADIOBUTTONLIST,
// because it is a child control under CONTROL_RADIOBUTTONLIST
#define CONTROL_RADIOBUTTON2 \
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
#define IDC_BASICFILEOPEN 100
@ -33,201 +34,179 @@ HWND ghMainWnd = 0;
HINSTANCE ghAppInst = 0;
RECT winRect;
class CDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents
{
class CDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents {
public:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{0},
};
return QISearch(this, qit, riid, ppv);
}
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{0},
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
IFACEMETHODIMP_(ULONG) Release() {
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd);
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog* pfd);
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem);
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) { return S_OK; };
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem);
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize*, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize*, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize*, DWORD) { return S_OK; };
CDialogEventHandler() : _cRef(1){};
CDialogEventHandler() : _cRef(1){};
private:
~CDialogEventHandler(){};
long _cRef;
~CDialogEventHandler(){};
long _cRef;
};
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv);
std::wstring openDB()
{
std::wstring ret;
CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
std::wstring openDB() {
std::wstring ret;
CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
// Cocreate the file open dialog object
IFileDialog* pfd = NULL;
// Cocreate the file open dialog object
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))
{
// Stuff needed for later
const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}};
if (SUCCEEDED(hr)) {
// Stuff needed for later
const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}};
// Create event handling
IFileDialogEvents* pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
// Create event handling
IFileDialogEvents* pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook the event handler
DWORD dwCookie;
if (SUCCEEDED(hr)) {
// Hook the event handler
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
// Set options for the dialog
DWORD dwFlags;
if (SUCCEEDED(hr)) {
// Set options for the dialog
DWORD dwFlags;
// Get options first so we do not override
hr = pfd->GetOptions(&dwFlags);
// Get options first so we do not override
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
// Get shell items only
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr)) {
// Get shell items only
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr))
{
// Types of files to display (not default)
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
if (SUCCEEDED(hr)) {
// Types of files to display (not default)
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
if (SUCCEEDED(hr))
{
// Set default file type to display
// hr = pfd->SetDefaultExtension(L"sqlite");
if (SUCCEEDED(hr)) {
// Set default file type to display
// hr = pfd->SetDefaultExtension(L"sqlite");
// if (SUCCEEDED(hr))
//{
// Show dialog
hr = pfd->Show(NULL);
// if (SUCCEEDED(hr))
//{
// Show dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
// Get the result once the user clicks on open
IShellItem* result;
if (SUCCEEDED(hr)) {
// Get the result once the user clicks on open
IShellItem* result;
hr = pfd->GetResult(&result);
hr = pfd->GetResult(&result);
if (SUCCEEDED(hr))
{
// Print out the file name
PWSTR fName = NULL;
if (SUCCEEDED(hr)) {
// Print out the file name
PWSTR fName = NULL;
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
if (SUCCEEDED(hr))
{
ret.assign(fName);
CoTaskMemFree(fName);
}
if (SUCCEEDED(hr)) {
ret.assign(fName);
CoTaskMemFree(fName);
}
result->Release();
}
}
//}
}
}
result->Release();
}
}
//}
}
pfd->Unadvise(dwCookie);
}
}
}
pfde->Release();
pfd->Unadvise(dwCookie);
}
pfd->Release();
return ret;
pfde->Release();
}
pfd->Release();
return ret;
}
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv)
{
*ppv = NULL;
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv) {
*ppv = NULL;
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr)) {
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog* pfd)
{
IFileSaveDialog* pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr))
{
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr))
{
IPropertyDescriptionList* pdl = NULL;
}
pfsd->Release();
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog* pfd) {
IFileSaveDialog* pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr)) {
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr)) {
IPropertyDescriptionList* pdl = NULL;
}
return hr;
pfsd->Release();
}
return hr;
}
// IFileDialogControlEvents
// 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.
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem)
{
IFileDialog* pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr))
{
if (dwIDCtl == CONTROL_RADIOBUTTONLIST)
{
switch (dwIDItem)
{
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem) {
IFileDialog* pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr)) {
if (dwIDCtl == CONTROL_RADIOBUTTONLIST) {
switch (dwIDItem) {
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
pfd->Release();
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
return hr;
pfd->Release();
}
return hr;
}

View File

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

View File

@ -6,389 +6,343 @@
#undef min
#undef max
struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
{
std::vector<float> m_interleavedBuf;
float** m_outputData = nullptr;
size_t m_renderFrames = 0;
size_t m_curBufFrame = 0;
struct VSTVoiceEngine : boo::BaseAudioVoiceEngine {
std::vector<float> m_interleavedBuf;
float** m_outputData = nullptr;
size_t m_renderFrames = 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
{
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) : IMIDIIn(virt, std::move(receiver)) {}
struct MIDIIn : public boo::IMIDIIn {
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> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
m_midiReceiver = &ret->m_receiver;
return ret;
}
std::unique_ptr<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) {
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
m_midiReceiver = &ret->m_receiver;
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()
{
m_mixInfo.m_periodFrames = 1024;
m_mixInfo.m_sampleRate = 44100.0;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
m_mixInfo.m_bitsPerSample = 32;
_buildAudioRenderClient();
}
VSTVoiceEngine() {
m_mixInfo.m_periodFrames = 1024;
m_mixInfo.m_sampleRate = 44100.0;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
m_mixInfo.m_bitsPerSample = 32;
_buildAudioRenderClient();
}
void _buildAudioRenderClient()
{
m_mixInfo.m_channels = _getAvailableSet();
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
void _buildAudioRenderClient() {
m_mixInfo.m_channels = _getAvailableSet();
unsigned chCount = ChannelCount(m_mixInfo.m_channels);
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
m_curBufFrame = m_5msFrames;
m_mixInfo.m_periodFrames = m_5msFrames;
m_interleavedBuf.resize(m_5msFrames * 2);
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
m_curBufFrame = m_5msFrames;
m_mixInfo.m_periodFrames = m_5msFrames;
m_interleavedBuf.resize(m_5msFrames * 2);
boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap;
chMapOut.m_channelCount = 2;
chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft;
chMapOut.m_channels[1] = boo::AudioChannel::FrontRight;
boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap;
chMapOut.m_channelCount = 2;
chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft;
chMapOut.m_channels[1] = boo::AudioChannel::FrontRight;
while (chMapOut.m_channelCount < chCount)
chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown;
}
while (chMapOut.m_channelCount < chCount)
chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown;
}
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames)
{
m_mixInfo.m_periodFrames = periodFrames;
m_mixInfo.m_sampleRate = sampleRate;
_buildAudioRenderClient();
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) {
m_mixInfo.m_periodFrames = periodFrames;
m_mixInfo.m_sampleRate = sampleRate;
_buildAudioRenderClient();
for (boo::AudioVoice* vox : m_activeVoices)
vox->_resetSampleRate(vox->m_sampleRateIn);
for (boo::AudioSubmix* smx : m_activeSubmixes)
smx->_resetOutputSampleRate();
}
for (boo::AudioVoice* vox : m_activeVoices)
vox->_resetSampleRate(vox->m_sampleRateIn);
for (boo::AudioSubmix* smx : m_activeSubmixes)
smx->_resetOutputSampleRate();
}
void pumpAndMixVoices()
{
for (size_t f = 0; f < m_renderFrames;)
{
if (m_curBufFrame == m_5msFrames)
{
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data());
m_curBufFrame = 0;
}
void pumpAndMixVoices() {
for (size_t f = 0; f < m_renderFrames;) {
if (m_curBufFrame == m_5msFrames) {
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data());
m_curBufFrame = 0;
}
size_t remRenderFrames = std::min(m_renderFrames - f, m_5msFrames - m_curBufFrame);
if (remRenderFrames)
{
for (size_t i = 0; i < 2; ++i)
{
float* bufOut = m_outputData[i];
for (size_t lf = 0; lf < remRenderFrames; ++lf)
bufOut[f + lf] = m_interleavedBuf[(m_curBufFrame + lf) * 2 + i];
}
m_curBufFrame += remRenderFrames;
f += remRenderFrames;
}
size_t remRenderFrames = std::min(m_renderFrames - f, m_5msFrames - m_curBufFrame);
if (remRenderFrames) {
for (size_t i = 0; i < 2; ++i) {
float* bufOut = m_outputData[i];
for (size_t lf = 0; lf < remRenderFrames; ++lf)
bufOut[f + lf] = m_interleavedBuf[(m_curBufFrame + lf) * 2 + i];
}
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')
static logvisor::Module Log("amuse::AudioUnitBackend");
VSTBackend::VSTBackend(audioMasterCallback cb) : AudioEffectX(cb, 0, 0), m_filePresenter(*this), m_editor(*this)
{
isSynth();
setUniqueID(kBackendID);
setNumInputs(0);
setNumOutputs(2);
setEditor(&m_editor);
sizeWindow(600, 420);
programsAreChunks();
VSTBackend::VSTBackend(audioMasterCallback cb) : AudioEffectX(cb, 0, 0), m_filePresenter(*this), m_editor(*this) {
isSynth();
setUniqueID(kBackendID);
setNumInputs(0);
setNumOutputs(2);
setEditor(&m_editor);
sizeWindow(600, 420);
programsAreChunks();
m_booBackend = std::make_unique<VSTVoiceEngine>();
m_voxAlloc.emplace(*m_booBackend);
m_engine.emplace(*m_voxAlloc);
m_booBackend = std::make_unique<VSTVoiceEngine>();
m_voxAlloc.emplace(*m_booBackend);
m_engine.emplace(*m_voxAlloc);
WCHAR path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path)))
{
m_userDir = std::wstring(path) + L"\\Amuse";
CreateDirectory(m_userDir.c_str(), nullptr);
}
WCHAR path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path))) {
m_userDir = std::wstring(path) + L"\\Amuse";
CreateDirectory(m_userDir.c_str(), nullptr);
}
m_filePresenter.update();
m_filePresenter.update();
}
VSTBackend::~VSTBackend() { editor = nullptr; }
AEffEditor* VSTBackend::getEditor() { return &m_editor; }
VstInt32 VSTBackend::processEvents(VstEvents* events)
{
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
VstInt32 VSTBackend::processEvents(VstEvents* events) {
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
/* Handle group load request */
if (m_curGroup != m_reqGroup)
{
m_curGroup = m_reqGroup;
if (m_curSeq)
m_curSeq->kill();
m_curSeq = m_engine->seqPlay(m_reqGroup, -1, nullptr);
m_editor.reselectPage();
}
/* Handle group load request */
if (m_curGroup != m_reqGroup) {
m_curGroup = m_reqGroup;
if (m_curSeq)
m_curSeq->kill();
m_curSeq = m_engine->seqPlay(m_reqGroup, -1, nullptr);
m_editor.reselectPage();
}
if (engine.m_midiReceiver)
{
for (VstInt32 i = 0; i < events->numEvents; ++i)
{
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
if (evt->type == kVstMidiType)
{
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);
}
if (engine.m_midiReceiver) {
for (VstInt32 i = 0; i < events->numEvents; ++i) {
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
if (evt->type == kVstMidiType) {
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);
}
}
}
return 1;
return 1;
}
void VSTBackend::processReplacing(float**, float** outputs, VstInt32 sampleFrames)
{
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
void VSTBackend::processReplacing(float**, float** outputs, VstInt32 sampleFrames) {
std::unique_lock<std::mutex> lk(m_lock);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
/* Output buffers */
engine.m_renderFrames = sampleFrames;
engine.m_outputData = outputs;
m_engine->pumpEngine();
/* Output buffers */
engine.m_renderFrames = sampleFrames;
engine.m_outputData = outputs;
m_engine->pumpEngine();
m_curFrame += sampleFrames;
m_curFrame += sampleFrames;
}
VstInt32 VSTBackend::canDo(char* text)
{
VstInt32 returnCode = 0;
VstInt32 VSTBackend::canDo(char* text) {
VstInt32 returnCode = 0;
if (!strcmp(text, "receiveVstEvents"))
returnCode = 1;
else if (!strcmp(text, "receiveVstMidiEvent"))
returnCode = 1;
if (!strcmp(text, "receiveVstEvents"))
returnCode = 1;
else if (!strcmp(text, "receiveVstMidiEvent"))
returnCode = 1;
return returnCode;
return returnCode;
}
VstPlugCategory VSTBackend::getPlugCategory() { return kPlugCategSynth; }
bool VSTBackend::getEffectName(char* text)
{
strcpy(text, "Amuse");
return true;
bool VSTBackend::getEffectName(char* text) {
strcpy(text, "Amuse");
return true;
}
bool VSTBackend::getProductString(char* text)
{
strcpy(text, "Amuse");
return true;
bool VSTBackend::getProductString(char* text) {
strcpy(text, "Amuse");
return true;
}
bool VSTBackend::getVendorString(char* text)
{
strcpy(text, "AxioDL");
return true;
bool VSTBackend::getVendorString(char* text) {
strcpy(text, "AxioDL");
return true;
}
bool VSTBackend::getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text)
{
strcpy(text, "Sampler");
return true;
bool VSTBackend::getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text) {
strcpy(text, "Sampler");
return true;
}
bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* properties)
{
bool returnCode = false;
if (index == 0)
{
strcpy(properties->label, "Amuse Out");
properties->flags = kVstPinIsStereo | kVstPinIsActive;
properties->arrangementType = kSpeakerArrStereo;
returnCode = true;
}
return returnCode;
bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* properties) {
bool returnCode = false;
if (index == 0) {
strcpy(properties->label, "Amuse Out");
properties->flags = kVstPinIsStereo | kVstPinIsActive;
properties->arrangementType = kSpeakerArrStereo;
returnCode = true;
}
return returnCode;
}
VstInt32 VSTBackend::getNumMidiInputChannels() { return 1; }
void VSTBackend::setSampleRate(float sampleRate)
{
AudioEffectX::setSampleRate(sampleRate);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(sampleRate, engine.mixInfo().m_periodFrames);
void VSTBackend::setSampleRate(float sampleRate) {
AudioEffectX::setSampleRate(sampleRate);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(sampleRate, engine.mixInfo().m_periodFrames);
}
void VSTBackend::setBlockSize(VstInt32 blockSize)
{
AudioEffectX::setBlockSize(blockSize);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(engine.mixInfo().m_sampleRate, blockSize);
void VSTBackend::setBlockSize(VstInt32 blockSize) {
AudioEffectX::setBlockSize(blockSize);
VSTVoiceEngine& engine = static_cast<VSTVoiceEngine&>(*m_booBackend);
engine._rebuildAudioRenderClient(engine.mixInfo().m_sampleRate, blockSize);
}
void VSTBackend::loadGroupFile(int collectionIdx, int fileIdx)
{
std::unique_lock<std::mutex> lk(m_lock);
void VSTBackend::loadGroupFile(int collectionIdx, int fileIdx) {
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.reset();
m_curGroup = -1;
m_reqGroup = -1;
m_curSeq = m_engine->seqPlay(groupTok.m_groupId, -1, nullptr);
}
}
}
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::_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;
}
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;
}
}
} // namespace amuse
AudioEffect* createEffectInstance(audioMasterCallback audioMaster) { return new amuse::VSTBackend(audioMaster); }

View File

@ -12,64 +12,60 @@
#include "amuse/IBackendVoiceAllocator.hpp"
#include "AudioGroupFilePresenter.hpp"
namespace amuse
{
namespace amuse {
class VSTBackend;
/** Backend voice allocator implementation for AudioUnit mixer */
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator
{
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator {
public:
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
};
/** Actual plugin implementation class */
class VSTBackend : public AudioEffectX
{
std::mutex m_lock;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::VSTBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
std::shared_ptr<amuse::Sequencer> m_curSeq;
int m_reqGroup = -1;
int m_curGroup = -1;
const AudioGroupDataCollection* m_curData = nullptr;
size_t m_curFrame = 0;
std::wstring m_userDir;
int m_routeChannel = -1;
AudioGroupFilePresenter m_filePresenter;
VSTEditor m_editor;
class VSTBackend : public AudioEffectX {
std::mutex m_lock;
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
std::experimental::optional<amuse::VSTBackendVoiceAllocator> m_voxAlloc;
std::experimental::optional<amuse::Engine> m_engine;
std::shared_ptr<amuse::Sequencer> m_curSeq;
int m_reqGroup = -1;
int m_curGroup = -1;
const AudioGroupDataCollection* m_curData = nullptr;
size_t m_curFrame = 0;
std::wstring m_userDir;
int m_routeChannel = -1;
AudioGroupFilePresenter m_filePresenter;
VSTEditor m_editor;
public:
VSTBackend(audioMasterCallback cb);
~VSTBackend();
AEffEditor* getEditor();
VstInt32 processEvents(VstEvents* events);
void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);
VstInt32 canDo(char* text);
VstPlugCategory getPlugCategory();
bool getEffectName(char* text);
bool getProductString(char* text);
bool getVendorString(char* text);
bool getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text);
bool getOutputProperties(VstInt32 index, VstPinProperties* properties);
VstInt32 getNumMidiInputChannels();
void setSampleRate(float sampleRate);
void setBlockSize(VstInt32 blockSize);
VSTBackend(audioMasterCallback cb);
~VSTBackend();
AEffEditor* getEditor();
VstInt32 processEvents(VstEvents* events);
void processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);
VstInt32 canDo(char* text);
VstPlugCategory getPlugCategory();
bool getEffectName(char* text);
bool getProductString(char* text);
bool getVendorString(char* text);
bool getProgramNameIndexed(VstInt32 category, VstInt32 index, char* text);
bool getOutputProperties(VstInt32 index, VstPinProperties* properties);
VstInt32 getNumMidiInputChannels();
void setSampleRate(float sampleRate);
void setBlockSize(VstInt32 blockSize);
amuse::Engine& getAmuseEngine() { return *m_engine; }
std::wstring_view getUserDir() const { return m_userDir; }
AudioGroupFilePresenter& getFilePresenter() { return m_filePresenter; }
amuse::Engine& getAmuseEngine() { return *m_engine; }
std::wstring_view getUserDir() const { return m_userDir; }
AudioGroupFilePresenter& getFilePresenter() { return m_filePresenter; }
void loadGroupFile(int collectionIdx, int fileIdx);
void setGroup(int groupIdx, bool immediate);
void _setNormalProgram(int programNo);
void setNormalProgram(int programNo);
void _setDrumProgram(int programNo);
void setDrumProgram(int programNo);
void loadGroupFile(int collectionIdx, int fileIdx);
void setGroup(int groupIdx, bool immediate);
void _setNormalProgram(int programNo);
void setNormalProgram(int programNo);
void _setDrumProgram(int programNo);
void setDrumProgram(int programNo);
VstInt32 getChunk(void** data, bool isPreset);
VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset);
VstInt32 getChunk(void** data, 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 HBRUSH gGreyBorderBrush;
namespace amuse
{
namespace amuse {
VSTEditor::VSTEditor(VSTBackend& backend) : AEffEditor(&backend), m_backend(backend) {}
bool VSTEditor::getRect(ERect** rect)
{
*rect = &m_windowRect;
return true;
bool VSTEditor::getRect(ERect** rect) {
*rect = &m_windowRect;
return true;
}
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
VSTEditor& editor = *reinterpret_cast<VSTEditor*>(GetWindowLongPtrW(hwnd, 0));
switch (uMsg)
{
case WM_NOTIFY:
{
NMHDR& itemAct = *reinterpret_cast<LPNMHDR>(lParam);
switch (itemAct.code)
{
case HDN_BEGINTRACK:
return TRUE;
case NM_CLICK:
{
NMITEMACTIVATE& itemAct = *reinterpret_cast<LPNMITEMACTIVATE>(lParam);
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);
}
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
VSTEditor& editor = *reinterpret_cast<VSTEditor*>(GetWindowLongPtrW(hwnd, 0));
switch (uMsg) {
case WM_NOTIFY: {
NMHDR& itemAct = *reinterpret_cast<LPNMHDR>(lParam);
switch (itemAct.code) {
case HDN_BEGINTRACK:
return TRUE;
case NM_CLICK: {
NMITEMACTIVATE& itemAct = *reinterpret_cast<LPNMITEMACTIVATE>(lParam);
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 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 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 WM_ERASEBKGND:
{
RECT rect;
GetClientRect(hwnd, &rect);
FillRect(HDC(wParam), &rect, gGreyBorderBrush);
return 1;
case TVN_GETDISPINFO: {
NMTVDISPINFO& treeDispInfo = *reinterpret_cast<LPNMTVDISPINFO>(lParam);
if (treeDispInfo.item.mask & TVIF_CHILDREN) {}
return 0;
}
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)
{
switch (uMsg)
{
case WM_SETCURSOR:
return TRUE;
case WM_LBUTTONDBLCLK:
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_SETCURSOR:
return TRUE;
case WM_LBUTTONDBLCLK:
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
TRIVERTEX verts[] = {{rect.left, rect.top, 0x6000, 0x6000, 0x7000, 0xff00},
{rect.right, rect.bottom, 0x2000, 0x2000, 0x2800, 0xff00}};
GRADIENT_RECT grect = {0, 1};
GradientFill(dc, verts, 2, &grect, 1, GRADIENT_FILL_RECT_V);
TRIVERTEX verts[] = {{rect.left, rect.top, 0x6000, 0x6000, 0x7000, 0xff00},
{rect.right, rect.bottom, 0x2000, 0x2000, 0x2800, 0xff00}};
GRADIENT_RECT grect = {0, 1};
GradientFill(dc, verts, 2, &grect, 1, GRADIENT_FILL_RECT_V);
SetTextColor(dc, RGB(255, 255, 255));
SetBkMode(dc, TRANSPARENT);
SelectObject(dc, GetStockObject(ANSI_VAR_FONT));
rect.left += 6;
SetTextColor(dc, RGB(255, 255, 255));
SetBkMode(dc, TRANSPARENT);
SelectObject(dc, GetStockObject(ANSI_VAR_FONT));
rect.left += 6;
LPWSTR str = LPWSTR(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
DrawText(dc, str, -1, &rect, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
LPWSTR str = LPWSTR(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
DrawText(dc, str, -1, &rect, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
EndPaint(hwnd, &ps);
return 0;
}
}
EndPaint(hwnd, &ps);
return 0;
}
}
return CallWindowProc(OriginalListViewProc, hwnd, uMsg, wParam, lParam);
return CallWindowProc(OriginalListViewProc, hwnd, uMsg, wParam, lParam);
}
void VSTEditor::_reselectColumns()
{
if (m_deferredCollectionSel)
{
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_selPageIdx != -1)
ListView_SetItemState(m_pageListView, m_selPageIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
void VSTEditor::_reselectColumns() {
if (m_deferredCollectionSel) {
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_selPageIdx != -1)
ListView_SetItemState(m_pageListView, m_selPageIdx, LVIS_FOCUSED | LVIS_SELECTED, 0xf);
}
bool VSTEditor::open(void* ptr)
{
AEffEditor::open(ptr);
HWND hostView = HWND(ptr);
gGreyBorderBrush = CreateSolidBrush(RGB(100, 100, 100));
bool VSTEditor::open(void* ptr) {
AEffEditor::open(ptr);
HWND hostView = HWND(ptr);
gGreyBorderBrush = CreateSolidBrush(RGB(100, 100, 100));
WNDCLASSW notifyCls = {CS_HREDRAW | CS_VREDRAW,
WindowProc,
0,
8,
HINSTANCE(hInstance),
nullptr,
nullptr,
nullptr,
nullptr,
L"VSTNotify"};
RegisterClassW(&notifyCls);
WNDCLASSW notifyCls = {CS_HREDRAW | CS_VREDRAW,
WindowProc,
0,
8,
HINSTANCE(hInstance),
nullptr,
nullptr,
nullptr,
nullptr,
L"VSTNotify"};
RegisterClassW(&notifyCls);
m_rootView = CreateWindowW(L"VSTNotify", L"", WS_CHILD, 0, 0, m_windowRect.right, m_windowRect.bottom, hostView,
nullptr, HINSTANCE(hInstance), nullptr);
SetWindowLongPtrW(m_rootView, 0, LONG_PTR(this));
ShowWindow(m_rootView, SW_SHOW);
m_rootView = CreateWindowW(L"VSTNotify", L"", WS_CHILD, 0, 0, m_windowRect.right, m_windowRect.bottom, hostView,
nullptr, HINSTANCE(hInstance), nullptr);
SetWindowLongPtrW(m_rootView, 0, LONG_PTR(this));
ShowWindow(m_rootView, SW_SHOW);
TVINSERTSTRUCT treeItem = {};
treeItem.hParent = TVI_ROOT;
treeItem.hInsertAfter = TVI_LAST;
TVINSERTSTRUCT treeItem = {};
treeItem.hParent = TVI_ROOT;
treeItem.hInsertAfter = TVI_LAST;
treeItem.item.mask = TVIF_CHILDREN | TVIF_TEXT;
treeItem.item.cChildren = 1;
treeItem.item.pszText = L"Root A";
treeItem.item.mask = TVIF_CHILDREN | TVIF_TEXT;
treeItem.item.cChildren = 1;
treeItem.item.pszText = L"Root A";
LVCOLUMN column = {};
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
column.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH;
column.cx = 199;
LVCOLUMN column = {};
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
column.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH;
column.cx = 199;
m_collectionTree =
CreateWindowW(WC_TREEVIEW, L"",
WS_CHILD | WS_CLIPSIBLINGS | TVS_SHOWSELALWAYS | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS,
1, 25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
TreeView_SetBkColor(m_collectionTree, RGB(64, 64, 64));
TreeView_SetTextColor(m_collectionTree, RGB(255, 255, 255));
HTREEITEM rootItemA = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Root B";
HTREEITEM rootItemB = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemA;
treeItem.item.cChildren = 0;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemB;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
ShowWindow(m_collectionTree, SW_SHOW);
m_collectionTree =
CreateWindowW(WC_TREEVIEW, L"",
WS_CHILD | WS_CLIPSIBLINGS | TVS_SHOWSELALWAYS | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, 1,
25, 199, m_windowRect.bottom - m_windowRect.top - 26, m_rootView, nullptr, nullptr, nullptr);
TreeView_SetBkColor(m_collectionTree, RGB(64, 64, 64));
TreeView_SetTextColor(m_collectionTree, RGB(255, 255, 255));
HTREEITEM rootItemA = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Root B";
HTREEITEM rootItemB = TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemA;
treeItem.item.cChildren = 0;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.hParent = rootItemB;
treeItem.item.pszText = L"Child A";
TreeView_InsertItem(m_collectionTree, &treeItem);
treeItem.item.pszText = L"Child B";
TreeView_InsertItem(m_collectionTree, &treeItem);
ShowWindow(m_collectionTree, SW_SHOW);
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"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(cHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(cHeader, SW_SHOW);
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"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(cHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(cHeader, SW_SHOW);
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"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(gHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(gHeader, SW_SHOW);
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"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(gHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(gHeader, SW_SHOW);
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"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(pHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(pHeader, SW_SHOW);
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"));
OriginalListViewProc = WNDPROC(SetWindowLongPtr(pHeader, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc)));
ShowWindow(pHeader, SW_SHOW);
m_collectionAdd =
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);
SetWindowFont(m_collectionAdd, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionAdd, TRUE);
SetWindowPos(m_collectionAdd, HWND_TOP, 1, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_collectionAdd =
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);
SetWindowFont(m_collectionAdd, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionAdd, TRUE);
SetWindowPos(m_collectionAdd, HWND_TOP, 1, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_collectionRemove =
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);
SetWindowFont(m_collectionRemove, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionRemove, FALSE);
SetWindowPos(m_collectionRemove, HWND_TOP, 26, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_collectionRemove =
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);
SetWindowFont(m_collectionRemove, GetStockObject(ANSI_FIXED_FONT), FALSE);
Button_Enable(m_collectionRemove, FALSE);
SetWindowPos(m_collectionRemove, HWND_TOP, 26, m_windowRect.bottom - m_windowRect.top - 25, 25, 24, SWP_SHOWWINDOW);
m_groupListView =
CreateWindowW(WC_LISTVIEW, L"",
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);
column.pszText = L"Group";
HWND header = ListView_GetHeader(m_groupListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_groupListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_groupListView, CLR_NONE);
ListView_SetTextColor(m_groupListView, RGB(255, 255, 255));
ListView_InsertColumn(m_groupListView, 0, &column);
ShowWindow(m_groupListView, SW_SHOW);
m_groupListView =
CreateWindowW(WC_LISTVIEW, L"",
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);
column.pszText = L"Group";
HWND header = ListView_GetHeader(m_groupListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_groupListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_groupListView, CLR_NONE);
ListView_SetTextColor(m_groupListView, RGB(255, 255, 255));
ListView_InsertColumn(m_groupListView, 0, &column);
ShowWindow(m_groupListView, SW_SHOW);
m_pageListView =
CreateWindowW(WC_LISTVIEW, L"",
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);
column.pszText = L"Page";
column.cx = 198 - GetSystemMetrics(SM_CXVSCROLL);
header = ListView_GetHeader(m_pageListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_pageListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_pageListView, CLR_NONE);
ListView_SetTextColor(m_pageListView, RGB(255, 255, 255));
ListView_InsertColumn(m_pageListView, 0, &column);
ShowWindow(m_pageListView, SW_SHOW);
m_pageListView =
CreateWindowW(WC_LISTVIEW, L"",
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);
column.pszText = L"Page";
column.cx = 198 - GetSystemMetrics(SM_CXVSCROLL);
header = ListView_GetHeader(m_pageListView);
SetWindowLongPtrW(header, GWLP_USERDATA, LONG_PTR(column.pszText));
SetWindowLongPtr(header, GWLP_WNDPROC, LONG_PTR(ColHeaderWindowProc));
ListView_SetBkColor(m_pageListView, RGB(64, 64, 64));
ListView_SetTextBkColor(m_pageListView, CLR_NONE);
ListView_SetTextColor(m_pageListView, RGB(255, 255, 255));
ListView_InsertColumn(m_pageListView, 0, &column);
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();
return true;
}
}
void VSTEditor::close()
{
AEffEditor::close();
UnregisterClassW(L"VSTNotify", HINSTANCE(hInstance));
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);
}
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();
}
}
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);
void VSTEditor::selectCollection(LPARAM idx) {
if (0x80000000 & idx) {
/* Sub-item */
int rootIdx = (idx >> 16) & 0x7fff;
int subIdx = idx & 0xffff;
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)
{
if (0x80000000 & idx)
{
/* Sub-item */
int rootIdx = (idx >> 16) & 0x7fff;
int subIdx = idx & 0xffff;
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);
}
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
{
/* 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);
}
m_backend._setNormalProgram(m_lastLParam & 0x7fffffff);
}
}
void VSTEditor::selectNormalPage(int idx) { m_backend.setNormalProgram(idx); }
void VSTEditor::selectDrumPage(int idx) { m_backend.setDrumProgram(idx); }
}
} // namespace amuse

View File

@ -6,57 +6,54 @@
#include "aeffeditor.h"
namespace amuse
{
namespace amuse {
class VSTBackend;
/** Editor UI class */
class VSTEditor : public AEffEditor
{
friend class VSTBackend;
friend class AudioGroupFilePresenter;
friend struct AudioGroupCollection;
class VSTEditor : public AEffEditor {
friend class VSTBackend;
friend class AudioGroupFilePresenter;
friend struct AudioGroupCollection;
VSTBackend& m_backend;
ERect m_windowRect = {0, 0, 420, 600};
VSTBackend& m_backend;
ERect m_windowRect = {0, 0, 420, 600};
HWND m_rootView = 0;
HWND m_collectionTree = 0;
HWND m_collectionAdd = 0;
HWND m_collectionRemove = 0;
HWND m_groupListView = 0;
HWND m_pageListView = 0;
HWND m_rootView = 0;
HWND m_collectionTree = 0;
HWND m_collectionAdd = 0;
HWND m_collectionRemove = 0;
HWND m_groupListView = 0;
HWND m_pageListView = 0;
int m_selCollectionIdx = -1;
int m_selFileIdx = -1;
int m_selGroupIdx = -1;
int m_selPageIdx = -1;
int m_lastLParam = -1;
int m_selCollectionIdx = -1;
int m_selFileIdx = -1;
int m_selGroupIdx = -1;
int m_selPageIdx = -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 ColHeaderWindowProc(_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);
void _reselectColumns();
void _reselectColumns();
public:
VSTEditor(VSTBackend& backend);
VSTEditor(VSTBackend& backend);
bool getRect(ERect** rect);
bool open(void* ptr);
void close();
void update();
bool getRect(ERect** rect);
bool open(void* ptr);
void close();
void update();
void addAction();
void removeAction();
void addAction();
void removeAction();
void selectCollection(LPARAM idx);
void selectGroup(int idx);
void selectPage(int idx);
void reselectPage();
void selectNormalPage(int idx);
void selectDrumPage(int idx);
void selectCollection(LPARAM idx);
void selectGroup(int idx);
void selectPage(int idx);
void reselectPage();
void selectNormalPage(int idx);
void selectDrumPage(int idx);
};
}
} // namespace amuse

View File

@ -7,130 +7,111 @@
static logvisor::Module Log("amuseconv");
enum ConvType
{
ConvN64,
ConvGCN,
ConvPC
};
enum ConvType { ConvN64, ConvGCN, ConvPC };
static void ReportConvType(ConvType tp)
{
switch (tp)
{
case ConvN64:
Log.report(logvisor::Info, _SYS_STR("using N64 format"));
break;
case ConvPC:
Log.report(logvisor::Info, _SYS_STR("using PC format"));
break;
case ConvGCN:
default:
Log.report(logvisor::Info, _SYS_STR("using GameCube format"));
break;
static void ReportConvType(ConvType tp) {
switch (tp) {
case ConvN64:
Log.report(logvisor::Info, _SYS_STR("using N64 format"));
break;
case ConvPC:
Log.report(logvisor::Info, _SYS_STR("using PC format"));
break;
case ConvGCN:
default:
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)
{
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());
}
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _SYS_STR("/midifiles");
bool madeDir = false;
for (auto& pair : songs) {
if (!madeDir) {
amuse::Mkdir(targetPath.data(), 0755);
amuse::Mkdir(songsDir.c_str(), 0755);
madeDir = true;
}
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _SYS_STR("/midifiles");
bool madeDir = false;
for (auto& pair : songs)
{
if (!madeDir)
{
amuse::Mkdir(targetPath.data(), 0755);
amuse::Mkdir(songsDir.c_str(), 0755);
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);
}
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)
{
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
if (!fp)
return false;
static bool BuildSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath, int version, bool big) {
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
if (!fp)
return false;
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
if (out.empty())
return false;
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
if (out.empty())
return false;
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
return true;
return true;
}
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
{
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
if (!fp)
return false;
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath) {
FILE* fp = amuse::FOpen(inPath.data(), _SYS_STR("rb"));
if (!fp)
return false;
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::vector<uint8_t> data(sz, 0);
fread(&data[0], 1, sz, fp);
fclose(fp);
int extractedVersion;
bool isBig;
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
if (out.empty())
return false;
int extractedVersion;
bool isBig;
std::vector<uint8_t> out = amuse::SongConverter::SongToMIDI(data.data(), extractedVersion, isBig);
if (out.empty())
return false;
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
fp = amuse::FOpen(targetPath.data(), _SYS_STR("wb"));
fwrite(out.data(), 1, out.size(), fp);
fclose(fp);
return true;
return true;
}
#if _WIN32
@ -139,77 +120,64 @@ int wmain(int argc, const amuse::SystemChar** argv)
int main(int argc, const amuse::SystemChar** argv)
#endif
{
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;
}
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;
}
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)))
#endif
static inline void
SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...)
{
va_list va;
va_start(va, format);
SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...) {
va_list va;
va_start(va, format);
#if _WIN32
_vsnwprintf(str, maxlen, format, va);
_vsnwprintf(str, maxlen, format, va);
#else
vsnprintf(str, maxlen, format, va);
vsnprintf(str, maxlen, format, va);
#endif
va_end(va);
va_end(va);
}
#if _WIN32
@ -39,49 +38,44 @@ SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...
#include <signal.h>
static void abortHandler(int signum)
{
unsigned int i;
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
HANDLE process;
static void abortHandler(int signum) {
unsigned int i;
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for (i = 0; i < frames; i++)
{
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
for (i = 0; i < frames; i++) {
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;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line))
{
// SymGetLineFromAddr64 returned success
printf(" LINE %d\n", line.LineNumber);
}
else
{
printf("\n");
}
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line)) {
// SymGetLineFromAddr64 returned success
printf(" LINE %d\n", line.LineNumber);
} else {
printf("\n");
}
}
free(symbol);
free(symbol);
// If you caught one of the above signals, it is likely you just
// want to quit your program right now.
system("PAUSE");
exit(signum);
// If you caught one of the above signals, it is likely you just
// want to quit your program right now.
system("PAUSE");
exit(signum);
}
#endif
@ -95,456 +89,378 @@ int wmain(int argc, const boo::SystemChar** argv)
int main(int argc, const boo::SystemChar** argv)
#endif
{
logvisor::RegisterConsoleLogger();
logvisor::RegisterConsoleLogger();
std::vector<boo::SystemString> m_args;
m_args.reserve(argc);
double rate = NativeSampleRate;
int chCount = 2;
double volume = 1.0;
for (int i = 1; i < argc; ++i)
{
std::vector<boo::SystemString> m_args;
m_args.reserve(argc);
double rate = NativeSampleRate;
int chCount = 2;
double volume = 1.0;
for (int i = 1; i < argc; ++i) {
#if _WIN32
if (!wcsncmp(argv[i], L"-r", 2))
{
if (argv[i][2])
rate = wcstod(&argv[i][2], nullptr);
else if (argc > (i + 1))
{
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 (argc > (i + 1))
{
chCount = wcstoul(argv[i + 1], nullptr, 0);
++i;
}
}
else if (!wcsncmp(argv[i], L"-v", 2))
{
if (argv[i][2])
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]);
if (!wcsncmp(argv[i], L"-r", 2)) {
if (argv[i][2])
rate = wcstod(&argv[i][2], nullptr);
else if (argc > (i + 1)) {
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 (argc > (i + 1)) {
chCount = wcstoul(argv[i + 1], nullptr, 0);
++i;
}
} else if (!wcsncmp(argv[i], L"-v", 2)) {
if (argv[i][2])
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
if (!strncmp(argv[i], "-r", 2))
{
if (argv[i][2])
rate = strtod(&argv[i][2], nullptr);
else if (argc > (i + 1))
{
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 (argc > (i + 1))
{
chCount = strtoul(argv[i + 1], nullptr, 0);
++i;
}
}
else if (!strncmp(argv[i], "-v", 2))
{
if (argv[i][2])
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]);
if (!strncmp(argv[i], "-r", 2)) {
if (argv[i][2])
rate = strtod(&argv[i][2], nullptr);
else if (argc > (i + 1)) {
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 (argc > (i + 1)) {
chCount = strtoul(argv[i + 1], nullptr, 0);
++i;
}
} else if (!strncmp(argv[i], "-v", 2)) {
if (argv[i][2])
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
}
/* 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 (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;
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;
}
}
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());
}
amuse::Printf(_SYS_STR(" %d %s (Group %d, Setup %d)\n"), idx++, pair.first.c_str(), grpId, setupId);
}
int userSel = 0;
printf("Enter Group Number: ");
if (scanf("%d", &userSel) <= 0)
{
Log.report(logvisor::Error, "unable to parse prompt");
return 1;
printf("Enter Song Number: ");
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;
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;
}
}
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;
/* 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);
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;
}
m_setupId = userSel;
}
if (sortSetups.find(m_setupId) == sortSetups.cend())
{
Log.report(logvisor::Error, "unable to find setup %d", m_setupId);
return 1;
}
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
{
auto sfxSearch = allSFXGroups.find(m_groupId);
if (sfxSearch != allSFXGroups.end())
{
selData = &sfxSearch->second.first->second;
sfxIndex = sfxSearch->second.second;
}
} 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());
}
if (!selData)
{
Log.report(logvisor::Error, "unable to select audio group data");
int userSel = 0;
printf("Enter Group Number: ");
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;
}
m_setupId = userSel;
}
if (m_sfxGroup)
{
Log.report(logvisor::Error, "amuserender is currently only able to render SongGroups");
return 1;
if (sortSetups.find(m_setupId) == sortSetups.cend()) {
Log.report(logvisor::Error, "unable to find setup %d", m_setupId);
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;
} else {
auto sfxSearch = allSFXGroups.find(m_groupId);
if (sfxSearch != allSFXGroups.end()) {
selData = &sfxSearch->second.first->second;
sfxIndex = sfxSearch->second.second;
}
}
/* 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));
if (!selData) {
Log.report(logvisor::Error, "unable to select audio group data");
return 1;
}
printf("\n");
return 0;
if (m_sfxGroup) {
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
#include <shellapi.h>
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)
{
signal(SIGABRT, abortHandler);
signal(SIGSEGV, abortHandler);
signal(SIGILL, abortHandler);
signal(SIGFPE, abortHandler);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int) {
signal(SIGABRT, abortHandler);
signal(SIGSEGV, abortHandler);
signal(SIGILL, abortHandler);
signal(SIGFPE, abortHandler);
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i=0 ; i<argc ; ++i)
booArgv[i+1] = argv[i];
int argc = 0;
const boo::SystemChar** argv;
if (lpCmdLine[0])
argv = (const wchar_t**)(CommandLineToArgvW(lpCmdLine, &argc));
static boo::SystemChar selfPath[1024];
GetModuleFileNameW(nullptr, selfPath, 1024);
static const boo::SystemChar* booArgv[32] = {};
booArgv[0] = selfPath;
for (int i = 0; i < argc; ++i)
booArgv[i + 1] = argv[i];
logvisor::CreateWin32Console();
SetConsoleOutputCP(65001);
return wmain(argc + 1, booArgv);
logvisor::CreateWin32Console();
SetConsoleOutputCP(65001);
return wmain(argc + 1, booArgv);
}
#endif

View File

@ -5,113 +5,103 @@
#include "AudioGroupSampleDirectory.hpp"
#include <unordered_set>
namespace amuse
{
namespace amuse {
class AudioGroupData;
class ProjectDatabase;
/** Runtime audio group index container */
class AudioGroup
{
friend class AudioGroupSampleDirectory;
class AudioGroup {
friend class AudioGroupSampleDirectory;
protected:
AudioGroupProject m_proj;
AudioGroupPool m_pool;
AudioGroupSampleDirectory m_sdir;
const unsigned char* m_samp = nullptr;
SystemString m_groupPath; /* Typically only set by editor */
bool m_valid;
AudioGroupProject m_proj;
AudioGroupPool m_pool;
AudioGroupSampleDirectory m_sdir;
const unsigned char* m_samp = nullptr;
SystemString m_groupPath; /* Typically only set by editor */
bool m_valid;
public:
SystemString getSampleBasePath(SampleId sfxId) const;
operator bool() const { return m_valid; }
AudioGroup() = default;
explicit AudioGroup(const AudioGroupData& data) { assign(data); }
explicit AudioGroup(SystemStringView groupPath) { assign(groupPath); }
explicit AudioGroup(const AudioGroup& data, SystemStringView groupPath) { assign(data, groupPath); }
SystemString getSampleBasePath(SampleId sfxId) const;
operator bool() const { return m_valid; }
AudioGroup() = default;
explicit AudioGroup(const AudioGroupData& data) { assign(data); }
explicit AudioGroup(SystemStringView groupPath) { assign(groupPath); }
explicit AudioGroup(const AudioGroup& data, SystemStringView groupPath) { assign(data, groupPath); }
void assign(const AudioGroupData& data);
void assign(SystemStringView groupPath);
void assign(const AudioGroup& data, SystemStringView groupPath);
void setGroupPath(SystemStringView groupPath) { m_groupPath = groupPath; }
void assign(const AudioGroupData& data);
void assign(SystemStringView groupPath);
void assign(const AudioGroup& data, SystemStringView groupPath);
void setGroupPath(SystemStringView groupPath) { m_groupPath = groupPath; }
const SampleEntry* getSample(SampleId sfxId) const;
std::pair<ObjToken<SampleEntryData>, const unsigned char*>
getSampleData(SampleId sfxId, const SampleEntry* sample) const;
SampleFileState getSampleFileState(SampleId sfxId,
const SampleEntry* sample, SystemString* pathOut = nullptr) const;
void patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const;
void makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const;
void makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const;
const AudioGroupProject& getProj() const { return m_proj; }
const AudioGroupPool& getPool() const { return m_pool; }
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
AudioGroupProject& getProj() { return m_proj; }
AudioGroupPool& getPool() { return m_pool; }
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
const SampleEntry* getSample(SampleId sfxId) const;
std::pair<ObjToken<SampleEntryData>, const unsigned char*> getSampleData(SampleId sfxId,
const SampleEntry* sample) const;
SampleFileState getSampleFileState(SampleId sfxId, const SampleEntry* sample, SystemString* pathOut = nullptr) const;
void patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const;
void makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const;
void makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const;
const AudioGroupProject& getProj() const { return m_proj; }
const AudioGroupPool& getPool() const { return m_pool; }
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
AudioGroupProject& getProj() { return m_proj; }
AudioGroupPool& getPool() { return m_pool; }
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
virtual void setIdDatabases() const {}
virtual void setIdDatabases() const {}
};
class AudioGroupDatabase final : public AudioGroup
{
NameDB m_soundMacroDb;
NameDB m_sampleDb;
NameDB m_tableDb;
NameDB m_keymapDb;
NameDB m_layersDb;
class AudioGroupDatabase final : public AudioGroup {
NameDB m_soundMacroDb;
NameDB m_sampleDb;
NameDB m_tableDb;
NameDB m_keymapDb;
NameDB m_layersDb;
void _recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
std::unordered_set<SoundMacroId>& renamedIds);
void _recursiveRenameMacro(SoundMacroId id, std::string_view str, int& macroIdx,
std::unordered_set<SoundMacroId>& renamedIds);
public:
AudioGroupDatabase() = default;
explicit AudioGroupDatabase(const AudioGroupData& data)
{
setIdDatabases();
assign(data);
}
explicit AudioGroupDatabase(SystemStringView groupPath)
{
setIdDatabases();
assign(groupPath);
}
explicit AudioGroupDatabase(const AudioGroupDatabase& data, SystemStringView groupPath)
{
setIdDatabases();
assign(data, groupPath);
}
AudioGroupDatabase() = default;
explicit AudioGroupDatabase(const AudioGroupData& data) {
setIdDatabases();
assign(data);
}
explicit AudioGroupDatabase(SystemStringView groupPath) {
setIdDatabases();
assign(groupPath);
}
explicit AudioGroupDatabase(const AudioGroupDatabase& data, SystemStringView groupPath) {
setIdDatabases();
assign(data, groupPath);
}
void setIdDatabases() const
{
SoundMacroId::CurNameDB = const_cast<NameDB*>(&m_soundMacroDb);
SampleId::CurNameDB = const_cast<NameDB*>(&m_sampleDb);
TableId::CurNameDB = const_cast<NameDB*>(&m_tableDb);
KeymapId::CurNameDB = const_cast<NameDB*>(&m_keymapDb);
LayersId::CurNameDB = const_cast<NameDB*>(&m_layersDb);
}
void setIdDatabases() const {
SoundMacroId::CurNameDB = const_cast<NameDB*>(&m_soundMacroDb);
SampleId::CurNameDB = const_cast<NameDB*>(&m_sampleDb);
TableId::CurNameDB = const_cast<NameDB*>(&m_tableDb);
KeymapId::CurNameDB = const_cast<NameDB*>(&m_keymapDb);
LayersId::CurNameDB = const_cast<NameDB*>(&m_layersDb);
}
void renameSample(SampleId id, std::string_view str);
void deleteSample(SampleId id);
void copySampleInto(const SystemString& basePath, const SystemString& newBasePath);
void renameSample(SampleId id, std::string_view str);
void deleteSample(SampleId id);
void copySampleInto(const SystemString& basePath, const SystemString& newBasePath);
void importCHeader(std::string_view header);
std::string exportCHeader(std::string_view projectName, std::string_view groupName) const;
void importCHeader(std::string_view header);
std::string exportCHeader(std::string_view projectName, std::string_view groupName) const;
};
class ProjectDatabase
{
NameDB m_songDb;
NameDB m_sfxDb;
NameDB m_groupDb;
class ProjectDatabase {
NameDB m_songDb;
NameDB m_sfxDb;
NameDB m_groupDb;
public:
void setIdDatabases() const
{
SongId::CurNameDB = const_cast<NameDB*>(&m_songDb);
SFXId::CurNameDB = const_cast<NameDB*>(&m_sfxDb);
GroupId::CurNameDB = const_cast<NameDB*>(&m_groupDb);
}
void setIdDatabases() const {
SongId::CurNameDB = const_cast<NameDB*>(&m_songDb);
SFXId::CurNameDB = const_cast<NameDB*>(&m_sfxDb);
GroupId::CurNameDB = const_cast<NameDB*>(&m_groupDb);
}
};
}
} // namespace amuse

View File

@ -2,123 +2,111 @@
#include "Common.hpp"
namespace amuse
{
namespace amuse {
/** Simple pointer-container of the four Audio Group chunks */
class AudioGroupData
{
friend class Engine;
class AudioGroupData {
friend class Engine;
protected:
unsigned char* m_proj;
size_t m_projSz;
unsigned char* m_pool;
size_t m_poolSz;
unsigned char* m_sdir;
size_t m_sdirSz;
unsigned char* m_samp;
size_t m_sampSz;
unsigned char* m_proj;
size_t m_projSz;
unsigned char* m_pool;
size_t m_poolSz;
unsigned char* m_sdir;
size_t m_sdirSz;
unsigned char* m_samp;
size_t m_sampSz;
DataFormat m_fmt;
bool m_absOffs;
DataFormat m_fmt;
bool m_absOffs;
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)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(fmt)
, m_absOffs(absOffs)
{
}
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)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(fmt)
, m_absOffs(absOffs) {}
public:
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)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::GCN)
, 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)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::N64)
, m_absOffs(absOffs)
{
}
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, PCDataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::PC)
, m_absOffs(absOffs)
{
}
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)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::GCN)
, 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)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::N64)
, m_absOffs(absOffs) {}
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, PCDataTag)
: m_proj(proj)
, m_projSz(projSz)
, m_pool(pool)
, m_poolSz(poolSz)
, m_sdir(sdir)
, m_sdirSz(sdirSz)
, m_samp(samp)
, m_sampSz(sampSz)
, m_fmt(DataFormat::PC)
, m_absOffs(absOffs) {}
const unsigned char* getProj() const { return m_proj; }
const unsigned char* getPool() const { return m_pool; }
const unsigned char* getSdir() const { return m_sdir; }
const unsigned char* getSamp() const { return m_samp; }
const unsigned char* getProj() const { return m_proj; }
const unsigned char* getPool() const { return m_pool; }
const unsigned char* getSdir() const { return m_sdir; }
const unsigned char* getSamp() const { return m_samp; }
unsigned char* getProj() { return m_proj; }
unsigned char* getPool() { return m_pool; }
unsigned char* getSdir() { return m_sdir; }
unsigned char* getSamp() { return m_samp; }
unsigned char* getProj() { return m_proj; }
unsigned char* getPool() { return m_pool; }
unsigned char* getSdir() { return m_sdir; }
unsigned char* getSamp() { return m_samp; }
size_t getProjSize() const { return m_projSz; }
size_t getPoolSize() const { return m_poolSz; }
size_t getSdirSize() const { return m_sdirSz; }
size_t getSampSize() const { return m_sampSz; }
size_t getProjSize() const { return m_projSz; }
size_t getPoolSize() const { return m_poolSz; }
size_t getSdirSize() const { return m_sdirSz; }
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; }
bool getAbsoluteProjOffsets() const { return m_absOffs; }
DataFormat getDataFormat() const { return m_fmt; }
bool getAbsoluteProjOffsets() const { return m_absOffs; }
};
/** A buffer-owning version of AudioGroupData */
class IntrusiveAudioGroupData : public AudioGroupData
{
bool m_owns = true;
class IntrusiveAudioGroupData : public AudioGroupData {
bool m_owns = true;
public:
using AudioGroupData::AudioGroupData;
~IntrusiveAudioGroupData();
using AudioGroupData::AudioGroupData;
~IntrusiveAudioGroupData();
IntrusiveAudioGroupData(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData& operator=(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData& operator=(const IntrusiveAudioGroupData&) = delete;
IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept;
IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other) noexcept;
IntrusiveAudioGroupData(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 <memory>
namespace amuse
{
namespace amuse {
class AudioGroupData;
class AudioGroupPool;
class AudioGroupSampleDirectory;
enum class GroupType : atUint16
{
Song,
SFX
};
enum class GroupType : atUint16 { Song, SFX };
/** Header at top of project file */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
GroupHeader : BigDNA
{
AT_DECL_DNA
Value<atUint32, DNAEn> groupEndOff;
GroupIdDNA<DNAEn> groupId;
Value<GroupType, DNAEn> type;
Value<atUint32, DNAEn> soundMacroIdsOff;
Value<atUint32, DNAEn> samplIdsOff;
Value<atUint32, DNAEn> tableIdsOff;
Value<atUint32, DNAEn> keymapIdsOff;
Value<atUint32, DNAEn> layerIdsOff;
Value<atUint32, DNAEn> pageTableOff;
Value<atUint32, DNAEn> drumTableOff;
Value<atUint32, DNAEn> midiSetupsOff;
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) GroupHeader : BigDNA {
AT_DECL_DNA
Value<atUint32, DNAEn> groupEndOff;
GroupIdDNA<DNAEn> groupId;
Value<GroupType, DNAEn> type;
Value<atUint32, DNAEn> soundMacroIdsOff;
Value<atUint32, DNAEn> samplIdsOff;
Value<atUint32, DNAEn> tableIdsOff;
Value<atUint32, DNAEn> keymapIdsOff;
Value<atUint32, DNAEn> layerIdsOff;
Value<atUint32, DNAEn> pageTableOff;
Value<atUint32, DNAEn> drumTableOff;
Value<atUint32, DNAEn> midiSetupsOff;
};
/** Common index members of SongGroups and SFXGroups */
struct AudioGroupIndex {};
/** Root index of SongGroup */
struct SongGroupIndex : AudioGroupIndex
{
/** Maps GM program numbers to sound entities */
struct SongGroupIndex : AudioGroupIndex {
/** 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>
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;
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;
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>
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>
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);
void toYAML(athena::io::YAMLDocWriter& w) const;
void fromYAML(athena::io::YAMLDocReader& r);
};
/** Root index of SFXGroup */
struct SFXGroupIndex : AudioGroupIndex
{
/** Maps game-side SFX define IDs to sound entities */
struct SFXGroupIndex : AudioGroupIndex {
/** 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>
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;
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;
SFXEntry() = default;
SFXGroupIndex() = default;
SFXGroupIndex(const SFXGroupIndex& other);
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>
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);
void toYAML(athena::io::YAMLDocWriter& w) const;
void fromYAML(athena::io::YAMLDocReader& r);
};
/** Collection of SongGroup and SFXGroup indexes */
class AudioGroupProject
{
std::unordered_map<GroupId, ObjToken<SongGroupIndex>> m_songGroups;
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>> m_sfxGroups;
class AudioGroupProject {
std::unordered_map<GroupId, ObjToken<SongGroupIndex>> m_songGroups;
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>> m_sfxGroups;
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
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:
AudioGroupProject() = default;
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
static AudioGroupProject CreateAudioGroupProject(const AudioGroupProject& oldProj);
static void BootstrapObjectIDs(const AudioGroupData& data);
AudioGroupProject() = default;
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
static AudioGroupProject CreateAudioGroupProject(const AudioGroupProject& oldProj);
static void BootstrapObjectIDs(const AudioGroupData& data);
const SongGroupIndex* getSongGroupIndex(int groupId) const;
const SFXGroupIndex* getSFXGroupIndex(int groupId) const;
const SongGroupIndex* getSongGroupIndex(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<SFXGroupIndex>>& sfxGroups() const { return m_sfxGroups; }
std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() { return m_songGroups; }
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
const std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() const { return m_songGroups; }
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<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
std::vector<uint8_t> toYAML() const;
std::vector<uint8_t> toGCNData(const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const;
std::vector<uint8_t> toYAML() const;
std::vector<uint8_t> toGCNData(const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const;
AudioGroupProject(const AudioGroupProject&) = delete;
AudioGroupProject& operator=(const AudioGroupProject&) = delete;
AudioGroupProject(AudioGroupProject&&) = default;
AudioGroupProject& operator=(AudioGroupProject&&) = default;
AudioGroupProject(const AudioGroupProject&) = delete;
AudioGroupProject& operator=(const AudioGroupProject&) = delete;
AudioGroupProject(AudioGroupProject&&) = default;
AudioGroupProject& operator=(AudioGroupProject&&) = default;
};
}
} // namespace amuse

View File

@ -4,381 +4,351 @@
#include <cstdint>
#include "Common.hpp"
namespace amuse
{
namespace amuse {
class AudioGroupData;
class AudioGroupDatabase;
struct DSPADPCMHeader : BigDNA
{
AT_DECL_DNA
Value<atUint32> x0_num_samples;
Value<atUint32> x4_num_nibbles;
Value<atUint32> x8_sample_rate;
Value<atUint16> xc_loop_flag = 0;
Value<atUint16> xe_format = 0; /* 0 for ADPCM */
Value<atUint32> x10_loop_start_nibble = 0;
Value<atUint32> x14_loop_end_nibble = 0;
Value<atUint32> x18_ca = 0;
Value<atInt16> x1c_coef[8][2];
Value<atInt16> x3c_gain = 0;
Value<atInt16> x3e_ps = 0;
Value<atInt16> x40_hist1 = 0;
Value<atInt16> x42_hist2 = 0;
Value<atInt16> x44_loop_ps = 0;
Value<atInt16> x46_loop_hist1 = 0;
Value<atInt16> x48_loop_hist2 = 0;
Value<atUint8> m_pitch = 0; // Stash this in the padding
Seek<21, athena::Current> pad;
struct DSPADPCMHeader : BigDNA {
AT_DECL_DNA
Value<atUint32> x0_num_samples;
Value<atUint32> x4_num_nibbles;
Value<atUint32> x8_sample_rate;
Value<atUint16> xc_loop_flag = 0;
Value<atUint16> xe_format = 0; /* 0 for ADPCM */
Value<atUint32> x10_loop_start_nibble = 0;
Value<atUint32> x14_loop_end_nibble = 0;
Value<atUint32> x18_ca = 0;
Value<atInt16> x1c_coef[8][2];
Value<atInt16> x3c_gain = 0;
Value<atInt16> x3e_ps = 0;
Value<atInt16> x40_hist1 = 0;
Value<atInt16> x42_hist2 = 0;
Value<atInt16> x44_loop_ps = 0;
Value<atInt16> x46_loop_hist1 = 0;
Value<atInt16> x48_loop_hist2 = 0;
Value<atUint8> m_pitch = 0; // Stash this in the padding
Seek<21, athena::Current> pad;
};
struct VADPCMHeader : BigDNA
{
AT_DECL_DNA
Value<uint32_t> m_pitchSampleRate;
Value<uint32_t> m_numSamples;
Value<uint32_t> m_loopStartSample;
Value<uint32_t> m_loopLengthSamples;
struct VADPCMHeader : BigDNA {
AT_DECL_DNA
Value<uint32_t> m_pitchSampleRate;
Value<uint32_t> m_numSamples;
Value<uint32_t> m_loopStartSample;
Value<uint32_t> m_loopLengthSamples;
};
struct WAVFormatChunk : LittleDNA
{
AT_DECL_DNA
Value<atUint16> sampleFmt = 1;
Value<atUint16> numChannels = 1;
Value<atUint32> sampleRate;
Value<atUint32> byteRate; // sampleRate * numChannels * bitsPerSample/8
Value<atUint16> blockAlign = 2; // numChannels * bitsPerSample/8
Value<atUint16> bitsPerSample = 16;
struct WAVFormatChunk : LittleDNA {
AT_DECL_DNA
Value<atUint16> sampleFmt = 1;
Value<atUint16> numChannels = 1;
Value<atUint32> sampleRate;
Value<atUint32> byteRate; // sampleRate * numChannels * bitsPerSample/8
Value<atUint16> blockAlign = 2; // numChannels * bitsPerSample/8
Value<atUint16> bitsPerSample = 16;
};
struct WAVSampleChunk : LittleDNA
{
AT_DECL_DNA
Value<atUint32> smplManufacturer = 0;
Value<atUint32> smplProduct = 0;
Value<atUint32> smplPeriod; // 1 / sampleRate in nanoseconds
Value<atUint32> midiNote; // native MIDI note of sample
Value<atUint32> midiPitchFrac = 0;
Value<atUint32> smpteFormat = 0;
Value<atUint32> smpteOffset = 0;
Value<atUint32> numSampleLoops = 0;
Value<atUint32> additionalDataSize = 0;
struct WAVSampleChunk : LittleDNA {
AT_DECL_DNA
Value<atUint32> smplManufacturer = 0;
Value<atUint32> smplProduct = 0;
Value<atUint32> smplPeriod; // 1 / sampleRate in nanoseconds
Value<atUint32> midiNote; // native MIDI note of sample
Value<atUint32> midiPitchFrac = 0;
Value<atUint32> smpteFormat = 0;
Value<atUint32> smpteOffset = 0;
Value<atUint32> numSampleLoops = 0;
Value<atUint32> additionalDataSize = 0;
};
struct WAVSampleLoop : LittleDNA
{
AT_DECL_DNA
Value<atUint32> cuePointId = 0;
Value<atUint32> loopType = 0; // 0: forward loop
Value<atUint32> start;
Value<atUint32> end;
Value<atUint32> fraction = 0;
Value<atUint32> playCount = 0;
struct WAVSampleLoop : LittleDNA {
AT_DECL_DNA
Value<atUint32> cuePointId = 0;
Value<atUint32> loopType = 0; // 0: forward loop
Value<atUint32> start;
Value<atUint32> end;
Value<atUint32> fraction = 0;
Value<atUint32> playCount = 0;
};
struct WAVHeader : LittleDNA
{
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
struct WAVHeader : LittleDNA {
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 36; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 36; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
};
struct WAVHeaderLoop : LittleDNA
{
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
struct WAVHeaderLoop : LittleDNA {
AT_DECL_DNA
Value<atUint32> riffMagic = SBIG('RIFF');
Value<atUint32> wavChuckSize; // everything - 8
Value<atUint32> wavMagic = SBIG('WAVE');
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> fmtMagic = SBIG('fmt ');
Value<atUint32> fmtChunkSize = 16;
WAVFormatChunk fmtChunk;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 60; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
WAVSampleLoop sampleLoop;
Value<atUint32> smplMagic = SBIG('smpl');
Value<atUint32> smplChunkSize = 60; // 36 + numSampleLoops*24
WAVSampleChunk smplChunk;
WAVSampleLoop sampleLoop;
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
Value<atUint32> dataMagic = SBIG('data');
Value<atUint32> dataChunkSize; // numSamples * numChannels * bitsPerSample/8
};
enum class SampleFormat : uint8_t
{
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) */
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
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) */
enum class SampleFormat : uint8_t {
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)
*/
PCM, /**< Big-endian PCM found in MusyX2 demo GM instruments */
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) */
};
enum class SampleFileState
{
NoData,
MemoryOnlyWAV,
MemoryOnlyCompressed,
WAVRecent,
CompressedRecent,
CompressedNoWAV,
WAVNoCompressed
enum class SampleFileState {
NoData,
MemoryOnlyWAV,
MemoryOnlyCompressed,
WAVRecent,
CompressedRecent,
CompressedNoWAV,
WAVNoCompressed
};
/** Indexes individual samples in SAMP chunk */
class AudioGroupSampleDirectory
{
friend class AudioGroup;
class AudioGroupSampleDirectory {
friend class AudioGroup;
public:
union ADPCMParms {
struct DSPParms
{
uint16_t m_bytesPerFrame;
uint8_t m_ps;
uint8_t m_lps;
int16_t m_hist2;
int16_t m_hist1;
int16_t m_coefs[8][2];
} dsp;
struct VADPCMParms
{
int16_t m_coefs[8][2][8];
} vadpcm;
void swapBigDSP();
void swapBigVADPCM();
};
union ADPCMParms {
struct DSPParms {
uint16_t m_bytesPerFrame;
uint8_t m_ps;
uint8_t m_lps;
int16_t m_hist2;
int16_t m_hist1;
int16_t m_coefs[8][2];
} dsp;
struct VADPCMParms {
int16_t m_coefs[8][2][8];
} vadpcm;
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>
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;
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)) {}
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;
EntryDNA<DNAEn> toDNA(SFXId id) const {
return m_data->toDNA<DNAEn>(id);
}
/* 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>
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;
};
void loadLooseData(SystemStringView basePath);
SampleFileState getFileState(SystemStringView basePath, SystemString* pathOut = nullptr) const;
void patchSampleMetadata(SystemStringView basePath) const;
};
private:
std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp, bool compressWAV = false);
std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp, bool compressWAV = false);
public:
AudioGroupSampleDirectory() = default;
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
AudioGroupSampleDirectory() = default;
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
const std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() const { return m_entries; }
std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() { 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; }
void extractWAV(SampleId id, 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 extractAllCompressed(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 extractCompressed(SampleId id, 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& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
};
using SampleEntry = AudioGroupSampleDirectory::Entry;
using SampleEntryData = AudioGroupSampleDirectory::EntryData;
}
} // namespace amuse

View File

@ -10,136 +10,130 @@
#include <mutex>
#include <list>
namespace amuse
{
namespace amuse {
/** Backend voice implementation for boo mixer */
class BooBackendVoice : public IBackendVoice
{
friend class BooBackendVoiceAllocator;
Voice& m_clientVox;
struct VoiceCallback : boo::IAudioVoiceCallback
{
BooBackendVoice& m_parent;
void preSupplyAudio(boo::IAudioVoice& voice, double dt);
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
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, int32_t* in, int32_t* out);
void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out);
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
} m_cb;
boo::ObjToken<boo::IAudioVoice> m_booVoice;
class BooBackendVoice : public IBackendVoice {
friend class BooBackendVoiceAllocator;
Voice& m_clientVox;
struct VoiceCallback : boo::IAudioVoiceCallback {
BooBackendVoice& m_parent;
void preSupplyAudio(boo::IAudioVoice& voice, double dt);
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
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, int32_t* in, int32_t* out);
void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out);
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
} m_cb;
boo::ObjToken<boo::IAudioVoice> m_booVoice;
public:
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch);
void resetSampleRate(double sampleRate);
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch);
void resetSampleRate(double sampleRate);
void resetChannelLevels();
void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew);
void setPitchRatio(double ratio, bool slew);
void start();
void stop();
void resetChannelLevels();
void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew);
void setPitchRatio(double ratio, bool slew);
void start();
void stop();
};
/** Backend submix implementation for boo mixer */
class BooBackendSubmix : public IBackendSubmix
{
friend class BooBackendVoiceAllocator;
friend class BooBackendVoice;
Submix& m_clientSmx;
struct SubmixCallback : boo::IAudioSubmixCallback
{
BooBackendSubmix& m_parent;
bool canApplyEffect() const;
void applyEffect(int16_t* 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 applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
void resetOutputSampleRate(double sampleRate);
SubmixCallback(BooBackendSubmix& parent) : m_parent(parent) {}
} m_cb;
boo::ObjToken<boo::IAudioSubmix> m_booSubmix;
class BooBackendSubmix : public IBackendSubmix {
friend class BooBackendVoiceAllocator;
friend class BooBackendVoice;
Submix& m_clientSmx;
struct SubmixCallback : boo::IAudioSubmixCallback {
BooBackendSubmix& m_parent;
bool canApplyEffect() const;
void applyEffect(int16_t* 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 applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap, double sampleRate) const;
void resetOutputSampleRate(double sampleRate);
SubmixCallback(BooBackendSubmix& parent) : m_parent(parent) {}
} m_cb;
boo::ObjToken<boo::IAudioSubmix> m_booSubmix;
public:
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId);
void setSendLevel(IBackendSubmix* submix, float level, bool slew);
double getSampleRate() const;
SubmixFormat getSampleFormat() const;
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId);
void setSendLevel(IBackendSubmix* submix, float level, bool slew);
double getSampleRate() const;
SubmixFormat getSampleFormat() const;
};
/** Backend MIDI event reader for controlling sequencer with external hardware / software */
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
{
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;
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader {
friend class BooBackendVoiceAllocator;
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);
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;
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:
~BooBackendMIDIReader();
BooBackendMIDIReader(Engine& engine, bool useLock);
~BooBackendMIDIReader();
BooBackendMIDIReader(Engine& engine, bool useLock);
void addMIDIIn(const char* name);
void removeMIDIIn(const char* name);
bool hasMIDIIn(const char* name) const;
void setVirtualIn(bool v);
bool hasVirtualIn() const;
void addMIDIIn(const char* name);
void removeMIDIIn(const char* name);
bool hasMIDIIn(const char* name) const;
void setVirtualIn(bool v);
bool hasVirtualIn() const;
void pumpReader(double dt);
void pumpReader(double dt);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
void programChange(uint8_t chan, uint8_t program);
void channelPressure(uint8_t chan, uint8_t pressure);
void pitchBend(uint8_t chan, int16_t pitch);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
void programChange(uint8_t chan, uint8_t program);
void channelPressure(uint8_t chan, uint8_t pressure);
void pitchBend(uint8_t chan, int16_t pitch);
void allSoundOff(uint8_t chan);
void resetAllControllers(uint8_t chan);
void localControl(uint8_t chan, bool on);
void allNotesOff(uint8_t chan);
void omniMode(uint8_t chan, bool on);
void polyMode(uint8_t chan, bool on);
void allSoundOff(uint8_t chan);
void resetAllControllers(uint8_t chan);
void localControl(uint8_t chan, bool on);
void allNotesOff(uint8_t chan);
void omniMode(uint8_t chan, bool on);
void polyMode(uint8_t chan, bool on);
void sysex(const void* data, size_t len);
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
void songPositionPointer(uint16_t pointer);
void songSelect(uint8_t song);
void tuneRequest();
void sysex(const void* data, size_t len);
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
void songPositionPointer(uint16_t pointer);
void songSelect(uint8_t song);
void tuneRequest();
void startSeq();
void continueSeq();
void stopSeq();
void startSeq();
void continueSeq();
void stopSeq();
void reset();
void reset();
};
/** Backend voice allocator implementation for boo mixer */
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback
{
friend class BooBackendMIDIReader;
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback {
friend class BooBackendMIDIReader;
protected:
boo::IAudioVoiceEngine& m_booEngine;
Engine* m_cbInterface = nullptr;
boo::IAudioVoiceEngine& m_booEngine;
Engine* m_cbInterface = nullptr;
public:
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId);
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices();
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine);
void setCallbackInterface(Engine* engine);
AudioChannelSet getAvailableSet();
void setVolume(float vol);
void on5MsInterval(boo::IAudioVoiceEngine& engine, double dt);
void onPumpCycleComplete(boo::IAudioVoiceEngine& engine);
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId);
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices();
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine);
void setCallbackInterface(Engine* engine);
AudioChannelSet getAvailableSet();
void setVolume(float vol);
void on5MsInterval(boo::IAudioVoiceEngine& engine, double dt);
void onPumpCycleComplete(boo::IAudioVoiceEngine& engine);
};
}
} // namespace amuse

View File

@ -35,8 +35,7 @@
constexpr float NativeSampleRate = 32000.0f;
namespace amuse
{
namespace amuse {
struct NameDB;
using BigDNA = athena::io::DNA<athena::Big>;
@ -46,48 +45,45 @@ using LittleDNAV = athena::io::DNAVYaml<athena::Little>;
/** Common ID structure statically tagging
* SoundMacros, Tables, Keymaps, Layers, Samples, SFX, Songs */
struct ObjectId
{
uint16_t id = 0xffff;
operator uint16_t() const { return id; }
ObjectId() = default;
ObjectId(uint16_t idIn) : id(idIn) {}
ObjectId& operator=(uint16_t idIn) { id = idIn; return *this; }
static thread_local NameDB* CurNameDB;
struct ObjectId {
uint16_t id = 0xffff;
operator uint16_t() const { return id; }
ObjectId() = default;
ObjectId(uint16_t idIn) : id(idIn) {}
ObjectId& operator=(uint16_t idIn) {
id = idIn;
return *this;
}
static thread_local NameDB* CurNameDB;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
ObjectIdDNA : BigDNA
{
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
ObjectId id;
ObjectIdDNA() = default;
ObjectIdDNA(ObjectId idIn) : id(idIn) {}
operator ObjectId() const { return id; }
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) ObjectIdDNA : BigDNA {
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
ObjectId id;
ObjectIdDNA() = default;
ObjectIdDNA(ObjectId idIn) : id(idIn) {}
operator ObjectId() const { return id; }
};
#define DECL_ID_TYPE(type) \
struct type : ObjectId \
{ \
using ObjectId::ObjectId; \
type() = default; \
type(const ObjectId& id) : ObjectId(id) {} \
static thread_local NameDB* CurNameDB; \
}; \
template <athena::Endian DNAEn> \
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) \
type##DNA : BigDNA \
{ \
AT_DECL_EXPLICIT_DNA_YAML \
void _read(athena::io::YAMLDocReader& r); \
void _write(athena::io::YAMLDocWriter& w); \
type id; \
type##DNA() = default; \
type##DNA(type idIn) : id(idIn) {} \
operator type() const { return id; } \
};
#define DECL_ID_TYPE(type) \
struct type : ObjectId { \
using ObjectId::ObjectId; \
type() = default; \
type(const ObjectId& id) : ObjectId(id) {} \
static thread_local NameDB* CurNameDB; \
}; \
template <athena::Endian DNAEn> \
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) type##DNA : BigDNA { \
AT_DECL_EXPLICIT_DNA_YAML \
void _read(athena::io::YAMLDocReader& r); \
void _write(athena::io::YAMLDocWriter& w); \
type id; \
type##DNA() = default; \
type##DNA(type idIn) : id(idIn) {} \
operator type() const { return id; } \
};
DECL_ID_TYPE(SoundMacroId)
DECL_ID_TYPE(SampleId)
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,
* this indicates a layer type. */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
PageObjectIdDNA : BigDNA
{
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
ObjectId id;
PageObjectIdDNA() = default;
PageObjectIdDNA(ObjectId idIn) : id(idIn) {}
operator ObjectId() const { return id; }
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) PageObjectIdDNA : BigDNA {
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
ObjectId id;
PageObjectIdDNA() = default;
PageObjectIdDNA(ObjectId idIn) : id(idIn) {}
operator ObjectId() const { return id; }
};
struct SoundMacroStep
{
uint16_t step = 0;
operator uint16_t() const { return step; }
SoundMacroStep() = default;
SoundMacroStep(uint16_t idIn) : step(idIn) {}
SoundMacroStep& operator=(uint16_t idIn) { step = idIn; return *this; }
struct SoundMacroStep {
uint16_t step = 0;
operator uint16_t() const { return step; }
SoundMacroStep() = default;
SoundMacroStep(uint16_t idIn) : step(idIn) {}
SoundMacroStep& operator=(uint16_t idIn) {
step = idIn;
return *this;
}
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
SoundMacroStepDNA : BigDNA
{
AT_DECL_EXPLICIT_DNA_YAML
SoundMacroStep step;
SoundMacroStepDNA() = default;
SoundMacroStepDNA(SoundMacroStep idIn) : step(idIn) {}
operator SoundMacroStep() const { return step; }
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) SoundMacroStepDNA : BigDNA {
AT_DECL_EXPLICIT_DNA_YAML
SoundMacroStep step;
SoundMacroStepDNA() = default;
SoundMacroStepDNA(SoundMacroStep idIn) : step(idIn) {}
operator SoundMacroStep() const { return step; }
};
struct LittleUInt24 : LittleDNA
{
AT_DECL_EXPLICIT_DNA_YAML
atUint32 val;
operator uint32_t() const { return val; }
LittleUInt24() = default;
LittleUInt24(uint32_t valIn) : val(valIn) {}
LittleUInt24& operator=(uint32_t valIn) { val = valIn; return *this; }
struct LittleUInt24 : LittleDNA {
AT_DECL_EXPLICIT_DNA_YAML
atUint32 val;
operator uint32_t() const { return val; }
LittleUInt24() = default;
LittleUInt24(uint32_t valIn) : val(valIn) {}
LittleUInt24& operator=(uint32_t valIn) {
val = valIn;
return *this;
}
};
class IObj
{
std::atomic_int m_refCount = {0};
class IObj {
std::atomic_int m_refCount = {0};
protected:
virtual ~IObj() = default;
virtual ~IObj() = default;
public:
void increment() { m_refCount++; }
void decrement()
{
if (m_refCount.fetch_sub(1) == 1)
delete this;
}
void increment() { m_refCount++; }
void decrement() {
if (m_refCount.fetch_sub(1) == 1)
delete this;
}
};
template<class SubCls>
class ObjWrapper : public IObj
{
SubCls m_obj;
template <class SubCls>
class ObjWrapper : public IObj {
SubCls m_obj;
public:
template <class... _Args>
ObjWrapper(_Args&&... args) : m_obj(std::forward<_Args>(args)...) {}
SubCls* get() { return &m_obj; }
const SubCls* get() const { return &m_obj; }
template <class... _Args>
ObjWrapper(_Args&&... args) : m_obj(std::forward<_Args>(args)...) {}
SubCls* get() { return &m_obj; }
const SubCls* get() const { return &m_obj; }
};
template<class SubCls>
class ObjTokenBase
{
template <class SubCls>
class ObjTokenBase {
protected:
IObj* m_obj = nullptr;
ObjTokenBase(IObj* obj) : m_obj(obj) { if (m_obj) m_obj->increment(); }
IObj* m_obj = nullptr;
ObjTokenBase(IObj* obj) : m_obj(obj) {
if (m_obj)
m_obj->increment();
}
public:
ObjTokenBase() = default;
ObjTokenBase(const ObjTokenBase& other) : m_obj(other.m_obj) { if (m_obj) m_obj->increment(); }
ObjTokenBase(ObjTokenBase&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; }
ObjTokenBase& operator=(const ObjTokenBase& other)
{ if (m_obj) m_obj->decrement(); m_obj = other.m_obj; if (m_obj) m_obj->increment(); return *this; }
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; }
ObjTokenBase() = default;
ObjTokenBase(const ObjTokenBase& other) : m_obj(other.m_obj) {
if (m_obj)
m_obj->increment();
}
ObjTokenBase(ObjTokenBase&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; }
ObjTokenBase& operator=(const ObjTokenBase& other) {
if (m_obj)
m_obj->decrement();
m_obj = other.m_obj;
if (m_obj)
m_obj->increment();
return *this;
}
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>
class ObjToken : public ObjTokenBase<SubCls>
{
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
template <class SubCls, class Enable = void>
class ObjToken : public ObjTokenBase<SubCls> {
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
IObj* const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
public:
using ObjTokenBase<SubCls>::ObjTokenBase;
ObjToken() = default;
ObjToken(ObjWrapper<SubCls>* obj) : ObjTokenBase<SubCls>(obj) {}
ObjToken& operator=(ObjWrapper<SubCls>* obj)
{ if (_obj()) _obj()->decrement(); _obj() = obj; 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(); }
using ObjTokenBase<SubCls>::ObjTokenBase;
ObjToken() = default;
ObjToken(ObjWrapper<SubCls>* obj) : ObjTokenBase<SubCls>(obj) {}
ObjToken& operator=(ObjWrapper<SubCls>* obj) {
if (_obj())
_obj()->decrement();
_obj() = obj;
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>
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*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
template <class 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* const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
public:
using ObjTokenBase<SubCls>::ObjTokenBase;
ObjToken() = default;
ObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
ObjToken& operator=(IObj* obj)
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); 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()); }
using ObjTokenBase<SubCls>::ObjTokenBase;
ObjToken() = default;
ObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
ObjToken& operator=(IObj* obj) {
if (_obj())
_obj()->decrement();
_obj() = obj;
if (_obj())
_obj()->increment();
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!
* Bypasses type_traits tests for incomplete type definitions. */
template<class SubCls>
class IObjToken : public ObjTokenBase<SubCls>
{
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
template <class SubCls>
class IObjToken : public ObjTokenBase<SubCls> {
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
IObj* const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
public:
using ObjTokenBase<SubCls>::ObjTokenBase;
IObjToken() = default;
IObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
IObjToken& operator=(IObj* obj)
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); 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()); }
using ObjTokenBase<SubCls>::ObjTokenBase;
IObjToken() = default;
IObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
IObjToken& operator=(IObj* obj) {
if (_obj())
_obj()->decrement();
_obj() = obj;
if (_obj())
_obj()->increment();
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>
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)...);
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)...);
}
template <class Tp, class... _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)...);
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)...);
}
#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); }
#endif
static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath)
{
static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath) {
#if _WIN32
//return _wrename(oldpath, newpath);
return MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
// return _wrename(oldpath, newpath);
return MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
#else
return rename(oldpath, newpath);
return rename(oldpath, newpath);
#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); }
#endif
static inline int CompareCaseInsensitive(const SystemChar* a, const SystemChar* b)
{
static inline int CompareCaseInsensitive(const SystemChar* a, const SystemChar* b) {
#if _WIN32
return _wcsicmp(a, b);
return _wcsicmp(a, b);
#else
return strcasecmp(a, b);
return strcasecmp(a, b);
#endif
}
template <typename T>
static inline T clamp(T a, T val, T b)
{
return std::max<T>(a, std::min<T>(b, val));
static inline T clamp(T a, T val, T b) {
return std::max<T>(a, std::min<T>(b, val));
}
template <typename T>
inline T ClampFull(float in)
{
if (std::is_floating_point<T>())
{
return in;
}
inline T ClampFull(float in) {
if (std::is_floating_point<T>()) {
return in;
} else {
constexpr T MAX = std::numeric_limits<T>::max();
constexpr T MIN = std::numeric_limits<T>::min();
if (in < MIN)
return MIN;
else if (in > MAX)
return MAX;
else
{
constexpr T MAX = std::numeric_limits<T>::max();
constexpr T MIN = std::numeric_limits<T>::min();
if (in < MIN)
return MIN;
else if (in > MAX)
return MAX;
else
return in;
}
return in;
}
}
#ifndef M_PIF
@ -345,94 +378,86 @@ inline T ClampFull(float in)
__attribute__((__format__(__printf__, 1, 2)))
#endif
static inline void
Printf(const SystemChar* fmt, ...)
{
va_list args;
va_start(args, fmt);
Printf(const SystemChar* fmt, ...) {
va_list args;
va_start(args, fmt);
#if _WIN32
vwprintf(fmt, args);
vwprintf(fmt, args);
#else
vprintf(fmt, args);
vprintf(fmt, args);
#endif
va_end(args);
va_end(args);
}
#if __GNUC__
__attribute__((__format__(__printf__, 3, 4)))
#endif
static inline void
SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...)
{
va_list va;
va_start(va, format);
SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...) {
va_list va;
va_start(va, format);
#if _WIN32
_vsnwprintf(str, maxlen, format, va);
_vsnwprintf(str, maxlen, format, va);
#else
vsnprintf(str, maxlen, format, va);
vsnprintf(str, maxlen, format, va);
#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
return wcsrchr(str, ch);
return wcsrchr(str, ch);
#else
return strrchr(str, ch);
return strrchr(str, ch);
#endif
}
static inline SystemChar* StrRChr(SystemChar* str, SystemChar ch)
{
static inline SystemChar* StrRChr(SystemChar* str, SystemChar ch) {
#if _WIN32
return wcsrchr(str, ch);
return wcsrchr(str, ch);
#else
return strrchr(str, ch);
return strrchr(str, ch);
#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
return _fseeki64(fp, offset, whence);
return _fseeki64(fp, offset, whence);
#elif __APPLE__ || __FreeBSD__
return fseeko(fp, offset, whence);
return fseeko(fp, offset, whence);
#else
return fseeko64(fp, offset, whence);
return fseeko64(fp, offset, whence);
#endif
}
static inline int64_t FTell(FILE* fp)
{
static inline int64_t FTell(FILE* fp) {
#if _WIN32
return _ftelli64(fp);
return _ftelli64(fp);
#elif __APPLE__ || __FreeBSD__
return ftello(fp);
return ftello(fp);
#else
return ftello64(fp);
return ftello64(fp);
#endif
}
static inline FILE* FOpen(const SystemChar* path, const SystemChar* mode)
{
static inline FILE* FOpen(const SystemChar* path, const SystemChar* mode) {
#if _WIN32
FILE* fp = _wfopen(path, mode);
if (!fp)
return nullptr;
FILE* fp = _wfopen(path, mode);
if (!fp)
return nullptr;
#else
FILE* fp = fopen(path, mode);
if (!fp)
return nullptr;
FILE* fp = fopen(path, mode);
if (!fp)
return nullptr;
#endif
return fp;
return fp;
}
static inline void Unlink(const SystemChar* file)
{
static inline void Unlink(const SystemChar* file) {
#if _WIN32
_wunlink(file);
_wunlink(file);
#else
unlink(file);
unlink(file);
#endif
}
@ -444,43 +469,40 @@ bool Copy(const SystemChar* from, const SystemChar* to);
/* Type-sensitive byte swappers */
template <typename T>
static inline T bswap16(T val)
{
static inline T bswap16(T val) {
#if __GNUC__
return __builtin_bswap16(val);
return __builtin_bswap16(val);
#elif _WIN32
return _byteswap_ushort(val);
return _byteswap_ushort(val);
#else
return (val = (val << 8) | ((val >> 8) & 0xFF));
return (val = (val << 8) | ((val >> 8) & 0xFF));
#endif
}
template <typename T>
static inline T bswap32(T val)
{
static inline T bswap32(T val) {
#if __GNUC__
return __builtin_bswap32(val);
return __builtin_bswap32(val);
#elif _WIN32
return _byteswap_ulong(val);
return _byteswap_ulong(val);
#else
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
return val;
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
return val;
#endif
}
template <typename T>
static inline T bswap64(T val)
{
static inline T bswap64(T val) {
#if __GNUC__
return __builtin_bswap64(val);
return __builtin_bswap64(val);
#elif _WIN32
return _byteswap_uint64(val);
return _byteswap_uint64(val);
#else
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) |
((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) |
((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) |
((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56);
#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 int64_t SBig(int64_t val) { return bswap64(val); }
static inline uint64_t SBig(uint64_t val) { return bswap64(val); }
static inline float SBig(float val)
{
int32_t ival = bswap32(*((int32_t*)(&val)));
return *((float*)(&ival));
static inline float SBig(float val) {
int32_t ival = bswap32(*((int32_t*)(&val)));
return *((float*)(&ival));
}
static inline double SBig(double val)
{
int64_t ival = bswap64(*((int64_t*)(&val)));
return *((double*)(&ival));
static inline double SBig(double val) {
int64_t ival = bswap64(*((int64_t*)(&val)));
return *((double*)(&ival));
}
#ifndef SBIG
#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 int64_t SLittle(int64_t val) { return bswap64(val); }
static inline uint64_t SLittle(uint64_t val) { return bswap64(val); }
static inline float SLittle(float val)
{
int32_t ival = bswap32(*((int32_t*)(&val)));
return *((float*)(&ival));
static inline float SLittle(float val) {
int32_t ival = bswap32(*((int32_t*)(&val)));
return *((float*)(&ival));
}
static inline double SLittle(double val)
{
int64_t ival = bswap64(*((int64_t*)(&val)));
return *((double*)(&ival));
static inline double SLittle(double val) {
int64_t ival = bswap64(*((int64_t*)(&val)));
return *((double*)(&ival));
}
#ifndef SLITTLE
#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
/** Versioned data format to interpret */
enum class DataFormat
{
GCN,
N64,
PC
};
enum class DataFormat { GCN, N64, PC };
/** Meta-type for selecting GameCube (MusyX 2.0) data formats */
struct GCNDataTag
{
};
struct GCNDataTag {};
/** Meta-type for selecting N64 (MusyX 1.0) data formats */
struct N64DataTag
{
};
struct N64DataTag {};
/** Meta-type for selecting PC (MusyX 1.0) data formats */
struct PCDataTag
{
};
struct PCDataTag {};
template <class T>
static std::vector<std::pair<typename T::key_type,
std::reference_wrapper<typename T::mapped_type>>> SortUnorderedMap(T& um)
{
std::vector<std::pair<typename T::key_type, std::reference_wrapper<typename T::mapped_type>>> ret;
ret.reserve(um.size());
for (auto& p : um)
ret.emplace_back(p.first, p.second);
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
return ret;
static std::vector<std::pair<typename T::key_type, std::reference_wrapper<typename T::mapped_type>>>
SortUnorderedMap(T& um) {
std::vector<std::pair<typename T::key_type, std::reference_wrapper<typename T::mapped_type>>> ret;
ret.reserve(um.size());
for (auto& p : um)
ret.emplace_back(p.first, p.second);
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
return ret;
}
template <class T>
static std::vector<std::pair<typename T::key_type,
std::reference_wrapper<const typename T::mapped_type>>> SortUnorderedMap(const T& um)
{
std::vector<std::pair<typename T::key_type, std::reference_wrapper<const typename T::mapped_type>>> ret;
ret.reserve(um.size());
for (const auto& p : um)
ret.emplace_back(p.first, p.second);
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
return ret;
static std::vector<std::pair<typename T::key_type, std::reference_wrapper<const typename T::mapped_type>>>
SortUnorderedMap(const T& um) {
std::vector<std::pair<typename T::key_type, std::reference_wrapper<const typename T::mapped_type>>> ret;
ret.reserve(um.size());
for (const auto& p : um)
ret.emplace_back(p.first, p.second);
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
return ret;
}
template <class T>
static std::vector<typename T::key_type> SortUnorderedSet(T& us)
{
std::vector<typename T::key_type> ret;
ret.reserve(us.size());
for (auto& p : us)
ret.emplace_back(p);
std::sort(ret.begin(), ret.end());
return ret;
static std::vector<typename T::key_type> SortUnorderedSet(T& us) {
std::vector<typename T::key_type> ret;
ret.reserve(us.size());
for (auto& p : us)
ret.emplace_back(p);
std::sort(ret.begin(), ret.end());
return ret;
}
template <class T>
static std::vector<typename T::key_type> SortUnorderedSet(const T& us)
{
std::vector<typename T::key_type> ret;
ret.reserve(us.size());
for (const auto& p : us)
ret.emplace_back(p);
std::sort(ret.begin(), ret.end());
return ret;
}
static std::vector<typename T::key_type> SortUnorderedSet(const T& us) {
std::vector<typename T::key_type> ret;
ret.reserve(us.size());
for (const auto& p : us)
ret.emplace_back(p);
std::sort(ret.begin(), ret.end());
return ret;
}
} // namespace amuse
namespace std
{
#define DECL_ID_HASH(type) \
template<> \
struct hash<amuse::type> \
{ \
size_t operator()(const amuse::type& val) const noexcept { return val.id; } \
};
namespace std {
#define DECL_ID_HASH(type) \
template <> \
struct hash<amuse::type> { \
size_t operator()(const amuse::type& val) const noexcept { return val.id; } \
};
DECL_ID_HASH(ObjectId)
DECL_ID_HASH(SoundMacroId)
DECL_ID_HASH(SampleId)
@ -638,40 +639,26 @@ DECL_ID_HASH(SongId)
DECL_ID_HASH(SFXId)
DECL_ID_HASH(GroupId)
template<class T>
struct hash<amuse::ObjToken<T>>
{
size_t operator()(const amuse::ObjToken<T>& val) const noexcept { return hash<T*>()(val.get()); }
template <class T>
struct hash<amuse::ObjToken<T>> {
size_t operator()(const amuse::ObjToken<T>& val) const noexcept { return hash<T*>()(val.get()); }
};
}
} // namespace std
namespace amuse
{
struct NameDB
{
enum class Type
{
SoundMacro,
Table,
Keymap,
Layer,
Song,
SFX,
Group,
Sample
};
namespace amuse {
struct NameDB {
enum class Type { SoundMacro, Table, Keymap, Layer, Song, SFX, Group, Sample };
std::unordered_map<std::string, ObjectId> m_stringToId;
std::unordered_map<ObjectId, std::string> m_idToString;
std::unordered_map<std::string, ObjectId> m_stringToId;
std::unordered_map<ObjectId, std::string> m_idToString;
ObjectId generateId(Type tp) const;
static std::string generateName(ObjectId id, Type tp);
std::string generateDefaultName(Type tp) const;
std::string_view registerPair(std::string_view str, ObjectId id);
std::string_view resolveNameFromId(ObjectId id) const;
ObjectId resolveIdFromName(std::string_view str) const;
void remove(ObjectId id);
void rename(ObjectId id, std::string_view str);
ObjectId generateId(Type tp) const;
static std::string generateName(ObjectId id, Type tp);
std::string generateDefaultName(Type tp) const;
std::string_view registerPair(std::string_view str, ObjectId id);
std::string_view resolveNameFromId(ObjectId id) const;
ObjectId resolveIdFromName(std::string_view str) const;
void remove(ObjectId id);
void rename(ObjectId id, std::string_view str);
};
}
} // namespace amuse

View File

@ -6,41 +6,34 @@
#include <vector>
#include <memory>
namespace amuse
{
namespace amuse {
class ContainerRegistry
{
class ContainerRegistry {
public:
enum class Type
{
Invalid = -1,
Raw4 = 0,
MetroidPrime,
MetroidPrime2,
RogueSquadronPC,
RogueSquadronN64,
Factor5N64Rev,
RogueSquadron2,
RogueSquadron3
};
struct SongData
{
std::unique_ptr<uint8_t[]> m_data;
size_t m_size;
int16_t m_groupId;
int16_t m_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,
Type& typeOut);
static std::vector<std::pair<SystemString, SongData>> LoadSongs(const SystemChar* path);
enum class Type {
Invalid = -1,
Raw4 = 0,
MetroidPrime,
MetroidPrime2,
RogueSquadronPC,
RogueSquadronN64,
Factor5N64Rev,
RogueSquadron2,
RogueSquadron3
};
struct SongData {
std::unique_ptr<uint8_t[]> m_data;
size_t m_size;
int16_t m_groupId;
int16_t m_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,
Type& typeOut);
static std::vector<std::pair<SystemString, SongData>> LoadSongs(const SystemChar* path);
};
}
} // namespace amuse

View File

@ -3,36 +3,30 @@
#include <cstdint>
#include <cfloat>
static inline int16_t DSPSampClamp(int32_t val)
{
if (val < -32768) val = -32768;
else if (val > 32767) val = 32767;
return val;
static inline int16_t DSPSampClamp(int32_t val) {
if (val < -32768)
val = -32768;
else if (val > 32767)
val = 32767;
return val;
}
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
int16_t* prev2, unsigned lastSample);
unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
int16_t* prev2, unsigned lastSample);
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned firstSample, unsigned lastSample);
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1,
int16_t* prev2, unsigned firstSample, unsigned lastSample);
unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned DSPDecompressFrameStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in, const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned firstSample, unsigned lastSample);
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]);

View File

@ -3,64 +3,51 @@
#include "Common.hpp"
#include <vector>
namespace amuse
{
namespace amuse {
struct CaseInsensitiveCompare
{
bool operator()(std::string_view lhs, std::string_view rhs) const
{
struct CaseInsensitiveCompare {
bool operator()(std::string_view lhs, std::string_view rhs) const {
#if _WIN32
if (_stricmp(lhs.data(), rhs.data()) < 0)
if (_stricmp(lhs.data(), rhs.data()) < 0)
#else
if (strcasecmp(lhs.data(), rhs.data()) < 0)
if (strcasecmp(lhs.data(), rhs.data()) < 0)
#endif
return true;
return false;
}
return true;
return false;
}
#if _WIN32
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const
{
if (_wcsicmp(lhs.data(), rhs.data()) < 0)
return true;
return false;
}
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const {
if (_wcsicmp(lhs.data(), rhs.data()) < 0)
return true;
return false;
}
#endif
};
class DirectoryEnumerator
{
class DirectoryEnumerator {
public:
enum class Mode
{
Native,
DirsSorted,
FilesSorted,
DirsThenFilesSorted
};
struct Entry
{
SystemString m_path;
SystemString m_name;
size_t m_fileSz;
bool m_isDir;
enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted };
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)
: m_path(path), m_name(name), m_fileSz(sz), m_isDir(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) {}
};
private:
std::vector<Entry> m_entries;
std::vector<Entry> m_entries;
public:
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
bool reverse = false, bool noHidden = false);
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
bool reverse = false, bool noHidden = false);
operator bool() const { return m_entries.size() != 0; }
size_t size() const { return m_entries.size(); }
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
operator bool() const { return m_entries.size() != 0; }
size_t size() const { return m_entries.size(); }
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
};
}
} // namespace amuse

View File

@ -3,33 +3,21 @@
#include <cstdint>
#include <cstdlib>
namespace amuse
{
namespace amuse {
struct ChannelMap;
enum class EffectType
{
Invalid,
ReverbStd,
ReverbHi,
Delay,
Chorus,
EffectTypeMAX
};
enum class EffectType { Invalid, ReverbStd, ReverbHi, Delay, Chorus, EffectTypeMAX };
class EffectBaseTypeless
{
class EffectBaseTypeless {
public:
virtual ~EffectBaseTypeless() = default;
virtual void resetOutputSampleRate(double sampleRate) = 0;
virtual EffectType Isa() const = 0;
virtual ~EffectBaseTypeless() = default;
virtual void resetOutputSampleRate(double sampleRate) = 0;
virtual EffectType Isa() const = 0;
};
template <typename T>
class EffectBase : public EffectBaseTypeless
{
class EffectBase : public EffectBaseTypeless {
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 <cstdint>
namespace amuse
{
namespace amuse {
template <typename T>
class EffectChorusImp;
#define AMUSE_CHORUS_NUM_BLOCKS 3
/** Parameters needed to create EffectChorus */
struct EffectChorusInfo
{
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 period = 500; /**< [500, 10000] time (in ms) of one delay-shift cycle */
struct EffectChorusInfo {
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 period = 500; /**< [500, 10000] time (in ms) of one delay-shift cycle */
EffectChorusInfo() = default;
EffectChorusInfo(uint32_t baseDelay, uint32_t variation, uint32_t period)
: baseDelay(baseDelay), variation(variation), period(period) {}
EffectChorusInfo() = default;
EffectChorusInfo(uint32_t baseDelay, uint32_t variation, uint32_t period)
: baseDelay(baseDelay), variation(variation), period(period) {}
};
/** Mixes the audio back into itself after continuously-varying delay */
class EffectChorus
{
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 x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
bool m_dirty = true; /**< needs update of internal parameter data */
class EffectChorus {
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 x98_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
bool m_dirty = true; /**< needs update of internal parameter data */
template <typename T>
friend class EffectChorusImp;
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
template <typename T>
friend class EffectChorusImp;
EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
public:
template <typename T>
using ImpType = EffectChorusImp<T>;
template <typename T>
using ImpType = EffectChorusImp<T>;
void setBaseDelay(uint32_t baseDelay)
{
baseDelay = clamp(5u, baseDelay, 15u);
x90_baseDelay = baseDelay;
m_dirty = true;
}
uint32_t getBaseDelay() const { return x90_baseDelay; }
void setBaseDelay(uint32_t baseDelay) {
baseDelay = clamp(5u, baseDelay, 15u);
x90_baseDelay = baseDelay;
m_dirty = true;
}
uint32_t getBaseDelay() const { return x90_baseDelay; }
void setVariation(uint32_t variation)
{
variation = clamp(0u, variation, 5u);
x94_variation = variation;
m_dirty = true;
}
uint32_t getVariation() const { return x94_variation; }
void setVariation(uint32_t variation) {
variation = clamp(0u, variation, 5u);
x94_variation = variation;
m_dirty = true;
}
uint32_t getVariation() const { return x94_variation; }
void setPeriod(uint32_t period)
{
period = clamp(500u, period, 10000u);
x98_period = period;
m_dirty = true;
}
uint32_t getPeriod() const { return x98_period; }
void setPeriod(uint32_t period) {
period = clamp(500u, period, 10000u);
x98_period = period;
m_dirty = true;
}
uint32_t getPeriod() const { return x98_period; }
void updateParams(const EffectChorusInfo& info)
{
setBaseDelay(info.baseDelay);
setVariation(info.variation);
setPeriod(info.period);
}
void updateParams(const EffectChorusInfo& info) {
setBaseDelay(info.baseDelay);
setVariation(info.variation);
setPeriod(info.period);
}
};
/** Type-specific implementation of chorus effect */
template <typename T>
class EffectChorusImp : public EffectBase<T>, public EffectChorus
{
T* x0_lastChans[8][AMUSE_CHORUS_NUM_BLOCKS] = {}; /**< Evenly-allocated pointer-table for each channel's delay */
class EffectChorusImp : public EffectBase<T>, public EffectChorus {
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 */
T x28_oldChans[8][4] = {}; /**< Unprocessed history of previous 4 samples */
uint8_t x24_currentLast = 1; /**< Last 5ms block-idx to be processed */
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 x5c_currentPosHi = 0; /**< 16.7 fixed-point high-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 */
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 x68_pitchOffsetPeriod; /**< intermediate block window quantity for calculating SRC state */
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 x68_pitchOffsetPeriod; /**< intermediate block window quantity for calculating SRC state */
struct SrcInfo
{
T* x6c_dest; /**< selected channel's live buffer */
T* x70_smpBase; /**< selected channel's delay 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 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 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 x8c_target = 0; /**< value to reset to when trigger hit */
struct SrcInfo {
T* x6c_dest; /**< selected channel's live buffer */
T* x70_smpBase; /**< selected channel's delay 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 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 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 x8c_target = 0; /**< value to reset to when trigger hit */
void doSrc1(size_t blockSamples, size_t chanCount);
void doSrc2(size_t blockSamples, size_t chanCount);
} x6c_src;
void doSrc1(size_t blockSamples, size_t chanCount);
void doSrc2(size_t blockSamples, size_t chanCount);
} x6c_src;
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_sampsPerMs; /**< canonical count of samples per ms for the current backend */
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
void _setup(double sampleRate);
void _update();
void _setup(double sampleRate);
void _update();
public:
~EffectChorusImp();
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
EffectChorusImp(const EffectChorusInfo& info, double sampleRate)
: EffectChorusImp(info.baseDelay, info.variation, info.period, sampleRate) {}
~EffectChorusImp();
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
EffectChorusImp(const EffectChorusInfo& info, double sampleRate)
: EffectChorusImp(info.baseDelay, info.variation, info.period, sampleRate) {}
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
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 <memory>
namespace amuse
{
namespace amuse {
template <typename T>
class EffectDelayImp;
/** Parameters needed to create EffectDelay */
struct EffectDelayInfo
{
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 output[8] = {}; /**< [0, 100] total output percent */
struct EffectDelayInfo {
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 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)
{
arr[int(AudioChannel::FrontLeft)] = L;
arr[int(AudioChannel::FrontRight)] = R;
arr[int(AudioChannel::RearLeft)] = lerp(L, S, 0.75f);
arr[int(AudioChannel::RearRight)] = lerp(R, S, 0.75f);
arr[int(AudioChannel::FrontCenter)] = lerp(L, R, 0.5f);
arr[int(AudioChannel::LFE)] = arr[int(AudioChannel::FrontCenter)];
arr[int(AudioChannel::SideLeft)] = lerp(L, S, 0.5f);
arr[int(AudioChannel::SideRight)] = lerp(R, S, 0.5f);
}
static void Interp3To8(uint32_t arr[8], uint32_t L, uint32_t R, uint32_t S) {
arr[int(AudioChannel::FrontLeft)] = L;
arr[int(AudioChannel::FrontRight)] = R;
arr[int(AudioChannel::RearLeft)] = lerp(L, S, 0.75f);
arr[int(AudioChannel::RearRight)] = lerp(R, S, 0.75f);
arr[int(AudioChannel::FrontCenter)] = lerp(L, R, 0.5f);
arr[int(AudioChannel::LFE)] = arr[int(AudioChannel::FrontCenter)];
arr[int(AudioChannel::SideLeft)] = lerp(L, S, 0.5f);
arr[int(AudioChannel::SideRight)] = lerp(R, S, 0.5f);
}
EffectDelayInfo() { std::fill_n(delay, 8, 10); }
EffectDelayInfo(uint32_t delayL, uint32_t delayR, uint32_t delayS,
uint32_t feedbackL, uint32_t feedbackR, uint32_t feedbackS,
uint32_t outputL, uint32_t outputR, uint32_t outputS)
{
Interp3To8(delay, delayL, delayR, delayS);
Interp3To8(feedback, feedbackL, feedbackR, feedbackS);
Interp3To8(output, outputL, outputR, outputS);
}
EffectDelayInfo() { std::fill_n(delay, 8, 10); }
EffectDelayInfo(uint32_t delayL, uint32_t delayR, uint32_t delayS, uint32_t feedbackL, uint32_t feedbackR,
uint32_t feedbackS, uint32_t outputL, uint32_t outputR, uint32_t outputS) {
Interp3To8(delay, delayL, delayR, delayS);
Interp3To8(feedback, feedbackL, feedbackR, feedbackS);
Interp3To8(output, outputL, outputR, outputS);
}
};
/** Mixes the audio back into itself after specified delay */
class EffectDelay
{
class EffectDelay {
protected:
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 x54_output[8]; /**< [0, 100] total output percent */
bool m_dirty = true; /**< needs update of internal parameter data */
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 x54_output[8]; /**< [0, 100] total output percent */
bool m_dirty = true; /**< needs update of internal parameter data */
public:
template <typename T>
using ImpType = EffectDelayImp<T>;
template <typename T>
using ImpType = EffectDelayImp<T>;
void setDelay(uint32_t delay)
{
delay = clamp(10u, delay, 5000u);
for (int i = 0; i < 8; ++i)
x3c_delay[i] = delay;
m_dirty = true;
}
void setChanDelay(int chanIdx, uint32_t delay)
{
delay = clamp(10u, delay, 5000u);
x3c_delay[chanIdx] = delay;
m_dirty = true;
}
uint32_t getChanDelay(int chanIdx) const { return x3c_delay[chanIdx]; }
void setDelay(uint32_t delay) {
delay = clamp(10u, delay, 5000u);
for (int i = 0; i < 8; ++i)
x3c_delay[i] = delay;
m_dirty = true;
}
void setChanDelay(int chanIdx, uint32_t delay) {
delay = clamp(10u, delay, 5000u);
x3c_delay[chanIdx] = delay;
m_dirty = true;
}
uint32_t getChanDelay(int chanIdx) const { return x3c_delay[chanIdx]; }
void setFeedback(uint32_t feedback)
{
feedback = clamp(0u, feedback, 100u);
for (int i = 0; i < 8; ++i)
x48_feedback[i] = feedback;
m_dirty = true;
}
void setFeedback(uint32_t feedback) {
feedback = clamp(0u, feedback, 100u);
for (int i = 0; i < 8; ++i)
x48_feedback[i] = feedback;
m_dirty = true;
}
void setChanFeedback(int chanIdx, uint32_t feedback)
{
feedback = clamp(0u, feedback, 100u);
x48_feedback[chanIdx] = feedback;
m_dirty = true;
}
uint32_t getChanFeedback(int chanIdx) const { return x48_feedback[chanIdx]; }
void setChanFeedback(int chanIdx, uint32_t feedback) {
feedback = clamp(0u, feedback, 100u);
x48_feedback[chanIdx] = feedback;
m_dirty = true;
}
uint32_t getChanFeedback(int chanIdx) const { return x48_feedback[chanIdx]; }
void setOutput(uint32_t output)
{
output = clamp(0u, output, 100u);
for (int i = 0; i < 8; ++i)
x54_output[i] = output;
m_dirty = true;
}
void setOutput(uint32_t output) {
output = clamp(0u, output, 100u);
for (int i = 0; i < 8; ++i)
x54_output[i] = output;
m_dirty = true;
}
void setChanOutput(int chanIdx, uint32_t output)
{
output = clamp(0u, output, 100u);
x54_output[chanIdx] = output;
m_dirty = true;
}
uint32_t getChanOutput(int chanIdx) const { return x54_output[chanIdx]; }
void setChanOutput(int chanIdx, uint32_t output) {
output = clamp(0u, output, 100u);
x54_output[chanIdx] = output;
m_dirty = true;
}
uint32_t getChanOutput(int chanIdx) const { return x54_output[chanIdx]; }
void setParams(const EffectDelayInfo& info)
{
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);
x54_output[i] = clamp(0u, info.output[i], 100u);
}
m_dirty = true;
void setParams(const EffectDelayInfo& info) {
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);
x54_output[i] = clamp(0u, info.output[i], 100u);
}
m_dirty = true;
}
};
/** Type-specific implementation of delay effect */
template <typename T>
class EffectDelayImp : public EffectBase<T>, public EffectDelay
{
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
uint32_t xc_currentPos[8]; /**< per-channel block-index */
uint32_t x18_currentFeedback[8]; /**< [0, 128] feedback attenuator */
uint32_t x24_currentOutput[8]; /**< [0, 128] total attenuator */
class EffectDelayImp : public EffectBase<T>, public EffectDelay {
uint32_t x0_currentSize[8]; /**< per-channel delay-line buffer sizes */
uint32_t xc_currentPos[8]; /**< per-channel block-index */
uint32_t x18_currentFeedback[8]; /**< [0, 128] feedback 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_blockSamples; /**< count of samples in a 5ms block */
void _setup(double sampleRate);
void _update();
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 */
void _setup(double sampleRate);
void _update();
public:
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
EffectDelayImp(const EffectDelayInfo& info, double sampleRate);
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
EffectDelayImp(const EffectDelayInfo& info, double sampleRate);
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
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 <memory>
namespace amuse
{
namespace amuse {
/** Parameters needed to create EffectReverbStd */
struct EffectReverbStdInfo
{
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 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 preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
struct EffectReverbStdInfo {
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 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 preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */
EffectReverbStdInfo() = default;
EffectReverbStdInfo(float coloration, float mix, float time, float damping, float preDelay)
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay) {}
EffectReverbStdInfo() = default;
EffectReverbStdInfo(float coloration, float mix, float time, float damping, float preDelay)
: coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay) {}
};
/** Parameters needed to create EffectReverbHi */
struct EffectReverbHiInfo
{
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 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 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 */
struct EffectReverbHiInfo {
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 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 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 */
EffectReverbHiInfo() = default;
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) {}
EffectReverbHiInfo() = default;
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) {}
};
/** Delay state for one 'tap' of the reverb effect */
struct ReverbDelayLine
{
int32_t x0_inPoint = 0;
int32_t x4_outPoint = 0;
int32_t x8_length = 0;
std::unique_ptr<float[]> xc_inputs;
float x10_lastInput = 0.f;
struct ReverbDelayLine {
int32_t x0_inPoint = 0;
int32_t x4_outPoint = 0;
int32_t x8_length = 0;
std::unique_ptr<float[]> xc_inputs;
float x10_lastInput = 0.f;
void allocate(int32_t delay);
void setdelay(int32_t delay);
void allocate(int32_t delay);
void setdelay(int32_t delay);
};
template <typename T>
@ -56,166 +52,153 @@ template <typename T>
class EffectReverbHiImp;
/** Reverb effect with configurable reflection filtering */
class EffectReverbStd
{
class EffectReverbStd {
protected:
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a
room */
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 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 */
bool m_dirty = true; /**< needs update of internal parameter data */
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a
room */
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 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 */
bool m_dirty = true; /**< needs update of internal parameter data */
template <typename T>
friend class EffectReverbStdImp;
template <typename T>
friend class EffectReverbHiImp;
EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay);
template <typename T>
friend class EffectReverbStdImp;
template <typename T>
friend class EffectReverbHiImp;
EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay);
public:
template <typename T>
using ImpType = EffectReverbStdImp<T>;
template <typename T>
using ImpType = EffectReverbStdImp<T>;
void setColoration(float coloration)
{
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
m_dirty = true;
}
float getColoration() const { return x140_x1c8_coloration; }
void setColoration(float coloration) {
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
m_dirty = true;
}
float getColoration() const { return x140_x1c8_coloration; }
void setMix(float mix)
{
x144_x1cc_mix = clamp(0.f, mix, 1.f);
m_dirty = true;
}
float getMix() const { return x144_x1cc_mix; }
void setMix(float mix) {
x144_x1cc_mix = clamp(0.f, mix, 1.f);
m_dirty = true;
}
float getMix() const { return x144_x1cc_mix; }
void setTime(float time)
{
x148_x1d0_time = clamp(0.01f, time, 10.f);
m_dirty = true;
}
float getTime() const { return x148_x1d0_time; }
void setTime(float time) {
x148_x1d0_time = clamp(0.01f, time, 10.f);
m_dirty = true;
}
float getTime() const { return x148_x1d0_time; }
void setDamping(float damping)
{
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
m_dirty = true;
}
float getDamping() const { return x14c_x1d4_damping; }
void setDamping(float damping) {
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
m_dirty = true;
}
float getDamping() const { return x14c_x1d4_damping; }
void setPreDelay(float preDelay)
{
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
m_dirty = true;
}
float getPreDelay() const { return x150_x1d8_preDelay; }
void setPreDelay(float preDelay) {
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
m_dirty = true;
}
float getPreDelay() const { return x150_x1d8_preDelay; }
void setParams(const EffectReverbStdInfo& info)
{
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
}
void setParams(const EffectReverbStdInfo& info) {
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
}
};
/** Reverb effect with configurable reflection filtering, adds per-channel low-pass and crosstalk */
class EffectReverbHi : public EffectReverbStd
{
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
class EffectReverbHi : public EffectReverbStd {
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
template <typename T>
friend class EffectReverbHiImp;
EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
template <typename T>
friend class EffectReverbHiImp;
EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
public:
template <typename T>
using ImpType = EffectReverbHiImp<T>;
template <typename T>
using ImpType = EffectReverbHiImp<T>;
void setCrosstalk(float crosstalk)
{
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
m_dirty = true;
}
float getCrosstalk() const { return x1dc_crosstalk; }
void setCrosstalk(float crosstalk) {
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
m_dirty = true;
}
float getCrosstalk() const { return x1dc_crosstalk; }
void setParams(const EffectReverbHiInfo& info)
{
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
setCrosstalk(info.crosstalk);
}
void setParams(const EffectReverbHiInfo& info) {
setColoration(info.coloration);
setMix(info.mix);
setTime(info.time);
setDamping(info.damping);
setPreDelay(info.preDelay);
setCrosstalk(info.crosstalk);
}
};
/** Standard-quality 2-stage reverb */
template <typename T>
class EffectReverbStdImp : public EffectBase<T>, public EffectReverbStd
{
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_C[8][2] = {}; /**< Comb delay lines */
float xf0_allPassCoef = 0.f; /**< All-pass mix coefficient */
float xf4_combCoef[8][2] = {}; /**< Comb mix coefficients */
float x10c_lpLastout[8] = {}; /**< Last low-pass results */
float x118_level = 0.f; /**< Internal wet/dry mix factor */
float x11c_damping = 0.f; /**< Low-pass damping */
int32_t x120_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x124_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x130_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
class EffectReverbStdImp : public EffectBase<T>, public EffectReverbStd {
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_C[8][2] = {}; /**< Comb delay lines */
float xf0_allPassCoef = 0.f; /**< All-pass mix coefficient */
float xf4_combCoef[8][2] = {}; /**< Comb mix coefficients */
float x10c_lpLastout[8] = {}; /**< Last low-pass results */
float x118_level = 0.f; /**< Internal wet/dry mix factor */
float x11c_damping = 0.f; /**< Low-pass damping */
int32_t x120_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x124_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x130_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
public:
EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay, double sampleRate);
EffectReverbStdImp(const EffectReverbStdInfo& info, double sampleRate)
: EffectReverbStdImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, sampleRate) {}
EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay, double sampleRate);
EffectReverbStdImp(const EffectReverbStdInfo& info, double sampleRate)
: EffectReverbStdImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, sampleRate) {}
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
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 */
template <typename T>
class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi
{
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
ReverbDelayLine xb4_C[8][3] = {}; /**< Comb delay lines */
float x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
float x16c_combCoef[8][3] = {}; /**< Comb mix coefficients */
float x190_lpLastout[8] = {}; /**< Last low-pass results */
float x19c_level = 0.f; /**< Internal wet/dry mix factor */
float x1a0_damping = 0.f; /**< Low-pass damping */
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
float x1a8_internalCrosstalk = 0.f;
class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi {
ReverbDelayLine x0_AP[8][2] = {}; /**< All-pass delay lines */
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
ReverbDelayLine xb4_C[8][3] = {}; /**< Comb delay lines */
float x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
float x16c_combCoef[8][3] = {}; /**< Comb mix coefficients */
float x190_lpLastout[8] = {}; /**< Last low-pass results */
float x19c_level = 0.f; /**< Internal wet/dry mix factor */
float x1a0_damping = 0.f; /**< Low-pass damping */
int32_t x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
std::unique_ptr<float[]> x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
float* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
float x1a8_internalCrosstalk = 0.f;
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
double m_sampleRate; /**< copy of sample rate */
void _setup(double sampleRate);
void _update();
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
public:
EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay, float crosstalk,
double sampleRate);
EffectReverbHiImp(const EffectReverbHiInfo& info, double sampleRate)
: EffectReverbHiImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, info.crosstalk, sampleRate) {}
EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay, float crosstalk,
double sampleRate);
EffectReverbHiImp(const EffectReverbHiInfo& info, double 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 resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
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 <cfloat>
namespace amuse
{
namespace amuse {
class Listener;
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 Length(const Vector3f& a)
{
if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON)
return 0.f;
return std::sqrt(Dot(a, 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)
return 0.f;
return std::sqrt(Dot(a, a));
}
static inline float Normalize(Vector3f& out)
{
float dist = Length(out);
if (dist == 0.f)
return 0.f;
out[0] /= dist;
out[1] /= dist;
out[2] /= dist;
return dist;
static inline float Normalize(Vector3f& out) {
float dist = Length(out);
if (dist == 0.f)
return 0.f;
out[0] /= dist;
out[1] /= dist;
out[2] /= dist;
return dist;
}
/** Voice wrapper with positional-3D level control */
class Emitter : public Entity
{
ObjToken<Voice> m_vox;
Vector3f m_pos = {};
Vector3f m_dir = {};
float m_maxDist;
float m_maxVol = 1.f;
float m_minVol;
float m_falloff;
bool m_doppler;
bool m_dirty = true;
Voice::VolumeCache m_attCache;
class Emitter : public Entity {
ObjToken<Voice> m_vox;
Vector3f m_pos = {};
Vector3f m_dir = {};
float m_maxDist;
float m_maxVol = 1.f;
float m_minVol;
float m_falloff;
bool m_doppler;
bool m_dirty = true;
Voice::VolumeCache m_attCache;
friend class Engine;
void _destroy();
float _attenuationCurve(float dist) const;
void _update();
friend class Engine;
void _destroy();
float _attenuationCurve(float dist) const;
void _update();
public:
~Emitter();
Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox,
float maxDist, float minVol, float falloff, bool doppler);
~Emitter();
Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox, float maxDist, float minVol, float falloff,
bool doppler);
void setVectors(const float* pos, const float* dir);
void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); m_dirty = true; }
void setVectors(const float* pos, const float* dir);
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 "IBackendVoiceAllocator.hpp"
namespace amuse
{
namespace amuse {
class IBackendVoiceAllocator;
class Voice;
class Submix;
@ -22,175 +21,162 @@ class AudioGroup;
class AudioGroupData;
class IMIDIReader;
enum class AmplitudeMode
{
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
enum class AmplitudeMode {
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
};
/** Main audio playback system for a single audio output */
class Engine
{
friend class Voice;
friend class Emitter;
friend class Sequencer;
friend class Studio;
friend struct Sequencer::ChannelState;
class Engine {
friend class Voice;
friend class Emitter;
friend class Sequencer;
friend class Studio;
friend struct Sequencer::ChannelState;
IBackendVoiceAllocator& m_backend;
AmplitudeMode m_ampMode;
std::unique_ptr<IMIDIReader> m_midiReader;
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
std::list<ObjToken<Voice>> m_activeVoices;
std::list<ObjToken<Emitter>> m_activeEmitters;
std::list<ObjToken<Listener>> m_activeListeners;
std::list<ObjToken<Sequencer>> m_activeSequencers;
bool m_defaultStudioReady = false;
ObjToken<Studio> m_defaultStudio;
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;
int m_nextVid = 0;
float m_masterVolume = 1.f;
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
IBackendVoiceAllocator& m_backend;
AmplitudeMode m_ampMode;
std::unique_ptr<IMIDIReader> m_midiReader;
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
std::list<ObjToken<Voice>> m_activeVoices;
std::list<ObjToken<Emitter>> m_activeEmitters;
std::list<ObjToken<Listener>> m_activeListeners;
std::list<ObjToken<Sequencer>> m_activeSequencers;
bool m_defaultStudioReady = false;
ObjToken<Studio> m_defaultStudio;
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;
int m_nextVid = 0;
float m_masterVolume = 1.f;
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(GroupId groupId) const;
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(GroupId groupId) const;
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(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,
bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, GroupId groupId,
SongId setupId, ObjToken<Studio> studio);
ObjToken<Studio> _allocateStudio(bool mainOut);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
std::list<ObjToken<Sequencer>>::iterator
_destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
void _bringOutYourDead();
std::list<ObjToken<Voice>>::iterator _allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, GroupId groupId, SongId setupId,
ObjToken<Studio> studio);
ObjToken<Studio> _allocateStudio(bool mainOut);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
std::list<ObjToken<Sequencer>>::iterator _destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
void _bringOutYourDead();
public:
~Engine();
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
~Engine();
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
/** Access voice backend of engine */
IBackendVoiceAllocator& getBackend() { return m_backend; }
/** Access voice backend of engine */
IBackendVoiceAllocator& getBackend() { return m_backend; }
/** Access MIDI reader */
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
/** Access MIDI reader */
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
/** Add audio group data pointers to engine; must remain resident! */
const AudioGroup* addAudioGroup(const AudioGroupData& data);
/** Add audio group data pointers to engine; must remain resident! */
const AudioGroup* addAudioGroup(const AudioGroupData& data);
/** Remove audio group from engine */
void removeAudioGroup(const AudioGroupData& data);
/** Remove audio group from engine */
void removeAudioGroup(const AudioGroupData& data);
/** Access engine's default studio */
ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
/** Access engine's default studio */
ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
/** Create new Studio within engine */
ObjToken<Studio> addStudio(bool mainOut);
/** Create new Studio within engine */
ObjToken<Studio> addStudio(bool mainOut);
/** 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)
{
return fxStart(sfxId, vol, pan, m_defaultStudio);
}
/** 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) { return fxStart(sfxId, vol, pan, m_defaultStudio); }
/** 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)
{
return fxStart(group, groupId, sfxId, vol, pan, m_defaultStudio);
}
/** 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) {
return fxStart(group, groupId, sfxId, vol, pan, m_defaultStudio);
}
/** Start SoundMacro node playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
uint8_t vel, uint8_t mod)
{
return macroStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start SoundMacro node playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod) {
return macroStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start SoundMacro object playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
uint8_t vel, uint8_t mod)
{
return macroStart(group, macro, key, vel, mod, m_defaultStudio);
}
/** Start SoundMacro object playing directly (for editor use) */
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx);
ObjToken<Voice> macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel, uint8_t mod) {
return macroStart(group, macro, key, vel, mod, m_defaultStudio);
}
/** Start PageObject node playing directly (for editor use) */
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
uint8_t vel, uint8_t mod)
{
return pageObjectStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start PageObject node playing directly (for editor use) */
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx);
ObjToken<Voice> pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod) {
return pageObjectStart(group, id, key, vel, mod, m_defaultStudio);
}
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
SFXId sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
SFXId sfxId, float minVol, float maxVol, bool doppler)
{
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
}
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
float minVol, float maxVol, bool doppler) {
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
}
/** Build listener and add to engine's listener list */
ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume);
/** Build listener and add to engine's listener list */
ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume);
/** Remove listener from engine's listener list */
void removeListener(Listener* listener);
/** Remove listener from engine's listener list */
void removeListener(Listener* listener);
/** 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 = true)
{
return seqPlay(groupId, songId, arrData, loop, m_defaultStudio);
}
/** 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 = true) {
return seqPlay(groupId, songId, arrData, loop, m_defaultStudio);
}
/** 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, bool loop = true)
{
return seqPlay(group, groupId, songId, arrData, loop, m_defaultStudio);
}
/** 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,
bool loop = true) {
return seqPlay(group, groupId, songId, arrData, loop, m_defaultStudio);
}
/** Set total volume of engine */
void setVolume(float vol);
/** Set total volume of engine */
void setVolume(float vol);
/** Find voice from VoiceId */
ObjToken<Voice> findVoice(int vid);
/** Find voice from VoiceId */
ObjToken<Voice> findVoice(int vid);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Obtain next random number from engine's PRNG */
uint32_t nextRandom() { return m_random(); }
/** Obtain next random number from engine's PRNG */
uint32_t nextRandom() { return m_random(); }
/** Obtain list of active voices */
std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
/** Obtain list of active voices */
std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
/** Obtain total active voice count (including child voices) */
size_t getNumTotalActiveVoices() const;
/** Obtain total active voice count (including child voices) */
size_t getNumTotalActiveVoices() const;
/** Obtain list of active sequencers */
std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
/** Obtain list of active sequencers */
std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
/** All mixing occurs in virtual 5ms intervals;
* this is called at the start of each interval for all mixable entities */
void _on5MsInterval(IBackendVoiceAllocator& engine, double dt);
/** All mixing occurs in virtual 5ms intervals;
* this is called at the start of each interval for all mixable entities */
void _on5MsInterval(IBackendVoiceAllocator& engine, double dt);
/** When a pumping cycle is complete this is called to allow the client to
* perform periodic cleanup tasks */
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
/** When a pumping cycle is complete this is called to allow the client to
* perform periodic cleanup tasks */
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
};
}
} // namespace amuse

View File

@ -5,47 +5,40 @@
#include <cassert>
#include "Common.hpp"
namespace amuse
{
namespace amuse {
class Engine;
class AudioGroup;
/** Common 'engine child' class */
class Entity : public IObj
{
/* Only the Engine will manage Entity lifetimes,
* but shared_ptrs are issued to the client so it can safely track state */
friend class Engine;
friend struct SoundMacroState;
class Entity : public IObj {
/* Only the Engine will manage Entity lifetimes,
* but shared_ptrs are issued to the client so it can safely track state */
friend class Engine;
friend struct SoundMacroState;
protected:
bool m_destroyed = false;
void _destroy()
{
assert(!m_destroyed);
m_destroyed = true;
}
Engine& m_engine;
const AudioGroup& m_audioGroup;
GroupId m_groupId;
ObjectId m_objectId; /* if applicable */
bool m_destroyed = false;
void _destroy() {
assert(!m_destroyed);
m_destroyed = true;
}
Engine& m_engine;
const AudioGroup& m_audioGroup;
GroupId m_groupId;
ObjectId m_objectId; /* if applicable */
public:
Entity(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid = ObjectId())
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid)
{
}
~Entity()
{
/* Ensure proper destruction procedure followed */
assert(m_destroyed);
}
Entity(Engine& engine, const AudioGroup& group, GroupId groupId, ObjectId oid = ObjectId())
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid) {}
~Entity() {
/* Ensure proper destruction procedure followed */
assert(m_destroyed);
}
Engine& getEngine() { return m_engine; }
const AudioGroup& getAudioGroup() const { return m_audioGroup; }
GroupId getGroupId() const { return m_groupId; }
ObjectId getObjectId() const { return m_objectId; }
bool isDestroyed() const { return m_destroyed; }
Engine& getEngine() { return m_engine; }
const AudioGroup& getAudioGroup() const { return m_audioGroup; }
GroupId getGroupId() const { return m_groupId; }
ObjectId getObjectId() const { return m_objectId; }
bool isDestroyed() const { return m_destroyed; }
};
}
} // namespace amuse

View File

@ -2,42 +2,32 @@
#include "AudioGroupPool.hpp"
namespace amuse
{
namespace amuse {
class Voice;
/** Per-sample state tracker for ADSR envelope data */
class Envelope
{
class Envelope {
public:
enum class State
{
Attack,
Decay,
Sustain,
Release,
Complete
};
enum class State { Attack, Decay, Sustain, Release, Complete };
private:
State m_phase = State::Attack; /**< Current envelope state */
double m_attackTime = 0.01; /**< Time of attack in seconds */
double m_decayTime = 0.0; /**< Time of decay in seconds */
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
double m_releaseTime = 0.01; /**< Time of release in seconds */
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
bool m_adsrSet = false;
State m_phase = State::Attack; /**< Current envelope state */
double m_attackTime = 0.01; /**< Time of attack in seconds */
double m_decayTime = 0.0; /**< Time of decay in seconds */
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
double m_releaseTime = 0.01; /**< Time of release in seconds */
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
bool m_adsrSet = false;
public:
void reset(const ADSR* adsr);
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
void keyOff(const Voice& vox);
void keyOff();
float advance(double dt, const Voice& vox);
float advance(double dt);
bool isComplete() const { return m_phase == State::Complete; }
bool isAdsrSet() const { return m_adsrSet; }
void reset(const ADSR* adsr);
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
void keyOff(const Voice& vox);
void keyOff();
float advance(double dt, const Voice& vox);
float advance(double dt);
bool isComplete() const { return m_phase == State::Complete; }
bool isAdsrSet() const { return m_adsrSet; }
};
}
} // namespace amuse

View File

@ -2,32 +2,24 @@
#include <memory>
namespace amuse
{
namespace amuse {
class IBackendVoice;
class Voice;
enum class SubmixFormat
{
Int16,
Int32,
Float
};
enum class SubmixFormat { Int16, Int32, Float };
/** Client-implemented submix instance */
class IBackendSubmix
{
class IBackendSubmix {
public:
virtual ~IBackendSubmix() = default;
virtual ~IBackendSubmix() = default;
/** Set send level for submix (AudioChannel enum for array index) */
virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew) = 0;
/** Set send level for submix (AudioChannel enum for array index) */
virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew) = 0;
/** Amuse gets fixed sample rate of submix this way */
virtual double getSampleRate() const = 0;
/** Amuse gets fixed sample rate of submix this way */
virtual double getSampleRate() const = 0;
/** Amuse gets fixed sample format of submix this way */
virtual SubmixFormat getSampleFormat() const = 0;
/** Amuse gets fixed sample format of submix this way */
virtual SubmixFormat getSampleFormat() const = 0;
};
}
} // namespace amuse

View File

@ -1,53 +1,48 @@
#pragma once
namespace amuse
{
namespace amuse {
class IBackendSubmix;
/** Same channel enums from boo, used for matrix coefficient table index */
enum class AudioChannel
{
FrontLeft,
FrontRight,
RearLeft,
RearRight,
FrontCenter,
LFE,
SideLeft,
SideRight,
Unknown = 0xff
enum class AudioChannel {
FrontLeft,
FrontRight,
RearLeft,
RearRight,
FrontCenter,
LFE,
SideLeft,
SideRight,
Unknown = 0xff
};
/** Same structure from boo, used to represent interleaved speaker layout */
struct ChannelMap
{
unsigned m_channelCount = 0;
AudioChannel m_channels[8] = {};
struct ChannelMap {
unsigned m_channelCount = 0;
AudioChannel m_channels[8] = {};
};
/** Client-implemented voice instance */
class IBackendVoice
{
class IBackendVoice {
public:
virtual ~IBackendVoice() = default;
virtual ~IBackendVoice() = default;
/** Set new sample rate into platform voice (may result in artifacts while playing) */
virtual void resetSampleRate(double sampleRate) = 0;
/** Set new sample rate into platform voice (may result in artifacts while playing) */
virtual void resetSampleRate(double sampleRate) = 0;
/** Reset channel-gains to silence and unbind all submixes */
virtual void resetChannelLevels() = 0;
/** Reset channel-gains to silence and unbind all submixes */
virtual void resetChannelLevels() = 0;
/** Set channel-gains for audio source (AudioChannel enum for array index) */
virtual void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew) = 0;
/** Set channel-gains for audio source (AudioChannel enum for array index) */
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 */
virtual void setPitchRatio(double ratio, bool slew) = 0;
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
virtual void setPitchRatio(double ratio, bool slew) = 0;
/** Instructs platform to begin consuming sample data; invoking callback as needed */
virtual void start() = 0;
/** Instructs platform to begin consuming sample data; invoking callback as needed */
virtual void start() = 0;
/** Instructs platform to stop consuming sample data */
virtual void stop() = 0;
/** Instructs platform to stop consuming sample data */
virtual void stop() = 0;
};
}
} // namespace amuse

View File

@ -4,8 +4,7 @@
#include <functional>
#include <vector>
namespace amuse
{
namespace amuse {
class IBackendVoice;
class IBackendSubmix;
class Voice;
@ -13,49 +12,39 @@ class Submix;
class Engine;
/** Same enum as boo for describing speaker-configuration */
enum class AudioChannelSet
{
Stereo,
Quad,
Surround51,
Surround71,
Unknown = 0xff
};
enum class AudioChannelSet { Stereo, Quad, Surround51, Surround71, Unknown = 0xff };
/** Handle to MIDI-reader, implementation opaque */
class IMIDIReader
{
class IMIDIReader {
public:
virtual ~IMIDIReader() = default;
virtual void pumpReader(double dt) = 0;
virtual ~IMIDIReader() = default;
virtual void pumpReader(double dt) = 0;
};
/** Client-implemented voice allocator */
class IBackendVoiceAllocator
{
class IBackendVoiceAllocator {
public:
virtual ~IBackendVoiceAllocator() = default;
virtual ~IBackendVoiceAllocator() = default;
/** Amuse obtains a new voice from the platform this way */
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) = 0;
/** Amuse obtains a new voice from the platform this way */
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) = 0;
/** Amuse obtains a new submix from the platform this way */
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId) = 0;
/** Amuse obtains a new submix from the platform this way */
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId) = 0;
/** Amuse obtains a list of all MIDI devices this way */
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
/** Amuse obtains a list of all MIDI devices this way */
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
/** Amuse obtains an interactive MIDI-in connection from the OS this way */
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) = 0;
/** Amuse obtains an interactive MIDI-in connection from the OS this way */
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) = 0;
/** Amuse obtains speaker-configuration from the platform this way */
virtual AudioChannelSet getAvailableSet() = 0;
/** Amuse obtains speaker-configuration from the platform this way */
virtual AudioChannelSet getAvailableSet() = 0;
/** Set volume of main mix out */
virtual void setVolume(float vol) = 0;
/** Set volume of main mix out */
virtual void setVolume(float vol) = 0;
/** Amuse registers for key callback events from the mixing engine this way */
virtual void setCallbackInterface(Engine* engine) = 0;
/** Amuse registers for key callback events from the mixing engine this way */
virtual void setCallbackInterface(Engine* engine) = 0;
};
}
} // namespace amuse

View File

@ -2,27 +2,28 @@
#include "amuse/Emitter.hpp"
namespace amuse
{
class Listener
{
friend class Emitter;
friend class Engine;
Vector3f m_pos = {};
Vector3f m_dir = {};
Vector3f m_heading = {};
Vector3f m_up = {};
Vector3f m_right = {};
float m_volume;
float m_frontDiff;
float m_backDiff;
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; }
};
}
namespace amuse {
class Listener {
friend class Emitter;
friend class Engine;
Vector3f m_pos = {};
Vector3f m_dir = {};
Vector3f m_heading = {};
Vector3f m_up = {};
Vector3f m_right = {};
float m_volume;
float m_frontDiff;
float m_backDiff;
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;
}
};
} // namespace amuse

View File

@ -2,18 +2,15 @@
#include <cstdint>
static inline int16_t N64MusyXSampClamp(int32_t val)
{
if (val < -32768) val = -32768;
else if (val > 32767) val = 32767;
return val;
static inline int16_t N64MusyXSampClamp(int32_t val) {
if (val < -32768)
val = -32768;
else if (val > 32767)
val = 32767;
return val;
}
unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2][8],
unsigned lastSample);
unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], unsigned lastSample);
unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2][8],
unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8],
unsigned firstSample, unsigned lastSample);

View File

@ -10,158 +10,153 @@
#include <memory>
#include <list>
namespace amuse
{
namespace amuse {
/** State of sequencer over lifetime */
enum class SequencerState
{
Playing, /**< Sequencer actively playing arrangement */
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() */
enum class SequencerState {
Playing, /**< Sequencer actively playing arrangement */
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() */
};
/** Multi-voice lifetime manager and polyphonic parameter tracking */
class Sequencer : public Entity
{
friend class Engine;
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 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 */
ObjToken<Studio> m_studio; /**< Studio this sequencer outputs to */
class Sequencer : public Entity {
friend class Engine;
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 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 */
ObjToken<Studio> m_studio; /**< Studio this sequencer outputs to */
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
SongState m_songState; /**< State of current arrangement playback */
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
SongState m_songState; /**< State of current arrangement playback */
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
float m_curVol = 1.f; /**< Current volume of sequencer */
float m_volFadeTime = 0.f;
float m_volFadeTarget = 0.f;
float m_volFadeStart = 0.f;
float m_stopFadeTime = 0.f;
float m_stopFadeBeginVol = 0.f;
float m_curVol = 1.f; /**< Current volume of sequencer */
float m_volFadeTime = 0.f;
float m_volFadeTarget = 0.f;
float m_volFadeStart = 0.f;
float m_stopFadeTime = 0.f;
float m_stopFadeBeginVol = 0.f;
/** State of a single MIDI channel */
struct ChannelState
{
Sequencer* m_parent = nullptr;
uint8_t m_chanId;
const SongGroupIndex::MIDISetup* m_setup = nullptr; /* Channel defaults to program 0 if null */
const SongGroupIndex::PageEntry* m_page = nullptr;
~ChannelState();
ChannelState() = default;
ChannelState(Sequencer& parent, uint8_t chanId);
operator bool() const { return m_parent != nullptr; }
/** State of a single MIDI channel */
struct ChannelState {
Sequencer* m_parent = nullptr;
uint8_t m_chanId;
const SongGroupIndex::MIDISetup* m_setup = nullptr; /* Channel defaults to program 0 if null */
const SongGroupIndex::PageEntry* m_page = nullptr;
~ChannelState();
ChannelState() = default;
ChannelState(Sequencer& parent, uint8_t chanId);
operator bool() const { return m_parent != nullptr; }
/** Voices corresponding to currently-pressed keys in channel */
std::unordered_map<uint8_t, ObjToken<Voice>> m_chanVoxs;
std::unordered_set<ObjToken<Voice>> m_keyoffVoxs;
ObjToken<Voice> m_lastVoice;
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
int8_t m_pitchWheelRange = -1; /**< Pitch wheel range settable by RPN 0 */
int8_t m_curProgram = 0; /**< MIDI program number */
float m_curVol = 1.f; /**< Current volume of channel */
float m_curPan = 0.f; /**< Current panning of channel */
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
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 */
/** Voices corresponding to currently-pressed keys in channel */
std::unordered_map<uint8_t, ObjToken<Voice>> m_chanVoxs;
std::unordered_set<ObjToken<Voice>> m_keyoffVoxs;
ObjToken<Voice> m_lastVoice;
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
int8_t m_pitchWheelRange = -1; /**< Pitch wheel range settable by RPN 0 */
int8_t m_curProgram = 0; /**< MIDI program number */
float m_curVol = 1.f; /**< Current volume of channel */
float m_curPan = 0.f; /**< Current panning of channel */
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for channel */
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:
~Sequencer();
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup, SongId setupId,
ObjToken<Studio> studio);
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup,
ObjToken<Studio> studio);
~Sequencer();
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup, SongId setupId,
ObjToken<Studio> studio);
Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup,
ObjToken<Studio> studio);
/** Advance current song data (if any) */
void advance(double dt);
/** Advance current song data (if any) */
void advance(double dt);
/** Obtain pointer to Sequencer's Submix */
ObjToken<Studio> getStudio() { return m_studio; }
/** Obtain pointer to Sequencer's Submix */
ObjToken<Studio> getStudio() { return m_studio; }
/** Get current state of sequencer */
SequencerState state() const { return m_state; }
/** Get current state of sequencer */
SequencerState state() const { return m_state; }
/** Get number of active voices */
size_t getVoiceCount() const;
/** Get number of active voices */
size_t getVoiceCount() const;
/** Register key press with voice set */
ObjToken<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
/** Register key press with voice set */
ObjToken<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
/** Register key release with voice set */
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
/** Register key release with voice set */
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
/** Set MIDI control value [0,127] for all voices */
void setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val);
/** Set MIDI control value [0,127] for all voices */
void setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val);
/** Set pitchwheel value for use with voice controllers */
void setPitchWheel(uint8_t chan, float pitchWheel);
/** Set pitchwheel value for use with voice controllers */
void setPitchWheel(uint8_t chan, float pitchWheel);
/** Send keyoffs to all active notes, silence immediately if `now` set */
void allOff(bool now = false);
/** Send keyoffs to all active notes, silence immediately if `now` set */
void allOff(bool now = false);
/** Send keyoffs to all active notes on specified channel, silence immediately if `now` set */
void allOff(uint8_t chan, bool now = false);
/** Send keyoffs to all active notes on specified channel, silence immediately if `now` set */
void allOff(uint8_t chan, bool now = false);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now);
/** Find voice instance contained within Sequencer */
ObjToken<Voice> findVoice(int vid);
/** Find voice instance contained within Sequencer */
ObjToken<Voice> findVoice(int vid);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val);
/** Set tempo of sequencer and all voices in ticks per second */
void setTempo(uint8_t chan, double ticksPerSec);
void setTempo(double ticksPerSec);
/** Set tempo of sequencer and all voices in ticks per second */
void setTempo(uint8_t chan, double ticksPerSec);
void setTempo(double ticksPerSec);
/** Play MIDI arrangement */
void playSong(const unsigned char* arrData, bool loop = true, bool dieOnEnd = true);
/** Play MIDI arrangement */
void playSong(const unsigned char* arrData, bool loop = true, bool dieOnEnd = true);
/** Stop current MIDI arrangement */
void stopSong(float fadeTime = 0.f, bool now = false);
/** Stop current MIDI arrangement */
void stopSong(float fadeTime = 0.f, bool now = false);
/** Set total volume of sequencer */
void setVolume(float vol, float fadeTime = 0.f);
/** Set total volume of sequencer */
void setVolume(float vol, float fadeTime = 0.f);
/** Get current program number of channel */
int8_t getChanProgram(int8_t chanId) const;
/** Get current program number of channel */
int8_t getChanProgram(int8_t chanId) const;
/** Set current program number of channel */
bool setChanProgram(int8_t chanId, int8_t prog);
/** Set current program number of channel */
bool setChanProgram(int8_t chanId, int8_t prog);
/** Advance to next program in channel */
void nextChanProgram(int8_t chanId);
/** Advance to next program in channel */
void nextChanProgram(int8_t chanId);
/** Advance to prev program in channel */
void prevChanProgram(int8_t chanId);
/** Advance to prev program in channel */
void prevChanProgram(int8_t chanId);
/** Manually kill sequencer for deferred release from engine */
void kill() { m_state = SequencerState::Dead; }
/** Manually kill sequencer for deferred release from engine */
void kill() { m_state = SequencerState::Dead; }
};
}
} // namespace amuse

View File

@ -3,14 +3,11 @@
#include <vector>
#include <cstdint>
namespace amuse
{
namespace amuse {
class SongConverter
{
class SongConverter {
public:
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> 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);
};
}
} // namespace amuse

View File

@ -6,133 +6,121 @@
#include <list>
#include "Entity.hpp"
namespace amuse
{
namespace amuse {
class Sequencer;
enum class SongPlayState
{
Stopped,
Playing
};
enum class SongPlayState { Stopped, Playing };
/** Real-time state of Song execution */
class SongState
{
friend class Voice;
friend class SongConverter;
class SongState {
friend class Voice;
friend class SongConverter;
/** Song header */
struct Header
{
uint32_t m_trackIdxOff;
uint32_t m_regionIdxOff;
uint32_t m_chanMapOff;
uint32_t m_tempoTableOff;
uint32_t m_initialTempo; /* Top bit indicates per-channel looping */
uint32_t m_loopStartTicks[16];
uint32_t m_chanMapOff2;
void swapToBig();
void swapFromBig();
Header& operator=(const Header& other);
Header(const Header& other) { *this = other; }
Header() = default;
} m_header;
/** Song header */
struct Header {
uint32_t m_trackIdxOff;
uint32_t m_regionIdxOff;
uint32_t m_chanMapOff;
uint32_t m_tempoTableOff;
uint32_t m_initialTempo; /* Top bit indicates per-channel looping */
uint32_t m_loopStartTicks[16];
uint32_t m_chanMapOff2;
void swapToBig();
void swapFromBig();
Header& operator=(const Header& other);
Header(const Header& other) { *this = other; }
Header() = default;
} m_header;
/** Track region ('clip' in an NLA representation) */
struct TrackRegion
{
uint32_t m_startTick;
uint8_t m_progNum;
uint8_t m_unk1;
uint16_t m_unk2;
int16_t m_regionIndex; /* -1 to terminate song, -2 to loop to previous region */
int16_t m_loopToRegion;
bool indexDone(bool bigEndian, bool loop) const;
bool indexValid(bool bigEndian) const;
int indexLoop(bool bigEndian) const;
/** Track region ('clip' in an NLA representation) */
struct TrackRegion {
uint32_t m_startTick;
uint8_t m_progNum;
uint8_t m_unk1;
uint16_t m_unk2;
int16_t m_regionIndex; /* -1 to terminate song, -2 to loop to previous region */
int16_t m_loopToRegion;
bool indexDone(bool bigEndian, bool loop) const;
bool indexValid(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 */
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();
};
SongState* m_parent = nullptr;
uint8_t m_midiChan = 0xff; /**< MIDI channel number of song channel */
const TrackRegion* m_initRegion = nullptr; /**< Pointer to first track region */
const TrackRegion* m_curRegion = nullptr; /**< Pointer to currently-playing track region */
const TrackRegion* m_nextRegion = nullptr; /**< Pointer to next-queued track region */
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 */
double m_remDt = 0.0; /**< Remaining dt for keeping remainder between cycles */
uint32_t m_curTick = 0; /**< Current playback position for this track */
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 */
struct Track
{
struct Header
{
uint32_t m_type;
uint32_t m_pitchOff;
uint32_t m_modOff;
void swapBig();
};
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
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 */
SongState* m_parent = nullptr;
uint8_t m_midiChan = 0xff; /**< MIDI channel number of song channel */
const TrackRegion* m_initRegion = nullptr; /**< Pointer to first track region */
const TrackRegion* m_curRegion = nullptr; /**< Pointer to currently-playing track region */
const TrackRegion* m_nextRegion = nullptr; /**< Pointer to next-queued track region */
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) */
double m_remDt = 0.0; /**< Remaining dt for keeping remainder between cycles */
uint32_t m_curTick = 0; /**< Current playback position for this track */
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) */
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 */
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
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 */
SongPlayState m_songState = SongPlayState::Playing; /**< High-level state of Song playback */
bool m_loop = true; /**< Enable looping */
public:
/** Determine SNG version
* @param isBig returns true if big-endian SNG
* @return 0 for initial version, 1 for delta-time revision, -1 for non-SNG */
static int DetectVersion(const unsigned char* ptr, bool& isBig);
/** Determine SNG version
* @param isBig returns true if big-endian SNG
* @return 0 for initial version, 1 for delta-time revision, -1 for non-SNG */
static int DetectVersion(const unsigned char* ptr, bool& isBig);
/** initialize state for Song data at `ptr` */
bool initialize(const unsigned char* ptr, bool loop);
/** initialize state for Song data at `ptr` */
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
* @return `true` if END reached
*/
bool advance(Sequencer& seq, double dt);
/** advances `dt` seconds worth of commands in the Song
* @return `true` if END reached
*/
bool advance(Sequencer& seq, double dt);
};
}
} // namespace amuse

View File

@ -11,134 +11,114 @@
#undef SendMessage
#undef GetMessage
namespace amuse
{
namespace amuse {
class Voice;
/** Real-time state of SoundMacro execution */
struct SoundMacroState
{
/** 'program counter' stack for the active SoundMacro */
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);
}
struct SoundMacroState {
/** 'program counter' stack for the active SoundMacro */
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); }
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_initMod; /**< Modulation 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_curMod; /**< Current modulation played for this macro invocation */
uint32_t m_curPitch; /**< Current key played for this macro invocation (in cents) */
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_initMod; /**< Modulation 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_curMod; /**< Current modulation played for this macro invocation */
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) */
bool m_keyoff; /**< keyoff message has been received */
bool m_sampleEnd; /**< sample has finished playback */
double m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
bool m_keyoff; /**< keyoff message has been received */
bool m_sampleEnd; /**< sample has finished playback */
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_keyoffWait = false; /**< set when active wait is a keyoff wait */
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
double m_waitCountdown; /**< countdown timer for active wait */
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_keyoffWait = false; /**< set when active wait is a keyoff wait */
bool m_sampleEndWait = false; /**< set when active wait is a sampleend wait */
double m_waitCountdown; /**< countdown timer for active wait */
int m_loopCountdown = -1; /**< countdown for current loop */
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
int m_loopCountdown = -1; /**< countdown for current loop */
int m_lastPlayMacroVid = -1; /**< VoiceId from last PlayMacro command */
bool m_useAdsrControllers; /**< when set, use the following controllers for envelope times */
uint8_t m_midiAttack; /**< Attack MIDI controller */
uint8_t m_midiDecay; /**< Decay MIDI controller */
uint8_t m_midiSustain; /**< Sustain MIDI controller */
uint8_t m_midiRelease; /**< Release MIDI controller */
bool m_useAdsrControllers; /**< when set, use the following controllers for envelope times */
uint8_t m_midiAttack; /**< Attack MIDI controller */
uint8_t m_midiDecay; /**< Decay MIDI controller */
uint8_t m_midiSustain; /**< Sustain MIDI controller */
uint8_t m_midiRelease; /**< Release MIDI controller */
SoundMacro::CmdPortamento::PortState m_portamentoMode =
SoundMacro::CmdPortamento::PortState::MIDIControlled; /**< (0: Off, 1: On, 2: MIDI specified) */
SoundMacro::CmdPortamento::PortType m_portamentoType =
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 */
SoundMacro::CmdPortamento::PortState m_portamentoMode =
SoundMacro::CmdPortamento::PortState::MIDIControlled; /**< (0: Off, 1: On, 2: MIDI specified) */
SoundMacro::CmdPortamento::PortType m_portamentoType =
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 */
/** Used to build a multi-component formula for overriding controllers */
struct Evaluator
{
enum class Combine : uint8_t
{
Set,
Add,
Mult
};
enum class VarType : uint8_t
{
Ctrl,
Var
};
/** Used to build a multi-component formula for overriding controllers */
struct Evaluator {
enum class Combine : uint8_t { Set, Add, Mult };
enum class VarType : uint8_t { Ctrl, Var };
/** Represents one term of the formula assembled via *_SELECT commands */
struct Component
{
uint8_t m_midiCtrl;
float m_scale;
Combine m_combine;
VarType m_varType;
/** Represents one term of the formula assembled via *_SELECT commands */
struct Component {
uint8_t m_midiCtrl;
float m_scale;
Combine m_combine;
VarType m_varType;
Component(uint8_t midiCtrl, float scale, Combine combine, 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; }
Component(uint8_t midiCtrl, float scale, Combine combine, 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 */
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;
/** Combine additional component(s) to formula */
void addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType);
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 */
struct EventTrap
{
ObjectId macroId = 0xffff;
uint16_t macroStep;
};
/** Determine if able to use */
operator bool() const { return m_comps.size() != 0; }
};
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:
/** 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, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
/** 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, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod);
/** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached
*/
bool advance(Voice& vox, double dt);
/** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached
*/
bool advance(Voice& vox, double dt);
/** keyoff event */
void keyoffNotify(Voice& vox);
/** keyoff event */
void keyoffNotify(Voice& vox);
/** sample end event */
void sampleEndNotify(Voice& vox);
/** sample end event */
void sampleEndNotify(Voice& vox);
};
}
} // namespace amuse

View File

@ -6,50 +6,44 @@
#include "Submix.hpp"
#include <type_traits>
namespace amuse
{
namespace amuse {
struct StudioSend;
class Studio
{
friend class Engine;
Engine& m_engine;
Submix m_master;
Submix m_auxA;
Submix m_auxB;
class Studio {
friend class Engine;
Engine& m_engine;
Submix m_master;
Submix m_auxA;
Submix m_auxB;
std::list<StudioSend> m_studiosOut;
std::list<StudioSend> m_studiosOut;
#ifndef NDEBUG
bool _cyclicCheck(Studio* leaf);
bool _cyclicCheck(Studio* leaf);
#endif
public:
Studio(Engine& engine, bool mainOut);
Studio(Engine& engine, bool mainOut);
/** Register a target Studio to send this Studio's mixing busses */
void addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB);
/** Register a target Studio to send this Studio's mixing busses */
void addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB);
/** Advise submixes of changing sample rate */
void resetOutputSampleRate(double sampleRate);
/** Advise submixes of changing sample rate */
void resetOutputSampleRate(double sampleRate);
Submix& getMaster() { return m_master; }
Submix& getAuxA() { return m_auxA; }
Submix& getAuxB() { return m_auxB; }
Submix& getMaster() { return m_master; }
Submix& getAuxA() { return m_auxA; }
Submix& getAuxB() { return m_auxB; }
Engine& getEngine() { return m_engine; }
Engine& getEngine() { return m_engine; }
};
struct StudioSend
{
ObjToken<Studio> m_targetStudio;
float m_dryLevel;
float m_auxALevel;
float m_auxBLevel;
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB)
{
}
struct StudioSend {
ObjToken<Studio> m_targetStudio;
float m_dryLevel;
float m_auxALevel;
float m_auxBLevel;
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float 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 <unordered_set>
namespace amuse
{
namespace amuse {
class IBackendSubmix;
class Sequencer;
/** Intermediate mix of voices for applying auxiliary effects */
class Submix
{
friend class Engine;
friend class Voice;
friend class Sequencer;
Engine& m_root;
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 */
class Submix {
friend class Engine;
friend class Voice;
friend class Sequencer;
Engine& m_root;
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 */
public:
Submix(Engine& engine);
Submix(Engine& engine);
/** Construct new effect */
template <class T, class... Args>
std::unique_ptr<EffectBaseTypeless> _makeEffect(Args... args)
{
switch (m_backendSubmix->getSampleFormat())
{
case SubmixFormat::Int16:
{
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());
}
}
/** Construct new effect */
template <class T, class... Args>
std::unique_ptr<EffectBaseTypeless> _makeEffect(Args... args) {
switch (m_backendSubmix->getSampleFormat()) {
case SubmixFormat::Int16: {
using ImpType = typename T::template ImpType<int16_t>;
return std::make_unique<ImpType>(args..., m_backendSubmix->getSampleRate());
}
/** Add new effect to effect stack and assume ownership */
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());
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());
}
}
}
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
/** Add new effect to effect stack and assume ownership */
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 */
EffectChorus& makeChorus(const EffectChorusInfo& info);
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
/** Add new chorus effect to effect stack and assume ownership */
EffectChorus& makeChorus(const EffectChorusInfo& info);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(const EffectDelayInfo& info);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay);
/** Add new delay effect to effect stack and assume ownership */
EffectDelay& makeDelay(const EffectDelayInfo& info);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(const EffectReverbStdInfo& info);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay,
float crosstalk);
/** Add new standard-quality reverb effect to effect stack and assume ownership */
EffectReverbStd& makeReverbStd(const EffectReverbStdInfo& info);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info);
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
/** Remove and deallocate all effects from effect stack */
void clearEffects() { m_effectStack.clear(); }
/** Add new high-quality reverb effect to effect stack and assume ownership */
EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info);
/** Returns true when an effect callback is bound */
bool canApplyEffect() const { return m_effectStack.size() != 0; }
/** Remove and deallocate all effects from effect stack */
void clearEffects() { m_effectStack.clear(); }
/** in/out transformation entry for audio effect */
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** Returns true when an effect callback is bound */
bool canApplyEffect() const { return m_effectStack.size() != 0; }
/** in/out transformation entry for audio effect */
void applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** in/out transformation entry for audio effect */
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** in/out transformation entry for audio effect */
void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** in/out transformation entry for audio effect */
void applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
/** advice effects of changing sample rate */
void resetOutputSampleRate(double sampleRate);
/** in/out transformation entry for audio effect */
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 "Studio.hpp"
namespace amuse
{
namespace amuse {
class IBackendVoice;
struct Keymap;
struct LayerMapping;
/** State of voice over lifetime */
enum class VoiceState
{
Playing, /**< SoundMacro actively executing, not in KeyOff */
KeyOff, /**< KeyOff event issued, macro beginning fade-out */
Dead /**< Default state, causes Engine to remove voice at end of pump cycle */
enum class VoiceState {
Playing, /**< SoundMacro actively executing, not in KeyOff */
KeyOff, /**< KeyOff event issued, macro beginning fade-out */
Dead /**< Default state, causes Engine to remove voice at end of pump cycle */
};
/** Individual source of audio */
class Voice : public Entity
{
friend class Engine;
friend class Sequencer;
friend struct SoundMacroState;
friend class Envelope;
friend class Emitter;
friend struct SoundMacro::CmdScaleVolume;
friend struct SoundMacro::CmdKeyOff;
friend struct SoundMacro::CmdScaleVolumeDLS;
friend struct SoundMacro::CmdReturn;
friend struct SoundMacro::CmdGoSub;
friend struct SoundMacro::CmdTrapEvent;
friend struct SoundMacro::CmdUntrapEvent;
friend struct SoundMacro::CmdGetMessage;
friend struct SoundMacro::CmdModeSelect;
class Voice : public Entity {
friend class Engine;
friend class Sequencer;
friend struct SoundMacroState;
friend class Envelope;
friend class Emitter;
friend struct SoundMacro::CmdScaleVolume;
friend struct SoundMacro::CmdKeyOff;
friend struct SoundMacro::CmdScaleVolumeDLS;
friend struct SoundMacro::CmdReturn;
friend struct SoundMacro::CmdGoSub;
friend struct SoundMacro::CmdTrapEvent;
friend struct SoundMacro::CmdUntrapEvent;
friend struct SoundMacro::CmdGetMessage;
friend struct SoundMacro::CmdModeSelect;
struct VolumeCache
{
bool m_curDLS = false; /**< Current DLS status of cached lookup */
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 getVolume(float vol, bool dls);
float getCachedVolume() const { return m_curVolLUTVal; }
};
struct VolumeCache {
bool m_curDLS = false; /**< Current DLS status of cached lookup */
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 getVolume(float vol, bool dls);
float getCachedVolume() const { return m_curVolLUTVal; }
};
struct Panning
{
double m_time; /**< time of PANNING command */
double m_dur; /**< requested duration 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 */
};
struct Panning {
double m_time; /**< time of PANNING command */
double m_dur; /**< requested duration 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 */
};
void _setObjectId(ObjectId id) { m_objectId = id; }
void _setObjectId(ObjectId id) { m_objectId = id; }
int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */
ObjToken<Studio> m_studio; /**< Studio this voice outputs to */
IObjToken<Sequencer> m_sequencer; /**< Strong reference to parent sequencer to retain ctrl vals */
int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */
ObjToken<Studio> m_studio; /**< Studio this voice outputs to */
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 */
SoundMacroState m_state; /**< State container for SoundMacro playback */
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_messageTrap; /**< Trap for messages sent from other SoundMacros */
int32_t m_latestMessage = 0; /**< Latest message received on voice */
std::list<ObjToken<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */
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_messageTrap; /**< Trap for messages sent from other SoundMacros */
int32_t m_latestMessage = 0; /**< Latest message received on voice */
std::list<ObjToken<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
ObjToken<SampleEntryData> m_curSample; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
int16_t m_prev1 = 0; /**< DSPADPCM 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_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) */
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_nextLevel = 0.f; /**< Next computed level used for lerp-mode amplitude */
VolumeCache m_nextLevelCache;
VolumeCache m_lerpedCache;
ObjToken<SampleEntryData> m_curSample; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
int16_t m_prev1 = 0; /**< DSPADPCM 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_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) */
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_nextLevel = 0.f; /**< Next computed level used for lerp-mode amplitude */
VolumeCache m_nextLevelCache;
VolumeCache m_lerpedCache;
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
bool m_sustained = false; /**< Sustain pedal pressed for this voice */
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
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_curUserVol = 1.f; /**< Current user volume of voice */
float m_curVol = 1.f; /**< Current 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_curPan = 0.f; /**< Current pan of voice */
float m_curSpan = -1.f; /**< Current surround pan of voice */
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_pitchWheelDown = 200; /**< Down range for pitchwheel control in cents */
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
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_needsSlew = false; /**< next _setTotalPitch will be slewed */
bool m_dlsVol = false; /**< Use DLS LUT for resolving voice volume */
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_curVol = 1.f; /**< Current 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_curPan = 0.f; /**< Current pan of voice */
float m_curSpan = -1.f; /**< Current surround pan of voice */
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_pitchWheelDown = 200; /**< Down range for pitchwheel control in cents */
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
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_needsSlew = false; /**< next _setTotalPitch will be slewed */
bool m_dlsVol = false; /**< Use DLS LUT for resolving voice volume */
Envelope m_volAdsr; /**< Volume envelope */
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 */
float m_envelopeStart; /**< initial value for last ENVELOPE command */
float m_envelopeEnd; /**< final value for last ENVELOPE command */
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
Envelope m_volAdsr; /**< Volume envelope */
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 */
float m_envelopeStart; /**< initial value for last ENVELOPE command */
float m_envelopeEnd; /**< final value for last ENVELOPE command */
const Curve* m_envelopeCurve; /**< curve to use for ENVELOPE command */
bool m_pitchEnv = false; /**< Pitch envelope activated */
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */
bool m_pitchEnv = false; /**< Pitch envelope activated */
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
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 */
int32_t m_portamentoTarget; /**< destination pitch for latest portamento invocation */
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_pitchSweep1 = 0; /**< Current value of PITCHSWEEP1 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_pitchSweep2Add = 0; /**< Value to add to PITCHSWEEP2 controller each cycle */
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_pitchSweep1It = 0; /**< Current iteration of PITCHSWEEP1 controller */
uint8_t m_pitchSweep2It = 0; /**< Current iteration of PITCHSWEEP2 controller */
int32_t m_pitchSweep1 = 0; /**< Current value of PITCHSWEEP1 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_pitchSweep2Add = 0; /**< Value to add to PITCHSWEEP2 controller each cycle */
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_pitchSweep1It = 0; /**< Current iteration of PITCHSWEEP1 controller */
uint8_t m_pitchSweep2It = 0; /**< Current iteration of PITCHSWEEP2 controller */
std::queue<Panning> m_panningQueue; /**< Queue of PANNING commands */
std::queue<Panning> m_spanningQueue; /**< Queue of SPANNING commands */
std::queue<Panning> m_panningQueue; /**< Queue of PANNING 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 */
int32_t m_vibratoLevel = 0; /**< scale of vibrato effect (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 */
bool m_vibratoModWheel = false; /**< vibrato scaled with mod-wheel if set */
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_vibratoModLevel = 0; /**< scale of vibrato mod-wheel influence (in cents) */
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 */
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_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_lfoPeriods[2] = {}; /**< time-periods for LFO1 and LFO2 */
std::unique_ptr<int8_t[]> m_ctrlValsSelf; /**< Self-owned MIDI Controller values */
int8_t* m_extCtrlVals = nullptr; /**< MIDI Controller values (external storage) */
float m_lfoPeriods[2] = {}; /**< time-periods for LFO1 and LFO2 */
std::unique_ptr<int8_t[]> m_ctrlValsSelf; /**< Self-owned MIDI Controller values */
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();
bool _checkSamplePos(bool& looped);
void _doKeyOff();
void _macroKeyOff();
void _macroSampleEnd();
void _procSamplePre(int16_t& samp);
VolumeCache m_masterCache;
template <typename T>
T _procSampleMaster(double time, T samp);
VolumeCache m_auxACache;
template <typename T>
T _procSampleAuxA(double time, T samp);
VolumeCache m_auxBCache;
template <typename T>
T _procSampleAuxB(double time, T samp);
void _setTotalPitch(int32_t cents, bool slew);
bool _isRecursivelyDead();
void _bringOutYourDead();
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
void _destroy();
bool _checkSamplePos(bool& looped);
void _doKeyOff();
void _macroKeyOff();
void _macroSampleEnd();
void _procSamplePre(int16_t& samp);
VolumeCache m_masterCache;
template <typename T>
T _procSampleMaster(double time, T samp);
VolumeCache m_auxACache;
template <typename T>
T _procSampleAuxA(double time, T samp);
VolumeCache m_auxBCache;
template <typename T>
T _procSampleAuxB(double time, T samp);
void _setTotalPitch(int32_t cents, bool slew);
bool _isRecursivelyDead();
void _bringOutYourDead();
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
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 _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadKeymap(const Keymap* keymap, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
bool pushPc = false);
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
void _setPan(float pan);
void _setSurroundPan(float span);
void _setChannelCoefs(const float coefs[8]);
void _setPitchWheel(float pitchWheel);
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
void _setPan(float pan);
void _setSurroundPan(float span);
void _setChannelCoefs(const float coefs[8]);
void _setPitchWheel(float pitchWheel);
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
public:
~Voice();
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,
ObjToken<Studio> studio);
~Voice();
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,
ObjToken<Studio> studio);
/** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */
void preSupplyAudio(double dt);
/** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */
void preSupplyAudio(double dt);
/** Request specified count of audio frames (samples) from voice,
* internally advancing the voice stream */
size_t supplyAudio(size_t frames, int16_t* data);
/** Request specified count of audio frames (samples) from voice,
* internally advancing the voice stream */
size_t supplyAudio(size_t frames, int16_t* data);
/** Called three times after resampling supplyAudio output, voice should
* 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, int32_t* in, int32_t* out);
void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
/** Called three times after resampling supplyAudio output, voice should
* 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, int32_t* in, int32_t* out);
void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
/** Obtain pointer to Voice's Studio */
ObjToken<Studio> getStudio() { return m_studio; }
/** Obtain pointer to Voice's Studio */
ObjToken<Studio> getStudio() { return m_studio; }
/** Get current state of voice */
VoiceState state() const { return m_voxState; }
/** Get current state of voice */
VoiceState state() const { return m_voxState; }
/** Get VoiceId of this voice (unique to all currently-playing voices) */
int vid() const { return m_vid; }
/** Get VoiceId of this voice (unique to all currently-playing voices) */
int vid() const { return m_vid; }
/** Get max VoiceId of this voice and any contained children */
int maxVid() const;
/** Get max VoiceId of this voice and any contained children */
int maxVid() const;
/** Allocate parallel macro and tie to voice for possible emitter influence */
ObjToken<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
/** Allocate parallel macro and tie to voice for possible emitter influence */
ObjToken<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
/** Load specified SoundMacro Object from within group into voice */
bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** Load specified SoundMacro Object from within group into voice */
bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** Load specified SoundMacro Object into voice */
bool loadMacroObject(const SoundMacro* macro, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** Load specified SoundMacro Object into voice */
bool loadMacroObject(const SoundMacro* macro, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc = false);
/** 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);
/** 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);
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
void keyOff();
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
void keyOff();
/** Sends numeric message to voice and all siblings */
void message(int32_t val);
/** Sends numeric message to voice and all siblings */
void message(int32_t val);
/** Start playing specified sample from within group, optionally by sample offset */
void startSample(SampleId sampId, int32_t offset);
/** Start playing specified sample from within group, optionally by sample offset */
void startSample(SampleId sampId, int32_t offset);
/** Stop playing current sample */
void stopSample();
/** Stop playing current sample */
void stopSample();
/** Set current voice volume immediately */
void setVolume(float vol);
/** Set current voice volume immediately */
void setVolume(float vol);
/** Set current voice panning immediately */
void setPan(float pan);
/** Set current voice panning immediately */
void setPan(float pan);
/** Set current voice surround-panning immediately */
void setSurroundPan(float span);
/** Set current voice surround-panning immediately */
void setSurroundPan(float span);
/** Set current voice channel coefficients immediately */
void setChannelCoefs(const float coefs[8]);
/** Set current voice channel coefficients immediately */
void setChannelCoefs(const float coefs[8]);
/** Start volume envelope to specified level */
void startEnvelope(double dur, float vol, const Curve* envCurve);
/** Start volume envelope to specified level */
void startEnvelope(double dur, float vol, const Curve* envCurve);
/** Start volume envelope from zero to current level */
void startFadeIn(double dur, float vol, const Curve* envCurve);
/** Start volume envelope from zero to current level */
void startFadeIn(double dur, float vol, const Curve* envCurve);
/** Start pan envelope to specified position */
void startPanning(double dur, uint8_t panPos, int8_t panWidth);
/** Start pan envelope to specified position */
void startPanning(double dur, uint8_t panPos, int8_t panWidth);
/** Start span envelope to specified position */
void startSpanning(double dur, uint8_t spanPos, int8_t spanWidth);
/** Start span envelope to specified position */
void startSpanning(double dur, uint8_t spanPos, int8_t spanWidth);
/** Set voice relative-pitch in cents */
void setPitchKey(int32_t cents);
/** Set voice relative-pitch in cents */
void setPitchKey(int32_t cents);
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
void setPedal(bool pedal);
/** Set sustain status within voice; clearing will trigger a deferred keyoff */
void setPedal(bool pedal);
/** Set doppler factor for voice */
void setDoppler(float doppler);
/** Set doppler factor for voice */
void setDoppler(float doppler);
/** Set vibrato parameters for voice */
void setVibrato(int32_t level, bool modScale, float period);
/** Set vibrato parameters for voice */
void setVibrato(int32_t level, bool modScale, float period);
/** Configure modwheel influence range over vibrato */
void setMod2VibratoRange(int32_t modLevel);
/** Configure modwheel influence range over vibrato */
void setMod2VibratoRange(int32_t modLevel);
/** Setup tremolo parameters for voice */
void setTremolo(float tremoloScale, float tremoloModScale);
/** Setup tremolo parameters for voice */
void setTremolo(float tremoloScale, float tremoloModScale);
/** Setup LFO1 for voice */
void setLFO1Period(float period) { m_lfoPeriods[0] = period; }
/** Setup LFO1 for voice */
void setLFO1Period(float period) { m_lfoPeriods[0] = period; }
/** Setup LFO2 for voice */
void setLFO2Period(float period) { m_lfoPeriods[1] = period; }
/** Setup LFO2 for voice */
void setLFO2Period(float period) { m_lfoPeriods[1] = period; }
/** Setup pitch sweep controller 1 */
void setPitchSweep1(uint8_t times, int16_t add);
/** Setup pitch sweep controller 1 */
void setPitchSweep1(uint8_t times, int16_t add);
/** Setup pitch sweep controller 2 */
void setPitchSweep2(uint8_t times, int16_t add);
/** Setup pitch sweep controller 2 */
void setPitchSweep2(uint8_t times, int16_t add);
/** Set reverb mix for voice */
void setReverbVol(float rvol);
/** Set reverb mix for voice */
void setReverbVol(float rvol);
/** Set AuxB volume for voice */
void setAuxBVol(float bvol);
/** Set AuxB volume for voice */
void setAuxBVol(float bvol);
/** Set envelope for voice */
void setAdsr(ObjectId adsrId, bool dls);
/** Set envelope for voice */
void setAdsr(ObjectId adsrId, bool dls);
/** Set pitch in absolute hertz */
void setPitchFrequency(uint32_t hz, uint16_t fine);
/** Set pitch in absolute hertz */
void setPitchFrequency(uint32_t hz, uint16_t fine);
/** Set pitch envelope */
void setPitchAdsr(ObjectId adsrId, int32_t cents);
/** Set pitch envelope */
void setPitchAdsr(ObjectId adsrId, int32_t cents);
/** Set pitchwheel value for use with controller */
void setPitchWheel(float pitchWheel);
/** Set pitchwheel value for use with controller */
void setPitchWheel(float pitchWheel);
/** Set effective pitch range via the pitchwheel controller */
void setPitchWheelRange(int8_t up, int8_t down);
/** Set effective pitch range via the pitchwheel controller */
void setPitchWheelRange(int8_t up, int8_t down);
/** Set aftertouch */
void setAftertouch(uint8_t aftertouch);
/** Set aftertouch */
void setAftertouch(uint8_t aftertouch);
/** Assign voice to keygroup for coordinated mass-silencing */
void setKeygroup(uint8_t kg) { m_keygroup = kg; }
/** Assign voice to keygroup for coordinated mass-silencing */
void setKeygroup(uint8_t kg) { m_keygroup = kg; }
/** Get note played on voice */
uint8_t getLastNote() const { return m_state.m_initKey; }
/** Get note played on voice */
uint8_t getLastNote() const { return m_state.m_initKey; }
/** Do portamento glide; returns `false` if portamento disabled */
bool doPortamento(uint8_t newNote);
/** Do portamento glide; returns `false` if portamento disabled */
bool doPortamento(uint8_t newNote);
/** Get MIDI Controller value on voice */
int8_t getCtrlValue(uint8_t ctrl) const
{
if (!m_extCtrlVals)
{
if (m_ctrlValsSelf)
return m_ctrlValsSelf[ctrl];
return 0;
}
return m_extCtrlVals[ctrl];
/** Get MIDI Controller value on voice */
int8_t getCtrlValue(uint8_t ctrl) const {
if (!m_extCtrlVals) {
if (m_ctrlValsSelf)
return m_ctrlValsSelf[ctrl];
return 0;
}
return m_extCtrlVals[ctrl];
}
/** Set MIDI Controller value on voice */
void setCtrlValue(uint8_t ctrl, int8_t val)
{
if (!m_extCtrlVals)
{
std::unique_ptr<int8_t[]>& vals = _ensureCtrlVals();
vals[ctrl] = val;
}
else
m_extCtrlVals[ctrl] = val;
_notifyCtrlChange(ctrl, val);
}
/** Set MIDI Controller value on voice */
void setCtrlValue(uint8_t ctrl, int8_t val) {
if (!m_extCtrlVals) {
std::unique_ptr<int8_t[]>& vals = _ensureCtrlVals();
vals[ctrl] = val;
} else
m_extCtrlVals[ctrl] = val;
_notifyCtrlChange(ctrl, val);
}
/** 'install' external MIDI controller storage */
void installCtrlValues(int8_t* cvs)
{
m_ctrlValsSelf.reset();
m_extCtrlVals = cvs;
for (ObjToken<Voice>& vox : m_childVoices)
vox->installCtrlValues(cvs);
}
/** 'install' external MIDI controller storage */
void installCtrlValues(int8_t* cvs) {
m_ctrlValsSelf.reset();
m_extCtrlVals = cvs;
for (ObjToken<Voice>& vox : m_childVoices)
vox->installCtrlValues(cvs);
}
/** Get MIDI pitch wheel value on voice */
float getPitchWheel() const { return m_curPitchWheel; }
/** Get MIDI pitch wheel value on voice */
float getPitchWheel() const { return m_curPitchWheel; }
/** Get MIDI aftertouch value on voice */
int8_t getAftertouch() const { return m_curAftertouch; }
/** Get MIDI aftertouch value on voice */
int8_t getAftertouch() const { return m_curAftertouch; }
/** Get count of all voices in hierarchy, including this one */
size_t getTotalVoices() const;
/** Get count of all voices in hierarchy, including this one */
size_t getTotalVoices() const;
/** Get latest decoded sample index */
uint32_t getSamplePos() const { return m_curSamplePos; }
/** Get latest decoded sample index */
uint32_t getSamplePos() const { return m_curSamplePos; }
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
void kill();
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
void kill();
};
}
} // namespace amuse

View File

@ -1,8 +1,6 @@
#pragma once
namespace amuse
{
namespace amuse {
float LookupVolume(float vol);
float LookupDLSVolume(float vol);
}
} // namespace amuse

View File

@ -1,6 +1,5 @@
#pragma once
#include "AudioGroup.hpp"
#include "AudioGroupData.hpp"
#include "AudioGroupPool.hpp"
@ -20,4 +19,3 @@
#include "SongState.hpp"
#include "Submix.hpp"
#include "Voice.hpp"

View File

@ -10,11 +10,9 @@
#undef log2
#undef fabs
namespace std
{
using ::exp2;
using ::log2;
using ::fabs;
}
namespace std {
using ::exp2;
using ::fabs;
using ::log2;
} // namespace std
#endif

View File

@ -5,372 +5,329 @@
using namespace std::literals;
namespace amuse
{
namespace amuse {
void AudioGroup::assign(const AudioGroupData& data)
{
m_pool = AudioGroupPool::CreateAudioGroupPool(data);
m_proj = AudioGroupProject::CreateAudioGroupProject(data);
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data);
m_samp = data.getSamp();
void AudioGroup::assign(const AudioGroupData& data) {
m_pool = AudioGroupPool::CreateAudioGroupPool(data);
m_proj = AudioGroupProject::CreateAudioGroupProject(data);
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data);
m_samp = data.getSamp();
}
void AudioGroup::assign(SystemStringView groupPath)
{
/* Reverse order when loading intermediates */
m_groupPath = groupPath;
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
m_samp = nullptr;
void AudioGroup::assign(SystemStringView groupPath) {
/* Reverse order when loading intermediates */
m_groupPath = groupPath;
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
m_samp = nullptr;
}
void AudioGroup::assign(const AudioGroup& data, SystemStringView groupPath)
{
/* Reverse order when loading intermediates */
m_groupPath = groupPath;
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
m_proj = AudioGroupProject::CreateAudioGroupProject(data.getProj());
m_samp = nullptr;
void AudioGroup::assign(const AudioGroup& data, SystemStringView groupPath) {
/* Reverse order when loading intermediates */
m_groupPath = groupPath;
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
m_proj = AudioGroupProject::CreateAudioGroupProject(data.getProj());
m_samp = nullptr;
}
const SampleEntry* AudioGroup::getSample(SampleId sfxId) const
{
auto search = m_sdir.m_entries.find(sfxId);
if (search == m_sdir.m_entries.cend())
return nullptr;
return search->second.get();
const SampleEntry* AudioGroup::getSample(SampleId sfxId) const {
auto search = m_sdir.m_entries.find(sfxId);
if (search == m_sdir.m_entries.cend())
return nullptr;
return search->second.get();
}
SystemString AudioGroup::getSampleBasePath(SampleId sfxId) const
{
SystemString AudioGroup::getSampleBasePath(SampleId sfxId) const {
#if _WIN32
return m_groupPath + _SYS_STR('/') +
athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId));
return m_groupPath + _SYS_STR('/') + athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId));
#else
return m_groupPath + _SYS_STR('/') +
SampleId::CurNameDB->resolveNameFromId(sfxId).data();
return m_groupPath + _SYS_STR('/') + SampleId::CurNameDB->resolveNameFromId(sfxId).data();
#endif
}
std::pair<ObjToken<SampleEntryData>, const unsigned char*>
AudioGroup::getSampleData(SampleId sfxId, const SampleEntry* sample) const
{
if (sample->m_data->m_looseData)
{
setIdDatabases();
SystemString basePath = getSampleBasePath(sfxId);
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};
std::pair<ObjToken<SampleEntryData>, const unsigned char*> AudioGroup::getSampleData(SampleId sfxId,
const SampleEntry* sample) const {
if (sample->m_data->m_looseData) {
setIdDatabases();
SystemString basePath = getSampleBasePath(sfxId);
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};
}
SampleFileState AudioGroup::getSampleFileState(SampleId sfxId, const SampleEntry* sample, SystemString* pathOut) const
{
if (sample->m_data->m_looseData)
{
setIdDatabases();
SystemString basePath = getSampleBasePath(sfxId);
return sample->getFileState(basePath, pathOut);
}
if (sample->m_data->isFormatDSP() || sample->m_data->getSampleFormat() == SampleFormat::N64)
return SampleFileState::MemoryOnlyCompressed;
return SampleFileState::MemoryOnlyWAV;
SampleFileState AudioGroup::getSampleFileState(SampleId sfxId, const SampleEntry* sample, SystemString* pathOut) const {
if (sample->m_data->m_looseData) {
setIdDatabases();
SystemString basePath = getSampleBasePath(sfxId);
return sample->getFileState(basePath, pathOut);
}
if (sample->m_data->isFormatDSP() || sample->m_data->getSampleFormat() == SampleFormat::N64)
return SampleFileState::MemoryOnlyCompressed;
return SampleFileState::MemoryOnlyWAV;
}
void AudioGroup::patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const
{
if (sample->m_data->m_looseData)
{
setIdDatabases();
SystemString basePath = getSampleBasePath(sfxId);
sample->patchSampleMetadata(basePath);
}
void AudioGroup::patchSampleMetadata(SampleId sfxId, const SampleEntry* sample) const {
if (sample->m_data->m_looseData) {
setIdDatabases();
SystemString basePath = getSampleBasePath(sfxId);
sample->patchSampleMetadata(basePath);
}
}
void AudioGroup::makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const
{
if (sample->m_data->m_looseData)
{
setIdDatabases();
m_sdir._extractWAV(sfxId, *sample->m_data, m_groupPath, sample->m_data->m_looseData.get());
}
void AudioGroup::makeWAVVersion(SampleId sfxId, const SampleEntry* sample) const {
if (sample->m_data->m_looseData) {
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
{
if (sample->m_data->m_looseData)
{
setIdDatabases();
m_sdir._extractCompressed(sfxId, *sample->m_data, m_groupPath, sample->m_data->m_looseData.get(), true);
}
void AudioGroup::makeCompressedVersion(SampleId sfxId, const SampleEntry* sample) const {
if (sample->m_data->m_looseData) {
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)
{
SystemString oldBasePath = getSampleBasePath(id);
SampleId::CurNameDB->rename(id, str);
SystemString newBasePath = getSampleBasePath(id);
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(".vadpcm")).c_str(), (newBasePath + _SYS_STR(".vadpcm")).c_str());
void AudioGroupDatabase::renameSample(SampleId id, std::string_view str) {
SystemString oldBasePath = getSampleBasePath(id);
SampleId::CurNameDB->rename(id, str);
SystemString newBasePath = getSampleBasePath(id);
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(".vadpcm")).c_str(), (newBasePath + _SYS_STR(".vadpcm")).c_str());
}
void AudioGroupDatabase::deleteSample(SampleId id)
{
SystemString basePath = getSampleBasePath(id);
Unlink((basePath + _SYS_STR(".wav")).c_str());
Unlink((basePath + _SYS_STR(".dsp")).c_str());
Unlink((basePath + _SYS_STR(".vadpcm")).c_str());
void AudioGroupDatabase::deleteSample(SampleId id) {
SystemString basePath = getSampleBasePath(id);
Unlink((basePath + _SYS_STR(".wav")).c_str());
Unlink((basePath + _SYS_STR(".dsp")).c_str());
Unlink((basePath + _SYS_STR(".vadpcm")).c_str());
}
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(".dsp")).c_str(), (newBasePath + _SYS_STR(".dsp")).c_str());
Copy((basePath + _SYS_STR(".vadpcm")).c_str(), (newBasePath + _SYS_STR(".vadpcm")).c_str());
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(".dsp")).c_str(), (newBasePath + _SYS_STR(".dsp")).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,
std::unordered_set<SoundMacroId>& renamedIds)
{
if (renamedIds.find(id) != renamedIds.cend())
return;
if (const SoundMacro* macro = getPool().soundMacro(id))
{
if (!strncmp(SoundMacroId::CurNameDB->resolveNameFromId(id).data(), "macro", 5))
{
std::string macroName("macro"sv);
if (macroIdx)
{
char num[16];
snprintf(num, 16, "%d", macroIdx);
macroName += num;
}
macroName += '_';
macroName += str;
++macroIdx;
SoundMacroId::CurNameDB->rename(id, macroName);
renamedIds.insert(id);
int sampleIdx = 0;
for (const auto& cmd : macro->m_cmds)
{
switch (cmd->Isa())
{
case SoundMacro::CmdOp::StartSample:
{
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;
}
std::unordered_set<SoundMacroId>& renamedIds) {
if (renamedIds.find(id) != renamedIds.cend())
return;
if (const SoundMacro* macro = getPool().soundMacro(id)) {
if (!strncmp(SoundMacroId::CurNameDB->resolveNameFromId(id).data(), "macro", 5)) {
std::string macroName("macro"sv);
if (macroIdx) {
char num[16];
snprintf(num, 16, "%d", macroIdx);
macroName += num;
}
macroName += '_';
macroName += str;
++macroIdx;
SoundMacroId::CurNameDB->rename(id, macroName);
renamedIds.insert(id);
int sampleIdx = 0;
for (const auto& cmd : macro->m_cmds) {
switch (cmd->Isa()) {
case SoundMacro::CmdOp::StartSample: {
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;
}
}
}
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);
static const std::regex DefineSFXEntry(R"(#define\s+SFX(\S+)\s+(\S+))",
std::regex::ECMAScript|std::regex::optimize);
void AudioGroupDatabase::importCHeader(std::string_view header)
{
setIdDatabases();
std::match_results<std::string_view::const_iterator> dirMatch;
auto begin = header.cbegin();
auto end = header.cend();
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);
}
}
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;
}
begin = dirMatch.suffix().first;
}
}
}
}
static void WriteDefineLine(std::string& ret, std::string_view typeStr, std::string_view name, ObjectId id)
{
ret += "#define "sv;
ret += typeStr;
ret += name;
ret += ' ';
char idStr[16];
snprintf(idStr, 16, "%d", id.id);
ret += idStr;
ret += '\n';
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);
static const std::regex DefineSFXEntry(R"(#define\s+SFX(\S+)\s+(\S+))", std::regex::ECMAScript | std::regex::optimize);
void AudioGroupDatabase::importCHeader(std::string_view header) {
setIdDatabases();
std::match_results<std::string_view::const_iterator> dirMatch;
auto begin = header.cbegin();
auto end = header.cend();
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
{
setIdDatabases();
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);
static void WriteDefineLine(std::string& ret, std::string_view typeStr, std::string_view name, ObjectId id) {
ret += "#define "sv;
ret += typeStr;
ret += name;
ret += ' ';
char idStr[16];
snprintf(idStr, 16, "%d", id.id);
ret += idStr;
ret += '\n';
}
std::string AudioGroupDatabase::exportCHeader(std::string_view projectName, std::string_view groupName) const {
setIdDatabases();
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
struct tm curTm;
localtime_r(&curTime, &curTm);
char curTmStr[26];
asctime_r(&curTm, curTmStr);
struct tm curTm;
localtime_r(&curTime, &curTm);
char curTmStr[26];
asctime_r(&curTm, curTmStr);
#else
struct tm curTm;
localtime_s(&curTm, &curTime);
char curTmStr[26];
asctime_s(curTmStr, &curTm);
struct tm curTm;
localtime_s(&curTm, &curTime);
char curTmStr[26];
asctime_s(curTmStr, &curTm);
#endif
if (char* ch = strchr(curTmStr, '\n'))
*ch = '\0';
ret += curTmStr;
ret += "\n"
" */\n\n\n"sv;
if (char* ch = strchr(curTmStr, '\n'))
*ch = '\0';
ret += curTmStr;
ret +=
"\n"
" */\n\n\n"sv;
bool addLF = false;
for (const auto& sg : SortUnorderedMap(getProj().songGroups()))
{
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
WriteDefineLine(ret, "GRP"sv, name, sg.first);
addLF = true;
}
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups()))
{
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
WriteDefineLine(ret, "GRP"sv, name, sg.first);
addLF = true;
bool addLF = false;
for (const auto& sg : SortUnorderedMap(getProj().songGroups())) {
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
WriteDefineLine(ret, "GRP"sv, name, sg.first);
addLF = true;
}
for (const auto& sg : SortUnorderedMap(getProj().sfxGroups())) {
auto name = amuse::GroupId::CurNameDB->resolveNameFromId(sg.first);
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)
ret += "\n\n"sv;
addLF = false;
if (addLF)
ret += "\n\n"sv;
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)
ret += "\n\n"sv;
return ret;
}
return ret;
}
} // namespace amuse

View File

@ -1,47 +1,41 @@
#include "amuse/AudioGroupData.hpp"
namespace amuse
{
namespace amuse {
IntrusiveAudioGroupData::~IntrusiveAudioGroupData()
{
if (m_owns)
{
delete[] m_pool;
delete[] m_proj;
delete[] m_sdir;
delete[] m_samp;
}
IntrusiveAudioGroupData::~IntrusiveAudioGroupData() {
if (m_owns) {
delete[] m_pool;
delete[] m_proj;
delete[] m_sdir;
delete[] m_samp;
}
}
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,
other.m_sampSz, other.m_fmt, other.m_absOffs)
{
m_owns = other.m_owns;
other.m_owns = false;
other.m_sampSz, other.m_fmt, other.m_absOffs) {
m_owns = other.m_owns;
other.m_owns = false;
}
IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupData&& other) noexcept
{
if (m_owns)
{
delete[] m_pool;
delete[] m_proj;
delete[] m_sdir;
delete[] m_samp;
}
IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupData&& other) noexcept {
if (m_owns) {
delete[] m_pool;
delete[] m_proj;
delete[] m_sdir;
delete[] m_samp;
}
m_owns = other.m_owns;
other.m_owns = false;
m_owns = other.m_owns;
other.m_owns = false;
m_proj = other.m_proj;
m_pool = other.m_pool;
m_sdir = other.m_sdir;
m_samp = other.m_samp;
m_fmt = other.m_fmt;
m_absOffs = other.m_absOffs;
m_proj = other.m_proj;
m_pool = other.m_pool;
m_sdir = other.m_sdir;
m_samp = other.m_samp;
m_fmt = other.m_fmt;
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/Engine.hpp"
namespace amuse
{
namespace amuse {
void BooBackendVoice::VoiceCallback::preSupplyAudio(boo::IAudioVoice&, double dt)
{
m_parent.m_clientVox.preSupplyAudio(dt);
void BooBackendVoice::VoiceCallback::preSupplyAudio(boo::IAudioVoice&, double dt) {
m_parent.m_clientVox.preSupplyAudio(dt);
}
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&, size_t frames, int16_t* data)
{
return m_parent.m_clientVox.supplyAudio(frames, data);
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&, size_t frames, int16_t* 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)
{
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, 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);
}
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);
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);
}
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);
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);
}
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::resetChannelLevels() { m_booVoice->resetChannelLevels(); }
void BooBackendVoice::setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew)
{
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
m_booVoice->setMonoChannelLevels(smx.m_booSubmix.get(), coefs, slew);
void BooBackendVoice::setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew) {
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
m_booVoice->setMonoChannelLevels(smx.m_booSubmix.get(), coefs, 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(); }
void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, size_t frameCount, const boo::ChannelMap& chanMap,
double) const
{
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
double) const {
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,
double) const
{
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
double) const {
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,
double) const
{
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
double) const {
return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast<const ChannelMap&>(chanMap));
}
void BooBackendSubmix::SubmixCallback::resetOutputSampleRate(double sampleRate)
{
m_parent.m_clientSmx.resetOutputSampleRate(sampleRate);
void BooBackendSubmix::SubmixCallback::resetOutputSampleRate(double sampleRate) {
m_parent.m_clientSmx.resetOutputSampleRate(sampleRate);
}
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)
{
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
m_booSubmix->setSendLevel(smx.m_booSubmix.get(), level, slew);
void BooBackendSubmix::setSendLevel(IBackendSubmix* submix, float level, bool slew) {
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
m_booSubmix->setSendLevel(smx.m_booSubmix.get(), level, slew);
}
double BooBackendSubmix::getSampleRate() const { return m_booSubmix->getSampleRate(); }
@ -95,67 +82,51 @@ SubmixFormat BooBackendSubmix::getSampleFormat() const { return SubmixFormat(m_b
BooBackendMIDIReader::~BooBackendMIDIReader() {}
BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, bool useLock)
: m_engine(engine), m_decoder(*this), m_useLock(useLock)
{
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(engine.getBackend());
auto devices = voxAlloc.m_booEngine.enumerateMIDIInputs();
for (const auto& dev : devices)
{
auto midiIn = voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(),
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));
: m_engine(engine), m_decoder(*this), m_useLock(useLock) {
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(engine.getBackend());
auto devices = voxAlloc.m_booEngine.enumerateMIDIInputs();
for (const auto& dev : devices) {
auto midiIn =
voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(), std::bind(&BooBackendMIDIReader::_MIDIReceive, this,
std::placeholders::_1, std::placeholders::_2));
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)
{
m_midiIns.erase(name);
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)
m_midiIns[name] = std::move(midiIn);
}
bool BooBackendMIDIReader::hasMIDIIn(const char* name) const
{
return m_midiIns.find(name) != m_midiIns.cend();
void BooBackendMIDIReader::removeMIDIIn(const char* name) { m_midiIns.erase(name); }
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)
{
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 { return m_virtualIn.operator bool(); }
bool BooBackendMIDIReader::hasVirtualIn() const
{
return m_virtualIn.operator bool();
}
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));
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
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
syslog(LOG_EMERG, "MIDI receive %f\n", time);
@ -163,32 +134,29 @@ void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes, double tim
#endif
}
void BooBackendMIDIReader::pumpReader(double dt)
{
dt += 0.001; /* Add 1ms to ensure consumer keeps up with producer */
void BooBackendMIDIReader::pumpReader(double dt) {
dt += 0.001; /* Add 1ms to ensure consumer keeps up with producer */
std::unique_lock<std::mutex> lk(m_midiMutex, std::defer_lock_t{});
if (m_useLock)
lk.lock();
if (m_queue.empty())
return;
std::unique_lock<std::mutex> lk(m_midiMutex, std::defer_lock_t{});
if (m_useLock)
lk.lock();
if (m_queue.empty())
return;
/* Determine range of buffer updates within this period */
auto periodEnd = m_queue.cbegin();
double startPt = m_queue.front().first;
for (; periodEnd != m_queue.cend(); ++periodEnd)
{
double delta = periodEnd->first - startPt;
if (delta > dt)
break;
}
/* Determine range of buffer updates within this period */
auto periodEnd = m_queue.cbegin();
double startPt = m_queue.front().first;
for (; periodEnd != m_queue.cend(); ++periodEnd) {
double delta = periodEnd->first - startPt;
if (delta > dt)
break;
}
if (m_queue.cbegin() == periodEnd)
return;
if (m_queue.cbegin() == periodEnd)
return;
/* Dispatch buffers */
for (auto it = m_queue.begin(); it != periodEnd;)
{
/* Dispatch buffers */
for (auto it = m_queue.begin(); it != periodEnd;) {
#if 0
char str[64];
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);
closelog();
#endif
m_decoder.receiveBytes(it->second.cbegin(), it->second.cend());
it = m_queue.erase(it);
}
m_decoder.receiveBytes(it->second.cbegin(), it->second.cend());
it = m_queue.erase(it);
}
}
void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
{
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOff(chan, key, velocity);
void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) {
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOff(chan, key, velocity);
#if 0
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
syslog(LOG_EMERG, "NoteOff %d", key);
@ -214,10 +181,9 @@ void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
#endif
}
void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
{
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOn(chan, key, velocity);
void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) {
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOn(chan, key, velocity);
#if 0
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
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::controlChange(uint8_t chan, uint8_t control, uint8_t value)
{
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setCtrlValue(chan, control, value);
void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) {
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setCtrlValue(chan, control, value);
}
void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program)
{
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setChanProgram(chan, program);
void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program) {
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setChanProgram(chan, program);
}
void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch)
{
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000));
void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch) {
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000));
}
void BooBackendMIDIReader::allSoundOff(uint8_t chan)
{
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->allOff(chan, true);
void BooBackendMIDIReader::allSoundOff(uint8_t chan) {
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->allOff(chan, true);
}
void BooBackendMIDIReader::resetAllControllers(uint8_t /*chan*/) {}
void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
void BooBackendMIDIReader::allNotesOff(uint8_t chan)
{
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->allOff(chan, false);
void BooBackendMIDIReader::allNotesOff(uint8_t chan) {
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->allOff(chan, false);
}
void BooBackendMIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
@ -285,51 +246,40 @@ void BooBackendMIDIReader::stopSeq() {}
void BooBackendMIDIReader::reset() {}
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
: m_booEngine(booEngine)
{
booEngine.setCallbackInterface(this);
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : m_booEngine(booEngine) {
booEngine.setCallbackInterface(this);
}
std::unique_ptr<IBackendVoice> BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate,
bool dynamicPitch)
{
return std::make_unique<BooBackendVoice>(m_booEngine, clientVox, sampleRate, dynamicPitch);
bool dynamicPitch) {
return std::make_unique<BooBackendVoice>(m_booEngine, clientVox, sampleRate, dynamicPitch);
}
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx, bool mainOut, int busId)
{
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx, mainOut, busId);
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx, bool mainOut, int busId) {
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx, mainOut, busId);
}
std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices()
{
return m_booEngine.enumerateMIDIInputs();
std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices() {
return m_booEngine.enumerateMIDIInputs();
}
std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine)
{
return std::make_unique<BooBackendMIDIReader>(engine, m_booEngine.useMIDILock());
std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine) {
return std::make_unique<BooBackendMIDIReader>(engine, m_booEngine.useMIDILock());
}
void BooBackendVoiceAllocator::setCallbackInterface(Engine* engine)
{
m_cbInterface = engine;
}
void BooBackendVoiceAllocator::setCallbackInterface(Engine* engine) { m_cbInterface = engine; }
AudioChannelSet BooBackendVoiceAllocator::getAvailableSet() { return AudioChannelSet(m_booEngine.getAvailableSet()); }
void BooBackendVoiceAllocator::setVolume(float vol) { m_booEngine.setVolume(vol); }
void BooBackendVoiceAllocator::on5MsInterval(boo::IAudioVoiceEngine& engine, double dt)
{
if (m_cbInterface)
m_cbInterface->_on5MsInterval(*this, dt);
void BooBackendVoiceAllocator::on5MsInterval(boo::IAudioVoiceEngine& engine, double dt) {
if (m_cbInterface)
m_cbInterface->_on5MsInterval(*this, dt);
}
void BooBackendVoiceAllocator::onPumpCycleComplete(boo::IAudioVoiceEngine& engine)
{
if (m_cbInterface)
m_cbInterface->_onPumpCycleComplete(*this);
}
void BooBackendVoiceAllocator::onPumpCycleComplete(boo::IAudioVoiceEngine& engine) {
if (m_cbInterface)
m_cbInterface->_onPumpCycleComplete(*this);
}
} // namespace amuse

View File

@ -3,128 +3,121 @@
using namespace std::literals;
namespace amuse
{
namespace amuse {
static logvisor::Module Log("amuse");
bool Copy(const SystemChar* from, const SystemChar* to)
{
bool Copy(const SystemChar* from, const SystemChar* to) {
#if _WIN32
return CopyFileW(from, to, FALSE) != 0;
return CopyFileW(from, to, FALSE) != 0;
#else
FILE* fi = fopen(from, "rb");
if (!fi)
return false;
FILE* fo = fopen(to, "wb");
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);
FILE* fi = fopen(from, "rb");
if (!fi)
return false;
FILE* fo = fopen(to, "wb");
if (!fo) {
fclose(fi);
fclose(fo);
struct stat theStat;
if (::stat(from, &theStat))
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 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(fo);
struct stat theStat;
if (::stat(from, &theStat))
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
}
#define DEFINE_ID_TYPE(type, typeName) \
thread_local NameDB* type::CurNameDB = nullptr; \
template<> template<> \
void type##DNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) \
{ \
id = reader.readUint16Little(); \
} \
template<> template<> \
void type##DNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) \
{ \
writer.writeUint16Little(id); \
} \
template<> template<> \
void type##DNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz) \
{ \
sz += 2; \
} \
template<> template<> \
void type##DNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) \
{ \
_read(reader); \
} \
template<> template<> \
void type##DNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) \
{ \
_write(writer); \
} \
template<> template<> \
void type##DNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) \
{ \
id = reader.readUint16Big(); \
} \
template<> template<> \
void type##DNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) \
{ \
writer.writeUint16Big(id); \
} \
template<> template<> \
void type##DNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz) \
{ \
sz += 2; \
} \
template<> template<> \
void type##DNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) \
{ \
_read(reader); \
} \
template<> template<> \
void type##DNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) \
{ \
_write(writer); \
} \
template <athena::Endian DNAE> \
void type##DNA<DNAE>::_read(athena::io::YAMLDocReader& r) \
{ \
std::string name = r.readString(nullptr); \
if (!type::CurNameDB) \
Log.report(logvisor::Fatal, "Unable to resolve " typeName " name %s, no database present", name.c_str()); \
if (name.empty()) \
{ \
id.id = 0xffff; \
return; \
} \
id = type::CurNameDB->resolveIdFromName(name); \
} \
template <athena::Endian DNAE> \
void type##DNA<DNAE>::_write(athena::io::YAMLDocWriter& w) \
{ \
if (!type::CurNameDB) \
Log.report(logvisor::Fatal, "Unable to resolve " typeName " ID %d, no database present", id.id); \
if (id.id == 0xffff) \
return; \
std::string_view name = type::CurNameDB->resolveNameFromId(id); \
if (!name.empty()) \
w.writeString(nullptr, name); \
} \
template <athena::Endian DNAE> \
const char* type##DNA<DNAE>::DNAType() \
{ \
return "amuse::" #type "DNA"; \
} \
template struct type##DNA<athena::Big>; \
template struct type##DNA<athena::Little>;
#define DEFINE_ID_TYPE(type, typeName) \
thread_local NameDB* type::CurNameDB = nullptr; \
template <> \
template <> \
void type##DNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader & reader) { \
id = reader.readUint16Little(); \
} \
template <> \
template <> \
void type##DNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter & writer) { \
writer.writeUint16Little(id); \
} \
template <> \
template <> \
void type##DNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t & sz) { \
sz += 2; \
} \
template <> \
template <> \
void type##DNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader & reader) { \
_read(reader); \
} \
template <> \
template <> \
void type##DNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter & writer) { \
_write(writer); \
} \
template <> \
template <> \
void type##DNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader & reader) { \
id = reader.readUint16Big(); \
} \
template <> \
template <> \
void type##DNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter & writer) { \
writer.writeUint16Big(id); \
} \
template <> \
template <> \
void type##DNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t & sz) { \
sz += 2; \
} \
template <> \
template <> \
void type##DNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader & reader) { \
_read(reader); \
} \
template <> \
template <> \
void type##DNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter & writer) { \
_write(writer); \
} \
template <athena::Endian DNAE> \
void type##DNA<DNAE>::_read(athena::io::YAMLDocReader& r) { \
std::string name = r.readString(nullptr); \
if (!type::CurNameDB) \
Log.report(logvisor::Fatal, "Unable to resolve " typeName " name %s, no database present", name.c_str()); \
if (name.empty()) { \
id.id = 0xffff; \
return; \
} \
id = type::CurNameDB->resolveIdFromName(name); \
} \
template <athena::Endian DNAE> \
void type##DNA<DNAE>::_write(athena::io::YAMLDocWriter& w) { \
if (!type::CurNameDB) \
Log.report(logvisor::Fatal, "Unable to resolve " typeName " ID %d, no database present", id.id); \
if (id.id == 0xffff) \
return; \
std::string_view name = type::CurNameDB->resolveNameFromId(id); \
if (!name.empty()) \
w.writeString(nullptr, name); \
} \
template <athena::Endian DNAE> \
const char* type##DNA<DNAE>::DNAType() { \
return "amuse::" #type "DNA"; \
} \
template struct type##DNA<athena::Big>; \
template struct type##DNA<athena::Little>;
DEFINE_ID_TYPE(ObjectId, "object")
DEFINE_ID_TYPE(SoundMacroId, "SoundMacro")
@ -136,336 +129,300 @@ DEFINE_ID_TYPE(SongId, "song")
DEFINE_ID_TYPE(SFXId, "sfx")
DEFINE_ID_TYPE(GroupId, "group")
template<> template<>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Little();
template <>
template <>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
id = reader.readUint16Little();
}
template<> template<>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Little(id);
template <>
template <>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
writer.writeUint16Little(id);
}
template<> template<>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
template <>
template <>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
sz += 2;
}
template<> template<>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
template <>
template <>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
_read(reader);
}
template<> template<>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
template <>
template <>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
_write(writer);
}
template<> template<>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Big();
template <>
template <>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
id = reader.readUint16Big();
}
template<> template<>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Big(id);
template <>
template <>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
writer.writeUint16Big(id);
}
template<> template<>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
template <>
template <>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
sz += 2;
}
template<> template<>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
template <>
template <>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
_read(reader);
}
template<> template<>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
template <>
template <>
void PageObjectIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
_write(writer);
}
template <athena::Endian DNAE>
void PageObjectIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r)
{
std::string name = r.readString(nullptr);
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers name %s, no database present", name.c_str());
if (name.empty())
{
void PageObjectIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r) {
std::string name = r.readString(nullptr);
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers name %s, no database present", name.c_str());
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;
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;
return;
}
}
}
id = search->second;
}
id = search->second;
}
template <athena::Endian DNAE>
void PageObjectIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w)
{
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers ID %d, no database present", id.id);
if (id.id == 0xffff)
return;
if (id.id & 0x8000)
{
std::string_view name = LayersId::CurNameDB->resolveNameFromId(id);
if (!name.empty())
w.writeString(nullptr, name);
}
else if (id.id & 0x4000)
{
std::string_view name = KeymapId::CurNameDB->resolveNameFromId(id);
if (!name.empty())
w.writeString(nullptr, name);
}
else
{
std::string_view name = SoundMacroId::CurNameDB->resolveNameFromId(id);
if (!name.empty())
w.writeString(nullptr, name);
}
void PageObjectIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w) {
if (!KeymapId::CurNameDB || !LayersId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve keymap or layers ID %d, no database present", id.id);
if (id.id == 0xffff)
return;
if (id.id & 0x8000) {
std::string_view name = LayersId::CurNameDB->resolveNameFromId(id);
if (!name.empty())
w.writeString(nullptr, name);
} else if (id.id & 0x4000) {
std::string_view name = KeymapId::CurNameDB->resolveNameFromId(id);
if (!name.empty())
w.writeString(nullptr, name);
} else {
std::string_view name = SoundMacroId::CurNameDB->resolveNameFromId(id);
if (!name.empty())
w.writeString(nullptr, name);
}
}
template <athena::Endian DNAE>
const char* PageObjectIdDNA<DNAE>::DNAType()
{
return "amuse::PageObjectIdDNA";
const char* PageObjectIdDNA<DNAE>::DNAType() {
return "amuse::PageObjectIdDNA";
}
template struct PageObjectIdDNA<athena::Big>;
template struct PageObjectIdDNA<athena::Little>;
template<> template<>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
step = reader.readUint16Little();
template <>
template <>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
step = reader.readUint16Little();
}
template<> template<>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Little(step);
template <>
template <>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
writer.writeUint16Little(step);
}
template<> template<>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
template <>
template <>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
sz += 2;
}
template<> template<>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
step = reader.readUint16(nullptr);
template <>
template <>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
step = reader.readUint16(nullptr);
}
template<> template<>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
writer.writeUint16(nullptr, step);
template <>
template <>
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
writer.writeUint16(nullptr, step);
}
template<> template<>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
step = reader.readUint16Big();
template <>
template <>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader) {
step = reader.readUint16Big();
}
template<> template<>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Big(step);
template <>
template <>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer) {
writer.writeUint16Big(step);
}
template<> template<>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
template <>
template <>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz) {
sz += 2;
}
template<> template<>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
step = reader.readUint16(nullptr);
template <>
template <>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
step = reader.readUint16(nullptr);
}
template<> template<>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
writer.writeUint16(nullptr, step);
template <>
template <>
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
writer.writeUint16(nullptr, step);
}
template <athena::Endian DNAE>
const char* SoundMacroStepDNA<DNAE>::DNAType()
{
return "amuse::SoundMacroStepDNA";
const char* SoundMacroStepDNA<DNAE>::DNAType() {
return "amuse::SoundMacroStepDNA";
}
template struct SoundMacroStepDNA<athena::Big>;
template struct SoundMacroStepDNA<athena::Little>;
ObjectId NameDB::generateId(Type tp) const
{
uint16_t maxMatch = 0;
if (tp == Type::Layer)
maxMatch = 0x8000;
else if (tp == Type::Keymap)
maxMatch = 0x4000;
for (const auto& p : m_idToString)
if (p.first >= maxMatch)
maxMatch = p.first + 1;
return maxMatch;
ObjectId NameDB::generateId(Type tp) const {
uint16_t maxMatch = 0;
if (tp == Type::Layer)
maxMatch = 0x8000;
else if (tp == Type::Keymap)
maxMatch = 0x4000;
for (const auto& p : m_idToString)
if (p.first >= maxMatch)
maxMatch = p.first + 1;
return maxMatch;
}
std::string NameDB::generateName(ObjectId id, Type tp)
{
char name[32];
switch (tp)
{
case Type::SoundMacro:
snprintf(name, 32, "macro%04X", id.id);
break;
case Type::Table:
snprintf(name, 32, "table%04X", id.id);
break;
case Type::Keymap:
snprintf(name, 32, "keymap%04X", id.id);
break;
case Type::Layer:
snprintf(name, 32, "layers%04X", id.id);
break;
case Type::Song:
snprintf(name, 32, "song%04X", id.id);
break;
case Type::SFX:
snprintf(name, 32, "sfx%04X", id.id);
break;
case Type::Group:
snprintf(name, 32, "group%04X", id.id);
break;
case Type::Sample:
snprintf(name, 32, "sample%04X", id.id);
break;
default:
snprintf(name, 32, "obj%04X", id.id);
break;
}
return name;
std::string NameDB::generateName(ObjectId id, Type tp) {
char name[32];
switch (tp) {
case Type::SoundMacro:
snprintf(name, 32, "macro%04X", id.id);
break;
case Type::Table:
snprintf(name, 32, "table%04X", id.id);
break;
case Type::Keymap:
snprintf(name, 32, "keymap%04X", id.id);
break;
case Type::Layer:
snprintf(name, 32, "layers%04X", id.id);
break;
case Type::Song:
snprintf(name, 32, "song%04X", id.id);
break;
case Type::SFX:
snprintf(name, 32, "sfx%04X", id.id);
break;
case Type::Group:
snprintf(name, 32, "group%04X", id.id);
break;
case Type::Sample:
snprintf(name, 32, "sample%04X", id.id);
break;
default:
snprintf(name, 32, "obj%04X", id.id);
break;
}
return name;
}
std::string NameDB::generateDefaultName(Type tp) const
{
return generateName(generateId(tp), tp);
std::string NameDB::generateDefaultName(Type tp) const { 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)
{
m_stringToId[std::string(str)] = id;
return m_idToString.insert(std::make_pair(id, str)).first->second;
std::string_view NameDB::resolveNameFromId(ObjectId id) const {
auto search = m_idToString.find(id);
if (search == m_idToString.cend()) {
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
{
auto search = m_idToString.find(id);
if (search == m_idToString.cend())
{
Log.report(logvisor::Error, "Unable to resolve ID 0x%04X", id.id);
return ""sv;
}
return search->second;
ObjectId NameDB::resolveIdFromName(std::string_view str) const {
auto search = m_stringToId.find(std::string(str));
if (search == m_stringToId.cend()) {
Log.report(logvisor::Error, "Unable to resolve name %s", str.data());
return {};
}
return search->second;
}
ObjectId NameDB::resolveIdFromName(std::string_view str) const
{
auto search = m_stringToId.find(std::string(str));
if (search == m_stringToId.cend())
{
Log.report(logvisor::Error, "Unable to resolve name %s", str.data());
return {};
}
return search->second;
void NameDB::remove(ObjectId id) {
auto search = m_idToString.find(id);
if (search == m_idToString.cend())
return;
auto search2 = m_stringToId.find(search->second);
if (search2 == m_stringToId.cend())
return;
m_idToString.erase(search);
m_stringToId.erase(search2);
}
void NameDB::remove(ObjectId id)
{
auto search = m_idToString.find(id);
if (search == m_idToString.cend())
return;
auto search2 = m_stringToId.find(search->second);
if (search2 == m_stringToId.cend())
return;
m_idToString.erase(search);
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;
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__
std::swap(m_stringToId[std::string(str)], search2->second);
m_stringToId.erase(search2);
std::swap(m_stringToId[std::string(str)], search2->second);
m_stringToId.erase(search2);
#else
auto nh = m_stringToId.extract(search2);
nh.key() = str;
m_stringToId.insert(std::move(nh));
auto nh = m_stringToId.extract(search2);
nh.key() = str;
m_stringToId.insert(std::move(nh));
#endif
search->second = str;
search->second = str;
}
template<>
void LittleUInt24::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& reader)
{
union
{
atUint32 val;
char bytes[4];
} data = {};
reader.readBytesToBuf(data.bytes, 3);
val = SLittle(data.val);
template <>
void LittleUInt24::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& reader) {
union {
atUint32 val;
char bytes[4];
} data = {};
reader.readBytesToBuf(data.bytes, 3);
val = SLittle(data.val);
}
template<>
void LittleUInt24::Enumerate<LittleDNA::Write>(athena::io::IStreamWriter& writer)
{
union
{
atUint32 val;
char bytes[4];
} data;
data.val = SLittle(val);
writer.writeBytes(data.bytes, 3);
template <>
void LittleUInt24::Enumerate<LittleDNA::Write>(athena::io::IStreamWriter& writer) {
union {
atUint32 val;
char bytes[4];
} data;
data.val = SLittle(val);
writer.writeBytes(data.bytes, 3);
}
template<>
void LittleUInt24::Enumerate<LittleDNA::BinarySize>(size_t& sz)
{
sz += 3;
template <>
void LittleUInt24::Enumerate<LittleDNA::BinarySize>(size_t& sz) {
sz += 3;
}
template<>
void LittleUInt24::Enumerate<LittleDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
val = reader.readUint32(nullptr);
template <>
void LittleUInt24::Enumerate<LittleDNA::ReadYaml>(athena::io::YAMLDocReader& reader) {
val = reader.readUint32(nullptr);
}
template<>
void LittleUInt24::Enumerate<LittleDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
writer.writeUint32(nullptr, val);
template <>
void LittleUInt24::Enumerate<LittleDNA::WriteYaml>(athena::io::YAMLDocWriter& writer) {
writer.writeUint32(nullptr, val);
}
const char* LittleUInt24::DNAType()
{
return "amuse::LittleUInt24";
}
const char* LittleUInt24::DNAType() { 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"
namespace amuse
{
namespace amuse {
DirectoryEnumerator::DirectoryEnumerator(SystemStringView path, Mode mode, bool sizeSort, bool reverse, bool noHidden)
{
Sstat theStat;
if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode))
return;
DirectoryEnumerator::DirectoryEnumerator(SystemStringView path, Mode mode, bool sizeSort, bool reverse, bool noHidden) {
Sstat theStat;
if (Stat(path.data(), &theStat) || !S_ISDIR(theStat.st_mode))
return;
#if _WIN32
SystemString wc(path);
wc += _SYS_STR("/*");
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(wc.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
switch (mode)
{
case Mode::Native:
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))
continue;
SystemString wc(path);
wc += _SYS_STR("/*");
WIN32_FIND_DATAW d;
HANDLE dir = FindFirstFileW(wc.c_str(), &d);
if (dir == INVALID_HANDLE_VALUE)
return;
switch (mode) {
case Mode::Native:
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))
continue;
size_t sz = 0;
bool isDir = false;
if (S_ISDIR(st.st_mode))
isDir = true;
else if (S_ISREG(st.st_mode))
sz = st.st_size;
else
continue;
size_t sz = 0;
bool isDir = false;
if (S_ISDIR(st.st_mode))
isDir = true;
else if (S_ISREG(st.st_mode))
sz = st.st_size;
else
continue;
m_entries.emplace_back(fp, d.cFileName, sz, isDir);
} while (FindNextFileW(dir, &d));
break;
case Mode::DirsThenFilesSorted:
case Mode::DirsSorted:
{
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_ISDIR(st.st_mode))
continue;
sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, 0, true)));
} while (FindNextFileW(dir, &d));
m_entries.emplace_back(fp, d.cFileName, sz, isDir);
} while (FindNextFileW(dir, &d));
break;
case Mode::DirsThenFilesSorted:
case Mode::DirsSorted: {
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_ISDIR(st.st_mode))
continue;
sort.emplace(std::make_pair(d.cFileName, Entry(fp, d.cFileName, 0, true)));
} 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));
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;
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;
}
}
if (mode == Mode::DirsSorted)
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);
#else
DIR* dir = opendir(path.data());
if (!dir)
return;
const dirent* d;
switch (mode)
{
case Mode::Native:
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))
continue;
DIR* dir = opendir(path.data());
if (!dir)
return;
const dirent* d;
switch (mode) {
case Mode::Native:
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))
continue;
size_t sz = 0;
bool isDir = false;
if (S_ISDIR(st.st_mode))
isDir = true;
else if (S_ISREG(st.st_mode))
sz = st.st_size;
else
continue;
size_t sz = 0;
bool isDir = false;
if (S_ISDIR(st.st_mode))
isDir = true;
else if (S_ISREG(st.st_mode))
sz = st.st_size;
else
continue;
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);
m_entries.emplace_back(fp, d->d_name, sz, isDir);
}
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));
}
break;
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: {
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
}
}
} // namespace amuse

View File

@ -4,8 +4,7 @@
#include <cstring>
#include <cmath>
namespace amuse
{
namespace amuse {
/* clang-format off */
static const float rsmpTab12khz[] =
@ -144,230 +143,209 @@ static const float rsmpTab12khz[] =
EffectChorus::EffectChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)
: x90_baseDelay(clamp(5u, baseDelay, 15u))
, x94_variation(clamp(0u, variation, 5u))
, x98_period(clamp(500u, period, 10000u))
{
}
, x98_period(clamp(500u, period, 10000u)) {}
template <typename T>
EffectChorusImp<T>::EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate)
: EffectChorus(baseDelay, variation, period)
{
_setup(sampleRate);
: EffectChorus(baseDelay, variation, period) {
_setup(sampleRate);
}
template <typename T>
void EffectChorusImp<T>::_setup(double sampleRate)
{
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
m_blockSamples = m_sampsPerMs * 5;
void EffectChorusImp<T>::_setup(double sampleRate) {
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
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];
memset(buf, 0, m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS * 8 * sizeof(T));
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
T* buf = new T[m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS * 8];
memset(buf, 0, m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS * 8 * sizeof(T));
size_t chanPitch = m_blockSamples * AMUSE_CHORUS_NUM_BLOCKS;
for (int c = 0; c < 8; ++c)
for (int i = 0; i < AMUSE_CHORUS_NUM_BLOCKS; ++i)
x0_lastChans[c][i] = buf + chanPitch * c + m_blockSamples * i;
for (int c = 0; c < 8; ++c)
for (int i = 0; i < AMUSE_CHORUS_NUM_BLOCKS; ++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>
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 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 fifteenSamps = 15 * m_sampsPerMs;
x5c_currentPosHi = x6c_src.x7c_posHi % (chanPitch / fifteenSamps * fifteenSamps);
x58_currentPosLo = x6c_src.x78_posLo;
x24_currentLast = buf;
}
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<int32_t>;
template class EffectChorusImp<float>;
}
} // namespace amuse

View File

@ -4,90 +4,78 @@
#include <cstring>
#include <cmath>
namespace amuse
{
namespace amuse {
template <typename T>
EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate)
{
initDelay = clamp(10u, initDelay, 5000u);
initFeedback = clamp(0u, initFeedback, 100u);
initOutput = clamp(0u, initOutput, 100u);
EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate) {
initDelay = clamp(10u, initDelay, 5000u);
initFeedback = clamp(0u, initFeedback, 100u);
initOutput = clamp(0u, initOutput, 100u);
for (int i = 0; i < 8; ++i)
{
x3c_delay[i] = initDelay;
x48_feedback[i] = initFeedback;
x54_output[i] = initOutput;
}
for (int i = 0; i < 8; ++i) {
x3c_delay[i] = initDelay;
x48_feedback[i] = initFeedback;
x54_output[i] = initOutput;
}
_setup(sampleRate);
_setup(sampleRate);
}
template <typename T>
EffectDelayImp<T>::EffectDelayImp(const EffectDelayInfo& info, double sampleRate)
{
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);
x54_output[i] = clamp(0u, info.output[i], 100u);
}
EffectDelayImp<T>::EffectDelayImp(const EffectDelayInfo& info, double sampleRate) {
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);
x54_output[i] = clamp(0u, info.output[i], 100u);
}
_setup(sampleRate);
_setup(sampleRate);
}
template <typename T>
void EffectDelayImp<T>::_setup(double sampleRate)
{
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
m_blockSamples = m_sampsPerMs * 5;
void EffectDelayImp<T>::_setup(double sampleRate) {
m_sampsPerMs = std::ceil(sampleRate / 1000.0);
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();
}
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();
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;
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;
}
}
template class EffectDelayImp<int16_t>;
template class EffectDelayImp<int32_t>;
template class EffectDelayImp<float>;
}
} // namespace amuse

View File

@ -3,8 +3,7 @@
#include <cmath>
#include <cstring>
namespace amuse
{
namespace amuse {
/* clang-format off */
@ -38,23 +37,21 @@ static const size_t LPTapDelays[] =
/* clang-format on */
void ReverbDelayLine::allocate(int32_t delay)
{
delay += 2;
x8_length = delay;
xc_inputs.reset(new float[delay]);
memset(xc_inputs.get(), 0, x8_length * sizeof(float));
x10_lastInput = 0.f;
setdelay(delay / 2);
x0_inPoint = 0;
x4_outPoint = 0;
void ReverbDelayLine::allocate(int32_t delay) {
delay += 2;
x8_length = delay;
xc_inputs.reset(new float[delay]);
memset(xc_inputs.get(), 0, x8_length * sizeof(float));
x10_lastInput = 0.f;
setdelay(delay / 2);
x0_inPoint = 0;
x4_outPoint = 0;
}
void ReverbDelayLine::setdelay(int32_t delay)
{
x4_outPoint = x0_inPoint - delay;
while (x4_outPoint < 0)
x4_outPoint += x8_length;
void ReverbDelayLine::setdelay(int32_t delay) {
x4_outPoint = x0_inPoint - delay;
while (x4_outPoint < 0)
x4_outPoint += x8_length;
}
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))
, x148_x1d0_time(clamp(0.01f, time, 10.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)
: 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>
EffectReverbStdImp<T>::EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay,
double sampleRate)
: EffectReverbStd(coloration, mix, time, damping, preDelay)
{
_setup(sampleRate);
: EffectReverbStd(coloration, mix, time, damping, preDelay) {
_setup(sampleRate);
}
template <typename T>
void EffectReverbStdImp<T>::_setup(double sampleRate)
{
m_sampleRate = sampleRate;
void EffectReverbStdImp<T>::_setup(double 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();
}
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);
}
float dampWet = x118_level * 0.6f;
float dampDry = 0.6f - dampWet;
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);
}
}
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];
xf0_allPassCoef = x140_x1c8_coloration;
x118_level = x144_x1cc_mix;
x11c_damping = x14c_x1d4_damping;
ReverbDelayLine* linesC = x78_C[c];
ReverbDelayLine* linesAP = x0_AP[c];
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();
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];
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 (preDelayTime != 0)
{
sample2 = *preDelayPtr;
*preDelayPtr = sample;
preDelayPtr += 1;
if (preDelayPtr == lastPreDelaySamp)
preDelayPtr = preDelayLine;
if (x120_preDelayTime != 0) {
sample2 = *preDelayPtr;
*preDelayPtr = sample;
preDelayPtr += 1;
if (preDelayPtr == lastPreDelaySamp)
preDelayPtr = preDelayLine;
}
/* 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].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;
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;
linesC[1].x0_inPoint = 0;
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)
linesC[1].x4_outPoint = 0;
if (linesC[2].x4_outPoint == linesC[2].x8_length)
linesC[2].x4_outPoint = 0;
linesC[1].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].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[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;
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[0].x4_outPoint == linesAP[0].x8_length)
linesAP[0].x4_outPoint = 0;
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;
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;
linesAP[1].x4_outPoint = 0;
/* 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;
}
x1b8_preDelayPtr[c] = preDelayPtr;
audio += chanMap.m_channelCount * 160;
}
}
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);
}
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>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
{
if (m_dirty)
_update();
void EffectReverbHiImp<T>::_setup(double sampleRate) {
m_sampleRate = sampleRate;
_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 <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 */
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>;
@ -493,4 +447,4 @@ template class EffectReverbStdImp<float>;
template class EffectReverbHiImp<int16_t>;
template class EffectReverbHiImp<int32_t>;
template class EffectReverbHiImp<float>;
}
} // namespace amuse

View File

@ -3,133 +3,116 @@
#include "amuse/Voice.hpp"
#include "amuse/Engine.hpp"
namespace amuse
{
namespace amuse {
static void Delta(Vector3f& out, const Vector3f& a, const Vector3f& b)
{
out[0] = a[0] - b[0];
out[1] = a[1] - b[1];
out[2] = a[2] - b[2];
static void Delta(Vector3f& out, const Vector3f& a, const Vector3f& b) {
out[0] = a[0] - b[0];
out[1] = a[1] - b[1];
out[2] = a[2] - b[2];
}
Emitter::~Emitter() {}
Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox,
float maxDist, float minVol, float falloff, bool doppler)
: Entity(engine, group, vox->getGroupId(), vox->getObjectId()), 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)
{
Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox, float maxDist, float minVol,
float falloff, bool doppler)
: Entity(engine, group, vox->getGroupId(), vox->getObjectId())
, 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()
{
Entity::_destroy();
m_vox->kill();
float Emitter::_attenuationCurve(float dist) const {
if (dist > m_maxDist)
return 0.f;
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
{
if (dist > m_maxDist)
return 0.f;
float t = dist / m_maxDist;
if (m_falloff >= 0.f)
{
return 1.f - (m_falloff * t * t + (1.f - m_falloff) * t);
void Emitter::_update() {
if (!m_dirty) {
/* Ensure that all listeners are also not dirty */
bool dirty = false;
for (auto& listener : m_engine.m_activeListeners) {
if (listener->m_dirty) {
dirty = true;
break;
}
}
else
{
float omt = 1.f - t;
return 1.f - ((1.f + m_falloff) * t - (1.f - omt * omt) * m_falloff);
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::_update()
{
if (!m_dirty)
{
/* Ensure that all listeners are also not dirty */
bool dirty = false;
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) {
for (int i = 0; i < 3; ++i) {
m_pos[i] = pos[i];
m_dir[i] = dir[i];
}
m_dirty = true;
}
void Emitter::setVectors(const float* pos, const float* dir)
{
for (int i=0 ; i<3 ; ++i)
{
m_pos[i] = pos[i];
m_dir[i] = dir[i];
}
m_dirty = true;
}
}
} // namespace amuse

View File

@ -8,559 +8,484 @@
#include "amuse/AudioGroup.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};
Engine::~Engine()
{
m_backend.setCallbackInterface(nullptr);
for (ObjToken<Sequencer>& seq : m_activeSequencers)
if (!seq->m_destroyed)
seq->_destroy();
for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_destroy();
for (ObjToken<Voice>& vox : m_activeVoices)
vox->_destroy();
Engine::~Engine() {
m_backend.setCallbackInterface(nullptr);
for (ObjToken<Sequencer>& seq : m_activeSequencers)
if (!seq->m_destroyed)
seq->_destroy();
for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_destroy();
for (ObjToken<Voice>& vox : m_activeVoices)
vox->_destroy();
}
Engine::Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode)
: 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->getAuxB().makeChorus(15, 0, 500);
m_defaultStudioReady = true;
m_backend.setCallbackInterface(this);
m_midiReader = backend.allocateMIDIReader(*this);
: 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->getAuxB().makeChorus(15, 0, 500);
m_defaultStudioReady = true;
m_backend.setCallbackInterface(this);
m_midiReader = backend.allocateMIDIReader(*this);
}
std::pair<AudioGroup*, const SongGroupIndex*> Engine::_findSongGroup(GroupId groupId) const
{
for (const auto& pair : m_audioGroups)
{
const SongGroupIndex* ret = pair.second->getProj().getSongGroupIndex(groupId);
if (ret)
return {pair.second.get(), ret};
}
return {};
std::pair<AudioGroup*, const SongGroupIndex*> Engine::_findSongGroup(GroupId groupId) const {
for (const auto& pair : m_audioGroups) {
const SongGroupIndex* ret = pair.second->getProj().getSongGroupIndex(groupId);
if (ret)
return {pair.second.get(), ret};
}
return {};
}
std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(GroupId groupId) const
{
for (const auto& pair : m_audioGroups)
{
const SFXGroupIndex* ret = pair.second->getProj().getSFXGroupIndex(groupId);
if (ret)
return {pair.second.get(), ret};
}
return {};
std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(GroupId groupId) const {
for (const auto& pair : m_audioGroups) {
const SFXGroupIndex* ret = pair.second->getProj().getSFXGroupIndex(groupId);
if (ret)
return {pair.second.get(), ret};
}
return {};
}
std::list<ObjToken<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, GroupId groupId,
double sampleRate, bool dynamicPitch, bool emitter,
ObjToken<Studio> studio)
{
auto it =
m_activeVoices.emplace(m_activeVoices.end(), MakeObj<Voice>(*this, group, groupId, m_nextVid++, emitter, studio));
m_activeVoices.back()->m_backendVoice = m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getMaster().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxA().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxB().m_backendSubmix.get(), FullLevels, false);
return it;
std::list<ObjToken<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, GroupId groupId, double sampleRate,
bool dynamicPitch, bool emitter, ObjToken<Studio> studio) {
auto it =
m_activeVoices.emplace(m_activeVoices.end(), MakeObj<Voice>(*this, group, groupId, m_nextVid++, emitter, studio));
m_activeVoices.back()->m_backendVoice = m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getMaster().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxA().m_backendSubmix.get(), FullLevels, false);
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,
SongId setupId, ObjToken<Studio> studio)
{
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
if (songGroup)
{
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio);
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
return it;
SongId setupId, ObjToken<Studio> studio) {
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
if (songGroup) {
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio);
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);
if (sfxGroup)
{
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, sfxGroup, studio);
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
return it;
++it;
}
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();) {
Voice* vox = it->get();
vox->_bringOutYourDead();
if (vox->_isRecursivelyDead()) {
it = _destroyVoice(it);
continue;
}
return {};
}
++it;
}
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;
}
++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;
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;
}
}
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
{
m_channelSet = engine.getAvailableSet();
if (m_midiReader)
m_midiReader->pumpReader(dt);
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->advance(dt);
for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_update();
for (ObjToken<Listener>& listener : m_activeListeners)
listener->m_dirty = false;
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt) {
m_channelSet = engine.getAvailableSet();
if (m_midiReader)
m_midiReader->pumpReader(dt);
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->advance(dt);
for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_update();
for (ObjToken<Listener>& listener : m_activeListeners)
listener->m_dirty = false;
}
void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine)
{
_bringOutYourDead();
void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine) {
_bringOutYourDead();
/* Determine lowest available free vid */
int maxVid = -1;
for (ObjToken<Voice>& vox : m_activeVoices)
maxVid = std::max(maxVid, vox->maxVid());
m_nextVid = maxVid + 1;
/* Determine lowest available free vid */
int maxVid = -1;
for (ObjToken<Voice>& vox : m_activeVoices)
maxVid = std::max(maxVid, vox->maxVid());
m_nextVid = maxVid + 1;
}
AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp)
{
AudioGroup* ret = grp.get();
m_audioGroups.emplace(std::make_pair(&data, std::move(grp)));
AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp) {
AudioGroup* ret = grp.get();
m_audioGroups.emplace(std::make_pair(&data, std::move(grp)));
/* setup SFX index for contained objects */
for (const auto& grp : ret->getProj().sfxGroups())
{
const SFXGroupIndex& sfxGroup = *grp.second;
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
for (const auto& ent : sfxGroup.m_sfxEntries)
m_sfxLookup[ent.first] = std::make_tuple(ret, grp.first, &ent.second);
}
/* setup SFX index for contained objects */
for (const auto& grp : ret->getProj().sfxGroups()) {
const SFXGroupIndex& sfxGroup = *grp.second;
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
for (const auto& ent : sfxGroup.m_sfxEntries)
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! */
const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data)
{
removeAudioGroup(data);
return _addAudioGroup(data, std::make_unique<AudioGroup>(data));
const AudioGroup* Engine::addAudioGroup(const AudioGroupData& data) {
removeAudioGroup(data);
return _addAudioGroup(data, std::make_unique<AudioGroup>(data));
}
/** Remove audio group from engine */
void Engine::removeAudioGroup(const AudioGroupData& data)
{
auto search = m_audioGroups.find(&data);
if (search == m_audioGroups.cend())
return;
AudioGroup* grp = search->second.get();
void Engine::removeAudioGroup(const AudioGroupData& data) {
auto search = m_audioGroups.find(&data);
if (search == m_audioGroups.cend())
return;
AudioGroup* grp = search->second.get();
/* Destroy runtime entities within group */
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
{
Voice* vox = it->get();
if (&vox->getAudioGroup() == grp)
{
vox->_destroy();
it = m_activeVoices.erase(it);
continue;
}
++it;
/* Destroy runtime entities within group */
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();) {
Voice* vox = it->get();
if (&vox->getAudioGroup() == grp) {
vox->_destroy();
it = m_activeVoices.erase(it);
continue;
}
++it;
}
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();)
{
Emitter* emitter = it->get();
if (&emitter->getAudioGroup() == grp)
{
emitter->_destroy();
it = m_activeEmitters.erase(it);
continue;
}
++it;
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();) {
Emitter* emitter = it->get();
if (&emitter->getAudioGroup() == grp) {
emitter->_destroy();
it = m_activeEmitters.erase(it);
continue;
}
++it;
}
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();)
{
Sequencer* seq = it->get();
if (&seq->getAudioGroup() == grp)
{
seq->_destroy();
it = m_activeSequencers.erase(it);
continue;
}
++it;
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();) {
Sequencer* seq = it->get();
if (&seq->getAudioGroup() == grp) {
seq->_destroy();
it = m_activeSequencers.erase(it);
continue;
}
++it;
}
/* teardown SFX index for contained objects */
for (const auto& pair : grp->getProj().sfxGroups())
{
const SFXGroupIndex& sfxGroup = *pair.second;
for (const auto& pair : sfxGroup.m_sfxEntries)
m_sfxLookup.erase(pair.first);
}
/* teardown SFX index for contained objects */
for (const auto& pair : grp->getProj().sfxGroups()) {
const SFXGroupIndex& sfxGroup = *pair.second;
for (const auto& pair : sfxGroup.m_sfxEntries)
m_sfxLookup.erase(pair.first);
}
m_audioGroups.erase(search);
m_audioGroups.erase(search);
}
/** Create new Studio within engine */
ObjToken<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); }
/** Start soundFX playing from loaded audio groups */
ObjToken<Voice> Engine::fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx)
{
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
return {};
ObjToken<Voice> Engine::fxStart(SFXId sfxId, float vol, float pan, ObjToken<Studio> smx) {
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
return {};
AudioGroup* grp = std::get<0>(search->second);
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
if (!grp)
return {};
AudioGroup* grp = std::get<0>(search->second);
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
if (!grp)
return {};
std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
if (!(*ret)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0))
{
_destroyVoice(ret);
return {};
}
if (!(*ret)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0)) {
_destroyVoice(ret);
return {};
}
(*ret)->setVolume(vol);
float evalPan = pan != 0.f ? pan : ((entry->panning - 64.f) / 63.f);
evalPan = clamp(-1.f, evalPan, 1.f);
(*ret)->setPan(evalPan);
return *ret;
(*ret)->setVolume(vol);
float evalPan = pan != 0.f ? pan : ((entry->panning - 64.f) / 63.f);
evalPan = clamp(-1.f, evalPan, 1.f);
(*ret)->setPan(evalPan);
return *ret;
}
/** 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)
{
const SFXGroupIndex* sfxIdx = group->getProj().getSFXGroupIndex(groupId);
if (sfxIdx)
{
auto search = sfxIdx->m_sfxEntries.find(sfxId);
if (search != sfxIdx->m_sfxEntries.cend())
{
std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*group, groupId, NativeSampleRate, true, false, 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);
if (sfxIdx) {
auto search = sfxIdx->m_sfxEntries.find(sfxId);
if (search != sfxIdx->m_sfxEntries.cend()) {
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, groupId, NativeSampleRate, true, false, smx);
auto& entry = search->second;
if (!(*ret)->loadPageObject(entry.objId, 1000.f, entry.defKey, entry.defVel, 0))
{
_destroyVoice(ret);
return {};
}
auto& entry = search->second;
if (!(*ret)->loadPageObject(entry.objId, 1000.f, entry.defKey, entry.defVel, 0)) {
_destroyVoice(ret);
return {};
}
(*ret)->setVolume(vol);
float evalPan = pan != 0.f ? pan : ((entry.panning - 64.f) / 63.f);
evalPan = clamp(-1.f, evalPan, 1.f);
(*ret)->setPan(evalPan);
return *ret;
}
(*ret)->setVolume(vol);
float evalPan = pan != 0.f ? pan : ((entry.panning - 64.f) / 63.f);
evalPan = clamp(-1.f, evalPan, 1.f);
(*ret)->setPan(evalPan);
return *ret;
}
}
return {};
return {};
}
/** Start SoundMacro node playing directly (for editor use) */
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel,
uint8_t mod, ObjToken<Studio> smx)
{
if (!group)
return {};
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx) {
if (!group)
return {};
std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod))
{
_destroyVoice(ret);
return {};
}
if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod)) {
_destroyVoice(ret);
return {};
}
(*ret)->setVolume(1.f);
(*ret)->setPan(0.f);
return *ret;
(*ret)->setVolume(1.f);
(*ret)->setPan(0.f);
return *ret;
}
/** Start SoundMacro object playing directly (for editor use) */
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx)
{
if (!group)
return {};
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, const SoundMacro* macro, uint8_t key, uint8_t vel,
uint8_t mod, ObjToken<Studio> smx) {
if (!group)
return {};
std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
if (!(*ret)->loadMacroObject(macro, 0, 1000.f, key, vel, mod))
{
_destroyVoice(ret);
return {};
}
if (!(*ret)->loadMacroObject(macro, 0, 1000.f, key, vel, mod)) {
_destroyVoice(ret);
return {};
}
(*ret)->setVolume(1.f);
(*ret)->setPan(0.f);
return *ret;
(*ret)->setVolume(1.f);
(*ret)->setPan(0.f);
return *ret;
}
/** Start PageObject node playing directly (for editor use) */
ObjToken<Voice> Engine::pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key,
uint8_t vel, uint8_t mod, ObjToken<Studio> smx)
{
if (!group)
return {};
ObjToken<Voice> Engine::pageObjectStart(const AudioGroup* group, ObjectId id, uint8_t key, uint8_t vel, uint8_t mod,
ObjToken<Studio> smx) {
if (!group)
return {};
std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
std::list<ObjToken<Voice>>::iterator ret = _allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
if (!(*ret)->loadPageObject(id, 1000.f, key, vel, mod))
{
_destroyVoice(ret);
return {};
}
if (!(*ret)->loadPageObject(id, 1000.f, key, vel, mod)) {
_destroyVoice(ret);
return {};
}
return *ret;
return *ret;
}
/** 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,
SFXId sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx)
{
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
return {};
ObjToken<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff, SFXId sfxId,
float minVol, float maxVol, bool doppler, ObjToken<Studio> smx) {
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
return {};
AudioGroup* grp = std::get<0>(search->second);
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
if (!grp)
return {};
AudioGroup* grp = std::get<0>(search->second);
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
if (!grp)
return {};
std::list<ObjToken<Voice>>::iterator vox =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
std::list<ObjToken<Voice>>::iterator vox =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
if (!(*vox)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0))
{
_destroyVoice(vox);
return {};
}
if (!(*vox)->loadPageObject(entry->objId, 1000.f, entry->defKey, entry->defVel, 0)) {
_destroyVoice(vox);
return {};
}
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(),
MakeObj<Emitter>(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
Emitter& ret = *(*emitIt);
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(),
MakeObj<Emitter>(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
Emitter& ret = *(*emitIt);
ret.getVoice()->setPan(entry->panning);
ret.setVectors(pos, dir);
ret.setMaxVol(maxVol);
ret.getVoice()->setPan(entry->panning);
ret.setVectors(pos, dir);
ret.setMaxVol(maxVol);
return *emitIt;
return *emitIt;
}
/** 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,
float frontDiff, float backDiff, float soundSpeed, float volume)
{
auto listenerIt = m_activeListeners.emplace(m_activeListeners.end(),
MakeObj<Listener>(volume, frontDiff, backDiff, soundSpeed));
Listener& ret = *(*listenerIt);
ret.setVectors(pos, dir, heading, up);
return *listenerIt;
float frontDiff, float backDiff, float soundSpeed, float volume) {
auto listenerIt =
m_activeListeners.emplace(m_activeListeners.end(), MakeObj<Listener>(volume, frontDiff, backDiff, soundSpeed));
Listener& ret = *(*listenerIt);
ret.setVectors(pos, dir, heading, up);
return *listenerIt;
}
/** Remove listener from engine's listener list */
void Engine::removeListener(Listener* listener)
{
for (auto it = m_activeListeners.begin() ; it != m_activeListeners.end() ; ++it)
{
if (it->get() == listener)
{
m_activeListeners.erase(it);
return;
}
void Engine::removeListener(Listener* listener) {
for (auto it = m_activeListeners.begin(); it != m_activeListeners.end(); ++it) {
if (it->get() == listener) {
m_activeListeners.erase(it);
return;
}
}
}
/** Start song playing from loaded audio groups */
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);
if (songGrp.second)
{
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx);
if (!*ret)
return {};
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);
if (songGrp.second) {
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx);
if (!*ret)
return {};
if (arrData)
(*ret)->playSong(arrData, loop);
return *ret;
}
if (arrData)
(*ret)->playSong(arrData, loop);
return *ret;
}
std::pair<AudioGroup*, const SFXGroupIndex*> sfxGrp = _findSFXGroup(groupId);
if (sfxGrp.second)
{
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx);
if (!*ret)
return {};
return *ret;
}
std::pair<AudioGroup*, const SFXGroupIndex*> sfxGrp = _findSFXGroup(groupId);
if (sfxGrp.second) {
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx);
if (!*ret)
return {};
return *ret;
}
return {};
return {};
}
ObjToken<Sequencer> Engine::seqPlay(const AudioGroup* group, GroupId groupId, SongId songId,
const unsigned char* arrData, bool loop, ObjToken<Studio> smx)
{
const SongGroupIndex* sgIdx = group->getProj().getSongGroupIndex(groupId);
if (sgIdx)
{
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
if (!*ret)
return {};
const unsigned char* arrData, bool loop, ObjToken<Studio> smx) {
const SongGroupIndex* sgIdx = group->getProj().getSongGroupIndex(groupId);
if (sgIdx) {
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
if (!*ret)
return {};
if (arrData)
(*ret)->playSong(arrData, loop);
return *ret;
}
if (arrData)
(*ret)->playSong(arrData, loop);
return *ret;
}
const SFXGroupIndex* sfxIdx = group->getProj().getSFXGroupIndex(groupId);
if (sfxIdx)
{
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
if (!*ret)
return {};
return *ret;
}
const SFXGroupIndex* sfxIdx = group->getProj().getSFXGroupIndex(groupId);
if (sfxIdx) {
std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*group, groupId, songId, smx);
if (!*ret)
return {};
return *ret;
}
return {};
return {};
}
/** Set total volume of engine */
void Engine::setVolume(float vol)
{
m_masterVolume = vol;
}
void Engine::setVolume(float vol) { m_masterVolume = vol; }
/** Find voice from VoiceId */
ObjToken<Voice> Engine::findVoice(int vid)
{
for (ObjToken<Voice>& vox : m_activeVoices)
{
ObjToken<Voice> ret = vox->_findVoice(vid, vox);
if (ret)
return ret;
}
ObjToken<Voice> Engine::findVoice(int vid) {
for (ObjToken<Voice>& vox : m_activeVoices) {
ObjToken<Voice> ret = vox->_findVoice(vid, vox);
if (ret)
return ret;
}
for (ObjToken<Sequencer>& seq : m_activeSequencers)
{
ObjToken<Voice> ret = seq->findVoice(vid);
if (ret)
return ret;
}
for (ObjToken<Sequencer>& seq : m_activeSequencers) {
ObjToken<Voice> ret = seq->findVoice(vid);
if (ret)
return ret;
}
return {};
return {};
}
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `flag` set */
void Engine::killKeygroup(uint8_t kg, bool now)
{
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
{
Voice* vox = it->get();
if (vox->m_keygroup == kg)
{
if (now)
{
it = _destroyVoice(it);
continue;
}
vox->keyOff();
}
++it;
void Engine::killKeygroup(uint8_t kg, bool now) {
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();) {
Voice* vox = it->get();
if (vox->m_keygroup == kg) {
if (now) {
it = _destroyVoice(it);
continue;
}
vox->keyOff();
}
++it;
}
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->killKeygroup(kg, now);
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->killKeygroup(kg, now);
}
/** Send all voices using `macroId` the message `val` */
void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
{
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end(); ++it)
{
Voice* vox = it->get();
if (vox->getObjectId() == macroId)
vox->message(val);
}
void Engine::sendMacroMessage(ObjectId macroId, int32_t val) {
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end(); ++it) {
Voice* vox = it->get();
if (vox->getObjectId() == macroId)
vox->message(val);
}
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->sendMacroMessage(macroId, val);
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->sendMacroMessage(macroId, val);
}
size_t Engine::getNumTotalActiveVoices() const
{
size_t ret = 0;
for (const auto& vox : m_activeVoices)
ret += vox->getTotalVoices();
return ret;
}
size_t Engine::getNumTotalActiveVoices() const {
size_t ret = 0;
for (const auto& vox : m_activeVoices)
ret += vox->getTotalVoices();
return ret;
}
} // namespace amuse

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