//----------------------------------------------------------------------------- // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/interappaudio/PresetManager.mm // Created by : Steinberg, 10/2013 // Description : VST 3 InterAppAudio // Flags : clang-format SMTGSequencer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #import "PresetManager.h" #import "PresetBrowserViewController.h" #import "PresetSaveViewController.h" #import "public.sdk/source/vst/vstpresetfile.h" #import "pluginterfaces/vst/ivstattributes.h" namespace Steinberg { namespace Vst { namespace InterAppAudio { //----------------------------------------------------------------------------- class PresetStream : public ReadOnlyBStream, public IStreamAttributes { public: PresetStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize, const char* utf8Path) : ReadOnlyBStream (sourceStream, sourceOffset, sectionSize), fileName (utf8Path) { fileName.toWideString (kCP_Utf8); } virtual tresult PLUGIN_API getFileName (String128 name) override { if (fileName.length () > 0) { fileName.copyTo (name, 0, 128); return kResultTrue; } return kResultFalse; } virtual IAttributeList* PLUGIN_API getAttributes () override { return nullptr; } DEF_INTERFACES_1 (IStreamAttributes, ReadOnlyBStream) REFCOUNT_METHODS (ReadOnlyBStream) protected: String fileName; }; //----------------------------------------------------------------------------- PresetManager::PresetManager (VST3Plugin* plugin, const TUID& cid) : plugin (plugin) , visiblePresetBrowserViewController (nil) , visibleSavePresetViewController (nil) , cid (cid) { id obj = [[NSUserDefaults standardUserDefaults] objectForKey:@"PresetManager|lastPreset"]; if (obj && [obj isKindOfClass:[NSString class]]) { lastPreset = [obj UTF8String]; } } //----------------------------------------------------------------------------- NSArray* PresetManager::getPresetPaths (PresetPathType type) { if (type == kFactory) { return [[NSBundle mainBundle] URLsForResourcesWithExtension:@"vstpreset" subdirectory:@"Presets"]; } NSFileManager* fs = [NSFileManager defaultManager]; NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:Nil create:YES error:NULL]; if (documentsUrl) { NSMutableArray* userUrls = [NSMutableArray new]; NSDirectoryEnumerator* enumerator = [fs enumeratorAtURL:documentsUrl includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:nil]; for (NSURL* url in enumerator.allObjects) { if ([[url pathExtension] isEqualToString:@"vstpreset"]) { [userUrls addObject:url]; } } return [userUrls sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; }]; } return nil; } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::runLoadPresetBrowser () { if (visiblePresetBrowserViewController != nil) return kResultFalse; addRef (); visiblePresetBrowserViewController = [[PresetBrowserViewController alloc] initWithCallback:[this] (const char* path) { loadPreset (path); visiblePresetBrowserViewController = nil; release (); }]; addRef (); dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if (visiblePresetBrowserViewController) { [visiblePresetBrowserViewController setFactoryPresets:getPresetPaths (kFactory) userPresets:getPresetPaths (kUser)]; } release (); }); return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::runSavePresetBrowser () { if (visibleSavePresetViewController != nil) return kResultFalse; addRef (); visibleSavePresetViewController = [[PresetSaveViewController alloc] initWithCallback:[this] (const char* path) { savePreset (path); visibleSavePresetViewController = nil; release (); }]; return kResultTrue; } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::loadNextPreset () { return loadPreset (true); } //----------------------------------------------------------------------------- tresult PLUGIN_API PresetManager::loadPreviousPreset () { return loadPreset (false); } //----------------------------------------------------------------------------- tresult PresetManager::loadPreset (bool next) { NSArray* presets = [[getPresetPaths (kFactory) arrayByAddingObjectsFromArray:getPresetPaths (kUser)] sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) { return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; }]; __block NSUInteger index = NSNotFound; if (lastPreset.isEmpty () == false) { NSURL* lastUrl = [[NSURL fileURLWithPath:[NSString stringWithUTF8String:lastPreset]] fileReferenceURL]; if (lastUrl) { [presets enumerateObjectsUsingBlock:^(NSURL* obj, NSUInteger idx, BOOL* stop) { if ([[obj fileReferenceURL] isEqual:lastUrl]) { index = idx; *stop = YES; } }]; } } if (index == NSNotFound) { if (next) index = [presets count] - 1; else index = 1; } if (index != NSNotFound) { if (next) { if (index >= [presets count] - 1) index = 0; else index++; } else { if (index == 0) index = [presets count] - 1; else index--; } return loadPreset ([[[presets objectAtIndex:index] path] UTF8String]); } return kResultFalse; } //----------------------------------------------------------------------------- tresult PresetManager::loadPreset (const char* path) { if (path) { IPtr stream = owned (FileStream::open (path, "r")); if (stream) { [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:path] forKey:@"PresetManager|lastPreset"]; lastPreset = path; auto component = U::cast (plugin->getAudioProcessor ()); IEditController* controller = plugin->getEditController (); if (component) { PresetFile pf (stream); if (!pf.readChunkList ()) return kResultFalse; if (pf.getClassID () != cid) return kResultFalse; const PresetFile::Entry* e = pf.getEntry (kComponentState); if (e == nullptr) return kResultFalse; auto filename = strrchr (path, '/'); if (filename) filename++; IPtr readOnlyBStream = owned (new PresetStream (stream, e->offset, e->size, filename)); tresult result = component->setState (readOnlyBStream); if ((result == kResultTrue || result == kNotImplemented) && controller) { readOnlyBStream->seek (0, IBStream::kIBSeekSet); controller->setComponentState (readOnlyBStream); if (pf.contains (kControllerState)) { e = pf.getEntry (kControllerState); if (e) { readOnlyBStream = owned (new PresetStream (stream, e->offset, e->size, filename)); controller->setState (readOnlyBStream); } } } return result; } } } return kResultFalse; } //----------------------------------------------------------------------------- void PresetManager::savePreset (const char* path) { IBStream* stream = FileStream::open (path, "w"); if (stream) { auto component = U::cast (plugin->getAudioProcessor ()); IEditController* controller = plugin->getEditController (); if (component) { PresetFile::savePreset (stream, cid, component, controller); } stream->release (); loadPreset (path); } } } } } // namespaces