Compare commits
98 Commits
v1.2
...
77c7daa67c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77c7daa67c | ||
| e3c936648b | |||
|
|
1fefba66e7 | ||
|
|
594fb346e1 | ||
|
|
3833c137f6 | ||
|
|
912eb7950c | ||
| 4146cdd049 | |||
|
|
f5a0a46453 | ||
|
|
8f94bdd488 | ||
|
|
f37296d560 | ||
| fd292491c7 | |||
|
|
380381c7d1 | ||
|
|
3f9a91b0ab | ||
|
|
985994a291 | ||
|
|
e37128d657 | ||
|
|
4c2a262478 | ||
|
|
3eba4cc1ac | ||
|
|
91b88c0568 | ||
|
|
6f7d09ce45 | ||
|
|
5caf6bf017 | ||
|
|
d399f1d302 | ||
|
|
77215fc996 | ||
|
|
2f4d0e7541 | ||
|
|
89233e98b5 | ||
|
|
b890a76e20 | ||
|
|
3c7cf5515f | ||
|
|
ebe6f18898 | ||
|
|
cab7402104 | ||
|
|
862c618b7e | ||
|
|
448b212ad9 | ||
|
|
03d597b0ac | ||
|
|
75830dc6dd | ||
|
|
e18c0a62de | ||
|
|
047a91452e | ||
|
|
fdf07d6c33 | ||
|
|
4b2b86f420 | ||
|
|
c6781df90a | ||
|
|
aef2b2a707 | ||
| 0bace131e8 | |||
|
|
4e7c31849d | ||
|
|
3d56d5f0cc | ||
| 54bbc7399a | |||
|
|
a23af16349 | ||
|
|
5c8fa2e8ab | ||
|
|
aff8880595 | ||
|
|
2e7345f11d | ||
|
|
ecd990e94e | ||
|
|
2836e73812 | ||
|
|
df167556fb | ||
|
|
72d0df7d46 | ||
|
|
1dfdf4c392 | ||
|
|
9cf96ad6f9 | ||
|
|
eb948dfd63 | ||
| c886bfd7d2 | |||
|
|
3fd3f3edc5 | ||
|
|
7cb7ed73ea | ||
|
|
2521f37408 | ||
|
|
bfe6668d0c | ||
|
|
38f24ce3e4 | ||
|
|
218fad7541 | ||
| af68ee61e1 | |||
| b40b2c031a | |||
| aa32ff7e84 | |||
|
|
28cac7ff83 | ||
|
|
055e73183a | ||
|
|
a048605011 | ||
|
|
260ec5bb93 | ||
|
|
2a2a16fd17 | ||
|
|
b421412cac | ||
|
|
5bae40d3c3 | ||
|
|
d06a5ebcfa | ||
|
|
e2581cea8b | ||
|
|
d602fbacd3 | ||
|
|
2dcb9dd1c7 | ||
|
|
d3d5595422 | ||
|
|
596bc66ce6 | ||
|
|
3a7b43a63a | ||
|
|
83a2bf0b4e | ||
|
|
695fc10b8f | ||
| feea7c2ecc | |||
|
|
1be5d6e821 | ||
|
|
2d31313594 | ||
|
|
52cba61f76 | ||
|
|
fe78a675d7 | ||
|
|
3427515960 | ||
|
|
5ad8c06b99 | ||
|
|
e99dbc7e0a | ||
|
|
d6b9d4fca1 | ||
|
|
c7f093c5ee | ||
|
|
22a8534887 | ||
|
|
0c606fa9b7 | ||
|
|
a0241574ba | ||
|
|
bd10015024 | ||
|
|
a0bb35433a | ||
|
|
3bc47baa1d | ||
|
|
7666b51c50 | ||
|
|
ee29fb4b1e | ||
|
|
ad23f9d0c4 |
30
.clang-format
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
||||
UseTab: Never
|
||||
---
|
||||
Language: Cpp
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
BreakBeforeBraces: Allman
|
||||
IndentCaseLabels: false
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
BreakStringLiterals: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
Cpp11BracedListStyle: true
|
||||
NamespaceIndentation: None
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
SortIncludes: false
|
||||
AccessModifierOffset: -4
|
||||
ConstructorInitializerIndentWidth: 0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
@@ -12,7 +12,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.io.github.axiodl.Amuse.AudioGroups</string>
|
||||
<string>group.com.axiodl.Amuse.AudioGroups</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -36,15 +36,15 @@
|
||||
IBOutlet NSTableView* sfxTable;
|
||||
IBOutlet NSTableView* samplesTable;
|
||||
IBOutlet NSTextView* creditsView;
|
||||
|
||||
|
||||
IBOutlet NSButton* removeDataButton;
|
||||
IBOutlet NSMenuItem* removeDataMenu;
|
||||
|
||||
|
||||
AudioGroupFilePresenter* groupFilePresenter;
|
||||
|
||||
|
||||
SamplesTableController* samplesController;
|
||||
SFXTableController* sfxController;
|
||||
|
||||
|
||||
@public
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> booEngine;
|
||||
std::experimental::optional<amuse::BooBackendVoiceAllocator> amuseAllocator;
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
|
||||
- (void)pumpTimer:(NSTimer*)timer
|
||||
{
|
||||
amuseEngine->pumpEngine();
|
||||
booEngine->pumpAndMixVoices();
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
|
||||
@@ -251,7 +251,7 @@
|
||||
{
|
||||
__block NSOpenPanel* panel = [NSOpenPanel openPanel];
|
||||
[panel beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) {
|
||||
if (result == NSFileHandlingPanelOKButton)
|
||||
if (result == NSModalResponseOK)
|
||||
{
|
||||
[self importURL:panel.URL];
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.io.github.axiodl.Amuse.AudioGroups</string>
|
||||
<string>group.com.axiodl.Amuse.AudioGroups</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -30,24 +30,28 @@ struct AudioGroupDataCollection
|
||||
NSURL* m_meta;
|
||||
|
||||
AudioGroupDataToken* m_token;
|
||||
|
||||
|
||||
std::vector<uint8_t> m_projData;
|
||||
std::vector<uint8_t> m_poolData;
|
||||
std::vector<uint8_t> m_sdirData;
|
||||
std::vector<uint8_t> m_sampData;
|
||||
|
||||
|
||||
struct MetaData
|
||||
{
|
||||
amuse::DataFormat fmt;
|
||||
uint32_t absOffs;
|
||||
uint32_t active;
|
||||
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
||||
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {}
|
||||
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
|
||||
{
|
||||
}
|
||||
MetaData(athena::io::FileReader& r)
|
||||
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
|
||||
: 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;
|
||||
@@ -60,8 +64,11 @@ struct AudioGroupDataCollection
|
||||
bool loadSamp(AudioGroupFilePresenter* presenter);
|
||||
bool loadMeta(AudioGroupFilePresenter* presenter);
|
||||
|
||||
AudioGroupDataCollection(const std::string& name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta);
|
||||
bool isDataComplete() const {return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;}
|
||||
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);
|
||||
|
||||
@@ -72,16 +79,16 @@ struct AudioGroupDataCollection
|
||||
struct AudioGroupCollection
|
||||
{
|
||||
NSURL* m_url;
|
||||
|
||||
|
||||
AudioGroupCollectionToken* m_token;
|
||||
std::map<std::string, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
|
||||
|
||||
|
||||
AudioGroupCollection(NSURL* url);
|
||||
void addCollection(AudioGroupFilePresenter* presenter,
|
||||
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||
void update(AudioGroupFilePresenter* presenter);
|
||||
bool doSearch(const std::string& str);
|
||||
bool doSearch(std::string_view str);
|
||||
bool doActiveFilter();
|
||||
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
|
||||
void addSamples(std::vector<AudioGroupSampleToken*>& vecOut);
|
||||
@@ -119,8 +126,10 @@ struct AudioGroupCollection
|
||||
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
|
||||
@@ -144,12 +153,13 @@ struct AudioGroupCollection
|
||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
|
||||
NSOutlineView* m_lastOutlineView;
|
||||
NSString* m_searchStr;
|
||||
|
||||
|
||||
std::vector<AudioGroupSFXToken*> m_sfxTableData;
|
||||
std::vector<AudioGroupSampleToken*> m_sampleTableData;
|
||||
}
|
||||
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
|
||||
- (BOOL)addCollectionName:(std::string&&)name items:(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&&)collection;
|
||||
- (BOOL)addCollectionName:(std::string&&)name
|
||||
items:(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&&)collection;
|
||||
- (void)update;
|
||||
- (void)resetIterators;
|
||||
- (void)setSearchFilter:(NSString*)str;
|
||||
|
||||
@@ -161,13 +161,13 @@ void AudioGroupCollection::update(AudioGroupFilePresenter* presenter)
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioGroupCollection::doSearch(const std::string& str)
|
||||
bool AudioGroupCollection::doSearch(std::string_view str)
|
||||
{
|
||||
bool ret = false;
|
||||
m_filterGroups.clear();
|
||||
m_filterGroups.reserve(m_groups.size());
|
||||
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
|
||||
if (str.empty() || StrToLower(it->first).find(str) != std::string::npos)
|
||||
if (str.empty() || StrToLower(it->first).find(str.data()) != std::string::npos)
|
||||
{
|
||||
m_filterGroups.push_back(it);
|
||||
ret = true;
|
||||
@@ -239,7 +239,7 @@ void AudioGroupCollection::addSamples(std::vector<AudioGroupSampleToken*>& vecOu
|
||||
}
|
||||
}
|
||||
|
||||
AudioGroupDataCollection::AudioGroupDataCollection(const std::string& name, NSURL* proj, NSURL* pool,
|
||||
AudioGroupDataCollection::AudioGroupDataCollection(std::string_view name, NSURL* proj, NSURL* pool,
|
||||
NSURL* sdir, NSURL* samp, NSURL* meta)
|
||||
: m_name(name), m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), m_meta(meta),
|
||||
m_token([[AudioGroupDataToken alloc] initWithDataCollection:this]) {}
|
||||
@@ -849,7 +849,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter)
|
||||
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client
|
||||
{
|
||||
m_audioGroupClient = client;
|
||||
m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.io.github.axiodl.Amuse.AudioGroups"];
|
||||
m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.axiodl.Amuse.AudioGroups"];
|
||||
if (!m_groupURL)
|
||||
return nil;
|
||||
[NSFileCoordinator addFilePresenter:self];
|
||||
|
||||
@@ -27,12 +27,10 @@ namespace amuse
|
||||
class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator
|
||||
{
|
||||
public:
|
||||
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
||||
: BooBackendVoiceAllocator(booEngine) {}
|
||||
AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
|
||||
};
|
||||
|
||||
void RegisterAudioUnit();
|
||||
|
||||
}
|
||||
|
||||
@interface AmuseAudioUnit : AUAudioUnit <AudioGroupClient>
|
||||
@@ -47,9 +45,9 @@ void RegisterAudioUnit();
|
||||
AUAudioUnitBusArray* m_outs;
|
||||
}
|
||||
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
|
||||
error:(NSError * __nullable * __nonnull)outError
|
||||
error:(NSError* __nullable* __nonnull)outError
|
||||
viewController:(AudioUnitViewController* __nonnull)vc;
|
||||
- (void)requestAudioGroup:(AudioGroupToken*)group;
|
||||
- (void)requestAudioGroup:(AudioGroupToken* _Nonnull)group;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@@ -112,11 +112,13 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine
|
||||
m_mixInfo.m_periodFrames = periodFrames;
|
||||
m_mixInfo.m_sampleRate = sampleRate;
|
||||
_buildAudioRenderClient();
|
||||
|
||||
for (boo::AudioVoice* vox : m_activeVoices)
|
||||
vox->_resetSampleRate(vox->m_sampleRateIn);
|
||||
for (boo::AudioSubmix* smx : m_activeSubmixes)
|
||||
smx->_resetOutputSampleRate();
|
||||
|
||||
if (m_voiceHead)
|
||||
for (boo::AudioVoice& vox : *m_voiceHead)
|
||||
vox._resetSampleRate(vox.m_sampleRateIn);
|
||||
if (m_submixHead)
|
||||
for (boo::AudioSubmix& smx : *m_submixHead)
|
||||
smx._resetOutputSampleRate();
|
||||
}
|
||||
|
||||
void pumpAndMixVoices()
|
||||
@@ -281,7 +283,7 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine
|
||||
/* Output buffers */
|
||||
voxEngine.m_renderFrames = frameCount;
|
||||
voxEngine.m_outputData = outputData;
|
||||
amuseEngine.pumpEngine();
|
||||
voxEngine.pumpAndMixVoices();
|
||||
return noErr;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,109 +1,113 @@
|
||||
if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER 10.10))
|
||||
set(APPLE_DEV_ID "" CACHE STRING "Mac Developer ID string 'Mac Developer: John Smith (XXXXXXXXXX)'")
|
||||
set(APPLE_TEAM_ID "" CACHE STRING "Team ID string provisioned within Xcode / Apple's portal")
|
||||
find_library(AVFOUNDATION_LIBRARY AVFoundation)
|
||||
find_library(AUDIOUNIT_LIBRARY AudioUnit)
|
||||
find_library(COREAUDIOKIT_LIBRARY CoreAudioKit)
|
||||
if (NOT (AUDIOUNIT_LIBRARY STREQUAL AUDIOUNIT_LIBRARY-NOTFOUND))
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if (APPLE_DEV_ID AND APPLE_TEAM_ID)
|
||||
find_library(AVFOUNDATION_LIBRARY AVFoundation)
|
||||
find_library(AUDIOUNIT_LIBRARY AudioUnit)
|
||||
find_library(COREAUDIOKIT_LIBRARY CoreAudioKit)
|
||||
if (NOT (AUDIOUNIT_LIBRARY STREQUAL AUDIOUNIT_LIBRARY-NOTFOUND))
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
# Search for provision profile to make AudioUnit extension on OS X
|
||||
unset(PROV_PROFILE)
|
||||
file(GLOB PROV_FILES "$ENV{HOME}/Library/MobileDevice/Provisioning Profiles/*.provisionprofile")
|
||||
foreach(FILE ${PROV_FILES})
|
||||
file(STRINGS "${FILE}" NAME REGEX ${APPLE_TEAM_ID})
|
||||
if(NAME)
|
||||
set(PROV_PROFILE "${FILE}")
|
||||
break()
|
||||
# Search for provision profile to make AudioUnit extension on OS X
|
||||
unset(PROV_PROFILE)
|
||||
file(GLOB PROV_FILES "$ENV{HOME}/Library/MobileDevice/Provisioning Profiles/*.provisionprofile")
|
||||
foreach(FILE ${PROV_FILES})
|
||||
file(STRINGS "${FILE}" NAME REGEX ${APPLE_TEAM_ID})
|
||||
if(NAME)
|
||||
set(PROV_PROFILE "${FILE}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(EXISTS "${PROV_PROFILE}")
|
||||
|
||||
# Extension App
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
|
||||
COMMAND ibtool --errors --warnings --notices --module amuse_au --auto-activate-custom-fonts
|
||||
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
|
||||
)
|
||||
add_executable(amuse-au MACOSX_BUNDLE AudioUnitBackend.hpp AudioUnitBackend.mm
|
||||
AudioUnitViewController.hpp AudioUnitViewController.mm
|
||||
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
|
||||
AudioUnitViewController.nib)
|
||||
|
||||
set(APPLE_BUNDLE_ID "com.axiodl.Amuse.AudioUnit")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseExtension.entitlements.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements)
|
||||
|
||||
target_link_libraries(amuse-au amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY}
|
||||
${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core)
|
||||
set_target_properties(amuse-au PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ExtensionInfo.plist"
|
||||
BUNDLE_EXTENSION "appex" BUNDLE TRUE
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}"
|
||||
LINK_FLAGS "-e _NSExtensionMain -fobjc-arc -fobjc-link-runtime -fapplication-extension")
|
||||
|
||||
|
||||
# Containing App
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
|
||||
COMMAND ibtool --errors --warnings --notices --module amuse_au_container --auto-activate-custom-fonts
|
||||
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
|
||||
)
|
||||
add_executable(amuse-au-container MACOSX_BUNDLE AmuseContainingApp.hpp AmuseContainingApp.mm
|
||||
AudioUnitBackend.hpp AudioUnitBackend.mm
|
||||
AudioUnitViewController.hpp AudioUnitViewController.mm
|
||||
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
|
||||
AmuseContainerMainMenu.nib)
|
||||
set_source_files_properties(AudioUnitBackend.mm AudioUnitViewController.mm
|
||||
AmuseContainingApp.mm AudioGroupFilePresenter.mm
|
||||
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
target_link_libraries(amuse-au-container amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY}
|
||||
${AVFOUNDATION_LIBRARY} ${ZLIB_LIBRARIES} ${LZO_LIB} ${BOO_SYS_LIBS} logvisor athena-core)
|
||||
|
||||
set(APPLE_BUNDLE_ID "com.axiodl.Amuse")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainer.entitlements.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements)
|
||||
|
||||
set_target_properties(amuse-au-container PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ContainerInfo.plist"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}")
|
||||
|
||||
add_custom_command(TARGET amuse-au POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib"
|
||||
"$<TARGET_FILE_DIR:amuse-au>/../Resources/AudioUnitViewController.nib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$<TARGET_FILE_DIR:amuse-au>/../embedded.provisionprofile"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "$<TARGET_FILE_DIR:amuse-au>/../.."
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
|
||||
COMMAND codesign --force --sign
|
||||
${APPLE_DEV_ID} --entitlements "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
|
||||
COMMAND codesign --force --sign ${APPLE_DEV_ID}
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../.."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET amuse-au-container POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib"
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../Resources/AmuseContainerMainMenu.nib"
|
||||
COMMAND codesign --force --sign
|
||||
${APPLE_DEV_ID} "$<TARGET_FILE_DIR:amuse-au-container>/../.."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
else()
|
||||
message(WARNING "Unable to find developer provisioning profile; skipping Amuse-AU")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(EXISTS "${PROV_PROFILE}")
|
||||
|
||||
# Extension App
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
|
||||
COMMAND ibtool --errors --warnings --notices --module amuse_au --auto-activate-custom-fonts
|
||||
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AudioUnitViewController.xib
|
||||
)
|
||||
add_executable(amuse-au MACOSX_BUNDLE AudioUnitBackend.hpp AudioUnitBackend.mm
|
||||
AudioUnitViewController.hpp AudioUnitViewController.mm
|
||||
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
|
||||
AudioUnitViewController.nib)
|
||||
|
||||
set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse.AudioUnit")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseExtension.entitlements.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements)
|
||||
|
||||
target_link_libraries(amuse-au amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY}
|
||||
${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core)
|
||||
set_target_properties(amuse-au PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ExtensionInfo.plist"
|
||||
BUNDLE_EXTENSION "appex" BUNDLE TRUE
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}"
|
||||
LINK_FLAGS "-e _NSExtensionMain -fobjc-arc -fobjc-link-runtime -fapplication-extension")
|
||||
|
||||
|
||||
# Containing App
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
|
||||
COMMAND ibtool --errors --warnings --notices --module amuse_au_container --auto-activate-custom-fonts
|
||||
--target-device mac --minimum-deployment-target 10.11 --output-format human-readable-text --compile
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainerMainMenu.xib
|
||||
)
|
||||
add_executable(amuse-au-container MACOSX_BUNDLE AmuseContainingApp.hpp AmuseContainingApp.mm
|
||||
AudioUnitBackend.hpp AudioUnitBackend.mm
|
||||
AudioUnitViewController.hpp AudioUnitViewController.mm
|
||||
AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm
|
||||
AmuseContainerMainMenu.nib)
|
||||
set_source_files_properties(AudioUnitBackend.mm AudioUnitViewController.mm
|
||||
AmuseContainingApp.mm AudioGroupFilePresenter.mm
|
||||
PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||
target_link_libraries(amuse-au-container amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY}
|
||||
${AVFOUNDATION_LIBRARY} ${ZLIB_LIBRARIES} ${BOO_SYS_LIBS} logvisor athena-core)
|
||||
|
||||
set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseContainer.entitlements.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements)
|
||||
|
||||
set_target_properties(amuse-au-container PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ContainerInfo.plist"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/AmuseContainer.entitlements"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${APPLE_DEV_ID}")
|
||||
|
||||
add_custom_command(TARGET amuse-au POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/AudioUnitViewController.nib"
|
||||
"$<TARGET_FILE_DIR:amuse-au>/../Resources/AudioUnitViewController.nib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$<TARGET_FILE_DIR:amuse-au>/../embedded.provisionprofile"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "$<TARGET_FILE_DIR:amuse-au>/../.."
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
|
||||
COMMAND codesign --force --sign
|
||||
${APPLE_DEV_ID} --entitlements "${CMAKE_CURRENT_BINARY_DIR}/AmuseExtension.entitlements"
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
|
||||
COMMAND codesign --force --sign ${APPLE_DEV_ID}
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../.."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(TARGET amuse-au-container POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/AmuseContainerMainMenu.nib"
|
||||
"$<TARGET_FILE_DIR:amuse-au-container>/../Resources/AmuseContainerMainMenu.nib"
|
||||
COMMAND codesign --force --sign
|
||||
${APPLE_DEV_ID} "$<TARGET_FILE_DIR:amuse-au-container>/../.."
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
else()
|
||||
message(WARNING "Unable to find developer provision profile; skipping Amuse-AU")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "APPLE_DEV_ID and/or APPLE_TEAM_ID not set; skipping Amuse-AU")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.github.axiodl.Amuse</string>
|
||||
<string>com.axiodl.Amuse</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>amuse-au</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.github.axiodl.Amuse.AudioUnit</string>
|
||||
<string>com.axiodl.Amuse.AudioUnit</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
|
||||
|
||||
project(amuse)
|
||||
|
||||
if(EXISTS boo)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing")
|
||||
endif()
|
||||
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/boo AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/athena)
|
||||
message(STATUS "Preparing standalone build")
|
||||
add_subdirectory(boo)
|
||||
add_subdirectory(athena)
|
||||
include_directories(athena/include)
|
||||
endif()
|
||||
|
||||
set(SOURCES
|
||||
@@ -10,21 +22,21 @@ set(SOURCES
|
||||
lib/AudioGroupPool.cpp
|
||||
lib/AudioGroupProject.cpp
|
||||
lib/AudioGroupSampleDirectory.cpp
|
||||
lib/DirectoryEnumerator.cpp
|
||||
lib/Emitter.cpp
|
||||
lib/Engine.cpp
|
||||
lib/Envelope.cpp
|
||||
lib/Listener.cpp
|
||||
lib/Sequencer.cpp
|
||||
lib/SoundMacroState.cpp
|
||||
lib/SongConverter.cpp
|
||||
lib/SongState.cpp
|
||||
lib/Voice.cpp
|
||||
lib/VolumeLUT.cpp
|
||||
lib/Submix.cpp
|
||||
lib/EffectBase.cpp
|
||||
lib/Studio.cpp
|
||||
lib/EffectReverb.cpp
|
||||
lib/EffectChorus.cpp
|
||||
lib/EffectDelay.cpp
|
||||
lib/SurroundProfiles.cpp
|
||||
lib/ContainerRegistry.cpp
|
||||
lib/DSPCodec.c
|
||||
lib/N64MusyXCodec.c)
|
||||
@@ -35,6 +47,7 @@ set(HEADERS
|
||||
include/amuse/AudioGroupPool.hpp
|
||||
include/amuse/AudioGroupProject.hpp
|
||||
include/amuse/AudioGroupSampleDirectory.hpp
|
||||
include/amuse/DirectoryEnumerator.hpp
|
||||
include/amuse/Emitter.hpp
|
||||
include/amuse/Engine.hpp
|
||||
include/amuse/Entity.hpp
|
||||
@@ -42,9 +55,11 @@ set(HEADERS
|
||||
include/amuse/Listener.hpp
|
||||
include/amuse/Sequencer.hpp
|
||||
include/amuse/SoundMacroState.hpp
|
||||
include/amuse/SongConverter.hpp
|
||||
include/amuse/SongState.hpp
|
||||
include/amuse/Voice.hpp
|
||||
include/amuse/Submix.hpp
|
||||
include/amuse/Studio.hpp
|
||||
include/amuse/IBackendSubmix.hpp
|
||||
include/amuse/IBackendVoice.hpp
|
||||
include/amuse/IBackendVoiceAllocator.hpp
|
||||
@@ -52,7 +67,6 @@ set(HEADERS
|
||||
include/amuse/EffectReverb.hpp
|
||||
include/amuse/EffectChorus.hpp
|
||||
include/amuse/EffectDelay.hpp
|
||||
include/amuse/SurroundProfiles.hpp
|
||||
include/amuse/ContainerRegistry.hpp
|
||||
include/amuse/Common.hpp
|
||||
include/amuse/amuse.hpp
|
||||
@@ -62,7 +76,7 @@ set(HEADERS
|
||||
unset(EXTRAS)
|
||||
if(TARGET boo)
|
||||
include_directories(${BOO_INCLUDE_DIR} ${BOO_INCLUDE_DIR}/../lib ${BOO_INCLUDE_DIR}/../soxr/src
|
||||
${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
|
||||
${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${LZO_INCLUDE_DIR})
|
||||
list(APPEND EXTRAS lib/BooBackend.cpp include/amuse/BooBackend.hpp)
|
||||
endif()
|
||||
|
||||
@@ -73,15 +87,47 @@ add_library(amuse
|
||||
${SOURCES}
|
||||
${HEADERS}
|
||||
${EXTRAS})
|
||||
if(COMMAND add_sanitizers)
|
||||
add_sanitizers(amuse)
|
||||
endif()
|
||||
if(COMMAND cotire)
|
||||
set_target_properties(amuse PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
|
||||
cotire(amuse)
|
||||
endif()
|
||||
|
||||
if(TARGET boo)
|
||||
if(TARGET boo AND NOT WINDOWS_STORE)
|
||||
# AudioUnit Target (OS X only)
|
||||
add_subdirectory(AudioUnit)
|
||||
|
||||
# VST Target
|
||||
add_subdirectory(VST)
|
||||
|
||||
# Multi-platform CLI tool
|
||||
add_executable(amuseplay WIN32 driver/main.cpp)
|
||||
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core ${ZLIB_LIBRARIES})
|
||||
# Multi-platform CLI tools
|
||||
|
||||
# Player
|
||||
add_executable(amuseplay WIN32 driver/amuseplay.cpp)
|
||||
target_link_libraries(amuseplay amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} ${LZO_LIB})
|
||||
|
||||
# Converter
|
||||
add_executable(amuseconv driver/amuseconv.cpp)
|
||||
target_link_libraries(amuseconv amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} ${LZO_LIB})
|
||||
|
||||
# Renderer
|
||||
add_executable(amuserender driver/amuserender.cpp)
|
||||
target_link_libraries(amuserender amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml ${ZLIB_LIBRARIES} ${LZO_LIB})
|
||||
|
||||
if(COMMAND add_sanitizers)
|
||||
add_sanitizers(amuseplay)
|
||||
add_sanitizers(amuseconv)
|
||||
add_sanitizers(amuserender)
|
||||
endif()
|
||||
|
||||
# Editor
|
||||
find_package(Qt5Widgets)
|
||||
if (Qt5Widgets_FOUND)
|
||||
message(STATUS "Qt5 found, amuse-gui will be built")
|
||||
add_subdirectory(Editor)
|
||||
else()
|
||||
message(STATUS "Qt5 not found, amuse-gui will not be built")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
35
Editor/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
find_package(Qt5Widgets)
|
||||
find_package(Qt5Network)
|
||||
find_package(Qt5Xml)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND PLAT_SRCS platforms/win/amuse-gui.rc platforms/win/amuse-gui.manifest)
|
||||
elseif(APPLE)
|
||||
list(APPEND PLAT_SRCS platforms/mac/mainicon.icns)
|
||||
set_source_files_properties(platforms/mac/mainicon.icns PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION Resources)
|
||||
endif()
|
||||
|
||||
add_subdirectory(platforms/freedesktop)
|
||||
declare_qticon_target()
|
||||
list(APPEND PLAT_SRCS mainicon_qt.cpp)
|
||||
|
||||
add_executable(amuse-gui WIN32 MACOSX_BUNDLE
|
||||
MainWindow.ui MainWindow.hpp MainWindow.cpp
|
||||
${PLAT_SRCS}
|
||||
main.cpp)
|
||||
|
||||
set_target_properties(amuse-gui PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist")
|
||||
|
||||
target_link_libraries(amuse-gui ${PLAT_LIBS}
|
||||
${Qt5Widgets_LIBRARIES}
|
||||
${Qt5Network_LIBRARIES}
|
||||
${Qt5Xml_LIBRARIES}
|
||||
boo logvisor zeus athena-core athena-libyaml xxhash z)
|
||||
5
Editor/MainWindow.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by Jack Andersen on 3/5/18.
|
||||
//
|
||||
|
||||
#include "MainWindow.hpp"
|
||||
15
Editor/MainWindow.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Created by Jack Andersen on 3/5/18.
|
||||
//
|
||||
|
||||
#ifndef URDE_MAINWINDOW_HPP
|
||||
#define URDE_MAINWINDOW_HPP
|
||||
|
||||
|
||||
class MainWindow
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //URDE_MAINWINDOW_HPP
|
||||
24
Editor/MainWindow.ui
Normal file
@@ -0,0 +1,24 @@
|
||||
<ui version="4.0" >
|
||||
<author></author>
|
||||
<comment></comment>
|
||||
<exportmacro></exportmacro>
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QMenuBar" name="menubar" />
|
||||
<widget class="QWidget" name="centralwidget" />
|
||||
<widget class="QStatusBar" name="statusbar" />
|
||||
</widget>
|
||||
<pixmapfunction></pixmapfunction>
|
||||
<connections/>
|
||||
</ui>
|
||||
8
Editor/main.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by Jack Andersen on 3/5/18.
|
||||
//
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// TODO: Do
|
||||
}
|
||||
BIN
Editor/platforms/freedesktop/1024x1024/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
Editor/platforms/freedesktop/128x128/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
Editor/platforms/freedesktop/16x16/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 924 B |
BIN
Editor/platforms/freedesktop/256x256/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
Editor/platforms/freedesktop/32x32/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Editor/platforms/freedesktop/48x48/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
Editor/platforms/freedesktop/512x512/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
Editor/platforms/freedesktop/64x64/apps/amuse.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
18
Editor/platforms/freedesktop/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
include_directories(${LIBPNG_INCLUDE_DIR})
|
||||
add_executable(amuse-mkqticon mkqticon.c)
|
||||
target_link_libraries(amuse-mkqticon ${PNG_LIB} ${ZLIB_LIBRARIES})
|
||||
|
||||
macro(declare_qticon_target)
|
||||
add_custom_command(OUTPUT ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin
|
||||
COMMAND $<TARGET_FILE:amuse-mkqticon>
|
||||
ARGS ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin
|
||||
DEPENDS
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/128x128/apps/amuse.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/64x64/apps/amuse.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/48x48/apps/amuse.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/32x32/apps/amuse.png
|
||||
${amuse_SOURCE_DIR}/Editor/platforms/freedesktop/16x16/apps/amuse.png
|
||||
WORKING_DIRECTORY ${amuse_SOURCE_DIR}/Editor/platforms/freedesktop
|
||||
COMMENT "Generating mainicon_qt.bin")
|
||||
bintoc(mainicon_qt.cpp ${amuse_BINARY_DIR}/Editor/platforms/freedesktop/mainicon_qt.bin MAINICON_QT)
|
||||
endmacro()
|
||||
9
Editor/platforms/freedesktop/amuse.desktop
Normal file
@@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Name=Amuse
|
||||
GenericName=MusyX Game Audio Editor
|
||||
Comment=Edit Audio Data of MusyX Sound Groups
|
||||
Exec=amuse-gui
|
||||
Icon=amuse
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Audio
|
||||
71
Editor/platforms/freedesktop/mkqticon.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const int DIMS[] =
|
||||
{
|
||||
16,
|
||||
32,
|
||||
48,
|
||||
64,
|
||||
128,
|
||||
256,
|
||||
0
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: makeqticon <out.bin>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE* ofp = fopen(argv[1], "wb");
|
||||
if (!ofp)
|
||||
{
|
||||
fprintf(stderr, "'%s' is not able to be opened for writing as a regular file\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char command[2048];
|
||||
|
||||
for (const int* d = DIMS ; *d != 0 ; ++d)
|
||||
{
|
||||
printf("Rendering main icon @%dx%d\n", *d, *d);
|
||||
fflush(stdout);
|
||||
|
||||
snprintf(command, 2048, "%dx%d/apps/amuse.png", *d, *d);
|
||||
FILE* fp = fopen(command, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
fprintf(stderr, "unable to open '%s' for reading\n", command);
|
||||
fclose(ofp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
uint32_t size = (uint32_t)ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
fwrite(&size, 1, 4, ofp);
|
||||
|
||||
uint8_t buf[1024];
|
||||
while (size > 0)
|
||||
{
|
||||
long thisSize = size;
|
||||
if (thisSize > 1024)
|
||||
thisSize = 1024;
|
||||
|
||||
fread(buf, 1, (size_t)thisSize, fp);
|
||||
fwrite(buf, 1, (size_t)thisSize, ofp);
|
||||
size -= thisSize;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
fclose(ofp);
|
||||
return 0;
|
||||
}
|
||||
32
Editor/platforms/mac/Info.plist
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>amuse-gui</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>Version BETA</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>BETA</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>2015-2018 Antidote / Jackoalan</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>mainicon.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.axiodl.Amuse</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Amuse</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>BETA</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
Editor/platforms/mac/mainicon.icns
Normal file
23
Editor/platforms/win/amuse-gui.manifest
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<description>Amuse</description>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>true/PM</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
||||
33
Editor/platforms/win/amuse-gui.rc
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "winver.h"
|
||||
#define IDI_ICON1 101
|
||||
|
||||
IDI_ICON1 ICON DISCARDABLE "mainicon.ico"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
PRODUCTVERSION 1,0,0,0
|
||||
FILEFLAGS 0x0L
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEOS 0x00040004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "000004b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Antidote / Jackoalan"
|
||||
VALUE "FileDescription", "Amuse"
|
||||
VALUE "FileVersion", "BETA"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015-2018 Antidote / Jackoalan"
|
||||
VALUE "InternalName", "amuse"
|
||||
VALUE "OriginalFilename", "amuse-gui.exe"
|
||||
VALUE "ProductName", "Amuse"
|
||||
VALUE "ProductVersion", "BETA"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0, 1200
|
||||
END
|
||||
END
|
||||
BIN
Editor/platforms/win/mainicon.ico
Normal file
|
After Width: | Height: | Size: 118 KiB |
@@ -6,42 +6,153 @@
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
static const wchar_t *const GMNames[128] =
|
||||
{
|
||||
L"Acoustic Grand Piano", L"Bright Acoustic Piano", L"Electric Grand Piano", L"Honky-tonk Piano", L"Rhodes Piano", L"Chorused Piano",
|
||||
L"Harpsichord", L"Clavinet", L"Celesta", L"Glockenspiel", L"Music Box", L"Vibraphone", L"Marimba", L"Xylophone", L"Tubular Bells", L"Dulcimer",
|
||||
L"Drawbar Organ", L"Percussive Organ", L"Rock Organ", L"Church Organ", L"Reed Organ", L"Accordion", L"Harmonica", L"Tango Accordion",
|
||||
L"Acoustic Guitar (nylon)", L"Acoustic Guitar (steel)", L"Electric Guitar (jazz)", L"Electric Guitar (clean)", L"Electric Guitar (muted)",
|
||||
L"Overdriven Guitar", L"Distortion Guitar", L"Guitar Harmonics", L"Acoustic Bass", L"Electric Bass (finger)", L"Electric Bass (pick)",
|
||||
L"Fretless Bass", L"Slap Bass 1", L"Slap Bass 2", L"Synth Bass 1", L"Synth Bass 2", L"Violin", L"Viola", L"Cello", L"Contrabass",
|
||||
L"Tremelo Strings", L"Pizzicato Strings", L"Orchestral Harp", L"Timpani", L"String Ensemble 1", L"String Ensemble 2", L"SynthStrings 1",
|
||||
L"SynthStrings 2", L"Choir Aahs", L"Voice Oohs", L"Synth Voice", L"Orchestra Hit", L"Trumpet", L"Trombone", L"Tuba", L"Muted Trumpet",
|
||||
L"French Horn", L"Brass Section", L"Synth Brass 1", L"Synth Brass 2", L"Soprano Sax", L"Alto Sax", L"Tenor Sax", L"Baritone Sax",
|
||||
L"Oboe", L"English Horn", L"Bassoon", L"Clarinet", L"Piccolo", L"Flute", L"Recorder", L"Pan Flute", L"Bottle Blow", L"Shakuhachi", L"Whistle",
|
||||
L"Ocarina", L"Lead 1 (square)", L"Lead 2 (sawtooth)", L"Lead 3 (calliope lead)", L"Lead 4 (chiff lead)", L"Lead 5 (charang)",
|
||||
L"Lead 6 (voice)", L"Lead 7 (fifths)", L"Lead 8 (bass + lead)", L"Pad 1 (new age)", L"Pad 2 (warm)", L"Pad 3 (polysynth)", L"Pad 4 (choir)",
|
||||
L"Pad 5 (bowed)", L"Pad 6 (metallic)", L"Pad 7 (halo)", L"Pad 8 (sweep)", L"FX 1 (rain)", L"FX 2 (soundtrack)", L"FX 3 (crystal)",
|
||||
L"FX 4 (atmosphere)", L"FX 5 (brightness)", L"FX 6 (goblins)", L"FX 7 (echoes)", L"FX 8 (sci-fi)", L"Sitar", L"Banjo", L"Shamisen", L"Koto",
|
||||
L"Kalimba", L"Bagpipe", L"Fiddle", L"Shanai", L"Tinkle Bell", L"Agogo", L"Steel Drums", L"Woodblock", L"Taiko Drum", L"Melodic Tom",
|
||||
L"Synth Drum", L"Reverse Cymbal", L"Guitar Fret Noise", L"Breath Noise", L"Seashore", L"Bird Tweet", L"Telephone Ring", L"Helicopter",
|
||||
L"Applause", L"Gunshot"
|
||||
};
|
||||
static const wchar_t* const GMNames[128] = {L"Acoustic Grand Piano",
|
||||
L"Bright Acoustic Piano",
|
||||
L"Electric Grand Piano",
|
||||
L"Honky-tonk Piano",
|
||||
L"Rhodes Piano",
|
||||
L"Chorused Piano",
|
||||
L"Harpsichord",
|
||||
L"Clavinet",
|
||||
L"Celesta",
|
||||
L"Glockenspiel",
|
||||
L"Music Box",
|
||||
L"Vibraphone",
|
||||
L"Marimba",
|
||||
L"Xylophone",
|
||||
L"Tubular Bells",
|
||||
L"Dulcimer",
|
||||
L"Drawbar Organ",
|
||||
L"Percussive Organ",
|
||||
L"Rock Organ",
|
||||
L"Church Organ",
|
||||
L"Reed Organ",
|
||||
L"Accordion",
|
||||
L"Harmonica",
|
||||
L"Tango Accordion",
|
||||
L"Acoustic Guitar (nylon)",
|
||||
L"Acoustic Guitar (steel)",
|
||||
L"Electric Guitar (jazz)",
|
||||
L"Electric Guitar (clean)",
|
||||
L"Electric Guitar (muted)",
|
||||
L"Overdriven Guitar",
|
||||
L"Distortion Guitar",
|
||||
L"Guitar Harmonics",
|
||||
L"Acoustic Bass",
|
||||
L"Electric Bass (finger)",
|
||||
L"Electric Bass (pick)",
|
||||
L"Fretless Bass",
|
||||
L"Slap Bass 1",
|
||||
L"Slap Bass 2",
|
||||
L"Synth Bass 1",
|
||||
L"Synth Bass 2",
|
||||
L"Violin",
|
||||
L"Viola",
|
||||
L"Cello",
|
||||
L"Contrabass",
|
||||
L"Tremelo Strings",
|
||||
L"Pizzicato Strings",
|
||||
L"Orchestral Harp",
|
||||
L"Timpani",
|
||||
L"String Ensemble 1",
|
||||
L"String Ensemble 2",
|
||||
L"SynthStrings 1",
|
||||
L"SynthStrings 2",
|
||||
L"Choir Aahs",
|
||||
L"Voice Oohs",
|
||||
L"Synth Voice",
|
||||
L"Orchestra Hit",
|
||||
L"Trumpet",
|
||||
L"Trombone",
|
||||
L"Tuba",
|
||||
L"Muted Trumpet",
|
||||
L"French Horn",
|
||||
L"Brass Section",
|
||||
L"Synth Brass 1",
|
||||
L"Synth Brass 2",
|
||||
L"Soprano Sax",
|
||||
L"Alto Sax",
|
||||
L"Tenor Sax",
|
||||
L"Baritone Sax",
|
||||
L"Oboe",
|
||||
L"English Horn",
|
||||
L"Bassoon",
|
||||
L"Clarinet",
|
||||
L"Piccolo",
|
||||
L"Flute",
|
||||
L"Recorder",
|
||||
L"Pan Flute",
|
||||
L"Bottle Blow",
|
||||
L"Shakuhachi",
|
||||
L"Whistle",
|
||||
L"Ocarina",
|
||||
L"Lead 1 (square)",
|
||||
L"Lead 2 (sawtooth)",
|
||||
L"Lead 3 (calliope lead)",
|
||||
L"Lead 4 (chiff lead)",
|
||||
L"Lead 5 (charang)",
|
||||
L"Lead 6 (voice)",
|
||||
L"Lead 7 (fifths)",
|
||||
L"Lead 8 (bass + lead)",
|
||||
L"Pad 1 (new age)",
|
||||
L"Pad 2 (warm)",
|
||||
L"Pad 3 (polysynth)",
|
||||
L"Pad 4 (choir)",
|
||||
L"Pad 5 (bowed)",
|
||||
L"Pad 6 (metallic)",
|
||||
L"Pad 7 (halo)",
|
||||
L"Pad 8 (sweep)",
|
||||
L"FX 1 (rain)",
|
||||
L"FX 2 (soundtrack)",
|
||||
L"FX 3 (crystal)",
|
||||
L"FX 4 (atmosphere)",
|
||||
L"FX 5 (brightness)",
|
||||
L"FX 6 (goblins)",
|
||||
L"FX 7 (echoes)",
|
||||
L"FX 8 (sci-fi)",
|
||||
L"Sitar",
|
||||
L"Banjo",
|
||||
L"Shamisen",
|
||||
L"Koto",
|
||||
L"Kalimba",
|
||||
L"Bagpipe",
|
||||
L"Fiddle",
|
||||
L"Shanai",
|
||||
L"Tinkle Bell",
|
||||
L"Agogo",
|
||||
L"Steel Drums",
|
||||
L"Woodblock",
|
||||
L"Taiko Drum",
|
||||
L"Melodic Tom",
|
||||
L"Synth Drum",
|
||||
L"Reverse Cymbal",
|
||||
L"Guitar Fret Noise",
|
||||
L"Breath Noise",
|
||||
L"Seashore",
|
||||
L"Bird Tweet",
|
||||
L"Telephone Ring",
|
||||
L"Helicopter",
|
||||
L"Applause",
|
||||
L"Gunshot"};
|
||||
|
||||
static const wchar_t *const GMPercNames[128] =
|
||||
{
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, L"Acoustic Bass Drum", L"Bass Drum 1", L"Side Stick",
|
||||
L"Acoustic Snare", L"Hand Clap", L"Electric Snare", L"Low Floor Tom", L"Closed Hi-Hat",
|
||||
L"High Floor Tom", L"Pedal Hi-Hat", L"Low Tom", L"Open Hi-Hat", L"Low-Mid Tom", L"Hi-Mid Tom",
|
||||
L"Crash Cymbal 1", L"High Tom", L"Ride Cymbal 1", L"Chinese Cymbal", L"Ride Bell", L"Tambourine",
|
||||
L"Splash Cymbal", L"Cowbell", L"Crash Cymbal 2", L"Vibraslap", L"Ride Cymbal 2", L"Hi Bongo",
|
||||
L"Low Bongo", L"Mute Hi Conga", L"Open Hi Conga", L"Low Conga", L"High Timbale", L"Low Timbale",
|
||||
L"High Agogo", L"Low Agogo", L"Cabasa", L"Maracas", L"Short Whistle", L"Long Whistle", L"Short Guiro",
|
||||
L"Long Guiro", L"Claves", L"Hi Wood Block", L"Low Wood Block", L"Mute Cuica", L"Open Cuica",
|
||||
L"Mute Triangle", L"Open Triangle"
|
||||
};
|
||||
static const wchar_t* const GMPercNames[128] = {
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, L"Acoustic Bass Drum",
|
||||
L"Bass Drum 1", L"Side Stick", L"Acoustic Snare", L"Hand Clap", L"Electric Snare",
|
||||
L"Low Floor Tom", L"Closed Hi-Hat", L"High Floor Tom", L"Pedal Hi-Hat", L"Low Tom",
|
||||
L"Open Hi-Hat", L"Low-Mid Tom", L"Hi-Mid Tom", L"Crash Cymbal 1", L"High Tom",
|
||||
L"Ride Cymbal 1", L"Chinese Cymbal", L"Ride Bell", L"Tambourine", L"Splash Cymbal",
|
||||
L"Cowbell", L"Crash Cymbal 2", L"Vibraslap", L"Ride Cymbal 2", L"Hi Bongo",
|
||||
L"Low Bongo", L"Mute Hi Conga", L"Open Hi Conga", L"Low Conga", L"High Timbale",
|
||||
L"Low Timbale", L"High Agogo", L"Low Agogo", L"Cabasa", L"Maracas",
|
||||
L"Short Whistle", L"Long Whistle", L"Short Guiro", L"Long Guiro", L"Claves",
|
||||
L"Hi Wood Block", L"Low Wood Block", L"Mute Cuica", L"Open Cuica", L"Mute Triangle",
|
||||
L"Open Triangle"};
|
||||
|
||||
bool AudioGroupDataCollection::loadProj()
|
||||
{
|
||||
@@ -106,10 +217,9 @@ bool AudioGroupDataCollection::loadMeta()
|
||||
return ret.operator bool();
|
||||
}
|
||||
|
||||
AudioGroupDataCollection::AudioGroupDataCollection(const std::wstring& path, const std::wstring& name)
|
||||
AudioGroupDataCollection::AudioGroupDataCollection(std::wstring_view path, std::wstring_view name)
|
||||
: m_path(path), m_name(name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool AudioGroupDataCollection::_attemptLoad()
|
||||
@@ -134,28 +244,22 @@ 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;
|
||||
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 m_loadedData.operator bool();
|
||||
@@ -189,18 +293,15 @@ void AudioGroupDataCollection::addToEngine(amuse::Engine& engine)
|
||||
}
|
||||
}
|
||||
|
||||
void AudioGroupDataCollection::removeFromEngine(amuse::Engine& engine) const
|
||||
{
|
||||
engine.removeAudioGroup(*m_loadedData);
|
||||
}
|
||||
void AudioGroupDataCollection::removeFromEngine(amuse::Engine& engine) const { engine.removeAudioGroup(*m_loadedData); }
|
||||
|
||||
AudioGroupCollection::AudioGroupCollection(const std::wstring& path, const std::wstring& name)
|
||||
AudioGroupCollection::AudioGroupCollection(std::wstring_view path, std::wstring_view name)
|
||||
: m_path(path), m_name(name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AudioGroupCollection::addCollection(std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
|
||||
void AudioGroupCollection::addCollection(
|
||||
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
|
||||
{
|
||||
for (std::pair<std::wstring, amuse::IntrusiveAudioGroupData>& pair : collection)
|
||||
{
|
||||
@@ -210,9 +311,9 @@ void AudioGroupCollection::addCollection(std::vector<std::pair<std::wstring, amu
|
||||
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;
|
||||
search =
|
||||
m_groups.emplace(pair.first, std::make_unique<AudioGroupDataCollection>(collectionPath, pair.first))
|
||||
.first;
|
||||
}
|
||||
|
||||
AudioGroupDataCollection& dataCollection = *search->second;
|
||||
@@ -253,9 +354,9 @@ void AudioGroupCollection::update(AudioGroupFilePresenter& presenter)
|
||||
if (search == m_groups.end())
|
||||
{
|
||||
search =
|
||||
m_groups.emplace(nameStr,
|
||||
std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr,
|
||||
nameStr)).first;
|
||||
m_groups
|
||||
.emplace(nameStr, std::make_unique<AudioGroupDataCollection>(m_path + L'\\' + nameStr, nameStr))
|
||||
.first;
|
||||
search->second->_attemptLoad();
|
||||
}
|
||||
}
|
||||
@@ -284,8 +385,10 @@ void AudioGroupFilePresenter::update()
|
||||
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 = theMap
|
||||
.emplace(nameStr, std::make_unique<AudioGroupCollection>(
|
||||
m_backend.getUserDir() + L'\\' + nameStr, nameStr))
|
||||
.first;
|
||||
search->second->update(*this);
|
||||
}
|
||||
}
|
||||
@@ -294,11 +397,12 @@ void AudioGroupFilePresenter::update()
|
||||
FindClose(dir);
|
||||
}
|
||||
|
||||
void AudioGroupFilePresenter::addCollection(const std::wstring& name,
|
||||
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection)
|
||||
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;
|
||||
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));
|
||||
|
||||
@@ -368,7 +472,7 @@ void AudioGroupCollection::populateFiles(VSTEditor& editor, HTREEITEM colHandle,
|
||||
|
||||
m_iteratorVec.clear();
|
||||
m_iteratorVec.reserve(m_groups.size());
|
||||
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
|
||||
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());
|
||||
@@ -389,7 +493,7 @@ void AudioGroupFilePresenter::populateCollectionColumn(VSTEditor& editor)
|
||||
|
||||
m_iteratorVec.clear();
|
||||
m_iteratorVec.reserve(m_audioGroupCollections.size());
|
||||
for (auto it = m_audioGroupCollections.begin() ; it != m_audioGroupCollections.end() ; ++it)
|
||||
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());
|
||||
@@ -453,7 +557,8 @@ void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collecti
|
||||
for (auto& pair : sortPages)
|
||||
{
|
||||
wchar_t name[256];
|
||||
wnsprintf(name, 256, L"%d (%s)", pair.first, GMNames[pair.first] ? GMNames[pair.first] : L"???");
|
||||
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;
|
||||
@@ -467,7 +572,8 @@ void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collecti
|
||||
for (auto& pair : sortPages)
|
||||
{
|
||||
wchar_t name[256];
|
||||
wnsprintf(name, 256, L"%d (%s)", pair.first, GMPercNames[pair.first] ? GMPercNames[pair.first] : L"???");
|
||||
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;
|
||||
@@ -478,5 +584,4 @@ void AudioGroupFilePresenter::populatePageColumn(VSTEditor& editor, int collecti
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,9 +33,13 @@ struct AudioGroupDataCollection
|
||||
uint32_t absOffs;
|
||||
uint32_t active;
|
||||
MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn)
|
||||
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {}
|
||||
: fmt(fmtIn), absOffs(absOffsIn), active(activeIn)
|
||||
{
|
||||
}
|
||||
MetaData(athena::io::FileReader& r)
|
||||
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little()) {}
|
||||
: fmt(amuse::DataFormat(r.readUint32Little())), absOffs(r.readUint32Little()), active(r.readUint32Little())
|
||||
{
|
||||
}
|
||||
};
|
||||
std::experimental::optional<MetaData> m_metaData;
|
||||
|
||||
@@ -57,8 +61,11 @@ struct AudioGroupDataCollection
|
||||
bool loadSamp();
|
||||
bool loadMeta();
|
||||
|
||||
AudioGroupDataCollection(const std::wstring& path, const std::wstring& name);
|
||||
bool isDataComplete() const {return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;}
|
||||
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();
|
||||
|
||||
@@ -75,7 +82,7 @@ struct AudioGroupCollection
|
||||
std::map<std::wstring, std::unique_ptr<AudioGroupDataCollection>> m_groups;
|
||||
std::vector<GroupIterator> m_iteratorVec;
|
||||
|
||||
AudioGroupCollection(const std::wstring& path, const std::wstring& name);
|
||||
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);
|
||||
@@ -84,24 +91,26 @@ struct AudioGroupCollection
|
||||
class AudioGroupFilePresenter
|
||||
{
|
||||
friend class VSTBackend;
|
||||
|
||||
public:
|
||||
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;
|
||||
|
||||
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(const std::wstring& name,
|
||||
void addCollection(std::wstring_view name,
|
||||
std::vector<std::pair<std::wstring, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||
void removeCollection(unsigned idx);
|
||||
VSTBackend& getBackend() {return m_backend;}
|
||||
VSTBackend& getBackend() { return m_backend; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_AUDIOGROUPFILEPRESENTER_HPP__
|
||||
|
||||
@@ -12,7 +12,7 @@ if (WIN32 AND (EXISTS ${VST3_SDK_ROOT}))
|
||||
${VST2_DIR}/audioeffect.cpp
|
||||
${VST2_DIR}/audioeffectx.cpp
|
||||
FileOpenDialog.hpp FileOpenDialog.cpp)
|
||||
target_link_libraries(amuse-vst amuse boo soxr ${ZLIB_LIBRARIES} Winmm soxr
|
||||
target_link_libraries(amuse-vst amuse boo soxr ${ZLIB_LIBRARIES} ${LZO_LIB} Winmm soxr
|
||||
Msimg32 Shlwapi logvisor athena-core)
|
||||
set_target_properties(amuse-vst PROPERTIES LINK_FLAGS "/EXPORT:VSTPluginMain")
|
||||
endif()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "FileOpenDialog.hpp"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h> // For common windows data types and function headers
|
||||
#include <windows.h> // For common windows data types and function headers
|
||||
#define STRICT_TYPED_ITEMIDS
|
||||
#include <objbase.h> // For COM headers
|
||||
#include <shobjidl.h> // for IFileDialogEvents and IFileDialogControlEvents
|
||||
#include <objbase.h> // For COM headers
|
||||
#include <shobjidl.h> // for IFileDialogEvents and IFileDialogControlEvents
|
||||
#include <shlwapi.h>
|
||||
#include <knownfolders.h> // for KnownFolder APIs/datatypes/function headers
|
||||
#include <propvarutil.h> // for PROPVAR-related functions
|
||||
@@ -15,26 +15,25 @@
|
||||
#include <new>
|
||||
|
||||
// Controls
|
||||
#define CONTROL_GROUP 2000
|
||||
#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_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
|
||||
|
||||
// IDs for the Task Dialog Buttons
|
||||
#define IDC_BASICFILEOPEN 100
|
||||
#define IDC_ADDITEMSTOCUSTOMPLACES 101
|
||||
#define IDC_ADDCUSTOMCONTROLS 102
|
||||
#define IDC_SETDEFAULTVALUESFORPROPERTIES 103
|
||||
#define IDC_WRITEPROPERTIESUSINGHANDLERS 104
|
||||
#define IDC_BASICFILEOPEN 100
|
||||
#define IDC_ADDITEMSTOCUSTOMPLACES 101
|
||||
#define IDC_ADDCUSTOMCONTROLS 102
|
||||
#define IDC_SETDEFAULTVALUESFORPROPERTIES 103
|
||||
#define IDC_WRITEPROPERTIESUSINGHANDLERS 104
|
||||
#define IDC_WRITEPROPERTIESWITHOUTUSINGHANDLERS 105
|
||||
|
||||
HWND ghMainWnd = 0;
|
||||
HINSTANCE ghAppInst = 0;
|
||||
RECT winRect;
|
||||
|
||||
class CDialogEventHandler : public IFileDialogEvents,
|
||||
public IFileDialogControlEvents
|
||||
class CDialogEventHandler : public IFileDialogEvents, public IFileDialogControlEvents
|
||||
{
|
||||
public:
|
||||
// IUnknown methods
|
||||
@@ -43,15 +42,12 @@ public:
|
||||
static const QITAB qit[] = {
|
||||
QITABENT(CDialogEventHandler, IFileDialogEvents),
|
||||
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
|
||||
{ 0 },
|
||||
{0},
|
||||
};
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&_cRef);
|
||||
}
|
||||
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&_cRef); }
|
||||
|
||||
IFACEMETHODIMP_(ULONG) Release()
|
||||
{
|
||||
@@ -62,106 +58,107 @@ public:
|
||||
}
|
||||
|
||||
// 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; };
|
||||
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; };
|
||||
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() { };
|
||||
~CDialogEventHandler(){};
|
||||
long _cRef;
|
||||
};
|
||||
|
||||
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv);
|
||||
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv);
|
||||
|
||||
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));
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
//Stuff needed for later
|
||||
// Stuff needed for later
|
||||
const COMDLG_FILTERSPEC rgFExt[] = {{L"Audio Group Archive (*.*)", L"*.*"}};
|
||||
|
||||
//Create event handling
|
||||
IFileDialogEvents *pfde = NULL;
|
||||
// Create event handling
|
||||
IFileDialogEvents* pfde = NULL;
|
||||
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
//Hook the event handler
|
||||
// Hook the event handler
|
||||
DWORD dwCookie;
|
||||
|
||||
hr = pfd->Advise(pfde, &dwCookie);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
//Set options for the dialog
|
||||
// Set options for the dialog
|
||||
DWORD dwFlags;
|
||||
|
||||
//Get options first so we do not override
|
||||
// Get options first so we do not override
|
||||
hr = pfd->GetOptions(&dwFlags);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
//Get shell items only
|
||||
// Get shell items only
|
||||
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
//Types of files to display (not default)
|
||||
// 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");
|
||||
// Set default file type to display
|
||||
// hr = pfd->SetDefaultExtension(L"sqlite");
|
||||
|
||||
//if (SUCCEEDED(hr))
|
||||
// if (SUCCEEDED(hr))
|
||||
//{
|
||||
//Show dialog
|
||||
hr = pfd->Show(NULL);
|
||||
// Show dialog
|
||||
hr = pfd->Show(NULL);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Get the result once the user clicks on open
|
||||
IShellItem* result;
|
||||
|
||||
hr = pfd->GetResult(&result);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
//Get the result once the user clicks on open
|
||||
IShellItem *result;
|
||||
// Print out the file name
|
||||
PWSTR fName = NULL;
|
||||
|
||||
hr = pfd->GetResult(&result);
|
||||
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
//Print out the file name
|
||||
PWSTR fName = NULL;
|
||||
|
||||
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &fName);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ret.assign(fName);
|
||||
CoTaskMemFree(fName);
|
||||
}
|
||||
|
||||
result->Release();
|
||||
ret.assign(fName);
|
||||
CoTaskMemFree(fName);
|
||||
}
|
||||
|
||||
result->Release();
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -178,11 +175,10 @@ std::wstring openDB()
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv)
|
||||
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void** ppv)
|
||||
{
|
||||
*ppv = NULL;
|
||||
CDialogEventHandler *pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
|
||||
CDialogEventHandler* pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
|
||||
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
@@ -192,19 +188,17 @@ HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv)
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog *pfd)
|
||||
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog* pfd)
|
||||
{
|
||||
IFileSaveDialog *pfsd;
|
||||
IFileSaveDialog* pfsd;
|
||||
HRESULT hr = pfd->QueryInterface(&pfsd);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
UINT uIndex;
|
||||
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
|
||||
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
IPropertyDescriptionList *pdl = NULL;
|
||||
|
||||
|
||||
IPropertyDescriptionList* pdl = NULL;
|
||||
}
|
||||
pfsd->Release();
|
||||
}
|
||||
@@ -214,9 +208,9 @@ HRESULT CDialogEventHandler::OnTypeChange(IFileDialog *pfd)
|
||||
// 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)
|
||||
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize* pfdc, DWORD dwIDCtl, DWORD dwIDItem)
|
||||
{
|
||||
IFileDialog *pfd = NULL;
|
||||
IFileDialog* pfd = NULL;
|
||||
HRESULT hr = pfdc->QueryInterface(&pfd);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "VSTBackend.hpp"
|
||||
#include "audiodev/AudioVoiceEngine.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include <Shlobj.h>
|
||||
#include <logvisor/logvisor.hpp>
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
@@ -13,27 +13,17 @@ struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
|
||||
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;
|
||||
|
||||
struct MIDIIn : public boo::IMIDIIn
|
||||
{
|
||||
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver)
|
||||
: IMIDIIn(virt, std::move(receiver)) {}
|
||||
MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) : IMIDIIn(virt, std::move(receiver)) {}
|
||||
|
||||
std::string description() const
|
||||
{
|
||||
return "VST MIDI";
|
||||
}
|
||||
std::string description() const { return "VST MIDI"; }
|
||||
};
|
||||
|
||||
std::unique_ptr<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver)
|
||||
@@ -43,32 +33,17 @@ struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
|
||||
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()
|
||||
{
|
||||
@@ -112,7 +87,7 @@ struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
|
||||
|
||||
void pumpAndMixVoices()
|
||||
{
|
||||
for (size_t f=0 ; f<m_renderFrames ;)
|
||||
for (size_t f = 0; f < m_renderFrames;)
|
||||
{
|
||||
if (m_curBufFrame == m_5msFrames)
|
||||
{
|
||||
@@ -123,11 +98,11 @@ struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
|
||||
size_t remRenderFrames = std::min(m_renderFrames - f, m_5msFrames - m_curBufFrame);
|
||||
if (remRenderFrames)
|
||||
{
|
||||
for (size_t i=0 ; i<2 ; ++i)
|
||||
for (size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
float* bufOut = m_outputData[i];
|
||||
for (size_t lf=0 ; lf<remRenderFrames ; ++lf)
|
||||
bufOut[f+lf] = m_interleavedBuf[(m_curBufFrame+lf)*2+i];
|
||||
for (size_t lf = 0; lf < remRenderFrames; ++lf)
|
||||
bufOut[f + lf] = m_interleavedBuf[(m_curBufFrame + lf) * 2 + i];
|
||||
}
|
||||
m_curBufFrame += remRenderFrames;
|
||||
f += remRenderFrames;
|
||||
@@ -135,18 +110,17 @@ struct VSTVoiceEngine : boo::BaseAudioVoiceEngine
|
||||
}
|
||||
}
|
||||
|
||||
double getCurrentSampleRate() const {return m_mixInfo.m_sampleRate;}
|
||||
double getCurrentSampleRate() const { return m_mixInfo.m_sampleRate; }
|
||||
};
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
#define kBackendID CCONST ('a','m','u','s')
|
||||
#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)
|
||||
VSTBackend::VSTBackend(audioMasterCallback cb) : AudioEffectX(cb, 0, 0), m_filePresenter(*this), m_editor(*this)
|
||||
{
|
||||
isSynth();
|
||||
setUniqueID(kBackendID);
|
||||
@@ -170,15 +144,9 @@ VSTBackend::VSTBackend(audioMasterCallback cb)
|
||||
m_filePresenter.update();
|
||||
}
|
||||
|
||||
VSTBackend::~VSTBackend()
|
||||
{
|
||||
editor = nullptr;
|
||||
}
|
||||
VSTBackend::~VSTBackend() { editor = nullptr; }
|
||||
|
||||
AEffEditor* VSTBackend::getEditor()
|
||||
{
|
||||
return &m_editor;
|
||||
}
|
||||
AEffEditor* VSTBackend::getEditor() { return &m_editor; }
|
||||
|
||||
VstInt32 VSTBackend::processEvents(VstEvents* events)
|
||||
{
|
||||
@@ -197,7 +165,7 @@ VstInt32 VSTBackend::processEvents(VstEvents* events)
|
||||
|
||||
if (engine.m_midiReceiver)
|
||||
{
|
||||
for (VstInt32 i=0 ; i<events->numEvents ; ++i)
|
||||
for (VstInt32 i = 0; i < events->numEvents; ++i)
|
||||
{
|
||||
VstMidiEvent* evt = reinterpret_cast<VstMidiEvent*>(events->events[i]);
|
||||
if (evt->type == kVstMidiType)
|
||||
@@ -207,9 +175,9 @@ VstInt32 VSTBackend::processEvents(VstEvents* events)
|
||||
evt->midiData[0] &= ~0xf;
|
||||
evt->midiData[0] |= m_routeChannel & 0xf;
|
||||
}
|
||||
(*engine.m_midiReceiver)(std::vector<uint8_t>(std::cbegin(evt->midiData),
|
||||
std::cbegin(evt->midiData) + evt->byteSize),
|
||||
(m_curFrame + evt->deltaFrames) / sampleRate);
|
||||
(*engine.m_midiReceiver)(
|
||||
std::vector<uint8_t>(std::cbegin(evt->midiData), std::cbegin(evt->midiData) + evt->byteSize),
|
||||
(m_curFrame + evt->deltaFrames) / sampleRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,10 +210,7 @@ VstInt32 VSTBackend::canDo(char* text)
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
VstPlugCategory VSTBackend::getPlugCategory()
|
||||
{
|
||||
return kPlugCategSynth;
|
||||
}
|
||||
VstPlugCategory VSTBackend::getPlugCategory() { return kPlugCategSynth; }
|
||||
|
||||
bool VSTBackend::getEffectName(char* text)
|
||||
{
|
||||
@@ -284,10 +249,7 @@ bool VSTBackend::getOutputProperties(VstInt32 index, VstPinProperties* propertie
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
VstInt32 VSTBackend::getNumMidiInputChannels()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
VstInt32 VSTBackend::getNumMidiInputChannels() { return 1; }
|
||||
|
||||
void VSTBackend::setSampleRate(float sampleRate)
|
||||
{
|
||||
@@ -427,10 +389,6 @@ VstInt32 VSTBackend::setChunk(void* data, VstInt32 byteSize, bool)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AudioEffect* createEffectInstance(audioMasterCallback audioMaster)
|
||||
{
|
||||
return new amuse::VSTBackend(audioMaster);
|
||||
}
|
||||
AudioEffect* createEffectInstance(audioMasterCallback audioMaster) { return new amuse::VSTBackend(audioMaster); }
|
||||
|
||||
@@ -21,8 +21,7 @@ class VSTBackend;
|
||||
class VSTBackendVoiceAllocator : public BooBackendVoiceAllocator
|
||||
{
|
||||
public:
|
||||
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
||||
: BooBackendVoiceAllocator(booEngine) {}
|
||||
VSTBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {}
|
||||
};
|
||||
|
||||
/** Actual plugin implementation class */
|
||||
@@ -41,6 +40,7 @@ class VSTBackend : public AudioEffectX
|
||||
int m_routeChannel = -1;
|
||||
AudioGroupFilePresenter m_filePresenter;
|
||||
VSTEditor m_editor;
|
||||
|
||||
public:
|
||||
VSTBackend(audioMasterCallback cb);
|
||||
~VSTBackend();
|
||||
@@ -58,9 +58,9 @@ public:
|
||||
void setSampleRate(float sampleRate);
|
||||
void setBlockSize(VstInt32 blockSize);
|
||||
|
||||
amuse::Engine& getAmuseEngine() {return *m_engine;}
|
||||
const std::wstring& 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);
|
||||
@@ -72,7 +72,6 @@ public:
|
||||
VstInt32 getChunk(void** data, bool isPreset);
|
||||
VstInt32 setChunk(void* data, VstInt32 byteSize, bool isPreset);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_VSTBACKEND_HPP__
|
||||
|
||||
@@ -16,10 +16,7 @@ static HBRUSH gGreyBorderBrush;
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
VSTEditor::VSTEditor(VSTBackend& backend)
|
||||
: AEffEditor(&backend), m_backend(backend)
|
||||
{
|
||||
}
|
||||
VSTEditor::VSTEditor(VSTBackend& backend) : AEffEditor(&backend), m_backend(backend) {}
|
||||
|
||||
bool VSTEditor::getRect(ERect** rect)
|
||||
{
|
||||
@@ -27,10 +24,7 @@ bool VSTEditor::getRect(ERect** rect)
|
||||
return true;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd,
|
||||
UINT uMsg,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
VSTEditor& editor = *reinterpret_cast<VSTEditor*>(GetWindowLongPtrW(hwnd, 0));
|
||||
switch (uMsg)
|
||||
@@ -101,10 +95,7 @@ LRESULT CALLBACK VSTEditor::WindowProc(HWND hwnd,
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd,
|
||||
UINT uMsg,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
@@ -119,15 +110,12 @@ LRESULT CALLBACK VSTEditor::ColHeaderWindowProc(HWND hwnd,
|
||||
RECT rect;
|
||||
GetClientRect(hwnd, &rect);
|
||||
|
||||
TRIVERTEX verts[] =
|
||||
{
|
||||
{rect.left, rect.top, 0x6000, 0x6000, 0x7000, 0xff00},
|
||||
{rect.right, rect.bottom, 0x2000, 0x2000, 0x2800, 0xff00}
|
||||
};
|
||||
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));
|
||||
SetTextColor(dc, RGB(255, 255, 255));
|
||||
SetBkMode(dc, TRANSPARENT);
|
||||
SelectObject(dc, GetStockObject(ANSI_VAR_FONT));
|
||||
rect.left += 6;
|
||||
@@ -160,33 +148,22 @@ bool VSTEditor::open(void* ptr)
|
||||
{
|
||||
AEffEditor::open(ptr);
|
||||
HWND hostView = HWND(ptr);
|
||||
gGreyBorderBrush = CreateSolidBrush(RGB(100,100,100));
|
||||
gGreyBorderBrush = CreateSolidBrush(RGB(100, 100, 100));
|
||||
|
||||
WNDCLASSW notifyCls =
|
||||
{
|
||||
CS_HREDRAW | CS_VREDRAW,
|
||||
WindowProc,
|
||||
0,
|
||||
8,
|
||||
HINSTANCE(hInstance),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
L"VSTNotify"
|
||||
};
|
||||
WNDCLASSW notifyCls = {CS_HREDRAW | CS_VREDRAW,
|
||||
WindowProc,
|
||||
0,
|
||||
8,
|
||||
HINSTANCE(hInstance),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
L"VSTNotify"};
|
||||
RegisterClassW(¬ifyCls);
|
||||
|
||||
m_rootView = CreateWindowW(L"VSTNotify",
|
||||
L"",
|
||||
WS_CHILD,
|
||||
0, 0,
|
||||
m_windowRect.right,
|
||||
m_windowRect.bottom,
|
||||
hostView,
|
||||
nullptr,
|
||||
HINSTANCE(hInstance),
|
||||
nullptr);
|
||||
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);
|
||||
|
||||
@@ -203,18 +180,12 @@ bool VSTEditor::open(void* ptr)
|
||||
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));
|
||||
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);
|
||||
@@ -231,113 +202,61 @@ bool VSTEditor::open(void* ptr)
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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_SetBkColor(m_groupListView, RGB(64, 64, 64));
|
||||
ListView_SetTextBkColor(m_groupListView, CLR_NONE);
|
||||
ListView_SetTextColor(m_groupListView, RGB(255,255,255));
|
||||
ListView_SetTextColor(m_groupListView, RGB(255, 255, 255));
|
||||
ListView_InsertColumn(m_groupListView, 0, &column);
|
||||
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);
|
||||
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_SetBkColor(m_pageListView, RGB(64, 64, 64));
|
||||
ListView_SetTextBkColor(m_pageListView, CLR_NONE);
|
||||
ListView_SetTextColor(m_pageListView, RGB(255,255,255));
|
||||
ListView_SetTextColor(m_pageListView, RGB(255, 255, 255));
|
||||
ListView_InsertColumn(m_pageListView, 0, &column);
|
||||
ShowWindow(m_pageListView, SW_SHOW);
|
||||
|
||||
@@ -385,7 +304,7 @@ void VSTEditor::addAction()
|
||||
if (dotpos != std::string::npos)
|
||||
name.assign(path.cbegin(), path.cbegin() + dotpos);
|
||||
size_t slashpos = name.rfind(L'\\');
|
||||
size_t fslashpos = name.rfind(L"/");
|
||||
size_t fslashpos = name.rfind(L'/');
|
||||
if (slashpos == std::string::npos)
|
||||
slashpos = fslashpos;
|
||||
else if (fslashpos != std::string::npos)
|
||||
@@ -468,14 +387,7 @@ void VSTEditor::reselectPage()
|
||||
}
|
||||
}
|
||||
|
||||
void VSTEditor::selectNormalPage(int idx)
|
||||
{
|
||||
m_backend.setNormalProgram(idx);
|
||||
}
|
||||
|
||||
void VSTEditor::selectDrumPage(int idx)
|
||||
{
|
||||
m_backend.setDrumProgram(idx);
|
||||
}
|
||||
void VSTEditor::selectNormalPage(int idx) { m_backend.setNormalProgram(idx); }
|
||||
|
||||
void VSTEditor::selectDrumPage(int idx) { m_backend.setDrumProgram(idx); }
|
||||
}
|
||||
|
||||
@@ -36,20 +36,11 @@ class VSTEditor : public AEffEditor
|
||||
|
||||
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();
|
||||
|
||||
public:
|
||||
VSTEditor(VSTBackend& backend);
|
||||
|
||||
@@ -68,7 +59,6 @@ public:
|
||||
void selectNormalPage(int idx);
|
||||
void selectDrumPage(int idx);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_VSTEDITOR_HPP__
|
||||
|
||||
215
driver/amuseconv.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include "amuse/amuse.hpp"
|
||||
#include "athena/FileReader.hpp"
|
||||
#include "athena/DNAYaml.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
static logvisor::Module Log("amuseconv");
|
||||
|
||||
enum ConvType
|
||||
{
|
||||
ConvN64,
|
||||
ConvGCN,
|
||||
ConvPC
|
||||
};
|
||||
|
||||
static void ReportConvType(ConvType tp)
|
||||
{
|
||||
switch (tp)
|
||||
{
|
||||
case ConvN64:
|
||||
Log.report(logvisor::Info, _S("using N64 format"));
|
||||
break;
|
||||
case ConvPC:
|
||||
Log.report(logvisor::Info, _S("using PC format"));
|
||||
break;
|
||||
case ConvGCN:
|
||||
default:
|
||||
Log.report(logvisor::Info, _S("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, _S("Found '%s'"), amuse::ContainerRegistry::TypeToName(type));
|
||||
|
||||
amuse::Mkdir(targetPath.data(), 0755);
|
||||
Log.report(logvisor::Info, _S("Established directory at %s"), targetPath.data());
|
||||
|
||||
for (auto& group : groups)
|
||||
{
|
||||
Log.report(logvisor::Info, _S("Extracting %s"), group.first.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
auto songs = amuse::ContainerRegistry::LoadSongs(inPath.data());
|
||||
amuse::SystemString songsDir = amuse::SystemString(targetPath) + _S("/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 + _S('/') + pair.first + _S(".mid");
|
||||
FILE* fp = amuse::FOpen(songPath.c_str(), _S("wb"));
|
||||
if (fp)
|
||||
{
|
||||
Log.report(logvisor::Info, _S("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;
|
||||
}
|
||||
|
||||
static bool BuildSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath, int version, bool big)
|
||||
{
|
||||
FILE* fp = amuse::FOpen(inPath.data(), _S("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);
|
||||
|
||||
std::vector<uint8_t> out = amuse::SongConverter::MIDIToSong(data, version, big);
|
||||
if (out.empty())
|
||||
return false;
|
||||
|
||||
fp = amuse::FOpen(targetPath.data(), _S("wb"));
|
||||
fwrite(out.data(), 1, out.size(), fp);
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExtractSNG(amuse::SystemStringView inPath, amuse::SystemStringView targetPath)
|
||||
{
|
||||
FILE* fp = amuse::FOpen(inPath.data(), _S("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);
|
||||
|
||||
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(), _S("wb"));
|
||||
fwrite(out.data(), 1, out.size(), fp);
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
int wmain(int argc, const amuse::SystemChar** argv)
|
||||
#else
|
||||
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], _S("n64")))
|
||||
type = ConvN64;
|
||||
else if (!amuse::CompareCaseInsensitive(argv[3], _S("gcn")))
|
||||
type = ConvGCN;
|
||||
else if (!amuse::CompareCaseInsensitive(argv[3], _S("pc")))
|
||||
type = ConvPC;
|
||||
else
|
||||
{
|
||||
Log.report(logvisor::Error, _S("unrecognized format: %s"), argv[3]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool good = false;
|
||||
FILE* fin = amuse::FOpen(argv[1], _S("rb"));
|
||||
if (fin)
|
||||
{
|
||||
fclose(fin);
|
||||
amuse::SystemString barePath(argv[1]);
|
||||
size_t dotPos = barePath.rfind(_S('.'));
|
||||
const amuse::SystemChar* dot = barePath.c_str() + dotPos;
|
||||
if (dotPos != amuse::SystemString::npos)
|
||||
{
|
||||
if (!amuse::CompareCaseInsensitive(dot, _S(".mid")) || !amuse::CompareCaseInsensitive(dot, _S(".midi")))
|
||||
{
|
||||
ReportConvType(type);
|
||||
good = BuildSNG(barePath, argv[2], 1, true);
|
||||
}
|
||||
else if (!amuse::CompareCaseInsensitive(dot, _S(".son")) || !amuse::CompareCaseInsensitive(dot, _S(".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 += _S("/project.yaml");
|
||||
fin = amuse::FOpen(projectPath.c_str(), _S("rb"));
|
||||
if (fin)
|
||||
{
|
||||
fclose(fin);
|
||||
ReportConvType(type);
|
||||
good = BuildAudioGroup(argv[1], argv[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!good)
|
||||
{
|
||||
Log.report(logvisor::Error, _S("unable to convert %s to %s"), argv[1], argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,24 +1,18 @@
|
||||
#include "amuse/amuse.hpp"
|
||||
#include "amuse/BooBackend.hpp"
|
||||
#include "athena/FileReader.hpp"
|
||||
#include "boo/boo.hpp"
|
||||
#include "boo/audiodev/IAudioVoiceEngine.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include "optional.hpp"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define EMITTER_TEST 0
|
||||
|
||||
static logvisor::Module Log("amuseplay");
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((__format__ (__printf__, 3, 4)))
|
||||
__attribute__((__format__(__printf__, 3, 4)))
|
||||
#endif
|
||||
static inline void SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...)
|
||||
static inline void
|
||||
SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::SystemChar* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
@@ -30,70 +24,19 @@ static inline void SNPrintf(boo::SystemChar* str, size_t maxlen, const boo::Syst
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
#include <DbgHelp.h>
|
||||
#pragma comment(lib, "Dbghelp.lib")
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
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 );
|
||||
|
||||
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 );
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
#endif
|
||||
|
||||
struct AppCallback;
|
||||
|
||||
struct EventCallback : boo::IWindowCallback
|
||||
{
|
||||
AppCallback& m_app;
|
||||
bool m_tracking = false;
|
||||
|
||||
public:
|
||||
void charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat);
|
||||
void charKeyUp(unsigned long charCode, boo::EModifierKey mods);
|
||||
void specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat);
|
||||
void specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods);
|
||||
void resized(const boo::SWindowRect&, const boo::SWindowRect&) {}
|
||||
void resized(const boo::SWindowRect&, bool) {}
|
||||
|
||||
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||
@@ -110,10 +53,11 @@ struct AppCallback : boo::IApplicationCallback
|
||||
/* Boo window and events */
|
||||
EventCallback m_eventRec;
|
||||
boo::DeferredWindowEvents<EventCallback> m_events;
|
||||
boo::IWindow* m_win = nullptr;
|
||||
std::shared_ptr<boo::IWindow> m_win;
|
||||
|
||||
/* Amuse engine */
|
||||
std::experimental::optional<amuse::Engine> m_engine;
|
||||
std::unique_ptr<amuse::BooBackendVoiceAllocator> m_booBackend;
|
||||
std::unique_ptr<amuse::Engine> m_engine;
|
||||
int m_groupId = -1;
|
||||
bool m_sfxGroup;
|
||||
|
||||
@@ -132,7 +76,7 @@ struct AppCallback : boo::IApplicationCallback
|
||||
int8_t m_lastChanProg = -1;
|
||||
|
||||
/* Control state */
|
||||
float m_volume = 0.5f;
|
||||
float m_volume = 0.8f;
|
||||
float m_modulation = 0.f;
|
||||
float m_pitchBend = 0.f;
|
||||
bool m_updateDisp = false;
|
||||
@@ -142,6 +86,14 @@ struct AppCallback : boo::IApplicationCallback
|
||||
bool m_breakout = false;
|
||||
int m_panicCount = 0;
|
||||
|
||||
#if EMITTER_TEST
|
||||
amuse::Vector3f m_pos = {};
|
||||
amuse::Vector3f m_dir = {0.f, 0.f, 0.f};
|
||||
amuse::Vector3f m_listenerDir = {0.f, 40.f, 0.f};
|
||||
std::shared_ptr<amuse::Emitter> m_emitter;
|
||||
std::shared_ptr<amuse::Listener> m_listener;
|
||||
#endif
|
||||
|
||||
void UpdateSongDisplay()
|
||||
{
|
||||
size_t voxCount = 0;
|
||||
@@ -151,9 +103,10 @@ struct AppCallback : boo::IApplicationCallback
|
||||
voxCount = m_seq->getVoiceCount();
|
||||
program = m_seq->getChanProgram(m_chanId);
|
||||
}
|
||||
printf("\r "
|
||||
"\r %" PRISize " Setup %d, Chan %d, Prog %d, Octave: %d, Vel: %d, VOL: %d%%\r", voxCount,
|
||||
m_setupId, m_chanId, program, m_octave, m_velocity, int(std::rint(m_volume * 100)));
|
||||
printf(
|
||||
"\r "
|
||||
"\r %" PRISize " Setup %d, Chan %d, Prog %d, Octave: %d, Vel: %d, VOL: %d%%\r",
|
||||
voxCount, m_setupId, m_chanId, program, m_octave, m_velocity, int(std::rint(m_volume * 100)));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -162,11 +115,11 @@ struct AppCallback : boo::IApplicationCallback
|
||||
m_setupId = setupId;
|
||||
if (m_seq)
|
||||
{
|
||||
m_seq->stopSong(true);
|
||||
m_seq->stopSong(0.f, true);
|
||||
m_seq->kill();
|
||||
}
|
||||
m_seq = m_engine->seqPlay(m_groupId, setupId, nullptr);
|
||||
m_seq->setVolume(m_volume);
|
||||
m_engine->setVolume(m_volume);
|
||||
|
||||
if (m_arrData)
|
||||
m_seq->playSong(m_arrData->m_data.get(), false);
|
||||
@@ -174,21 +127,22 @@ struct AppCallback : boo::IApplicationCallback
|
||||
UpdateSongDisplay();
|
||||
}
|
||||
|
||||
void SongLoop(const amuse::SongGroupIndex& index)
|
||||
void SongLoop(const amuse::SongGroupIndex& index, boo::IAudioVoiceEngine& booEngine)
|
||||
{
|
||||
printf("░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||
"░░░ ████ ████ ┃ ████ ████ ████ ┃ ████ ████ ░░░\n"
|
||||
"░░░ ████ ████ ┃ ████ ████ ████ ┃ ████ ████ ░░░\n"
|
||||
"░░░ ▌W▐█ ▌E▐█ ┃ ▌T▐█ ▌Y▐█ ▌U▐█ ┃ ▌O▐█ ▌P▐█ ░░░\n"
|
||||
"░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░\n"
|
||||
"░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░\n"
|
||||
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||
"<left/right>: cycle MIDI setup, <up/down>: volume, <space>: PANIC\n"
|
||||
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
||||
"<Z/X>: octave, <C/V>: velocity, <B/N>: channel, <,/.>: program, <Q>: quit\n");
|
||||
printf(
|
||||
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||
"░░░ ████ ████ ┃ ████ ████ ████ ┃ ████ ████ ░░░\n"
|
||||
"░░░ ████ ████ ┃ ████ ████ ████ ┃ ████ ████ ░░░\n"
|
||||
"░░░ ▌W▐█ ▌E▐█ ┃ ▌T▐█ ▌Y▐█ ▌U▐█ ┃ ▌O▐█ ▌P▐█ ░░░\n"
|
||||
"░░░ │ │ ┃ │ │ │ ┃ │ │ ░░░\n"
|
||||
"░░░ A │ S │ D ┃ F │ G │ H │ J ┃ K │ L │ ; ░░░\n"
|
||||
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
|
||||
"<left/right>: cycle MIDI setup, <up/down>: volume, <space>: PANIC\n"
|
||||
"<tab>: sustain pedal, <window-Y>: pitch wheel, <window-X>: mod wheel\n"
|
||||
"<Z/X>: octave, <C/V>: velocity, <B/N>: channel, <,/.>: program, <Q>: quit\n");
|
||||
|
||||
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries
|
||||
(index.m_midiSetups.cbegin(), index.m_midiSetups.cend());
|
||||
std::map<int, const std::array<amuse::SongGroupIndex::MIDISetup, 16>*> sortEntries(index.m_midiSetups.cbegin(),
|
||||
index.m_midiSetups.cend());
|
||||
auto setupIt = sortEntries.cbegin();
|
||||
if (setupIt != sortEntries.cend())
|
||||
{
|
||||
@@ -250,7 +204,7 @@ struct AppCallback : boo::IApplicationCallback
|
||||
UpdateSongDisplay();
|
||||
}
|
||||
|
||||
m_engine->pumpEngine();
|
||||
m_win->waitForRetrace(&booEngine);
|
||||
|
||||
size_t voxCount;
|
||||
int8_t progId;
|
||||
@@ -279,17 +233,23 @@ struct AppCallback : boo::IApplicationCallback
|
||||
m_seq.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
m_win->waitForRetrace();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateSFXDisplay()
|
||||
{
|
||||
bool playing = m_vox && m_vox->state() == amuse::VoiceState::Playing;
|
||||
printf("\r "
|
||||
"\r %c SFX %d, VOL: %d%%\r", playing ? '>' : ' ',
|
||||
m_sfxId, int(std::rint(m_volume * 100)));
|
||||
#if EMITTER_TEST
|
||||
printf(
|
||||
"\r "
|
||||
"\r %c SFX %d, VOL: %d%% POS: (%f,%f)\r",
|
||||
playing ? '>' : ' ', m_sfxId, int(std::rint(m_volume * 100)), m_pos[0], m_pos[1]);
|
||||
#else
|
||||
printf(
|
||||
"\r "
|
||||
"\r %c SFX %d, VOL: %d%%\r",
|
||||
playing ? '>' : ' ', m_sfxId, int(std::rint(m_volume * 100)));
|
||||
#endif
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
@@ -300,25 +260,52 @@ struct AppCallback : boo::IApplicationCallback
|
||||
bool playing = m_vox && m_vox->state() == amuse::VoiceState::Playing;
|
||||
if (playing)
|
||||
{
|
||||
#if EMITTER_TEST
|
||||
if (m_emitter)
|
||||
m_emitter->getVoice()->keyOff();
|
||||
m_emitter = m_engine->addEmitter(m_pos, m_dir, 100.f, 0.f, m_sfxId, 0.f, 1.f, true);
|
||||
m_vox = m_emitter->getVoice();
|
||||
#else
|
||||
m_vox->keyOff();
|
||||
m_vox = m_engine->fxStart(m_sfxId, m_volume, 0.f);
|
||||
#endif
|
||||
}
|
||||
|
||||
UpdateSFXDisplay();
|
||||
}
|
||||
|
||||
void SFXLoop(const amuse::SFXGroupIndex& index)
|
||||
void SFXLoop(const amuse::SFXGroupIndex& index, boo::IAudioVoiceEngine& booEngine)
|
||||
{
|
||||
printf("<space>: keyon/keyoff, <left/right>: cycle SFX, <up/down>: volume, <Q>: quit\n");
|
||||
|
||||
std::map<uint16_t, const amuse::SFXGroupIndex::SFXEntry*> sortEntries
|
||||
(index.m_sfxEntries.cbegin(), index.m_sfxEntries.cend());
|
||||
m_seq = m_engine->seqPlay(m_groupId, 0, nullptr);
|
||||
|
||||
std::map<uint16_t, const amuse::SFXGroupIndex::SFXEntry*> sortEntries(index.m_sfxEntries.cbegin(),
|
||||
index.m_sfxEntries.cend());
|
||||
auto sfxIt = sortEntries.cbegin();
|
||||
if (sfxIt != sortEntries.cend())
|
||||
SelectSFX(sfxIt->first);
|
||||
|
||||
#if EMITTER_TEST
|
||||
float emitterTheta = 0.f;
|
||||
float zeroVec[3] = {};
|
||||
float heading[3] = {0.f, 1.f, 0.f};
|
||||
float up[3] = {0.f, 0.f, 1.f};
|
||||
m_listener = m_engine->addListener(zeroVec, m_listenerDir, heading, up, 5.f, 5.f, 1000.f, 1.f);
|
||||
#endif
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
#if EMITTER_TEST
|
||||
//float dist = std::sin(emitterTheta * 0.25f);
|
||||
m_pos[0] = std::cos(emitterTheta) * 5.f;
|
||||
m_pos[1] = std::sin(emitterTheta) * 5.f;
|
||||
if (m_emitter)
|
||||
m_emitter->setVectors(m_pos, m_dir);
|
||||
emitterTheta += 1.f / 60.f;
|
||||
m_updateDisp = true;
|
||||
#endif
|
||||
|
||||
m_events.dispatchEvents();
|
||||
|
||||
if (m_wantsNext)
|
||||
@@ -351,11 +338,14 @@ struct AppCallback : boo::IApplicationCallback
|
||||
UpdateSFXDisplay();
|
||||
}
|
||||
|
||||
m_engine->pumpEngine();
|
||||
m_win->waitForRetrace(&booEngine);
|
||||
|
||||
if (m_vox && m_vox->state() == amuse::VoiceState::Dead)
|
||||
{
|
||||
m_vox.reset();
|
||||
#if EMITTER_TEST
|
||||
m_emitter.reset();
|
||||
#endif
|
||||
UpdateSFXDisplay();
|
||||
}
|
||||
|
||||
@@ -363,11 +353,19 @@ struct AppCallback : boo::IApplicationCallback
|
||||
{
|
||||
m_breakout = false;
|
||||
m_vox.reset();
|
||||
#if EMITTER_TEST
|
||||
m_emitter.reset();
|
||||
#endif
|
||||
m_seq->allOff(true);
|
||||
m_seq.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
m_win->waitForRetrace();
|
||||
}
|
||||
|
||||
#if EMITTER_TEST
|
||||
m_engine->removeListener(m_listener.get());
|
||||
m_listener.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void charKeyDownRepeat(unsigned long charCode)
|
||||
@@ -410,7 +408,8 @@ struct AppCallback : boo::IApplicationCallback
|
||||
m_seq->nextChanProgram(m_chanId);
|
||||
m_updateDisp = true;
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,12 +428,22 @@ struct AppCallback : boo::IApplicationCallback
|
||||
switch (charCode)
|
||||
{
|
||||
case ' ':
|
||||
if (m_seq)
|
||||
m_seq->allOff(true);
|
||||
if (m_vox && m_vox->state() == amuse::VoiceState::Playing)
|
||||
m_vox->keyOff();
|
||||
else if (m_sfxId != -1)
|
||||
{
|
||||
#if EMITTER_TEST
|
||||
m_emitter = m_engine->addEmitter(m_pos, m_dir, 100.f, 0.f, m_sfxId, 0.f, 1.f, true);
|
||||
m_vox = m_emitter->getVoice();
|
||||
#else
|
||||
m_vox = m_engine->fxStart(m_sfxId, m_volume, 0.f);
|
||||
#endif
|
||||
}
|
||||
m_updateDisp = true;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (m_seq && m_chanId != -1)
|
||||
@@ -534,7 +543,8 @@ struct AppCallback : boo::IApplicationCallback
|
||||
case ':':
|
||||
m_seq->keyOn(m_chanId, (m_octave + 1) * 12 + 16, m_velocity);
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!setPanic)
|
||||
@@ -605,7 +615,8 @@ struct AppCallback : boo::IApplicationCallback
|
||||
case ':':
|
||||
m_seq->keyOff(m_chanId, (m_octave + 1) * 12 + 16, m_velocity);
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -613,19 +624,16 @@ struct AppCallback : boo::IApplicationCallback
|
||||
int appMain(boo::IApplication* app)
|
||||
{
|
||||
/* Event window */
|
||||
m_win = app->newWindow(_S("amuseplay"), 1);
|
||||
m_win = app->newWindow(_S("amuseplay"));
|
||||
m_win->setCallback(&m_events);
|
||||
m_win->setWindowFrame(100, 100, 100, 100);
|
||||
m_win->setStyle(~boo::EWindowStyle::Resize);
|
||||
m_win->showWindow();
|
||||
boo::ITextureR* tex = nullptr;
|
||||
boo::GraphicsDataToken gfxToken =
|
||||
m_win->getMainContextDataFactory()->commitTransaction(
|
||||
[&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
tex = ctx.newRenderTexture(100, 100, false, false);
|
||||
boo::ObjToken<boo::ITextureR> tex;
|
||||
m_win->getMainContextDataFactory()->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) {
|
||||
tex = ctx.newRenderTexture(100, 100, boo::TextureClampMode::Repeat, 1, 0);
|
||||
return true;
|
||||
});
|
||||
} BooTrace);
|
||||
boo::IGraphicsCommandQueue* q = m_win->getCommandQueue();
|
||||
q->setRenderTarget(tex);
|
||||
q->clearTarget();
|
||||
@@ -656,8 +664,12 @@ struct AppCallback : boo::IApplicationCallback
|
||||
}
|
||||
|
||||
std::list<amuse::AudioGroupProject> m_projs;
|
||||
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, const amuse::SongGroupIndex*>> allSongGroups;
|
||||
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, const amuse::SFXGroupIndex*>> allSFXGroups;
|
||||
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
|
||||
const amuse::SongGroupIndex*>>
|
||||
allSongGroups;
|
||||
std::map<int, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
|
||||
const amuse::SFXGroupIndex*>>
|
||||
allSFXGroups;
|
||||
size_t totalGroups = 0;
|
||||
|
||||
for (auto& grp : data)
|
||||
@@ -667,10 +679,10 @@ struct AppCallback : boo::IApplicationCallback
|
||||
amuse::AudioGroupProject& proj = m_projs.back();
|
||||
totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
|
||||
|
||||
for (auto it = proj.songGroups().begin() ; it != proj.songGroups().end() ; ++it)
|
||||
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)
|
||||
for (auto it = proj.sfxGroups().begin(); it != proj.sfxGroups().end(); ++it)
|
||||
allSFXGroups[it->first] = std::make_pair(&grp, &it->second);
|
||||
}
|
||||
|
||||
@@ -724,8 +736,27 @@ struct AppCallback : boo::IApplicationCallback
|
||||
int idx = 0;
|
||||
for (const auto& pair : songs)
|
||||
{
|
||||
amuse::Printf(_S(" %d %s (Group %d, Setup %d)\n"), idx++,
|
||||
pair.first.c_str(), pair.second.m_groupId, pair.second.m_setupId);
|
||||
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(_S(" %d %s (Group %d, Setup %d)\n"), idx++, pair.first.c_str(), grpId,
|
||||
setupId);
|
||||
}
|
||||
|
||||
int userSel = 0;
|
||||
@@ -757,7 +788,25 @@ struct AppCallback : boo::IApplicationCallback
|
||||
}
|
||||
}
|
||||
|
||||
/* Get group selection from user */
|
||||
/* 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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_groupId != -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get group selection via user */
|
||||
if (m_groupId != -1)
|
||||
{
|
||||
if (allSongGroups.find(m_groupId) != allSongGroups.end())
|
||||
@@ -776,15 +825,15 @@ struct AppCallback : boo::IApplicationCallback
|
||||
printf("Multiple Audio Groups discovered:\n");
|
||||
for (const auto& pair : allSFXGroups)
|
||||
{
|
||||
amuse::Printf(_S(" %d %s (SFXGroup) %" PRISize " sfx-entries\n"),
|
||||
pair.first, pair.second.first->first.c_str(),
|
||||
pair.second.second->m_sfxEntries.size());
|
||||
amuse::Printf(_S(" %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(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages\n"),
|
||||
pair.first, pair.second.first->first.c_str(),
|
||||
pair.second.second->m_normPages.size(), pair.second.second->m_drumPages.size());
|
||||
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize
|
||||
" drum-pages, %" PRISize " MIDI-setups\n"),
|
||||
pair.first, pair.second.first->first.c_str(), pair.second.second->m_normPages.size(),
|
||||
pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size());
|
||||
}
|
||||
|
||||
int userSel = 0;
|
||||
@@ -861,8 +910,8 @@ struct AppCallback : boo::IApplicationCallback
|
||||
|
||||
/* Build voice engine */
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine();
|
||||
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
||||
m_engine.emplace(booBackend, amuse::AmplitudeMode::PerSample);
|
||||
m_booBackend.reset(new amuse::BooBackendVoiceAllocator(*voxEngine));
|
||||
m_engine.reset(new amuse::Engine(*m_booBackend, amuse::AmplitudeMode::PerSample));
|
||||
|
||||
/* Load group into engine */
|
||||
const amuse::AudioGroup* group = m_engine->addAudioGroup(*selData);
|
||||
@@ -874,23 +923,26 @@ struct AppCallback : boo::IApplicationCallback
|
||||
|
||||
/* Enter playback loop */
|
||||
if (m_sfxGroup)
|
||||
SFXLoop(*sfxIndex);
|
||||
SFXLoop(*sfxIndex, *voxEngine);
|
||||
else
|
||||
SongLoop(*songIndex);
|
||||
SongLoop(*songIndex, *voxEngine);
|
||||
|
||||
m_vox.reset();
|
||||
m_seq.reset();
|
||||
m_engine.reset();
|
||||
m_booBackend.reset();
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void appQuitting(boo::IApplication*)
|
||||
{
|
||||
m_running = false;
|
||||
}
|
||||
void appQuitting(boo::IApplication*) { m_running = false; }
|
||||
|
||||
AppCallback(int argc, const boo::SystemChar** argv)
|
||||
: m_argc(argc), m_argv(argv), m_eventRec(*this), m_events(m_eventRec) {}
|
||||
: m_argc(argc), m_argv(argv), m_eventRec(*this), m_events(m_eventRec)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void EventCallback::charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat)
|
||||
@@ -901,10 +953,7 @@ void EventCallback::charKeyDown(unsigned long charCode, boo::EModifierKey mods,
|
||||
m_app.charKeyDown(charCode);
|
||||
}
|
||||
|
||||
void EventCallback::charKeyUp(unsigned long charCode, boo::EModifierKey mods)
|
||||
{
|
||||
m_app.charKeyUp(charCode);
|
||||
}
|
||||
void EventCallback::charKeyUp(unsigned long charCode, boo::EModifierKey mods) { m_app.charKeyUp(charCode); }
|
||||
|
||||
void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat)
|
||||
{
|
||||
@@ -919,30 +968,23 @@ void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods,
|
||||
case boo::ESpecialKey::Up:
|
||||
if (m_app.m_volume < 1.f)
|
||||
m_app.m_volume = amuse::clamp(0.f, m_app.m_volume + 0.05f, 1.f);
|
||||
if (m_app.m_vox)
|
||||
m_app.m_vox->setVolume(m_app.m_volume);
|
||||
if (m_app.m_seq)
|
||||
m_app.m_seq->setVolume(m_app.m_volume);
|
||||
m_app.m_engine->setVolume(m_app.m_volume);
|
||||
m_app.m_updateDisp = true;
|
||||
break;
|
||||
case boo::ESpecialKey::Down:
|
||||
if (m_app.m_volume > 0.f)
|
||||
m_app.m_volume = amuse::clamp(0.f, m_app.m_volume - 0.05f, 1.f);
|
||||
if (m_app.m_vox)
|
||||
m_app.m_vox->setVolume(m_app.m_volume);
|
||||
if (m_app.m_seq)
|
||||
m_app.m_seq->setVolume(m_app.m_volume);
|
||||
m_app.m_engine->setVolume(m_app.m_volume);
|
||||
m_app.m_updateDisp = true;
|
||||
break;
|
||||
case boo::ESpecialKey::Esc:
|
||||
m_app.m_breakout = true;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventCallback::specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods)
|
||||
{
|
||||
}
|
||||
void EventCallback::specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods) {}
|
||||
|
||||
void EventCallback::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton, boo::EModifierKey)
|
||||
{
|
||||
@@ -989,32 +1031,30 @@ int main(int argc, const boo::SystemChar** argv)
|
||||
#endif
|
||||
{
|
||||
logvisor::RegisterConsoleLogger();
|
||||
logvisor::RegisterStandardExceptions();
|
||||
AppCallback app(argc, argv);
|
||||
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto,
|
||||
app, _S("amuseplay"), _S("Amuse Player"), argc, argv, false);
|
||||
int ret = boo::ApplicationRun(boo::IApplication::EPlatformType::Auto, app,
|
||||
_S("amuseplay"), _S("Amuse Player"),
|
||||
argc, argv, {}, 1, 1, false);
|
||||
printf("IM DYING!!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#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 argc = 0;
|
||||
const boo::SystemChar** 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];
|
||||
for (int i = 0; i < argc; ++i)
|
||||
booArgv[i + 1] = argv[i];
|
||||
|
||||
logvisor::CreateWin32Console();
|
||||
SetConsoleOutputCP(65001);
|
||||
return wmain(argc+1, booArgv);
|
||||
return wmain(argc + 1, booArgv);
|
||||
}
|
||||
#endif
|
||||
505
driver/amuserender.cpp
Normal file
@@ -0,0 +1,505 @@
|
||||
#include "amuse/amuse.hpp"
|
||||
#include "amuse/BooBackend.hpp"
|
||||
#include "athena/FileReader.hpp"
|
||||
#include "boo/boo.hpp"
|
||||
#include "boo/audiodev/IAudioVoiceEngine.hpp"
|
||||
#include "logvisor/logvisor.hpp"
|
||||
#include "optional.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <signal.h>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <cstdarg>
|
||||
|
||||
static logvisor::Module Log("amuserender");
|
||||
|
||||
#if __GNUC__
|
||||
__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);
|
||||
#if _WIN32
|
||||
_vsnwprintf(str, maxlen, format, va);
|
||||
#else
|
||||
vsnprintf(str, maxlen, format, va);
|
||||
#endif
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
#include <DbgHelp.h>
|
||||
#pragma comment(lib, "Dbghelp.lib")
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* SIGINT will gracefully break write loop */
|
||||
static bool g_BreakLoop = false;
|
||||
static void SIGINTHandler(int sig) { g_BreakLoop = true; }
|
||||
|
||||
#if _WIN32
|
||||
int wmain(int argc, const boo::SystemChar** argv)
|
||||
#else
|
||||
int main(int argc, const boo::SystemChar** argv)
|
||||
#endif
|
||||
{
|
||||
logvisor::RegisterConsoleLogger();
|
||||
|
||||
std::vector<boo::SystemString> m_args;
|
||||
m_args.reserve(argc);
|
||||
double rate = NativeSampleRate;
|
||||
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
|
||||
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
|
||||
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>]");
|
||||
exit(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");
|
||||
exit(1);
|
||||
}
|
||||
Log.report(logvisor::Info, _S("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");
|
||||
exit(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>*, const amuse::SongGroupIndex*>>
|
||||
allSongGroups;
|
||||
std::map<int,
|
||||
std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, const 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;
|
||||
}
|
||||
}
|
||||
|
||||
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(_S(" %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");
|
||||
exit(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);
|
||||
exit(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);
|
||||
exit(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(_S(" %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(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize
|
||||
" MIDI-setups\n"),
|
||||
pair.first, pair.second.first->first.c_str(), pair.second.second->m_normPages.size(),
|
||||
pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size());
|
||||
}
|
||||
|
||||
int userSel = 0;
|
||||
printf("Enter Group Number: ");
|
||||
if (scanf("%d", &userSel) <= 0)
|
||||
{
|
||||
Log.report(logvisor::Error, "unable to parse prompt");
|
||||
exit(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);
|
||||
exit(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");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Make final group selection */
|
||||
amuse::IntrusiveAudioGroupData* selData = nullptr;
|
||||
const amuse::SongGroupIndex* songIndex = nullptr;
|
||||
const amuse::SFXGroupIndex* sfxIndex = nullptr;
|
||||
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");
|
||||
exit(1);
|
||||
}
|
||||
m_setupId = userSel;
|
||||
}
|
||||
if (sortSetups.find(m_setupId) == sortSetups.cend())
|
||||
{
|
||||
Log.report(logvisor::Error, "unable to find setup %d", m_setupId);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto sfxSearch = allSFXGroups.find(m_groupId);
|
||||
if (sfxSearch != allSFXGroups.end())
|
||||
{
|
||||
selData = &sfxSearch->second.first->second;
|
||||
sfxIndex = sfxSearch->second.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selData)
|
||||
{
|
||||
Log.report(logvisor::Error, "unable to select audio group data");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (m_sfxGroup)
|
||||
{
|
||||
Log.report(logvisor::Error, "amuserender is currently only able to render SongGroups");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* WAV out path */
|
||||
amuse::SystemChar pathOut[1024];
|
||||
SNPrintf(pathOut, 1024, _S("%s-%s.wav"), m_groupName->c_str(), m_songName->c_str());
|
||||
Log.report(logvisor::Info, _S("Writing to %s"), pathOut);
|
||||
|
||||
/* Build voice engine */
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewWAVAudioVoiceEngine(pathOut, rate);
|
||||
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
||||
amuse::Engine engine(booBackend, amuse::AmplitudeMode::PerSample);
|
||||
|
||||
/* Load group into engine */
|
||||
const amuse::AudioGroup* group = engine.addAudioGroup(*selData);
|
||||
if (!group)
|
||||
{
|
||||
Log.report(logvisor::Error, "unable to add audio group");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Enter playback loop */
|
||||
std::shared_ptr<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get());
|
||||
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 argc = 0;
|
||||
const boo::SystemChar** 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);
|
||||
}
|
||||
#endif
|
||||
@@ -9,8 +9,7 @@ namespace amuse
|
||||
{
|
||||
class AudioGroupData;
|
||||
|
||||
using Sample = std::pair<AudioGroupSampleDirectory::Entry,
|
||||
AudioGroupSampleDirectory::ADPCMParms>;
|
||||
using Sample = std::pair<AudioGroupSampleDirectory::Entry, AudioGroupSampleDirectory::ADPCMParms>;
|
||||
|
||||
/** Runtime audio group index container */
|
||||
class AudioGroup
|
||||
@@ -21,20 +20,20 @@ class AudioGroup
|
||||
const unsigned char* m_samp;
|
||||
DataFormat m_fmt;
|
||||
bool m_valid;
|
||||
|
||||
public:
|
||||
operator bool() const {return m_valid;}
|
||||
operator bool() const { return m_valid; }
|
||||
AudioGroup(const AudioGroupData& data, GCNDataTag);
|
||||
AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag);
|
||||
AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag);
|
||||
|
||||
const Sample* getSample(int sfxId) const;
|
||||
const unsigned char* getSampleData(uint32_t offset) const;
|
||||
const AudioGroupProject& getProj() const {return m_proj;}
|
||||
const AudioGroupPool& getPool() const {return m_pool;}
|
||||
const AudioGroupSampleDirectory& getSdir() const {return m_sdir;}
|
||||
DataFormat getDataFormat() const {return m_fmt;}
|
||||
const AudioGroupProject& getProj() const { return m_proj; }
|
||||
const AudioGroupPool& getPool() const { return m_pool; }
|
||||
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
|
||||
DataFormat getDataFormat() const { return m_fmt; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_AUDIOGROUP_HPP__
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace amuse
|
||||
class AudioGroupData
|
||||
{
|
||||
friend class Engine;
|
||||
|
||||
protected:
|
||||
unsigned char* m_proj;
|
||||
size_t m_projSz;
|
||||
@@ -19,90 +20,107 @@ protected:
|
||||
size_t m_sdirSz;
|
||||
unsigned char* m_samp;
|
||||
size_t m_sampSz;
|
||||
|
||||
|
||||
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) {}
|
||||
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) {}
|
||||
|
||||
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;}
|
||||
|
||||
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
|
||||
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)
|
||||
{
|
||||
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;}
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
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; }
|
||||
|
||||
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;
|
||||
|
||||
public:
|
||||
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);
|
||||
IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other);
|
||||
|
||||
void dangleOwnership() {m_owns = false;}
|
||||
IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) noexcept;
|
||||
IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other) noexcept;
|
||||
|
||||
void dangleOwnership() { m_owns = false; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_AUDIOGROUPDATA_HPP__
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef __AMUSE_AUDIOGROUPPOOL_HPP__
|
||||
#define __AMUSE_AUDIOGROUPPOOL_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
@@ -22,35 +22,31 @@ static inline double TimeCentsToSeconds(int32_t tc)
|
||||
/** Defines phase-based volume curve for macro volume control */
|
||||
struct ADSR
|
||||
{
|
||||
uint8_t attackFine; /* 0-255ms */
|
||||
uint8_t attackCoarse; /* 0-65280ms */
|
||||
uint8_t decayFine; /* 0-255ms */
|
||||
uint8_t decayCoarse; /* 0-65280ms */
|
||||
uint8_t sustainFine; /* multiply by 0.0244 for percentage */
|
||||
uint8_t sustainCoarse; /* multiply by 6.25 for percentage */
|
||||
uint8_t releaseFine; /* 0-255ms */
|
||||
uint8_t releaseCoarse; /* 0-65280ms */
|
||||
uint16_t attack;
|
||||
uint16_t decay;
|
||||
uint16_t sustain; /* 0x1000 == 100% */
|
||||
uint16_t release; /* milliseconds */
|
||||
|
||||
double getAttack() const {return (attackCoarse * 255 + attackFine) / 1000.0;}
|
||||
double getDecay() const {return decayCoarse == 128 ? 0.0 : ((decayCoarse * 255 + decayFine) / 1000.0);}
|
||||
double getSustain() const {return decayCoarse == 128 ? 1.0 : ((sustainCoarse * 6.25 + sustainFine * 0.0244) / 100.0);}
|
||||
double getRelease() const {return (releaseCoarse * 255 + releaseFine) / 1000.0;}
|
||||
double getAttack() const { return attack / 1000.0; }
|
||||
double getDecay() const { return (decay == 0x8000) ? 0.0 : (decay / 1000.0); }
|
||||
double getSustain() const { return sustain / double(0x1000); }
|
||||
double getRelease() const { return release / 1000.0; }
|
||||
};
|
||||
|
||||
/** Defines phase-based volume curve for macro volume control (modified DLS standard) */
|
||||
struct ADSRDLS
|
||||
{
|
||||
uint32_t attack; /* 16.16 Time-cents */
|
||||
uint32_t decay; /* 16.16 Time-cents */
|
||||
uint16_t sustain; /* 0x1000 == 100% */
|
||||
uint16_t release; /* milliseconds */
|
||||
uint32_t attack; /* 16.16 Time-cents */
|
||||
uint32_t decay; /* 16.16 Time-cents */
|
||||
uint16_t sustain; /* 0x1000 == 100% */
|
||||
uint16_t release; /* milliseconds */
|
||||
uint32_t velToAttack; /* 16.16, 1000.0 == 100%; attack = <attack> + (vel/128) * <velToAttack> */
|
||||
uint32_t keyToDecay; /* 16.16, 1000.0 == 100%; decay = <decay> + (note/128) * <keyToDecay> */
|
||||
uint32_t keyToDecay; /* 16.16, 1000.0 == 100%; decay = <decay> + (note/128) * <keyToDecay> */
|
||||
|
||||
double getAttack() const {return TimeCentsToSeconds(attack);}
|
||||
double getDecay() const {return TimeCentsToSeconds(decay);}
|
||||
double getSustain() const {return sustain / double(0x1000);}
|
||||
double getRelease() const {return release / double(1000);}
|
||||
double getAttack() const { return TimeCentsToSeconds(attack); }
|
||||
double getDecay() const { return TimeCentsToSeconds(decay); }
|
||||
double getSustain() const { return sustain / double(0x1000); }
|
||||
double getRelease() const { return release / 1000.0; }
|
||||
double getVelToAttack(int8_t vel) const
|
||||
{
|
||||
if (velToAttack == 0x80000000)
|
||||
@@ -96,6 +92,7 @@ class AudioGroupPool
|
||||
std::unordered_map<ObjectId, const unsigned char*> m_tables;
|
||||
std::unordered_map<ObjectId, const Keymap*> m_keymaps;
|
||||
std::unordered_map<ObjectId, std::vector<const LayerMapping*>> m_layers;
|
||||
|
||||
public:
|
||||
AudioGroupPool(const unsigned char* data);
|
||||
AudioGroupPool(const unsigned char* data, PCDataTag);
|
||||
@@ -103,12 +100,9 @@ public:
|
||||
const Keymap* keymap(ObjectId id) const;
|
||||
const std::vector<const LayerMapping*>* layer(ObjectId id) const;
|
||||
const ADSR* tableAsAdsr(ObjectId id) const;
|
||||
const ADSRDLS* tableAsAdsrDLS(ObjectId id) const
|
||||
{return reinterpret_cast<const ADSRDLS*>(tableAsAdsr(id));}
|
||||
const Curve* tableAsCurves(ObjectId id) const
|
||||
{return reinterpret_cast<const Curve*>(tableAsAdsr(id));}
|
||||
const ADSRDLS* tableAsAdsrDLS(ObjectId id) const { return reinterpret_cast<const ADSRDLS*>(tableAsAdsr(id)); }
|
||||
const Curve* tableAsCurves(ObjectId id) const { return reinterpret_cast<const Curve*>(tableAsAdsr(id)); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_AUDIOGROUPPOOL_HPP__
|
||||
|
||||
@@ -76,8 +76,8 @@ class AudioGroupProject
|
||||
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convNormalPages;
|
||||
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convDrumPages;
|
||||
std::unique_ptr<std::array<SongGroupIndex::MIDISetup, 16>[]> m_convMidiSetups;
|
||||
void _allocateConvBuffers(const unsigned char* data, bool absOffs, N64DataTag);
|
||||
void _allocateConvBuffers(const unsigned char* data, bool absOffs, PCDataTag);
|
||||
void _allocateConvBuffers(const unsigned char* data, N64DataTag);
|
||||
void _allocateConvBuffers(const unsigned char* data, PCDataTag);
|
||||
|
||||
public:
|
||||
AudioGroupProject(const unsigned char* data, GCNDataTag);
|
||||
@@ -88,10 +88,9 @@ public:
|
||||
const SongGroupIndex* getSongGroupIndex(int groupId) const;
|
||||
const SFXGroupIndex* getSFXGroupIndex(int groupId) const;
|
||||
|
||||
const std::unordered_map<int, SongGroupIndex>& songGroups() const {return m_songGroups;}
|
||||
const std::unordered_map<int, SFXGroupIndex>& sfxGroups() const {return m_sfxGroups;}
|
||||
const std::unordered_map<int, SongGroupIndex>& songGroups() const { return m_songGroups; }
|
||||
const std::unordered_map<int, SFXGroupIndex>& sfxGroups() const { return m_sfxGroups; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_AUDIOGROUPPROJECT_HPP__
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
|
||||
|
||||
#include <unordered_map>
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include "Common.hpp"
|
||||
|
||||
namespace amuse
|
||||
@@ -12,6 +12,7 @@ namespace amuse
|
||||
class AudioGroupSampleDirectory
|
||||
{
|
||||
friend class AudioGroup;
|
||||
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
@@ -26,8 +27,7 @@ public:
|
||||
uint32_t m_adpcmParmOffset;
|
||||
void swapBig();
|
||||
};
|
||||
union ADPCMParms
|
||||
{
|
||||
union ADPCMParms {
|
||||
struct DSPParms
|
||||
{
|
||||
uint16_t m_bytesPerFrame;
|
||||
@@ -44,17 +44,17 @@ public:
|
||||
void swapBigDSP();
|
||||
void swapBigVADPCM();
|
||||
};
|
||||
|
||||
private:
|
||||
std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>> m_entries;
|
||||
|
||||
public:
|
||||
AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag);
|
||||
AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData,
|
||||
bool absOffs, N64DataTag);
|
||||
AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, bool absOffs, N64DataTag);
|
||||
AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag);
|
||||
|
||||
const std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>>& sampleEntries() const {return m_entries;}
|
||||
};
|
||||
|
||||
const std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>>& sampleEntries() const { return m_entries; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__
|
||||
|
||||
@@ -22,18 +22,21 @@ class BooBackendVoice : public IBackendVoice
|
||||
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;
|
||||
std::unique_ptr<boo::IAudioVoice> m_booVoice;
|
||||
boo::ObjToken<boo::IAudioVoice> m_booVoice;
|
||||
|
||||
public:
|
||||
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox,
|
||||
double sampleRate, bool dynamicPitch);
|
||||
BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
|
||||
double sampleRate, bool dynamicPitch);
|
||||
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch);
|
||||
void resetSampleRate(double sampleRate);
|
||||
void setMatrixCoefficients(const float coefs[8], bool slew);
|
||||
void setSubmixMatrixCoefficients(const float coefs[8], bool slew);
|
||||
|
||||
void resetChannelLevels();
|
||||
void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew);
|
||||
void setPitchRatio(double ratio, bool slew);
|
||||
void start();
|
||||
void stop();
|
||||
@@ -43,26 +46,23 @@ public:
|
||||
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 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;
|
||||
std::unique_ptr<boo::IAudioSubmix> m_booSubmix;
|
||||
boo::ObjToken<boo::IAudioSubmix> m_booSubmix;
|
||||
|
||||
public:
|
||||
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx);
|
||||
BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx);
|
||||
void setChannelGains(const float gains[8]);
|
||||
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
|
||||
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut, int busId);
|
||||
void setSendLevel(IBackendSubmix* submix, float level, bool slew);
|
||||
double getSampleRate() const;
|
||||
SubmixFormat getSampleFormat() const;
|
||||
};
|
||||
@@ -116,21 +116,24 @@ public:
|
||||
};
|
||||
|
||||
/** Backend voice allocator implementation for boo mixer */
|
||||
class BooBackendVoiceAllocator : public IBackendVoiceAllocator
|
||||
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback
|
||||
{
|
||||
friend class BooBackendMIDIReader;
|
||||
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);
|
||||
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, const char* name=nullptr);
|
||||
void register5MsCallback(std::function<void(double)>&& callback);
|
||||
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine, const char* name = nullptr);
|
||||
void setCallbackInterface(Engine* engine);
|
||||
AudioChannelSet getAvailableSet();
|
||||
void pumpAndMixVoices();
|
||||
void setVolume(float vol);
|
||||
void on5MsInterval(boo::IAudioVoiceEngine& engine, double dt);
|
||||
void onPumpCycleComplete(boo::IAudioVoiceEngine& engine);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_BOO_BACKEND_HPP__
|
||||
|
||||
@@ -2,16 +2,29 @@
|
||||
#define __AMUSE_COMMON_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstring>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _WIN32
|
||||
#include <strings.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
constexpr float NativeSampleRate = 32000.0f;
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -25,24 +38,29 @@ namespace amuse
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
using SystemString = std::wstring;
|
||||
using SystemChar = wchar_t;
|
||||
# ifndef _S
|
||||
# define _S(val) L ## val
|
||||
# endif
|
||||
using SystemString = std::wstring;
|
||||
using SystemStringView = std::wstring_view;
|
||||
using SystemChar = wchar_t;
|
||||
#ifndef _S
|
||||
#define _S(val) L##val
|
||||
#endif
|
||||
typedef struct _stat Sstat;
|
||||
static inline int Mkdir(const wchar_t* path, int) { return _wmkdir(path); }
|
||||
static inline int Stat(const wchar_t* path, Sstat* statout) { return _wstat(path, statout); }
|
||||
#else
|
||||
using SystemString = std::string;
|
||||
using SystemChar = char;
|
||||
# ifndef _S
|
||||
# define _S(val) val
|
||||
# endif
|
||||
using SystemString = std::string;
|
||||
using SystemStringView = std::string_view;
|
||||
using SystemChar = char;
|
||||
#ifndef _S
|
||||
#define _S(val) val
|
||||
#endif
|
||||
typedef struct stat Sstat;
|
||||
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
|
||||
|
||||
#if _WIN32
|
||||
static inline int CompareCaseInsensitive(const char* a, const char* b)
|
||||
{
|
||||
return _stricmp(a, b);
|
||||
}
|
||||
static inline int CompareCaseInsensitive(const char* a, const char* b) { return _stricmp(a, b); }
|
||||
#endif
|
||||
|
||||
static inline int CompareCaseInsensitive(const SystemChar* a, const SystemChar* b)
|
||||
@@ -55,35 +73,30 @@ static inline int CompareCaseInsensitive(const SystemChar* a, const SystemChar*
|
||||
}
|
||||
|
||||
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>
|
||||
static inline T ClampFull(float in) {return in;}
|
||||
|
||||
template <>
|
||||
inline int16_t ClampFull<int16_t>(float in)
|
||||
{
|
||||
if (in < SHRT_MIN)
|
||||
return SHRT_MIN;
|
||||
else if (in > SHRT_MAX)
|
||||
return SHRT_MAX;
|
||||
return in;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int32_t ClampFull<int32_t>(float in)
|
||||
{
|
||||
if (in < INT_MIN)
|
||||
return INT_MIN;
|
||||
else if (in > INT_MAX)
|
||||
return INT_MAX;
|
||||
return in;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float ClampFull<float>(float in)
|
||||
{
|
||||
return in;
|
||||
inline T ClampFull(float in)
|
||||
{
|
||||
if (std::is_floating_point<T>())
|
||||
{
|
||||
return std::min<T>(std::max<T>(in, -1.f), 1.f);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef M_PIF
|
||||
@@ -91,9 +104,10 @@ inline float ClampFull<float>(float in)
|
||||
#endif
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((__format__ (__printf__, 1, 2)))
|
||||
__attribute__((__format__(__printf__, 1, 2)))
|
||||
#endif
|
||||
static inline void Printf(const SystemChar* fmt, ...)
|
||||
static inline void
|
||||
Printf(const SystemChar* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
@@ -106,9 +120,10 @@ static inline void Printf(const SystemChar* fmt, ...)
|
||||
}
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((__format__ (__printf__, 3, 4)))
|
||||
__attribute__((__format__(__printf__, 3, 4)))
|
||||
#endif
|
||||
static inline void SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...)
|
||||
static inline void
|
||||
SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
@@ -174,6 +189,15 @@ static inline FILE* FOpen(const SystemChar* path, const SystemChar* mode)
|
||||
return fp;
|
||||
}
|
||||
|
||||
static inline void Unlink(const SystemChar* file)
|
||||
{
|
||||
#if _WIN32
|
||||
_wunlink(file);
|
||||
#else
|
||||
unlink(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef bswap16
|
||||
#undef bswap32
|
||||
#undef bswap64
|
||||
@@ -213,24 +237,20 @@ static inline T bswap64(T val)
|
||||
#elif _WIN32
|
||||
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
|
||||
}
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
static inline int16_t SBig(int16_t val) {return bswap16(val);}
|
||||
static inline uint16_t SBig(uint16_t val) {return bswap16(val);}
|
||||
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 int16_t SBig(int16_t val) { return bswap16(val); }
|
||||
static inline uint16_t SBig(uint16_t val) { return bswap16(val); }
|
||||
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)));
|
||||
@@ -242,28 +262,27 @@ static inline double SBig(double val)
|
||||
return *((double*)(&ival));
|
||||
}
|
||||
#ifndef SBIG
|
||||
#define SBIG(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \
|
||||
| ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 )
|
||||
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
||||
#endif
|
||||
|
||||
static inline int16_t SLittle(int16_t val) {return val;}
|
||||
static inline uint16_t SLittle(uint16_t val) {return val;}
|
||||
static inline int32_t SLittle(int32_t val) {return val;}
|
||||
static inline uint32_t SLittle(uint32_t val) {return val;}
|
||||
static inline int64_t SLittle(int64_t val) {return val;}
|
||||
static inline uint64_t SLittle(uint64_t val) {return val;}
|
||||
static inline float SLittle(float val) {return val;}
|
||||
static inline double SLittle(double val) {return val;}
|
||||
static inline int16_t SLittle(int16_t val) { return val; }
|
||||
static inline uint16_t SLittle(uint16_t val) { return val; }
|
||||
static inline int32_t SLittle(int32_t val) { return val; }
|
||||
static inline uint32_t SLittle(uint32_t val) { return val; }
|
||||
static inline int64_t SLittle(int64_t val) { return val; }
|
||||
static inline uint64_t SLittle(uint64_t val) { return val; }
|
||||
static inline float SLittle(float val) { return val; }
|
||||
static inline double SLittle(double val) { return val; }
|
||||
#ifndef SLITTLE
|
||||
#define SLITTLE(q) (q)
|
||||
#endif
|
||||
#else
|
||||
static inline int16_t SLittle(int16_t val) {return bswap16(val);}
|
||||
static inline uint16_t SLittle(uint16_t val) {return bswap16(val);}
|
||||
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 int16_t SLittle(int16_t val) { return bswap16(val); }
|
||||
static inline uint16_t SLittle(uint16_t val) { return bswap16(val); }
|
||||
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)));
|
||||
@@ -275,18 +294,17 @@ static inline double SLittle(double val)
|
||||
return *((double*)(&ival));
|
||||
}
|
||||
#ifndef SLITTLE
|
||||
#define SLITTLE(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \
|
||||
| ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 )
|
||||
#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
||||
#endif
|
||||
|
||||
static inline int16_t SBig(int16_t val) {return val;}
|
||||
static inline uint16_t SBig(uint16_t val) {return val;}
|
||||
static inline int32_t SBig(int32_t val) {return val;}
|
||||
static inline uint32_t SBig(uint32_t val) {return val;}
|
||||
static inline int64_t SBig(int64_t val) {return val;}
|
||||
static inline uint64_t SBig(uint64_t val) {return val;}
|
||||
static inline float SBig(float val) {return val;}
|
||||
static inline double SBig(double val) {return val;}
|
||||
static inline int16_t SBig(int16_t val) { return val; }
|
||||
static inline uint16_t SBig(uint16_t val) { return val; }
|
||||
static inline int32_t SBig(int32_t val) { return val; }
|
||||
static inline uint32_t SBig(uint32_t val) { return val; }
|
||||
static inline int64_t SBig(int64_t val) { return val; }
|
||||
static inline uint64_t SBig(uint64_t val) { return val; }
|
||||
static inline float SBig(float val) { return val; }
|
||||
static inline double SBig(double val) { return val; }
|
||||
#ifndef SBIG
|
||||
#define SBIG(q) (q)
|
||||
#endif
|
||||
@@ -301,14 +319,19 @@ enum class DataFormat
|
||||
};
|
||||
|
||||
/** 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
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_COMMON_HPP__
|
||||
|
||||
@@ -21,8 +21,7 @@ public:
|
||||
MetroidPrime2,
|
||||
RogueSquadronPC,
|
||||
RogueSquadronN64,
|
||||
BattleForNabooPC,
|
||||
BattleForNabooN64,
|
||||
Factor5N64Rev,
|
||||
RogueSquadron2,
|
||||
RogueSquadron3
|
||||
};
|
||||
@@ -33,15 +32,17 @@ public:
|
||||
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) {}
|
||||
: 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, IntrusiveAudioGroupData>> LoadContainer(const SystemChar* path,
|
||||
Type& typeOut);
|
||||
static std::vector<std::pair<SystemString, SongData>> LoadSongs(const SystemChar* path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_CONTAINERREGISTRY_HPP__
|
||||
|
||||
68
include/amuse/DirectoryEnumerator.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef __AMUSE_DIRECTORY_ENUMERATOR__
|
||||
#define __AMUSE_DIRECTORY_ENUMERATOR__
|
||||
|
||||
#include "Common.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
struct CaseInsensitiveCompare
|
||||
{
|
||||
bool operator()(std::string_view lhs, std::string_view rhs) const
|
||||
{
|
||||
#if _WIN32
|
||||
if (_stricmp(lhs.data(), rhs.data()) < 0)
|
||||
#else
|
||||
if (strcasecmp(lhs.data(), rhs.data()) < 0)
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
public:
|
||||
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(); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_DIRECTORY_ENUMERATOR__
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef __AMUSE_EFFECTBASE_HPP__
|
||||
#define __AMUSE_EFFECTBASE_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -12,16 +12,15 @@ class EffectBaseTypeless
|
||||
{
|
||||
public:
|
||||
virtual ~EffectBaseTypeless() = default;
|
||||
virtual void resetOutputSampleRate(double sampleRate)=0;
|
||||
virtual void resetOutputSampleRate(double sampleRate) = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTBASE_HPP__
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "EffectBase.hpp"
|
||||
#include "Common.hpp"
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -12,17 +12,30 @@ 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 */
|
||||
|
||||
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 */
|
||||
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);
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
using ImpType = EffectChorusImp<T>;
|
||||
@@ -47,6 +60,13 @@ public:
|
||||
x98_period = period;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void updateParams(const EffectChorusInfo& info)
|
||||
{
|
||||
setBaseDelay(info.baseDelay);
|
||||
setVariation(info.variation);
|
||||
setPeriod(info.period);
|
||||
}
|
||||
};
|
||||
|
||||
/** Type-specific implementation of chorus effect */
|
||||
@@ -56,32 +76,32 @@ 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 */
|
||||
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 */
|
||||
|
||||
int32_t x60_pitchOffset; /**< packed 16.16 fixed-point value of pitchHi and pitchLo quantities */
|
||||
int32_t x60_pitchOffset; /**< packed 16.16 fixed-point value of pitchHi and pitchLo quantities */
|
||||
uint32_t x64_pitchOffsetPeriodCount; /**< trigger value for flipping SRC state */
|
||||
uint32_t x68_pitchOffsetPeriod; /**< intermediate block window quantity for calculating 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 */
|
||||
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;
|
||||
|
||||
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
||||
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
||||
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
||||
|
||||
void _setup(double sampleRate);
|
||||
@@ -90,10 +110,12 @@ class EffectChorusImp : public EffectBase<T>, public EffectChorus
|
||||
public:
|
||||
~EffectChorusImp();
|
||||
EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate);
|
||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||
void resetOutputSampleRate(double sampleRate) {_setup(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); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTCHORUS_HPP__
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
#define __AMUSE_EFFECTDELAY_HPP__
|
||||
|
||||
#include "EffectBase.hpp"
|
||||
#include "IBackendVoice.hpp"
|
||||
#include "Common.hpp"
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace amuse
|
||||
@@ -11,14 +12,46 @@ 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 */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
protected:
|
||||
uint32_t x3c_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
|
||||
uint32_t x3c_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
|
||||
uint32_t x48_feedback[8]; /**< [0, 100] percent to mix delayed signal with input signal */
|
||||
uint32_t x54_output[8]; /**< [0, 100] total output percent */
|
||||
bool m_dirty = true; /**< needs update of internal parameter data */
|
||||
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>;
|
||||
@@ -26,7 +59,7 @@ public:
|
||||
void setDelay(uint32_t delay)
|
||||
{
|
||||
delay = clamp(10u, delay, 5000u);
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
x3c_delay[i] = delay;
|
||||
m_dirty = true;
|
||||
}
|
||||
@@ -40,7 +73,7 @@ public:
|
||||
void setFeedback(uint32_t feedback)
|
||||
{
|
||||
feedback = clamp(0u, feedback, 100u);
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
x48_feedback[i] = feedback;
|
||||
m_dirty = true;
|
||||
}
|
||||
@@ -55,39 +88,53 @@ public:
|
||||
void setOutput(uint32_t output)
|
||||
{
|
||||
output = clamp(0u, output, 100u);
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
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;
|
||||
}
|
||||
|
||||
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 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 */
|
||||
uint32_t x24_currentOutput[8]; /**< [0, 128] total attenuator */
|
||||
|
||||
std::unique_ptr<T[]> x30_chanLines[8]; /**< delay-line buffers for each channel */
|
||||
|
||||
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
||||
uint32_t m_sampsPerMs; /**< canonical count of samples per ms for the current backend */
|
||||
uint32_t m_blockSamples; /**< count of samples in a 5ms block */
|
||||
void _setup(double sampleRate);
|
||||
void _update();
|
||||
|
||||
public:
|
||||
EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate);
|
||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||
void resetOutputSampleRate(double sampleRate) {_setup(sampleRate);}
|
||||
};
|
||||
EffectDelayImp(const EffectDelayInfo& info, double sampleRate);
|
||||
|
||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTDELAY_HPP__
|
||||
|
||||
@@ -8,6 +8,35 @@
|
||||
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 */
|
||||
|
||||
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 */
|
||||
|
||||
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
|
||||
{
|
||||
@@ -31,19 +60,20 @@ class EffectReverbHiImp;
|
||||
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);
|
||||
EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay);
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
using ImpType = EffectReverbStdImp<T>;
|
||||
@@ -77,6 +107,15 @@ public:
|
||||
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
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 */
|
||||
@@ -86,8 +125,8 @@ class EffectReverbHi : public EffectReverbStd
|
||||
|
||||
template <typename T>
|
||||
friend class EffectReverbHiImp;
|
||||
EffectReverbHi(float coloration, float mix, float time,
|
||||
float damping, float preDelay, float crosstalk);
|
||||
EffectReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk);
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
using ImpType = EffectReverbHiImp<T>;
|
||||
@@ -97,48 +136,61 @@ public:
|
||||
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
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 */
|
||||
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 */
|
||||
float* x130_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
||||
|
||||
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(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 resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
||||
};
|
||||
|
||||
/** 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 */
|
||||
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* x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
||||
float x1a8_internalCrosstalk = 0.f;
|
||||
|
||||
double m_sampleRate; /**< copy of sample rate */
|
||||
@@ -146,13 +198,16 @@ class EffectReverbHiImp : public EffectBase<T>, public EffectReverbHi
|
||||
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);
|
||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||
void resetOutputSampleRate(double sampleRate) {_setup(sampleRate);}
|
||||
};
|
||||
|
||||
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) {}
|
||||
|
||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||
void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTREVERB_HPP__
|
||||
|
||||
@@ -2,35 +2,66 @@
|
||||
#define __AMUSE_EMITTER_HPP__
|
||||
|
||||
#include "Entity.hpp"
|
||||
#include "Common.hpp"
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
class Voice;
|
||||
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 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
|
||||
{
|
||||
std::shared_ptr<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;
|
||||
|
||||
friend class Engine;
|
||||
void _destroy();
|
||||
float _attenuationCurve(float dist) const;
|
||||
void _update();
|
||||
|
||||
public:
|
||||
~Emitter();
|
||||
Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>&& vox);
|
||||
Emitter(Engine& engine, const AudioGroup& group, const std::shared_ptr<Voice>& vox,
|
||||
float maxDist, float minVol, float falloff, bool doppler);
|
||||
|
||||
void setPos(const Vector3f& pos);
|
||||
void setDir(const Vector3f& dir);
|
||||
void setMaxDist(float maxDist);
|
||||
void setMaxVol(float maxVol);
|
||||
void setMinVol(float minVol);
|
||||
void setFalloff(float falloff);
|
||||
void setVectors(const float* pos, const float* dir);
|
||||
void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); m_dirty = true; }
|
||||
|
||||
std::shared_ptr<Voice>& getVoice() {return m_vox;}
|
||||
const std::shared_ptr<Voice>& getVoice() const { return m_vox; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EMITTER_HPP__
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "Emitter.hpp"
|
||||
#include "Listener.hpp"
|
||||
#include "AudioGroupSampleDirectory.hpp"
|
||||
#include "Sequencer.hpp"
|
||||
#include "Studio.hpp"
|
||||
#include "IBackendVoiceAllocator.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -22,7 +25,7 @@ class IMIDIReader;
|
||||
|
||||
enum class AmplitudeMode
|
||||
{
|
||||
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
|
||||
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
|
||||
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
|
||||
};
|
||||
|
||||
@@ -32,6 +35,7 @@ class Engine
|
||||
friend class Voice;
|
||||
friend class Emitter;
|
||||
friend class Sequencer;
|
||||
friend class Studio;
|
||||
friend struct Sequencer::ChannelState;
|
||||
|
||||
IBackendVoiceAllocator& m_backend;
|
||||
@@ -40,38 +44,38 @@ class Engine
|
||||
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
|
||||
std::list<std::shared_ptr<Voice>> m_activeVoices;
|
||||
std::list<std::shared_ptr<Emitter>> m_activeEmitters;
|
||||
std::list<std::shared_ptr<Listener>> m_activeListeners;
|
||||
std::list<std::shared_ptr<Sequencer>> m_activeSequencers;
|
||||
std::list<Submix> m_activeSubmixes;
|
||||
std::list<std::weak_ptr<Studio>> m_activeStudios; /* lifetime dependent on contributing audio entities */
|
||||
bool m_defaultStudioReady = false;
|
||||
std::shared_ptr<Studio> m_defaultStudio;
|
||||
std::unordered_map<uint16_t, std::tuple<AudioGroup*, int, 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(int groupId) const;
|
||||
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(int groupId) const;
|
||||
|
||||
std::list<std::shared_ptr<Voice>>::iterator
|
||||
_allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
|
||||
bool dynamicPitch, bool emitter, Submix* smx);
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator
|
||||
_allocateSequencer(const AudioGroup& group, int groupId,
|
||||
int setupId, Submix* smx);
|
||||
std::list<Submix>::iterator _allocateSubmix(Submix* smx);
|
||||
std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
|
||||
bool dynamicPitch, bool emitter,
|
||||
std::weak_ptr<Studio> studio);
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, int groupId,
|
||||
int setupId, std::weak_ptr<Studio> studio);
|
||||
std::shared_ptr<Studio> _allocateStudio(bool mainOut);
|
||||
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator _destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it);
|
||||
std::list<Submix>::iterator _destroySubmix(std::list<Submix>::iterator it);
|
||||
std::list<Submix>::iterator _removeSubmix(std::list<Submix>::iterator it);
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator
|
||||
_destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it);
|
||||
void _bringOutYourDead();
|
||||
void _5MsCallback(double dt);
|
||||
|
||||
public:
|
||||
~Engine();
|
||||
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode=AmplitudeMode::PerSample);
|
||||
Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode = AmplitudeMode::PerSample);
|
||||
|
||||
/** Access voice backend of engine */
|
||||
IBackendVoiceAllocator& getBackend() {return m_backend;}
|
||||
|
||||
/** Update all active audio entities and fill OS audio buffers as needed */
|
||||
void pumpEngine();
|
||||
IBackendVoiceAllocator& getBackend() { return m_backend; }
|
||||
|
||||
/** Add audio group data pointers to engine; must remain resident! */
|
||||
const AudioGroup* addAudioGroup(const AudioGroupData& data);
|
||||
@@ -79,23 +83,46 @@ public:
|
||||
/** Remove audio group from engine */
|
||||
void removeAudioGroup(const AudioGroupData& data);
|
||||
|
||||
/** Create new Submix (a.k.a 'Studio') within root mix engine */
|
||||
Submix* addSubmix(Submix* parent=nullptr);
|
||||
/** Access engine's default studio */
|
||||
std::shared_ptr<Studio> getDefaultStudio() { return m_defaultStudio; }
|
||||
|
||||
/** Remove Submix and deallocate */
|
||||
void removeSubmix(Submix* smx);
|
||||
/** Create new Studio within engine */
|
||||
std::shared_ptr<Studio> addStudio(bool mainOut);
|
||||
|
||||
/** Start soundFX playing from loaded audio groups */
|
||||
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan, Submix* smx=nullptr);
|
||||
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan, std::weak_ptr<Studio> smx);
|
||||
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan)
|
||||
{
|
||||
return fxStart(sfxId, vol, pan, m_defaultStudio);
|
||||
}
|
||||
|
||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||
std::shared_ptr<Emitter> addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
|
||||
float falloff, int sfxId, float minVol, float maxVol,
|
||||
Submix* smx=nullptr);
|
||||
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||
int sfxId, float minVol, float maxVol, bool doppler,
|
||||
std::weak_ptr<Studio> smx);
|
||||
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||
int 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 */
|
||||
std::shared_ptr<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);
|
||||
|
||||
/** Start song playing from loaded audio groups */
|
||||
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData,
|
||||
Submix* smx=nullptr);
|
||||
std::weak_ptr<Studio> smx);
|
||||
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData)
|
||||
{
|
||||
return seqPlay(groupId, songId, arrData, m_defaultStudio);
|
||||
}
|
||||
|
||||
/** Set total volume of engine */
|
||||
void setVolume(float vol);
|
||||
|
||||
/** Find voice from VoiceId */
|
||||
std::shared_ptr<Voice> findVoice(int vid);
|
||||
@@ -107,12 +134,19 @@ public:
|
||||
void sendMacroMessage(ObjectId macroId, int32_t val);
|
||||
|
||||
/** Obtain next random number from engine's PRNG */
|
||||
uint32_t nextRandom() {return m_random();}
|
||||
uint32_t nextRandom() { return m_random(); }
|
||||
|
||||
/** Obtain list of active sequencers */
|
||||
std::list<std::shared_ptr<Sequencer>>& getActiveSequencers() {return m_activeSequencers;}
|
||||
};
|
||||
std::list<std::shared_ptr<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);
|
||||
|
||||
/** When a pumping cycle is complete this is called to allow the client to
|
||||
* perform periodic cleanup tasks */
|
||||
void _onPumpCycleComplete(IBackendVoiceAllocator& engine);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_ENGINE_HPP__
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef __AMUSE_ENTITY_HPP__
|
||||
#define __AMUSE_ENTITY_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -21,13 +21,12 @@ class Entity
|
||||
* but shared_ptrs are issued to the client so it can safely track state */
|
||||
friend class Engine;
|
||||
friend class SoundMacroState;
|
||||
|
||||
protected:
|
||||
bool m_destroyed = false;
|
||||
void _destroy()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
assert(!m_destroyed);
|
||||
#endif
|
||||
m_destroyed = true;
|
||||
}
|
||||
Engine& m_engine;
|
||||
@@ -35,26 +34,25 @@ protected:
|
||||
int m_groupId;
|
||||
ObjectId m_objectId = 0xffff; /* if applicable */
|
||||
public:
|
||||
Entity(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid=ObjectId())
|
||||
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid) {}
|
||||
Entity(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid = ObjectId())
|
||||
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid)
|
||||
{
|
||||
}
|
||||
~Entity()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* Ensure proper destruction procedure followed */
|
||||
assert(m_destroyed);
|
||||
#endif
|
||||
}
|
||||
|
||||
Engine& getEngine() {return m_engine;}
|
||||
const AudioGroup& getAudioGroup() const {return m_audioGroup;}
|
||||
int getGroupId() const {return m_groupId;}
|
||||
ObjectId getObjectId() const {return m_objectId;}
|
||||
Engine& getEngine() { return m_engine; }
|
||||
const AudioGroup& getAudioGroup() const { return m_audioGroup; }
|
||||
int getGroupId() const { return m_groupId; }
|
||||
ObjectId getObjectId() const { return m_objectId; }
|
||||
};
|
||||
|
||||
/** Curves for mapping velocity to volume and other functional mappings
|
||||
* (defined here for visibility)*/
|
||||
using Curve = uint8_t[128];
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_ENTITY_HPP__
|
||||
|
||||
@@ -19,15 +19,17 @@ public:
|
||||
Release,
|
||||
Complete
|
||||
};
|
||||
|
||||
private:
|
||||
State m_phase = State::Attack; /**< Current envelope state */
|
||||
double m_attackTime = 0.02; /**< 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.02; /**< Time of release in seconds */
|
||||
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 */
|
||||
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);
|
||||
@@ -35,10 +37,9 @@ public:
|
||||
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;}
|
||||
bool isComplete() const { return m_phase == State::Complete; }
|
||||
bool isAdsrSet() const { return m_adsrSet; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_ENVELOPE_HPP__
|
||||
|
||||
@@ -21,19 +21,15 @@ class IBackendSubmix
|
||||
public:
|
||||
virtual ~IBackendSubmix() = default;
|
||||
|
||||
/** Set channel-gains for submix (AudioChannel enum for array index) */
|
||||
virtual void setChannelGains(const float gains[8])=0;
|
||||
|
||||
/** Amuse obtains a new voice from the platform outputting to this submix */
|
||||
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)=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;
|
||||
virtual double getSampleRate() const = 0;
|
||||
|
||||
/** Amuse gets fixed sample format of submix this way */
|
||||
virtual SubmixFormat getSampleFormat() const=0;
|
||||
virtual SubmixFormat getSampleFormat() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_IBACKENDSUBMIX_HPP__
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
class IBackendSubmix;
|
||||
|
||||
/** Same channel enums from boo, used for matrix coefficient table index */
|
||||
enum class AudioChannel
|
||||
@@ -32,24 +33,23 @@ public:
|
||||
virtual ~IBackendVoice() = default;
|
||||
|
||||
/** Set new sample rate into platform voice (may result in artifacts while playing) */
|
||||
virtual void resetSampleRate(double sampleRate)=0;
|
||||
virtual void resetSampleRate(double sampleRate) = 0;
|
||||
|
||||
/** Reset channel-gains to silence and unbind all submixes */
|
||||
virtual void resetChannelLevels() = 0;
|
||||
|
||||
/** Set channel-gains for audio source (AudioChannel enum for array index) */
|
||||
virtual void setMatrixCoefficients(const float coefs[8], bool slew)=0;
|
||||
|
||||
/** Set submix-channel-gains for audio source (AudioChannel enum for array index) */
|
||||
virtual void setSubmixMatrixCoefficients(const float coefs[8], bool slew)=0;
|
||||
virtual void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew) = 0;
|
||||
|
||||
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
|
||||
virtual void setPitchRatio(double ratio, bool slew)=0;
|
||||
virtual void setPitchRatio(double ratio, bool slew) = 0;
|
||||
|
||||
/** Instructs platform to begin consuming sample data; invoking callback as needed */
|
||||
virtual void start()=0;
|
||||
virtual void start() = 0;
|
||||
|
||||
/** Instructs platform to stop consuming sample data */
|
||||
virtual void stop()=0;
|
||||
virtual void stop() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_IBACKENDVOICE_HPP__
|
||||
|
||||
@@ -27,9 +27,9 @@ enum class AudioChannelSet
|
||||
class IMIDIReader
|
||||
{
|
||||
public:
|
||||
virtual ~IMIDIReader()=default;
|
||||
virtual std::string description()=0;
|
||||
virtual void pumpReader(double dt)=0;
|
||||
virtual ~IMIDIReader() = default;
|
||||
virtual std::string description() = 0;
|
||||
virtual void pumpReader(double dt) = 0;
|
||||
};
|
||||
|
||||
/** Client-implemented voice allocator */
|
||||
@@ -39,29 +39,26 @@ public:
|
||||
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;
|
||||
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)=0;
|
||||
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;
|
||||
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, const char* name=nullptr)=0;
|
||||
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine, const char* name = nullptr) = 0;
|
||||
|
||||
/** Amuse obtains speaker-configuration from the platform this way */
|
||||
virtual AudioChannelSet getAvailableSet()=0;
|
||||
virtual AudioChannelSet getAvailableSet() = 0;
|
||||
|
||||
/** Amuse flushes voice samples to the backend this way */
|
||||
virtual void pumpAndMixVoices()=0;
|
||||
/** Set volume of main mix out */
|
||||
virtual void setVolume(float vol) = 0;
|
||||
|
||||
/** Amuse may request callbacks 200-updates-per-second virtually */
|
||||
virtual void register5MsCallback(std::function<void(double dt)>&& callback)=0;
|
||||
/** Amuse registers for key callback events from the mixing engine this way */
|
||||
virtual void setCallbackInterface(Engine* engine) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_IBACKENDVOICEALLOCATOR_HPP__
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
#ifndef __AMUSE_LISTENER_HPP__
|
||||
#define __AMUSE_LISTENER_HPP__
|
||||
|
||||
#include "Entity.hpp"
|
||||
#include "amuse/Emitter.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
class Listener : public Entity
|
||||
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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_LISTENER_HPP__
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "Entity.hpp"
|
||||
#include "AudioGroupProject.hpp"
|
||||
#include "SongState.hpp"
|
||||
#include "optional.hpp"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
@@ -12,44 +11,51 @@
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
class Submix;
|
||||
class Studio;
|
||||
class Voice;
|
||||
|
||||
/** State of sequencer over lifetime */
|
||||
enum class SequencerState
|
||||
{
|
||||
Playing, /**< Sequencer actively playing arrangement */
|
||||
Playing, /**< Sequencer actively playing arrangement */
|
||||
Interactive, /**< Interactive sequencer for live MIDI message processing, will not automatically die */
|
||||
Dead /**< Set when arrangement complete and `dieOnEnd` was set, or manually with die() */
|
||||
Dead /**< Set when arrangement complete and `dieOnEnd` was set, or manually with die() */
|
||||
};
|
||||
|
||||
/** Multi-voice lifetime manager and polyphonic parameter tracking */
|
||||
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 */
|
||||
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 */
|
||||
Submix* m_submix = nullptr; /**< Submix this sequencer outputs to (or NULL for the main output mix) */
|
||||
std::shared_ptr<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 */
|
||||
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for arrangement data */
|
||||
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
|
||||
SongState m_songState; /**< State of current arrangement playback */
|
||||
double m_ticksPerSec = 1000.0; /**< Current ticks per second (tempo) for arrangement data */
|
||||
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;
|
||||
|
||||
/** State of a single MIDI channel */
|
||||
struct ChannelState
|
||||
{
|
||||
Sequencer& m_parent;
|
||||
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, std::shared_ptr<Voice>> m_chanVoxs;
|
||||
@@ -57,9 +63,9 @@ class Sequencer : public Entity
|
||||
std::weak_ptr<Voice> m_lastVoice;
|
||||
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
|
||||
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
|
||||
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 */
|
||||
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 */
|
||||
|
||||
void _bringOutYourDead();
|
||||
size_t getVoiceCount() const;
|
||||
@@ -77,26 +83,26 @@ class Sequencer : public Entity
|
||||
std::shared_ptr<Voice> findVoice(int vid);
|
||||
void sendMacroMessage(ObjectId macroId, int32_t val);
|
||||
};
|
||||
std::array<std::experimental::optional<ChannelState>, 16> m_chanStates; /**< Lazily-allocated channel states */
|
||||
std::array<ChannelState, 16> m_chanStates; /**< Lazily-allocated channel states */
|
||||
|
||||
void _bringOutYourDead();
|
||||
void _destroy();
|
||||
|
||||
public:
|
||||
~Sequencer();
|
||||
Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||
const SongGroupIndex* songGroup, int setupId, Submix* smx);
|
||||
Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||
const SFXGroupIndex* sfxGroup, Submix* smx);
|
||||
Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId,
|
||||
std::weak_ptr<Studio> studio);
|
||||
Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup,
|
||||
std::weak_ptr<Studio> studio);
|
||||
|
||||
/** Advance current song data (if any) */
|
||||
void advance(double dt);
|
||||
|
||||
/** Obtain pointer to Sequencer's Submix */
|
||||
Submix* getSubmix() {return m_submix;}
|
||||
std::shared_ptr<Studio> getStudio() { return m_studio; }
|
||||
|
||||
/** Get current state of sequencer */
|
||||
SequencerState state() const {return m_state;}
|
||||
SequencerState state() const { return m_state; }
|
||||
|
||||
/** Get number of active voices */
|
||||
size_t getVoiceCount() const;
|
||||
@@ -114,10 +120,10 @@ public:
|
||||
void setPitchWheel(uint8_t chan, float pitchWheel);
|
||||
|
||||
/** Send keyoffs to all active notes, silence immediately if `now` set */
|
||||
void allOff(bool now=false);
|
||||
void allOff(bool now = false);
|
||||
|
||||
/** Send keyoffs to all active notes on specified channel, silence immediately if `now` set */
|
||||
void allOff(uint8_t chan, bool now=false);
|
||||
void allOff(uint8_t chan, bool now = false);
|
||||
|
||||
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
|
||||
void killKeygroup(uint8_t kg, bool now);
|
||||
@@ -132,13 +138,13 @@ public:
|
||||
void setTempo(double ticksPerSec);
|
||||
|
||||
/** Play MIDI arrangement */
|
||||
void playSong(const unsigned char* arrData, bool dieOnEnd=true);
|
||||
void playSong(const unsigned char* arrData, bool dieOnEnd = true);
|
||||
|
||||
/** Stop current MIDI arrangement */
|
||||
void stopSong(bool now=false);
|
||||
void stopSong(float fadeTime = 0.f, bool now = false);
|
||||
|
||||
/** Set total volume of sequencer */
|
||||
void setVolume(float vol);
|
||||
void setVolume(float vol, float fadeTime = 0.f);
|
||||
|
||||
/** Get current program number of channel */
|
||||
int8_t getChanProgram(int8_t chanId) const;
|
||||
@@ -153,9 +159,8 @@ public:
|
||||
void prevChanProgram(int8_t chanId);
|
||||
|
||||
/** Manually kill sequencer for deferred release from engine */
|
||||
void kill() {m_state = SequencerState::Dead;}
|
||||
void kill() { m_state = SequencerState::Dead; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SEQUENCER_HPP__
|
||||
|
||||
18
include/amuse/SongConverter.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef __AMUSE_SONGCONVERTER_HPP__
|
||||
#define __AMUSE_SONGCONVERTER_HPP__
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SONGCONVERTER_HPP__
|
||||
@@ -1,11 +1,10 @@
|
||||
#ifndef __AMUSE_SONGSTATE_HPP__
|
||||
#define __AMUSE_SONGSTATE_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include "optional.hpp"
|
||||
#include "Entity.hpp"
|
||||
|
||||
namespace amuse
|
||||
@@ -22,46 +21,46 @@ enum class SongPlayState
|
||||
class SongState
|
||||
{
|
||||
friend class Voice;
|
||||
friend class SongConverter;
|
||||
|
||||
/** Song header */
|
||||
struct Header
|
||||
{
|
||||
uint32_t m_version;
|
||||
uint32_t m_chanIdxOff;
|
||||
uint32_t m_trackIdxOff;
|
||||
uint32_t m_regionIdxOff;
|
||||
uint32_t m_chanMapOff;
|
||||
uint32_t m_tempoTableOff;
|
||||
uint32_t m_initialTempo;
|
||||
uint32_t m_unkOff;
|
||||
uint32_t m_chanOffs[64];
|
||||
void swapBig();
|
||||
} m_header;
|
||||
|
||||
/** Channel header */
|
||||
struct ChanHeader
|
||||
/** Track region ('clip' in an NLA representation) */
|
||||
struct TrackRegion
|
||||
{
|
||||
uint32_t m_startTick;
|
||||
uint16_t m_unk1;
|
||||
uint8_t m_progNum;
|
||||
uint8_t m_unk1;
|
||||
uint16_t m_unk2;
|
||||
uint16_t m_dataIndex;
|
||||
uint16_t m_unk3;
|
||||
uint32_t m_startTick2;
|
||||
uint16_t m_unk4;
|
||||
uint16_t m_unk5;
|
||||
uint16_t m_unk6;
|
||||
uint16_t m_unk7;
|
||||
void swapBig();
|
||||
int16_t m_regionIndex;
|
||||
int16_t m_unk3;
|
||||
bool indexValid(bool bigEndian) const;
|
||||
};
|
||||
|
||||
/** Tempo change entry */
|
||||
struct TempoChange
|
||||
{
|
||||
uint32_t m_tick; /**< Relative song ticks from previous tempo change */
|
||||
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();
|
||||
};
|
||||
|
||||
/** State of a single channel within arrangement */
|
||||
struct Channel
|
||||
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
|
||||
{
|
||||
@@ -71,39 +70,50 @@ class SongState
|
||||
void swapBig();
|
||||
};
|
||||
|
||||
SongState& m_parent;
|
||||
uint8_t m_midiChan; /**< MIDI channel number of song channel */
|
||||
uint32_t m_startTick; /**< Tick to start execution of channel commands */
|
||||
SongState* m_parent = nullptr;
|
||||
uint8_t m_midiChan; /**< MIDI channel number of song channel */
|
||||
const TrackRegion* m_curRegion; /**< Pointer to currently-playing track region */
|
||||
const TrackRegion* m_nextRegion; /**< Pointer to next-queued track region */
|
||||
|
||||
const unsigned char* m_dataBase; /**< Base pointer to command data */
|
||||
const unsigned char* m_data; /**< Pointer to upcoming command 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 */
|
||||
uint32_t m_lastPitchTick = 0; /**< Last position of pitch wheel change */
|
||||
int32_t m_lastPitchVal = 0; /**< Last value of pitch */
|
||||
uint32_t m_lastModTick = 0; /**< Last position of mod wheel change */
|
||||
int32_t m_lastModVal = 0; /**< Last value of mod */
|
||||
std::array<uint16_t, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */
|
||||
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
|
||||
uint32_t m_lastPitchTick = 0; /**< Last position of pitch wheel change */
|
||||
int32_t m_lastPitchVal = 0; /**< Last value of pitch */
|
||||
uint32_t m_lastModTick = 0; /**< Last position of mod wheel change */
|
||||
int32_t m_lastModVal = 0; /**< Last value of mod */
|
||||
std::array<int, 128> m_remNoteLengths; /**< Remaining ticks per note */
|
||||
|
||||
int32_t m_waitCountdown = 0; /**< Current wait in ticks */
|
||||
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) */
|
||||
|
||||
Channel(SongState& parent, uint8_t midiChan, uint32_t startTick,
|
||||
const unsigned char* song, const unsigned char* chan);
|
||||
Track() = default;
|
||||
Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions);
|
||||
operator bool() const { return m_parent != nullptr; }
|
||||
void setRegion(Sequencer* seq, const TrackRegion* region);
|
||||
void advanceRegion(Sequencer* seq);
|
||||
bool advance(Sequencer& seq, int32_t ticks);
|
||||
};
|
||||
std::array<std::experimental::optional<Channel>, 64> m_channels;
|
||||
std::array<Track, 64> m_tracks;
|
||||
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
|
||||
|
||||
/** Current pointer to tempo control, iterated over playback */
|
||||
const TempoChange* m_tempoPtr = nullptr;
|
||||
uint32_t m_tempo = 120; /**< Current tempo (beats per minute) */
|
||||
|
||||
uint32_t m_curTick = 0; /**< Current playback position for all channels */
|
||||
uint32_t m_curTick = 0; /**< Current playback position for all channels */
|
||||
SongPlayState m_songState = SongPlayState::Playing; /**< High-level state of Song playback */
|
||||
double m_curDt = 0.f; /**< Cumulative dt value for time-remainder tracking */
|
||||
double m_curDt = 0.f; /**< Cumulative dt value for time-remainder tracking */
|
||||
|
||||
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);
|
||||
|
||||
/** initialize state for Song data at `ptr` */
|
||||
void initialize(const unsigned char* ptr);
|
||||
bool initialize(const unsigned char* ptr);
|
||||
|
||||
/** advances `dt` seconds worth of commands in the Song
|
||||
* @return `true` if END reached
|
||||
@@ -111,9 +121,8 @@ public:
|
||||
bool advance(Sequencer& seq, double dt);
|
||||
|
||||
/** Get current song tempo in BPM */
|
||||
uint32_t getTempo() const {return m_tempo;}
|
||||
uint32_t getTempo() const { return m_tempo; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SONGSTATE_HPP__
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#ifndef __AMUSE_SOUNDMACROSTATE_HPP__
|
||||
#define __AMUSE_SOUNDMACROSTATE_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include "Entity.hpp"
|
||||
#include "Common.hpp"
|
||||
|
||||
/* Squelch Win32 macro pollution >.< */
|
||||
#undef SendMessage
|
||||
#undef GetMessage
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -72,33 +77,33 @@ class SoundMacroState
|
||||
SendMessage,
|
||||
GetMessage,
|
||||
GetVid,
|
||||
AddAgeCount = 0x30,
|
||||
SetAgeCount,
|
||||
SendFlag,
|
||||
AddAgeCount = 0x30, /* unimplemented */
|
||||
SetAgeCount, /* unimplemented */
|
||||
SendFlag, /* unimplemented */
|
||||
PitchWheelR,
|
||||
SetPriority,
|
||||
AddPriority,
|
||||
AgeCntSpeed,
|
||||
AgeCntVel,
|
||||
SetPriority = 0x36, /* unimplemented */
|
||||
AddPriority, /* unimplemented */
|
||||
AgeCntSpeed, /* unimplemented */
|
||||
AgeCntVel, /* unimplemented */
|
||||
VolSelect = 0x40,
|
||||
PanSelect,
|
||||
PitchWheelSelect,
|
||||
ModWheelSelect,
|
||||
PedalSelect,
|
||||
PortamentoSelect,
|
||||
ReverbSelect,
|
||||
ReverbSelect, /* serves as PostASelect */
|
||||
SpanSelect,
|
||||
DopplerSelect,
|
||||
TremoloSelect,
|
||||
PreASelect,
|
||||
PreBSelect,
|
||||
PostBSelect,
|
||||
AuxAFXSelect,
|
||||
AuxBFXSelect,
|
||||
AuxAFXSelect, /* unimplemented */
|
||||
AuxBFXSelect, /* unimplemented */
|
||||
SetupLFO = 0x50,
|
||||
ModeSelect = 0x58,
|
||||
SetKeygroup,
|
||||
SRCmodeSelect,
|
||||
SRCmodeSelect, /* unimplemented */
|
||||
AddVars = 0x60,
|
||||
SubVars,
|
||||
MulVars,
|
||||
@@ -119,35 +124,46 @@ class SoundMacroState
|
||||
/** 'program counter' stack for the active SoundMacro */
|
||||
std::vector<std::pair<const unsigned char*, int>> m_pc;
|
||||
|
||||
static int _assertPC(int pc, uint32_t size);
|
||||
static int _assertPC(int pc, uint32_t size, bool swapSize)
|
||||
{
|
||||
return _assertPC(pc, swapSize ? SBig(size) : size);
|
||||
}
|
||||
|
||||
void _setPC(int pc)
|
||||
{
|
||||
m_pc.back().second = _assertPC(pc, m_header.m_size);
|
||||
}
|
||||
|
||||
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) */
|
||||
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 */
|
||||
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_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_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_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 */
|
||||
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 */
|
||||
|
||||
uint8_t m_portamentoMode = 2; /**< (0: Off, 1: On, 2: MIDI specified) */
|
||||
uint8_t m_portamentoType = 0; /**< (0: New key pressed while old key pressed, 1: Always) */
|
||||
uint8_t m_portamentoMode = 2; /**< (0: Off, 1: On, 2: MIDI specified) */
|
||||
uint8_t m_portamentoType = 0; /**< (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 */
|
||||
@@ -174,19 +190,20 @@ class SoundMacroState
|
||||
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) {}
|
||||
: 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);
|
||||
void addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType);
|
||||
|
||||
/** Calculate value */
|
||||
float evaluate(const Voice& vox, const SoundMacroState& st) const;
|
||||
float evaluate(double time, const Voice& vox, const SoundMacroState& st) const;
|
||||
|
||||
/** Determine if able to use */
|
||||
operator bool() const {return m_comps.size() != 0;}
|
||||
operator bool() const { return m_comps.size() != 0; }
|
||||
};
|
||||
|
||||
Evaluator m_volumeSel;
|
||||
@@ -217,8 +234,8 @@ class SoundMacroState
|
||||
public:
|
||||
/** initialize state for SoundMacro data at `ptr` */
|
||||
void initialize(const unsigned char* ptr, int step, bool swapData);
|
||||
void initialize(const unsigned char* ptr, int step, double ticksPerSec,
|
||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool swapData);
|
||||
void initialize(const unsigned char* ptr, int step, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
||||
uint8_t midiMod, bool swapData);
|
||||
|
||||
/** advances `dt` seconds worth of commands in the SoundMacro
|
||||
* @return `true` if END reached
|
||||
@@ -231,7 +248,6 @@ public:
|
||||
/** sample end event */
|
||||
void sampleEndNotify(Voice& vox);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SOUNDMACROSTATE_HPP__
|
||||
|
||||
54
include/amuse/Studio.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef __AMUSE_STUDIO_HPP__
|
||||
#define __AMUSE_STUDIO_HPP__
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include "Entity.hpp"
|
||||
#include "Voice.hpp"
|
||||
#include "Submix.hpp"
|
||||
#include <type_traits>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
class Studio
|
||||
{
|
||||
friend class Engine;
|
||||
Engine& m_engine;
|
||||
Submix m_master;
|
||||
Submix m_auxA;
|
||||
Submix m_auxB;
|
||||
struct StudioSend
|
||||
{
|
||||
std::shared_ptr<Studio> m_targetStudio;
|
||||
float m_dryLevel;
|
||||
float m_auxALevel;
|
||||
float m_auxBLevel;
|
||||
StudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB)
|
||||
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB)
|
||||
{
|
||||
}
|
||||
};
|
||||
std::list<StudioSend> m_studiosOut;
|
||||
#ifndef NDEBUG
|
||||
bool _cyclicCheck(Studio* leaf);
|
||||
#endif
|
||||
|
||||
public:
|
||||
Studio(Engine& engine, bool mainOut);
|
||||
|
||||
/** Register a target Studio to send this Studio's mixing busses */
|
||||
void addStudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB);
|
||||
|
||||
/** 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; }
|
||||
|
||||
Engine& getEngine() { return m_engine; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_STUDIO_HPP__
|
||||
@@ -23,27 +23,14 @@ class Submix
|
||||
friend class Voice;
|
||||
friend class Sequencer;
|
||||
Engine& m_root;
|
||||
Submix* m_submix = nullptr; /**< Parent submix of this submix (or NULL if mixing to main output) */
|
||||
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
|
||||
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
|
||||
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
|
||||
bool m_destroyed = false;
|
||||
void _destroy();
|
||||
|
||||
public:
|
||||
Submix(Engine& engine, Submix* smx);
|
||||
~Submix()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* Ensure proper destruction procedure followed */
|
||||
assert(m_destroyed);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Obtain pointer to Submix's parent Submix */
|
||||
Submix* getParentSubmix() {return m_submix;}
|
||||
Submix(Engine& engine);
|
||||
|
||||
/** Add new effect to effect stack and assume ownership */
|
||||
template <class T, class ...Args>
|
||||
template <class T, class... Args>
|
||||
T& makeEffect(Args... args)
|
||||
{
|
||||
switch (m_backendSubmix->getSampleFormat())
|
||||
@@ -73,22 +60,33 @@ public:
|
||||
/** Add new chorus effect to effect stack and assume ownership */
|
||||
EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period);
|
||||
|
||||
/** 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(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput);
|
||||
|
||||
/** 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(float coloration, float mix, float time,
|
||||
float damping, float preDelay);
|
||||
EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay);
|
||||
|
||||
/** 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(float coloration, float mix, float time,
|
||||
float damping, float preDelay, float crosstalk);
|
||||
EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay,
|
||||
float crosstalk);
|
||||
|
||||
/** Add new high-quality reverb effect to effect stack and assume ownership */
|
||||
EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info);
|
||||
|
||||
/** Remove and deallocate all effects from effect stack */
|
||||
void clearEffects() {m_effectStack.clear();}
|
||||
void clearEffects() { m_effectStack.clear(); }
|
||||
|
||||
/** Returns true when an effect callback is bound */
|
||||
bool canApplyEffect() const {return m_effectStack.size() != 0;}
|
||||
bool canApplyEffect() const { return m_effectStack.size() != 0; }
|
||||
|
||||
/** in/out transformation entry for audio effect */
|
||||
void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const;
|
||||
@@ -102,9 +100,8 @@ public:
|
||||
/** advice effects of changing sample rate */
|
||||
void resetOutputSampleRate(double sampleRate);
|
||||
|
||||
Engine& getEngine() {return m_root;}
|
||||
Engine& getEngine() { return m_root; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SUBMIX_HPP__
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef __AMUSE_SURROUNDPROFILES_HPP__
|
||||
#define __AMUSE_SURROUNDPROFILES_HPP__
|
||||
|
||||
#include "IBackendVoice.hpp"
|
||||
#include "IBackendVoiceAllocator.hpp"
|
||||
#include "Emitter.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
struct ReferenceVector;
|
||||
|
||||
/** Support class for attenuating channel audio based on speaker 'positions' */
|
||||
class SurroundProfiles
|
||||
{
|
||||
static void SetupRefs(float matOut[8], const ChannelMap& map,
|
||||
const Vector3f& listenEmit, const ReferenceVector refs[]);
|
||||
public:
|
||||
static void SetupMatrix(float matOut[8], const ChannelMap& map, AudioChannelSet set,
|
||||
const Vector3f& emitPos, const Vector3f& listenPos,
|
||||
const Vector3f& listenDir, const Vector3f& listenUp);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SURROUNDPROFILES_HPP__
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef __AMUSE_VOICE_HPP__
|
||||
#define __AMUSE_VOICE_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include "SoundMacroState.hpp"
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace amuse
|
||||
{
|
||||
class IBackendVoice;
|
||||
class Submix;
|
||||
class Studio;
|
||||
struct Keymap;
|
||||
struct LayerMapping;
|
||||
|
||||
@@ -22,8 +22,8 @@ struct LayerMapping;
|
||||
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 */
|
||||
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 */
|
||||
@@ -33,111 +33,123 @@ class Voice : public Entity
|
||||
friend class Sequencer;
|
||||
friend class SoundMacroState;
|
||||
friend class Envelope;
|
||||
int m_vid; /**< VoiceID of this voice instance */
|
||||
bool m_emitter; /**< Voice is part of an Emitter */
|
||||
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
|
||||
friend class Emitter;
|
||||
int m_vid; /**< VoiceID of this voice instance */
|
||||
bool m_emitter; /**< Voice is part of an Emitter */
|
||||
std::shared_ptr<Studio> m_studio; /**< Studio this voice outputs to */
|
||||
|
||||
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 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 */
|
||||
std::list<int32_t> m_messageQueue; /**< Messages pending processing for SoundMacros in this voice */
|
||||
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
|
||||
std::list<int32_t> m_messageQueue; /**< Messages pending processing for SoundMacros in this voice */
|
||||
std::list<std::shared_ptr<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
|
||||
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
||||
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
|
||||
|
||||
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) */
|
||||
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) */
|
||||
};
|
||||
const Sample* m_curSample = nullptr; /**< Current sample entry playing */
|
||||
const Sample* m_curSample = nullptr; /**< 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_sampleRate = 32000.0; /**< 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 */
|
||||
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 */
|
||||
|
||||
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') */
|
||||
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_userPan = 0.f; /**< User pan of voice */
|
||||
float m_curPan = 0.f; /**< Current pan of voice */
|
||||
float m_userSpan = 0.f; /**< User span of voice */
|
||||
float m_curSpan = 0.f; /**< Current surround pan of voice */
|
||||
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
|
||||
int32_t m_pitchWheelUp = 600; /**< Up range for pitchwheel control in cents */
|
||||
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_userPan = 0.f; /**< User pan of voice */
|
||||
float m_curPan = 0.f; /**< Current pan of voice */
|
||||
float m_userSpan = -1.f; /**< User span 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 = 600; /**< Up range for pitchwheel control in cents */
|
||||
int32_t m_pitchWheelDown = 600; /**< 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 */
|
||||
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 */
|
||||
|
||||
Envelope m_volAdsr; /**< Volume envelope */
|
||||
Envelope m_volAdsr; /**< Volume envelope */
|
||||
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
|
||||
double m_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 */
|
||||
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 */
|
||||
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 */
|
||||
int32_t m_portamentoTarget; /**< destination pitch for latest portamento invocation */
|
||||
|
||||
uint32_t m_pitchSweep1 = 0; /**< Current value of PITCHSWEEP1 controller (in cents) */
|
||||
uint32_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 */
|
||||
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 */
|
||||
uint8_t m_pitchSweep1It = 0; /**< Current iteration of PITCHSWEEP1 controller */
|
||||
uint8_t m_pitchSweep2It = 0; /**< Current iteration of PITCHSWEEP2 controller */
|
||||
|
||||
float m_panningTime = -1.f; /**< time since last PANNING command, -1 for no active pan-sweep */
|
||||
float m_panningDur; /**< requested duration of last PANNING command */
|
||||
uint8_t m_panPos; /**< initial pan value of last PANNING command */
|
||||
int8_t m_panWidth; /**< delta pan value to target of last PANNING command */
|
||||
float m_panningDur; /**< requested duration of last PANNING command */
|
||||
uint8_t m_panPos; /**< initial pan value of last PANNING command */
|
||||
int8_t m_panWidth; /**< delta pan value to target of last PANNING command */
|
||||
|
||||
float m_spanningTime = -1.f; /**< time since last SPANNING command, -1 for no active span-sweep */
|
||||
float m_spanningDur; /**< requested duration of last SPANNING command */
|
||||
uint8_t m_spanPos; /**< initial pan value of last SPANNING command */
|
||||
int8_t m_spanWidth; /**< delta pan value to target of last SPANNING command */
|
||||
float m_spanningDur; /**< requested duration of last SPANNING command */
|
||||
uint8_t m_spanPos; /**< initial pan value of last SPANNING command */
|
||||
int8_t m_spanWidth; /**< delta pan value to target of last SPANNING command */
|
||||
|
||||
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 */
|
||||
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_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 */
|
||||
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) */
|
||||
int8_t* m_extCtrlVals = nullptr; /**< MIDI Controller values (external storage) */
|
||||
|
||||
void _destroy();
|
||||
bool _checkSamplePos(bool& looped);
|
||||
void _doKeyOff();
|
||||
void _macroKeyOff();
|
||||
void _macroSampleEnd();
|
||||
bool _advanceSample(int16_t& samp, int32_t& curPitch);
|
||||
void _procSamplePre(int16_t& samp);
|
||||
template <typename T>
|
||||
T _procSampleMaster(double time, T samp);
|
||||
template <typename T>
|
||||
T _procSampleAuxA(double time, T samp);
|
||||
template <typename T>
|
||||
T _procSampleAuxB(double time, T samp);
|
||||
void _setTotalPitch(int32_t cents, bool slew);
|
||||
bool _isRecursivelyDead();
|
||||
void _bringOutYourDead();
|
||||
@@ -148,35 +160,50 @@ class Voice : public Entity
|
||||
std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
|
||||
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
|
||||
|
||||
bool _loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec,
|
||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||
bool _loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec,
|
||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||
bool _loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec,
|
||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec,
|
||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
|
||||
bool _loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec, uint8_t midiKey,
|
||||
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
||||
bool _loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
||||
uint8_t midiMod, bool pushPc = false);
|
||||
bool _loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec, uint8_t midiKey,
|
||||
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
||||
std::shared_ptr<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);
|
||||
|
||||
public:
|
||||
~Voice();
|
||||
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx);
|
||||
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx);
|
||||
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr<Studio> studio);
|
||||
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter,
|
||||
std::weak_ptr<Studio> studio);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Obtain pointer to Voice's Submix */
|
||||
Submix* getSubmix() {return m_submix;}
|
||||
/** 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 */
|
||||
std::shared_ptr<Studio> getStudio() { return m_studio; }
|
||||
|
||||
/** Get current state of voice */
|
||||
VoiceState state() const {return m_voxState;}
|
||||
VoiceState state() const { return m_voxState; }
|
||||
|
||||
/** Get VoiceId of this voice (unique to all currently-playing voices) */
|
||||
int vid() const {return m_vid;}
|
||||
int vid() const { return m_vid; }
|
||||
|
||||
/** Get max VoiceId of this voice and any contained children */
|
||||
int maxVid() const;
|
||||
@@ -185,9 +212,8 @@ public:
|
||||
std::shared_ptr<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
|
||||
|
||||
/** Load specified Sound Object from within group into voice */
|
||||
bool loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec,
|
||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod,
|
||||
bool pushPc=false);
|
||||
bool loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
|
||||
uint8_t midiMod, bool pushPc = false);
|
||||
|
||||
/** Signals voice to begin fade-out (or defer if sustained), eventually reaching silence */
|
||||
void keyOff();
|
||||
@@ -210,6 +236,9 @@ public:
|
||||
/** Set current voice surround-panning immediately */
|
||||
void setSurroundPan(float span);
|
||||
|
||||
/** 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);
|
||||
|
||||
@@ -232,7 +261,7 @@ public:
|
||||
void setDoppler(float doppler);
|
||||
|
||||
/** Set vibrato parameters for voice */
|
||||
void setVibrato(int32_t level, int32_t modLevel, float period);
|
||||
void setVibrato(int32_t level, bool modScale, float period);
|
||||
|
||||
/** Configure modwheel influence range over vibrato */
|
||||
void setMod2VibratoRange(int32_t modLevel);
|
||||
@@ -241,10 +270,10 @@ public:
|
||||
void setTremolo(float tremoloScale, float tremoloModScale);
|
||||
|
||||
/** Setup LFO1 for voice */
|
||||
void setLFO1Period(float period) {m_lfoPeriods[0] = period;}
|
||||
void setLFO1Period(float period) { m_lfoPeriods[0] = period; }
|
||||
|
||||
/** Setup LFO2 for voice */
|
||||
void setLFO2Period(float period) {m_lfoPeriods[1] = period;}
|
||||
void setLFO2Period(float period) { m_lfoPeriods[1] = period; }
|
||||
|
||||
/** Setup pitch sweep controller 1 */
|
||||
void setPitchSweep1(uint8_t times, int16_t add);
|
||||
@@ -255,6 +284,9 @@ public:
|
||||
/** Set reverb mix for voice */
|
||||
void setReverbVol(float rvol);
|
||||
|
||||
/** Set AuxB volume for voice */
|
||||
void setAuxBVol(float bvol);
|
||||
|
||||
/** Set envelope for voice */
|
||||
void setAdsr(ObjectId adsrId, bool dls);
|
||||
|
||||
@@ -274,10 +306,10 @@ public:
|
||||
void setAftertouch(uint8_t aftertouch);
|
||||
|
||||
/** Assign voice to keygroup for coordinated mass-silencing */
|
||||
void setKeygroup(uint8_t kg) {m_keygroup = kg;}
|
||||
void setKeygroup(uint8_t kg) { m_keygroup = kg; }
|
||||
|
||||
/** Get note played on voice */
|
||||
uint8_t getLastNote() const {return m_state.m_initKey;}
|
||||
uint8_t getLastNote() const { return m_state.m_initKey; }
|
||||
|
||||
/** Do portamento glide; returns `false` if portamento disabled */
|
||||
bool doPortamento(uint8_t newNote);
|
||||
@@ -288,7 +320,7 @@ public:
|
||||
if (!m_extCtrlVals)
|
||||
{
|
||||
if (m_ctrlValsSelf)
|
||||
m_ctrlValsSelf[ctrl];
|
||||
return m_ctrlValsSelf[ctrl];
|
||||
return 0;
|
||||
}
|
||||
return m_extCtrlVals[ctrl];
|
||||
@@ -307,12 +339,6 @@ public:
|
||||
_notifyCtrlChange(ctrl, val);
|
||||
}
|
||||
|
||||
/** Get ModWheel value on voice */
|
||||
int8_t getModWheel() const
|
||||
{
|
||||
return m_state.m_modWheelSel ? m_state.m_modWheelSel.evaluate(*this, m_state) : getCtrlValue(1);
|
||||
}
|
||||
|
||||
/** 'install' external MIDI controller storage */
|
||||
void installCtrlValues(int8_t* cvs)
|
||||
{
|
||||
@@ -321,19 +347,17 @@ public:
|
||||
}
|
||||
|
||||
/** Get MIDI pitch wheel value on voice */
|
||||
int8_t getPitchWheel() const {return m_curPitchWheel * 127;}
|
||||
float getPitchWheel() const { return m_curPitchWheel; }
|
||||
|
||||
/** Get MIDI aftertouch value on voice */
|
||||
int8_t getAftertouch() const {return m_curAftertouch;}
|
||||
int8_t getAftertouch() const { return m_curAftertouch; }
|
||||
|
||||
/** Get count of all voices in hierarchy, including this one */
|
||||
size_t getTotalVoices() const;
|
||||
|
||||
/** Recursively mark voice as dead for Engine to deallocate on next cycle */
|
||||
void kill();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_VOICE_HPP__
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef __AMUSE_AMUSE_HPP__
|
||||
#define __AMUSE_AMUSE_HPP__
|
||||
|
||||
|
||||
#include "AudioGroup.hpp"
|
||||
#include "AudioGroupData.hpp"
|
||||
#include "AudioGroupPool.hpp"
|
||||
@@ -16,6 +17,8 @@
|
||||
#include "Listener.hpp"
|
||||
#include "Sequencer.hpp"
|
||||
#include "SoundMacroState.hpp"
|
||||
#include "SongConverter.hpp"
|
||||
#include "SongState.hpp"
|
||||
#include "Submix.hpp"
|
||||
#include "Voice.hpp"
|
||||
|
||||
|
||||
@@ -5,28 +5,31 @@ namespace amuse
|
||||
{
|
||||
|
||||
AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag)
|
||||
: m_proj(data.getProj(), GCNDataTag{}),
|
||||
m_pool(data.getPool()),
|
||||
m_sdir(data.getSdir(), GCNDataTag{}),
|
||||
m_samp(data.getSamp()),
|
||||
m_fmt(DataFormat::GCN)
|
||||
{}
|
||||
: m_proj(data.getProj(), GCNDataTag{})
|
||||
, m_pool(data.getPool())
|
||||
, m_sdir(data.getSdir(), GCNDataTag{})
|
||||
, m_samp(data.getSamp())
|
||||
, m_fmt(DataFormat::GCN)
|
||||
{
|
||||
}
|
||||
|
||||
AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag)
|
||||
: m_proj(data.getProj(), absOffs, N64DataTag{}),
|
||||
m_pool(data.getPool()),
|
||||
m_sdir(data.getSdir(), data.getSamp(), absOffs, N64DataTag{}),
|
||||
m_samp(data.getSamp()),
|
||||
m_fmt(DataFormat::N64)
|
||||
{}
|
||||
: m_proj(data.getProj(), absOffs, N64DataTag{})
|
||||
, m_pool(data.getPool())
|
||||
, m_sdir(data.getSdir(), data.getSamp(), absOffs, N64DataTag{})
|
||||
, m_samp(data.getSamp())
|
||||
, m_fmt(DataFormat::N64)
|
||||
{
|
||||
}
|
||||
|
||||
AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag)
|
||||
: m_proj(data.getProj(), absOffs, PCDataTag{}),
|
||||
m_pool(data.getPool(), PCDataTag{}),
|
||||
m_sdir(data.getSdir(), absOffs, PCDataTag{}),
|
||||
m_samp(data.getSamp()),
|
||||
m_fmt(DataFormat::PC)
|
||||
{}
|
||||
: m_proj(data.getProj(), absOffs, PCDataTag{})
|
||||
, m_pool(data.getPool(), PCDataTag{})
|
||||
, m_sdir(data.getSdir(), absOffs, PCDataTag{})
|
||||
, m_samp(data.getSamp())
|
||||
, m_fmt(DataFormat::PC)
|
||||
{
|
||||
}
|
||||
|
||||
const Sample* AudioGroup::getSample(int sfxId) const
|
||||
{
|
||||
@@ -36,9 +39,5 @@ const Sample* AudioGroup::getSample(int sfxId) const
|
||||
return &search->second;
|
||||
}
|
||||
|
||||
const unsigned char* AudioGroup::getSampleData(uint32_t offset) const
|
||||
{
|
||||
return m_samp + offset;
|
||||
}
|
||||
|
||||
const unsigned char* AudioGroup::getSampleData(uint32_t offset) const { return m_samp + offset; }
|
||||
}
|
||||
|
||||
@@ -7,30 +7,29 @@ IntrusiveAudioGroupData::~IntrusiveAudioGroupData()
|
||||
{
|
||||
if (m_owns)
|
||||
{
|
||||
delete m_pool;
|
||||
delete m_proj;
|
||||
delete m_sdir;
|
||||
delete m_samp;
|
||||
delete[] m_pool;
|
||||
delete[] m_proj;
|
||||
delete[] m_sdir;
|
||||
delete[] m_samp;
|
||||
}
|
||||
}
|
||||
|
||||
IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other)
|
||||
: 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)
|
||||
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;
|
||||
}
|
||||
|
||||
IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupData&& other)
|
||||
IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupData&& other) noexcept
|
||||
{
|
||||
if (m_owns)
|
||||
{
|
||||
delete m_pool;
|
||||
delete m_proj;
|
||||
delete m_sdir;
|
||||
delete m_samp;
|
||||
delete[] m_pool;
|
||||
delete[] m_proj;
|
||||
delete[] m_sdir;
|
||||
delete[] m_samp;
|
||||
}
|
||||
|
||||
m_owns = other.m_owns;
|
||||
@@ -45,5 +44,4 @@ IntrusiveAudioGroupData& IntrusiveAudioGroupData::operator=(IntrusiveAudioGroupD
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,10 +70,10 @@ AudioGroupPool::AudioGroupPool(const unsigned char* data)
|
||||
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4));
|
||||
std::vector<const LayerMapping*>& mappingsOut = m_layers[id];
|
||||
|
||||
uint32_t count = SBig(*reinterpret_cast<const uint32_t*>(cur+8));
|
||||
uint32_t count = SBig(*reinterpret_cast<const uint32_t*>(cur + 8));
|
||||
mappingsOut.reserve(count);
|
||||
const unsigned char* subcur = cur + 12;
|
||||
for (int i=0 ; i<count ; ++i)
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
mappingsOut.push_back(reinterpret_cast<const LayerMapping*>(subcur + i * 12));
|
||||
|
||||
cur += size;
|
||||
@@ -130,10 +130,10 @@ AudioGroupPool::AudioGroupPool(const unsigned char* data, PCDataTag)
|
||||
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
|
||||
std::vector<const LayerMapping*>& mappingsOut = m_layers[id];
|
||||
|
||||
uint32_t count = *reinterpret_cast<const uint32_t*>(cur+8);
|
||||
uint32_t count = *reinterpret_cast<const uint32_t*>(cur + 8);
|
||||
mappingsOut.reserve(count);
|
||||
const unsigned char* subcur = cur + 12;
|
||||
for (int i=0 ; i<count ; ++i)
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
mappingsOut.push_back(reinterpret_cast<const LayerMapping*>(subcur + i * 12));
|
||||
|
||||
cur += size;
|
||||
@@ -172,5 +172,4 @@ const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const
|
||||
return nullptr;
|
||||
return reinterpret_cast<const ADSR*>(search->second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, GCNDataTag)
|
||||
idx.m_sfxEntries.reserve(count);
|
||||
const SFXGroupIndex::SFXEntry* entries =
|
||||
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff + 4);
|
||||
for (int i=0 ; i<count ; ++i)
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
|
||||
++entries;
|
||||
@@ -152,7 +152,7 @@ struct MusyX1MIDISetup
|
||||
}
|
||||
};
|
||||
|
||||
void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool absOffs, N64DataTag)
|
||||
void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataTag)
|
||||
{
|
||||
size_t normPageCount = 0;
|
||||
size_t drumPageCount = 0;
|
||||
@@ -161,7 +161,7 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool abs
|
||||
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
|
||||
while (group->groupEndOff != 0xffffffff)
|
||||
{
|
||||
const unsigned char* subData = absOffs ? data : data + 8;
|
||||
const unsigned char* subData = data + 8;
|
||||
GroupHeader header = *group;
|
||||
header.swapBig();
|
||||
|
||||
@@ -187,20 +187,16 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool abs
|
||||
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = subData + header.midiSetupsOff;
|
||||
while (*reinterpret_cast<const uint32_t*>(setupData) != 0xffffffff)
|
||||
const uint8_t* setupEnd = subData + header.groupEndOff;
|
||||
while (setupData < setupEnd)
|
||||
{
|
||||
++midiSetupCount;
|
||||
setupData += 8 * 16 + 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (absOffs)
|
||||
group = reinterpret_cast<const GroupHeader*>(data + header.groupEndOff);
|
||||
else
|
||||
{
|
||||
data += header.groupEndOff;
|
||||
group = reinterpret_cast<const GroupHeader*>(data);
|
||||
}
|
||||
data += header.groupEndOff;
|
||||
group = reinterpret_cast<const GroupHeader*>(data);
|
||||
}
|
||||
|
||||
if (normPageCount)
|
||||
@@ -208,12 +204,13 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool abs
|
||||
if (drumPageCount)
|
||||
m_convDrumPages.reset(new SongGroupIndex::PageEntry[drumPageCount]);
|
||||
if (midiSetupCount)
|
||||
m_convMidiSetups.reset(new std::array<SongGroupIndex::MIDISetup, 16>[midiSetupCount]);
|
||||
m_convMidiSetups.reset(new std::array<SongGroupIndex::MIDISetup, 16>[ midiSetupCount ]);
|
||||
}
|
||||
|
||||
AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, N64DataTag)
|
||||
{
|
||||
_allocateConvBuffers(data, absOffs, N64DataTag{});
|
||||
if (!absOffs)
|
||||
_allocateConvBuffers(data, N64DataTag{});
|
||||
SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get();
|
||||
SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get();
|
||||
std::array<SongGroupIndex::MIDISetup, 16>* midiSetupsBuf = m_convMidiSetups.get();
|
||||
@@ -232,42 +229,77 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, N6
|
||||
SongGroupIndex& idx = m_songGroups[header.groupId];
|
||||
bIdx = &idx;
|
||||
|
||||
/* Normal pages */
|
||||
const MusyX1PageEntry* normEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + header.pageTableOff);
|
||||
while (normEntries->objId != 0xffff)
|
||||
if (absOffs)
|
||||
{
|
||||
normEntries->setIntoMusyX2(*normPagesBuf);
|
||||
idx.m_normPages[normEntries->programNo] = normPagesBuf;
|
||||
++normEntries;
|
||||
++normPagesBuf;
|
||||
/* Normal pages */
|
||||
const SongGroupIndex::PageEntry* normEntries =
|
||||
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.pageTableOff);
|
||||
while (normEntries->objId != 0xffff)
|
||||
{
|
||||
idx.m_normPages[normEntries->programNo] = normEntries;
|
||||
++normEntries;
|
||||
}
|
||||
|
||||
/* Drum pages */
|
||||
const SongGroupIndex::PageEntry* drumEntries =
|
||||
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.drumTableOff);
|
||||
while (drumEntries->objId != 0xffff)
|
||||
{
|
||||
idx.m_drumPages[drumEntries->programNo] = drumEntries;
|
||||
++drumEntries;
|
||||
}
|
||||
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = data + header.midiSetupsOff;
|
||||
const uint8_t* setupEnd = data + header.groupEndOff;
|
||||
while (setupData < setupEnd)
|
||||
{
|
||||
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
|
||||
idx.m_midiSetups[songId] =
|
||||
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4);
|
||||
setupData += 5 * 16 + 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drum pages */
|
||||
const MusyX1PageEntry* drumEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + header.drumTableOff);
|
||||
while (drumEntries->objId != 0xffff)
|
||||
else
|
||||
{
|
||||
drumEntries->setIntoMusyX2(*drumPagesBuf);
|
||||
idx.m_drumPages[drumEntries->programNo] = drumPagesBuf;
|
||||
++drumEntries;
|
||||
++drumPagesBuf;
|
||||
}
|
||||
/* Normal pages */
|
||||
const MusyX1PageEntry* normEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + header.pageTableOff);
|
||||
while (normEntries->objId != 0xffff)
|
||||
{
|
||||
normEntries->setIntoMusyX2(*normPagesBuf);
|
||||
idx.m_normPages[normEntries->programNo] = normPagesBuf;
|
||||
++normEntries;
|
||||
++normPagesBuf;
|
||||
}
|
||||
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = subData + header.midiSetupsOff;
|
||||
while (*reinterpret_cast<const uint32_t*>(setupData) != 0xffffffff)
|
||||
{
|
||||
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
|
||||
const std::array<MusyX1MIDISetup, 16>* midiSetups =
|
||||
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(setupData + 4);
|
||||
/* Drum pages */
|
||||
const MusyX1PageEntry* drumEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + header.drumTableOff);
|
||||
while (drumEntries->objId != 0xffff)
|
||||
{
|
||||
drumEntries->setIntoMusyX2(*drumPagesBuf);
|
||||
idx.m_drumPages[drumEntries->programNo] = drumPagesBuf;
|
||||
++drumEntries;
|
||||
++drumPagesBuf;
|
||||
}
|
||||
|
||||
for (int i=0 ; i<16 ; ++i)
|
||||
(*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]);
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = subData + header.midiSetupsOff;
|
||||
const uint8_t* setupEnd = subData + header.groupEndOff;
|
||||
while (setupData < setupEnd)
|
||||
{
|
||||
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
|
||||
const std::array<MusyX1MIDISetup, 16>* midiSetups =
|
||||
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(setupData + 4);
|
||||
|
||||
idx.m_midiSetups[songId] = midiSetupsBuf;
|
||||
setupData += 8 * 16 + 4;
|
||||
++midiSetupsBuf;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
(*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]);
|
||||
|
||||
idx.m_midiSetups[songId] = midiSetupsBuf;
|
||||
setupData += 8 * 16 + 4;
|
||||
++midiSetupsBuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (header.type == GroupType::SFX)
|
||||
@@ -278,7 +310,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, N6
|
||||
/* SFX entries */
|
||||
uint16_t count = SBig(*reinterpret_cast<const uint16_t*>(subData + header.pageTableOff));
|
||||
idx.m_sfxEntries.reserve(count);
|
||||
for (int i=0 ; i<count ; ++i)
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
const SFXGroupIndex::SFXEntry* entries =
|
||||
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + header.pageTableOff + 4 + i * 12);
|
||||
@@ -304,7 +336,7 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, N6
|
||||
}
|
||||
}
|
||||
|
||||
void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool absOffs, PCDataTag)
|
||||
void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTag)
|
||||
{
|
||||
size_t normPageCount = 0;
|
||||
size_t drumPageCount = 0;
|
||||
@@ -313,13 +345,13 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool abs
|
||||
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
|
||||
while (group->groupEndOff != 0xffffffff)
|
||||
{
|
||||
const unsigned char* subData = absOffs ? data : data + 8;
|
||||
const unsigned char* subData = data + 8;
|
||||
|
||||
if (group->type == GroupType::Song)
|
||||
{
|
||||
/* Normal pages */
|
||||
const MusyX1PageEntry* normEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->pageTableOff);
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->pageTableOff);
|
||||
while (normEntries->objId != 0xffff)
|
||||
{
|
||||
++normPageCount;
|
||||
@@ -328,7 +360,7 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool abs
|
||||
|
||||
/* Drum pages */
|
||||
const MusyX1PageEntry* drumEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->drumTableOff);
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->drumTableOff);
|
||||
while (drumEntries->objId != 0xffff)
|
||||
{
|
||||
++drumPageCount;
|
||||
@@ -337,20 +369,16 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool abs
|
||||
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = subData + group->midiSetupsOff;
|
||||
while (*reinterpret_cast<const uint32_t*>(setupData) != 0xffffffff)
|
||||
const uint8_t* setupEnd = subData + group->groupEndOff;
|
||||
while (setupData < setupEnd)
|
||||
{
|
||||
++midiSetupCount;
|
||||
setupData += 8 * 16 + 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (absOffs)
|
||||
group = reinterpret_cast<const GroupHeader*>(data + group->groupEndOff);
|
||||
else
|
||||
{
|
||||
data += group->groupEndOff;
|
||||
group = reinterpret_cast<const GroupHeader*>(data);
|
||||
}
|
||||
data += group->groupEndOff;
|
||||
group = reinterpret_cast<const GroupHeader*>(data);
|
||||
}
|
||||
|
||||
if (normPageCount)
|
||||
@@ -358,12 +386,13 @@ void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, bool abs
|
||||
if (drumPageCount)
|
||||
m_convDrumPages.reset(new SongGroupIndex::PageEntry[drumPageCount]);
|
||||
if (midiSetupCount)
|
||||
m_convMidiSetups.reset(new std::array<SongGroupIndex::MIDISetup, 16>[midiSetupCount]);
|
||||
m_convMidiSetups.reset(new std::array<SongGroupIndex::MIDISetup, 16>[ midiSetupCount ]);
|
||||
}
|
||||
|
||||
AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, PCDataTag)
|
||||
{
|
||||
_allocateConvBuffers(data, absOffs, PCDataTag{});
|
||||
if (!absOffs)
|
||||
_allocateConvBuffers(data, PCDataTag{});
|
||||
SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get();
|
||||
SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get();
|
||||
std::array<SongGroupIndex::MIDISetup, 16>* midiSetupsBuf = m_convMidiSetups.get();
|
||||
@@ -380,42 +409,77 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, PC
|
||||
SongGroupIndex& idx = m_songGroups[group->groupId];
|
||||
bIdx = &idx;
|
||||
|
||||
/* Normal pages */
|
||||
const MusyX1PageEntry* normEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->pageTableOff);
|
||||
while (normEntries->objId != 0xffff)
|
||||
if (absOffs)
|
||||
{
|
||||
normEntries->setIntoMusyX2(*normPagesBuf);
|
||||
idx.m_normPages[normEntries->programNo] = normPagesBuf;
|
||||
++normEntries;
|
||||
++normPagesBuf;
|
||||
/* Normal pages */
|
||||
const SongGroupIndex::PageEntry* normEntries =
|
||||
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + group->pageTableOff);
|
||||
while (normEntries->objId != 0xffff)
|
||||
{
|
||||
idx.m_normPages[normEntries->programNo] = normEntries;
|
||||
++normEntries;
|
||||
}
|
||||
|
||||
/* Drum pages */
|
||||
const SongGroupIndex::PageEntry* drumEntries =
|
||||
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + group->drumTableOff);
|
||||
while (drumEntries->objId != 0xffff)
|
||||
{
|
||||
idx.m_drumPages[drumEntries->programNo] = drumEntries;
|
||||
++drumEntries;
|
||||
}
|
||||
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = data + group->midiSetupsOff;
|
||||
const uint8_t* setupEnd = data + group->groupEndOff;
|
||||
while (setupData < setupEnd)
|
||||
{
|
||||
uint16_t songId = *reinterpret_cast<const uint16_t*>(setupData);
|
||||
idx.m_midiSetups[songId] =
|
||||
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4);
|
||||
setupData += 5 * 16 + 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drum pages */
|
||||
const MusyX1PageEntry* drumEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->drumTableOff);
|
||||
while (drumEntries->objId != 0xffff)
|
||||
else
|
||||
{
|
||||
drumEntries->setIntoMusyX2(*drumPagesBuf);
|
||||
idx.m_drumPages[drumEntries->programNo] = drumPagesBuf;
|
||||
++drumEntries;
|
||||
++drumPagesBuf;
|
||||
}
|
||||
/* Normal pages */
|
||||
const MusyX1PageEntry* normEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->pageTableOff);
|
||||
while (normEntries->objId != 0xffff)
|
||||
{
|
||||
normEntries->setIntoMusyX2(*normPagesBuf);
|
||||
idx.m_normPages[normEntries->programNo] = normPagesBuf;
|
||||
++normEntries;
|
||||
++normPagesBuf;
|
||||
}
|
||||
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = subData + group->midiSetupsOff;
|
||||
while (*reinterpret_cast<const uint32_t*>(setupData) != 0xffffffff)
|
||||
{
|
||||
uint16_t songId = *reinterpret_cast<const uint16_t*>(setupData);
|
||||
const std::array<MusyX1MIDISetup, 16>* midiSetups =
|
||||
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(setupData + 4);
|
||||
/* Drum pages */
|
||||
const MusyX1PageEntry* drumEntries =
|
||||
reinterpret_cast<const MusyX1PageEntry*>(subData + group->drumTableOff);
|
||||
while (drumEntries->objId != 0xffff)
|
||||
{
|
||||
drumEntries->setIntoMusyX2(*drumPagesBuf);
|
||||
idx.m_drumPages[drumEntries->programNo] = drumPagesBuf;
|
||||
++drumEntries;
|
||||
++drumPagesBuf;
|
||||
}
|
||||
|
||||
for (int i=0 ; i<16 ; ++i)
|
||||
(*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]);
|
||||
/* MIDI setups */
|
||||
const uint8_t* setupData = subData + group->midiSetupsOff;
|
||||
const uint8_t* setupEnd = subData + group->groupEndOff;
|
||||
while (setupData < setupEnd)
|
||||
{
|
||||
uint16_t songId = *reinterpret_cast<const uint16_t*>(setupData);
|
||||
const std::array<MusyX1MIDISetup, 16>* midiSetups =
|
||||
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(setupData + 4);
|
||||
|
||||
idx.m_midiSetups[songId] = midiSetupsBuf;
|
||||
setupData += 8 * 16 + 4;
|
||||
++midiSetupsBuf;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
(*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]);
|
||||
|
||||
idx.m_midiSetups[songId] = midiSetupsBuf;
|
||||
setupData += 8 * 16 + 4;
|
||||
++midiSetupsBuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (group->type == GroupType::SFX)
|
||||
@@ -426,10 +490,10 @@ AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, PC
|
||||
/* SFX entries */
|
||||
uint16_t count = *reinterpret_cast<const uint16_t*>(subData + group->pageTableOff);
|
||||
idx.m_sfxEntries.reserve(count);
|
||||
for (int i=0 ; i<count ; ++i)
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
const SFXGroupIndex::SFXEntry* entries =
|
||||
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + group->pageTableOff + 4 + i * 12);
|
||||
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + group->pageTableOff + 4 + i * 12);
|
||||
idx.m_sfxEntries[entries->defineId] = entries;
|
||||
}
|
||||
}
|
||||
@@ -481,5 +545,4 @@ const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const
|
||||
return nullptr;
|
||||
return &search->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "amuse/AudioGroupSampleDirectory.hpp"
|
||||
#include "amuse/Common.hpp"
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
@@ -22,7 +22,7 @@ void AudioGroupSampleDirectory::ADPCMParms::swapBigDSP()
|
||||
dsp.m_bytesPerFrame = SBig(dsp.m_bytesPerFrame);
|
||||
dsp.m_hist2 = SBig(dsp.m_hist2);
|
||||
dsp.m_hist1 = SBig(dsp.m_hist1);
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
dsp.m_coefs[i][0] = SBig(dsp.m_coefs[i][0]);
|
||||
dsp.m_coefs[i][1] = SBig(dsp.m_coefs[i][1]);
|
||||
@@ -32,7 +32,7 @@ void AudioGroupSampleDirectory::ADPCMParms::swapBigDSP()
|
||||
void AudioGroupSampleDirectory::ADPCMParms::swapBigVADPCM()
|
||||
{
|
||||
int16_t* allCoefs = reinterpret_cast<int16_t*>(vadpcm.m_coefs[0][0]);
|
||||
for (int i=0 ; i<128 ; ++i)
|
||||
for (int i = 0; i < 128; ++i)
|
||||
allCoefs[i] = SBig(allCoefs[i]);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
|
||||
const unsigned char* cur = data;
|
||||
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
|
||||
{
|
||||
const AudioGroupSampleDirectory::Entry* ent =
|
||||
reinterpret_cast<const AudioGroupSampleDirectory::Entry*>(cur);
|
||||
const AudioGroupSampleDirectory::Entry* ent = reinterpret_cast<const AudioGroupSampleDirectory::Entry*>(cur);
|
||||
|
||||
std::pair<Entry, ADPCMParms>& store = m_entries[SBig(ent->m_sfxId)];
|
||||
store.first = *ent;
|
||||
@@ -51,8 +50,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
|
||||
if (store.first.m_adpcmParmOffset)
|
||||
{
|
||||
const AudioGroupSampleDirectory::ADPCMParms* adpcm =
|
||||
reinterpret_cast<const AudioGroupSampleDirectory::ADPCMParms*>(data +
|
||||
store.first.m_adpcmParmOffset);
|
||||
reinterpret_cast<const AudioGroupSampleDirectory::ADPCMParms*>(data + store.first.m_adpcmParmOffset);
|
||||
store.second.dsp = adpcm->dsp;
|
||||
store.second.swapBigDSP();
|
||||
}
|
||||
@@ -129,8 +127,7 @@ struct MusyX1AbsSdirEntry
|
||||
}
|
||||
};
|
||||
|
||||
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
|
||||
const unsigned char* sampData,
|
||||
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData,
|
||||
bool absOffs, N64DataTag)
|
||||
{
|
||||
const unsigned char* cur = data;
|
||||
@@ -169,8 +166,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
|
||||
}
|
||||
}
|
||||
|
||||
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
|
||||
bool absOffs, PCDataTag)
|
||||
AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag)
|
||||
{
|
||||
const unsigned char* cur = data;
|
||||
|
||||
@@ -199,5 +195,4 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,73 +6,68 @@
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&,
|
||||
size_t frames, int16_t* data)
|
||||
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);
|
||||
}
|
||||
|
||||
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))
|
||||
{}
|
||||
|
||||
BooBackendVoice::BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
|
||||
double sampleRate, bool dynamicPitch)
|
||||
: m_clientVox(clientVox), m_cb(*this),
|
||||
m_booVoice(submix.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch))
|
||||
{}
|
||||
|
||||
void BooBackendVoice::resetSampleRate(double sampleRate)
|
||||
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out)
|
||||
{
|
||||
m_booVoice->resetSampleRate(sampleRate);
|
||||
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
||||
}
|
||||
|
||||
void BooBackendVoice::setMatrixCoefficients(const float coefs[8], bool slew)
|
||||
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out)
|
||||
{
|
||||
m_booVoice->setMonoMatrixCoefficients(coefs, slew);
|
||||
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
||||
}
|
||||
|
||||
void BooBackendVoice::setSubmixMatrixCoefficients(const float coefs[8], bool slew)
|
||||
void BooBackendVoice::VoiceCallback::routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out)
|
||||
{
|
||||
m_booVoice->setMonoSubmixMatrixCoefficients(coefs, slew);
|
||||
m_parent.m_clientVox.routeAudio(frames, dt, busId, in, out);
|
||||
}
|
||||
|
||||
void BooBackendVoice::setPitchRatio(double ratio, bool slew)
|
||||
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_booVoice->setPitchRatio(ratio, slew);
|
||||
}
|
||||
|
||||
void BooBackendVoice::start()
|
||||
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)
|
||||
{
|
||||
m_booVoice->start();
|
||||
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
|
||||
m_booVoice->setMonoChannelLevels(smx.m_booSubmix.get(), coefs, slew);
|
||||
}
|
||||
|
||||
void BooBackendVoice::stop()
|
||||
{
|
||||
m_booVoice->stop();
|
||||
}
|
||||
void BooBackendVoice::setPitchRatio(double ratio, bool slew) { m_booVoice->setPitchRatio(ratio, slew); }
|
||||
|
||||
bool BooBackendSubmix::SubmixCallback::canApplyEffect() const
|
||||
{
|
||||
return m_parent.m_clientSmx.canApplyEffect();
|
||||
}
|
||||
void BooBackendVoice::start() { m_booVoice->start(); }
|
||||
|
||||
void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, size_t frameCount,
|
||||
const boo::ChannelMap& chanMap, double) const
|
||||
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));
|
||||
}
|
||||
|
||||
void BooBackendSubmix::SubmixCallback::applyEffect(int32_t* audio, size_t frameCount,
|
||||
const boo::ChannelMap& chanMap, double) const
|
||||
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));
|
||||
}
|
||||
|
||||
void BooBackendSubmix::SubmixCallback::applyEffect(float* audio, size_t frameCount,
|
||||
const boo::ChannelMap& chanMap, double) const
|
||||
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));
|
||||
}
|
||||
@@ -82,40 +77,22 @@ void BooBackendSubmix::SubmixCallback::resetOutputSampleRate(double sampleRate)
|
||||
m_parent.m_clientSmx.resetOutputSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx)
|
||||
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(&m_cb))
|
||||
{}
|
||||
|
||||
BooBackendSubmix::BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx)
|
||||
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(parent.allocateNewSubmix(&m_cb))
|
||||
{}
|
||||
|
||||
void BooBackendSubmix::setChannelGains(const float gains[8])
|
||||
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_booSubmix->setChannelGains(gains);
|
||||
}
|
||||
|
||||
std::unique_ptr<IBackendVoice>
|
||||
BooBackendSubmix::allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)
|
||||
void BooBackendSubmix::setSendLevel(IBackendSubmix* submix, float level, bool slew)
|
||||
{
|
||||
return std::make_unique<BooBackendVoice>(*m_booSubmix, clientVox, sampleRate, dynamicPitch);
|
||||
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
|
||||
m_booSubmix->setSendLevel(smx.m_booSubmix.get(), level, slew);
|
||||
}
|
||||
|
||||
double BooBackendSubmix::getSampleRate() const
|
||||
{
|
||||
return m_booSubmix->getSampleRate();
|
||||
}
|
||||
double BooBackendSubmix::getSampleRate() const { return m_booSubmix->getSampleRate(); }
|
||||
|
||||
SubmixFormat BooBackendSubmix::getSampleFormat() const
|
||||
{
|
||||
return SubmixFormat(m_booSubmix->getSampleFormat());
|
||||
}
|
||||
SubmixFormat BooBackendSubmix::getSampleFormat() const { return SubmixFormat(m_booSubmix->getSampleFormat()); }
|
||||
|
||||
|
||||
std::string BooBackendMIDIReader::description()
|
||||
{
|
||||
return m_midiIn->description();
|
||||
}
|
||||
std::string BooBackendMIDIReader::description() { return m_midiIn->description(); }
|
||||
|
||||
BooBackendMIDIReader::~BooBackendMIDIReader() {}
|
||||
|
||||
@@ -128,25 +105,25 @@ BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name, boo
|
||||
auto devices = voxAlloc.m_booEngine.enumerateMIDIDevices();
|
||||
for (const auto& dev : devices)
|
||||
{
|
||||
m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(),
|
||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(
|
||||
dev.first.c_str(),
|
||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
||||
if (m_midiIn)
|
||||
return;
|
||||
}
|
||||
m_midiIn = voxAlloc.m_booEngine.newVirtualMIDIIn(std::bind(&BooBackendMIDIReader::_MIDIReceive, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
m_midiIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
|
||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(name,
|
||||
std::bind(&BooBackendMIDIReader::_MIDIReceive, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(
|
||||
name, std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
@@ -160,14 +137,15 @@ 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_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)
|
||||
for (; periodEnd != m_queue.cend(); ++periodEnd)
|
||||
{
|
||||
double delta = periodEnd->first - startPt;
|
||||
if (delta > dt)
|
||||
@@ -178,7 +156,7 @@ void BooBackendMIDIReader::pumpReader(double dt)
|
||||
return;
|
||||
|
||||
/* Dispatch buffers */
|
||||
for (auto it = m_queue.begin() ; it != periodEnd ;)
|
||||
for (auto it = m_queue.begin(); it != periodEnd;)
|
||||
{
|
||||
#if 0
|
||||
char str[64];
|
||||
@@ -216,9 +194,7 @@ void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
|
||||
#endif
|
||||
}
|
||||
|
||||
void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
|
||||
|
||||
void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
|
||||
{
|
||||
@@ -232,9 +208,7 @@ void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program)
|
||||
seq->setChanProgram(chan, program);
|
||||
}
|
||||
|
||||
void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
|
||||
|
||||
void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch)
|
||||
{
|
||||
@@ -242,20 +216,15 @@ void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch)
|
||||
seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000));
|
||||
}
|
||||
|
||||
|
||||
void BooBackendMIDIReader::allSoundOff(uint8_t chan)
|
||||
{
|
||||
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers())
|
||||
seq->allOff(chan, true);
|
||||
}
|
||||
|
||||
void BooBackendMIDIReader::resetAllControllers(uint8_t /*chan*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::resetAllControllers(uint8_t /*chan*/) {}
|
||||
|
||||
void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
|
||||
|
||||
void BooBackendMIDIReader::allNotesOff(uint8_t chan)
|
||||
{
|
||||
@@ -263,67 +232,43 @@ void BooBackendMIDIReader::allNotesOff(uint8_t chan)
|
||||
seq->allOff(chan, false);
|
||||
}
|
||||
|
||||
void BooBackendMIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||
|
||||
void BooBackendMIDIReader::polyMode(uint8_t /*chan*/, bool /*on*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::polyMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||
|
||||
void BooBackendMIDIReader::sysex(const void* /*data*/, size_t /*len*/) {}
|
||||
|
||||
void BooBackendMIDIReader::sysex(const void* /*data*/, size_t /*len*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::timeCodeQuarterFrame(uint8_t /*message*/, uint8_t /*value*/) {}
|
||||
|
||||
void BooBackendMIDIReader::timeCodeQuarterFrame(uint8_t /*message*/, uint8_t /*value*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::songPositionPointer(uint16_t /*pointer*/) {}
|
||||
|
||||
void BooBackendMIDIReader::songPositionPointer(uint16_t /*pointer*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::songSelect(uint8_t /*song*/) {}
|
||||
|
||||
void BooBackendMIDIReader::songSelect(uint8_t /*song*/)
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::tuneRequest() {}
|
||||
|
||||
void BooBackendMIDIReader::tuneRequest()
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::startSeq() {}
|
||||
|
||||
void BooBackendMIDIReader::continueSeq() {}
|
||||
|
||||
void BooBackendMIDIReader::startSeq()
|
||||
{
|
||||
}
|
||||
|
||||
void BooBackendMIDIReader::continueSeq()
|
||||
{
|
||||
}
|
||||
|
||||
void BooBackendMIDIReader::stopSeq()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void BooBackendMIDIReader::reset()
|
||||
{
|
||||
}
|
||||
void BooBackendMIDIReader::stopSeq() {}
|
||||
|
||||
void BooBackendMIDIReader::reset() {}
|
||||
|
||||
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
||||
: m_booEngine(booEngine)
|
||||
{}
|
||||
{
|
||||
booEngine.setCallbackInterface(this);
|
||||
}
|
||||
|
||||
std::unique_ptr<IBackendVoice>
|
||||
BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)
|
||||
std::unique_ptr<IBackendVoice> BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate,
|
||||
bool dynamicPitch)
|
||||
{
|
||||
return std::make_unique<BooBackendVoice>(m_booEngine, clientVox, sampleRate, dynamicPitch);
|
||||
}
|
||||
|
||||
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx)
|
||||
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx, bool mainOut, int busId)
|
||||
{
|
||||
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx);
|
||||
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx, mainOut, busId);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices()
|
||||
@@ -339,19 +284,24 @@ std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BooBackendVoiceAllocator::register5MsCallback(std::function<void(double)>&& callback)
|
||||
void BooBackendVoiceAllocator::setCallbackInterface(Engine* engine)
|
||||
{
|
||||
m_booEngine.register5MsCallback(std::move(callback));
|
||||
m_cbInterface = engine;
|
||||
}
|
||||
|
||||
AudioChannelSet BooBackendVoiceAllocator::getAvailableSet()
|
||||
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)
|
||||
{
|
||||
return AudioChannelSet(m_booEngine.getAvailableSet());
|
||||
if (m_cbInterface)
|
||||
m_cbInterface->_on5MsInterval(*this, dt);
|
||||
}
|
||||
|
||||
void BooBackendVoiceAllocator::pumpAndMixVoices()
|
||||
void BooBackendVoiceAllocator::onPumpCycleComplete(boo::IAudioVoiceEngine& engine)
|
||||
{
|
||||
m_booEngine.pumpAndMixVoices();
|
||||
if (m_cbInterface)
|
||||
m_cbInterface->_onPumpCycleComplete(*this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||
for (unsigned s=0 ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
@@ -41,7 +41,7 @@ unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint32_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||
for (unsigned s=0 ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
@@ -71,7 +71,7 @@ unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||
for (unsigned s=0 ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
@@ -102,7 +102,7 @@ unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=firstSample ; s<14 && s<lastSample ; ++s)
|
||||
for (unsigned s=firstSample ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
@@ -132,7 +132,7 @@ unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||
for (unsigned s=0 ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
|
||||
291
lib/DirectoryEnumerator.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <cstdio>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
|
||||
#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
|
||||
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
|
||||
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "amuse/DirectoryEnumerator.hpp"
|
||||
|
||||
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;
|
||||
|
||||
#if _WIN32
|
||||
SystemString wc(path);
|
||||
wc += _S("/*");
|
||||
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, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _S('/');
|
||||
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;
|
||||
|
||||
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, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _S('/');
|
||||
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));
|
||||
|
||||
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, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _S('/');
|
||||
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, _S(".")) || !wcscmp(d.cFileName, _S("..")))
|
||||
continue;
|
||||
if (noHidden && (d.cFileName[0] == L'.' || (d.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0))
|
||||
continue;
|
||||
SystemString fp(path);
|
||||
fp += _S('/');
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,153 +1,155 @@
|
||||
#include "amuse/EffectChorus.hpp"
|
||||
#include "amuse/Common.hpp"
|
||||
#include "amuse/IBackendVoice.hpp"
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
/* clang-format off */
|
||||
static const float rsmpTab12khz[] =
|
||||
{
|
||||
0.097504, 0.802216, 0.101593, -0.000977,
|
||||
0.093506, 0.802032, 0.105804, -0.001038,
|
||||
0.089600, 0.801697, 0.110107, -0.001160,
|
||||
0.085785, 0.801178, 0.114471, -0.001282,
|
||||
0.082031, 0.800476, 0.118927, -0.001404,
|
||||
0.078369, 0.799622, 0.123474, -0.001526,
|
||||
0.074799, 0.798615, 0.128143, -0.001648,
|
||||
0.071350, 0.797424, 0.132874, -0.001770,
|
||||
0.067963, 0.796051, 0.137695, -0.001923,
|
||||
0.064697, 0.794525, 0.142609, -0.002045,
|
||||
0.061493, 0.792847, 0.147614, -0.002197,
|
||||
0.058350, 0.790985, 0.152710, -0.002319,
|
||||
0.055328, 0.788940, 0.157898, -0.002472,
|
||||
0.052368, 0.786743, 0.163177, -0.002655,
|
||||
0.049500, 0.784424, 0.168518, -0.002808,
|
||||
0.046722, 0.781891, 0.173981, -0.002991,
|
||||
0.044006, 0.779205, 0.179504, -0.003143,
|
||||
0.041412, 0.776367, 0.185120, -0.003326,
|
||||
0.038879, 0.773376, 0.190826, -0.003510,
|
||||
0.036407, 0.770233, 0.196594, -0.003693,
|
||||
0.034027, 0.766937, 0.202484, -0.003876,
|
||||
0.031738, 0.763489, 0.208435, -0.004059,
|
||||
0.029510, 0.759857, 0.214447, -0.004272,
|
||||
0.027374, 0.756104, 0.220551, -0.004456,
|
||||
0.025299, 0.752197, 0.226746, -0.004669,
|
||||
0.023315, 0.748169, 0.233002, -0.004852,
|
||||
0.021393, 0.743988, 0.239319, -0.005066,
|
||||
0.019562, 0.739655, 0.245728, -0.005310,
|
||||
0.017792, 0.735199, 0.252197, -0.005524,
|
||||
0.016052, 0.730591, 0.258728, -0.005707,
|
||||
0.014404, 0.725861, 0.265350, -0.005920,
|
||||
0.012817, 0.721008, 0.272034, -0.006165,
|
||||
0.011322, 0.716003, 0.278778, -0.006378,
|
||||
0.009888, 0.710907, 0.285583, -0.006561,
|
||||
0.008514, 0.705658, 0.292450, -0.006775,
|
||||
0.007202, 0.700317, 0.299347, -0.007019,
|
||||
0.005920, 0.694855, 0.306335, -0.007233,
|
||||
0.004700, 0.689270, 0.313385, -0.007416,
|
||||
0.003571, 0.683563, 0.320465, -0.007629,
|
||||
0.002472, 0.677734, 0.327606, -0.007874,
|
||||
0.001434, 0.671844, 0.334778, -0.008087,
|
||||
0.000458, 0.665833, 0.341980, -0.008270,
|
||||
-0.000488, 0.659729, 0.349243, -0.008453,
|
||||
-0.001343, 0.653534, 0.356567, -0.008636,
|
||||
-0.002167, 0.647217, 0.363892, -0.008850,
|
||||
-0.002960, 0.640839, 0.371277, -0.009033,
|
||||
-0.003693, 0.634338, 0.378693, -0.009216,
|
||||
-0.004364, 0.627777, 0.386139, -0.009338,
|
||||
-0.004974, 0.621155, 0.393616, -0.009491,
|
||||
-0.005585, 0.614441, 0.401093, -0.009644,
|
||||
-0.006134, 0.607635, 0.408600, -0.009796,
|
||||
-0.006653, 0.600769, 0.416107, -0.009918,
|
||||
-0.007141, 0.593842, 0.423645, -0.010010,
|
||||
-0.007568, 0.586853, 0.431213, -0.010132,
|
||||
-0.007965, 0.579773, 0.438751, -0.010223,
|
||||
-0.008331, 0.572662, 0.446320, -0.010284,
|
||||
-0.008667, 0.565521, 0.453888, -0.010345,
|
||||
-0.008972, 0.558319, 0.461456, -0.010406,
|
||||
-0.009216, 0.551056, 0.469025, -0.010406,
|
||||
-0.009460, 0.543732, 0.476593, -0.010406,
|
||||
-0.009674, 0.536407, 0.484131, -0.010376,
|
||||
-0.009857, 0.529022, 0.491669, -0.010376,
|
||||
-0.010010, 0.521606, 0.499176, -0.010315,
|
||||
-0.010132, 0.514160, 0.506683, -0.010254,
|
||||
-0.010254, 0.506683, 0.514160, -0.010132,
|
||||
-0.010315, 0.499176, 0.521606, -0.010010,
|
||||
-0.010376, 0.491669, 0.529022, -0.009857,
|
||||
-0.010376, 0.484131, 0.536407, -0.009674,
|
||||
-0.010406, 0.476593, 0.543732, -0.009460,
|
||||
-0.010406, 0.469025, 0.551056, -0.009216,
|
||||
-0.010406, 0.461456, 0.558319, -0.008972,
|
||||
-0.010345, 0.453888, 0.565521, -0.008667,
|
||||
-0.010284, 0.446320, 0.572662, -0.008331,
|
||||
-0.010223, 0.438751, 0.579773, -0.007965,
|
||||
-0.010132, 0.431213, 0.586853, -0.007568,
|
||||
-0.010010, 0.423645, 0.593842, -0.007141,
|
||||
-0.009918, 0.416107, 0.600769, -0.006653,
|
||||
-0.009796, 0.408600, 0.607635, -0.006134,
|
||||
-0.009644, 0.401093, 0.614441, -0.005585,
|
||||
-0.009491, 0.393616, 0.621155, -0.004974,
|
||||
-0.009338, 0.386139, 0.627777, -0.004364,
|
||||
-0.009216, 0.378693, 0.634338, -0.003693,
|
||||
-0.009033, 0.371277, 0.640839, -0.002960,
|
||||
-0.008850, 0.363892, 0.647217, -0.002167,
|
||||
-0.008636, 0.356567, 0.653534, -0.001343,
|
||||
-0.008453, 0.349243, 0.659729, -0.000488,
|
||||
-0.008270, 0.341980, 0.665833, 0.000458,
|
||||
-0.008087, 0.334778, 0.671844, 0.001434,
|
||||
-0.007874, 0.327606, 0.677734, 0.002472,
|
||||
-0.007629, 0.320465, 0.683563, 0.003571,
|
||||
-0.007416, 0.313385, 0.689270, 0.004700,
|
||||
-0.007233, 0.306335, 0.694855, 0.005920,
|
||||
-0.007019, 0.299347, 0.700317, 0.007202,
|
||||
-0.006775, 0.292450, 0.705658, 0.008514,
|
||||
-0.006561, 0.285583, 0.710907, 0.009888,
|
||||
-0.006378, 0.278778, 0.716003, 0.011322,
|
||||
-0.006165, 0.272034, 0.721008, 0.012817,
|
||||
-0.005920, 0.265350, 0.725861, 0.014404,
|
||||
-0.005707, 0.258728, 0.730591, 0.016052,
|
||||
-0.005524, 0.252197, 0.735199, 0.017792,
|
||||
-0.005310, 0.245728, 0.739655, 0.019562,
|
||||
-0.005066, 0.239319, 0.743988, 0.021393,
|
||||
-0.004852, 0.233002, 0.748169, 0.023315,
|
||||
-0.004669, 0.226746, 0.752197, 0.025299,
|
||||
-0.004456, 0.220551, 0.756104, 0.027374,
|
||||
-0.004272, 0.214447, 0.759857, 0.029510,
|
||||
-0.004059, 0.208435, 0.763489, 0.031738,
|
||||
-0.003876, 0.202484, 0.766937, 0.034027,
|
||||
-0.003693, 0.196594, 0.770233, 0.036407,
|
||||
-0.003510, 0.190826, 0.773376, 0.038879,
|
||||
-0.003326, 0.185120, 0.776367, 0.041412,
|
||||
-0.003143, 0.179504, 0.779205, 0.044006,
|
||||
-0.002991, 0.173981, 0.781891, 0.046722,
|
||||
-0.002808, 0.168518, 0.784424, 0.049500,
|
||||
-0.002655, 0.163177, 0.786743, 0.052368,
|
||||
-0.002472, 0.157898, 0.788940, 0.055328,
|
||||
-0.002319, 0.152710, 0.790985, 0.058350,
|
||||
-0.002197, 0.147614, 0.792847, 0.061493,
|
||||
-0.002045, 0.142609, 0.794525, 0.064697,
|
||||
-0.001923, 0.137695, 0.796051, 0.067963,
|
||||
-0.001770, 0.132874, 0.797424, 0.071350,
|
||||
-0.001648, 0.128143, 0.798615, 0.074799,
|
||||
-0.001526, 0.123474, 0.799622, 0.078369,
|
||||
-0.001404, 0.118927, 0.800476, 0.082031,
|
||||
-0.001282, 0.114471, 0.801178, 0.085785,
|
||||
-0.001160, 0.110107, 0.801697, 0.089600,
|
||||
-0.001038, 0.105804, 0.802032, 0.093506,
|
||||
-0.000977, 0.101593, 0.802216, 0.097504,
|
||||
0.097504f, 0.802216f, 0.101593f, -0.000977f,
|
||||
0.093506f, 0.802032f, 0.105804f, -0.001038f,
|
||||
0.089600f, 0.801697f, 0.110107f, -0.001160f,
|
||||
0.085785f, 0.801178f, 0.114471f, -0.001282f,
|
||||
0.082031f, 0.800476f, 0.118927f, -0.001404f,
|
||||
0.078369f, 0.799622f, 0.123474f, -0.001526f,
|
||||
0.074799f, 0.798615f, 0.128143f, -0.001648f,
|
||||
0.071350f, 0.797424f, 0.132874f, -0.001770f,
|
||||
0.067963f, 0.796051f, 0.137695f, -0.001923f,
|
||||
0.064697f, 0.794525f, 0.142609f, -0.002045f,
|
||||
0.061493f, 0.792847f, 0.147614f, -0.002197f,
|
||||
0.058350f, 0.790985f, 0.152710f, -0.002319f,
|
||||
0.055328f, 0.788940f, 0.157898f, -0.002472f,
|
||||
0.052368f, 0.786743f, 0.163177f, -0.002655f,
|
||||
0.049500f, 0.784424f, 0.168518f, -0.002808f,
|
||||
0.046722f, 0.781891f, 0.173981f, -0.002991f,
|
||||
0.044006f, 0.779205f, 0.179504f, -0.003143f,
|
||||
0.041412f, 0.776367f, 0.185120f, -0.003326f,
|
||||
0.038879f, 0.773376f, 0.190826f, -0.003510f,
|
||||
0.036407f, 0.770233f, 0.196594f, -0.003693f,
|
||||
0.034027f, 0.766937f, 0.202484f, -0.003876f,
|
||||
0.031738f, 0.763489f, 0.208435f, -0.004059f,
|
||||
0.029510f, 0.759857f, 0.214447f, -0.004272f,
|
||||
0.027374f, 0.756104f, 0.220551f, -0.004456f,
|
||||
0.025299f, 0.752197f, 0.226746f, -0.004669f,
|
||||
0.023315f, 0.748169f, 0.233002f, -0.004852f,
|
||||
0.021393f, 0.743988f, 0.239319f, -0.005066f,
|
||||
0.019562f, 0.739655f, 0.245728f, -0.005310f,
|
||||
0.017792f, 0.735199f, 0.252197f, -0.005524f,
|
||||
0.016052f, 0.730591f, 0.258728f, -0.005707f,
|
||||
0.014404f, 0.725861f, 0.265350f, -0.005920f,
|
||||
0.012817f, 0.721008f, 0.272034f, -0.006165f,
|
||||
0.011322f, 0.716003f, 0.278778f, -0.006378f,
|
||||
0.009888f, 0.710907f, 0.285583f, -0.006561f,
|
||||
0.008514f, 0.705658f, 0.292450f, -0.006775f,
|
||||
0.007202f, 0.700317f, 0.299347f, -0.007019f,
|
||||
0.005920f, 0.694855f, 0.306335f, -0.007233f,
|
||||
0.004700f, 0.689270f, 0.313385f, -0.007416f,
|
||||
0.003571f, 0.683563f, 0.320465f, -0.007629f,
|
||||
0.002472f, 0.677734f, 0.327606f, -0.007874f,
|
||||
0.001434f, 0.671844f, 0.334778f, -0.008087f,
|
||||
0.000458f, 0.665833f, 0.341980f, -0.008270f,
|
||||
-0.000488f, 0.659729f, 0.349243f, -0.008453f,
|
||||
-0.001343f, 0.653534f, 0.356567f, -0.008636f,
|
||||
-0.002167f, 0.647217f, 0.363892f, -0.008850f,
|
||||
-0.002960f, 0.640839f, 0.371277f, -0.009033f,
|
||||
-0.003693f, 0.634338f, 0.378693f, -0.009216f,
|
||||
-0.004364f, 0.627777f, 0.386139f, -0.009338f,
|
||||
-0.004974f, 0.621155f, 0.393616f, -0.009491f,
|
||||
-0.005585f, 0.614441f, 0.401093f, -0.009644f,
|
||||
-0.006134f, 0.607635f, 0.408600f, -0.009796f,
|
||||
-0.006653f, 0.600769f, 0.416107f, -0.009918f,
|
||||
-0.007141f, 0.593842f, 0.423645f, -0.010010f,
|
||||
-0.007568f, 0.586853f, 0.431213f, -0.010132f,
|
||||
-0.007965f, 0.579773f, 0.438751f, -0.010223f,
|
||||
-0.008331f, 0.572662f, 0.446320f, -0.010284f,
|
||||
-0.008667f, 0.565521f, 0.453888f, -0.010345f,
|
||||
-0.008972f, 0.558319f, 0.461456f, -0.010406f,
|
||||
-0.009216f, 0.551056f, 0.469025f, -0.010406f,
|
||||
-0.009460f, 0.543732f, 0.476593f, -0.010406f,
|
||||
-0.009674f, 0.536407f, 0.484131f, -0.010376f,
|
||||
-0.009857f, 0.529022f, 0.491669f, -0.010376f,
|
||||
-0.010010f, 0.521606f, 0.499176f, -0.010315f,
|
||||
-0.010132f, 0.514160f, 0.506683f, -0.010254f,
|
||||
-0.010254f, 0.506683f, 0.514160f, -0.010132f,
|
||||
-0.010315f, 0.499176f, 0.521606f, -0.010010f,
|
||||
-0.010376f, 0.491669f, 0.529022f, -0.009857f,
|
||||
-0.010376f, 0.484131f, 0.536407f, -0.009674f,
|
||||
-0.010406f, 0.476593f, 0.543732f, -0.009460f,
|
||||
-0.010406f, 0.469025f, 0.551056f, -0.009216f,
|
||||
-0.010406f, 0.461456f, 0.558319f, -0.008972f,
|
||||
-0.010345f, 0.453888f, 0.565521f, -0.008667f,
|
||||
-0.010284f, 0.446320f, 0.572662f, -0.008331f,
|
||||
-0.010223f, 0.438751f, 0.579773f, -0.007965f,
|
||||
-0.010132f, 0.431213f, 0.586853f, -0.007568f,
|
||||
-0.010010f, 0.423645f, 0.593842f, -0.007141f,
|
||||
-0.009918f, 0.416107f, 0.600769f, -0.006653f,
|
||||
-0.009796f, 0.408600f, 0.607635f, -0.006134f,
|
||||
-0.009644f, 0.401093f, 0.614441f, -0.005585f,
|
||||
-0.009491f, 0.393616f, 0.621155f, -0.004974f,
|
||||
-0.009338f, 0.386139f, 0.627777f, -0.004364f,
|
||||
-0.009216f, 0.378693f, 0.634338f, -0.003693f,
|
||||
-0.009033f, 0.371277f, 0.640839f, -0.002960f,
|
||||
-0.008850f, 0.363892f, 0.647217f, -0.002167f,
|
||||
-0.008636f, 0.356567f, 0.653534f, -0.001343f,
|
||||
-0.008453f, 0.349243f, 0.659729f, -0.000488f,
|
||||
-0.008270f, 0.341980f, 0.665833f, 0.000458f,
|
||||
-0.008087f, 0.334778f, 0.671844f, 0.001434f,
|
||||
-0.007874f, 0.327606f, 0.677734f, 0.002472f,
|
||||
-0.007629f, 0.320465f, 0.683563f, 0.003571f,
|
||||
-0.007416f, 0.313385f, 0.689270f, 0.004700f,
|
||||
-0.007233f, 0.306335f, 0.694855f, 0.005920f,
|
||||
-0.007019f, 0.299347f, 0.700317f, 0.007202f,
|
||||
-0.006775f, 0.292450f, 0.705658f, 0.008514f,
|
||||
-0.006561f, 0.285583f, 0.710907f, 0.009888f,
|
||||
-0.006378f, 0.278778f, 0.716003f, 0.011322f,
|
||||
-0.006165f, 0.272034f, 0.721008f, 0.012817f,
|
||||
-0.005920f, 0.265350f, 0.725861f, 0.014404f,
|
||||
-0.005707f, 0.258728f, 0.730591f, 0.016052f,
|
||||
-0.005524f, 0.252197f, 0.735199f, 0.017792f,
|
||||
-0.005310f, 0.245728f, 0.739655f, 0.019562f,
|
||||
-0.005066f, 0.239319f, 0.743988f, 0.021393f,
|
||||
-0.004852f, 0.233002f, 0.748169f, 0.023315f,
|
||||
-0.004669f, 0.226746f, 0.752197f, 0.025299f,
|
||||
-0.004456f, 0.220551f, 0.756104f, 0.027374f,
|
||||
-0.004272f, 0.214447f, 0.759857f, 0.029510f,
|
||||
-0.004059f, 0.208435f, 0.763489f, 0.031738f,
|
||||
-0.003876f, 0.202484f, 0.766937f, 0.034027f,
|
||||
-0.003693f, 0.196594f, 0.770233f, 0.036407f,
|
||||
-0.003510f, 0.190826f, 0.773376f, 0.038879f,
|
||||
-0.003326f, 0.185120f, 0.776367f, 0.041412f,
|
||||
-0.003143f, 0.179504f, 0.779205f, 0.044006f,
|
||||
-0.002991f, 0.173981f, 0.781891f, 0.046722f,
|
||||
-0.002808f, 0.168518f, 0.784424f, 0.049500f,
|
||||
-0.002655f, 0.163177f, 0.786743f, 0.052368f,
|
||||
-0.002472f, 0.157898f, 0.788940f, 0.055328f,
|
||||
-0.002319f, 0.152710f, 0.790985f, 0.058350f,
|
||||
-0.002197f, 0.147614f, 0.792847f, 0.061493f,
|
||||
-0.002045f, 0.142609f, 0.794525f, 0.064697f,
|
||||
-0.001923f, 0.137695f, 0.796051f, 0.067963f,
|
||||
-0.001770f, 0.132874f, 0.797424f, 0.071350f,
|
||||
-0.001648f, 0.128143f, 0.798615f, 0.074799f,
|
||||
-0.001526f, 0.123474f, 0.799622f, 0.078369f,
|
||||
-0.001404f, 0.118927f, 0.800476f, 0.082031f,
|
||||
-0.001282f, 0.114471f, 0.801178f, 0.085785f,
|
||||
-0.001160f, 0.110107f, 0.801697f, 0.089600f,
|
||||
-0.001038f, 0.105804f, 0.802032f, 0.093506f,
|
||||
-0.000977f, 0.101593f, 0.802216f, 0.097504f,
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
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))
|
||||
{}
|
||||
: x90_baseDelay(clamp(5u, baseDelay, 15u))
|
||||
, x94_variation(clamp(0u, variation, 5u))
|
||||
, x98_period(clamp(500u, period, 10000u))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EffectChorusImp<T>::EffectChorusImp(uint32_t baseDelay, uint32_t variation,
|
||||
uint32_t period, double sampleRate)
|
||||
EffectChorusImp<T>::EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate)
|
||||
: EffectChorus(baseDelay, variation, period)
|
||||
{
|
||||
_setup(sampleRate);
|
||||
@@ -165,8 +167,8 @@ void EffectChorusImp<T>::_setup(double sampleRate)
|
||||
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)
|
||||
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;
|
||||
@@ -207,7 +209,7 @@ void EffectChorusImp<T>::SrcInfo::doSrc1(size_t blockSamples, size_t chanCount)
|
||||
float cur = x70_smpBase[x7c_posHi];
|
||||
|
||||
T* dest = x6c_dest;
|
||||
for (size_t i=0 ; i<blockSamples ; ++i)
|
||||
for (size_t i = 0; i < blockSamples; ++i)
|
||||
{
|
||||
const float* selTab = &rsmpTab12khz[x78_posLo >> 23 & 0x1fc];
|
||||
|
||||
@@ -248,7 +250,7 @@ void EffectChorusImp<T>::SrcInfo::doSrc2(size_t blockSamples, size_t chanCount)
|
||||
float cur = x70_smpBase[x7c_posHi];
|
||||
|
||||
T* dest = x6c_dest;
|
||||
for (size_t i=0 ; i<blockSamples ; ++i)
|
||||
for (size_t i = 0; i < blockSamples; ++i)
|
||||
{
|
||||
const float* selTab = &rsmpTab12khz[x78_posLo >> 23 & 0x1fc];
|
||||
++x7c_posHi;
|
||||
@@ -305,25 +307,18 @@ void EffectChorusImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelM
|
||||
_update();
|
||||
|
||||
size_t remFrames = frameCount;
|
||||
for (size_t f=0 ; f<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* 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)
|
||||
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;
|
||||
@@ -338,7 +333,7 @@ void EffectChorusImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelM
|
||||
|
||||
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)
|
||||
for (size_t c = 0; c < chanMap.m_channelCount && c < 8; ++c)
|
||||
{
|
||||
x6c_src.x7c_posHi = x5c_currentPosHi;
|
||||
x6c_src.x78_posLo = x58_currentPosLo;
|
||||
@@ -355,7 +350,8 @@ void EffectChorusImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelM
|
||||
case 1:
|
||||
x6c_src.doSrc2(bs, chanMap.m_channelCount);
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,5 +370,4 @@ void EffectChorusImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelM
|
||||
template class EffectChorusImp<int16_t>;
|
||||
template class EffectChorusImp<int32_t>;
|
||||
template class EffectChorusImp<float>;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
#include "amuse/EffectDelay.hpp"
|
||||
#include "amuse/Common.hpp"
|
||||
#include "amuse/IBackendVoice.hpp"
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback,
|
||||
uint32_t initOutput, double sampleRate)
|
||||
EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate)
|
||||
{
|
||||
initDelay = clamp(10u, initDelay, 5000u);
|
||||
initFeedback = clamp(0u, initFeedback, 100u);
|
||||
initOutput = clamp(0u, initOutput, 100u);
|
||||
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
x3c_delay[i] = initDelay;
|
||||
x48_feedback[i] = initFeedback;
|
||||
@@ -25,6 +24,19 @@ EffectDelayImp<T>::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback,
|
||||
_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);
|
||||
}
|
||||
|
||||
_setup(sampleRate);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EffectDelayImp<T>::_setup(double sampleRate)
|
||||
{
|
||||
@@ -37,7 +49,7 @@ void EffectDelayImp<T>::_setup(double sampleRate)
|
||||
template <typename T>
|
||||
void EffectDelayImp<T>::_update()
|
||||
{
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
x0_currentSize[i] = ((x3c_delay[i] - 5) * m_sampsPerMs + 159) / 160;
|
||||
xc_currentPos[i] = 0;
|
||||
@@ -57,12 +69,12 @@ void EffectDelayImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMa
|
||||
if (m_dirty)
|
||||
_update();
|
||||
|
||||
for (size_t f=0 ; f<frameCount ;)
|
||||
for (size_t f = 0; f < frameCount;)
|
||||
{
|
||||
for (int c=0 ; c<chanMap.m_channelCount ; ++c)
|
||||
for (unsigned c = 0; c < chanMap.m_channelCount; ++c)
|
||||
{
|
||||
T* chanAud = audio + c;
|
||||
for (int i=0 ; i<m_blockSamples && f<frameCount ; ++i, ++f)
|
||||
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];
|
||||
@@ -78,5 +90,4 @@ void EffectDelayImp<T>::applyEffect(T* audio, size_t frameCount, const ChannelMa
|
||||
template class EffectDelayImp<int16_t>;
|
||||
template class EffectDelayImp<int32_t>;
|
||||
template class EffectDelayImp<float>;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "amuse/EffectReverb.hpp"
|
||||
#include "amuse/IBackendVoice.hpp"
|
||||
#include <cmath>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
/* Comb-filter delays */
|
||||
static const size_t CTapDelays[] =
|
||||
{
|
||||
@@ -34,6 +36,8 @@ static const size_t LPTapDelays[] =
|
||||
73
|
||||
};
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
void ReverbDelayLine::allocate(int32_t delay)
|
||||
{
|
||||
delay += 2;
|
||||
@@ -53,24 +57,23 @@ void ReverbDelayLine::setdelay(int32_t delay)
|
||||
x4_outPoint += x8_length;
|
||||
}
|
||||
|
||||
EffectReverbStd::EffectReverbStd(float coloration, float mix, float time,
|
||||
float damping, float preDelay)
|
||||
: x140_x1c8_coloration(clamp(0.f, coloration, 1.f)),
|
||||
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))
|
||||
{}
|
||||
EffectReverbStd::EffectReverbStd(float coloration, float mix, float time, float damping, float preDelay)
|
||||
: x140_x1c8_coloration(clamp(0.f, coloration, 1.f))
|
||||
, 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))
|
||||
{
|
||||
}
|
||||
|
||||
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))
|
||||
{}
|
||||
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))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EffectReverbStdImp<T>::EffectReverbStdImp(float coloration, float mix, float time,
|
||||
float damping, float preDelay, double sampleRate)
|
||||
EffectReverbStdImp<T>::EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay,
|
||||
double sampleRate)
|
||||
: EffectReverbStd(coloration, mix, time, damping, preDelay)
|
||||
{
|
||||
_setup(sampleRate);
|
||||
@@ -87,21 +90,22 @@ template <typename T>
|
||||
void EffectReverbStdImp<T>::_update()
|
||||
{
|
||||
float timeSamples = x148_x1d0_time * m_sampleRate;
|
||||
for (int c=0 ; c<8 ; ++c)
|
||||
double rateRatio = m_sampleRate / NativeSampleRate;
|
||||
for (int c = 0; c < 8; ++c)
|
||||
{
|
||||
for (int t=0 ; t<2 ; ++t)
|
||||
for (int t = 0; t < 2; ++t)
|
||||
{
|
||||
ReverbDelayLine& combLine = x78_C[c][t];
|
||||
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
|
||||
size_t tapDelay = CTapDelays[t] * rateRatio;
|
||||
combLine.allocate(tapDelay);
|
||||
combLine.setdelay(tapDelay);
|
||||
xf4_combCoef[c][t] = std::pow(10.f, tapDelay * -3.f / timeSamples);
|
||||
}
|
||||
|
||||
for (int t=0 ; t<2 ; ++t)
|
||||
for (int t = 0; t < 2; ++t)
|
||||
{
|
||||
ReverbDelayLine& allPassLine = x0_AP[c][t];
|
||||
size_t tapDelay = APTapDelays[t] * m_sampleRate / 32000.0;
|
||||
size_t tapDelay = APTapDelays[t] * rateRatio;
|
||||
allPassLine.allocate(tapDelay);
|
||||
allPassLine.setdelay(tapDelay);
|
||||
}
|
||||
@@ -119,7 +123,7 @@ void EffectReverbStdImp<T>::_update()
|
||||
if (x150_x1d8_preDelay != 0.f)
|
||||
{
|
||||
x120_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
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));
|
||||
@@ -129,7 +133,7 @@ void EffectReverbStdImp<T>::_update()
|
||||
else
|
||||
{
|
||||
x120_preDelayTime = 0;
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
x124_preDelayLine[i] = nullptr;
|
||||
x130_preDelayPtr[i] = nullptr;
|
||||
@@ -148,9 +152,9 @@ void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const Chann
|
||||
float dampWet = x118_level * 0.6f;
|
||||
float dampDry = 0.6f - dampWet;
|
||||
|
||||
for (size_t f=0 ; f<frameCount ; f+=160)
|
||||
for (size_t f = 0; f < frameCount; f += 160)
|
||||
{
|
||||
for (unsigned c=0 ; c<chanMap.m_channelCount ; ++c)
|
||||
for (unsigned c = 0; c < chanMap.m_channelCount; ++c)
|
||||
{
|
||||
float* combCoefs = xf4_combCoef[c];
|
||||
float& lpLastOut = x10c_lpLastout[c];
|
||||
@@ -162,7 +166,7 @@ void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const Chann
|
||||
ReverbDelayLine* linesAP = x0_AP[c];
|
||||
|
||||
int procSamples = std::min(size_t(160), frameCount - f);
|
||||
for (int s=0 ; s<procSamples ; ++s)
|
||||
for (int s = 0; s < procSamples; ++s)
|
||||
{
|
||||
float sample = audio[s * chanMap.m_channelCount + c];
|
||||
|
||||
@@ -205,8 +209,8 @@ void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const Chann
|
||||
/* 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);
|
||||
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];
|
||||
@@ -220,8 +224,8 @@ void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const Chann
|
||||
|
||||
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);
|
||||
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];
|
||||
@@ -243,9 +247,8 @@ void EffectReverbStdImp<T>::applyEffect(T* audio, size_t frameCount, const Chann
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EffectReverbHiImp<T>::EffectReverbHiImp(float coloration, float mix, float time,
|
||||
float damping, float preDelay, float crosstalk,
|
||||
double sampleRate)
|
||||
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);
|
||||
@@ -262,27 +265,28 @@ template <typename T>
|
||||
void EffectReverbHiImp<T>::_update()
|
||||
{
|
||||
float timeSamples = x148_x1d0_time * m_sampleRate;
|
||||
for (int c=0 ; c<8 ; ++c)
|
||||
double rateRatio = m_sampleRate / NativeSampleRate;
|
||||
for (int c = 0; c < 8; ++c)
|
||||
{
|
||||
for (int t=0 ; t<3 ; ++t)
|
||||
for (int t = 0; t < 3; ++t)
|
||||
{
|
||||
ReverbDelayLine& combLine = xb4_C[c][t];
|
||||
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
|
||||
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)
|
||||
for (int t = 0; t < 2; ++t)
|
||||
{
|
||||
ReverbDelayLine& allPassLine = x0_AP[c][t];
|
||||
size_t tapDelay = APTapDelays[t] * m_sampleRate / 32000.0;
|
||||
size_t tapDelay = APTapDelays[t] * rateRatio;
|
||||
allPassLine.allocate(tapDelay);
|
||||
allPassLine.setdelay(tapDelay);
|
||||
}
|
||||
|
||||
ReverbDelayLine& lpLine = x78_LP[c];
|
||||
size_t tapDelay = LPTapDelays[c] * m_sampleRate / 32000.0;
|
||||
size_t tapDelay = LPTapDelays[c] * rateRatio;
|
||||
lpLine.allocate(tapDelay);
|
||||
lpLine.setdelay(tapDelay);
|
||||
}
|
||||
@@ -299,7 +303,7 @@ void EffectReverbHiImp<T>::_update()
|
||||
if (x150_x1d8_preDelay != 0.f)
|
||||
{
|
||||
x1a4_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
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));
|
||||
@@ -309,7 +313,7 @@ void EffectReverbHiImp<T>::_update()
|
||||
else
|
||||
{
|
||||
x1a4_preDelayTime = 0;
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
x1ac_preDelayLine[i] = nullptr;
|
||||
x1b8_preDelayPtr[i] = nullptr;
|
||||
@@ -340,7 +344,7 @@ void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sam
|
||||
float damping = x1a0_damping;
|
||||
int32_t preDelayTime = x1a4_preDelayTime;
|
||||
|
||||
for (int s=0 ; s<sampleCount ; ++s)
|
||||
for (int s = 0; s < sampleCount; ++s)
|
||||
{
|
||||
float sample = audio[s * chanCount + c];
|
||||
|
||||
@@ -393,8 +397,8 @@ void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sam
|
||||
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[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 -
|
||||
@@ -446,16 +450,16 @@ void EffectReverbHiImp<T>::_handleReverb(T* audio, int c, int chanCount, int sam
|
||||
template <typename T>
|
||||
void EffectReverbHiImp<T>::_doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount)
|
||||
{
|
||||
for (int i=0 ; i<sampleCount ; ++i)
|
||||
for (int i = 0; i < sampleCount; ++i)
|
||||
{
|
||||
T* base = &audio[chanCount*i];
|
||||
T* base = &audio[chanCount * i];
|
||||
float allWet = 0;
|
||||
for (int c=0 ; c<chanCount ; ++c)
|
||||
for (int c = 0; c < chanCount; ++c)
|
||||
{
|
||||
allWet += base[c] * wet;
|
||||
base[c] *= dry;
|
||||
}
|
||||
for (int c=0 ; c<chanCount ; ++c)
|
||||
for (int c = 0; c < chanCount; ++c)
|
||||
base[c] = ClampFull<T>(base[c] + allWet);
|
||||
}
|
||||
}
|
||||
@@ -466,10 +470,10 @@ void EffectReverbHiImp<T>::applyEffect(T* audio, size_t frameCount, const Channe
|
||||
if (m_dirty)
|
||||
_update();
|
||||
|
||||
for (size_t f=0 ; f<frameCount ; f+=160)
|
||||
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)
|
||||
for (unsigned i = 0; i < chanMap.m_channelCount; ++i)
|
||||
{
|
||||
if (i == 0 && x1a8_internalCrosstalk != 0.f)
|
||||
{
|
||||
@@ -489,5 +493,4 @@ template class EffectReverbStdImp<float>;
|
||||
template class EffectReverbHiImp<int16_t>;
|
||||
template class EffectReverbHiImp<int32_t>;
|
||||
template class EffectReverbHiImp<float>;
|
||||
|
||||
}
|
||||
|
||||
131
lib/Emitter.cpp
@@ -1,14 +1,24 @@
|
||||
#include "amuse/Emitter.hpp"
|
||||
#include "amuse/Listener.hpp"
|
||||
#include "amuse/Voice.hpp"
|
||||
#include "amuse/Engine.hpp"
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
Emitter::~Emitter() {}
|
||||
|
||||
Emitter::Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>&& vox)
|
||||
: Entity(engine, group, vox->getObjectId()), m_vox(std::move(vox))
|
||||
Emitter::Emitter(Engine& engine, const AudioGroup& group, const std::shared_ptr<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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -18,28 +28,115 @@ void Emitter::_destroy()
|
||||
m_vox->kill();
|
||||
}
|
||||
|
||||
void Emitter::setPos(const Vector3f& pos)
|
||||
float Emitter::_attenuationCurve(float dist) const
|
||||
{
|
||||
if (dist > m_maxDist)
|
||||
return 0.f;
|
||||
float t = dist / m_maxDist;
|
||||
if (m_falloff < 0.f)
|
||||
{
|
||||
float tmp = t * 10.f + 1.f;
|
||||
tmp = 1.f / (tmp * tmp);
|
||||
return (1.f + m_falloff) * (-t + 1.f) + -m_falloff * tmp;
|
||||
}
|
||||
else if (m_falloff > 0.f)
|
||||
{
|
||||
float tmp = (t - 1.f) * 10.f - 1.f;
|
||||
tmp = -1.f / (tmp * tmp) + 1.f;
|
||||
return (1.f - m_falloff) * (-t + 1.f) + m_falloff * tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -t + 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
void Emitter::setDir(const Vector3f& dir)
|
||||
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);
|
||||
if (att > FLT_EPSILON)
|
||||
{
|
||||
att = (1.f - att) * m_minVol + att * m_maxVol;
|
||||
|
||||
/* 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::setMaxDist(float maxDist)
|
||||
{
|
||||
}
|
||||
|
||||
void Emitter::setMaxVol(float maxVol)
|
||||
{
|
||||
}
|
||||
|
||||
void Emitter::setMinVol(float minVol)
|
||||
{
|
||||
}
|
||||
|
||||
void Emitter::setFalloff(float falloff)
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
234
lib/Engine.cpp
@@ -11,13 +11,14 @@
|
||||
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 (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
||||
if (!seq->m_destroyed)
|
||||
seq->_destroy();
|
||||
while (m_activeSubmixes.size())
|
||||
removeSubmix(&m_activeSubmixes.front());
|
||||
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
|
||||
emitter->_destroy();
|
||||
for (std::shared_ptr<Voice>& vox : m_activeVoices)
|
||||
@@ -25,9 +26,12 @@ Engine::~Engine()
|
||||
}
|
||||
|
||||
Engine::Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode)
|
||||
: m_backend(backend), m_ampMode(ampMode)
|
||||
: m_backend(backend), m_ampMode(ampMode), m_defaultStudio(_allocateStudio(true))
|
||||
{
|
||||
backend.register5MsCallback(std::bind(&Engine::_5MsCallback, this, std::placeholders::_1));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -53,85 +57,72 @@ std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(int groupId)
|
||||
return {};
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Voice>>::iterator
|
||||
Engine::_allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
|
||||
bool dynamicPitch, bool emitter, Submix* smx)
|
||||
std::list<std::shared_ptr<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, int groupId,
|
||||
double sampleRate, bool dynamicPitch, bool emitter,
|
||||
std::weak_ptr<Studio> studio)
|
||||
{
|
||||
auto it = m_activeVoices.emplace(m_activeVoices.end(),
|
||||
new Voice(*this, group, groupId, m_nextVid++, emitter, smx));
|
||||
if (smx)
|
||||
m_activeVoices.back()->m_backendVoice =
|
||||
smx->m_backendSubmix->allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
||||
else
|
||||
m_activeVoices.back()->m_backendVoice =
|
||||
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
|
||||
std::shared_ptr<Studio> st = studio.lock();
|
||||
auto it =
|
||||
m_activeVoices.emplace(m_activeVoices.end(), new 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(st->getMaster().m_backendSubmix.get(), FullLevels, false);
|
||||
m_activeVoices.back()->m_backendVoice->setChannelLevels(st->getAuxA().m_backendSubmix.get(), FullLevels, false);
|
||||
m_activeVoices.back()->m_backendVoice->setChannelLevels(st->getAuxB().m_backendSubmix.get(), FullLevels, false);
|
||||
return it;
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator
|
||||
Engine::_allocateSequencer(const AudioGroup& group, int groupId,
|
||||
int setupId, Submix* smx)
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator Engine::_allocateSequencer(const AudioGroup& group, int groupId,
|
||||
int setupId, std::weak_ptr<Studio> studio)
|
||||
{
|
||||
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
|
||||
if (songGroup)
|
||||
{
|
||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
|
||||
new Sequencer(*this, group, groupId, songGroup, setupId, smx));
|
||||
new Sequencer(*this, group, groupId, songGroup, setupId, studio));
|
||||
return it;
|
||||
}
|
||||
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
|
||||
if (sfxGroup)
|
||||
{
|
||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
|
||||
new Sequencer(*this, group, groupId, sfxGroup, smx));
|
||||
new Sequencer(*this, group, groupId, sfxGroup, studio));
|
||||
return it;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::list<Submix>::iterator Engine::_allocateSubmix(Submix* smx)
|
||||
std::shared_ptr<Studio> Engine::_allocateStudio(bool mainOut)
|
||||
{
|
||||
auto it = m_activeSubmixes.emplace(m_activeSubmixes.end(), *this, smx);
|
||||
m_activeSubmixes.back().m_backendSubmix = m_backend.allocateSubmix(m_activeSubmixes.back());
|
||||
return it;
|
||||
std::shared_ptr<Studio> ret = std::make_shared<Studio>(*this, mainOut);
|
||||
m_activeStudios.emplace(m_activeStudios.end(), ret);
|
||||
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<std::shared_ptr<Voice>>::iterator Engine::_destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
assert(this == &(*it)->getEngine());
|
||||
#endif
|
||||
if ((*it)->m_destroyed)
|
||||
return m_activeVoices.begin();
|
||||
(*it)->_destroy();
|
||||
return m_activeVoices.erase(it);
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator Engine::_destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it)
|
||||
std::list<std::shared_ptr<Sequencer>>::iterator
|
||||
Engine::_destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
assert(this == &(*it)->getEngine());
|
||||
#endif
|
||||
if ((*it)->m_destroyed)
|
||||
return m_activeSequencers.begin();
|
||||
(*it)->_destroy();
|
||||
return m_activeSequencers.erase(it);
|
||||
}
|
||||
|
||||
std::list<Submix>::iterator Engine::_destroySubmix(std::list<Submix>::iterator it)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
assert(this == &it->getEngine());
|
||||
#endif
|
||||
if (it->m_destroyed)
|
||||
return m_activeSubmixes.begin();
|
||||
it->_destroy();
|
||||
return m_activeSubmixes.erase(it);
|
||||
}
|
||||
|
||||
void Engine::_bringOutYourDead()
|
||||
{
|
||||
for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;)
|
||||
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();)
|
||||
{
|
||||
Emitter* emitter = it->get();
|
||||
if (emitter->getVoice()->_isRecursivelyDead())
|
||||
@@ -143,7 +134,7 @@ void Engine::_bringOutYourDead()
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
|
||||
{
|
||||
Voice* vox = it->get();
|
||||
vox->_bringOutYourDead();
|
||||
@@ -155,7 +146,7 @@ void Engine::_bringOutYourDead()
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ;)
|
||||
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();)
|
||||
{
|
||||
Sequencer* seq = it->get();
|
||||
seq->_bringOutYourDead();
|
||||
@@ -166,20 +157,32 @@ void Engine::_bringOutYourDead()
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_activeStudios.begin(); it != m_activeStudios.end();)
|
||||
{
|
||||
std::shared_ptr<Studio> st = it->lock();
|
||||
if (!st)
|
||||
it = m_activeStudios.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::_5MsCallback(double dt)
|
||||
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
|
||||
{
|
||||
m_channelSet = engine.getAvailableSet();
|
||||
if (m_midiReader)
|
||||
m_midiReader->pumpReader(dt);
|
||||
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
||||
seq->advance(dt);
|
||||
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
|
||||
emitter->_update();
|
||||
for (std::shared_ptr<Listener>& listener : m_activeListeners)
|
||||
listener->m_dirty = false;
|
||||
}
|
||||
|
||||
/** Update all active audio entities and fill OS audio buffers as needed */
|
||||
void Engine::pumpEngine()
|
||||
void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine)
|
||||
{
|
||||
m_backend.pumpAndMixVoices();
|
||||
_bringOutYourDead();
|
||||
|
||||
/* Determine lowest available free vid */
|
||||
@@ -239,7 +242,7 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
|
||||
AudioGroup* grp = search->second.get();
|
||||
|
||||
/* Destroy runtime entities within group */
|
||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
|
||||
{
|
||||
Voice* vox = it->get();
|
||||
if (&vox->getAudioGroup() == grp)
|
||||
@@ -251,7 +254,7 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;)
|
||||
for (auto it = m_activeEmitters.begin(); it != m_activeEmitters.end();)
|
||||
{
|
||||
Emitter* emitter = it->get();
|
||||
if (&emitter->getAudioGroup() == grp)
|
||||
@@ -263,7 +266,7 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ;)
|
||||
for (auto it = m_activeSequencers.begin(); it != m_activeSequencers.end();)
|
||||
{
|
||||
Sequencer* seq = it->get();
|
||||
if (&seq->getAudioGroup() == grp)
|
||||
@@ -286,67 +289,11 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
|
||||
m_audioGroups.erase(search);
|
||||
}
|
||||
|
||||
/** Create new Submix (a.k.a 'Studio') within root mix engine */
|
||||
Submix* Engine::addSubmix(Submix* smx)
|
||||
{
|
||||
return &*_allocateSubmix(smx);
|
||||
}
|
||||
|
||||
std::list<Submix>::iterator Engine::_removeSubmix(std::list<Submix>::iterator smx)
|
||||
{
|
||||
/* Delete all voices bound to submix */
|
||||
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ; ++it)
|
||||
{
|
||||
Voice* vox = it->get();
|
||||
Submix* vsmx = vox->getSubmix();
|
||||
if (vsmx == &*smx)
|
||||
vox->kill();
|
||||
}
|
||||
|
||||
/* Delete all sequencers bound to submix */
|
||||
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ; ++it)
|
||||
{
|
||||
Sequencer* seq = it->get();
|
||||
Submix* ssmx = seq->getSubmix();
|
||||
if (ssmx == &*smx)
|
||||
seq->kill();
|
||||
}
|
||||
|
||||
/* Delete all submixes bound to submix */
|
||||
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
|
||||
{
|
||||
Submix* ssmx = it->getParentSubmix();
|
||||
if (ssmx == &*smx)
|
||||
{
|
||||
it = _removeSubmix(it);
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
/* Delete submix */
|
||||
return _destroySubmix(smx);
|
||||
}
|
||||
|
||||
/** Remove Submix and deallocate */
|
||||
void Engine::removeSubmix(Submix* smx)
|
||||
{
|
||||
if (!smx)
|
||||
return;
|
||||
|
||||
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
|
||||
{
|
||||
if (&*it == &*smx)
|
||||
{
|
||||
it = _removeSubmix(it);
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
/** Create new Studio within engine */
|
||||
std::shared_ptr<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); }
|
||||
|
||||
/** Start soundFX playing from loaded audio groups */
|
||||
std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
|
||||
std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::weak_ptr<Studio> smx)
|
||||
{
|
||||
auto search = m_sfxLookup.find(sfxId);
|
||||
if (search == m_sfxLookup.end())
|
||||
@@ -358,8 +305,7 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix*
|
||||
return nullptr;
|
||||
|
||||
std::list<std::shared_ptr<Voice>>::iterator ret =
|
||||
_allocateVoice(*grp, std::get<1>(search->second),
|
||||
32000.0, true, false, smx);
|
||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
|
||||
|
||||
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
||||
if (!(*ret)->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
||||
@@ -374,8 +320,9 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix*
|
||||
}
|
||||
|
||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||
std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
|
||||
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
|
||||
std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||
int sfxId, float minVol, float maxVol, bool doppler,
|
||||
std::weak_ptr<Studio> smx)
|
||||
{
|
||||
auto search = m_sfxLookup.find(sfxId);
|
||||
if (search == m_sfxLookup.end())
|
||||
@@ -387,33 +334,53 @@ std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f&
|
||||
return nullptr;
|
||||
|
||||
std::list<std::shared_ptr<Voice>>::iterator vox =
|
||||
_allocateVoice(*grp, std::get<1>(search->second),
|
||||
32000.0, true, true, smx);
|
||||
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(*vox)));
|
||||
Emitter& ret = *(*emitIt);
|
||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
|
||||
|
||||
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
||||
if (!ret.getVoice()->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
||||
if (!(*vox)->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
|
||||
{
|
||||
ret._destroy();
|
||||
m_activeEmitters.erase(emitIt);
|
||||
_destroyVoice(vox);
|
||||
return {};
|
||||
}
|
||||
|
||||
(*vox)->setPan(entry->panning);
|
||||
ret.setPos(pos);
|
||||
ret.setDir(dir);
|
||||
ret.setMaxDist(maxDist);
|
||||
ret.setFalloff(falloff);
|
||||
ret.setMinVol(minVol);
|
||||
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(),
|
||||
new Emitter(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
|
||||
Emitter& ret = *(*emitIt);
|
||||
|
||||
ret.getVoice()->setPan(entry->panning);
|
||||
ret.setVectors(pos, dir);
|
||||
ret.setMaxVol(maxVol);
|
||||
|
||||
return *emitIt;
|
||||
}
|
||||
|
||||
/** Build listener and add to engine's listener list */
|
||||
std::shared_ptr<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(),
|
||||
new 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Start song playing from loaded audio groups */
|
||||
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId,
|
||||
const unsigned char* arrData, Submix* smx)
|
||||
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData,
|
||||
std::weak_ptr<Studio> smx)
|
||||
{
|
||||
std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId);
|
||||
if (songGrp.second)
|
||||
@@ -439,6 +406,12 @@ std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId,
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Set total volume of engine */
|
||||
void Engine::setVolume(float vol)
|
||||
{
|
||||
m_masterVolume = vol;
|
||||
}
|
||||
|
||||
/** Find voice from VoiceId */
|
||||
std::shared_ptr<Voice> Engine::findVoice(int vid)
|
||||
{
|
||||
@@ -462,7 +435,7 @@ std::shared_ptr<Voice> Engine::findVoice(int vid)
|
||||
/** 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() ;)
|
||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end();)
|
||||
{
|
||||
Voice* vox = it->get();
|
||||
if (vox->m_keygroup == kg)
|
||||
@@ -484,7 +457,7 @@ void Engine::killKeygroup(uint8_t kg, bool 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)
|
||||
for (auto it = m_activeVoices.begin(); it != m_activeVoices.end(); ++it)
|
||||
{
|
||||
Voice* vox = it->get();
|
||||
if (vox->getObjectId() == macroId)
|
||||
@@ -494,5 +467,4 @@ void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
|
||||
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
||||
seq->sendMacroMessage(macroId, val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,22 +4,16 @@
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
static int32_t MIDItoTIME[104] =
|
||||
{ /* [0..103] -> milliseconds */
|
||||
0, 10, 20, 30, 40, 50, 60, 70,
|
||||
80, 90, 100, 110, 110, 120, 130, 140,
|
||||
150, 160, 170, 190, 200, 220, 230, 250,
|
||||
270, 290, 310, 330, 350, 380, 410, 440,
|
||||
470, 500, 540, 580, 620, 660, 710, 760,
|
||||
820, 880, 940, 1000, 1000, 1100, 1200, 1300,
|
||||
1400, 1500, 1600, 1700, 1800, 2000, 2100, 2300,
|
||||
2400, 2600, 2800, 3000, 3200, 3500, 3700, 4000,
|
||||
4300, 4600, 4900, 5300, 5700, 6100, 6500, 7000,
|
||||
7500, 8100, 8600, 9300, 9900, 10000, 11000, 12000,
|
||||
13000, 14000, 15000, 16000, 17000, 18000, 19000, 21000,
|
||||
22000, 24000, 26000, 28000, 30000, 32000, 34000, 37000,
|
||||
39000, 42000, 45000, 49000, 50000, 55000, 60000, 65000
|
||||
};
|
||||
static int32_t MIDItoTIME[104] = {/* [0..103] -> milliseconds */
|
||||
0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110,
|
||||
110, 120, 130, 140, 150, 160, 170, 190, 200, 220, 230, 250,
|
||||
270, 290, 310, 330, 350, 380, 410, 440, 470, 500, 540, 580,
|
||||
620, 660, 710, 760, 820, 880, 940, 1000, 1000, 1100, 1200, 1300,
|
||||
1400, 1500, 1600, 1700, 1800, 2000, 2100, 2300, 2400, 2600, 2800, 3000,
|
||||
3200, 3500, 3700, 4000, 4300, 4600, 4900, 5300, 5700, 6100, 6500, 7000,
|
||||
7500, 8100, 8600, 9300, 9900, 10000, 11000, 12000, 13000, 14000, 15000, 16000,
|
||||
17000, 18000, 19000, 21000, 22000, 24000, 26000, 28000, 30000, 32000, 34000, 37000,
|
||||
39000, 42000, 45000, 49000, 50000, 55000, 60000, 65000};
|
||||
|
||||
void Envelope::reset(const ADSR* adsr)
|
||||
{
|
||||
@@ -231,5 +225,4 @@ float Envelope::advance(double dt)
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "amuse/Listener.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
static void Cross(Vector3f& out, const Vector3f& a, const Vector3f& b)
|
||||
{
|
||||
out[0] = a[1] * b[2] - a[2] * b[1];
|
||||
out[1] = a[2] * b[0] - a[0] * b[2];
|
||||
out[2] = a[0] * b[1] - a[1] * b[0];
|
||||
}
|
||||
|
||||
void Listener::setVectors(const float* pos, const float* dir, const float* heading, const float* up)
|
||||
{
|
||||
for (int i=0 ; i<3 ; ++i)
|
||||
{
|
||||
m_pos[i] = pos[i];
|
||||
m_dir[i] = dir[i];
|
||||
m_heading[i] = heading[i];
|
||||
m_up[i] = up[i];
|
||||
}
|
||||
|
||||
Normalize(m_heading);
|
||||
Normalize(m_up);
|
||||
Cross(m_right, m_heading, m_up);
|
||||
Normalize(m_right);
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace amuse
|
||||
|
||||
void Sequencer::ChannelState::_bringOutYourDead()
|
||||
{
|
||||
for (auto it = m_chanVoxs.begin() ; it != m_chanVoxs.end() ;)
|
||||
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
|
||||
{
|
||||
Voice* vox = it->second.get();
|
||||
vox->_bringOutYourDead();
|
||||
@@ -21,7 +21,7 @@ void Sequencer::ChannelState::_bringOutYourDead()
|
||||
++it;
|
||||
}
|
||||
|
||||
for (auto it = m_keyoffVoxs.begin() ; it != m_keyoffVoxs.end() ;)
|
||||
for (auto it = m_keyoffVoxs.begin(); it != m_keyoffVoxs.end();)
|
||||
{
|
||||
Voice* vox = it->get();
|
||||
vox->_bringOutYourDead();
|
||||
@@ -38,7 +38,7 @@ void Sequencer::_bringOutYourDead()
|
||||
{
|
||||
for (auto& chan : m_chanStates)
|
||||
if (chan)
|
||||
chan->_bringOutYourDead();
|
||||
chan._bringOutYourDead();
|
||||
|
||||
if (!m_arrData && m_dieOnEnd && getVoiceCount() == 0)
|
||||
m_state = SequencerState::Dead;
|
||||
@@ -47,41 +47,29 @@ void Sequencer::_bringOutYourDead()
|
||||
void Sequencer::_destroy()
|
||||
{
|
||||
Entity::_destroy();
|
||||
if (m_submix)
|
||||
{
|
||||
m_engine.removeSubmix(m_submix);
|
||||
m_submix = nullptr;
|
||||
}
|
||||
if (m_studio)
|
||||
m_studio.reset();
|
||||
}
|
||||
|
||||
Sequencer::~Sequencer()
|
||||
{
|
||||
if (m_submix)
|
||||
{
|
||||
m_engine.removeSubmix(m_submix);
|
||||
m_submix = nullptr;
|
||||
}
|
||||
if (m_studio)
|
||||
m_studio.reset();
|
||||
}
|
||||
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||
const SongGroupIndex* songGroup, int setupId, Submix* smx)
|
||||
: Entity(engine, group, groupId), m_songGroup(songGroup)
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId,
|
||||
std::weak_ptr<Studio> studio)
|
||||
: Entity(engine, group, groupId), m_songGroup(songGroup), m_studio(studio)
|
||||
{
|
||||
auto it = m_songGroup->m_midiSetups.find(setupId);
|
||||
if (it != m_songGroup->m_midiSetups.cend())
|
||||
m_midiSetup = it->second->data();
|
||||
|
||||
m_submix = m_engine.addSubmix(smx);
|
||||
m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||
const SFXGroupIndex* sfxGroup, Submix* smx)
|
||||
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup)
|
||||
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup,
|
||||
std::weak_ptr<Studio> studio)
|
||||
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup), m_studio(studio)
|
||||
{
|
||||
m_submix = m_engine.addSubmix(smx);
|
||||
m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f);
|
||||
|
||||
std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX;
|
||||
for (const auto& sfx : sfxGroup->m_sfxEntries)
|
||||
sortSFX[sfx.first] = sfx.second;
|
||||
@@ -91,30 +79,34 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
||||
m_sfxMappings.push_back(sfx.second);
|
||||
}
|
||||
|
||||
Sequencer::ChannelState::~ChannelState()
|
||||
{
|
||||
}
|
||||
Sequencer::ChannelState::~ChannelState() {}
|
||||
|
||||
Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||
: m_parent(parent), m_chanId(chanId)
|
||||
: m_parent(&parent), m_chanId(chanId)
|
||||
{
|
||||
if (m_parent.m_songGroup)
|
||||
if (m_parent->m_songGroup)
|
||||
{
|
||||
if (m_parent.m_midiSetup)
|
||||
if (m_parent->m_midiSetup)
|
||||
{
|
||||
m_setup = &m_parent.m_midiSetup[chanId];
|
||||
m_setup = &m_parent->m_midiSetup[chanId];
|
||||
|
||||
if (chanId == 9)
|
||||
{
|
||||
auto it = m_parent.m_songGroup->m_drumPages.find(m_setup->programNo);
|
||||
if (it != m_parent.m_songGroup->m_drumPages.cend())
|
||||
auto it = m_parent->m_songGroup->m_drumPages.find(m_setup->programNo);
|
||||
if (it != m_parent->m_songGroup->m_drumPages.cend())
|
||||
{
|
||||
m_page = it->second;
|
||||
m_curProgram = m_setup->programNo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = m_parent.m_songGroup->m_normPages.find(m_setup->programNo);
|
||||
if (it != m_parent.m_songGroup->m_normPages.cend())
|
||||
auto it = m_parent->m_songGroup->m_normPages.find(m_setup->programNo);
|
||||
if (it != m_parent->m_songGroup->m_normPages.cend())
|
||||
{
|
||||
m_page = it->second;
|
||||
m_curProgram = m_setup->programNo;
|
||||
}
|
||||
}
|
||||
|
||||
m_curVol = m_setup->volume / 127.f;
|
||||
@@ -126,14 +118,14 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||
{
|
||||
if (chanId == 9)
|
||||
{
|
||||
auto it = m_parent.m_songGroup->m_drumPages.find(0);
|
||||
if (it != m_parent.m_songGroup->m_drumPages.cend())
|
||||
auto it = m_parent->m_songGroup->m_drumPages.find(0);
|
||||
if (it != m_parent->m_songGroup->m_drumPages.cend())
|
||||
m_page = it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = m_parent.m_songGroup->m_normPages.find(0);
|
||||
if (it != m_parent.m_songGroup->m_normPages.cend())
|
||||
auto it = m_parent->m_songGroup->m_normPages.find(0);
|
||||
if (it != m_parent->m_songGroup->m_normPages.cend())
|
||||
m_page = it->second;
|
||||
}
|
||||
|
||||
@@ -143,7 +135,7 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||
m_ctrlVals[0x5d] = 0;
|
||||
}
|
||||
}
|
||||
else if (m_parent.m_sfxGroup)
|
||||
else if (m_parent->m_sfxGroup)
|
||||
{
|
||||
m_curVol = 1.f;
|
||||
m_curPan = 0.f;
|
||||
@@ -158,12 +150,45 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||
void Sequencer::advance(double dt)
|
||||
{
|
||||
if (m_state == SequencerState::Playing)
|
||||
{
|
||||
if (m_stopFadeTime)
|
||||
{
|
||||
float step = dt / m_stopFadeTime * m_stopFadeBeginVol;
|
||||
float vol = std::max(0.f, m_curVol - step);
|
||||
if (vol == 0.f)
|
||||
{
|
||||
m_arrData = nullptr;
|
||||
m_state = SequencerState::Interactive;
|
||||
allOff(true);
|
||||
m_stopFadeTime = 0.f;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
setVolume(vol);
|
||||
}
|
||||
}
|
||||
else if (m_volFadeTime)
|
||||
{
|
||||
float step = dt / m_volFadeTime * std::fabs(m_volFadeTarget - m_volFadeStart);
|
||||
float vol;
|
||||
if (m_curVol < m_volFadeTarget)
|
||||
vol = std::min(m_volFadeTarget, m_curVol + step);
|
||||
else
|
||||
vol = std::max(m_volFadeTarget, m_curVol - step);
|
||||
if (vol == m_volFadeTarget)
|
||||
m_volFadeTime = 0.f;
|
||||
else
|
||||
setVolume(vol);
|
||||
}
|
||||
|
||||
if (m_songState.advance(*this, dt))
|
||||
{
|
||||
m_arrData = nullptr;
|
||||
m_state = SequencerState::Interactive;
|
||||
allOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Sequencer::ChannelState::getVoiceCount() const
|
||||
@@ -181,13 +206,13 @@ size_t Sequencer::getVoiceCount() const
|
||||
size_t ret = 0;
|
||||
for (const auto& chan : m_chanStates)
|
||||
if (chan)
|
||||
ret += chan->getVoiceCount();
|
||||
ret += chan.getVoiceCount();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity)
|
||||
{
|
||||
if (m_parent.m_songGroup && !m_page)
|
||||
if (m_parent->m_songGroup && !m_page)
|
||||
return {};
|
||||
|
||||
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
||||
@@ -214,35 +239,34 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
|
||||
m_chanVoxs.erase(keySearch);
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Voice>>::iterator ret =
|
||||
m_parent.m_engine._allocateVoice(m_parent.m_audioGroup,
|
||||
m_parent.m_groupId, 32000.0,
|
||||
true, false, m_parent.m_submix);
|
||||
std::list<std::shared_ptr<Voice>>::iterator ret = m_parent->m_engine._allocateVoice(
|
||||
m_parent->m_audioGroup, m_parent->m_groupId, NativeSampleRate, true, false, m_parent->m_studio);
|
||||
if (*ret)
|
||||
{
|
||||
m_chanVoxs[note] = *ret;
|
||||
(*ret)->installCtrlValues(m_ctrlVals);
|
||||
|
||||
ObjectId oid;
|
||||
if (m_parent.m_songGroup)
|
||||
oid = (m_parent.m_audioGroup.getDataFormat() == DataFormat::PC) ? m_page->objId : SBig(m_page->objId);
|
||||
else if (m_parent.m_sfxMappings.size())
|
||||
if (m_parent->m_songGroup)
|
||||
oid = (m_parent->m_audioGroup.getDataFormat() == DataFormat::PC) ? m_page->objId : SBig(m_page->objId);
|
||||
else if (m_parent->m_sfxMappings.size())
|
||||
{
|
||||
size_t lookupIdx = note % m_parent.m_sfxMappings.size();
|
||||
const SFXGroupIndex::SFXEntry* sfxEntry = m_parent.m_sfxMappings[lookupIdx];
|
||||
oid = (m_parent.m_audioGroup.getDataFormat() == DataFormat::PC) ? sfxEntry->objId : SBig(sfxEntry->objId);
|
||||
size_t lookupIdx = note % m_parent->m_sfxMappings.size();
|
||||
const SFXGroupIndex::SFXEntry* sfxEntry = m_parent->m_sfxMappings[lookupIdx];
|
||||
oid = (m_parent->m_audioGroup.getDataFormat() == DataFormat::PC) ? sfxEntry->objId : SBig(sfxEntry->objId);
|
||||
note = sfxEntry->defKey;
|
||||
}
|
||||
else
|
||||
return {};
|
||||
|
||||
if (!(*ret)->loadSoundObject(oid, 0, m_parent.m_ticksPerSec, note, velocity, m_ctrlVals[1]))
|
||||
if (!(*ret)->loadSoundObject(oid, 0, m_parent->m_ticksPerSec, note, velocity, m_ctrlVals[1]))
|
||||
{
|
||||
m_parent.m_engine._destroyVoice(ret);
|
||||
m_parent->m_engine._destroyVoice(ret);
|
||||
return {};
|
||||
}
|
||||
(*ret)->setVolume(m_parent.m_curVol * m_curVol);
|
||||
(*ret)->setVolume(m_parent->m_curVol * m_curVol);
|
||||
(*ret)->setReverbVol(m_ctrlVals[0x5b] / 127.f);
|
||||
(*ret)->setAuxBVol(m_ctrlVals[0x5d] / 127.f);
|
||||
(*ret)->setPan(m_curPan);
|
||||
(*ret)->setPitchWheel(m_curPitchWheel);
|
||||
|
||||
@@ -261,9 +285,9 @@ std::shared_ptr<Voice> Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velo
|
||||
return {};
|
||||
|
||||
if (!m_chanStates[chan])
|
||||
m_chanStates[chan].emplace(*this, chan);
|
||||
m_chanStates[chan] = ChannelState(*this, chan);
|
||||
|
||||
return m_chanStates[chan]->keyOn(note, velocity);
|
||||
return m_chanStates[chan].keyOn(note, velocity);
|
||||
}
|
||||
|
||||
void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
|
||||
@@ -284,7 +308,7 @@ void Sequencer::keyOff(uint8_t chan, uint8_t note, uint8_t velocity)
|
||||
if (chan > 15 || !m_chanStates[chan])
|
||||
return;
|
||||
|
||||
m_chanStates[chan]->keyOff(note, velocity);
|
||||
m_chanStates[chan].keyOff(note, velocity);
|
||||
}
|
||||
|
||||
void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
|
||||
@@ -303,18 +327,19 @@ void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
|
||||
case 10:
|
||||
setPan(val / 64.f - 1.f);
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Sequencer::ChannelState::programChange(int8_t prog)
|
||||
{
|
||||
if (m_parent.m_songGroup)
|
||||
if (m_parent->m_songGroup)
|
||||
{
|
||||
if (m_chanId == 9)
|
||||
{
|
||||
auto it = m_parent.m_songGroup->m_drumPages.find(prog);
|
||||
if (it != m_parent.m_songGroup->m_drumPages.cend())
|
||||
auto it = m_parent->m_songGroup->m_drumPages.find(prog);
|
||||
if (it != m_parent->m_songGroup->m_drumPages.cend())
|
||||
{
|
||||
m_page = it->second;
|
||||
m_curProgram = prog;
|
||||
@@ -323,8 +348,8 @@ bool Sequencer::ChannelState::programChange(int8_t prog)
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = m_parent.m_songGroup->m_normPages.find(prog);
|
||||
if (it != m_parent.m_songGroup->m_normPages.cend())
|
||||
auto it = m_parent->m_songGroup->m_normPages.find(prog);
|
||||
if (it != m_parent->m_songGroup->m_normPages.cend())
|
||||
{
|
||||
m_page = it->second;
|
||||
m_curProgram = prog;
|
||||
@@ -356,10 +381,19 @@ void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val)
|
||||
if (chan > 15)
|
||||
return;
|
||||
|
||||
if (!m_chanStates[chan])
|
||||
m_chanStates[chan].emplace(*this, chan);
|
||||
if (ctrl == 0x66)
|
||||
{
|
||||
printf("Loop Start\n");
|
||||
}
|
||||
else if (ctrl == 0x67)
|
||||
{
|
||||
printf("Loop End\n");
|
||||
}
|
||||
|
||||
m_chanStates[chan]->setCtrlValue(ctrl, val);
|
||||
if (!m_chanStates[chan])
|
||||
m_chanStates[chan] = ChannelState(*this, chan);
|
||||
|
||||
m_chanStates[chan].setCtrlValue(ctrl, val);
|
||||
}
|
||||
|
||||
void Sequencer::ChannelState::setPitchWheel(float pitchWheel)
|
||||
@@ -377,19 +411,16 @@ void Sequencer::setPitchWheel(uint8_t chan, float pitchWheel)
|
||||
return;
|
||||
|
||||
if (!m_chanStates[chan])
|
||||
m_chanStates[chan].emplace(*this, chan);
|
||||
m_chanStates[chan] = ChannelState(*this, chan);
|
||||
|
||||
m_chanStates[chan]->setPitchWheel(pitchWheel);
|
||||
m_chanStates[chan].setPitchWheel(pitchWheel);
|
||||
}
|
||||
|
||||
void Sequencer::setTempo(double ticksPerSec)
|
||||
{
|
||||
m_ticksPerSec = ticksPerSec;
|
||||
}
|
||||
void Sequencer::setTempo(double ticksPerSec) { m_ticksPerSec = ticksPerSec; }
|
||||
|
||||
void Sequencer::ChannelState::allOff()
|
||||
{
|
||||
for (auto it = m_chanVoxs.begin() ; it != m_chanVoxs.end() ;)
|
||||
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
|
||||
{
|
||||
if (it->second == m_lastVoice.lock())
|
||||
m_lastVoice.reset();
|
||||
@@ -406,18 +437,18 @@ void Sequencer::allOff(bool now)
|
||||
{
|
||||
if (chan)
|
||||
{
|
||||
for (const auto& vox : chan->m_chanVoxs)
|
||||
for (const auto& vox : chan.m_chanVoxs)
|
||||
vox.second->kill();
|
||||
for (const auto& vox : chan->m_keyoffVoxs)
|
||||
for (const auto& vox : chan.m_keyoffVoxs)
|
||||
vox->kill();
|
||||
chan->m_chanVoxs.clear();
|
||||
chan->m_keyoffVoxs.clear();
|
||||
chan.m_chanVoxs.clear();
|
||||
chan.m_keyoffVoxs.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
for (auto& chan : m_chanStates)
|
||||
if (chan)
|
||||
chan->allOff();
|
||||
chan.allOff();
|
||||
}
|
||||
|
||||
void Sequencer::allOff(uint8_t chan, bool now)
|
||||
@@ -427,20 +458,20 @@ void Sequencer::allOff(uint8_t chan, bool now)
|
||||
|
||||
if (now)
|
||||
{
|
||||
for (const auto& vox : m_chanStates[chan]->m_chanVoxs)
|
||||
for (const auto& vox : m_chanStates[chan].m_chanVoxs)
|
||||
vox.second->kill();
|
||||
for (const auto& vox : m_chanStates[chan]->m_keyoffVoxs)
|
||||
for (const auto& vox : m_chanStates[chan].m_keyoffVoxs)
|
||||
vox->kill();
|
||||
m_chanStates[chan]->m_chanVoxs.clear();
|
||||
m_chanStates[chan]->m_keyoffVoxs.clear();
|
||||
m_chanStates[chan].m_chanVoxs.clear();
|
||||
m_chanStates[chan].m_keyoffVoxs.clear();
|
||||
}
|
||||
else
|
||||
m_chanStates[chan]->allOff();
|
||||
m_chanStates[chan].allOff();
|
||||
}
|
||||
|
||||
void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
|
||||
{
|
||||
for (auto it = m_chanVoxs.begin() ; it != m_chanVoxs.end() ;)
|
||||
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
|
||||
{
|
||||
Voice* vox = it->second.get();
|
||||
if (vox->m_keygroup == kg)
|
||||
@@ -462,7 +493,7 @@ void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
|
||||
|
||||
if (now)
|
||||
{
|
||||
for (auto it = m_keyoffVoxs.begin() ; it != m_keyoffVoxs.end() ;)
|
||||
for (auto it = m_keyoffVoxs.begin(); it != m_keyoffVoxs.end();)
|
||||
{
|
||||
Voice* vox = it->get();
|
||||
if (vox->m_keygroup == kg)
|
||||
@@ -479,7 +510,7 @@ void Sequencer::killKeygroup(uint8_t kg, bool now)
|
||||
{
|
||||
for (auto& chan : m_chanStates)
|
||||
if (chan)
|
||||
chan->killKeygroup(kg, now);
|
||||
chan.killKeygroup(kg, now);
|
||||
}
|
||||
|
||||
std::shared_ptr<Voice> Sequencer::ChannelState::findVoice(int vid)
|
||||
@@ -499,7 +530,7 @@ std::shared_ptr<Voice> Sequencer::findVoice(int vid)
|
||||
{
|
||||
if (chan)
|
||||
{
|
||||
std::shared_ptr<Voice> ret = chan->findVoice(vid);
|
||||
std::shared_ptr<Voice> ret = chan.findVoice(vid);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -527,7 +558,7 @@ void Sequencer::sendMacroMessage(ObjectId macroId, int32_t val)
|
||||
{
|
||||
for (auto& chan : m_chanStates)
|
||||
if (chan)
|
||||
chan->sendMacroMessage(macroId, val);
|
||||
chan.sendMacroMessage(macroId, val);
|
||||
}
|
||||
|
||||
void Sequencer::playSong(const unsigned char* arrData, bool dieOnEnd)
|
||||
@@ -539,17 +570,25 @@ void Sequencer::playSong(const unsigned char* arrData, bool dieOnEnd)
|
||||
m_state = SequencerState::Playing;
|
||||
}
|
||||
|
||||
void Sequencer::stopSong(bool now)
|
||||
void Sequencer::stopSong(float fadeTime, bool now)
|
||||
{
|
||||
allOff(now);
|
||||
m_arrData = nullptr;
|
||||
m_state = SequencerState::Interactive;
|
||||
if (fadeTime == 0.f)
|
||||
{
|
||||
allOff(now);
|
||||
m_arrData = nullptr;
|
||||
m_state = SequencerState::Interactive;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stopFadeTime = fadeTime;
|
||||
m_stopFadeBeginVol = m_curVol;
|
||||
}
|
||||
}
|
||||
|
||||
void Sequencer::ChannelState::setVolume(float vol)
|
||||
{
|
||||
m_curVol = vol;
|
||||
float voxVol = m_parent.m_curVol * m_curVol;
|
||||
float voxVol = m_parent->m_curVol * m_curVol;
|
||||
for (const auto& v : m_chanVoxs)
|
||||
{
|
||||
Voice* vox = v.second.get();
|
||||
@@ -577,20 +616,36 @@ void Sequencer::ChannelState::setPan(float pan)
|
||||
}
|
||||
}
|
||||
|
||||
void Sequencer::setVolume(float vol)
|
||||
void Sequencer::setVolume(float vol, float fadeTime)
|
||||
{
|
||||
m_curVol = vol;
|
||||
for (auto& chan : m_chanStates)
|
||||
if (chan)
|
||||
chan->setVolume(chan->m_curVol);
|
||||
if (fadeTime == 0.f)
|
||||
{
|
||||
m_curVol = vol;
|
||||
for (auto& chan : m_chanStates)
|
||||
if (chan)
|
||||
chan.setVolume(chan.m_curVol);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_volFadeTime = fadeTime;
|
||||
m_volFadeTarget = vol;
|
||||
m_volFadeStart = m_curVol;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t Sequencer::getChanProgram(int8_t chanId) const
|
||||
{
|
||||
if (chanId > 15 || !m_chanStates[chanId])
|
||||
if (chanId > 15)
|
||||
return 0;
|
||||
|
||||
return m_chanStates[chanId]->m_curProgram;
|
||||
if (!m_chanStates[chanId])
|
||||
{
|
||||
if (!m_midiSetup)
|
||||
return 0;
|
||||
return m_midiSetup[chanId].programNo;
|
||||
}
|
||||
|
||||
return m_chanStates[chanId].m_curProgram;
|
||||
}
|
||||
|
||||
bool Sequencer::setChanProgram(int8_t chanId, int8_t prog)
|
||||
@@ -599,9 +654,9 @@ bool Sequencer::setChanProgram(int8_t chanId, int8_t prog)
|
||||
return false;
|
||||
|
||||
if (!m_chanStates[chanId])
|
||||
m_chanStates[chanId].emplace(*this, chanId);
|
||||
m_chanStates[chanId] = ChannelState(*this, chanId);
|
||||
|
||||
return m_chanStates[chanId]->programChange(prog);
|
||||
return m_chanStates[chanId].programChange(prog);
|
||||
}
|
||||
|
||||
void Sequencer::nextChanProgram(int8_t chanId)
|
||||
@@ -610,9 +665,9 @@ void Sequencer::nextChanProgram(int8_t chanId)
|
||||
return;
|
||||
|
||||
if (!m_chanStates[chanId])
|
||||
m_chanStates[chanId].emplace(*this, chanId);
|
||||
m_chanStates[chanId] = ChannelState(*this, chanId);
|
||||
|
||||
return m_chanStates[chanId]->nextProgram();
|
||||
return m_chanStates[chanId].nextProgram();
|
||||
}
|
||||
|
||||
void Sequencer::prevChanProgram(int8_t chanId)
|
||||
@@ -621,9 +676,8 @@ void Sequencer::prevChanProgram(int8_t chanId)
|
||||
return;
|
||||
|
||||
if (!m_chanStates[chanId])
|
||||
m_chanStates[chanId].emplace(*this, chanId);
|
||||
m_chanStates[chanId] = ChannelState(*this, chanId);
|
||||
|
||||
return m_chanStates[chanId]->prevProgram();
|
||||
return m_chanStates[chanId].prevProgram();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1499
lib/SongConverter.cpp
Normal file
@@ -18,7 +18,10 @@ static uint32_t DecodeRLE(const unsigned char*& data)
|
||||
++data;
|
||||
thisPart = thisPart * 256 + *data;
|
||||
if (thisPart == 0)
|
||||
{
|
||||
++data;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (thisPart == 32767)
|
||||
@@ -68,28 +71,17 @@ static uint32_t DecodeTimeRLE(const unsigned char*& data)
|
||||
|
||||
void SongState::Header::swapBig()
|
||||
{
|
||||
m_version = SBig(m_version);
|
||||
m_chanIdxOff = SBig(m_chanIdxOff);
|
||||
m_trackIdxOff = SBig(m_trackIdxOff);
|
||||
m_regionIdxOff = SBig(m_regionIdxOff);
|
||||
m_chanMapOff = SBig(m_chanMapOff);
|
||||
m_tempoTableOff = SBig(m_tempoTableOff);
|
||||
m_initialTempo = SBig(m_initialTempo);
|
||||
m_unkOff = SBig(m_unkOff);
|
||||
for (int i=0 ; i<64 ; ++i)
|
||||
m_chanOffs[i] = SBig(m_chanOffs[i]);
|
||||
}
|
||||
|
||||
void SongState::ChanHeader::swapBig()
|
||||
bool SongState::TrackRegion::indexValid(bool bigEndian) const
|
||||
{
|
||||
m_startTick = SBig(m_startTick);
|
||||
m_unk1 = SBig(m_unk1);
|
||||
m_unk2 = SBig(m_unk2);
|
||||
m_dataIndex = SBig(m_dataIndex);
|
||||
m_unk3 = SBig(m_unk3);
|
||||
m_startTick2 = SBig(m_startTick2);
|
||||
m_unk4 = SBig(m_unk4);
|
||||
m_unk5 = SBig(m_unk5);
|
||||
m_unk6 = SBig(m_unk6);
|
||||
m_unk7 = SBig(m_unk7);
|
||||
return (bigEndian ? SBig(m_regionIndex) : m_regionIndex) >= 0;
|
||||
}
|
||||
|
||||
void SongState::TempoChange::swapBig()
|
||||
@@ -98,57 +90,265 @@ void SongState::TempoChange::swapBig()
|
||||
m_tempo = SBig(m_tempo);
|
||||
}
|
||||
|
||||
void SongState::Channel::Header::swapBig()
|
||||
void SongState::Track::Header::swapBig()
|
||||
{
|
||||
m_type = SBig(m_type);
|
||||
m_pitchOff = SBig(m_pitchOff);
|
||||
m_modOff = SBig(m_modOff);
|
||||
}
|
||||
|
||||
SongState::Channel::Channel(SongState& parent, uint8_t midiChan, uint32_t startTick,
|
||||
const unsigned char* song, const unsigned char* chan)
|
||||
: m_parent(parent), m_midiChan(midiChan), m_startTick(startTick), m_dataBase(chan + 12)
|
||||
SongState::Track::Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions)
|
||||
: m_parent(&parent), m_midiChan(midiChan), m_curRegion(nullptr), m_nextRegion(regions)
|
||||
{
|
||||
m_data = m_dataBase;
|
||||
|
||||
Header header = *reinterpret_cast<const Header*>(chan);
|
||||
header.swapBig();
|
||||
if (header.m_type != 8)
|
||||
{
|
||||
m_data = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.m_pitchOff)
|
||||
m_pitchWheelData = song + header.m_pitchOff;
|
||||
if (header.m_modOff)
|
||||
m_modWheelData = song + header.m_modOff;
|
||||
|
||||
m_waitCountdown = startTick;
|
||||
m_lastPitchTick = startTick;
|
||||
m_lastModTick = startTick;
|
||||
m_waitCountdown += int32_t(DecodeTimeRLE(m_data));
|
||||
for (int i = 0; i < 128; ++i)
|
||||
m_remNoteLengths[i] = std::numeric_limits<decltype(m_remNoteLengths)::value_type>::min();
|
||||
}
|
||||
|
||||
void SongState::initialize(const unsigned char* ptr)
|
||||
void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
||||
{
|
||||
m_header = *reinterpret_cast<const Header*>(ptr);
|
||||
m_header.swapBig();
|
||||
m_curRegion = region;
|
||||
uint32_t regionIdx = (m_parent->m_bigEndian ? SBig(m_curRegion->m_regionIndex) : m_curRegion->m_regionIndex);
|
||||
m_nextRegion = &m_curRegion[1];
|
||||
|
||||
/* Initialize all channels */
|
||||
for (int i=0 ; i<64 ; ++i)
|
||||
m_data = m_parent->m_songData +
|
||||
(m_parent->m_bigEndian ? SBig(m_parent->m_regionIdx[regionIdx]) : m_parent->m_regionIdx[regionIdx]);
|
||||
|
||||
Header header = *reinterpret_cast<const Header*>(m_data);
|
||||
if (m_parent->m_bigEndian)
|
||||
header.swapBig();
|
||||
m_data += 12;
|
||||
|
||||
if (header.m_pitchOff)
|
||||
m_pitchWheelData = m_parent->m_songData + header.m_pitchOff;
|
||||
if (header.m_modOff)
|
||||
m_modWheelData = m_parent->m_songData + header.m_modOff;
|
||||
|
||||
m_eventWaitCountdown = 0;
|
||||
m_lastPitchTick = m_parent->m_curTick;
|
||||
m_lastPitchVal = 0;
|
||||
m_lastModTick = m_parent->m_curTick;
|
||||
m_lastModVal = 0;
|
||||
if (seq)
|
||||
{
|
||||
if (m_header.m_chanOffs[i])
|
||||
seq->setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f));
|
||||
seq->setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||
}
|
||||
if (m_parent->m_sngVersion == 1)
|
||||
m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data));
|
||||
else
|
||||
{
|
||||
int32_t absTick = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data))
|
||||
: *reinterpret_cast<const int32_t*>(m_data));
|
||||
m_eventWaitCountdown = absTick;
|
||||
m_lastN64EventTick = absTick;
|
||||
m_data += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void SongState::Track::advanceRegion(Sequencer* seq) { setRegion(seq, m_nextRegion); }
|
||||
|
||||
int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
|
||||
{
|
||||
isBig = ptr[0] == 0;
|
||||
Header header = *reinterpret_cast<const Header*>(ptr);
|
||||
if (isBig)
|
||||
header.swapBig();
|
||||
const uint32_t* trackIdx = reinterpret_cast<const uint32_t*>(ptr + header.m_trackIdxOff);
|
||||
const uint32_t* regionIdxTable = reinterpret_cast<const uint32_t*>(ptr + header.m_regionIdxOff);
|
||||
|
||||
/* First determine maximum index of MIDI regions across all tracks */
|
||||
uint32_t maxRegionIdx = 0;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
if (trackIdx[i])
|
||||
{
|
||||
ChanHeader cHeader = *reinterpret_cast<const ChanHeader*>(ptr + m_header.m_chanOffs[i]);
|
||||
cHeader.swapBig();
|
||||
const uint32_t* chanIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_chanIdxOff);
|
||||
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
|
||||
m_channels[i].emplace(*this, chanMap[i], cHeader.m_startTick, ptr,
|
||||
ptr + SBig(chanIdx[cHeader.m_dataIndex]));
|
||||
const TrackRegion* region = nullptr;
|
||||
const TrackRegion* nextRegion =
|
||||
reinterpret_cast<const TrackRegion*>(ptr + (isBig ? SBig(trackIdx[i]) : trackIdx[i]));
|
||||
|
||||
/* Iterate all regions */
|
||||
while (nextRegion->indexValid(isBig))
|
||||
{
|
||||
region = nextRegion;
|
||||
uint32_t regionIdx = (isBig ? SBig(region->m_regionIndex) : region->m_regionIndex);
|
||||
maxRegionIdx = std::max(maxRegionIdx, regionIdx);
|
||||
nextRegion = ®ion[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform 2 trials, first assuming revised format (more likely) */
|
||||
int v = 1;
|
||||
for (; v >= 0; --v)
|
||||
{
|
||||
bool bad = false;
|
||||
|
||||
/* Validate all tracks */
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
if (trackIdx[i])
|
||||
{
|
||||
const TrackRegion* region = nullptr;
|
||||
const TrackRegion* nextRegion =
|
||||
reinterpret_cast<const TrackRegion*>(ptr + (isBig ? SBig(trackIdx[i]) : trackIdx[i]));
|
||||
|
||||
/* Iterate all regions */
|
||||
while (nextRegion->indexValid(isBig))
|
||||
{
|
||||
region = nextRegion;
|
||||
uint32_t regionIdx = (isBig ? SBig(region->m_regionIndex) : region->m_regionIndex);
|
||||
nextRegion = ®ion[1];
|
||||
|
||||
const unsigned char* data =
|
||||
ptr + (isBig ? SBig(regionIdxTable[regionIdx]) : regionIdxTable[regionIdx]);
|
||||
|
||||
/* Can't reliably validate final region */
|
||||
if (regionIdx == maxRegionIdx)
|
||||
continue;
|
||||
|
||||
/* Expected end pointer (next region) */
|
||||
const unsigned char* expectedEnd =
|
||||
ptr + (isBig ? SBig(regionIdxTable[regionIdx + 1]) : regionIdxTable[regionIdx + 1]);
|
||||
|
||||
Track::Header header = *reinterpret_cast<const Track::Header*>(data);
|
||||
if (isBig)
|
||||
header.swapBig();
|
||||
data += 12;
|
||||
|
||||
/* continuous pitch data */
|
||||
if (header.m_pitchOff)
|
||||
{
|
||||
const unsigned char* dptr = ptr + header.m_pitchOff;
|
||||
while (DecodeRLE(dptr) != 0xffffffff)
|
||||
{
|
||||
DecodeContinuousRLE(dptr);
|
||||
}
|
||||
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* continuous modulation data */
|
||||
if (header.m_modOff)
|
||||
{
|
||||
const unsigned char* dptr = ptr + header.m_modOff;
|
||||
while (DecodeRLE(dptr) != 0xffffffff)
|
||||
{
|
||||
DecodeContinuousRLE(dptr);
|
||||
}
|
||||
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
if (v == 1)
|
||||
{
|
||||
/* Revised */
|
||||
while (true)
|
||||
{
|
||||
/* Delta time */
|
||||
DecodeTimeRLE(data);
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(data) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
data += 2;
|
||||
break;
|
||||
}
|
||||
else if (data[0] & 0x80 && data[1] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
data += 2;
|
||||
}
|
||||
else if (data[0] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
data += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note */
|
||||
data += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Legacy */
|
||||
while (true)
|
||||
{
|
||||
/* Delta-time */
|
||||
data += 4;
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(&data[2]) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
data += 4;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((data[2] & 0x80) != 0x80)
|
||||
{
|
||||
/* Note */
|
||||
}
|
||||
else if (data[2] & 0x80 && data[3] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
}
|
||||
else if (data[2] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
}
|
||||
data += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data < (expectedEnd - 4) || (data > expectedEnd))
|
||||
{
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bad)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bad)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
bool SongState::initialize(const unsigned char* ptr)
|
||||
{
|
||||
m_sngVersion = DetectVersion(ptr, m_bigEndian);
|
||||
if (m_sngVersion < 0)
|
||||
return false;
|
||||
|
||||
m_songData = ptr;
|
||||
m_header = *reinterpret_cast<const Header*>(ptr);
|
||||
if (m_bigEndian)
|
||||
m_header.swapBig();
|
||||
const uint32_t* trackIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_trackIdxOff);
|
||||
m_regionIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_regionIdxOff);
|
||||
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
|
||||
|
||||
/* Initialize all tracks */
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
if (trackIdx[i])
|
||||
{
|
||||
const TrackRegion* region =
|
||||
reinterpret_cast<const TrackRegion*>(ptr + (m_bigEndian ? SBig(trackIdx[i]) : trackIdx[i]));
|
||||
m_tracks[i] = Track(*this, chanMap[i], region);
|
||||
}
|
||||
else
|
||||
m_channels[i] = std::experimental::nullopt;
|
||||
m_tracks[i] = Track();
|
||||
}
|
||||
|
||||
/* Initialize tempo */
|
||||
@@ -157,22 +357,49 @@ void SongState::initialize(const unsigned char* ptr)
|
||||
else
|
||||
m_tempoPtr = nullptr;
|
||||
|
||||
m_tempo = m_header.m_initialTempo;
|
||||
m_tempo = m_header.m_initialTempo & 0x7fffffff;
|
||||
m_curTick = 0;
|
||||
m_songState = SongPlayState::Playing;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||
bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
|
||||
{
|
||||
if (!m_data)
|
||||
return true;
|
||||
int32_t endTick = m_parent->m_curTick + ticks;
|
||||
|
||||
int32_t endTick = m_parent.m_curTick + ticks;
|
||||
/* Advance region if needed */
|
||||
while (m_nextRegion->indexValid(m_parent->m_bigEndian))
|
||||
{
|
||||
uint32_t nextRegTick = (m_parent->m_bigEndian ? SBig(m_nextRegion->m_startTick) : m_nextRegion->m_startTick);
|
||||
if (uint32_t(endTick) > nextRegTick)
|
||||
advanceRegion(&seq);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stop finished notes */
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
constexpr decltype(m_remNoteLengths)::value_type MIN = std::numeric_limits<decltype(MIN)>::min();
|
||||
if (m_remNoteLengths[i] != MIN)
|
||||
{
|
||||
m_remNoteLengths[i] -= ticks;
|
||||
if (m_remNoteLengths[i] <= 0)
|
||||
{
|
||||
seq.keyOff(m_midiChan, i, 0);
|
||||
m_remNoteLengths[i] = MIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_data)
|
||||
return !m_nextRegion->indexValid(m_parent->m_bigEndian);
|
||||
|
||||
/* Update continuous pitch data */
|
||||
if (m_pitchWheelData)
|
||||
{
|
||||
int32_t pitchTick = m_parent.m_curTick;
|
||||
int32_t pitchTick = m_parent->m_curTick;
|
||||
int32_t remPitchTicks = ticks;
|
||||
while (pitchTick < endTick)
|
||||
{
|
||||
@@ -205,7 +432,7 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||
/* Update continuous modulation data */
|
||||
if (m_modWheelData)
|
||||
{
|
||||
int32_t modTick = m_parent.m_curTick;
|
||||
int32_t modTick = m_parent->m_curTick;
|
||||
int32_t remModTicks = ticks;
|
||||
while (modTick < endTick)
|
||||
{
|
||||
@@ -224,7 +451,7 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||
m_lastModTick = nextTick;
|
||||
remModTicks -= (nextTick - modTick);
|
||||
modTick = nextTick;
|
||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, (m_lastModVal + 8192) * 128 / 16384, 127));
|
||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||
continue;
|
||||
}
|
||||
remModTicks -= (nextTick - modTick);
|
||||
@@ -235,61 +462,115 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop finished notes */
|
||||
for (int i=0 ; i<128 ; ++i)
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
if (m_parent->m_sngVersion == 1)
|
||||
{
|
||||
if (m_remNoteLengths[i])
|
||||
/* Revision */
|
||||
while (true)
|
||||
{
|
||||
if (m_remNoteLengths[i] <= ticks)
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
seq.keyOff(m_midiChan, i, 0);
|
||||
m_remNoteLengths[i] = 0;
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(m_data) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return !m_nextRegion->indexValid(m_parent->m_bigEndian);
|
||||
}
|
||||
else if (m_data[0] & 0x80 && m_data[1] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[0] & 0x7f;
|
||||
uint8_t ctrl = m_data[1] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
m_data += 2;
|
||||
}
|
||||
else if (m_data[0] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[0] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
m_data += 2;
|
||||
}
|
||||
else
|
||||
m_remNoteLengths[i] -= ticks;
|
||||
{
|
||||
/* Note */
|
||||
uint8_t note = m_data[0] & 0x7f;
|
||||
uint8_t vel = m_data[1] & 0x7f;
|
||||
uint16_t length = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data + 2))
|
||||
: *reinterpret_cast<const uint16_t*>(m_data + 2));
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
m_remNoteLengths[note] = length;
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
m_eventWaitCountdown += int32_t(DecodeTimeRLE(m_data));
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
while (true)
|
||||
else
|
||||
{
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_waitCountdown)
|
||||
/* Legacy */
|
||||
while (true)
|
||||
{
|
||||
m_waitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_waitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(m_data) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return true;
|
||||
}
|
||||
else if (m_data[0] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[0] & 0x7f;
|
||||
uint8_t ctrl = m_data[1] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
m_data += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note */
|
||||
uint8_t note = m_data[0] & 0x7f;
|
||||
uint8_t vel = m_data[1] & 0x7f;
|
||||
uint16_t length = SBig(*reinterpret_cast<const uint16_t*>(m_data + 2));
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
m_remNoteLengths[note] = length;
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(&m_data[2]) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return !m_nextRegion->indexValid(m_parent->m_bigEndian);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_data[2] & 0x80) != 0x80)
|
||||
{
|
||||
/* Note */
|
||||
uint16_t length = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data))
|
||||
: *reinterpret_cast<const uint16_t*>(m_data));
|
||||
uint8_t note = m_data[2] & 0x7f;
|
||||
uint8_t vel = m_data[3] & 0x7f;
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
m_remNoteLengths[note] = length;
|
||||
}
|
||||
else if (m_data[2] & 0x80 && m_data[3] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[2] & 0x7f;
|
||||
uint8_t ctrl = m_data[3] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
}
|
||||
else if (m_data[2] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[2] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
}
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
int32_t absTick = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data))
|
||||
: *reinterpret_cast<const int32_t*>(m_data));
|
||||
m_eventWaitCountdown += absTick - m_lastN64EventTick;
|
||||
m_lastN64EventTick = absTick;
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
m_waitCountdown += int32_t(DecodeTimeRLE(m_data));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -317,7 +598,8 @@ bool SongState::advance(Sequencer& seq, double dt)
|
||||
if (m_tempoPtr && m_tempoPtr->m_tick != 0xffffffff)
|
||||
{
|
||||
TempoChange change = *m_tempoPtr;
|
||||
change.swapBig();
|
||||
if (m_bigEndian)
|
||||
change.swapBig();
|
||||
|
||||
if (m_curTick + remTicks > change.m_tick)
|
||||
remTicks = change.m_tick - m_curTick;
|
||||
@@ -325,17 +607,17 @@ bool SongState::advance(Sequencer& seq, double dt)
|
||||
if (remTicks <= 0)
|
||||
{
|
||||
/* Turn over tempo */
|
||||
m_tempo = change.m_tempo;
|
||||
m_tempo = change.m_tempo & 0x7fffffff;
|
||||
seq.setTempo(m_tempo * 384 / 60);
|
||||
++m_tempoPtr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance all channels */
|
||||
for (std::experimental::optional<Channel>& chan : m_channels)
|
||||
if (chan)
|
||||
done &= chan->advance(seq, remTicks);
|
||||
/* Advance all tracks */
|
||||
for (Track& trk : m_tracks)
|
||||
if (trk)
|
||||
done &= trk.advance(seq, remTicks);
|
||||
|
||||
m_curTick += remTicks;
|
||||
|
||||
@@ -349,5 +631,4 @@ bool SongState::advance(Sequencer& seq, double dt)
|
||||
m_songState = SongPlayState::Stopped;
|
||||
return done;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,14 +4,28 @@
|
||||
#include "amuse/Common.hpp"
|
||||
#include "amuse/AudioGroup.hpp"
|
||||
#include "amuse/AudioGroupPool.hpp"
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
|
||||
/* Squelch Win32 macro pollution >.< */
|
||||
#undef SendMessage
|
||||
#undef GetMessage
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
int SoundMacroState::_assertPC(int pc, uint32_t size)
|
||||
{
|
||||
if (pc == -1)
|
||||
return -1;
|
||||
int cmdCount = (size - sizeof(Header)) / sizeof(Command);
|
||||
if (pc >= cmdCount)
|
||||
{
|
||||
fprintf(stderr, "SoundMacro PC bounds exceeded [%d/%d]\n", pc, cmdCount);
|
||||
abort();
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
void SoundMacroState::Header::swapBig()
|
||||
{
|
||||
m_size = SBig(m_size);
|
||||
@@ -25,18 +39,17 @@ void SoundMacroState::Command::swapBig()
|
||||
words[1] = SBig(words[1]);
|
||||
}
|
||||
|
||||
void SoundMacroState::Evaluator::addComponent(uint8_t midiCtrl, float scale,
|
||||
Combine combine, VarType varType)
|
||||
void SoundMacroState::Evaluator::addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType)
|
||||
{
|
||||
m_comps.push_back({midiCtrl, scale, combine, varType});
|
||||
}
|
||||
|
||||
float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroState& st) const
|
||||
float SoundMacroState::Evaluator::evaluate(double time, const Voice& vox, const SoundMacroState& st) const
|
||||
{
|
||||
float value = 0.f;
|
||||
|
||||
/* Iterate each component */
|
||||
for (auto it=m_comps.cbegin() ; it != m_comps.cend() ; ++it)
|
||||
for (auto it = m_comps.cbegin(); it != m_comps.cend(); ++it)
|
||||
{
|
||||
const Component& comp = *it;
|
||||
float thisValue = 0.f;
|
||||
@@ -52,21 +65,21 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
|
||||
break;
|
||||
case 129:
|
||||
/* Aftertouch */
|
||||
thisValue = vox.getAftertouch();
|
||||
thisValue = vox.getAftertouch() * (2.f / 127.f);
|
||||
break;
|
||||
case 130:
|
||||
/* LFO1 */
|
||||
if (vox.m_lfoPeriods[0])
|
||||
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF) / 2.f + 1.f) * 127.f;
|
||||
thisValue = std::sin(time / vox.m_lfoPeriods[0] * 2.f * M_PIF);
|
||||
break;
|
||||
case 131:
|
||||
/* LFO2 */
|
||||
if (vox.m_lfoPeriods[1])
|
||||
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF) / 2.f + 1.f) * 127.f;
|
||||
thisValue = std::sin(time / vox.m_lfoPeriods[1] * 2.f * M_PIF);
|
||||
break;
|
||||
case 132:
|
||||
/* Surround panning */
|
||||
thisValue = vox.m_curSpan * 64.f + 64.f;
|
||||
thisValue = vox.m_curSpan;
|
||||
break;
|
||||
case 133:
|
||||
/* Macro-starting key */
|
||||
@@ -78,15 +91,18 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
|
||||
break;
|
||||
case 135:
|
||||
/* Time since macro-start (ms) */
|
||||
thisValue = st.m_execTime * 1000.f;
|
||||
thisValue = clamp(0.f, float(st.m_execTime * 1000.f), 16383.f);
|
||||
break;
|
||||
default:
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl);
|
||||
if (comp.m_midiCtrl == 10) /* Centered pan computation */
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f) - 1.f;
|
||||
else
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (comp.m_varType == VarType::Var)
|
||||
thisValue = st.m_variables[std::max(0, std::min(255, int(comp.m_midiCtrl)))];
|
||||
thisValue = st.m_variables[clamp(0, int(comp.m_midiCtrl), 255)];
|
||||
|
||||
/* Apply scale */
|
||||
thisValue *= comp.m_scale;
|
||||
@@ -119,8 +135,8 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, bool swapDa
|
||||
initialize(ptr, step, 1000.f, 0, 0, 0, swapData);
|
||||
}
|
||||
|
||||
void SoundMacroState::initialize(const unsigned char* ptr, int step, double ticksPerSec,
|
||||
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool swapData)
|
||||
void SoundMacroState::initialize(const unsigned char* ptr, int step, double ticksPerSec, uint8_t midiKey,
|
||||
uint8_t midiVel, uint8_t midiMod, bool swapData)
|
||||
{
|
||||
m_ticksPerSec = ticksPerSec;
|
||||
m_initKey = midiKey;
|
||||
@@ -130,7 +146,8 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick
|
||||
m_curMod = midiMod;
|
||||
m_curPitch = midiKey * 100;
|
||||
m_pc.clear();
|
||||
m_pc.push_back({ptr, step});
|
||||
const Header& header = reinterpret_cast<const Header&>(ptr);
|
||||
m_pc.push_back({ptr, _assertPC(step, header.m_size, swapData)});
|
||||
m_inWait = false;
|
||||
m_execTime = 0.f;
|
||||
m_keyoff = false;
|
||||
@@ -140,7 +157,7 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick
|
||||
m_useAdsrControllers = false;
|
||||
m_portamentoMode = 2;
|
||||
m_portamentoTime = 0.5f;
|
||||
m_header = *reinterpret_cast<const Header*>(ptr);
|
||||
m_header = header;
|
||||
if (swapData)
|
||||
m_header.swapBig();
|
||||
}
|
||||
@@ -177,6 +194,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
|
||||
/* Load next command based on counter */
|
||||
const Command* commands = reinterpret_cast<const Command*>(m_pc.back().first + sizeof(Header));
|
||||
_assertPC(m_pc.back().second, m_header.m_size);
|
||||
Command cmd = commands[m_pc.back().second++];
|
||||
if (vox.getAudioGroup().getDataFormat() != DataFormat::PC)
|
||||
cmd.swapBig();
|
||||
@@ -186,7 +204,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
{
|
||||
case Op::End:
|
||||
case Op::Stop:
|
||||
m_pc.back().second = -1;
|
||||
_setPC(-1);
|
||||
return true;
|
||||
case Op::SplitKey:
|
||||
{
|
||||
@@ -198,10 +216,9 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
{
|
||||
/* Do Branch */
|
||||
if (macroId == m_header.m_macroId)
|
||||
m_pc.back().second = macroStep;
|
||||
_setPC(macroStep);
|
||||
else
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||
m_initKey, m_initVel, m_initMod);
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -216,10 +233,9 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
{
|
||||
/* Do Branch */
|
||||
if (macroId == m_header.m_macroId)
|
||||
m_pc.back().second = macroStep;
|
||||
_setPC(macroStep);
|
||||
else
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||
m_initKey, m_initVel, m_initMod);
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -286,7 +302,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
{
|
||||
/* Loop back to step */
|
||||
--m_loopCountdown;
|
||||
m_pc.back().second = step;
|
||||
_setPC(step);
|
||||
}
|
||||
else /* Break out of loop */
|
||||
m_loopCountdown = -1;
|
||||
@@ -300,10 +316,9 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
|
||||
/* Do Branch */
|
||||
if (macroId == m_header.m_macroId)
|
||||
m_pc.back().second = macroStep;
|
||||
_setPC(macroStep);
|
||||
else
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||
m_initKey, m_initVel, m_initMod);
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -347,8 +362,8 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
int8_t addNote = cmd.m_data[0];
|
||||
ObjectId macroId = *reinterpret_cast<ObjectId*>(&cmd.m_data[1]);
|
||||
int16_t macroStep = *reinterpret_cast<int16_t*>(&cmd.m_data[3]);
|
||||
//int8_t priority = cmd.m_data[5];
|
||||
//int8_t maxVoices = cmd.m_data[6];
|
||||
// int8_t priority = cmd.m_data[5];
|
||||
// int8_t maxVoices = cmd.m_data[6];
|
||||
|
||||
std::shared_ptr<Voice> sibVox = vox.startChildMacro(addNote, macroId, macroStep);
|
||||
if (sibVox)
|
||||
@@ -389,10 +404,9 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
{
|
||||
/* Do Branch */
|
||||
if (macroId == m_header.m_macroId)
|
||||
m_pc.back().second = macroStep;
|
||||
_setPC(macroStep);
|
||||
else
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||
m_initKey, m_initVel, m_initMod);
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -423,7 +437,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
bool orgVel = cmd.m_data[4];
|
||||
|
||||
int32_t eval = int32_t(orgVel ? m_initVel : m_curVel) * scale / 127 + add;
|
||||
eval = std::max(0, std::min(127, eval));
|
||||
eval = clamp(0, eval, 127);
|
||||
|
||||
if (curve != 0)
|
||||
{
|
||||
@@ -459,7 +473,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
double secTime = fadeTime / q;
|
||||
|
||||
int32_t eval = int32_t(m_curVel) * scale / 127 + add;
|
||||
eval = std::max(0, std::min(127, eval));
|
||||
eval = clamp(0, eval, 127);
|
||||
|
||||
const Curve* curveData;
|
||||
if (curve != 0)
|
||||
@@ -512,10 +526,9 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
{
|
||||
/* Do branch */
|
||||
if (macroId == m_header.m_macroId)
|
||||
m_pc.back().second = macroStep;
|
||||
_setPC(macroStep);
|
||||
else
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||
m_initKey, m_initVel, m_initMod);
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -532,7 +545,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
float secTime = fadeTime / q;
|
||||
|
||||
int32_t eval = int32_t(m_curVel) * scale / 127 + add;
|
||||
eval = std::max(0, std::min(127, eval));
|
||||
eval = clamp(0, eval, 127);
|
||||
|
||||
const Curve* curveData;
|
||||
if (curve != 0)
|
||||
@@ -559,6 +572,15 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
m_midiDecay = cmd.m_data[1];
|
||||
m_midiSustain = cmd.m_data[2];
|
||||
m_midiRelease = cmd.m_data[3];
|
||||
|
||||
/* Bootstrap ADSR defaults here */
|
||||
if (!vox.getCtrlValue(m_midiSustain))
|
||||
{
|
||||
vox.setCtrlValue(m_midiAttack, 10);
|
||||
vox.setCtrlValue(m_midiSustain, 127);
|
||||
vox.setCtrlValue(m_midiRelease, 10);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Op::RndNote:
|
||||
@@ -667,12 +689,12 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
case Op::Vibrato:
|
||||
{
|
||||
int32_t level = cmd.m_data[0] * 100 + cmd.m_data[1];
|
||||
int32_t modLevel = cmd.m_data[2];
|
||||
bool modScale = cmd.m_data[2] != 0;
|
||||
int8_t ms = cmd.m_data[4];
|
||||
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
|
||||
|
||||
float q = ms ? 1000.f : m_ticksPerSec;
|
||||
vox.setVibrato(level, modLevel, timeMs / q);
|
||||
vox.setVibrato(level, modScale, timeMs / q);
|
||||
break;
|
||||
}
|
||||
case Op::PitchSweep1:
|
||||
@@ -769,10 +791,9 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
int16_t macroStep = *reinterpret_cast<int16_t*>(&cmd.m_data[3]);
|
||||
|
||||
if (macroId == m_header.m_macroId)
|
||||
m_pc.push_back({m_pc.back().first, macroStep});
|
||||
m_pc.push_back({m_pc.back().first, _assertPC(macroStep, m_header.m_size)});
|
||||
else
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||
m_initKey, m_initVel, m_initMod, true);
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod, true);
|
||||
|
||||
m_header = *reinterpret_cast<const Header*>(m_pc.back().first);
|
||||
if (vox.getAudioGroup().getDataFormat() != DataFormat::PC)
|
||||
@@ -801,7 +822,8 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
vox.m_messageTrap.macroId = macroId;
|
||||
vox.m_messageTrap.macroStep = macroStep;
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -824,7 +846,8 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
vox.m_messageTrap.macroId = 0xffff;
|
||||
vox.m_messageTrap.macroStep = -1;
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -868,8 +891,8 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
}
|
||||
case Op::SendFlag:
|
||||
{
|
||||
int8_t id = cmd.m_data[0];
|
||||
int8_t val = cmd.m_data[1];
|
||||
//int8_t id = cmd.m_data[0];
|
||||
//int8_t val = cmd.m_data[1];
|
||||
break; /* TODO: figure out a good API */
|
||||
}
|
||||
case Op::PitchWheelR:
|
||||
@@ -1197,7 +1220,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
b = m_variables[b];
|
||||
|
||||
if ((a == b) ^ lnot)
|
||||
m_pc.back().second = macroStep;
|
||||
_setPC(macroStep);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1221,7 +1244,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
b = m_variables[b];
|
||||
|
||||
if ((a < b) ^ lnot)
|
||||
m_pc.back().second = macroStep;
|
||||
_setPC(macroStep);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1234,14 +1257,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundMacroState::keyoffNotify(Voice& vox)
|
||||
{
|
||||
m_keyoff = true;
|
||||
}
|
||||
|
||||
void SoundMacroState::sampleEndNotify(Voice& vox)
|
||||
{
|
||||
m_sampleEnd = true;
|
||||
}
|
||||
void SoundMacroState::keyoffNotify(Voice& vox) { m_keyoff = true; }
|
||||
|
||||
void SoundMacroState::sampleEndNotify(Voice& vox) { m_sampleEnd = true; }
|
||||
}
|
||||
|
||||
40
lib/Studio.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "amuse/Studio.hpp"
|
||||
#include "amuse/Engine.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool Studio::_cyclicCheck(Studio* leaf)
|
||||
{
|
||||
for (auto it = m_studiosOut.begin(); it != m_studiosOut.end();)
|
||||
{
|
||||
if (leaf == it->m_targetStudio.get() || it->m_targetStudio->_cyclicCheck(leaf))
|
||||
return true;
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
Studio::Studio(Engine& engine, bool mainOut) : m_engine(engine), m_master(engine), m_auxA(engine), m_auxB(engine)
|
||||
{
|
||||
if (mainOut && engine.m_defaultStudioReady)
|
||||
addStudioSend(engine.getDefaultStudio(), 1.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
void Studio::addStudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB)
|
||||
{
|
||||
m_studiosOut.emplace_back(studio, dry, auxA, auxB);
|
||||
|
||||
/* Cyclic check */
|
||||
assert(!_cyclicCheck(this));
|
||||
}
|
||||
|
||||
void Studio::resetOutputSampleRate(double sampleRate)
|
||||
{
|
||||
m_master.resetOutputSampleRate(sampleRate);
|
||||
m_auxA.resetOutputSampleRate(sampleRate);
|
||||
m_auxB.resetOutputSampleRate(sampleRate);
|
||||
}
|
||||
}
|
||||
@@ -3,37 +3,49 @@
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
void Submix::_destroy()
|
||||
{
|
||||
m_destroyed = true;
|
||||
}
|
||||
|
||||
Submix::Submix(Engine& engine, Submix* smx)
|
||||
: m_root(engine), m_submix(smx)
|
||||
{}
|
||||
Submix::Submix(Engine& engine) : m_root(engine) {}
|
||||
|
||||
EffectChorus& Submix::makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)
|
||||
{
|
||||
return makeEffect<EffectChorus>(baseDelay, variation, period);
|
||||
}
|
||||
|
||||
EffectChorus& Submix::makeChorus(const EffectChorusInfo& info)
|
||||
{
|
||||
return makeEffect<EffectChorus>(info);
|
||||
}
|
||||
|
||||
EffectDelay& Submix::makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput)
|
||||
{
|
||||
return makeEffect<EffectDelay>(initDelay, initFeedback, initOutput);
|
||||
}
|
||||
|
||||
EffectReverbStd& Submix::makeReverbStd(float coloration, float mix, float time,
|
||||
float damping, float preDelay)
|
||||
EffectDelay& Submix::makeDelay(const EffectDelayInfo& info)
|
||||
{
|
||||
return makeEffect<EffectDelay>(info);
|
||||
}
|
||||
|
||||
EffectReverbStd& Submix::makeReverbStd(float coloration, float mix, float time, float damping, float preDelay)
|
||||
{
|
||||
return makeEffect<EffectReverbStd>(coloration, mix, time, damping, preDelay);
|
||||
}
|
||||
|
||||
EffectReverbHi& Submix::makeReverbHi(float coloration, float mix, float time,
|
||||
float damping, float preDelay, float crosstalk)
|
||||
EffectReverbStd& Submix::makeReverbStd(const EffectReverbStdInfo& info)
|
||||
{
|
||||
return makeEffect<EffectReverbStd>(info);
|
||||
}
|
||||
|
||||
EffectReverbHi& Submix::makeReverbHi(float coloration, float mix, float time, float damping, float preDelay,
|
||||
float crosstalk)
|
||||
{
|
||||
return makeEffect<EffectReverbHi>(coloration, mix, time, damping, preDelay, crosstalk);
|
||||
}
|
||||
|
||||
EffectReverbHi& Submix::makeReverbHi(const EffectReverbHiInfo& info)
|
||||
{
|
||||
return makeEffect<EffectReverbHi>(info);
|
||||
}
|
||||
|
||||
void Submix::applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const
|
||||
{
|
||||
for (const std::unique_ptr<EffectBaseTypeless>& effect : m_effectStack)
|
||||
@@ -57,5 +69,4 @@ void Submix::resetOutputSampleRate(double sampleRate)
|
||||
for (const std::unique_ptr<EffectBaseTypeless>& effect : m_effectStack)
|
||||
effect->resetOutputSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#include "amuse/SurroundProfiles.hpp"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
static float Dot(const Vector3f& a, const Vector3f& b)
|
||||
{
|
||||
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||
}
|
||||
|
||||
static 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 float Normalize(Vector3f& out, const Vector3f& in)
|
||||
{
|
||||
out[0] = in[0];
|
||||
out[1] = in[1];
|
||||
out[2] = in[2];
|
||||
float dist = Length(out);
|
||||
out[0] /= dist;
|
||||
out[1] /= dist;
|
||||
out[2] /= dist;
|
||||
return dist;
|
||||
}
|
||||
|
||||
static void Cross(Vector3f& out, const Vector3f& a, const Vector3f& b)
|
||||
{
|
||||
out[0] = a[1] * b[2] - a[2] * b[1];
|
||||
out[1] = a[2] * b[0] - a[0] * b[2];
|
||||
out[2] = a[0] * b[1] - a[1] * b[0];
|
||||
}
|
||||
|
||||
class SimpleMatrix
|
||||
{
|
||||
Vector3f m_mat[3];
|
||||
public:
|
||||
SimpleMatrix(const Vector3f& dir, const Vector3f& up)
|
||||
{
|
||||
Vector3f temp;
|
||||
Normalize(temp, dir);
|
||||
m_mat[0][1] = temp[0];
|
||||
m_mat[1][1] = temp[1];
|
||||
m_mat[2][1] = temp[2];
|
||||
|
||||
Normalize(temp, up);
|
||||
m_mat[0][2] = temp[0];
|
||||
m_mat[1][2] = temp[1];
|
||||
m_mat[2][2] = temp[2];
|
||||
|
||||
Cross(temp, dir, up);
|
||||
m_mat[0][0] = temp[0];
|
||||
m_mat[1][0] = temp[1];
|
||||
m_mat[2][0] = temp[2];
|
||||
}
|
||||
|
||||
void vecMult(Vector3f& out, const Vector3f& in)
|
||||
{
|
||||
out[0] = Dot(m_mat[0], in);
|
||||
out[1] = Dot(m_mat[1], in);
|
||||
out[2] = Dot(m_mat[2], in);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReferenceVector
|
||||
{
|
||||
Vector3f vec;
|
||||
float bias;
|
||||
bool valid = false;
|
||||
ReferenceVector() = default;
|
||||
ReferenceVector(float x, float y, float z, float thres)
|
||||
{
|
||||
vec[0] = x;
|
||||
vec[1] = y;
|
||||
vec[2] = z;
|
||||
bias = thres;
|
||||
valid = true;
|
||||
}
|
||||
};
|
||||
|
||||
static const ReferenceVector StereoVectors[8] =
|
||||
{
|
||||
{-0.80901f, 0.58778f, 0.f, 0.3f},
|
||||
{ 0.80901f, 0.58778f, 0.f, 0.3f},
|
||||
};
|
||||
|
||||
static const ReferenceVector QuadVectors[8] =
|
||||
{
|
||||
{-0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{ 0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{ 0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
};
|
||||
|
||||
static const ReferenceVector Sur51Vectors[8] =
|
||||
{
|
||||
{-0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{ 0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{ 0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{ 0.0f, 1.0f, 0.f, 0.1f},
|
||||
{ 0.0f, 1.0f, 0.f, 1.0f},
|
||||
};
|
||||
|
||||
static const ReferenceVector Sur71Vectors[8] =
|
||||
{
|
||||
{-0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{ 0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{ 0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{ 0.0f, 1.0f, 0.f, 0.1f},
|
||||
{ 0.0f, 1.0f, 0.f, 1.0f},
|
||||
{-1.f, 0.0f, 0.f, 0.1f},
|
||||
{ 1.f, 0.0f, 0.f, 0.1f},
|
||||
};
|
||||
|
||||
void SurroundProfiles::SetupRefs(float matOut[8], const ChannelMap& map,
|
||||
const Vector3f& listenEmit, const ReferenceVector refs[])
|
||||
{
|
||||
for (unsigned i=0 ; i<map.m_channelCount && i<8 ; ++i)
|
||||
{
|
||||
matOut[i] = 0.f;
|
||||
if (map.m_channels[i] == AudioChannel::Unknown)
|
||||
continue;
|
||||
const ReferenceVector& refVec = refs[int(map.m_channels[i])];
|
||||
if (!refVec.valid)
|
||||
continue;
|
||||
matOut[i] = std::max(1.f, Dot(listenEmit, refVec.vec) + refVec.bias);
|
||||
}
|
||||
}
|
||||
|
||||
void SurroundProfiles::SetupMatrix(float matOut[8], const ChannelMap& map, AudioChannelSet set,
|
||||
const Vector3f& emitPos, const Vector3f& listenPos,
|
||||
const Vector3f& listenHeading, const Vector3f& listenUp)
|
||||
{
|
||||
Vector3f listenDelta;
|
||||
listenDelta[0] = emitPos[0] - listenPos[0];
|
||||
listenDelta[1] = emitPos[1] - listenPos[1];
|
||||
listenDelta[2] = emitPos[2] - listenPos[2];
|
||||
|
||||
Vector3f listenNorm;
|
||||
float dist = Normalize(listenNorm, listenDelta);
|
||||
|
||||
SimpleMatrix listenerMat(listenHeading, listenUp);
|
||||
Vector3f listenEmit;
|
||||
listenerMat.vecMult(listenEmit, listenNorm);
|
||||
|
||||
/* Factor for each channel in set */
|
||||
switch (set)
|
||||
{
|
||||
case AudioChannelSet::Stereo:
|
||||
default:
|
||||
SetupRefs(matOut, map, listenEmit, StereoVectors);
|
||||
break;
|
||||
case AudioChannelSet::Quad:
|
||||
SetupRefs(matOut, map, listenEmit, QuadVectors);
|
||||
break;
|
||||
case AudioChannelSet::Surround51:
|
||||
SetupRefs(matOut, map, listenEmit, Sur51Vectors);
|
||||
break;
|
||||
case AudioChannelSet::Surround71:
|
||||
SetupRefs(matOut, map, listenEmit, Sur71Vectors);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||