mirror of https://github.com/AxioDL/amuse.git
More AudioUnit integration
This commit is contained in:
parent
85b6f406f1
commit
f260019b89
|
@ -297,7 +297,7 @@ CA
|
|||
<rect key="frame" x="1" y="0.0" width="542" height="366"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="9pb-bl-sSa" viewBased="YES" id="4nw-rf-Dh4">
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="9pb-bl-sSa" viewBased="YES" id="4nw-rf-Dh4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="542" height="343"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
|
@ -418,7 +418,7 @@ CA
|
|||
<rect key="frame" x="1" y="0.0" width="542" height="365"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="fQK-tA-ezw" viewBased="YES" id="Frt-wZ-1ZI">
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="fQK-tA-ezw" viewBased="YES" id="Frt-wZ-1ZI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="542" height="342"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
- (id)initWithAudioGroupPresenter:(AudioGroupFilePresenter*)present;
|
||||
@end
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate, AudioGroupClient>
|
||||
{
|
||||
IBOutlet NSWindow* mainWindow;
|
||||
IBOutlet NSOutlineView* dataOutline;
|
||||
|
|
|
@ -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[])
|
||||
|
|
|
@ -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<amuse::AudioGroupData> m_loadedData;
|
||||
const amuse::AudioGroup* m_loadedGroup;
|
||||
std::vector<AudioGroupToken*> 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<std::map<std::string, std::unique_ptr<AudioGroupDataCollection>>::iterator> m_filterGroups;
|
||||
|
||||
AudioGroupCollection(NSURL* url);
|
||||
void addCollection(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||
void addCollection(AudioGroupFilePresenter* presenter,
|
||||
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection);
|
||||
void update(AudioGroupFilePresenter* presenter);
|
||||
bool doSearch(const std::string& str);
|
||||
bool doActiveFilter();
|
||||
void addSFX(std::vector<AudioGroupSFXToken*>& vecOut);
|
||||
void addSamples(std::vector<AudioGroupSampleToken*>& 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 <NSFilePresenter, NSOutlineViewDataSource, NSOutlineViewDelegate>
|
||||
{
|
||||
@public
|
||||
id<AudioGroupClient> m_audioGroupClient;
|
||||
NSURL* m_groupURL;
|
||||
NSOperationQueue* m_dataQueue;
|
||||
std::map<std::string, std::unique_ptr<AudioGroupCollection>> m_audioGroupCollections;
|
||||
std::vector<std::map<std::string, std::unique_ptr<AudioGroupCollection>>::iterator> m_filterAudioGroupCollections;
|
||||
NSOutlineView* lastOutlineView;
|
||||
NSString* searchStr;
|
||||
NSOutlineView* m_lastOutlineView;
|
||||
NSString* m_searchStr;
|
||||
|
||||
@public
|
||||
std::vector<AudioGroupSFXToken*> m_sfxTableData;
|
||||
std::vector<AudioGroupSampleToken*> m_sampleTableData;
|
||||
}
|
||||
- (id)initWithAudioGroupClient:(id<AudioGroupClient>)client;
|
||||
- (BOOL)addCollectionName:(std::string&&)name items:(std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&&)collection;
|
||||
- (void)update;
|
||||
- (void)resetIterators;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include <athena/FileReader.hpp>
|
||||
#include <amuse/AudioGroupProject.hpp>
|
||||
#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<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection)
|
||||
void AudioGroupCollection::addCollection(AudioGroupFilePresenter* presenter,
|
||||
std::vector<std::pair<std::string, amuse::IntrusiveAudioGroupData>>&& collection)
|
||||
{
|
||||
for (std::pair<std::string, amuse::IntrusiveAudioGroupData>& pair : collection)
|
||||
{
|
||||
|
@ -98,6 +118,7 @@ void AudioGroupCollection::addCollection(std::vector<std::pair<std::string, amus
|
|||
memmove(dataCollection.m_sampData.data(), dataIn.getSamp(), dataIn.getSampSize());
|
||||
|
||||
dataCollection.m_metaData.emplace(dataIn.getDataFormat(), dataIn.getAbsoluteProjOffsets(), true);
|
||||
dataCollection._indexData(presenter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +173,20 @@ bool AudioGroupCollection::doSearch(const std::string& str)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool AudioGroupCollection::doActiveFilter()
|
||||
{
|
||||
bool ret = false;
|
||||
m_filterGroups.clear();
|
||||
m_filterGroups.reserve(m_groups.size());
|
||||
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
|
||||
if (it->second->m_metaData->active)
|
||||
{
|
||||
m_filterGroups.push_back(it);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioGroupCollection::addSFX(std::vector<AudioGroupSFXToken*>& 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<int, const amuse::SongGroupIndex*> 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<int, const amuse::SFXGroupIndex*> 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<AudioGroupCollection>(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<AudioGroupClient>)client
|
||||
{
|
||||
m_audioGroupClient = client;
|
||||
m_groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.io.github.axiodl.Amuse.AudioGroups"];
|
||||
if (!m_groupURL)
|
||||
return nil;
|
||||
|
|
|
@ -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 <AudioGroupClient>
|
||||
{
|
||||
@public
|
||||
AudioUnitViewController* m_viewController;
|
||||
std::unique_ptr<boo::IAudioVoiceEngine> m_booBackend;
|
||||
std::experimental::optional<amuse::AudioUnitBackendVoiceAllocator> m_voxAlloc;
|
||||
std::experimental::optional<amuse::Engine> m_engine;
|
||||
AudioGroupFilePresenter* m_filePresenter;
|
||||
AUAudioUnitBus* m_outBus;
|
||||
AUAudioUnitBusArray* m_outs;
|
||||
}
|
||||
- (nullable id)initWithComponentDescription:(AudioComponentDescription)componentDescription
|
||||
error:(NSError * __nullable * __nonnull)outError
|
||||
viewController:(AudioUnitViewController* __nonnull)vc;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<float> m_interleavedBuf;
|
||||
std::vector<std::unique_ptr<float[]>> 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<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver)
|
||||
{
|
||||
m_midiReceiver = std::move(receiver);
|
||||
return {};
|
||||
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
|
||||
m_midiReceiver = &ret->m_receiver;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr<boo::IMIDIOut> newVirtualMIDIOut()
|
||||
|
@ -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 ; i<m_outputData->mNumberBuffers ; ++i)
|
||||
for (size_t i=0 ; i<m_renderBufs.size() ; ++i)
|
||||
{
|
||||
std::unique_ptr<float[]>& 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<float*>(auBuf.mData));
|
||||
for (size_t f=0 ; f<m_renderFrames ; ++f)
|
||||
{
|
||||
float* bufOut = reinterpret_cast<float*>(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<AudioUnitVoiceEngine>();
|
||||
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<AudioUnitVoiceEngine&>(*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<AudioUnitVoiceEngine&>(*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<uint8_t>(std::cbegin(event->data),
|
||||
std::cbegin(event->data) + event->length));
|
||||
NSLog(@"MIDI %d %d", event->length, event->data[0]);
|
||||
(*voxEngine.m_midiReceiver)(std::vector<uint8_t>(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
|
||||
|
|
|
@ -2,9 +2,24 @@
|
|||
#define __AMUSE_AUDIOUNIT_VIEWCONTROLLER_HPP__
|
||||
|
||||
#import <CoreAudioKit/CoreAudioKit.h>
|
||||
#import "AudioGroupFilePresenter.hpp"
|
||||
|
||||
@class AmuseAudioUnit;
|
||||
|
||||
@interface GroupBrowserDelegate : NSObject <NSBrowserDelegate>
|
||||
{
|
||||
AmuseAudioUnit* m_audioUnit;
|
||||
}
|
||||
- (id)initWithAudioUnit:(AmuseAudioUnit*)au;
|
||||
@end
|
||||
|
||||
@interface AudioUnitViewController : AUViewController <AUAudioUnitFactory>
|
||||
|
||||
{
|
||||
@public
|
||||
AmuseAudioUnit* m_audioUnit;
|
||||
IBOutlet NSBrowser* m_groupBrowser;
|
||||
GroupBrowserDelegate* m_groupBrowserDelegate;
|
||||
}
|
||||
@end
|
||||
|
||||
#endif // __AMUSE_AUDIOUNIT_VIEWCONTROLLER_HPP__
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="AudioUnitViewController">
|
||||
<connections>
|
||||
<outlet property="m_groupBrowser" destination="Pbx-jL-10p" id="BEh-LF-c7K"/>
|
||||
<outlet property="view" destination="Pbx-jL-10p" id="dfL-uY-h99"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<browser verticalHuggingPriority="750" allowsExpansionToolTips="YES" enabled="YES" hasHorizontalScroller="YES" allowsEmptySelection="YES" defaultColumnWidth="100" minColumnWidth="100" maxVisibleColumns="3" titled="YES" separatesColumns="YES" allowsTypeSelect="YES" sendsActionOnArrowKeys="YES" columnResizingType="auto" id="Pbx-jL-10p">
|
||||
<rect key="frame" x="0.0" y="0.0" width="510" height="312"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<point key="canvasLocation" x="313" y="621"/>
|
||||
</browser>
|
||||
</objects>
|
||||
</document>
|
|
@ -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"
|
||||
"$<TARGET_FILE_DIR:amuse-au>/../Resources/AudioUnitViewController.nib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${PROV_PROFILE}" "$<TARGET_FILE_DIR:amuse-au>/../embedded.provisionprofile"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "$<TARGET_FILE_DIR:amuse-au-container>/../PlugIns/amuse-au.appex"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "$<TARGET_FILE_DIR:amuse-au>/../.."
|
||||
|
|
|
@ -76,13 +76,14 @@ class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
|
|||
std::unique_ptr<boo::IMIDIIn> m_midiIn;
|
||||
boo::MIDIDecoder m_decoder;
|
||||
|
||||
bool m_useLock;
|
||||
std::list<std::pair<std::chrono::steady_clock::time_point, std::vector<uint8_t>>> m_queue;
|
||||
std::mutex m_midiMutex;
|
||||
void _MIDIReceive(std::vector<uint8_t>&& bytes);
|
||||
|
||||
public:
|
||||
~BooBackendMIDIReader();
|
||||
BooBackendMIDIReader(Engine& engine, const char* name);
|
||||
BooBackendMIDIReader(Engine& engine, const char* name, bool useLock);
|
||||
|
||||
std::string description();
|
||||
void pumpReader(double dt);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<BooBackendVoiceAllocator&>(engine.getBackend());
|
||||
if (!name)
|
||||
|
@ -145,7 +145,9 @@ BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name)
|
|||
|
||||
void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_midiMutex);
|
||||
std::unique_lock<std::mutex> 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<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enume
|
|||
|
||||
std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine, const char* name)
|
||||
{
|
||||
std::unique_ptr<IMIDIReader> ret = std::make_unique<BooBackendMIDIReader>(engine, name);
|
||||
std::unique_ptr<IMIDIReader> ret = std::make_unique<BooBackendMIDIReader>(engine, name, m_booEngine.useMIDILock());
|
||||
if (!static_cast<BooBackendMIDIReader&>(*ret).m_midiIn)
|
||||
return {};
|
||||
return ret;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue