From 81bd897ec18d7bbb759175439f71facdfbba1ae1 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 2 Jun 2016 16:53:52 -1000 Subject: [PATCH 1/7] Work on AudioUnit --- AudioUnit/AmuseContainerMainMenu.xib | 20 +- AudioUnit/AmuseContainingApp.mm | 113 +++++++++++- AudioUnit/AudioGroupFilePresenter.hpp | 34 ++-- AudioUnit/AudioGroupFilePresenter.mm | 254 +++++++++++++++----------- lib/Sequencer.cpp | 2 +- 5 files changed, 291 insertions(+), 132 deletions(-) diff --git a/AudioUnit/AmuseContainerMainMenu.xib b/AudioUnit/AmuseContainerMainMenu.xib index 83ba70a..f04a06a 100644 --- a/AudioUnit/AmuseContainerMainMenu.xib +++ b/AudioUnit/AmuseContainerMainMenu.xib @@ -149,7 +149,7 @@ - + @@ -190,10 +190,10 @@ - + - + @@ -287,7 +287,7 @@ @@ -372,7 +372,7 @@ - + @@ -467,7 +467,7 @@ - - diff --git a/AudioUnit/AmuseContainingApp.mm b/AudioUnit/AmuseContainingApp.mm index 6667e68..446601b 100644 --- a/AudioUnit/AmuseContainingApp.mm +++ b/AudioUnit/AmuseContainingApp.mm @@ -2,6 +2,11 @@ #import #import #import "AudioUnitViewController.hpp" +#import "AudioGroupFilePresenter.hpp" + +@class DataOutlineController; +@class SamplesTableController; +@class SFXTableController; @interface MainView : NSView { @@ -28,6 +33,22 @@ @end +@interface AppDelegate : NSObject +{ + IBOutlet NSWindow* mainWindow; + IBOutlet NSOutlineView* dataOutline; + IBOutlet NSTableView* sfxTable; + IBOutlet NSTableView* samplesTable; + IBOutlet NSTextView* creditsView; + + AudioGroupFilePresenter* groupFilePresenter; + + DataOutlineController* dataController; + SamplesTableController* samplesController; + SFXTableController* sfxController; +} +@end + @interface MainTabView : NSTabView {} - (IBAction)selectDataTab:(id)sender; @@ -55,22 +76,102 @@ } @end -@interface AppDelegate : NSObject +@interface DataOutlineView : NSOutlineView { - IBOutlet NSWindow* mainWindow; - IBOutlet NSOutlineView* dataOutline; - IBOutlet NSTableView* sfxTable; - IBOutlet NSTableView* samplesTable; - IBOutlet NSTextView* creditsView; + } @end +@implementation DataOutlineView + +- (id)initWithFrame:(NSRect)frameRect +{ + self = [super initWithFrame:frameRect]; + [self registerForDraggedTypes:@[(__bridge NSString*)kUTTypeData]]; + return self; +} + +- (BOOL)performDragOperation:(id)sender +{ + +} + +@end + +@interface DataOutlineController : NSObject +{ + +} +@end + +@implementation DataOutlineController + + + +@end + +@interface SamplesTableController : NSObject +{ + +} +@end + +@implementation SamplesTableController + +- (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView +{ + +} + +- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn*)tableColumn row:(NSInteger)row +{ + +} + +@end + +@interface SFXTableController : NSObject +{ + +} +@end + +@implementation SFXTableController + +- (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView +{ +} + +- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn*)tableColumn row:(NSInteger)row +{ + +} + +@end + @implementation AppDelegate - (void)applicationDidFinishLaunching:(NSNotification*)notification { [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"]; [mainWindow.toolbar setSelectedItemIdentifier:@"DataTab"]; + + groupFilePresenter = [AudioGroupFilePresenter new]; + + dataController = [DataOutlineController new]; + dataOutline.dataSource = dataController; + dataOutline.delegate = dataController; + [dataOutline reloadItem:nil reloadChildren:YES]; + + samplesController = [SamplesTableController new]; + samplesTable.dataSource = samplesController; + samplesTable.delegate = samplesController; + [samplesTable reloadData]; + + sfxController = [SFXTableController new]; + sfxTable.dataSource = sfxController; + sfxTable.delegate = sfxController; + [sfxTable reloadData]; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender diff --git a/AudioUnit/AudioGroupFilePresenter.hpp b/AudioUnit/AudioGroupFilePresenter.hpp index ccfabfb..df70dba 100644 --- a/AudioUnit/AudioGroupFilePresenter.hpp +++ b/AudioUnit/AudioGroupFilePresenter.hpp @@ -2,6 +2,7 @@ #define __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__ #import +#import #include #include @@ -9,29 +10,40 @@ struct AudioGroupDataCollection { - NSURL* m_proj = nullptr; /* Only this member set for single-file containers */ - NSURL* m_pool = nullptr; - NSURL* m_sdir = nullptr; - NSURL* m_samp = nullptr; + NSURL* m_proj; + NSURL* m_pool; + NSURL* m_sdir; + NSURL* m_samp; + + std::unique_ptr m_projData; + std::unique_ptr m_poolData; + std::unique_ptr m_sdirData; + std::unique_ptr m_sampData; - bool invalidateURL(NSURL* url); void moveURL(NSURL* oldUrl, NSURL* newUrl); - std::unique_ptr _coordinateRead(AudioGroupFilePresenter* presenter, size_t& szOut, NSURL* url); + bool loadProj(AudioGroupFilePresenter* presenter); + bool loadPool(AudioGroupFilePresenter* presenter); + bool loadSdir(AudioGroupFilePresenter* presenter); + bool loadSamp(AudioGroupFilePresenter* presenter); - std::unique_ptr coordinateProjRead(AudioGroupFilePresenter* presenter, size_t& szOut); - std::unique_ptr coordinatePoolRead(AudioGroupFilePresenter* presenter, size_t& szOut); - std::unique_ptr coordinateSdirRead(AudioGroupFilePresenter* presenter, size_t& szOut); - std::unique_ptr coordinateSampRead(AudioGroupFilePresenter* presenter, size_t& szOut); + AudioGroupDataCollection(NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp) + : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp) {} + bool isDataComplete () const {return m_projData && m_poolData && m_sdirData && m_sampData;} }; -@interface AudioGroupFilePresenter : NSObject +@interface AudioGroupFilePresenter : NSObject { NSURL* m_groupURL; NSOperationQueue* m_dataQueue; std::map m_audioGroupCollections; + + std::map::const_iterator m_audioGroupOutlineIt; + size_t m_audioGroupOutlineIdx; } +- (void)update; + @end #endif // __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__ diff --git a/AudioUnit/AudioGroupFilePresenter.mm b/AudioUnit/AudioGroupFilePresenter.mm index fd0e122..9adba9e 100644 --- a/AudioUnit/AudioGroupFilePresenter.mm +++ b/AudioUnit/AudioGroupFilePresenter.mm @@ -1,5 +1,6 @@ #include "AudioGroupFilePresenter.hpp" #include +#import @implementation AudioGroupFilePresenter @@ -13,36 +14,6 @@ return m_dataQueue; } -bool AudioGroupDataCollection::invalidateURL(NSURL* url) -{ - bool valid = false; - if (m_proj) - { - if ([m_proj isEqual:url]) - m_proj = nullptr; - valid |= m_proj != nullptr; - } - if (m_pool) - { - if ([m_pool isEqual:url]) - m_pool = nullptr; - valid |= m_pool != nullptr; - } - if (m_sdir) - { - if ([m_sdir isEqual:url]) - m_sdir = nullptr; - valid |= m_sdir != nullptr; - } - if (m_samp) - { - if ([m_samp isEqual:url]) - m_samp = nullptr; - valid |= m_samp != nullptr; - } - return valid; -} - void AudioGroupDataCollection::moveURL(NSURL* oldUrl, NSURL* newUrl) { if (m_proj) @@ -67,100 +38,109 @@ void AudioGroupDataCollection::moveURL(NSURL* oldUrl, NSURL* newUrl) } } -std::unique_ptr AudioGroupDataCollection::_coordinateRead(AudioGroupFilePresenter* presenter, size_t& szOut, NSURL* url) -{ - NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:presenter]; - if (!coord) - return {}; - NSError* err; - __block std::unique_ptr ret; - __block size_t retSz = 0; - [coord coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err - byAccessor:^(NSURL* newUrl) - { - athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); - if (r.hasError()) - return; - retSz = r.length(); - ret = r.readUBytes(retSz); - }]; - szOut = retSz; - return std::move(ret); -} - -std::unique_ptr AudioGroupDataCollection::coordinateProjRead(AudioGroupFilePresenter* presenter, size_t& szOut) +bool AudioGroupDataCollection::loadProj(AudioGroupFilePresenter* presenter) { if (!m_proj) - return {}; - return _coordinateRead(presenter, szOut, m_proj); + return false; + NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:presenter]; + if (!coord) + return false; + NSError* err; + __block std::unique_ptr& ret = m_projData; + [coord coordinateReadingItemAtURL:m_proj + options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err + byAccessor:^(NSURL* newUrl) + { + athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); + if (r.hasError()) + return; + ret = r.readUBytes(r.length()); + }]; + return m_projData.operator bool(); } -std::unique_ptr AudioGroupDataCollection::coordinatePoolRead(AudioGroupFilePresenter* presenter, size_t& szOut) +bool AudioGroupDataCollection::loadPool(AudioGroupFilePresenter* presenter) { if (!m_pool) - return {}; - return _coordinateRead(presenter, szOut, m_pool); + return false; + NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:presenter]; + if (!coord) + return false; + NSError* err; + __block std::unique_ptr& ret = m_poolData; + [coord coordinateReadingItemAtURL:m_pool + options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err + byAccessor:^(NSURL* newUrl) + { + athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); + if (r.hasError()) + return; + ret = r.readUBytes(r.length()); + }]; + return m_poolData.operator bool(); } -std::unique_ptr AudioGroupDataCollection::coordinateSdirRead(AudioGroupFilePresenter* presenter, size_t& szOut) +bool AudioGroupDataCollection::loadSdir(AudioGroupFilePresenter* presenter) { if (!m_sdir) - return {}; - return _coordinateRead(presenter, szOut, m_sdir); + return false; + NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:presenter]; + if (!coord) + return false; + NSError* err; + __block std::unique_ptr& ret = m_sdirData; + [coord coordinateReadingItemAtURL:m_sdir + options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err + byAccessor:^(NSURL* newUrl) + { + athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); + if (r.hasError()) + return; + ret = r.readUBytes(r.length()); + }]; + return m_sdirData.operator bool(); } -std::unique_ptr AudioGroupDataCollection::coordinateSampRead(AudioGroupFilePresenter* presenter, size_t& szOut) +bool AudioGroupDataCollection::loadSamp(AudioGroupFilePresenter* presenter) { if (!m_samp) - return {}; - return _coordinateRead(presenter, szOut, m_samp); -} - -- (void)accommodatePresentedSubitemDeletionAtURL:(NSURL*)url completionHandler:(void (^)(NSError* errorOrNil))completionHandler -{ - for (auto it = m_audioGroupCollections.begin() ; it != m_audioGroupCollections.end() ;) - { - std::pair& pair = *it; - if (pair.second.invalidateURL(url)) - { - it = m_audioGroupCollections.erase(it); - continue; - } - ++it; - } - completionHandler(nil); + return false; + NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:presenter]; + if (!coord) + return false; + NSError* err; + __block std::unique_ptr& ret = m_sampData; + [coord coordinateReadingItemAtURL:m_samp + options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err + byAccessor:^(NSURL* newUrl) + { + athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); + if (r.hasError()) + return; + ret = r.readUBytes(r.length()); + }]; + return m_sampData.operator bool(); } - (void)presentedSubitemDidAppearAtURL:(NSURL*)url { - NSString* path = [url path]; - if (!path) - return; + NSURL* dir = nil; + if ([url.lastPathComponent isEqualToString:@"proj"] || + [url.lastPathComponent isEqualToString:@"pool"] || + [url.lastPathComponent isEqualToString:@"sdir"] || + [url.lastPathComponent isEqualToString:@"samp"]) + dir = url.baseURL; - NSString* extension = [url pathExtension]; - NSString* lastComp = [url lastPathComponent]; - lastComp = [lastComp substringToIndex:[lastComp length] - [extension length]]; - AudioGroupDataCollection& collection = m_audioGroupCollections[[lastComp UTF8String]]; - - if ([extension isEqualToString:@"pro"] || [extension isEqualToString:@"proj"]) + auto search = m_audioGroupCollections.find(dir.lastPathComponent.UTF8String); + if (search == m_audioGroupCollections.end()) { - collection.m_proj = url; - } - else if ([extension isEqualToString:@"poo"] || [extension isEqualToString:@"pool"]) - { - collection.m_pool = url; - } - else if ([extension isEqualToString:@"sdi"] || [extension isEqualToString:@"sdir"]) - { - collection.m_sdir = url; - } - else if ([extension isEqualToString:@"sam"] || [extension isEqualToString:@"samp"]) - { - collection.m_samp = url; - } - else - { - collection.m_proj = url; + search = + m_audioGroupCollections.emplace(dir.lastPathComponent.UTF8String, + AudioGroupDataCollection{ + [dir URLByAppendingPathComponent:@"proj"], + [dir URLByAppendingPathComponent:@"pool"], + [dir URLByAppendingPathComponent:@"sdir"], + [dir URLByAppendingPathComponent:@"samp"]}).first; } } @@ -173,6 +153,71 @@ std::unique_ptr AudioGroupDataCollection::coordinateSampRead(AudioGro } } +- (NSInteger)outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(nullable id)item +{ + if (!item) + return m_audioGroupCollections.size(); + return 0; +} + +- (id)outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(nullable id)item +{ + if (!item) + { + + } + return nil; +} + +- (BOOL)outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item +{ + return NO; +} + +- (void)update +{ + NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:self]; + if (!coord) + return; + NSError* coordErr; + __block NSError* managerErr; + __block std::map& theMap = m_audioGroupCollections; + [coord coordinateReadingItemAtURL:m_groupURL options:NSFileCoordinatorReadingResolvesSymbolicLink error:&coordErr + byAccessor:^(NSURL* newUrl) + { + NSFileManager* fman = [NSFileManager defaultManager]; + NSArray* contents = + [fman contentsOfDirectoryAtURL:newUrl + includingPropertiesForKeys:@[NSURLIsDirectoryKey] + options:NSDirectoryEnumerationSkipsSubdirectoryDescendants | + NSDirectoryEnumerationSkipsHiddenFiles + error:&managerErr]; + if (!contents) + return; + + for (NSURL* path in contents) + { + NSNumber* isDir; + [path getResourceValue:&isDir forKey:NSURLIsDirectoryKey error:nil]; + + if (isDir.boolValue) + { + auto search = theMap.find(path.lastPathComponent.UTF8String); + if (search == theMap.end()) + { + search = + theMap.emplace(path.lastPathComponent.UTF8String, + AudioGroupDataCollection{ + [path URLByAppendingPathComponent:@"proj"], + [path URLByAppendingPathComponent:@"pool"], + [path URLByAppendingPathComponent:@"sdir"], + [path URLByAppendingPathComponent:@"samp"]}).first; + } + } + } + }]; +} + - (id)init { m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.io.github.axiodl.Amuse.AudioGroups"]; @@ -180,6 +225,7 @@ std::unique_ptr AudioGroupDataCollection::coordinateSampRead(AudioGro return nil; m_dataQueue = [NSOperationQueue new]; [NSFileCoordinator addFilePresenter:self]; + [self update]; return self; } diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp index e39e327..6e508d0 100644 --- a/lib/Sequencer.cpp +++ b/lib/Sequencer.cpp @@ -71,7 +71,7 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, m_midiSetup = it->second->data(); m_submix = m_engine.addSubmix(smx); - m_submix->makeReverbHi(0.2f, 1.f, 1.f, 0.5f, 0.f, 0.f); + m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f); } Sequencer::ChannelState::~ChannelState() From 9ae92313e35d9161c5b99793c6a06bc8ee583c8c Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 3 Jun 2016 16:34:35 -1000 Subject: [PATCH 2/7] Work on AudioUnit data UI --- AudioUnit/AmuseContainerMainMenu.xib | 142 +++------ AudioUnit/AmuseContainingApp.mm | 49 +-- AudioUnit/AudioGroupFilePresenter.hpp | 108 ++++++- AudioUnit/AudioGroupFilePresenter.mm | 421 ++++++++++++++++++++++---- AudioUnit/CMakeLists.txt | 2 +- include/amuse/AudioGroupData.hpp | 61 +++- include/amuse/ContainerRegistry.hpp | 1 + lib/AudioGroupData.cpp | 4 +- lib/ContainerRegistry.cpp | 191 ++++++++---- 9 files changed, 715 insertions(+), 264 deletions(-) diff --git a/AudioUnit/AmuseContainerMainMenu.xib b/AudioUnit/AmuseContainerMainMenu.xib index f04a06a..a7cc169 100644 --- a/AudioUnit/AmuseContainerMainMenu.xib +++ b/AudioUnit/AmuseContainerMainMenu.xib @@ -17,6 +17,7 @@ + @@ -69,7 +70,7 @@ - + @@ -149,7 +150,7 @@ - + @@ -166,121 +167,75 @@ - + - + - + - - + + - - + + - + + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -715,10 +669,10 @@ - - - - + + + + diff --git a/AudioUnit/AmuseContainingApp.mm b/AudioUnit/AmuseContainingApp.mm index 446601b..0c7c4a0 100644 --- a/AudioUnit/AmuseContainingApp.mm +++ b/AudioUnit/AmuseContainingApp.mm @@ -3,6 +3,7 @@ #import #import "AudioUnitViewController.hpp" #import "AudioGroupFilePresenter.hpp" +#include @class DataOutlineController; @class SamplesTableController; @@ -37,16 +38,17 @@ { IBOutlet NSWindow* mainWindow; IBOutlet NSOutlineView* dataOutline; + IBOutlet NSSearchField* dataSearchField; IBOutlet NSTableView* sfxTable; IBOutlet NSTableView* samplesTable; IBOutlet NSTextView* creditsView; AudioGroupFilePresenter* groupFilePresenter; - DataOutlineController* dataController; SamplesTableController* samplesController; SFXTableController* sfxController; } +- (BOOL)importURL:(NSURL*)url; @end @interface MainTabView : NSTabView @@ -98,17 +100,6 @@ @end -@interface DataOutlineController : NSObject -{ - -} -@end - -@implementation DataOutlineController - - - -@end @interface SamplesTableController : NSObject { @@ -158,9 +149,8 @@ groupFilePresenter = [AudioGroupFilePresenter new]; - dataController = [DataOutlineController new]; - dataOutline.dataSource = dataController; - dataOutline.delegate = dataController; + dataOutline.dataSource = groupFilePresenter; + dataOutline.delegate = groupFilePresenter; [dataOutline reloadItem:nil reloadChildren:YES]; samplesController = [SamplesTableController new]; @@ -179,9 +169,34 @@ return YES; } -- (IBAction)quitApp:(id)sender +- (BOOL)importURL:(NSURL*)url { - [NSApp terminate:sender]; + amuse::ContainerRegistry::Type containerType; + std::vector> data = + amuse::ContainerRegistry::LoadContainer(url.path.UTF8String, containerType); + if (data.empty()) + { + NSString* err = [NSString stringWithFormat:@"Unable to load Audio Groups from %s", url.path.UTF8String]; + NSAlert* alert = [[NSAlert alloc] init]; + alert.informativeText = err; + alert.messageText = @"Invalid Data File"; + [alert runModal]; + return false; + } + + std::string name(amuse::ContainerRegistry::TypeToName(containerType)); + return [groupFilePresenter addCollectionName:std::move(name) items:std::move(data)]; +} + +- (IBAction)importFile:(id)sender +{ + __block NSOpenPanel* panel = [NSOpenPanel openPanel]; + [panel beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) { + if (result == NSFileHandlingPanelOKButton) + { + [self importURL:panel.URL]; + } + }]; } @end diff --git a/AudioUnit/AudioGroupFilePresenter.hpp b/AudioUnit/AudioGroupFilePresenter.hpp index df70dba..6c71023 100644 --- a/AudioUnit/AudioGroupFilePresenter.hpp +++ b/AudioUnit/AudioGroupFilePresenter.hpp @@ -5,20 +5,44 @@ #import #include #include +#include "optional.hpp" +#include +#include @class AudioGroupFilePresenter; +@class AudioGroupDataToken; +@class AudioGroupCollectionToken; struct AudioGroupDataCollection { + std::string m_name; NSURL* m_proj; NSURL* m_pool; NSURL* m_sdir; NSURL* m_samp; + NSURL* m_meta; + + AudioGroupDataToken* m_token; - std::unique_ptr m_projData; - std::unique_ptr m_poolData; - std::unique_ptr m_sdirData; - std::unique_ptr m_sampData; + std::vector m_projData; + std::vector m_poolData; + std::vector m_sdirData; + std::vector m_sampData; + + struct MetaData + { + amuse::DataFormat fmt; + uint32_t absOffs; + uint32_t active; + MetaData(amuse::DataFormat fmtIn, uint32_t absOffsIn, uint32_t activeIn) + : fmt(fmtIn), absOffs(absOffsIn), active(activeIn) {} + MetaData(athena::io::FileReader& r) + : fmt(amuse::DataFormat(r.readUint32Big())), absOffs(r.readUint32Big()), active(r.readUint32Big()) {} + }; + std::experimental::optional m_metaData; + + std::experimental::optional m_loadedData; + std::experimental::optional m_loadedProj; void moveURL(NSURL* oldUrl, NSURL* newUrl); @@ -26,24 +50,82 @@ struct AudioGroupDataCollection bool loadPool(AudioGroupFilePresenter* presenter); bool loadSdir(AudioGroupFilePresenter* presenter); bool loadSamp(AudioGroupFilePresenter* presenter); + bool loadMeta(AudioGroupFilePresenter* presenter); - AudioGroupDataCollection(NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp) {} - bool isDataComplete () const {return m_projData && m_poolData && m_sdirData && m_sampData;} + AudioGroupDataCollection(const std::string& name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta); + bool isDataComplete() const {return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;} + bool _attemptLoad(AudioGroupFilePresenter* presenter); }; -@interface AudioGroupFilePresenter : NSObject +template +class IteratorTracker +{ + typename std::map::const_iterator m_audioGroupOutlineBegin; + typename std::map::const_iterator m_audioGroupOutlineEnd; + typename std::map::const_iterator m_audioGroupOutlineIt; + size_t m_audioGroupOutlineIdx = 0; + +public: + IteratorTracker(typename std::map::const_iterator begin, + typename std::map::const_iterator end) + : m_audioGroupOutlineBegin(begin), m_audioGroupOutlineEnd(end), m_audioGroupOutlineIt(begin) {} + typename std::map::const_iterator seekToIndex(size_t idx) + { + if (idx == m_audioGroupOutlineIdx) + return m_audioGroupOutlineIt; + if (idx < m_audioGroupOutlineIdx) + { + for (; idx < m_audioGroupOutlineIdx && m_audioGroupOutlineIt != m_audioGroupOutlineBegin ; + --m_audioGroupOutlineIdx, --m_audioGroupOutlineIt) {} + return m_audioGroupOutlineIt; + } + for (; idx > m_audioGroupOutlineIdx && m_audioGroupOutlineIt != m_audioGroupOutlineEnd ; + ++m_audioGroupOutlineIdx, ++m_audioGroupOutlineIt) {} + return m_audioGroupOutlineIt; + } +}; + +struct AudioGroupCollection +{ + NSURL* m_url; + + AudioGroupCollectionToken* m_token; + std::map m_groups; + std::experimental::optional> m_iteratorTracker; + + AudioGroupCollection(NSURL* url); + void addCollection(std::vector>&& collection); + void update(AudioGroupFilePresenter* presenter); +}; + +@interface AudioGroupDataToken : NSObject +{ +@public + AudioGroupDataCollection* m_collection; +} +- (id)initWithDataCollection:(AudioGroupDataCollection*)collection; +@end + +@interface AudioGroupCollectionToken : NSObject +{ +@public + AudioGroupCollection* m_collection; +} +- (id)initWithCollection:(AudioGroupCollection*)collection; +@end + +@interface AudioGroupFilePresenter : NSObject { NSURL* m_groupURL; NSOperationQueue* m_dataQueue; - std::map m_audioGroupCollections; + std::map m_audioGroupCollections; + NSOutlineView* lastOutlineView; - std::map::const_iterator m_audioGroupOutlineIt; - size_t m_audioGroupOutlineIdx; + std::experimental::optional> m_iteratorTracker; } - +- (BOOL)addCollectionName:(std::string&&)name items:(std::vector>&&)collection; - (void)update; - +- (void)resetIterators; @end #endif // __AMUSE_AUDIOUNIT_AUDIOGROUPFILEPRESENTER_HPP__ diff --git a/AudioUnit/AudioGroupFilePresenter.mm b/AudioUnit/AudioGroupFilePresenter.mm index 9adba9e..d5e4468 100644 --- a/AudioUnit/AudioGroupFilePresenter.mm +++ b/AudioUnit/AudioGroupFilePresenter.mm @@ -1,7 +1,30 @@ #include "AudioGroupFilePresenter.hpp" #include +#include #import +@implementation AudioGroupDataToken + +- (id)initWithDataCollection:(AudioGroupDataCollection *)collection +{ + self = [super init]; + m_collection = collection; + return self; +} + +@end + +@implementation AudioGroupCollectionToken + +- (id)initWithCollection:(AudioGroupCollection *)collection +{ + self = [super init]; + m_collection = collection; + return self; +} + +@end + @implementation AudioGroupFilePresenter - (NSURL*)presentedItemURL @@ -14,6 +37,138 @@ return m_dataQueue; } +AudioGroupCollection::AudioGroupCollection(NSURL* url) +: m_url(url), m_token([[AudioGroupCollectionToken alloc] initWithCollection:this]) {} + +void AudioGroupCollection::addCollection(std::vector>&& collection) +{ + for (std::pair& pair : collection) + { + NSURL* collectionUrl = [m_url URLByAppendingPathComponent:@(pair.first.c_str())]; + + amuse::IntrusiveAudioGroupData& dataIn = pair.second; + auto search = m_groups.find(pair.first); + if (search == m_groups.end()) + { + search = m_groups.emplace(pair.first, + AudioGroupDataCollection{pair.first, + [collectionUrl URLByAppendingPathComponent:@"proj"], + [collectionUrl URLByAppendingPathComponent:@"pool"], + [collectionUrl URLByAppendingPathComponent:@"sdir"], + [collectionUrl URLByAppendingPathComponent:@"samp"], + [collectionUrl URLByAppendingPathComponent:@"meta"]}).first; + } + + AudioGroupDataCollection& dataCollection = search->second; + dataCollection.m_projData.resize(dataIn.getProjSize()); + memmove(dataCollection.m_projData.data(), dataIn.getProj(), dataIn.getProjSize()); + + dataCollection.m_poolData.resize(dataIn.getPoolSize()); + memmove(dataCollection.m_poolData.data(), dataIn.getPool(), dataIn.getPoolSize()); + + dataCollection.m_sdirData.resize(dataIn.getSdirSize()); + memmove(dataCollection.m_sdirData.data(), dataIn.getSdir(), dataIn.getSdirSize()); + + dataCollection.m_sampData.resize(dataIn.getSampSize()); + memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize()); + + dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true); + } +} + +void AudioGroupCollection::update(AudioGroupFilePresenter* presenter) +{ + NSFileManager* fman = [NSFileManager defaultManager]; + NSArray* contents = + [fman contentsOfDirectoryAtURL:m_url + includingPropertiesForKeys:@[NSURLIsDirectoryKey] + options:NSDirectoryEnumerationSkipsSubdirectoryDescendants | + NSDirectoryEnumerationSkipsHiddenFiles + error:nil]; + if (!contents) + return; + + for (NSURL* path in contents) + { + NSNumber* isDir; + [path getResourceValue:&isDir forKey:NSURLIsDirectoryKey error:nil]; + + if (isDir.boolValue) + { + auto search = m_groups.find(path.lastPathComponent.UTF8String); + if (search == m_groups.end()) + { + std::string nameStr = path.lastPathComponent.UTF8String; + search = + m_groups.emplace(nameStr, + AudioGroupDataCollection{nameStr, + [path URLByAppendingPathComponent:@"proj"], + [path URLByAppendingPathComponent:@"pool"], + [path URLByAppendingPathComponent:@"sdir"], + [path URLByAppendingPathComponent:@"samp"], + [path URLByAppendingPathComponent:@"meta"]}).first; + search->second._attemptLoad(presenter); + } + } + } +} + +AudioGroupDataCollection::AudioGroupDataCollection(const std::string& 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]) {} + +bool AudioGroupDataCollection::_attemptLoad(AudioGroupFilePresenter* presenter) +{ + if (!isDataComplete()) + return false; + if (m_metaData && m_loadedProj) + return true; + if (!loadProj(presenter)) + return false; + if (!loadPool(presenter)) + return false; + if (!loadSdir(presenter)) + return false; + if (!loadSamp(presenter)) + return false; + if (!loadMeta(presenter)) + return false; + + 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; + } + + if (m_metaData && m_loadedProj) + { + m_loadedProj.emplace(amuse::AudioGroupProject::CreateAudioGroupProject(*m_loadedData)); + return true; + } + return false; +} + void AudioGroupDataCollection::moveURL(NSURL* oldUrl, NSURL* newUrl) { if (m_proj) @@ -46,7 +201,7 @@ bool AudioGroupDataCollection::loadProj(AudioGroupFilePresenter* presenter) if (!coord) return false; NSError* err; - __block std::unique_ptr& ret = m_projData; + __block std::vector& ret = m_projData; [coord coordinateReadingItemAtURL:m_proj options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err byAccessor:^(NSURL* newUrl) @@ -54,9 +209,11 @@ bool AudioGroupDataCollection::loadProj(AudioGroupFilePresenter* presenter) athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); if (r.hasError()) return; - ret = r.readUBytes(r.length()); + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); }]; - return m_projData.operator bool(); + return ret.size(); } bool AudioGroupDataCollection::loadPool(AudioGroupFilePresenter* presenter) @@ -67,7 +224,7 @@ bool AudioGroupDataCollection::loadPool(AudioGroupFilePresenter* presenter) if (!coord) return false; NSError* err; - __block std::unique_ptr& ret = m_poolData; + __block std::vector& ret = m_poolData; [coord coordinateReadingItemAtURL:m_pool options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err byAccessor:^(NSURL* newUrl) @@ -75,9 +232,11 @@ bool AudioGroupDataCollection::loadPool(AudioGroupFilePresenter* presenter) athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); if (r.hasError()) return; - ret = r.readUBytes(r.length()); + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); }]; - return m_poolData.operator bool(); + return ret.size(); } bool AudioGroupDataCollection::loadSdir(AudioGroupFilePresenter* presenter) @@ -88,7 +247,7 @@ bool AudioGroupDataCollection::loadSdir(AudioGroupFilePresenter* presenter) if (!coord) return false; NSError* err; - __block std::unique_ptr& ret = m_sdirData; + __block std::vector& ret = m_sdirData; [coord coordinateReadingItemAtURL:m_sdir options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err byAccessor:^(NSURL* newUrl) @@ -96,9 +255,11 @@ bool AudioGroupDataCollection::loadSdir(AudioGroupFilePresenter* presenter) athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); if (r.hasError()) return; - ret = r.readUBytes(r.length()); + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); }]; - return m_sdirData.operator bool(); + return ret.size(); } bool AudioGroupDataCollection::loadSamp(AudioGroupFilePresenter* presenter) @@ -109,7 +270,7 @@ bool AudioGroupDataCollection::loadSamp(AudioGroupFilePresenter* presenter) if (!coord) return false; NSError* err; - __block std::unique_ptr& ret = m_sampData; + __block std::vector& ret = m_sampData; [coord coordinateReadingItemAtURL:m_samp options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err byAccessor:^(NSURL* newUrl) @@ -117,63 +278,189 @@ bool AudioGroupDataCollection::loadSamp(AudioGroupFilePresenter* presenter) athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); if (r.hasError()) return; - ret = r.readUBytes(r.length()); + size_t len = r.length(); + ret.resize(len); + r.readUBytesToBuf(ret.data(), len); }]; - return m_sampData.operator bool(); + return ret.size(); +} + +bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) +{ + if (!m_meta) + return false; + NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:presenter]; + if (!coord) + return false; + NSError* err; + __block std::experimental::optional& ret = m_metaData; + [coord coordinateReadingItemAtURL:m_meta + options:NSFileCoordinatorReadingResolvesSymbolicLink error:&err + byAccessor:^(NSURL* newUrl) + { + athena::io::FileReader r([[newUrl path] UTF8String], 1024 * 32, false); + if (r.hasError()) + return; + ret.emplace(r); + }]; + return ret.operator bool(); } - (void)presentedSubitemDidAppearAtURL:(NSURL*)url { - NSURL* dir = nil; if ([url.lastPathComponent isEqualToString:@"proj"] || [url.lastPathComponent isEqualToString:@"pool"] || [url.lastPathComponent isEqualToString:@"sdir"] || [url.lastPathComponent isEqualToString:@"samp"]) - dir = url.baseURL; - - auto search = m_audioGroupCollections.find(dir.lastPathComponent.UTF8String); - if (search == m_audioGroupCollections.end()) - { - search = - m_audioGroupCollections.emplace(dir.lastPathComponent.UTF8String, - AudioGroupDataCollection{ - [dir URLByAppendingPathComponent:@"proj"], - [dir URLByAppendingPathComponent:@"pool"], - [dir URLByAppendingPathComponent:@"sdir"], - [dir URLByAppendingPathComponent:@"samp"]}).first; - } + [self update]; } - (void)presentedSubitemAtURL:(NSURL*)oldUrl didMoveToURL:(NSURL*)newUrl { - for (auto it = m_audioGroupCollections.begin() ; it != m_audioGroupCollections.end() ; ++it) + for (auto& pair : m_audioGroupCollections) { - std::pair& pair = *it; - pair.second.moveURL(oldUrl, newUrl); + for (auto& pair2 : pair.second.m_groups) + { + pair2.second.moveURL(oldUrl, newUrl); + } } } - (NSInteger)outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(nullable id)item { + lastOutlineView = outlineView; if (!item) return m_audioGroupCollections.size(); - return 0; + + AudioGroupCollection& collection = *((AudioGroupCollectionToken*)item)->m_collection; + return collection.m_groups.size(); } - (id)outlineView:(NSOutlineView*)outlineView child:(NSInteger)index ofItem:(nullable id)item { if (!item) { - + auto search = m_iteratorTracker->seekToIndex(index); + if (search == m_audioGroupCollections.end()) + return nil; + return search->second.m_token; } - return nil; + + AudioGroupCollection& collection = *((AudioGroupCollectionToken*)item)->m_collection; + auto search = collection.m_iteratorTracker->seekToIndex(index); + if (search == collection.m_groups.end()) + return nil; + return search->second.m_token; } - (BOOL)outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item { + if ([item isKindOfClass:[AudioGroupCollectionToken class]]) + return YES; return NO; } +- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +{ + if ([item isKindOfClass:[AudioGroupCollectionToken class]]) + { + AudioGroupCollection& collection = *((AudioGroupCollectionToken*)item)->m_collection; + if ([tableColumn.identifier isEqualToString:@"CollectionColumn"]) + return [NSNumber numberWithBool:true]; + else if ([tableColumn.identifier isEqualToString:@"DetailsColumn"]) + return [NSString stringWithFormat:@"%" PRISize " groups", collection.m_groups.size()]; + } + else if ([item isKindOfClass:[AudioGroupDataToken class]]) + { + AudioGroupDataCollection& data = *((AudioGroupDataToken*)item)->m_collection; + if ([tableColumn.identifier isEqualToString:@"CollectionColumn"]) + return [NSNumber numberWithBool:data.m_metaData->active]; + else if ([tableColumn.identifier isEqualToString:@"DetailsColumn"]) + { + if (!data.m_loadedProj) + return @""; + return [NSString stringWithFormat:@"%zu SongGroups, %zu SFXGroups", + data.m_loadedProj->songGroups().size(), data.m_loadedProj->sfxGroups().size()]; + } + } + + return nil; +} + +- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(nonnull id)cell forTableColumn:(nullable NSTableColumn *)tableColumn item:(nonnull id)item +{ + if ([item isKindOfClass:[AudioGroupCollectionToken class]]) + { + AudioGroupCollection& collection = *((AudioGroupCollectionToken*)item)->m_collection; + if ([tableColumn.identifier isEqualToString:@"CollectionColumn"]) + ((NSButtonCell*)cell).title = collection.m_url.lastPathComponent; + } + else if ([item isKindOfClass:[AudioGroupDataToken class]]) + { + AudioGroupDataCollection& data = *((AudioGroupDataToken*)item)->m_collection; + if ([tableColumn.identifier isEqualToString:@"CollectionColumn"]) + ((NSButtonCell*)cell).title = @(data.m_name.c_str()); + } +} + +- (BOOL)addCollectionName:(std::string&&)name items:(std::vector>&&)collection +{ + NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:self]; + if (!coord) + return false; + + NSURL* dir = [m_groupURL URLByAppendingPathComponent:@(name.c_str())]; + __block AudioGroupCollection& insert = m_audioGroupCollections.emplace(name, dir).first->second; + insert.addCollection(std::move(collection)); + + [coord coordinateWritingItemAtURL:m_groupURL options:0 error:nil + byAccessor:^(NSURL* newUrl) + { + for (std::pair& pair : insert.m_groups) + { + NSURL* collectionUrl = [insert.m_url URLByAppendingPathComponent:@(pair.first.c_str())]; + [[NSFileManager defaultManager] createDirectoryAtURL:collectionUrl withIntermediateDirectories:YES attributes:nil error:nil]; + + FILE* fp = fopen(pair.second.m_proj.path.UTF8String, "wb"); + if (fp) + { + fwrite(pair.second.m_projData.data(), 1, pair.second.m_projData.size(), fp); + fclose(fp); + } + + fp = fopen(pair.second.m_pool.path.UTF8String, "wb"); + if (fp) + { + fwrite(pair.second.m_poolData.data(), 1, pair.second.m_poolData.size(), fp); + fclose(fp); + } + + fp = fopen(pair.second.m_sdir.path.UTF8String, "wb"); + if (fp) + { + fwrite(pair.second.m_sdirData.data(), 1, pair.second.m_sdirData.size(), fp); + fclose(fp); + } + + fp = fopen(pair.second.m_samp.path.UTF8String, "wb"); + if (fp) + { + fwrite(pair.second.m_sampData.data(), 1, pair.second.m_sampData.size(), fp); + fclose(fp); + } + + fp = fopen(pair.second.m_meta.path.UTF8String, "wb"); + if (fp) + { + fwrite(&*pair.second.m_metaData, 1, sizeof(*pair.second.m_metaData), fp); + fclose(fp); + } + } + }]; + + return true; +} + - (void)update { NSFileCoordinator* coord = [[NSFileCoordinator alloc] initWithFilePresenter:self]; @@ -181,41 +468,47 @@ bool AudioGroupDataCollection::loadSamp(AudioGroupFilePresenter* presenter) return; NSError* coordErr; __block NSError* managerErr; - __block std::map& theMap = m_audioGroupCollections; + __block std::map& theMap = m_audioGroupCollections; + __block AudioGroupFilePresenter* presenter = self; [coord coordinateReadingItemAtURL:m_groupURL options:NSFileCoordinatorReadingResolvesSymbolicLink error:&coordErr byAccessor:^(NSURL* newUrl) - { - NSFileManager* fman = [NSFileManager defaultManager]; - NSArray* contents = - [fman contentsOfDirectoryAtURL:newUrl - includingPropertiesForKeys:@[NSURLIsDirectoryKey] - options:NSDirectoryEnumerationSkipsSubdirectoryDescendants | - NSDirectoryEnumerationSkipsHiddenFiles - error:&managerErr]; - if (!contents) - return; + { + NSFileManager* fman = [NSFileManager defaultManager]; + NSArray* contents = + [fman contentsOfDirectoryAtURL:newUrl + includingPropertiesForKeys:@[NSURLIsDirectoryKey] + options:NSDirectoryEnumerationSkipsSubdirectoryDescendants | + NSDirectoryEnumerationSkipsHiddenFiles + error:&managerErr]; + if (!contents) + return; - for (NSURL* path in contents) - { - NSNumber* isDir; - [path getResourceValue:&isDir forKey:NSURLIsDirectoryKey error:nil]; - - if (isDir.boolValue) - { - auto search = theMap.find(path.lastPathComponent.UTF8String); - if (search == theMap.end()) - { - search = - theMap.emplace(path.lastPathComponent.UTF8String, - AudioGroupDataCollection{ - [path URLByAppendingPathComponent:@"proj"], - [path URLByAppendingPathComponent:@"pool"], - [path URLByAppendingPathComponent:@"sdir"], - [path URLByAppendingPathComponent:@"samp"]}).first; - } - } - } - }]; + for (NSURL* path in contents) + { + NSNumber* isDir; + [path getResourceValue:&isDir forKey:NSURLIsDirectoryKey error:nil]; + + if (isDir.boolValue) + { + auto search = theMap.find(path.lastPathComponent.UTF8String); + if (search == theMap.end()) + { + search = theMap.emplace(path.lastPathComponent.UTF8String, path).first; + search->second.update(presenter); + } + } + } + }]; + + [self resetIterators]; +} + +- (void)resetIterators +{ + m_iteratorTracker.emplace(m_audioGroupCollections.begin(), m_audioGroupCollections.end()); + for (auto& pair : m_audioGroupCollections) + pair.second.m_iteratorTracker.emplace(pair.second.m_groups.begin(), pair.second.m_groups.end()); + [lastOutlineView reloadItem:nil reloadChildren:YES]; } - (id)init diff --git a/AudioUnit/CMakeLists.txt b/AudioUnit/CMakeLists.txt index 13b4e71..ea1fea7 100644 --- a/AudioUnit/CMakeLists.txt +++ b/AudioUnit/CMakeLists.txt @@ -57,7 +57,7 @@ if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VE AmuseContainingApp.mm AudioGroupFilePresenter.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) target_link_libraries(amuse-au-container amuse boo soxr ${AUDIOUNIT_LIBRARY} ${COREAUDIOKIT_LIBRARY} - ${AVFOUNDATION_LIBRARY} ${BOO_SYS_LIBS} logvisor athena-core) + ${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 diff --git a/include/amuse/AudioGroupData.hpp b/include/amuse/AudioGroupData.hpp index 4328d70..9a6d2ad 100644 --- a/include/amuse/AudioGroupData.hpp +++ b/include/amuse/AudioGroupData.hpp @@ -12,35 +12,70 @@ class AudioGroupData friend class Engine; protected: unsigned char* m_proj; + size_t m_projSz; unsigned char* m_pool; + size_t m_poolSz; unsigned char* m_sdir; + size_t m_sdirSz; unsigned char* m_samp; + size_t m_sampSz; + DataFormat m_fmt; bool m_absOffs; - AudioGroupData(unsigned char* proj, unsigned char* pool, - unsigned char* sdir, unsigned char* samp, + 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_pool(pool), m_sdir(sdir), m_samp(samp), + : 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, unsigned char* pool, - unsigned char* sdir, unsigned char* samp, GCNDataTag) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), + 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, unsigned char* pool, - unsigned char* sdir, unsigned char* samp, bool absOffs, N64DataTag) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), + 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, unsigned char* pool, - unsigned char* sdir, unsigned char* samp, bool absOffs, PCDataTag) - : m_proj(proj), m_pool(pool), m_sdir(sdir), m_samp(samp), + 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 { @@ -64,6 +99,8 @@ public: IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other); IntrusiveAudioGroupData& operator=(IntrusiveAudioGroupData&& other); + + void dangleOwnership() {m_owns = false;} }; } diff --git a/include/amuse/ContainerRegistry.hpp b/include/amuse/ContainerRegistry.hpp index ca670f8..a8fb8b5 100644 --- a/include/amuse/ContainerRegistry.hpp +++ b/include/amuse/ContainerRegistry.hpp @@ -37,6 +37,7 @@ public: static const char* TypeToName(Type tp); static Type DetectContainerType(const char* path); static std::vector> LoadContainer(const char* path); + static std::vector> LoadContainer(const char* path, Type& typeOut); static std::vector> LoadSongs(const char* path); }; diff --git a/lib/AudioGroupData.cpp b/lib/AudioGroupData.cpp index a68a241..3b2083e 100644 --- a/lib/AudioGroupData.cpp +++ b/lib/AudioGroupData.cpp @@ -15,7 +15,9 @@ IntrusiveAudioGroupData::~IntrusiveAudioGroupData() } IntrusiveAudioGroupData::IntrusiveAudioGroupData(IntrusiveAudioGroupData&& other) -: AudioGroupData(other.m_proj, other.m_pool, other.m_sdir, other.m_samp, other.m_fmt, other.m_absOffs) +: 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; diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index 20141d6..6ecf036 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -119,7 +119,8 @@ static bool IsSongExtension(const char* path, const char*& dotOut) static bool ValidateMP1(FILE* fp) { - FileLength(fp); + if (FileLength(fp) > 40 * 1024 * 1024) + return false; uint32_t magic; fread(&magic, 1, 4, fp); @@ -228,29 +229,34 @@ static std::vector> LoadMP1(FILE ReadString(fp); std::string name = ReadString(fp); - uint32_t len; - fread(&len, 1, 4, fp); - len = SBig(len); - std::unique_ptr pool(new uint8_t[len]); - fread(pool.get(), 1, len, fp); + uint32_t poolLen; + fread(&poolLen, 1, 4, fp); + poolLen = SBig(poolLen); + std::unique_ptr pool(new uint8_t[poolLen]); + fread(pool.get(), 1, poolLen, fp); - fread(&len, 1, 4, fp); - len = SBig(len); - std::unique_ptr proj(new uint8_t[len]); - fread(proj.get(), 1, len, fp); + uint32_t projLen; + fread(&projLen, 1, 4, fp); + projLen = SBig(projLen); + std::unique_ptr proj(new uint8_t[projLen]); + fread(proj.get(), 1, projLen, fp); - fread(&len, 1, 4, fp); - len = SBig(len); - std::unique_ptr samp(new uint8_t[len]); - fread(samp.get(), 1, len, fp); + uint32_t sampLen; + fread(&sampLen, 1, 4, fp); + sampLen = SBig(sampLen); + std::unique_ptr samp(new uint8_t[sampLen]); + fread(samp.get(), 1, sampLen, fp); - fread(&len, 1, 4, fp); - len = SBig(len); - std::unique_ptr sdir(new uint8_t[len]); - fread(sdir.get(), 1, len, fp); + uint32_t sdirLen; + fread(&sdirLen, 1, 4, fp); + sdirLen = SBig(sdirLen); + std::unique_ptr sdir(new uint8_t[sdirLen]); + fread(sdir.get(), 1, sdirLen, fp); - ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), GCNDataTag{}}); + ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), projLen, + pool.release(), poolLen, + sdir.release(), sdirLen, + samp.release(), sampLen, GCNDataTag{}}); } } FSeek(fp, origPos, SEEK_SET); @@ -263,7 +269,8 @@ static std::vector> LoadMP1(FILE static bool ValidateMP1Songs(FILE* fp) { - FileLength(fp); + if (FileLength(fp) > 40 * 1024 * 1024) + return false; uint32_t magic; fread(&magic, 1, 4, fp); @@ -404,7 +411,8 @@ static std::vector> LoadMP1S static bool ValidateMP2(FILE* fp) { - FileLength(fp); + if (FileLength(fp) > 40 * 1024 * 1024) + return false; uint32_t magic; fread(&magic, 1, 4, fp); @@ -541,8 +549,10 @@ static std::vector> LoadMP2(FILE std::unique_ptr samp(new uint8_t[sampSz]); fread(samp.get(), 1, sampSz, fp); - ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), GCNDataTag{}}); + ret.emplace_back(std::move(name), IntrusiveAudioGroupData{proj.release(), projSz, + pool.release(), poolSz, + sdir.release(), sdirSz, + samp.release(), sampSz, GCNDataTag{}}); } } FSeek(fp, origPos, SEEK_SET); @@ -589,6 +599,8 @@ static void SwapN64Rom32(void* data, size_t size) static bool ValidateRS1PC(FILE* fp) { size_t endPos = FileLength(fp); + if (endPos > 100 * 1024 * 1024) + return false; uint32_t fstOff; uint32_t fstSz; @@ -640,41 +652,49 @@ static std::vector> LoadRS1PC(FI fread(entries.get(), fstSz, 1, fp); std::unique_ptr proj; + size_t projSz = 0; std::unique_ptr pool; + size_t poolSz = 0; std::unique_ptr sdir; + size_t sdirSz = 0; std::unique_ptr samp; + size_t sampSz = 0; for (uint32_t i=0 ; i> LoadRS1N64(F const RS1FSTEntry* lastEnt = reinterpret_cast(dataSeg + fstEnd); std::unique_ptr proj; + size_t projSz = 0; std::unique_ptr pool; + size_t poolSz = 0; std::unique_ptr sdir; + size_t sdirSz = 0; std::unique_ptr samp; + size_t sampSz = 0; for (; entry != lastEnt ; ++entry) { @@ -786,6 +810,7 @@ static std::vector> LoadRS1N64(F uLongf outSz = ent.decompSz; uncompress(proj.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + projSz = ent.decompSz; } else if (!strncmp("pool_SND", ent.name, 16)) { @@ -800,6 +825,7 @@ static std::vector> LoadRS1N64(F uLongf outSz = ent.decompSz; uncompress(pool.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + poolSz = ent.decompSz; } else if (!strncmp("sdir_SND", ent.name, 16)) { @@ -814,6 +840,7 @@ static std::vector> LoadRS1N64(F uLongf outSz = ent.decompSz; uncompress(sdir.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + sdirSz = ent.decompSz; } else if (!strncmp("samp_SND", ent.name, 16)) { @@ -828,11 +855,12 @@ static std::vector> LoadRS1N64(F uLongf outSz = ent.decompSz; uncompress(samp.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + sampSz = ent.decompSz; } } - ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), projSz, pool.release(), poolSz, + sdir.release(), sdirSz, samp.release(), sampSz, false, N64DataTag{}}); } @@ -842,7 +870,9 @@ static std::vector> LoadRS1N64(F static bool ValidateBFNPC(FILE* fp) { size_t endPos = FileLength(fp); - + if (endPos > 100 * 1024 * 1024) + return false; + uint32_t fstOff; uint32_t fstSz; if (fread(&fstOff, 1, 4, fp) == 4 && fread(&fstSz, 1, 4, fp) == 4) @@ -893,9 +923,13 @@ static std::vector> LoadBFNPC(FI fread(entries.get(), fstSz, 1, fp); std::unique_ptr proj; + size_t projSz = 0; std::unique_ptr pool; + size_t poolSz = 0; std::unique_ptr sdir; + size_t sdirSz = 0; std::unique_ptr samp; + size_t sampSz = 0; for (uint32_t i=0 ; i> LoadBFNPC(FI proj.reset(new uint8_t[entry.decompSz]); FSeek(fp, entry.offset, SEEK_SET); fread(proj.get(), 1, entry.decompSz, fp); + projSz = entry.decompSz; } else if (!strncmp("pool", entry.name, 16)) { pool.reset(new uint8_t[entry.decompSz]); FSeek(fp, entry.offset, SEEK_SET); fread(pool.get(), 1, entry.decompSz, fp); + poolSz = entry.decompSz; } else if (!strncmp("sdir", entry.name, 16)) { sdir.reset(new uint8_t[entry.decompSz]); FSeek(fp, entry.offset, SEEK_SET); fread(sdir.get(), 1, entry.decompSz, fp); + sdirSz = entry.decompSz; } else if (!strncmp("samp", entry.name, 16)) { samp.reset(new uint8_t[entry.decompSz]); FSeek(fp, entry.offset, SEEK_SET); fread(samp.get(), 1, entry.decompSz, fp); + sampSz = entry.decompSz; } } - ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), projSz, pool.release(), poolSz, + sdir.release(), sdirSz, samp.release(), sampSz, true, PCDataTag{}}); } } @@ -1017,9 +1055,13 @@ static std::vector> LoadBFNN64(F const RS1FSTEntry* lastEnt = reinterpret_cast(dataSeg + fstEnd); std::unique_ptr proj; + size_t projSz = 0; std::unique_ptr pool; + size_t poolSz = 0; std::unique_ptr sdir; + size_t sdirSz = 0; std::unique_ptr samp; + size_t sampSz = 0; for (; entry != lastEnt ; ++entry) { @@ -1039,6 +1081,7 @@ static std::vector> LoadBFNN64(F uLongf outSz = ent.decompSz; uncompress(proj.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + projSz = ent.decompSz; } else if (!strncmp("pool", ent.name, 16)) { @@ -1053,6 +1096,7 @@ static std::vector> LoadBFNN64(F uLongf outSz = ent.decompSz; uncompress(pool.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + poolSz = ent.decompSz; } else if (!strncmp("sdir", ent.name, 16)) { @@ -1067,6 +1111,7 @@ static std::vector> LoadBFNN64(F uLongf outSz = ent.decompSz; uncompress(sdir.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + sdirSz = ent.decompSz; } else if (!strncmp("samp", ent.name, 16)) { @@ -1081,11 +1126,12 @@ static std::vector> LoadBFNN64(F uLongf outSz = ent.decompSz; uncompress(samp.get(), &outSz, dataSeg + ent.offset, ent.compSz); } + sampSz = ent.decompSz; } } - ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), projSz, pool.release(), poolSz, + sdir.release(), sdirSz, samp.release(), sampSz, true, N64DataTag{}}); } @@ -1164,7 +1210,9 @@ struct RS23SONHead static bool ValidateRS2(FILE* fp) { size_t endPos = FileLength(fp); - + if (endPos > 600 * 1024 * 1024) + return false; + uint64_t fstOff; fread(&fstOff, 1, 8, fp); fstOff = SBig(fstOff); @@ -1241,8 +1289,8 @@ static std::vector> LoadRS2(FILE char name[128]; snprintf(name, 128, "GroupFile%u", j); - ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), GCNDataTag{}}); + ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), head.projLen, pool.release(), head.poolLen, + sdir.release(), head.sdirLen, samp.release(), head.sampLen, GCNDataTag{}}); } break; @@ -1334,7 +1382,9 @@ struct RS3FSTEntry static bool ValidateRS3(FILE* fp) { size_t endPos = FileLength(fp); - + if (endPos > 600 * 1024 * 1024) + return false; + uint64_t fstOff; fread(&fstOff, 1, 8, fp); fstOff = SBig(fstOff); @@ -1409,8 +1459,8 @@ static std::vector> LoadRS3(FILE char name[128]; snprintf(name, 128, "GroupFile%u", j); - ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), GCNDataTag{}}); + ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), head.projLen, pool.release(), head.poolLen, + sdir.release(), head.sdirLen, samp.release(), head.sampLen, GCNDataTag{}}); } break; @@ -1538,11 +1588,19 @@ ContainerRegistry::Type ContainerRegistry::DetectContainerType(const char* path) return Type::Invalid; } - + std::vector> ContainerRegistry::LoadContainer(const char* path) +{ + Type typeOut; + return LoadContainer(path, typeOut); +}; + +std::vector> +ContainerRegistry::LoadContainer(const char* path, Type& typeOut) { FILE* fp; + typeOut = Type::Invalid; /* See if provided file is one of four raw chunks */ const char* dot = nullptr; @@ -1603,49 +1661,50 @@ ContainerRegistry::LoadContainer(const char* path) fclose(fp); fp = fopen(projPath, "rb"); - size_t fLen = FileLength(fp); - if (!fLen) + size_t projLen = FileLength(fp); + if (!projLen) return ret; - std::unique_ptr proj(new uint8_t[fLen]); - fread(proj.get(), 1, fLen, fp); + std::unique_ptr proj(new uint8_t[projLen]); + fread(proj.get(), 1, projLen, fp); fp = fopen(poolPath, "rb"); - fLen = FileLength(fp); - if (!fLen) + size_t poolLen = FileLength(fp); + if (!poolLen) return ret; - std::unique_ptr pool(new uint8_t[fLen]); - fread(pool.get(), 1, fLen, fp); + std::unique_ptr pool(new uint8_t[poolLen]); + fread(pool.get(), 1, poolLen, fp); fp = fopen(sdirPath, "rb"); - fLen = FileLength(fp); - if (!fLen) + size_t sdirLen = FileLength(fp); + if (!sdirLen) return ret; - std::unique_ptr sdir(new uint8_t[fLen]); - fread(sdir.get(), 1, fLen, fp); + std::unique_ptr sdir(new uint8_t[sdirLen]); + fread(sdir.get(), 1, sdirLen, fp); fp = fopen(sampPath, "rb"); - fLen = FileLength(fp); - if (!fLen) + size_t sampLen = FileLength(fp); + if (!sampPath) return ret; - std::unique_ptr samp(new uint8_t[fLen]); - fread(samp.get(), 1, fLen, fp); + std::unique_ptr samp(new uint8_t[sampLen]); + fread(samp.get(), 1, sampLen, fp); fclose(fp); /* SDIR-based format detection */ if (*reinterpret_cast(sdir.get() + 8) == 0x0) - ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, + sdir.release(), sdirLen, samp.release(), sampLen, GCNDataTag{}}); else if (sdir[9] == 0x0) - ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, + sdir.release(), sdirLen, samp.release(), sampLen, false, N64DataTag{}}); else - ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), pool.release(), - sdir.release(), samp.release(), + ret.emplace_back("Group", IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, + sdir.release(), sdirLen, samp.release(), sampLen, false, PCDataTag{}}); + typeOut = Type::Raw4; return ret; } @@ -1657,6 +1716,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadMP1(fp); fclose(fp); + typeOut = Type::MetroidPrime; return ret; } @@ -1664,6 +1724,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadMP2(fp); fclose(fp); + typeOut = Type::MetroidPrime2; return ret; } @@ -1671,6 +1732,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadRS1PC(fp); fclose(fp); + typeOut = Type::RogueSquadronPC; return ret; } @@ -1678,6 +1740,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadRS1N64(fp); fclose(fp); + typeOut = Type::RogueSquadronN64; return ret; } @@ -1685,6 +1748,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadBFNPC(fp); fclose(fp); + typeOut = Type::BattleForNabooPC; return ret; } @@ -1692,6 +1756,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadBFNN64(fp); fclose(fp); + typeOut = Type::BattleForNabooN64; return ret; } @@ -1699,6 +1764,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadRS2(fp); fclose(fp); + typeOut = Type::RogueSquadron2; return ret; } @@ -1706,6 +1772,7 @@ ContainerRegistry::LoadContainer(const char* path) { auto ret = LoadRS3(fp); fclose(fp); + typeOut = Type::RogueSquadron3; return ret; } From 62ece61cb2189c397969b8d313a6f63c4b56293b Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 4 Jun 2016 12:38:55 -1000 Subject: [PATCH 3/7] More UI bindings, metadata management --- AudioUnit/AmuseContainerMainMenu.xib | 23 ++- AudioUnit/AmuseContainingApp.hpp | 48 ++++++ AudioUnit/AmuseContainingApp.mm | 83 +++++------ AudioUnit/AudioGroupFilePresenter.hpp | 17 ++- AudioUnit/AudioGroupFilePresenter.mm | 205 ++++++++++++++++++++------ AudioUnit/CMakeLists.txt | 2 +- 6 files changed, 277 insertions(+), 101 deletions(-) create mode 100644 AudioUnit/AmuseContainingApp.hpp diff --git a/AudioUnit/AmuseContainerMainMenu.xib b/AudioUnit/AmuseContainerMainMenu.xib index a7cc169..380a37b 100644 --- a/AudioUnit/AmuseContainerMainMenu.xib +++ b/AudioUnit/AmuseContainerMainMenu.xib @@ -19,6 +19,8 @@ + + @@ -78,12 +80,15 @@ - + - + + +CA + - + @@ -182,10 +187,13 @@ - + + + + @@ -195,9 +190,9 @@ CA - - - - - - - - - - - - - - - - - + + + - - + + - - + + - + - + - + - + @@ -369,39 +331,39 @@ CA - - - + + + - + - - + + - + - + - + - + - + @@ -409,11 +371,11 @@ CA - - + + - + @@ -421,7 +383,7 @@ CA - + - - - - - - - - - - - - - - - - - - + + + + @@ -464,74 +412,39 @@ CA - - - - - - - - - - - - - - - - - + + + - - + + - - + + - + - + - + - + @@ -539,39 +452,39 @@ CA - - - + + + - + - - + + - + - + - + - + - + @@ -579,11 +492,11 @@ CA - - + + - + @@ -591,7 +504,7 @@ CA - + - - - - - - - - - - - - - - - - - - + + + + diff --git a/AudioUnit/AmuseContainingApp.hpp b/AudioUnit/AmuseContainingApp.hpp index 52f345f..f3eb80a 100644 --- a/AudioUnit/AmuseContainingApp.hpp +++ b/AudioUnit/AmuseContainingApp.hpp @@ -3,10 +3,12 @@ #import #import "AudioGroupFilePresenter.hpp" +#include +#include @interface DataOutlineView : NSOutlineView { - @public +@public IBOutlet NSButton* removeDataButton; IBOutlet NSMenuItem* deleteMenuItem; } @@ -14,14 +16,16 @@ @interface SamplesTableController : NSObject { - + AudioGroupFilePresenter* presenter; } +- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present; @end @interface SFXTableController : NSObject { - + AudioGroupFilePresenter* presenter; } +- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present; @end @interface AppDelegate : NSObject @@ -40,9 +44,18 @@ SamplesTableController* samplesController; SFXTableController* sfxController; + +@public + std::unique_ptr booEngine; + std::experimental::optional amuseAllocator; + std::experimental::optional amuseEngine; + std::shared_ptr activeSFXVox; } - (BOOL)importURL:(NSURL*)url; - (void)outlineView:(DataOutlineView*)ov selectionChanged:(id)item; +- (void)reloadTables; +- (void)startSFX:(int)sfxId; +- (void)startSample:(int)sampId; @end #endif // __AMUSE_AUDIOUNIT_CONTAININGAPP_HPP__ \ No newline at end of file diff --git a/AudioUnit/AmuseContainingApp.mm b/AudioUnit/AmuseContainingApp.mm index 76236c2..c281347 100644 --- a/AudioUnit/AmuseContainingApp.mm +++ b/AudioUnit/AmuseContainingApp.mm @@ -9,13 +9,27 @@ @class SamplesTableController; @class SFXTableController; -/* Blocks mousedown events */ +/* Blocks mousedown events (so button may be used as a visual element only) */ @interface InactiveButton : NSButton {} @end @implementation InactiveButton - (void)mouseDown:(NSEvent *)theEvent {} @end +/* Restricts mousedown to checkbox */ +@interface RestrictedCheckButton : NSButtonCell {} +@end +@implementation RestrictedCheckButton +- (NSCellHitResult)hitTestForEvent:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView +{ + NSRect restrictFrame = cellFrame; + restrictFrame.size.width = 22; + if (NSPointInRect([controlView convertPoint:[event locationInWindow] fromView:nil], restrictFrame)) + return NSCellHitTrackableArea; + return NSCellHitNone; +} +@end + @interface MainView : NSView { AudioUnitViewController* amuseVC; @@ -89,12 +103,49 @@ - (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView { - + return presenter->m_sampleTableData.size(); } -- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn*)tableColumn row:(NSInteger)row +- (NSView*)tableView:(NSTableView *)tableView viewForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row { - + if (presenter->m_sampleTableData.size() <= row) + return nil; + NSTableCellView* view = [tableView makeViewWithIdentifier:@"SampleIDColumn" owner:self]; + AudioGroupSampleToken* sampToken = presenter->m_sampleTableData[row]; + if ([tableColumn.identifier isEqualToString:@"SampleIDColumn"]) + view.textField.attributedStringValue = sampToken->m_name; + else if ([tableColumn.identifier isEqualToString:@"SampleDetailsColumn"]) + view.textField.stringValue = @""; + else + view.textField.attributedStringValue = sampToken->m_name; + return view; +} + +- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row +{ + if (presenter->m_sampleTableData.size() <= row) + return NO; + AudioGroupSampleToken* sampToken = presenter->m_sampleTableData[row]; + if (!sampToken->m_sample) + return YES; + return NO; +} + +- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + if (presenter->m_sampleTableData.size() <= row) + return NO; + AudioGroupSampleToken* sampToken = presenter->m_sampleTableData[row]; + if (!sampToken->m_sample) + return NO; + return YES; +} + +- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present +{ + self = [super init]; + presenter = present; + return self; } @end @@ -104,11 +155,60 @@ - (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView { + return presenter->m_sfxTableData.size(); } -- (nullable id)tableView:(NSTableView *)tableView objectValueForTableColumn:(nullable NSTableColumn*)tableColumn row:(NSInteger)row +- (NSView*)tableView:(NSTableView *)tableView viewForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row { - + if (presenter->m_sfxTableData.size() <= row) + return nil; + NSTableCellView* view = [tableView makeViewWithIdentifier:@"SFXIDColumn" owner:self]; + AudioGroupSFXToken* sfxToken = presenter->m_sfxTableData[row]; + if ([tableColumn.identifier isEqualToString:@"SFXIDColumn"]) + view.textField.attributedStringValue = sfxToken->m_name; + else if ([tableColumn.identifier isEqualToString:@"SFXDetailsColumn"]) + view.textField.stringValue = @""; + else + view.textField.attributedStringValue = sfxToken->m_name; + return view; +} + +- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row +{ + if (presenter->m_sfxTableData.size() <= row) + return NO; + AudioGroupSFXToken* sfxToken = presenter->m_sfxTableData[row]; + if (!sfxToken->m_sfx) + return YES; + return NO; +} + +- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + if (presenter->m_sfxTableData.size() <= row) + return NO; + AudioGroupSFXToken* sfxToken = presenter->m_sfxTableData[row]; + if (!sfxToken->m_sfx) + return NO; + return YES; +} + +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + NSTableView* table = notification.object; + NSInteger row = table.selectedRow; + if (presenter->m_sfxTableData.size() <= row) + return; + AudioGroupSFXToken* sfxToken = presenter->m_sfxTableData[row]; + AppDelegate* delegate = NSApp.delegate; + [delegate startSFX:sfxToken->m_loadId]; +} + +- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present +{ + self = [super init]; + presenter = present; + return self; } @end @@ -117,7 +217,10 @@ - (void)applicationWillFinishLaunching:(NSNotification*)notification { - [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"]; + booEngine = boo::NewAudioVoiceEngine(); + amuseAllocator.emplace(*booEngine); + amuseEngine.emplace(*amuseAllocator); + [mainWindow.toolbar setSelectedItemIdentifier:@"DataTab"]; groupFilePresenter = [AudioGroupFilePresenter new]; @@ -126,15 +229,22 @@ dataOutline.delegate = groupFilePresenter; [dataOutline reloadItem:nil reloadChildren:YES]; - samplesController = [SamplesTableController new]; + samplesController = [[SamplesTableController alloc] initWithAudioGroupPresenter:groupFilePresenter]; samplesTable.dataSource = samplesController; samplesTable.delegate = samplesController; [samplesTable reloadData]; - sfxController = [SFXTableController new]; + sfxController = [[SFXTableController alloc] initWithAudioGroupPresenter:groupFilePresenter]; sfxTable.dataSource = sfxController; sfxTable.delegate = sfxController; [sfxTable reloadData]; + + [NSTimer scheduledTimerWithTimeInterval:1.0 / 60.0 target:self selector:@selector(pumpTimer:) userInfo:nil repeats:YES]; +} + +- (void)pumpTimer:(NSTimer*)timer +{ + amuseEngine->pumpEngine(); } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender @@ -174,6 +284,23 @@ }]; } +- (void)startSFX:(int)sfxId +{ + if (activeSFXVox) + activeSFXVox->keyOff(); + activeSFXVox = amuseEngine->fxStart(sfxId, 1.f, 0.f); +} + +- (void)startSample:(int)sampleId +{ +} + +- (void)reloadTables +{ + [sfxTable reloadData]; + [samplesTable reloadData]; +} + - (IBAction)filterDataOutline:(id)sender { [groupFilePresenter setSearchFilter:[sender stringValue]]; diff --git a/AudioUnit/AudioGroupFilePresenter.hpp b/AudioUnit/AudioGroupFilePresenter.hpp index f5e249c..a34537a 100644 --- a/AudioUnit/AudioGroupFilePresenter.hpp +++ b/AudioUnit/AudioGroupFilePresenter.hpp @@ -12,6 +12,8 @@ @class AudioGroupFilePresenter; @class AudioGroupDataToken; @class AudioGroupCollectionToken; +@class AudioGroupSFXToken; +@class AudioGroupSampleToken; struct AudioGroupDataCollection { @@ -42,7 +44,7 @@ struct AudioGroupDataCollection std::experimental::optional m_metaData; std::experimental::optional m_loadedData; - std::experimental::optional m_loadedProj; + const amuse::AudioGroup* m_loadedGroup; void moveURL(NSURL* oldUrl, NSURL* newUrl); @@ -72,6 +74,8 @@ struct AudioGroupCollection void addCollection(std::vector>&& collection); void update(AudioGroupFilePresenter* presenter); bool doSearch(const std::string& str); + void addSFX(std::vector& vecOut); + void addSamples(std::vector& vecOut); }; @interface AudioGroupDataToken : NSObject @@ -90,6 +94,26 @@ struct AudioGroupCollection - (id)initWithCollection:(AudioGroupCollection*)collection; @end +@interface AudioGroupSFXToken : NSObject +{ +@public + NSAttributedString* m_name; + int m_loadId; + const amuse::SFXGroupIndex::SFXEntry* m_sfx; +} +- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx; +@end + +@interface AudioGroupSampleToken : NSObject +{ +@public + NSAttributedString* m_name; + const std::pair* m_sample; +} +- (id)initWithName:(NSAttributedString*)name samp:(const std::pair*)sample; +@end + @interface AudioGroupFilePresenter : NSObject { NSURL* m_groupURL; @@ -98,6 +122,10 @@ struct AudioGroupCollection std::vector>::iterator> m_filterAudioGroupCollections; NSOutlineView* lastOutlineView; NSString* searchStr; + +@public + std::vector m_sfxTableData; + std::vector m_sampleTableData; } - (BOOL)addCollectionName:(std::string&&)name items:(std::vector>&&)collection; - (void)update; diff --git a/AudioUnit/AudioGroupFilePresenter.mm b/AudioUnit/AudioGroupFilePresenter.mm index 61cdf2a..b42101d 100644 --- a/AudioUnit/AudioGroupFilePresenter.mm +++ b/AudioUnit/AudioGroupFilePresenter.mm @@ -28,6 +28,28 @@ static std::string StrToLower(const std::string& str) } @end +@implementation AudioGroupSFXToken +- (id)initWithName:(NSAttributedString*)name loadId:(int)loadId sfx:(const amuse::SFXGroupIndex::SFXEntry*)sfx +{ + self = [super init]; + m_name = name; + m_loadId = loadId; + m_sfx = sfx; + return self; +} +@end + +@implementation AudioGroupSampleToken +- (id)initWithName:(NSAttributedString*)name samp:(const std::pair*)sample +{ + self = [super init]; + m_name = name; + m_sample = sample; + return self; +} +@end + @implementation AudioGroupFilePresenter - (NSURL*)presentedItemURL @@ -130,6 +152,56 @@ bool AudioGroupCollection::doSearch(const std::string& str) return ret; } +void AudioGroupCollection::addSFX(std::vector& vecOut) +{ + for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it) + { + if (!it->second->m_metaData->active) + continue; + const auto& sfxGroups = it->second->m_loadedGroup->getProj().sfxGroups(); + std::map sortGroups; + for (const auto& pair : sfxGroups) + sortGroups[pair.first] = &pair.second; + for (const auto& pair : sortGroups) + { + NSMutableAttributedString* name = [[NSMutableAttributedString alloc] initWithString:m_url.lastPathComponent attributes:@{NSForegroundColorAttributeName: [NSColor grayColor]}]; + [name appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %s (%d)", it->first.c_str(), pair.first]]]; + vecOut.push_back([[AudioGroupSFXToken alloc] initWithName:name loadId:0 sfx:nil]); + std::map sortSfx; + for (const auto& pair : pair.second->m_sfxEntries) + sortSfx[pair.first] = pair.second; + for (const auto& sfx : sortSfx) + { + name = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d", sfx.first]]; + vecOut.push_back([[AudioGroupSFXToken alloc] initWithName:name loadId:sfx.first sfx:sfx.second]); + } + } + } +} + +void AudioGroupCollection::addSamples(std::vector& vecOut) +{ + for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it) + { + if (!it->second->m_metaData->active) + continue; + const auto& samps = it->second->m_loadedGroup->getSdir().sampleEntries(); + std::map*> sortSamps; + for (const auto& pair : samps) + sortSamps[pair.first] = &pair.second; + + NSMutableAttributedString* name = [[NSMutableAttributedString alloc] initWithString:m_url.lastPathComponent attributes:@{NSForegroundColorAttributeName: [NSColor grayColor]}]; + [name appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %s", it->first.c_str()]]]; + vecOut.push_back([[AudioGroupSampleToken alloc] initWithName:name samp:nil]); + + for (const auto& pair : sortSamps) + { + name = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d", pair.first]]; + vecOut.push_back([[AudioGroupSampleToken alloc] initWithName:name samp:pair.second]); + } + } +} + AudioGroupDataCollection::AudioGroupDataCollection(const std::string& 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), @@ -137,7 +209,9 @@ AudioGroupDataCollection::AudioGroupDataCollection(const std::string& name, NSUR bool AudioGroupDataCollection::_attemptLoad(AudioGroupFilePresenter* presenter) { - if (m_metaData && m_loadedData && m_loadedProj) + amuse::Engine& engine = *((AppDelegate*)NSApp.delegate)->amuseEngine; + + if (m_metaData && m_loadedData && m_loadedGroup) return true; if (!loadProj(presenter)) return false; @@ -176,12 +250,8 @@ bool AudioGroupDataCollection::_attemptLoad(AudioGroupFilePresenter* presenter) break; } - if (m_metaData && m_loadedData) - { - m_loadedProj.emplace(amuse::AudioGroupProject::CreateAudioGroupProject(*m_loadedData)); - return true; - } - return false; + m_loadedGroup = engine.addAudioGroup(*m_loadedData); + return m_loadedData && m_loadedGroup; } void AudioGroupDataCollection::enable(AudioGroupFilePresenter* presenter) @@ -437,22 +507,22 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) return [NSNumber numberWithInt:data.m_metaData->active ? NSOnState : NSOffState]; else if ([tableColumn.identifier isEqualToString:@"DetailsColumn"]) { - if (!data.m_loadedProj) + if (!data.m_loadedGroup) return @""; - if (data.m_loadedProj->songGroups().size() && data.m_loadedProj->sfxGroups().size()) + if (data.m_loadedGroup->getProj().songGroups().size() && data.m_loadedGroup->getProj().sfxGroups().size()) return [NSString stringWithFormat:@"%zu Song Group%s, %zu SFX Group%s", - data.m_loadedProj->songGroups().size(), - data.m_loadedProj->songGroups().size() > 1 ? "s" : "", - data.m_loadedProj->sfxGroups().size(), - data.m_loadedProj->sfxGroups().size() > 1 ? "s" : ""]; - else if (data.m_loadedProj->songGroups().size()) + data.m_loadedGroup->getProj().songGroups().size(), + data.m_loadedGroup->getProj().songGroups().size() > 1 ? "s" : "", + data.m_loadedGroup->getProj().sfxGroups().size(), + data.m_loadedGroup->getProj().sfxGroups().size() > 1 ? "s" : ""]; + else if (data.m_loadedGroup->getProj().songGroups().size()) return [NSString stringWithFormat:@"%zu Song Group%s", - data.m_loadedProj->songGroups().size(), - data.m_loadedProj->songGroups().size() > 1 ? "s" : ""]; - else if (data.m_loadedProj->sfxGroups().size()) + data.m_loadedGroup->getProj().songGroups().size(), + data.m_loadedGroup->getProj().songGroups().size() > 1 ? "s" : ""]; + else if (data.m_loadedGroup->getProj().sfxGroups().size()) return [NSString stringWithFormat:@"%zu SFX Group%s", - data.m_loadedProj->sfxGroups().size(), - data.m_loadedProj->sfxGroups().size() > 1 ? "s" : ""]; + data.m_loadedGroup->getProj().sfxGroups().size(), + data.m_loadedGroup->getProj().sfxGroups().size() > 1 ? "s" : ""]; else return @""; } @@ -495,7 +565,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) } if (dirty) - [outlineView reloadItem:nil reloadChildren:YES]; + [self resetIterators]; } - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(nonnull id)cell forTableColumn:(nullable NSTableColumn *)tableColumn item:(nonnull id)item @@ -649,12 +719,19 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) if (searchStr) search = searchStr.UTF8String; + m_sfxTableData.clear(); + m_sampleTableData.clear(); m_filterAudioGroupCollections.clear(); m_filterAudioGroupCollections.reserve(m_audioGroupCollections.size()); for (auto it = m_audioGroupCollections.begin() ; it != m_audioGroupCollections.end() ; ++it) + { + it->second->addSFX(m_sfxTableData); + it->second->addSamples(m_sampleTableData); if (it->second->doSearch(search) || !searchStr || StrToLower(it->first).find(search) != std::string::npos) m_filterAudioGroupCollections.push_back(it); + } [lastOutlineView reloadItem:nil reloadChildren:YES]; + [(AppDelegate*)NSApp.delegate reloadTables]; } - (void)setSearchFilter:(NSString*)str diff --git a/include/amuse/AudioGroup.hpp b/include/amuse/AudioGroup.hpp index ac79f78..5735192 100644 --- a/include/amuse/AudioGroup.hpp +++ b/include/amuse/AudioGroup.hpp @@ -31,6 +31,7 @@ public: 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;} }; diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index 7da4978..2e4679f 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -51,6 +51,8 @@ public: AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, bool absOffs, N64DataTag); AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag); + + const std::unordered_map>& sampleEntries() const {return m_entries;} }; } diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index 1fbfe29..74b0dfc 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -1293,7 +1293,7 @@ static std::vector> LoadRS2(FILE if (head.projLen && head.poolLen && head.sdirLen && head.sampLen) { char name[128]; - snprintf(name, 128, "GroupFile%u", j); + snprintf(name, 128, "GroupFile%02u", j); ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), head.projLen, pool.release(), head.poolLen, sdir.release(), head.sdirLen, samp.release(), head.sampLen, GCNDataTag{}}); } @@ -1353,7 +1353,7 @@ static std::vector> LoadRS2S sonHead.swapBig(); char name[128]; - snprintf(name, 128, "GroupFile%u-%u", j, s); + snprintf(name, 128, "GroupFile%02u-%u", j, s); std::unique_ptr song(new uint8_t[sonHead.length]); memcpy(song.get(), audData.get() + sonHead.offset, sonHead.length); ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), sonHead.length, @@ -1466,7 +1466,7 @@ static std::vector> LoadRS3(FILE if (head.projLen && head.poolLen && head.sdirLen && head.sampLen) { char name[128]; - snprintf(name, 128, "GroupFile%u", j); + snprintf(name, 128, "GroupFile%02u", j); ret.emplace_back(name, IntrusiveAudioGroupData{proj.release(), head.projLen, pool.release(), head.poolLen, sdir.release(), head.sdirLen, samp.release(), head.sampLen, GCNDataTag{}}); } From f260019b8905cdbb99a1e26195091fcef4ec6a7e Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Mon, 6 Jun 2016 17:42:51 -1000 Subject: [PATCH 6/7] More AudioUnit integration --- AudioUnit/AmuseContainerMainMenu.xib | 4 +- AudioUnit/AmuseContainingApp.hpp | 2 +- AudioUnit/AmuseContainingApp.mm | 35 +---- AudioUnit/AudioGroupFilePresenter.hpp | 32 ++++- AudioUnit/AudioGroupFilePresenter.mm | 139 +++++++++++++++---- AudioUnit/AudioUnitBackend.hpp | 22 +-- AudioUnit/AudioUnitBackend.mm | 163 +++++++++++++++-------- AudioUnit/AudioUnitViewController.hpp | 17 ++- AudioUnit/AudioUnitViewController.mm | 184 +++++++++++++++++++------- AudioUnit/AudioUnitViewController.xib | 21 +++ AudioUnit/CMakeLists.txt | 14 +- include/amuse/BooBackend.hpp | 3 +- include/amuse/Sequencer.hpp | 5 +- lib/BooBackend.cpp | 10 +- lib/Sequencer.cpp | 51 +++++-- 15 files changed, 503 insertions(+), 199 deletions(-) create mode 100644 AudioUnit/AudioUnitViewController.xib diff --git a/AudioUnit/AmuseContainerMainMenu.xib b/AudioUnit/AmuseContainerMainMenu.xib index 14bd957..be4a6ce 100644 --- a/AudioUnit/AmuseContainerMainMenu.xib +++ b/AudioUnit/AmuseContainerMainMenu.xib @@ -297,7 +297,7 @@ CA - + @@ -418,7 +418,7 @@ CA - + diff --git a/AudioUnit/AmuseContainingApp.hpp b/AudioUnit/AmuseContainingApp.hpp index f3eb80a..5441ba5 100644 --- a/AudioUnit/AmuseContainingApp.hpp +++ b/AudioUnit/AmuseContainingApp.hpp @@ -28,7 +28,7 @@ - (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present; @end -@interface AppDelegate : NSObject +@interface AppDelegate : NSObject { IBOutlet NSWindow* mainWindow; IBOutlet NSOutlineView* dataOutline; diff --git a/AudioUnit/AmuseContainingApp.mm b/AudioUnit/AmuseContainingApp.mm index c281347..bbd85b7 100644 --- a/AudioUnit/AmuseContainingApp.mm +++ b/AudioUnit/AmuseContainingApp.mm @@ -30,32 +30,6 @@ } @end -@interface MainView : NSView -{ - AudioUnitViewController* amuseVC; -} -@end - -@implementation MainView - -- (id)initWithFrame:(NSRect)frameRect -{ - self = [super initWithFrame:frameRect]; - if (!self) - return nil; - amuseVC = [[AudioUnitViewController alloc] initWithNibName:nil bundle:nil]; - [self addSubview:amuseVC.view]; - return self; -} - -- (BOOL)translatesAutoresizingMaskIntoConstraints -{ - return NO; -} - -@end - - @implementation DataOutlineView - (id)initWithCoder:(NSCoder *)coder { @@ -200,7 +174,7 @@ if (presenter->m_sfxTableData.size() <= row) return; AudioGroupSFXToken* sfxToken = presenter->m_sfxTableData[row]; - AppDelegate* delegate = NSApp.delegate; + AppDelegate* delegate = (AppDelegate*)NSApp.delegate; [delegate startSFX:sfxToken->m_loadId]; } @@ -223,7 +197,7 @@ [mainWindow.toolbar setSelectedItemIdentifier:@"DataTab"]; - groupFilePresenter = [AudioGroupFilePresenter new]; + groupFilePresenter = [[AudioGroupFilePresenter alloc] initWithAudioGroupClient:self]; dataOutline.dataSource = groupFilePresenter; dataOutline.delegate = groupFilePresenter; @@ -331,6 +305,11 @@ return [self importURL:url]; } +- (amuse::Engine&)getAmuseEngine +{ + return *amuseEngine; +} + @end int main(int argc, const char * argv[]) diff --git a/AudioUnit/AudioGroupFilePresenter.hpp b/AudioUnit/AudioGroupFilePresenter.hpp index a34537a..4a99c1a 100644 --- a/AudioUnit/AudioGroupFilePresenter.hpp +++ b/AudioUnit/AudioGroupFilePresenter.hpp @@ -14,6 +14,11 @@ @class AudioGroupCollectionToken; @class AudioGroupSFXToken; @class AudioGroupSampleToken; +@class AudioGroupToken; + +@protocol AudioGroupClient +- (amuse::Engine&)getAmuseEngine; +@end struct AudioGroupDataCollection { @@ -45,6 +50,7 @@ struct AudioGroupDataCollection std::experimental::optional m_loadedData; const amuse::AudioGroup* m_loadedGroup; + std::vector m_groupTokens; void moveURL(NSURL* oldUrl, NSURL* newUrl); @@ -57,7 +63,8 @@ struct AudioGroupDataCollection AudioGroupDataCollection(const std::string& name, NSURL* proj, NSURL* pool, NSURL* sdir, NSURL* samp, NSURL* meta); bool isDataComplete() const {return m_projData.size() && m_poolData.size() && m_sdirData.size() && m_sampData.size() && m_metaData;} bool _attemptLoad(AudioGroupFilePresenter* presenter); - + bool _indexData(AudioGroupFilePresenter* presenter); + void enable(AudioGroupFilePresenter* presenter); void disable(AudioGroupFilePresenter* presenter); }; @@ -71,9 +78,11 @@ struct AudioGroupCollection std::vector>::iterator> m_filterGroups; AudioGroupCollection(NSURL* url); - void addCollection(std::vector>&& collection); + void addCollection(AudioGroupFilePresenter* presenter, + std::vector>&& collection); void update(AudioGroupFilePresenter* presenter); bool doSearch(const std::string& str); + bool doActiveFilter(); void addSFX(std::vector& vecOut); void addSamples(std::vector& vecOut); }; @@ -114,19 +123,32 @@ struct AudioGroupCollection amuse::AudioGroupSampleDirectory::ADPCMParms>*)sample; @end +@interface AudioGroupToken : NSObject +{ +@public + NSString* m_name; + const amuse::SongGroupIndex* m_song; + const amuse::SFXGroupIndex* m_sfx; +} +- (id)initWithName:(NSString*)name songGroup:(const amuse::SongGroupIndex*)group; +- (id)initWithName:(NSString*)name sfxGroup:(const amuse::SFXGroupIndex*)group; +@end + @interface AudioGroupFilePresenter : NSObject { +@public + id m_audioGroupClient; NSURL* m_groupURL; NSOperationQueue* m_dataQueue; std::map> m_audioGroupCollections; std::vector>::iterator> m_filterAudioGroupCollections; - NSOutlineView* lastOutlineView; - NSString* searchStr; + NSOutlineView* m_lastOutlineView; + NSString* m_searchStr; -@public std::vector m_sfxTableData; std::vector m_sampleTableData; } +- (id)initWithAudioGroupClient:(id)client; - (BOOL)addCollectionName:(std::string&&)name items:(std::vector>&&)collection; - (void)update; - (void)resetIterators; diff --git a/AudioUnit/AudioGroupFilePresenter.mm b/AudioUnit/AudioGroupFilePresenter.mm index b42101d..cd9b16f 100644 --- a/AudioUnit/AudioGroupFilePresenter.mm +++ b/AudioUnit/AudioGroupFilePresenter.mm @@ -2,6 +2,8 @@ #include #include #import "AmuseContainingApp.hpp" +#import "AudioUnitBackend.hpp" +#import "AudioUnitViewController.hpp" static std::string StrToLower(const std::string& str) { @@ -50,6 +52,23 @@ static std::string StrToLower(const std::string& str) } @end +@implementation AudioGroupToken +- (id)initWithName:(NSString*)name songGroup:(const amuse::SongGroupIndex*)group +{ + self = [super init]; + m_name = name; + m_song = group; + return self; +} +- (id)initWithName:(NSString*)name sfxGroup:(const amuse::SFXGroupIndex*)group +{ + self = [super init]; + m_name = name; + m_sfx = group; + return self; +} +@end + @implementation AudioGroupFilePresenter - (NSURL*)presentedItemURL @@ -65,7 +84,8 @@ static std::string StrToLower(const std::string& str) AudioGroupCollection::AudioGroupCollection(NSURL* url) : m_url(url), m_token([[AudioGroupCollectionToken alloc] initWithCollection:this]) {} -void AudioGroupCollection::addCollection(std::vector>&& collection) +void AudioGroupCollection::addCollection(AudioGroupFilePresenter* presenter, + std::vector>&& collection) { for (std::pair& pair : collection) { @@ -98,6 +118,7 @@ void AudioGroupCollection::addCollection(std::vectorsecond->m_metaData->active) + { + m_filterGroups.push_back(it); + ret = true; + } + return ret; +} + void AudioGroupCollection::addSFX(std::vector& vecOut) { for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it) @@ -209,8 +244,6 @@ AudioGroupDataCollection::AudioGroupDataCollection(const std::string& name, NSUR bool AudioGroupDataCollection::_attemptLoad(AudioGroupFilePresenter* presenter) { - amuse::Engine& engine = *((AppDelegate*)NSApp.delegate)->amuseEngine; - if (m_metaData && m_loadedData && m_loadedGroup) return true; if (!loadProj(presenter)) @@ -224,6 +257,13 @@ bool AudioGroupDataCollection::_attemptLoad(AudioGroupFilePresenter* presenter) if (!loadMeta(presenter)) return false; + return _indexData(presenter); +} + +bool AudioGroupDataCollection::_indexData(AudioGroupFilePresenter* presenter) +{ + amuse::Engine& engine = [presenter->m_audioGroupClient getAmuseEngine]; + switch (m_metaData->fmt) { case amuse::DataFormat::GCN: @@ -251,6 +291,36 @@ bool AudioGroupDataCollection::_attemptLoad(AudioGroupFilePresenter* presenter) } m_loadedGroup = engine.addAudioGroup(*m_loadedData); + m_groupTokens.clear(); + if (m_loadedGroup) + { + m_groupTokens.reserve(m_loadedGroup->getProj().songGroups().size() + + m_loadedGroup->getProj().sfxGroups().size()); + + { + const auto& songGroups = m_loadedGroup->getProj().songGroups(); + std::map sortGroups; + for (const auto& pair : songGroups) + sortGroups[pair.first] = &pair.second; + for (const auto& pair : sortGroups) + { + NSString* name = [NSString stringWithFormat:@"%d", pair.first]; + m_groupTokens.push_back([[AudioGroupToken alloc] initWithName:name songGroup:pair.second]); + } + } + { + const auto& sfxGroups = m_loadedGroup->getProj().sfxGroups(); + std::map sortGroups; + for (const auto& pair : sfxGroups) + sortGroups[pair.first] = &pair.second; + for (const auto& pair : sortGroups) + { + NSString* name = [NSString stringWithFormat:@"%d", pair.first]; + m_groupTokens.push_back([[AudioGroupToken alloc] initWithName:name sfxGroup:pair.second]); + } + } + } + return m_loadedData && m_loadedGroup; } @@ -447,7 +517,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) - (NSInteger)outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(nullable id)item { - lastOutlineView = outlineView; + m_lastOutlineView = outlineView; if (!item) return m_filterAudioGroupCollections.size(); @@ -496,7 +566,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) return [NSNumber numberWithInt:NSMixedState]; } else if ([tableColumn.identifier isEqualToString:@"DetailsColumn"]) - return [NSString stringWithFormat:@"%zu Audio Group%s", + return [NSString stringWithFormat:@"%zu Group File%s", collection.m_groups.size(), collection.m_groups.size() > 1 ? "s" : ""]; } @@ -620,7 +690,7 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) NSURL* dir = [m_groupURL URLByAppendingPathComponent:@(name.c_str())]; __block AudioGroupCollection& insert = *m_audioGroupCollections.emplace(name, std::make_unique(dir)).first->second; - insert.addCollection(std::move(collection)); + insert.addCollection(self, std::move(collection)); [coord coordinateWritingItemAtURL:m_groupURL options:0 error:nil byAccessor:^(NSURL* newUrl) @@ -715,34 +785,52 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) - (void)resetIterators { - std::string search; - if (searchStr) - search = searchStr.UTF8String; - - m_sfxTableData.clear(); - m_sampleTableData.clear(); - m_filterAudioGroupCollections.clear(); - m_filterAudioGroupCollections.reserve(m_audioGroupCollections.size()); - for (auto it = m_audioGroupCollections.begin() ; it != m_audioGroupCollections.end() ; ++it) + if ([(NSObject*)m_audioGroupClient isKindOfClass:[AppDelegate class]]) { - it->second->addSFX(m_sfxTableData); - it->second->addSamples(m_sampleTableData); - if (it->second->doSearch(search) || !searchStr || StrToLower(it->first).find(search) != std::string::npos) - m_filterAudioGroupCollections.push_back(it); + std::string search; + if (m_searchStr) + search = m_searchStr.UTF8String; + + m_sfxTableData.clear(); + m_sampleTableData.clear(); + m_filterAudioGroupCollections.clear(); + m_filterAudioGroupCollections.reserve(m_audioGroupCollections.size()); + for (auto it = m_audioGroupCollections.begin() ; it != m_audioGroupCollections.end() ; ++it) + { + it->second->addSFX(m_sfxTableData); + it->second->addSamples(m_sampleTableData); + if (it->second->doSearch(search) || !m_searchStr || StrToLower(it->first).find(search) != std::string::npos) + m_filterAudioGroupCollections.push_back(it); + } + [m_lastOutlineView reloadItem:nil reloadChildren:YES]; + [(AppDelegate*)m_audioGroupClient reloadTables]; + } + else + { + m_sfxTableData.clear(); + m_sampleTableData.clear(); + m_filterAudioGroupCollections.clear(); + m_filterAudioGroupCollections.reserve(m_audioGroupCollections.size()); + for (auto it = m_audioGroupCollections.begin() ; it != m_audioGroupCollections.end() ; ++it) + { + it->second->addSFX(m_sfxTableData); + it->second->addSamples(m_sampleTableData); + if (it->second->doActiveFilter()) + m_filterAudioGroupCollections.push_back(it); + } + [((AmuseAudioUnit*)m_audioGroupClient)->m_viewController->m_groupBrowser loadColumnZero]; } - [lastOutlineView reloadItem:nil reloadChildren:YES]; - [(AppDelegate*)NSApp.delegate reloadTables]; } - (void)setSearchFilter:(NSString*)str { - searchStr = [str lowercaseString]; + m_searchStr = [str lowercaseString]; [self resetIterators]; } - (void)removeSelectedItem { - id item = [lastOutlineView itemAtRow:lastOutlineView.selectedRow]; + id item = [m_lastOutlineView itemAtRow:m_lastOutlineView.selectedRow]; if ([item isKindOfClass:[AudioGroupCollectionToken class]]) { AudioGroupCollection& collection = *((AudioGroupCollectionToken*)item)->m_collection; @@ -752,12 +840,13 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) [self resetIterators]; [[NSFileManager defaultManager] removeItemAtURL:collectionURL error:nil]; if (m_audioGroupCollections.empty()) - [(AppDelegate*)NSApp.delegate outlineView:(DataOutlineView*)lastOutlineView selectionChanged:nil]; + [(AppDelegate*)NSApp.delegate outlineView:(DataOutlineView*)m_lastOutlineView selectionChanged:nil]; } } -- (id)init +- (id)initWithAudioGroupClient:(id)client { + m_audioGroupClient = client; m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.io.github.axiodl.Amuse.AudioGroups"]; if (!m_groupURL) return nil; diff --git a/AudioUnit/AudioUnitBackend.hpp b/AudioUnit/AudioUnitBackend.hpp index ac90527..753fbba 100644 --- a/AudioUnit/AudioUnitBackend.hpp +++ b/AudioUnit/AudioUnitBackend.hpp @@ -16,23 +16,16 @@ #include "amuse/IBackendVoice.hpp" #include "amuse/IBackendSubmix.hpp" #include "amuse/IBackendVoiceAllocator.hpp" +#import "AudioGroupFilePresenter.hpp" + +@class AudioUnitViewController; namespace amuse { -/** Backend MIDI event reader for controlling sequencer with external hardware / software */ -class AudioUnitBackendMIDIReader : public BooBackendMIDIReader -{ - friend class AudioUnitBackendVoiceAllocator; -public: - AudioUnitBackendMIDIReader(Engine& engine) - : BooBackendMIDIReader(engine, "AudioUnit MIDI") {} -}; - /** Backend voice allocator implementation for AudioUnit mixer */ class AudioUnitBackendVoiceAllocator : public BooBackendVoiceAllocator { - friend class AudioUnitBackendMIDIReader; public: AudioUnitBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : BooBackendVoiceAllocator(booEngine) {} @@ -42,13 +35,20 @@ void RegisterAudioUnit(); } -@interface AmuseAudioUnit : AUAudioUnit +@interface AmuseAudioUnit : AUAudioUnit { +@public + AudioUnitViewController* m_viewController; std::unique_ptr m_booBackend; std::experimental::optional m_voxAlloc; std::experimental::optional m_engine; + AudioGroupFilePresenter* m_filePresenter; + AUAudioUnitBus* m_outBus; AUAudioUnitBusArray* m_outs; } +- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription + error:(NSError * __nullable * __nonnull)outError + viewController:(AudioUnitViewController* __nonnull)vc; @end #endif diff --git a/AudioUnit/AudioUnitBackend.mm b/AudioUnit/AudioUnitBackend.mm index 0c5f746..5143fc9 100644 --- a/AudioUnit/AudioUnitBackend.mm +++ b/AudioUnit/AudioUnitBackend.mm @@ -12,13 +12,15 @@ #include "logvisor/logvisor.hpp" #include "audiodev/AudioVoiceEngine.hpp" +#import "AudioUnitViewController.hpp" static logvisor::Module Log("amuse::AudioUnitBackend"); struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { + std::vector m_interleavedBuf; std::vector> m_renderBufs; - size_t m_frameBytes; + size_t m_renderFrames = 0; AudioBufferList* m_outputData = nullptr; boo::AudioChannelSet _getAvailableSet() @@ -31,12 +33,24 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine return {}; } - boo::ReceiveFunctor m_midiReceiver = nullptr; + boo::ReceiveFunctor* m_midiReceiver = nullptr; + struct MIDIIn : public boo::IMIDIIn + { + MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) + : IMIDIIn(virt, std::move(receiver)) {} + + std::string description() const + { + return "AudioUnit MIDI"; + } + }; + std::unique_ptr newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) { - m_midiReceiver = std::move(receiver); - return {}; + std::unique_ptr ret = std::make_unique(true, std::move(receiver)); + m_midiReceiver = &ret->m_receiver; + return ret; } std::unique_ptr newVirtualMIDIOut() @@ -63,106 +77,140 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { return {}; } + + bool useMIDILock() const {return false;} AudioUnitVoiceEngine() { - m_mixInfo.m_channels = _getAvailableSet(); - unsigned chCount = ChannelCount(m_mixInfo.m_channels); - + m_mixInfo.m_periodFrames = 512; m_mixInfo.m_sampleRate = 96000.0; m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I; m_mixInfo.m_bitsPerSample = 32; - m_5msFrames = 96000 * 5 / 1000; - + _buildAudioRenderClient(); + } + + void _buildAudioRenderClient() + { + m_mixInfo.m_channels = _getAvailableSet(); + unsigned chCount = ChannelCount(m_mixInfo.m_channels); + + m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000; + boo::ChannelMap& chMapOut = m_mixInfo.m_channelMap; chMapOut.m_channelCount = 2; chMapOut.m_channels[0] = boo::AudioChannel::FrontLeft; chMapOut.m_channels[1] = boo::AudioChannel::FrontRight; - + while (chMapOut.m_channelCount < chCount) chMapOut.m_channels[chMapOut.m_channelCount++] = boo::AudioChannel::Unknown; - - m_mixInfo.m_periodFrames = 2400; - - m_frameBytes = m_mixInfo.m_periodFrames * m_mixInfo.m_channelMap.m_channelCount * 4; + } + + void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) + { + m_mixInfo.m_periodFrames = periodFrames; + m_mixInfo.m_sampleRate = sampleRate; + _buildAudioRenderClient(); + + for (boo::AudioVoice* vox : m_activeVoices) + vox->_resetSampleRate(vox->m_sampleRateIn); + for (boo::AudioSubmix* smx : m_activeSubmixes) + smx->_resetOutputSampleRate(); } void pumpAndMixVoices() { - if (m_renderBufs.size() < m_outputData->mNumberBuffers) - m_renderBufs.resize(m_outputData->mNumberBuffers); + _pumpAndMixVoices(m_renderFrames, m_interleavedBuf.data()); - for (int i=0 ; imNumberBuffers ; ++i) + for (size_t i=0 ; i& buf = m_renderBufs[i]; AudioBuffer& auBuf = m_outputData->mBuffers[i]; if (!auBuf.mData) { - buf.reset(new float[auBuf.mDataByteSize]); + buf.reset(new float[auBuf.mDataByteSize / 4]); auBuf.mData = buf.get(); } - - _pumpAndMixVoices(auBuf.mDataByteSize / 2 / 4, reinterpret_cast(auBuf.mData)); + for (size_t f=0 ; f(auBuf.mData); + bufOut[f] = m_interleavedBuf[f*2+i]; + } } } }; @implementation AmuseAudioUnit +- (id)initWithComponentDescription:(AudioComponentDescription)componentDescription + error:(NSError * _Nullable *)outError + viewController:(AudioUnitViewController*)vc +{ + m_viewController = vc; + vc->m_audioUnit = self; + self = [super initWithComponentDescription:componentDescription error:outError]; + return self; +} + - (id)initWithComponentDescription:(AudioComponentDescription)componentDescription options:(AudioComponentInstantiationOptions)options - error:(NSError * _Nullable *)outError; + error:(NSError * _Nullable *)outError { self = [super initWithComponentDescription:componentDescription options:options error:outError]; if (!self) return nil; - - AUAudioUnitBus* outBus = [[AUAudioUnitBus alloc] initWithFormat: - [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 - sampleRate:96000.0 - channels:2 - interleaved:TRUE] - error:outError]; - if (!outBus) + + AVAudioFormat* format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:96000.0 channels:2]; + m_outBus = [[AUAudioUnitBus alloc] initWithFormat:format error:outError]; + if (!m_outBus) return nil; - - m_outs = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:@[outBus]]; - - self.maximumFramesToRender = 2400; - return self; -} - -- (BOOL)allocateRenderResourcesAndReturnError:(NSError * _Nullable *)outError -{ - if (![super allocateRenderResourcesAndReturnError:outError]) - return FALSE; + //m_outBus.supportedChannelCounts = @[@1,@2]; + m_outBus.maximumChannelCount = 2; + + m_outs = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:@[m_outBus]]; m_booBackend = std::make_unique(); if (!m_booBackend) { *outError = [NSError errorWithDomain:@"amuse" code:-1 - userInfo:@{NSLocalizedDescriptionKey:@"Unable to construct boo mixer"}]; + userInfo:@{NSLocalizedDescriptionKey:@"Unable to construct boo mixer"}]; return FALSE; } - + m_voxAlloc.emplace(*m_booBackend); m_engine.emplace(*m_voxAlloc); + dispatch_sync(dispatch_get_main_queue(), ^ + { + m_filePresenter = [[AudioGroupFilePresenter alloc] initWithAudioGroupClient:self]; + }); + + self.maximumFramesToRender = 512; + return self; +} + +- (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outError +{ + if (![super allocateRenderResourcesAndReturnError:outError]) + return FALSE; + + size_t chanCount = m_outBus.format.channelCount; + size_t renderFrames = self.maximumFramesToRender; + + NSLog(@"Alloc Chans: %zu Frames: %zu SampRate: %f", chanCount, renderFrames, m_outBus.format.sampleRate); + + AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); + voxEngine.m_renderFrames = renderFrames; + voxEngine.m_interleavedBuf.resize(renderFrames * std::max(2ul, chanCount)); + voxEngine.m_renderBufs.resize(chanCount); + voxEngine._rebuildAudioRenderClient(m_outBus.format.sampleRate, renderFrames); + *outError = nil; return TRUE; } - (void)deallocateRenderResources { - m_engine = std::experimental::nullopt; - m_voxAlloc = std::experimental::nullopt; - m_booBackend.reset(); [super deallocateRenderResources]; -} - -- (BOOL)renderResourcesAllocated -{ - if (m_engine) - return TRUE; - return FALSE; + AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); + voxEngine.m_renderBufs.clear(); } - (AUAudioUnitBusArray*)outputBusses @@ -197,18 +245,25 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { if (event->eventType == AURenderEventMIDI) { - voxEngine.m_midiReceiver(std::vector(std::cbegin(event->data), - std::cbegin(event->data) + event->length)); + NSLog(@"MIDI %d %d", event->length, event->data[0]); + (*voxEngine.m_midiReceiver)(std::vector(std::cbegin(event->data), + std::cbegin(event->data) + event->length)); } } } /* Output buffers */ + voxEngine.m_renderFrames = frameCount; voxEngine.m_outputData = outputData; amuseEngine.pumpEngine(); return noErr; }; } + +- (amuse::Engine&)getAmuseEngine +{ + return *m_engine; +} @end namespace amuse diff --git a/AudioUnit/AudioUnitViewController.hpp b/AudioUnit/AudioUnitViewController.hpp index 77da5ce..a3b4831 100644 --- a/AudioUnit/AudioUnitViewController.hpp +++ b/AudioUnit/AudioUnitViewController.hpp @@ -2,9 +2,24 @@ #define __AMUSE_AUDIOUNIT_VIEWCONTROLLER_HPP__ #import +#import "AudioGroupFilePresenter.hpp" + +@class AmuseAudioUnit; + +@interface GroupBrowserDelegate : NSObject +{ + AmuseAudioUnit* m_audioUnit; +} +- (id)initWithAudioUnit:(AmuseAudioUnit*)au; +@end @interface AudioUnitViewController : AUViewController - +{ +@public + AmuseAudioUnit* m_audioUnit; + IBOutlet NSBrowser* m_groupBrowser; + GroupBrowserDelegate* m_groupBrowserDelegate; +} @end #endif // __AMUSE_AUDIOUNIT_VIEWCONTROLLER_HPP__ diff --git a/AudioUnit/AudioUnitViewController.mm b/AudioUnit/AudioUnitViewController.mm index 6cf6ef9..6c8da5c 100644 --- a/AudioUnit/AudioUnitViewController.mm +++ b/AudioUnit/AudioUnitViewController.mm @@ -5,73 +5,155 @@ #error ARC Required #endif -@interface AudioUnitView : NSView +@implementation GroupBrowserDelegate + +- (BOOL)browser:(NSBrowser *)sender isColumnValid:(NSInteger)column { - NSButton* m_fileButton; + if (column == 0) + return YES; + else if (column == 1) + { + AudioGroupCollectionToken* collection = [sender selectedCellInColumn:0]; + if (collection) + return YES; + } + else if (column == 2) + { + AudioGroupDataToken* groupFile = [sender selectedCellInColumn:1]; + if (groupFile) + return YES; + } + return NO; } -- (void)clickFileButton; -@end -@implementation AudioUnitView - -- (id)init +- (NSInteger)browser:(NSBrowser *)sender numberOfRowsInColumn:(NSInteger)column { - self = [super initWithFrame:NSMakeRect(0, 0, 200, 300)]; - m_fileButton = [[NSButton alloc] initWithFrame:NSMakeRect(100, 100, 30, 10)]; - m_fileButton.target = self; - m_fileButton.action = @selector(clickFileButton); - [self addSubview:m_fileButton]; + if (column == 0) + return m_audioUnit->m_filePresenter->m_filterAudioGroupCollections.size(); + else if (column == 1) + { + AudioGroupCollectionToken* collection = [sender selectedCellInColumn:0]; + if (!collection) + return 0; + return collection->m_collection->m_filterGroups.size(); + } + else if (column == 2) + { + AudioGroupDataToken* groupFile = [sender selectedCellInColumn:1]; + if (!groupFile) + return 0; + const amuse::AudioGroup* audioGroupFile = groupFile->m_collection->m_loadedGroup; + return audioGroupFile->getProj().songGroups().size() + audioGroupFile->getProj().sfxGroups().size(); + } + return 0; +} + +- (NSInteger)browser:(NSBrowser *)browser numberOfChildrenOfItem:(id)item +{ + if (!item) + return m_audioUnit->m_filePresenter->m_filterAudioGroupCollections.size(); + else if ([item isKindOfClass:[AudioGroupCollectionToken class]]) + { + AudioGroupCollectionToken* collection = item; + return collection->m_collection->m_filterGroups.size(); + } + else if ([item isKindOfClass:[AudioGroupDataToken class]]) + { + AudioGroupDataToken* groupFile = item; + const amuse::AudioGroup* audioGroupFile = groupFile->m_collection->m_loadedGroup; + return audioGroupFile->getProj().songGroups().size() + audioGroupFile->getProj().sfxGroups().size(); + } + else + return 0; +} + +- (NSString *)browser:(NSBrowser *)sender titleOfColumn:(NSInteger)column +{ + if (column == 0) + return @"Collection"; + else if (column == 1) + return @"File"; + else if (column == 2) + return @"Group"; + return nil; +} + +- (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item +{ + if (!item) + return m_audioUnit->m_filePresenter->m_filterAudioGroupCollections[index]->second->m_token; + else if ([item isKindOfClass:[AudioGroupCollectionToken class]]) + { + AudioGroupCollectionToken* collection = item; + return collection->m_collection->m_filterGroups[index]->second->m_token; + } + else if ([item isKindOfClass:[AudioGroupDataToken class]]) + { + AudioGroupDataToken* groupFile = item; + return groupFile->m_collection->m_groupTokens[index]; + } + else + return 0; +} + +- (BOOL)browser:(NSBrowser *)browser isLeafItem:(id)item +{ + if ([item isKindOfClass:[AudioGroupToken class]]) + return YES; + return NO; +} + +- (BOOL)browser:(NSBrowser *)browser shouldEditItem:(id)item +{ + return NO; +} + +- (id)browser:(NSBrowser *)browser objectValueForItem:(id)item +{ + if ([item isKindOfClass:[AudioGroupCollectionToken class]]) + { + AudioGroupCollectionToken* collection = item; + return collection->m_collection->m_url.lastPathComponent; + } + else if ([item isKindOfClass:[AudioGroupDataToken class]]) + { + AudioGroupDataToken* groupFile = item; + return @(groupFile->m_collection->m_name.c_str()); + } + else if ([item isKindOfClass:[AudioGroupToken class]]) + { + AudioGroupToken* group = item; + return group->m_name; + } + return nil; +} + +- (id)initWithAudioUnit:(AmuseAudioUnit*)au +{ + self = [super init]; + m_audioUnit = au; return self; } -- (void)clickFileButton -{ - NSLog(@"Click"); -} - @end -@interface AudioUnitViewController () - -@end - -@implementation AudioUnitViewController { - AUAudioUnit *audioUnit; -} +@implementation AudioUnitViewController - (void) viewDidLoad { [super viewDidLoad]; - - if (!audioUnit) { - return; - } - + + self.preferredContentSize = NSMakeSize(510, 312); // Get the parameter tree and add observers for any parameters that the UI needs to keep in sync with the AudioUnit } -- (void)loadView -{ - self.view = [AudioUnitView new]; -} - -- (NSSize)preferredContentSize -{ - return NSMakeSize(200, 300); -} - -- (NSSize)preferredMaximumSize -{ - return NSMakeSize(200, 300); -} - -- (NSSize)preferredMinimumSize -{ - return NSMakeSize(200, 300); -} - - (AUAudioUnit*)createAudioUnitWithComponentDescription:(AudioComponentDescription)desc error:(NSError**)error { - audioUnit = [[AmuseAudioUnit alloc] initWithComponentDescription:desc error:error]; - return audioUnit; + m_audioUnit = [[AmuseAudioUnit alloc] initWithComponentDescription:desc error:error viewController:self]; + m_groupBrowserDelegate = [[GroupBrowserDelegate alloc] initWithAudioUnit:m_audioUnit]; + dispatch_sync(dispatch_get_main_queue(), ^ + { + m_groupBrowser.delegate = m_groupBrowserDelegate; + }); + return m_audioUnit; } @end diff --git a/AudioUnit/AudioUnitViewController.xib b/AudioUnit/AudioUnitViewController.xib new file mode 100644 index 0000000..b4207d9 --- /dev/null +++ b/AudioUnit/AudioUnitViewController.xib @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/AudioUnit/CMakeLists.txt b/AudioUnit/CMakeLists.txt index 0f99a86..b7a7aed 100644 --- a/AudioUnit/CMakeLists.txt +++ b/AudioUnit/CMakeLists.txt @@ -21,9 +21,18 @@ if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VE 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) + AudioGroupFilePresenter.hpp AudioGroupFilePresenter.mm + AudioUnitViewController.nib) set(APPLE_BUNDLE_ID "io.github.axiodl.Amuse.AudioUnit") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AmuseExtension.entitlements.in @@ -69,6 +78,9 @@ if (APPLE AND (NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VE 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" + "$/../Resources/AudioUnitViewController.nib" COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$/../embedded.provisionprofile" COMMAND ${CMAKE_COMMAND} -E remove_directory "$/../PlugIns/amuse-au.appex" COMMAND ${CMAKE_COMMAND} -E copy_directory "$/../.." diff --git a/include/amuse/BooBackend.hpp b/include/amuse/BooBackend.hpp index 9af8b7f..0835434 100644 --- a/include/amuse/BooBackend.hpp +++ b/include/amuse/BooBackend.hpp @@ -76,13 +76,14 @@ class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader std::unique_ptr m_midiIn; boo::MIDIDecoder m_decoder; + bool m_useLock; std::list>> m_queue; std::mutex m_midiMutex; void _MIDIReceive(std::vector&& bytes); public: ~BooBackendMIDIReader(); - BooBackendMIDIReader(Engine& engine, const char* name); + BooBackendMIDIReader(Engine& engine, const char* name, bool useLock); std::string description(); void pumpReader(double dt); diff --git a/include/amuse/Sequencer.hpp b/include/amuse/Sequencer.hpp index d512d1d..e5001a1 100644 --- a/include/amuse/Sequencer.hpp +++ b/include/amuse/Sequencer.hpp @@ -28,7 +28,7 @@ class Sequencer : public Entity { friend class Engine; const SongGroupIndex& m_songGroup; /**< Quick access to song group project index */ - const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup */ + const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */ Submix* m_submix = nullptr; /**< Submix this sequencer outputs to (or NULL for the main output mix) */ const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */ @@ -44,7 +44,7 @@ class Sequencer : public Entity { Sequencer& m_parent; uint8_t m_chanId; - const SongGroupIndex::MIDISetup& m_setup; + const SongGroupIndex::MIDISetup* m_setup = nullptr; /* Channel defaults to program 0 if null */ const SongGroupIndex::PageEntry* m_page = nullptr; ~ChannelState(); ChannelState(Sequencer& parent, uint8_t chanId); @@ -79,6 +79,7 @@ class Sequencer : public Entity void _bringOutYourDead(); void _destroy(); + public: ~Sequencer(); Sequencer(Engine& engine, const AudioGroup& group, int groupId, diff --git a/lib/BooBackend.cpp b/lib/BooBackend.cpp index a2fc95a..529161c 100644 --- a/lib/BooBackend.cpp +++ b/lib/BooBackend.cpp @@ -119,8 +119,8 @@ std::string BooBackendMIDIReader::description() BooBackendMIDIReader::~BooBackendMIDIReader() {} -BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name) -: m_engine(engine), m_decoder(*this) +BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name, bool useLock) +: m_engine(engine), m_decoder(*this), m_useLock(useLock) { BooBackendVoiceAllocator& voxAlloc = static_cast(engine.getBackend()); if (!name) @@ -145,7 +145,9 @@ BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name) void BooBackendMIDIReader::_MIDIReceive(std::vector&& bytes) { - std::unique_lock lk(m_midiMutex); + std::unique_lock lk(m_midiMutex, std::defer_lock_t{}); + if (m_useLock) + lk.lock(); m_queue.emplace_back(std::chrono::steady_clock::now(), std::move(bytes)); } @@ -308,7 +310,7 @@ std::vector> BooBackendVoiceAllocator::enume std::unique_ptr BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine, const char* name) { - std::unique_ptr ret = std::make_unique(engine, name); + std::unique_ptr ret = std::make_unique(engine, name, m_booEngine.useMIDILock()); if (!static_cast(*ret).m_midiIn) return {}; return ret; diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp index 6e508d0..175f2b2 100644 --- a/lib/Sequencer.cpp +++ b/lib/Sequencer.cpp @@ -79,25 +79,50 @@ Sequencer::ChannelState::~ChannelState() } Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId) -: m_parent(parent), m_chanId(chanId), m_setup(m_parent.m_midiSetup[chanId]) +: m_parent(parent), m_chanId(chanId) { - if (chanId == 9) + if (m_parent.m_midiSetup) { - 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_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()) + m_page = it->second; + } + else + { + 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_curVol = m_setup->volume / 127.f; + m_curPan = m_setup->panning / 64.f - 1.f; + m_ctrlVals[0x5b] = m_setup->reverb; + m_ctrlVals[0x5d] = m_setup->chorus; } else { - 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; + if (chanId == 9) + { + 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()) + m_page = it->second; + } + + m_curVol = 1.f; + m_curPan = 0.f; + m_ctrlVals[0x5b] = 0; + m_ctrlVals[0x5d] = 0; } - - m_curVol = m_setup.volume / 127.f; - m_curPan = m_setup.panning / 64.f - 1.f; - m_ctrlVals[0x5b] = m_setup.reverb; - m_ctrlVals[0x5d] = m_setup.chorus; } void Sequencer::advance(double dt) From 117d7046dd5667d75d8ffaa9fd031801c2ec8c9f Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 7 Jun 2016 18:33:15 -1000 Subject: [PATCH 7/7] Initial working AudioUnit integration --- AudioUnit/AudioGroupFilePresenter.hpp | 6 ++-- AudioUnit/AudioGroupFilePresenter.mm | 23 +++++++------- AudioUnit/AudioUnitBackend.hpp | 1 + AudioUnit/AudioUnitBackend.mm | 36 ++++++++++++++++++--- AudioUnit/AudioUnitViewController.mm | 11 +++++++ include/amuse/BooBackend.hpp | 5 ++- lib/AudioGroupSampleDirectory.cpp | 4 +-- lib/BooBackend.cpp | 46 ++++++++++++++++++++------- lib/ContainerRegistry.cpp | 34 ++++++++++---------- lib/N64MusyXCodec.c | 6 ++-- lib/Voice.cpp | 4 +-- 11 files changed, 119 insertions(+), 57 deletions(-) diff --git a/AudioUnit/AudioGroupFilePresenter.hpp b/AudioUnit/AudioGroupFilePresenter.hpp index 4a99c1a..e15f64c 100644 --- a/AudioUnit/AudioGroupFilePresenter.hpp +++ b/AudioUnit/AudioGroupFilePresenter.hpp @@ -127,11 +127,12 @@ struct AudioGroupCollection { @public NSString* m_name; + int m_id; const amuse::SongGroupIndex* m_song; const amuse::SFXGroupIndex* m_sfx; } -- (id)initWithName:(NSString*)name songGroup:(const amuse::SongGroupIndex*)group; -- (id)initWithName:(NSString*)name sfxGroup:(const amuse::SFXGroupIndex*)group; +- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group; +- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group; @end @interface AudioGroupFilePresenter : NSObject @@ -139,7 +140,6 @@ struct AudioGroupCollection @public id m_audioGroupClient; NSURL* m_groupURL; - NSOperationQueue* m_dataQueue; std::map> m_audioGroupCollections; std::vector>::iterator> m_filterAudioGroupCollections; NSOutlineView* m_lastOutlineView; diff --git a/AudioUnit/AudioGroupFilePresenter.mm b/AudioUnit/AudioGroupFilePresenter.mm index cd9b16f..4febcc3 100644 --- a/AudioUnit/AudioGroupFilePresenter.mm +++ b/AudioUnit/AudioGroupFilePresenter.mm @@ -53,18 +53,20 @@ static std::string StrToLower(const std::string& str) @end @implementation AudioGroupToken -- (id)initWithName:(NSString*)name songGroup:(const amuse::SongGroupIndex*)group +- (id)initWithName:(NSString*)name id:(int)gid songGroup:(const amuse::SongGroupIndex*)group { self = [super init]; m_name = name; m_song = group; + m_id = gid; return self; } -- (id)initWithName:(NSString*)name sfxGroup:(const amuse::SFXGroupIndex*)group +- (id)initWithName:(NSString*)name id:(int)gid sfxGroup:(const amuse::SFXGroupIndex*)group { self = [super init]; m_name = name; m_sfx = group; + m_id = gid; return self; } @end @@ -78,7 +80,7 @@ static std::string StrToLower(const std::string& str) - (NSOperationQueue*)presentedItemOperationQueue { - return m_dataQueue; + return [NSOperationQueue mainQueue]; } AudioGroupCollection::AudioGroupCollection(NSURL* url) @@ -305,7 +307,8 @@ bool AudioGroupDataCollection::_indexData(AudioGroupFilePresenter* presenter) for (const auto& pair : sortGroups) { NSString* name = [NSString stringWithFormat:@"%d", pair.first]; - m_groupTokens.push_back([[AudioGroupToken alloc] initWithName:name songGroup:pair.second]); + m_groupTokens.push_back([[AudioGroupToken alloc] initWithName:name id:pair.first + songGroup:pair.second]); } } { @@ -316,7 +319,8 @@ bool AudioGroupDataCollection::_indexData(AudioGroupFilePresenter* presenter) for (const auto& pair : sortGroups) { NSString* name = [NSString stringWithFormat:@"%d", pair.first]; - m_groupTokens.push_back([[AudioGroupToken alloc] initWithName:name sfxGroup:pair.second]); + m_groupTokens.push_back([[AudioGroupToken alloc] initWithName:name id:pair.first + sfxGroup:pair.second]); } } } @@ -495,12 +499,10 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) return ret.operator bool(); } -- (void)presentedSubitemDidAppearAtURL:(NSURL*)url +- (void)presentedSubitemDidChangeAtURL:(NSURL *)url { - if ([url.lastPathComponent isEqualToString:@"proj"] || - [url.lastPathComponent isEqualToString:@"pool"] || - [url.lastPathComponent isEqualToString:@"sdir"] || - [url.lastPathComponent isEqualToString:@"samp"]) + size_t relComps = url.pathComponents.count - m_groupURL.pathComponents.count; + if (relComps <= 1) [self update]; } @@ -850,7 +852,6 @@ bool AudioGroupDataCollection::loadMeta(AudioGroupFilePresenter* presenter) m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.io.github.axiodl.Amuse.AudioGroups"]; if (!m_groupURL) return nil; - m_dataQueue = [NSOperationQueue new]; [NSFileCoordinator addFilePresenter:self]; [self update]; return self; diff --git a/AudioUnit/AudioUnitBackend.hpp b/AudioUnit/AudioUnitBackend.hpp index 753fbba..7dda518 100644 --- a/AudioUnit/AudioUnitBackend.hpp +++ b/AudioUnit/AudioUnitBackend.hpp @@ -49,6 +49,7 @@ void RegisterAudioUnit(); - (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription error:(NSError * __nullable * __nonnull)outError viewController:(AudioUnitViewController* __nonnull)vc; +- (void)requestAudioGroup:(AudioGroupToken*)group; @end #endif diff --git a/AudioUnit/AudioUnitBackend.mm b/AudioUnit/AudioUnitBackend.mm index 5143fc9..c52931f 100644 --- a/AudioUnit/AudioUnitBackend.mm +++ b/AudioUnit/AudioUnitBackend.mm @@ -18,6 +18,8 @@ static logvisor::Module Log("amuse::AudioUnitBackend"); struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { + AudioGroupToken* m_reqGroup = nullptr; + AudioGroupToken* m_curGroup = nullptr; std::vector m_interleavedBuf; std::vector> m_renderBufs; size_t m_renderFrames = 0; @@ -137,6 +139,8 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine } } } + + double getCurrentSampleRate() const {return m_mixInfo.m_sampleRate;} }; @implementation AmuseAudioUnit @@ -165,7 +169,9 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine //m_outBus.supportedChannelCounts = @[@1,@2]; m_outBus.maximumChannelCount = 2; - m_outs = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:@[m_outBus]]; + m_outs = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self + busType:AUAudioUnitBusTypeOutput + busses:@[m_outBus]]; m_booBackend = std::make_unique(); if (!m_booBackend) @@ -177,8 +183,8 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine m_voxAlloc.emplace(*m_booBackend); m_engine.emplace(*m_voxAlloc); - dispatch_sync(dispatch_get_main_queue(), ^ - { + dispatch_sync(dispatch_get_main_queue(), + ^{ m_filePresenter = [[AudioGroupFilePresenter alloc] initWithAudioGroupClient:self]; }); @@ -186,6 +192,12 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine return self; } +- (void)requestAudioGroup:(AudioGroupToken*)group +{ + AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); + voxEngine.m_reqGroup = group; +} + - (BOOL)allocateRenderResourcesAndReturnError:(NSError **)outError { if (![super allocateRenderResourcesAndReturnError:outError]) @@ -232,11 +244,25 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { __block AudioUnitVoiceEngine& voxEngine = static_cast(*m_booBackend); __block amuse::Engine& amuseEngine = *m_engine; + __block std::shared_ptr curSeq; return ^AUAudioUnitStatus(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* timestamp, AUAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList* outputData, const AURenderEvent* realtimeEventListHead, AURenderPullInputBlock pullInputBlock) { + /* Handle group load request */ + AudioGroupToken* reqGroup = voxEngine.m_reqGroup; + if (voxEngine.m_curGroup != reqGroup) + { + voxEngine.m_curGroup = reqGroup; + if (reqGroup->m_song) + { + if (curSeq) + curSeq->kill(); + curSeq = amuseEngine.seqPlay(reqGroup->m_id, -1, nullptr); + } + } + /* Process MIDI events first */ if (voxEngine.m_midiReceiver) { @@ -245,9 +271,9 @@ struct AudioUnitVoiceEngine : boo::BaseAudioVoiceEngine { if (event->eventType == AURenderEventMIDI) { - NSLog(@"MIDI %d %d", event->length, event->data[0]); (*voxEngine.m_midiReceiver)(std::vector(std::cbegin(event->data), - std::cbegin(event->data) + event->length)); + std::cbegin(event->data) + event->length), + event->eventSampleTime / voxEngine.getCurrentSampleRate()); } } } diff --git a/AudioUnit/AudioUnitViewController.mm b/AudioUnit/AudioUnitViewController.mm index 6c8da5c..4957577 100644 --- a/AudioUnit/AudioUnitViewController.mm +++ b/AudioUnit/AudioUnitViewController.mm @@ -128,6 +128,16 @@ return nil; } +- (NSIndexSet *)browser:(NSBrowser *)browser selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes inColumn:(NSInteger)column +{ + if (column == 2) + { + AudioGroupToken* token = [browser itemAtRow:proposedSelectionIndexes.firstIndex inColumn:column]; + [m_audioUnit requestAudioGroup:token]; + } + return proposedSelectionIndexes; +} + - (id)initWithAudioUnit:(AmuseAudioUnit*)au { self = [super init]; @@ -152,6 +162,7 @@ dispatch_sync(dispatch_get_main_queue(), ^ { m_groupBrowser.delegate = m_groupBrowserDelegate; + [m_groupBrowser loadColumnZero]; }); return m_audioUnit; } diff --git a/include/amuse/BooBackend.hpp b/include/amuse/BooBackend.hpp index 0835434..8485168 100644 --- a/include/amuse/BooBackend.hpp +++ b/include/amuse/BooBackend.hpp @@ -10,7 +10,6 @@ #include "IBackendVoiceAllocator.hpp" #include #include -#include namespace amuse { @@ -77,9 +76,9 @@ class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader boo::MIDIDecoder m_decoder; bool m_useLock; - std::list>> m_queue; + std::list>> m_queue; std::mutex m_midiMutex; - void _MIDIReceive(std::vector&& bytes); + void _MIDIReceive(std::vector&& bytes, double time); public: ~BooBackendMIDIReader(); diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index 7f9baff..6bed603 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -145,7 +145,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, std::pair& store = m_entries[ent.m_sfxId]; ent.setIntoMusyX2(store.first); - memcpy(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); + memmove(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); store.second.swapBigVADPCM(); cur += 28; @@ -161,7 +161,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data, std::pair& store = m_entries[ent.m_sfxId]; ent.setIntoMusyX2(store.first); - memcpy(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); + memmove(&store.second.vadpcm.m_coefs, sampData + ent.m_sampleOff, 256); store.second.swapBigVADPCM(); cur += 24; diff --git a/lib/BooBackend.cpp b/lib/BooBackend.cpp index 529161c..b69355d 100644 --- a/lib/BooBackend.cpp +++ b/lib/BooBackend.cpp @@ -2,6 +2,7 @@ #include "amuse/Voice.hpp" #include "amuse/Submix.hpp" #include "amuse/Engine.hpp" +#include namespace amuse { @@ -130,42 +131,46 @@ BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name, boo { m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(), std::bind(&BooBackendMIDIReader::_MIDIReceive, this, - std::placeholders::_1)); + 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::_1, std::placeholders::_2)); } else m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(name, std::bind(&BooBackendMIDIReader::_MIDIReceive, this, - std::placeholders::_1)); + std::placeholders::_1, std::placeholders::_2)); } -void BooBackendMIDIReader::_MIDIReceive(std::vector&& bytes) +void BooBackendMIDIReader::_MIDIReceive(std::vector&& bytes, double time) { std::unique_lock lk(m_midiMutex, std::defer_lock_t{}); - if (m_useLock) - lk.lock(); - m_queue.emplace_back(std::chrono::steady_clock::now(), std::move(bytes)); + if (m_useLock) lk.lock(); + m_queue.emplace_back(time, std::move(bytes)); +#if 0 + openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); + syslog(LOG_EMERG, "MIDI receive %f\n", time); + closelog(); +#endif } void BooBackendMIDIReader::pumpReader(double dt) { dt += 0.001; /* Add 1ms to ensure consumer keeps up with producer */ - std::unique_lock lk(m_midiMutex); + std::unique_lock lk(m_midiMutex, std::defer_lock_t{}); + if (m_useLock) lk.lock(); if (m_queue.empty()) return; /* Determine range of buffer updates within this period */ auto periodEnd = m_queue.cbegin(); - std::chrono::steady_clock::time_point startPt = m_queue.front().first; + double startPt = m_queue.front().first; for (; periodEnd != m_queue.cend() ; ++periodEnd) { - double delta = std::chrono::duration_cast - (periodEnd->first - startPt).count() / 1000000.0; + double delta = periodEnd->first - startPt; if (delta > dt) break; } @@ -176,6 +181,15 @@ void BooBackendMIDIReader::pumpReader(double dt) /* Dispatch buffers */ for (auto it = m_queue.begin() ; it != periodEnd ;) { +#if 0 + char str[64]; + sprintf(str, "MIDI %zu %f ", it->second.size(), it->first); + for (uint8_t byte : it->second) + sprintf(str + strlen(str), "%02X ", byte); + openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); + syslog(LOG_EMERG, "%s\n", str); + closelog(); +#endif m_decoder.receiveBytes(it->second.cbegin(), it->second.cend()); it = m_queue.erase(it); } @@ -185,12 +199,22 @@ void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->keyOff(chan, key, velocity); +#if 0 + openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); + syslog(LOG_EMERG, "NoteOff %d", key); + closelog(); +#endif } void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->keyOn(chan, key, velocity); +#if 0 + openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); + syslog(LOG_EMERG, "NoteOn %d", key); + closelog(); +#endif } void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index 74b0dfc..a0b61f0 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -805,7 +805,7 @@ static std::vector> LoadRS1N64(F if (ent.compSz == 0xffffffff) { proj.reset(new uint8_t[ent.decompSz]); - memcpy(proj.get(), dataSeg + ent.offset, ent.decompSz); + memmove(proj.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -820,7 +820,7 @@ static std::vector> LoadRS1N64(F if (ent.compSz == 0xffffffff) { pool.reset(new uint8_t[ent.decompSz]); - memcpy(pool.get(), dataSeg + ent.offset, ent.decompSz); + memmove(pool.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -835,7 +835,7 @@ static std::vector> LoadRS1N64(F if (ent.compSz == 0xffffffff) { sdir.reset(new uint8_t[ent.decompSz]); - memcpy(sdir.get(), dataSeg + ent.offset, ent.decompSz); + memmove(sdir.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -850,7 +850,7 @@ static std::vector> LoadRS1N64(F if (ent.compSz == 0xffffffff) { samp.reset(new uint8_t[ent.decompSz]); - memcpy(samp.get(), dataSeg + ent.offset, ent.decompSz); + memmove(samp.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -1076,7 +1076,7 @@ static std::vector> LoadBFNN64(F if (ent.compSz == 0xffffffff) { proj.reset(new uint8_t[ent.decompSz]); - memcpy(proj.get(), dataSeg + ent.offset, ent.decompSz); + memmove(proj.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -1091,7 +1091,7 @@ static std::vector> LoadBFNN64(F if (ent.compSz == 0xffffffff) { pool.reset(new uint8_t[ent.decompSz]); - memcpy(pool.get(), dataSeg + ent.offset, ent.decompSz); + memmove(pool.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -1106,7 +1106,7 @@ static std::vector> LoadBFNN64(F if (ent.compSz == 0xffffffff) { sdir.reset(new uint8_t[ent.decompSz]); - memcpy(sdir.get(), dataSeg + ent.offset, ent.decompSz); + memmove(sdir.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -1121,7 +1121,7 @@ static std::vector> LoadBFNN64(F if (ent.compSz == 0xffffffff) { samp.reset(new uint8_t[ent.decompSz]); - memcpy(samp.get(), dataSeg + ent.offset, ent.decompSz); + memmove(samp.get(), dataSeg + ent.offset, ent.decompSz); } else { @@ -1279,16 +1279,16 @@ static std::vector> LoadRS2(FILE head.swapBig(); std::unique_ptr pool(new uint8_t[head.poolLen]); - memcpy(pool.get(), audData.get() + head.poolOff, head.poolLen); + memmove(pool.get(), audData.get() + head.poolOff, head.poolLen); std::unique_ptr proj(new uint8_t[head.projLen]); - memcpy(proj.get(), audData.get() + head.projOff, head.projLen); + memmove(proj.get(), audData.get() + head.projOff, head.projLen); std::unique_ptr sdir(new uint8_t[head.sdirLen]); - memcpy(sdir.get(), audData.get() + head.sdirOff, head.sdirLen); + memmove(sdir.get(), audData.get() + head.sdirOff, head.sdirLen); std::unique_ptr samp(new uint8_t[head.sampLen]); - memcpy(samp.get(), audData.get() + head.sampOff, head.sampLen); + memmove(samp.get(), audData.get() + head.sampOff, head.sampLen); if (head.projLen && head.poolLen && head.sdirLen && head.sampLen) { @@ -1355,7 +1355,7 @@ static std::vector> LoadRS2S char name[128]; snprintf(name, 128, "GroupFile%02u-%u", j, s); std::unique_ptr song(new uint8_t[sonHead.length]); - memcpy(song.get(), audData.get() + sonHead.offset, sonHead.length); + memmove(song.get(), audData.get() + sonHead.offset, sonHead.length); ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), sonHead.length, sonHead.groupId, sonHead.setupId)); } @@ -1452,16 +1452,16 @@ static std::vector> LoadRS3(FILE head.swapBig(); std::unique_ptr pool(new uint8_t[head.poolLen]); - memcpy(pool.get(), audData.get() + head.poolOff, head.poolLen); + memmove(pool.get(), audData.get() + head.poolOff, head.poolLen); std::unique_ptr proj(new uint8_t[head.projLen]); - memcpy(proj.get(), audData.get() + head.projOff, head.projLen); + memmove(proj.get(), audData.get() + head.projOff, head.projLen); std::unique_ptr sdir(new uint8_t[head.sdirLen]); - memcpy(sdir.get(), audData.get() + head.sdirOff, head.sdirLen); + memmove(sdir.get(), audData.get() + head.sdirOff, head.sdirLen); std::unique_ptr samp(new uint8_t[head.sampLen]); - memcpy(samp.get(), audData.get() + head.sampOff, head.sampLen); + memmove(samp.get(), audData.get() + head.sampOff, head.sampLen); if (head.projLen && head.poolLen && head.sdirLen && head.sampLen) { diff --git a/lib/N64MusyXCodec.c b/lib/N64MusyXCodec.c index 6df84f4..99fd562 100644 --- a/lib/N64MusyXCodec.c +++ b/lib/N64MusyXCodec.c @@ -92,7 +92,7 @@ unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, adpcm_get_predicted_frame(frame, &in[0x0], &in[0x8], rshift); procSamples = (remSamples < 2) ? remSamples : 2; - memcpy(out, frame, 2 * procSamples); + memmove(out, frame, 2 * procSamples); samples += procSamples; remSamples -= procSamples; if (samples == lastSample) @@ -124,7 +124,7 @@ unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, adpcm_get_predicted_frame(frame, &in[0x4], &in[0x18], rshift); procSamples = (remSamples < 2) ? remSamples : 2; - memcpy(out, frame, 2 * procSamples); + memmove(out, frame, 2 * procSamples); samples += procSamples; remSamples -= procSamples; if (samples == lastSample) @@ -145,7 +145,7 @@ unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, int16_t final[64]; unsigned procSamples = N64MusyXDecompressFrame(final, in, coefs, firstSample + lastSample); unsigned samples = procSamples - firstSample; - memcpy(out, final + firstSample, samples * 2); + memmove(out, final + firstSample, samples * 2); return samples; } diff --git a/lib/Voice.cpp b/lib/Voice.cpp index 4a8668a..a7901e9 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -474,7 +474,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) { const int16_t* pcm = reinterpret_cast(m_curSampleData); remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); - memcpy(data, pcm + m_curSamplePos, remCount * sizeof(int16_t)); + memmove(data, pcm + m_curSamplePos, remCount * sizeof(int16_t)); decSamples = remCount; break; } @@ -539,7 +539,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) { const int16_t* pcm = reinterpret_cast(m_curSampleData); remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos); - memcpy(data, pcm + m_curSamplePos, remCount * sizeof(int16_t)); + memmove(data, pcm + m_curSamplePos, remCount * sizeof(int16_t)); decSamples = remCount; break; }