├── Documentation~ ├── Example.gif ├── Generated Code Files.png └── Github Header.jpg ├── Editor.meta ├── Editor ├── AudioReferenceDropdown.cs ├── AudioReferenceDropdown.cs.meta ├── AudioReferencePropertyDrawer.cs ├── AudioReferencePropertyDrawer.cs.meta ├── CodeGenerator.cs ├── CodeGenerator.cs.meta ├── Extensions.meta ├── Extensions │ ├── BankEditorExtensions.cs │ ├── BankEditorExtensions.cs.meta │ ├── BusEditorExtensions.cs │ ├── BusEditorExtensions.cs.meta │ ├── EditorEventRefExtensions.cs │ ├── EditorEventRefExtensions.cs.meta │ ├── EditorParamRefExtensions.cs │ ├── EditorParamRefExtensions.cs.meta │ ├── MemberInfoExtensions.cs │ ├── MemberInfoExtensions.cs.meta │ ├── SerializedPropertyExtensions.cs │ ├── SerializedPropertyExtensions.cs.meta │ ├── TypeExtensions.cs │ ├── TypeExtensions.cs.meta │ ├── VCAEditorExtensions.cs │ └── VCAEditorExtensions.cs.meta ├── FmodCodeGenerator.cs ├── FmodCodeGenerator.cs.meta ├── FmodSettingsProvider.cs ├── FmodSettingsProvider.cs.meta ├── FmodSyntaxSettingsEditor.cs ├── FmodSyntaxSettingsEditor.cs.meta ├── FmodSyntaxUtilities.cs ├── FmodSyntaxUtilities.cs.meta ├── Resources.meta ├── Resources │ ├── Templates.meta │ └── Templates │ │ ├── Fmod.meta │ │ └── Fmod │ │ ├── Banks.meta │ │ ├── Banks │ │ ├── FmodBankField.cs.txt │ │ ├── FmodBankField.cs.txt.meta │ │ ├── FmodBanks.cs.txt │ │ └── FmodBanks.cs.txt.meta │ │ ├── Buses.meta │ │ ├── Buses │ │ ├── FmodBusField.cs.txt │ │ ├── FmodBusField.cs.txt.meta │ │ ├── FmodBuses.cs.txt │ │ └── FmodBuses.cs.txt.meta │ │ ├── Events.meta │ │ ├── Events │ │ ├── FmodEnum.cs.txt │ │ ├── FmodEnum.cs.txt.meta │ │ ├── FmodEventConfigPlayMethodWithParameters.cs.txt │ │ ├── FmodEventConfigPlayMethodWithParameters.cs.txt.meta │ │ ├── FmodEventField.cs.txt │ │ ├── FmodEventField.cs.txt.meta │ │ ├── FmodEventFolder.cs.txt │ │ ├── FmodEventFolder.cs.txt.meta │ │ ├── FmodEventParameter.cs.txt │ │ ├── FmodEventParameter.cs.txt.meta │ │ ├── FmodEventParametersInitialization.cs.txt │ │ ├── FmodEventParametersInitialization.cs.txt.meta │ │ ├── FmodEventPlaybackPlayMethodWithParameters.cs.txt │ │ ├── FmodEventPlaybackPlayMethodWithParameters.cs.txt.meta │ │ ├── FmodEventType.cs.txt │ │ ├── FmodEventType.cs.txt.meta │ │ ├── FmodEventTypes.cs.txt │ │ ├── FmodEventTypes.cs.txt.meta │ │ ├── FmodEvents.cs.txt │ │ ├── FmodEvents.cs.txt.meta │ │ ├── FmodGlobalParameter.cs.txt │ │ ├── FmodGlobalParameter.cs.txt.meta │ │ ├── FmodGlobalParameters.cs.txt │ │ └── FmodGlobalParameters.cs.txt.meta │ │ ├── FMOD-Syntax.asmdef.txt │ │ ├── FMOD-Syntax.asmdef.txt.meta │ │ ├── Snapshots.meta │ │ ├── Snapshots │ │ ├── FmodSnapshotFields.cs.txt │ │ ├── FmodSnapshotFields.cs.txt.meta │ │ ├── FmodSnapshotType.cs.txt │ │ ├── FmodSnapshotType.cs.txt.meta │ │ ├── FmodSnapshotTypes.cs.txt │ │ ├── FmodSnapshotTypes.cs.txt.meta │ │ ├── FmodSnapshots.cs.txt │ │ └── FmodSnapshots.cs.txt.meta │ │ ├── VCAs.meta │ │ └── VCAs │ │ ├── FmodVCAField.cs.txt │ │ ├── FmodVCAField.cs.txt.meta │ │ ├── FmodVCAs.cs.txt │ │ └── FmodVCAs.cs.txt.meta ├── RoyTheunissen.FMODSyntax.Editor.asmdef ├── RoyTheunissen.FMODSyntax.Editor.asmdef.meta ├── SetupWizard.cs ├── SetupWizard.cs.meta ├── SnapshotReferenceDropdown.cs ├── SnapshotReferenceDropdown.cs.meta ├── SnapshotReferencePropertyDrawer.cs ├── SnapshotReferencePropertyDrawer.cs.meta ├── Utilities.meta └── Utilities │ ├── EditorAssetReference.cs │ ├── EditorAssetReference.cs.meta │ ├── EditorPreference.cs │ └── EditorPreference.cs.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── AudioReference.cs ├── AudioReference.cs.meta ├── Banks.meta ├── Banks │ ├── BankExtensions.cs │ └── BankExtensions.cs.meta ├── Buses.meta ├── Buses │ ├── Bus.cs │ ├── Bus.cs.meta │ ├── BusExtensions.cs │ └── BusExtensions.cs.meta ├── Callbacks.meta ├── Callbacks │ ├── IOnFmodPlayback.cs │ └── IOnFmodPlayback.cs.meta ├── Events.meta ├── Events │ ├── FmodAudioConfig.cs │ ├── FmodAudioConfig.cs.meta │ ├── FmodAudioFolder.cs │ ├── FmodAudioFolder.cs.meta │ ├── FmodAudioPlayback.cs │ ├── FmodAudioPlayback.cs.meta │ ├── FmodParameterlessAudio.cs │ ├── FmodParameterlessAudio.cs.meta │ ├── FmodPlayableConfig.cs │ ├── FmodPlayableConfig.cs.meta │ ├── FmodPlayablePlaybackBase.cs │ ├── FmodPlayablePlaybackBase.cs.meta │ ├── Parameter.cs │ └── Parameter.cs.meta ├── Extensions.meta ├── Extensions │ ├── StringExtensions.cs │ └── StringExtensions.cs.meta ├── FmodLabelEnum.cs ├── FmodLabelEnum.cs.meta ├── FmodSyntaxSettings.cs ├── FmodSyntaxSettings.cs.meta ├── FmodSyntaxSystem.cs ├── FmodSyntaxSystem.cs.meta ├── IAudioConfig.cs ├── IAudioConfig.cs.meta ├── IAudioPlayback.cs ├── IAudioPlayback.cs.meta ├── RoyTheunissen.FMODSyntax.asmdef ├── RoyTheunissen.FMODSyntax.asmdef.meta ├── Snapshots.meta ├── Snapshots │ ├── FmodSnapshotConfig.cs │ ├── FmodSnapshotConfig.cs.meta │ ├── FmodSnapshotPlayback.cs │ ├── FmodSnapshotPlayback.cs.meta │ ├── SnapshotReference.cs │ └── SnapshotReference.cs.meta ├── VCAs.meta └── VCAs │ ├── VCA.cs │ ├── VCA.cs.meta │ ├── VCAExtensions.cs │ └── VCAExtensions.cs.meta ├── package.json └── package.json.meta /Documentation~/Example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoyTheunissen/FMOD-Syntax/1543a6bd00242d364ec3307a7b969726f6b815f1/Documentation~/Example.gif -------------------------------------------------------------------------------- /Documentation~/Generated Code Files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoyTheunissen/FMOD-Syntax/1543a6bd00242d364ec3307a7b969726f6b815f1/Documentation~/Generated Code Files.png -------------------------------------------------------------------------------- /Documentation~/Github Header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoyTheunissen/FMOD-Syntax/1543a6bd00242d364ec3307a7b969726f6b815f1/Documentation~/Github Header.jpg -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19a3ff78dd603fc4fa288a82d52ddaf1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/AudioReferenceDropdown.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEditor.IMGUI.Controls; 3 | using FMODUnity; 4 | using System.Linq; 5 | 6 | namespace RoyTheunissen.FMODSyntax 7 | { 8 | /// 9 | /// Dropdown to pick audio references from a list in a searchable way / with subfolders. 10 | /// 11 | public sealed class AudioReferenceDropdown : AdvancedDropdown 12 | { 13 | private readonly SerializedProperty serializedProperty; 14 | 15 | public AudioReferenceDropdown(AdvancedDropdownState state, SerializedProperty serializedProperty) 16 | : base(state) 17 | { 18 | this.serializedProperty = serializedProperty; 19 | } 20 | 21 | protected override AdvancedDropdownItem BuildRoot() 22 | { 23 | AudioConfigDropdownItem root = new AudioConfigDropdownItem( 24 | this, string.Empty, "Audio Configs", string.Empty); 25 | 26 | EditorEventRef[] parameterlessEvents = EventManager.Events 27 | .Where(e => e.Path.StartsWith(EditorEventRefExtensions.EventPrefix) && e.LocalParameters.Count == 0) 28 | .OrderBy(e => e.Path) 29 | .ToArray(); 30 | string[] paths = new string[parameterlessEvents.Length]; 31 | string[] guids = new string[parameterlessEvents.Length]; 32 | 33 | root.AddChildByPath("None", "None"); 34 | 35 | for (int i = 0; i < parameterlessEvents.Length; i++) 36 | { 37 | // Remove the event prefix otherwise it gets treated as a folder. 38 | string path = parameterlessEvents[i].Path; 39 | path = path.RemovePrefix(EditorEventRefExtensions.EventPrefix); 40 | paths[i] = path; 41 | 42 | guids[i] = parameterlessEvents[i].Guid.ToString(); 43 | } 44 | 45 | for (int i = 0; i < paths.Length; i++) 46 | { 47 | root.AddChildByPath(guids[i], paths[i]); 48 | } 49 | 50 | return root; 51 | } 52 | 53 | protected override void ItemSelected(AdvancedDropdownItem item) 54 | { 55 | base.ItemSelected(item); 56 | 57 | AudioConfigDropdownItem dropdownItem = (AudioConfigDropdownItem)item; 58 | 59 | serializedProperty.serializedObject.Update(); 60 | serializedProperty.stringValue = dropdownItem.Guid == "None" ? string.Empty : dropdownItem.Guid; 61 | serializedProperty.serializedObject.ApplyModifiedProperties(); 62 | } 63 | } 64 | 65 | public sealed class AudioConfigDropdownItem : AdvancedDropdownItem 66 | { 67 | private const char Separator = '/'; 68 | 69 | private readonly AudioReferenceDropdown dropdown; 70 | 71 | private string path; 72 | 73 | private string guid; 74 | public string Guid => guid; 75 | 76 | public AudioConfigDropdownItem( 77 | AudioReferenceDropdown dropdown, string guid, string name, string path) : base(name) 78 | { 79 | this.dropdown = dropdown; 80 | this.guid = guid; 81 | this.path = path; 82 | } 83 | 84 | private AudioConfigDropdownItem AddChild(string guid, string name) 85 | { 86 | string newPath = string.IsNullOrEmpty(path) ? name : path + Separator + name; 87 | 88 | AudioConfigDropdownItem child = new AudioConfigDropdownItem(dropdown, guid, name, newPath); 89 | 90 | AddChild(child); 91 | 92 | return child; 93 | } 94 | 95 | private AudioConfigDropdownItem GetOrCreateChild(string guid, string name) 96 | { 97 | // Find a child with the specified name. 98 | foreach (AdvancedDropdownItem child in children) 99 | { 100 | if (child.name == name) 101 | return (AudioConfigDropdownItem)child; 102 | } 103 | 104 | // Add a new one if it didn't exist yet. 105 | return AddChild(guid, name); 106 | } 107 | 108 | public void AddChildByPath(string guid, string relativePath) 109 | { 110 | // Leaf node, add the event there. 111 | if (!relativePath.Contains(Separator)) 112 | { 113 | AddChild(guid, relativePath); 114 | return; 115 | } 116 | 117 | string[] sections = relativePath.Split(Separator); 118 | 119 | // Ensure a child exists for the first section of the path. 120 | // This acts as a folder and and does not have a key itself. 121 | AudioConfigDropdownItem firstSection = GetOrCreateChild(string.Empty, sections[0]); 122 | 123 | // Determine the path relative to this first child. 124 | string pathRemaining = string.Empty; 125 | for (int i = 1; i < sections.Length; i++) 126 | { 127 | pathRemaining += sections[i]; 128 | 129 | if (i < sections.Length - 1) 130 | pathRemaining += Separator; 131 | } 132 | 133 | // Continue recursively from the first section. 134 | firstSection.AddChildByPath(guid, pathRemaining); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Editor/AudioReferenceDropdown.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79dc00952e9106c4ba77ca8b6d55e3f7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/AudioReferencePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using FMODUnity; 2 | using UnityEditor; 3 | using UnityEditor.IMGUI.Controls; 4 | using UnityEngine; 5 | using GUID = FMOD.GUID; 6 | 7 | namespace RoyTheunissen.FMODSyntax 8 | { 9 | /// 10 | /// Draws dropdowns for selecting an audio config. 11 | /// 12 | [CustomPropertyDrawer(typeof(AudioReference))] 13 | public class AudioReferencePropertyDrawer : PropertyDrawer 14 | { 15 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 16 | { 17 | // If this is an array, it would just show "Element 0" as the label which takes up a lot of space and is 18 | // useless. Even more useless is that it actually sees that the first property of AudioReference is a string 19 | // and shows that as the name instead for an array. That just means that it shows the GUID as the label. 20 | // HOW USEFUL. How about we don't show a label at all and just have a wider field that's easier to read? 21 | if (property.IsInArray()) 22 | label = GUIContent.none; 23 | 24 | SerializedProperty audioConfigProperty = property.FindPropertyRelative("fmodEventGuid"); 25 | 26 | // Figure out the display text and the content property. 27 | string displayedText; 28 | string guid = audioConfigProperty.stringValue; 29 | if (string.IsNullOrEmpty(guid)) 30 | { 31 | displayedText = ""; 32 | } 33 | else 34 | { 35 | GUID id = GUID.Parse(guid); 36 | EditorEventRef eventRef = EventManager.EventFromGUID(id); 37 | displayedText = eventRef.GetDisplayName(); 38 | } 39 | 40 | // Draw a dropdown button to select the audio config. 41 | EditorGUI.BeginProperty(position, label, audioConfigProperty); 42 | Rect valueRect = EditorGUI.PrefixLabel(position, label); 43 | bool didPress = EditorGUI.DropdownButton( 44 | valueRect, new GUIContent(displayedText), FocusType.Keyboard); 45 | if (didPress) 46 | { 47 | Rect dropDownRect = position; 48 | dropDownRect.xMax += 200; 49 | 50 | AudioReferenceDropdown menu = new AudioReferenceDropdown(new AdvancedDropdownState(), audioConfigProperty); 51 | menu.Show(dropDownRect); 52 | } 53 | EditorGUI.EndProperty(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Editor/AudioReferencePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d5afffc01088ad74cbb240d67ba72d56 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/CodeGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using UnityEditor; 5 | using UnityEngine; 6 | using UnityEngine.Assertions; 7 | 8 | namespace RoyTheunissen.FMODSyntax 9 | { 10 | /// 11 | /// Utility for generating code from a template .txt file. 12 | /// 13 | public sealed class CodeGenerator 14 | { 15 | private const string TemplateSuffix = ".txt"; 16 | 17 | private const string KeywordStart = "##"; 18 | private const string KeywordEnd = "##"; 19 | 20 | private const string Nl = "\r\n"; 21 | 22 | private string defaultPath; 23 | private string contents; 24 | 25 | private readonly EditorAssetReference textAsset; 26 | 27 | private bool isInitialized; 28 | 29 | public CodeGenerator(string templateFileName) 30 | { 31 | textAsset = new EditorAssetReference(templateFileName); 32 | 33 | // By default, just write to the folder where the template is. 34 | defaultPath = templateFileName.RemoveSuffix(TemplateSuffix); 35 | } 36 | 37 | private void Initialize() 38 | { 39 | if (isInitialized) 40 | return; 41 | 42 | isInitialized = true; 43 | 44 | Assert.IsNotNull(textAsset.Asset, $"Tried to load code template '{textAsset.Path}' which didn't exist."); 45 | 46 | Reset(); 47 | } 48 | 49 | public void Reset() 50 | { 51 | contents = textAsset.Asset.text; 52 | } 53 | 54 | private string GetKeywordFormatted(string keyword) 55 | { 56 | return KeywordStart + keyword + KeywordEnd; 57 | } 58 | 59 | public bool HasKeyword(string keyword) 60 | { 61 | keyword = GetKeywordFormatted(keyword); 62 | return contents.Contains(keyword); 63 | } 64 | 65 | public void ReplaceKeyword(string keyword, string code, bool removeLineIfCodeIsEmpty = false) 66 | { 67 | Initialize(); 68 | 69 | if (string.IsNullOrEmpty(keyword)) 70 | return; 71 | 72 | bool isCodeEmpty = string.IsNullOrEmpty(code); 73 | 74 | if (isCodeEmpty && removeLineIfCodeIsEmpty) 75 | { 76 | RemoveKeywordLines(keyword); 77 | return; 78 | } 79 | 80 | keyword = GetKeywordFormatted(keyword); 81 | 82 | if (isCodeEmpty) 83 | { 84 | contents = contents.Replace(keyword, code); 85 | return; 86 | } 87 | 88 | // Make indentation consistent... 89 | code = code.Replace("\t", " "); 90 | 91 | int indexOfKeyword = contents.IndexOf(keyword, StringComparison.Ordinal); 92 | while (indexOfKeyword != -1) 93 | { 94 | // Determine the whitespace immediately preceding the keyword. 95 | string whitespacePreceding = contents.GetWhitespacePreceding(indexOfKeyword, false); 96 | 97 | // Remove the keyword. 98 | contents = contents.Remove(indexOfKeyword, keyword.Length); 99 | 100 | // Now apply the correct indentation to every line of the code. 101 | string codeWithIndentation = string.Empty; 102 | string[] codeLines = code.TrimEnd().Split("\n"); 103 | for (int i = 0; i < codeLines.Length; i++) 104 | { 105 | // The first line doesn't need extra indentation because it already has it. 106 | if (i > 0) 107 | codeWithIndentation += whitespacePreceding; 108 | codeWithIndentation += codeLines[i]; 109 | 110 | if (i < codeLines.Length - 1) 111 | codeWithIndentation += "\n"; 112 | } 113 | 114 | // Now add the correctly formatted code at the specified position. 115 | contents = contents.Insert(indexOfKeyword, codeWithIndentation); 116 | 117 | // See if there's another keyword that should be replaced. 118 | indexOfKeyword = contents.IndexOf(keyword, StringComparison.Ordinal); 119 | } 120 | } 121 | 122 | public void RemoveKeywordLines(string keyword) 123 | { 124 | Initialize(); 125 | 126 | if (string.IsNullOrEmpty(keyword)) 127 | return; 128 | 129 | keyword = GetKeywordFormatted(keyword); 130 | 131 | List lines = new List(contents.Split("\n")); 132 | for (int i = lines.Count - 1; i >= 0; i--) 133 | { 134 | if (lines[i].Contains(keyword)) 135 | lines.RemoveAt(i); 136 | } 137 | 138 | contents = string.Join("\n", lines); 139 | } 140 | 141 | public string GetCode() 142 | { 143 | Initialize(); 144 | 145 | return contents; 146 | } 147 | 148 | public void GenerateFile(string path = null) 149 | { 150 | Initialize(); 151 | 152 | string pathAbsolute = 153 | string.IsNullOrEmpty(path) ? defaultPath.GetAbsolutePath() : path.GetAbsolutePath(); 154 | 155 | if (!File.Exists(pathAbsolute)) 156 | { 157 | Directory.CreateDirectory(Path.GetDirectoryName(pathAbsolute)); 158 | StreamWriter writeStream = File.CreateText(pathAbsolute); 159 | writeStream.Write(GetCode()); 160 | writeStream.Flush(); 161 | writeStream.Close(); 162 | } 163 | else 164 | File.WriteAllText(pathAbsolute, GetCode()); 165 | 166 | AssetDatabase.Refresh(); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Editor/CodeGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a72ba2bd2680e24b85a3a3ea76d6de1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e03ebd0ed0f28c46a7e0bc37582201d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Extensions/BankEditorExtensions.cs: -------------------------------------------------------------------------------- 1 | using RoyTheunissen.FMODSyntax; 2 | 3 | namespace FMOD.Studio 4 | { 5 | /// 6 | /// Extensions for bank to get the path without an out parameter because to make lambda functions easy to write. 7 | /// 8 | public static class BankEditorExtensions 9 | { 10 | public static string GetName(this Bank bank) 11 | { 12 | return FmodSyntaxUtilities.GetFilteredNameFromPath(bank.getPath()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Editor/Extensions/BankEditorExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e8e09e9174c82d4ea69845b9e6b1305 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/BusEditorExtensions.cs: -------------------------------------------------------------------------------- 1 | using RoyTheunissen.FMODSyntax; 2 | 3 | namespace FMOD.Studio 4 | { 5 | /// 6 | /// Extensions for bus to get the path without an out parameter because to make lambda functions easy to write. 7 | /// 8 | public static class BusEditorExtensions 9 | { 10 | public static string GetName(this Bus bus) 11 | { 12 | return FmodSyntaxUtilities.GetFilteredNameFromPath(bus.getPath()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Editor/Extensions/BusEditorExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fa1facdb911386e42bf43466f016d8de 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/EditorEventRefExtensions.cs: -------------------------------------------------------------------------------- 1 | using FMODUnity; 2 | 3 | namespace RoyTheunissen.FMODSyntax 4 | { 5 | /// 6 | /// Useful extension methods for EditorEventRef. 7 | /// 8 | public static class EditorEventRefExtensions 9 | { 10 | public const string EventPrefix = "event:/"; 11 | public const string SnapshotPrefix = "snapshot:/"; 12 | 13 | public static string GetDisplayName(this EditorEventRef @event) 14 | { 15 | return FmodSyntaxUtilities.GetDisplayNameFromPath(@event.name); 16 | } 17 | 18 | public static string GetFilteredName(this EditorEventRef @event) 19 | { 20 | return FmodSyntaxUtilities.GetFilteredNameFromPath(@event.name); 21 | } 22 | 23 | public static string GetFieldName(this EditorEventRef @event) 24 | { 25 | return FmodSyntaxUtilities.GetFilteredNameFromPathLowerCase(@event.name); 26 | } 27 | 28 | public static string GetFilteredPath(this EditorEventRef @event, bool stripSpecialCharacters = false) 29 | { 30 | return FmodSyntaxUtilities.GetFilteredPath(@event.Path, stripSpecialCharacters); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Editor/Extensions/EditorEventRefExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b0f5b1d8012caf4a81416d63f736d2d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/EditorParamRefExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FMODUnity; 3 | using UnityEngine; 4 | 5 | namespace RoyTheunissen.FMODSyntax 6 | { 7 | /// 8 | /// Useful extension methods for EditorParamRef. 9 | /// 10 | public static class EditorParamRefExtensions 11 | { 12 | public static string GetFilteredName(this EditorParamRef parameter) 13 | { 14 | return FmodSyntaxUtilities.GetFilteredNameFromPath(parameter.Name); 15 | } 16 | 17 | public static string GetArgumentName(this EditorParamRef parameter) 18 | { 19 | return FmodSyntaxUtilities.GetFilteredNameFromPathLowerCase(parameter.Name); 20 | } 21 | 22 | public static bool HasNormalizedRange(this EditorParamRef parameter) => 23 | Mathf.Approximately(parameter.Min, 0.0f) && Mathf.Approximately(parameter.Max, 1.0f); 24 | 25 | private static string GetLabelParameterTypeName( 26 | this EditorParamRef parameter, bool fullyQualified, EditorEventRef @event) 27 | { 28 | string name = parameter.GetFilteredName(); 29 | bool hasUserEnum = FmodCodeGenerator.GetUserSpecifiedLabelParameterType(name, out Type userEnum); 30 | if (hasUserEnum) 31 | return userEnum.FullName; 32 | 33 | string type = $"{name}Values"; 34 | 35 | if (fullyQualified) 36 | type = $"{@event.GetFilteredName()}Playback." + type; 37 | 38 | return type; 39 | } 40 | 41 | public static string GetWrapperType(this EditorParamRef parameter) 42 | { 43 | switch (parameter.Type) 44 | { 45 | default: 46 | return "ParameterFloat"; 47 | case ParameterType.Discrete: 48 | return parameter.HasNormalizedRange() ? "ParameterBool" : "ParameterInt"; 49 | case ParameterType.Labeled: 50 | { 51 | #if SCRIPTABLE_OBJECT_COLLECTION 52 | string name = parameter.GetFilteredName(); 53 | bool hasUserType = FmodCodeGenerator.GetUserSpecifiedLabelParameterType(name, out Type userType); 54 | if (hasUserType && !typeof(Enum).IsAssignableFrom(userType)) 55 | return $"ParameterScriptableObjectCollectionItem<{parameter.GetLabelParameterTypeName(false, null)}>"; 56 | #endif // SCRIPTABLE_OBJECT_COLLECTION 57 | 58 | return $"ParameterEnum<{parameter.GetLabelParameterTypeName(false, null)}>"; 59 | } 60 | } 61 | } 62 | 63 | public static string GetArgumentType(this EditorParamRef parameter) 64 | { 65 | switch (parameter.Type) 66 | { 67 | default: 68 | return "float"; 69 | case ParameterType.Discrete: 70 | return parameter.HasNormalizedRange() ? "bool" : "int"; 71 | case ParameterType.Labeled: 72 | return $"{parameter.GetLabelParameterTypeName(false, null)}"; 73 | } 74 | } 75 | 76 | public static string GetArgumentTypeFullyQualified(this EditorParamRef parameter, EditorEventRef @event) 77 | { 78 | string type = parameter.GetArgumentType(); 79 | if (parameter.Type == ParameterType.Labeled) 80 | type = parameter.GetLabelParameterTypeName(true, @event); 81 | return type; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Editor/Extensions/EditorParamRefExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 068ee796b2dd5d04480e943b36fbc692 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/MemberInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace RoyTheunissen.FMODSyntax 7 | { 8 | public static class MemberInfoExtensions 9 | { 10 | public static T GetAttribute(this MemberInfo memberInfo, bool inherit = true) 11 | where T : Attribute 12 | { 13 | object[] attributes = memberInfo.GetCustomAttributes(inherit); 14 | return attributes.OfType().FirstOrDefault(); 15 | } 16 | 17 | public static bool HasAttribute(this MemberInfo memberInfo, bool inherit = true) 18 | where T : Attribute 19 | { 20 | object[] attributes = memberInfo.GetCustomAttributes(inherit); 21 | return attributes.OfType().Any(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Editor/Extensions/MemberInfoExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 81ec3cf01af8bf343a61d77ead7b597a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/SerializedPropertyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using UnityEditor; 4 | 5 | namespace RoyTheunissen.FMODSyntax 6 | { 7 | /// 8 | /// Useful extension methods for EditorParamRef. 9 | /// 10 | public static class SerializedPropertyExtensions 11 | { 12 | /// 13 | /// From: https://gist.github.com/monry/9de7009689cbc5050c652bcaaaa11daa 14 | /// 15 | public static SerializedProperty GetParent(this SerializedProperty serializedProperty) 16 | { 17 | string[] propertyPaths = serializedProperty.propertyPath.Split('.'); 18 | if (propertyPaths.Length <= 1) 19 | return default; 20 | 21 | SerializedProperty parentSerializedProperty = 22 | serializedProperty.serializedObject.FindProperty(propertyPaths.First()); 23 | for (int index = 1; index < propertyPaths.Length - 1; index++) 24 | { 25 | if (propertyPaths[index] == "Array") 26 | { 27 | if (index + 1 == propertyPaths.Length - 1) 28 | { 29 | // reached the end 30 | break; 31 | } 32 | if (propertyPaths.Length > index + 1 && Regex.IsMatch(propertyPaths[index + 1], "^data\\[\\d+\\]$")) 33 | { 34 | Match match = Regex.Match(propertyPaths[index + 1], "^data\\[(\\d+)\\]$"); 35 | int arrayIndex = int.Parse(match.Groups[1].Value); 36 | parentSerializedProperty = parentSerializedProperty.GetArrayElementAtIndex(arrayIndex); 37 | index++; 38 | } 39 | } 40 | else 41 | { 42 | parentSerializedProperty = parentSerializedProperty.FindPropertyRelative(propertyPaths[index]); 43 | } 44 | } 45 | 46 | return parentSerializedProperty; 47 | } 48 | 49 | public static bool IsInArray(this SerializedProperty serializedProperty) 50 | { 51 | SerializedProperty parent = serializedProperty.GetParent(); 52 | 53 | if (parent == null) 54 | return false; 55 | 56 | return parent.isArray; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Editor/Extensions/SerializedPropertyExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a4bc8b960164ed04eb3c52ebe5d03071 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace RoyTheunissen.FMODSyntax 5 | { 6 | /// 7 | /// Useful extension methods for EditorParamRef. 8 | /// 9 | public static class TypeExtensions 10 | { 11 | public static Type[] GetAllTypesWithAttribute(bool includeAbstract = true) 12 | where T : Attribute 13 | { 14 | return AppDomain.CurrentDomain.GetAssemblies() 15 | .SelectMany(a => a.GetTypes()) 16 | .Where(t => t.HasAttribute() && (includeAbstract || !t.IsAbstract)).ToArray(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Editor/Extensions/TypeExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eaff6c4f01ccb5347aeded551238c288 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Extensions/VCAEditorExtensions.cs: -------------------------------------------------------------------------------- 1 | using RoyTheunissen.FMODSyntax; 2 | 3 | namespace FMOD.Studio 4 | { 5 | /// 6 | /// Extensions for bus to get the path without an out parameter because to make lambda functions easy to write. 7 | /// 8 | public static class VCAEditorExtensions 9 | { 10 | public static string GetName(this VCA vca) 11 | { 12 | return FmodSyntaxUtilities.GetFilteredNameFromPath(vca.getPath()); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Editor/Extensions/VCAEditorExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e813911a175d7e40aafd2411a769586 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/FmodCodeGenerator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63207498fd557844a9a89bd4f843df8b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/FmodSettingsProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | 4 | #if FMOD_AUTO_REGENERATE_CODE 5 | namespace RoyTheunissen.FMODSyntax 6 | { 7 | /// 8 | /// Exposes serialized editor-time FMOD preferences. 9 | /// 10 | public static class FmodPreferences 11 | { 12 | public static readonly EditorPreferenceBool GenerateCodeAutomaticallyPreference = 13 | new EditorPreferenceBool("FMOD/Generate Code Automatically", true); 14 | } 15 | 16 | /// 17 | /// Provides a window with which to tweak FMOD preferences such as whether to generate code automatically or not. 18 | /// 19 | public class FmodSettingsProvider : SettingsProvider 20 | { 21 | public FmodSettingsProvider(string path, SettingsScope scopes, IEnumerable keywords = null) 22 | : base(path, scopes, keywords) 23 | { 24 | } 25 | 26 | public override void OnGUI(string searchContext) 27 | { 28 | base.OnGUI(searchContext); 29 | 30 | EditorGUILayout.Space(); 31 | FmodPreferences.GenerateCodeAutomaticallyPreference.DrawGUILayoutLeft(); 32 | } 33 | 34 | [SettingsProvider] 35 | public static SettingsProvider CreateFMODSettingsProvider() 36 | { 37 | var provider = new FmodSettingsProvider("Preferences/FMOD", SettingsScope.User) 38 | { 39 | label = "FMOD", 40 | keywords = new HashSet(new[] { "FMOD", "Audio" }) 41 | }; 42 | 43 | return provider; 44 | } 45 | } 46 | } 47 | #endif // FMOD_AUTO_REGENERATE_CODE 48 | -------------------------------------------------------------------------------- /Editor/FmodSettingsProvider.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 488576f2fa669c24ebb57dbf0ed12c8b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/FmodSyntaxSettingsEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | 4 | namespace RoyTheunissen.FMODSyntax 5 | { 6 | /// 7 | /// Draws a button for re-generating FMOD code. 8 | /// 9 | [CustomEditor(typeof(FmodSyntaxSettings))] 10 | public class FmodSyntaxSettingsEditor : Editor 11 | { 12 | public override void OnInspectorGUI() 13 | { 14 | serializedObject.Update(); 15 | base.OnInspectorGUI(); 16 | serializedObject.ApplyModifiedProperties(); 17 | 18 | // Draw a button to generate FMOD code. This is particularly useful to have here because once you are done 19 | // tweaking the settings, you are likely to want to re-generate code anyway. 20 | EditorGUILayout.Space(); 21 | bool shouldGenerateCode = GUILayout.Button("Generate Code", GUILayout.Height(40)); 22 | if (shouldGenerateCode) 23 | FmodCodeGenerator.GenerateCode(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Editor/FmodSyntaxSettingsEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 931a0c2a865530e40aea0b4f88b99b42 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/FmodSyntaxUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace RoyTheunissen.FMODSyntax 6 | { 7 | /// 8 | /// Useful utility methods for the FMOD Syntax. 9 | /// 10 | public static class FmodSyntaxUtilities 11 | { 12 | private static readonly Dictionary replacements = new Dictionary() 13 | { 14 | { "base", "@base" }, 15 | }; 16 | 17 | private static readonly Dictionary substitutions = new Dictionary() 18 | { 19 | { "-", "_" }, 20 | { " ", "" }, 21 | { ".", "" }, 22 | { "(", "" }, 23 | { ")", "" }, 24 | { "&", "And" }, 25 | }; 26 | 27 | public static string Filter(string name, bool performReplacements = true) 28 | { 29 | if (performReplacements) 30 | { 31 | foreach (KeyValuePair replacement in replacements) 32 | { 33 | if (name == replacement.Key) 34 | return replacement.Value; 35 | } 36 | } 37 | 38 | foreach (KeyValuePair substitution in substitutions) 39 | { 40 | name = name.Replace(substitution.Key, substitution.Value); 41 | } 42 | 43 | return name; 44 | } 45 | 46 | public static string GetDisplayNameFromPath(string path) 47 | { 48 | return Path.GetFileName(path); 49 | } 50 | 51 | public static string GetFilteredNameFromPath(string path) 52 | { 53 | string name = Path.GetFileName(path).ToPascalCasing(); 54 | name = Filter(name); 55 | return name; 56 | } 57 | 58 | public static string GetFilteredNameFromPathLowerCase(string path) 59 | { 60 | string name = GetFilteredNameFromPath(path).ToCamelCasing(); 61 | name = Filter(name); 62 | return name; 63 | } 64 | 65 | public static string GetFilteredPath(string path, bool stripSpecialCharacters) 66 | { 67 | if (string.IsNullOrEmpty(path)) 68 | return string.Empty; 69 | 70 | const char separator = '/'; 71 | 72 | // Events start with something like event:/ , so get rid of that first. 73 | path = path.Substring(path.IndexOf(separator, StringComparison.Ordinal) + 1); 74 | 75 | if (stripSpecialCharacters) 76 | { 77 | // Clean up every section a bit. 78 | string[] pathSections = path.Split(separator); 79 | for (int i = 0; i < pathSections.Length; i++) 80 | { 81 | pathSections[i] = pathSections[i].ToPascalCasing(); 82 | pathSections[i] = Filter(pathSections[i], false); 83 | } 84 | 85 | path = string.Join(separator, pathSections); 86 | } 87 | 88 | return path; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Editor/FmodSyntaxUtilities.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fc901f3ea6389004bb35433168a2a3e4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71925b42d3892a3499dcbf63879d1d31 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1a686c0c3d6f2a44fbb4b7ccfa06856c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 54f74ae49e1b4bb478c1551717844e6c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Banks.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 66358b99ce7ac824baef31d437999f40 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Banks/FmodBankField.cs.txt: -------------------------------------------------------------------------------- 1 | public static readonly string ##BankName## = "##BankPath##"; 2 | 3 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Banks/FmodBankField.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 174f5f9b8db97714fa68ae70dd3ebac4 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Banks/FmodBanks.cs.txt: -------------------------------------------------------------------------------- 1 | namespace ##Namespace## 2 | { 3 | /// 4 | /// GENERATED: Contains FMOD banks so they can be accessed in a strongly-typed manner. 5 | /// 6 | public static class AudioBanks 7 | { 8 | ##Banks## 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Banks/FmodBanks.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38c472dd142c8a34bb565a31740da9d8 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Buses.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f8850eaaa5c0b14787b4fd2a739a50c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Buses/FmodBusField.cs.txt: -------------------------------------------------------------------------------- 1 | public static readonly Bus ##BusName## = new Bus("##BusPath##"); 2 | 3 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Buses/FmodBusField.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: af8bfc2375c239b49965fe664792d165 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Buses/FmodBuses.cs.txt: -------------------------------------------------------------------------------- 1 | using RoyTheunissen.FMODSyntax; 2 | 3 | namespace ##Namespace## 4 | { 5 | /// 6 | /// GENERATED: Contains FMOD buses so they can be accessed in a strongly-typed manner. 7 | /// 8 | public static class AudioBuses 9 | { 10 | ##Buses## 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Buses/FmodBuses.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c2a391e0c6b479f4b88f7ac9c81e5b5e 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e8149d83d8cc514881405f86b99be62 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEnum.cs.txt: -------------------------------------------------------------------------------- 1 | public enum ##Name## 2 | { 3 | ##Values## 4 | } 5 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEnum.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 34235fc742135d04ba0c67fbbd548265 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventConfigPlayMethodWithParameters.cs.txt: -------------------------------------------------------------------------------- 1 | 2 | public ##EventName##Playback Play(Transform source, ##ParameterArgumentsWithType##) 3 | { 4 | ##EventName##Playback instance = new ##EventName##Playback(); 5 | instance.Play(EventDescription, source, ##ParameterArguments##); 6 | return instance; 7 | } 8 | 9 | public ##EventName##Playback Play(##ParameterArgumentsWithType##) 10 | { 11 | return Play(null, ##ParameterArguments##); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventConfigPlayMethodWithParameters.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 01d00435ae4ef734bbc2016a51c46279 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventField.cs.txt: -------------------------------------------------------------------------------- 1 | ##Attributes## 2 | public static readonly ##EventName##Config ##EventName## = new ##EventName##Config("##GUID##"); 3 | 4 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventField.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 062ef2aacc2d8e349bce13d62a4ec33c 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventFolder.cs.txt: -------------------------------------------------------------------------------- 1 | public sealed partial class ##FolderName####BaseType## 2 | { 3 | ##Subfolders## 4 | ##EventTypes## 5 | ##EventTypeAliases## 6 | ##Events## 7 | ##EventAliases## 8 | } 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventFolder.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d9f4d72b6dbc00341a6243e35ba786ac 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventParameter.cs.txt: -------------------------------------------------------------------------------- 1 | ##ParameterEnum## 2 | public readonly ##ParameterType## ##ParameterName## = new ##ParameterType##(new PARAMETER_ID { data1 = ##ID1##, data2 = ##ID2## }, false); 3 | 4 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventParameter.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d10121bab27a9d74e87ce1baac4bd3ff 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventParametersInitialization.cs.txt: -------------------------------------------------------------------------------- 1 | protected override void InitializeParameters() 2 | { 3 | base.InitializeParameters(); 4 | 5 | ##EventParametersInitialization## 6 | } 7 | 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventParametersInitialization.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f92cedd8d9d9d8546afff76b27155f7e 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventPlaybackPlayMethodWithParameters.cs.txt: -------------------------------------------------------------------------------- 1 | public void Play(EventDescription eventDescription, Transform source, ##ParameterArgumentsWithType##) 2 | { 3 | Play(eventDescription, source); 4 | 5 | ##ParameterInitializationsFromArguments## 6 | } 7 | 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventPlaybackPlayMethodWithParameters.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 467afe0517051a74c84ede82e3de9d3a 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventType.cs.txt: -------------------------------------------------------------------------------- 1 | public sealed class ##EventName##Config : FmodAudioConfig<##EventName##Playback> 2 | { 3 | public ##EventName##Config(string guid) : base(guid) { } 4 | 5 | public override ##EventName##Playback Play(Transform source = null) 6 | { 7 | ##EventName##Playback instance = new ##EventName##Playback(); 8 | instance.Play(EventDescription, source); 9 | return instance; 10 | } 11 | ##ConfigPlayMethodWithParameters## 12 | } 13 | ##Attributes## 14 | public sealed class ##EventName##Playback : FmodAudioPlayback 15 | { 16 | ##EventParameters## 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventType.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a43b1a605d41e2149a82070129407bc4 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventTypes.cs.txt: -------------------------------------------------------------------------------- 1 | ##UsingDirectives## 2 | 3 | namespace ##Namespace## 4 | { 5 | /// 6 | /// GENERATED: Contains definition of FMOD events which is separated out from the declaration for readability. 7 | /// To get an overview of which folders and events exist see 'FmodEvents.cs' 8 | /// 9 | ##Events## 10 | } 11 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEventTypes.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0d3dedbaef1b8d547b1dae92da4d6099 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEvents.cs.txt: -------------------------------------------------------------------------------- 1 | ##UsingDirectives## 2 | 3 | namespace ##Namespace## 4 | { 5 | /// 6 | /// GENERATED: Contains FMOD event configs so they can be called in a strongly-typed manner to produce instances. 7 | /// For readability's sake we only define the folders and fields in this file so you can easily get an overview of 8 | /// the events that exist. The definitions of the classes in question can be found in 'FmodEventTypes.cs' 9 | /// 10 | ##Events## 11 | 12 | [Preserve] 13 | public static class AudioParameterlessEvents 14 | { 15 | public static readonly Dictionary EventsByGuid = 16 | new Dictionary 17 | { 18 | ##ParameterlessEventIds## 19 | }; 20 | } 21 | 22 | /* ---------------------------------------------- METADATA ------------------------------------------------------ 23 | ##MetaData## 24 | ------------------------------------------------- METADATA --------------------------------------------------- */ 25 | } 26 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodEvents.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 653d81bbd1daae54eaf97bbc9eafd6fb 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodGlobalParameter.cs.txt: -------------------------------------------------------------------------------- 1 | ##ParameterEnum## 2 | public static readonly ##ParameterType## ##ParameterName## = new ##ParameterType##(new PARAMETER_ID { data1 = ##ID1##, data2 = ##ID2## }, true); 3 | 4 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodGlobalParameter.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ac439fa414cd474a96c63fbb7f5ec73 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodGlobalParameters.cs.txt: -------------------------------------------------------------------------------- 1 | ##UsingDirectives## 2 | 3 | namespace ##Namespace## 4 | { 5 | /// 6 | /// GENERATED: Contains FMOD global parameters so they can be called in a strongly-typed manner. 7 | /// 8 | public static class AudioGlobalParameters 9 | { 10 | ##GlobalParameters## 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Events/FmodGlobalParameters.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 00e848c62e78c384fb4abbc0030c2fad 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/FMOD-Syntax.asmdef.txt: -------------------------------------------------------------------------------- 1 | { 2 | "name": "##Name##", 3 | "rootNamespace": "##Namespace##", 4 | "references": [ 5 | "GUID:f28b76e6ba53ea24ca2daf55c1581312", 6 | "GUID:0c752da273b17c547ae705acf0f2adf2" 7 | ], 8 | "includePlatforms": [], 9 | "excludePlatforms": [], 10 | "allowUnsafeCode": false, 11 | "overrideReferences": false, 12 | "precompiledReferences": [], 13 | "autoReferenced": true, 14 | "defineConstraints": [], 15 | "versionDefines": [], 16 | "noEngineReferences": false 17 | } 18 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/FMOD-Syntax.asmdef.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc252742681cda740a3873db3abc60a9 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3f745a8789110df4bb572289e12f6cd4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshotFields.cs.txt: -------------------------------------------------------------------------------- 1 | public static readonly FmodSnapshotConfig ##SnapshotName## = new FmodSnapshotConfig("##GUID##"); 2 | 3 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshotFields.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d107c8fa0fb4db94aa93a7719cb16972 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshotType.cs.txt: -------------------------------------------------------------------------------- 1 | public sealed class ##EventName##Config : FmodSnapshotConfig<##EventName##Playback> 2 | { 3 | public ##EventName##Config(string guid) : base(guid) { } 4 | 5 | public override ##EventName##Playback Play() 6 | { 7 | ##EventName##Playback instance = new ##EventName##Playback(); 8 | instance.Play(EventDescription); 9 | return instance; 10 | } 11 | ##ConfigPlayMethodWithParameters## 12 | } 13 | ##Attributes## 14 | public sealed class ##EventName##Playback : FmodSnapshotPlayback 15 | { 16 | ##EventParameters## 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshotType.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4319304daf4bd3842be3426174e00b41 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshotTypes.cs.txt: -------------------------------------------------------------------------------- 1 | ##UsingDirectives## 2 | 3 | namespace ##Namespace## 4 | { 5 | /// 6 | /// GENERATED: Contains definition of FMOD snapshots which is separated out from the declaration for readability. 7 | /// To get an overview of which folders and events exist see 'FmodSnapshots.cs' 8 | /// 9 | ##Events## 10 | } 11 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshotTypes.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 14ee39780660f2d43ac1edc8a3ffecaa 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshots.cs.txt: -------------------------------------------------------------------------------- 1 | ##UsingDirectives## 2 | 3 | namespace ##Namespace## 4 | { 5 | /// 6 | /// GENERATED: Contains FMOD snapshot configs so they can be called in a strongly-typed manner to produce instances. 7 | /// For readability's sake we only define the folders and fields in this file so you can easily get an overview of 8 | /// the events that exist. The definitions of the classes in question can be found in 'FmodSnapshotTypes.cs' 9 | /// 10 | ##Events## 11 | 12 | /* ---------------------------------------------- METADATA ------------------------------------------------------ 13 | ##MetaData## 14 | ------------------------------------------------- METADATA --------------------------------------------------- */ 15 | } 16 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/Snapshots/FmodSnapshots.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 78a1bcab036d63a4c841b79a54119bb9 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/VCAs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1ad4bf92acaa4a429e524795017102e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/VCAs/FmodVCAField.cs.txt: -------------------------------------------------------------------------------- 1 | public static readonly VCA ##VCAName## = new VCA("##VCAPath##"); 2 | 3 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/VCAs/FmodVCAField.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f549756ff96b2c741898d8afbc821adf 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/VCAs/FmodVCAs.cs.txt: -------------------------------------------------------------------------------- 1 | using RoyTheunissen.FMODSyntax; 2 | 3 | namespace ##Namespace## 4 | { 5 | /// 6 | /// GENERATED: Contains FMOD VCAs so they can be accessed in a strongly-typed manner. 7 | /// 8 | public static class AudioVCAs 9 | { 10 | ##VCAs## 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Editor/Resources/Templates/Fmod/VCAs/FmodVCAs.cs.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7760576ded9f70347abb205d8bcadfbe 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/RoyTheunissen.FMODSyntax.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RoyTheunissen.FMODSyntax.Editor", 3 | "rootNamespace": "RoyTheunissen.FMODSyntax", 4 | "references": [ 5 | "GUID:f28b76e6ba53ea24ca2daf55c1581312", 6 | "GUID:0c752da273b17c547ae705acf0f2adf2", 7 | "GUID:aab3caaf43456d6449a3e035348ff798" 8 | ], 9 | "includePlatforms": [ 10 | "Editor" 11 | ], 12 | "excludePlatforms": [], 13 | "allowUnsafeCode": false, 14 | "overrideReferences": false, 15 | "precompiledReferences": [], 16 | "autoReferenced": true, 17 | "defineConstraints": [], 18 | "versionDefines": [ 19 | { 20 | "name": "com.brunomikoski.scriptableobjectcollection", 21 | "expression": "2.2.1", 22 | "define": "SCRIPTABLE_OBJECT_COLLECTION" 23 | } 24 | ], 25 | "noEngineReferences": false 26 | } -------------------------------------------------------------------------------- /Editor/RoyTheunissen.FMODSyntax.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 908a1ca5dca0e3a428aedca6231395e4 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/SetupWizard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEditor; 4 | using System.IO; 5 | 6 | namespace RoyTheunissen.FMODSyntax 7 | { 8 | /// 9 | /// Window to show to users when they haven't set up the FMOD Syntax system yet to let them conveniently 10 | /// initialize it with appropriate settings. 11 | /// 12 | public class SetupWizard : EditorWindow 13 | { 14 | private string settingsFolderPath = string.Empty; 15 | private string generatedScriptsFolderPath = "Generated/Scripts/FMOD"; 16 | 17 | private string namespaceForGeneratedCode; 18 | private bool shouldGenerateAssemblyDefinition = true; 19 | 20 | [NonSerialized] private GUIContent cachedFolderIcon; 21 | [NonSerialized] private bool didCacheFolderIcon; 22 | private GUIContent FolderIcon 23 | { 24 | get 25 | { 26 | if (!didCacheFolderIcon) 27 | { 28 | didCacheFolderIcon = true; 29 | string path = "Folder Icon"; 30 | if (EditorGUIUtility.isProSkin) 31 | path = "d_" + path; 32 | 33 | cachedFolderIcon = EditorGUIUtility.IconContent(path, string.Empty); 34 | } 35 | return cachedFolderIcon; 36 | } 37 | } 38 | 39 | [InitializeOnLoadMethod] 40 | private static void Initialize() 41 | { 42 | string[] settingsAsset = AssetDatabase.FindAssets($"t:{nameof(FmodSyntaxSettings)}"); 43 | if (settingsAsset.Length > 0) 44 | return; 45 | 46 | EditorApplication.delayCall += () => 47 | { 48 | SetupWizard setupWizard = GetWindow(true, "FMOD Syntax Setup Wizard"); 49 | setupWizard.minSize = setupWizard.maxSize = new Vector2(500, 270); 50 | setupWizard.namespaceForGeneratedCode = 51 | $"{SanitizeNamespace(Application.companyName)}.{SanitizeNamespace(Application.productName)}.FMOD"; 52 | }; 53 | } 54 | 55 | private static string SanitizeNamespace(string @namespace) 56 | { 57 | return FmodSyntaxUtilities.Filter(@namespace, false); 58 | } 59 | 60 | private void OnGUI() 61 | { 62 | EditorGUILayout.BeginHorizontal(); 63 | EditorGUILayout.Space(); 64 | EditorGUILayout.BeginVertical(); 65 | 66 | EditorGUILayout.Space(); 67 | 68 | EditorGUILayout.LabelField("Welcome! You've installed the FMOD Syntax package."); 69 | 70 | EditorGUILayout.Space(); 71 | EditorGUILayout.LabelField("Let's initialize the system with some default settings."); 72 | EditorGUILayout.Space(); 73 | 74 | EditorGUILayout.BeginVertical("Box"); 75 | EditorGUILayout.LabelField("Folders", EditorStyles.boldLabel); 76 | 77 | settingsFolderPath = DrawFolderPathField(settingsFolderPath, "Settings Config", 78 | "Where to create the config file that has all the settings in it."); 79 | 80 | generatedScriptsFolderPath = DrawFolderPathField(generatedScriptsFolderPath, "Generated Scripts", 81 | "Where to place the generated scripts for events and such."); 82 | 83 | EditorGUILayout.Space(); 84 | 85 | EditorGUILayout.LabelField("Code Formatting", EditorStyles.boldLabel); 86 | namespaceForGeneratedCode = EditorGUILayout.TextField( 87 | new GUIContent("Namespace", "The namespace to use for all generated code."), namespaceForGeneratedCode); 88 | 89 | GUIContent generateAsmdefLabel = new GUIContent( 90 | "Make Assembly Definition", 91 | "Whether to generate an assembly definition file for the generated FMOD code. " + 92 | "If the code is to be generated within one central runtime assembly definition for your game, " + 93 | "you may want to turn this off."); 94 | shouldGenerateAssemblyDefinition = EditorGUILayout.Toggle( 95 | generateAsmdefLabel, shouldGenerateAssemblyDefinition); 96 | 97 | EditorGUILayout.Space(); 98 | EditorGUILayout.EndVertical(); 99 | 100 | EditorGUILayout.Space(); 101 | 102 | bool shouldInitialize = GUILayout.Button("Initialize", GUILayout.Height(40)); 103 | if (shouldInitialize) 104 | InitializeFmodSyntaxSystem(); 105 | 106 | EditorGUILayout.Space(); 107 | 108 | EditorGUILayout.EndVertical(); 109 | 110 | EditorGUILayout.Space(); 111 | 112 | EditorGUILayout.EndHorizontal(); 113 | } 114 | 115 | private void InitializeFmodSyntaxSystem() 116 | { 117 | FmodSyntaxSettings settings = CreateScriptableObject( 118 | settingsFolderPath, nameof(FmodSyntaxSettings)); 119 | 120 | settings.InitializeFromWizard( 121 | generatedScriptsFolderPath, namespaceForGeneratedCode, shouldGenerateAssemblyDefinition); 122 | 123 | EditorUtility.SetDirty(settings); 124 | AssetDatabase.SaveAssets(); 125 | 126 | Close(); 127 | } 128 | 129 | private static void EnsureFolderExists(string path) 130 | { 131 | string absolutePath = Path.Combine(Application.dataPath, path); 132 | 133 | if (Directory.Exists(absolutePath)) 134 | return; 135 | 136 | Directory.CreateDirectory(absolutePath); 137 | AssetDatabase.Refresh(); 138 | } 139 | 140 | private static Type CreateScriptableObject(string path, string name) 141 | where Type : ScriptableObject 142 | { 143 | ScriptableObject scriptableObject = CreateInstance(typeof(Type)); 144 | scriptableObject.name = name; 145 | 146 | EnsureFolderExists(path); 147 | 148 | path = Path.Combine(path, name + ".asset"); 149 | AssetDatabase.CreateAsset(scriptableObject, Path.Combine("Assets", path)); 150 | 151 | return (Type)scriptableObject; 152 | } 153 | 154 | private string DrawFolderPathField(string currentPath, string label, string tooltip = null) 155 | { 156 | EditorGUILayout.BeginHorizontal(); 157 | EditorGUILayout.LabelField(new GUIContent(label, tooltip), GUILayout.Width(EditorGUIUtility.labelWidth)); 158 | currentPath = EditorGUILayout.TextField(currentPath); 159 | float height = EditorGUIUtility.singleLineHeight; 160 | bool shouldPick = GUILayout.Button(FolderIcon, GUILayout.Width(1.5f * height), GUILayout.Height(height)); 161 | if (shouldPick) 162 | { 163 | string selectedFolder = EditorUtility.OpenFolderPanel( 164 | $"Pick {label} Folder", Path.Combine(Application.dataPath, currentPath), string.Empty); 165 | if (!string.IsNullOrEmpty(selectedFolder) && selectedFolder.StartsWith(Application.dataPath)) 166 | { 167 | selectedFolder = selectedFolder.Substring(Application.dataPath.Length); 168 | 169 | if (selectedFolder.StartsWith(Path.DirectorySeparatorChar) || 170 | selectedFolder.StartsWith(Path.AltDirectorySeparatorChar)) 171 | { 172 | selectedFolder = selectedFolder.Substring(1); 173 | } 174 | 175 | currentPath = selectedFolder; 176 | 177 | EditorApplication.delayCall += Repaint; 178 | } 179 | } 180 | 181 | EditorGUILayout.EndHorizontal(); 182 | 183 | return currentPath; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /Editor/SetupWizard.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8d24ea029abd0f24e9a188f63fe7fc12 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/SnapshotReferenceDropdown.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEditor.IMGUI.Controls; 3 | using FMODUnity; 4 | using System.Linq; 5 | 6 | namespace RoyTheunissen.FMODSyntax 7 | { 8 | /// 9 | /// Dropdown to pick snapshot references from a list in a searchable way / with subfolders. 10 | /// 11 | public sealed class SnapshotReferenceDropdown : AdvancedDropdown 12 | { 13 | private readonly SerializedProperty serializedProperty; 14 | 15 | public SnapshotReferenceDropdown(AdvancedDropdownState state, SerializedProperty serializedProperty) 16 | : base(state) 17 | { 18 | this.serializedProperty = serializedProperty; 19 | } 20 | 21 | protected override AdvancedDropdownItem BuildRoot() 22 | { 23 | SnapshotConfigDropdownItem root = new SnapshotConfigDropdownItem( 24 | this, string.Empty, "Snapshots", string.Empty); 25 | 26 | EditorEventRef[] snapshots = EventManager.Events 27 | .Where(e => e.Path.StartsWith(EditorEventRefExtensions.SnapshotPrefix)) 28 | .OrderBy(e => e.Path) 29 | .ToArray(); 30 | string[] paths = new string[snapshots.Length]; 31 | string[] guids = new string[snapshots.Length]; 32 | 33 | root.AddChildByPath("None", "None"); 34 | 35 | for (int i = 0; i < snapshots.Length; i++) 36 | { 37 | // Remove the event prefix otherwise it gets treated as a folder. 38 | string path = snapshots[i].Path; 39 | path = path.RemovePrefix(EditorEventRefExtensions.SnapshotPrefix); 40 | paths[i] = path; 41 | 42 | guids[i] = snapshots[i].Guid.ToString(); 43 | } 44 | 45 | for (int i = 0; i < paths.Length; i++) 46 | { 47 | root.AddChildByPath(guids[i], paths[i]); 48 | } 49 | 50 | return root; 51 | } 52 | 53 | protected override void ItemSelected(AdvancedDropdownItem item) 54 | { 55 | base.ItemSelected(item); 56 | 57 | SnapshotConfigDropdownItem dropdownItem = (SnapshotConfigDropdownItem)item; 58 | 59 | serializedProperty.serializedObject.Update(); 60 | serializedProperty.stringValue = dropdownItem.Guid == "None" ? string.Empty : dropdownItem.Guid; 61 | serializedProperty.serializedObject.ApplyModifiedProperties(); 62 | } 63 | } 64 | 65 | public sealed class SnapshotConfigDropdownItem : AdvancedDropdownItem 66 | { 67 | private const char Separator = '/'; 68 | 69 | private readonly SnapshotReferenceDropdown dropdown; 70 | 71 | private string path; 72 | 73 | private string guid; 74 | public string Guid => guid; 75 | 76 | public SnapshotConfigDropdownItem( 77 | SnapshotReferenceDropdown dropdown, string guid, string name, string path) : base(name) 78 | { 79 | this.dropdown = dropdown; 80 | this.guid = guid; 81 | this.path = path; 82 | } 83 | 84 | private SnapshotConfigDropdownItem AddChild(string guid, string name) 85 | { 86 | string newPath = string.IsNullOrEmpty(path) ? name : path + Separator + name; 87 | 88 | SnapshotConfigDropdownItem child = new SnapshotConfigDropdownItem(dropdown, guid, name, newPath); 89 | 90 | AddChild(child); 91 | 92 | return child; 93 | } 94 | 95 | private SnapshotConfigDropdownItem GetOrCreateChild(string guid, string name) 96 | { 97 | // Find a child with the specified name. 98 | foreach (AdvancedDropdownItem child in children) 99 | { 100 | if (child.name == name) 101 | return (SnapshotConfigDropdownItem)child; 102 | } 103 | 104 | // Add a new one if it didn't exist yet. 105 | return AddChild(guid, name); 106 | } 107 | 108 | public void AddChildByPath(string guid, string relativePath) 109 | { 110 | // Leaf node, add the event there. 111 | if (!relativePath.Contains(Separator)) 112 | { 113 | AddChild(guid, relativePath); 114 | return; 115 | } 116 | 117 | string[] sections = relativePath.Split(Separator); 118 | 119 | // Ensure a child exists for the first section of the path. 120 | // This acts as a folder and and does not have a key itself. 121 | SnapshotConfigDropdownItem firstSection = GetOrCreateChild(string.Empty, sections[0]); 122 | 123 | // Determine the path relative to this first child. 124 | string pathRemaining = string.Empty; 125 | for (int i = 1; i < sections.Length; i++) 126 | { 127 | pathRemaining += sections[i]; 128 | 129 | if (i < sections.Length - 1) 130 | pathRemaining += Separator; 131 | } 132 | 133 | // Continue recursively from the first section. 134 | firstSection.AddChildByPath(guid, pathRemaining); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Editor/SnapshotReferenceDropdown.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a6143441a05599143be8bb6880fb7d14 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/SnapshotReferencePropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using FMODUnity; 2 | using UnityEditor; 3 | using UnityEditor.IMGUI.Controls; 4 | using UnityEngine; 5 | using GUID = FMOD.GUID; 6 | 7 | namespace RoyTheunissen.FMODSyntax 8 | { 9 | /// 10 | /// Draws dropdowns for selecting a snapshot config. 11 | /// 12 | [CustomPropertyDrawer(typeof(SnapshotReference))] 13 | public class SnapshotReferencePropertyDrawer : PropertyDrawer 14 | { 15 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 16 | { 17 | // If this is an array, it would just show "Element 0" as the label which takes up a lot of space and is 18 | // useless. Even more useless is that it actually sees that the first property of AudioReference is a string 19 | // and shows that as the name instead for an array. That just means that it shows the GUID as the label. 20 | // HOW USEFUL. How about we don't show a label at all and just have a wider field that's easier to read? 21 | if (property.IsInArray()) 22 | label = GUIContent.none; 23 | 24 | SerializedProperty snapshotConfigProperty = property.FindPropertyRelative("fmodSnapshotGuid"); 25 | 26 | // Figure out the display text and the content property. 27 | string displayedText; 28 | string guid = snapshotConfigProperty.stringValue; 29 | if (string.IsNullOrEmpty(guid)) 30 | { 31 | displayedText = string.Empty; 32 | } 33 | else 34 | { 35 | GUID id = GUID.Parse(guid); 36 | EditorEventRef eventRef = EventManager.EventFromGUID(id); 37 | displayedText = eventRef == null ? string.Empty : eventRef.GetDisplayName(); 38 | } 39 | 40 | // Draw a dropdown button to select the snapshot config. 41 | EditorGUI.BeginProperty(position, label, snapshotConfigProperty); 42 | Rect valueRect = EditorGUI.PrefixLabel(position, label); 43 | bool didPress = EditorGUI.DropdownButton( 44 | valueRect, new GUIContent(displayedText), FocusType.Keyboard); 45 | if (didPress) 46 | { 47 | Rect dropDownRect = position; 48 | dropDownRect.xMax += 200; 49 | 50 | SnapshotReferenceDropdown menu = new SnapshotReferenceDropdown( 51 | new AdvancedDropdownState(), snapshotConfigProperty); 52 | menu.Show(dropDownRect); 53 | } 54 | EditorGUI.EndProperty(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Editor/SnapshotReferencePropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 041e64241708b274b9aa64dfd2f28da3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Utilities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27cdfa7b0bb6fdd44855b024da538980 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Utilities/EditorAssetReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using Object = UnityEngine.Object; 4 | 5 | namespace RoyTheunissen.FMODSyntax 6 | { 7 | /// 8 | /// Helps you access an asset at editor time. Useful for finding code templates, for example. 9 | /// 10 | public class EditorAssetReference where T : Object 11 | { 12 | [NonSerialized] private T cachedAsset; 13 | public T Asset 14 | { 15 | get 16 | { 17 | if (cachedAsset == null) 18 | cachedAsset = Resources.Load(path); 19 | return cachedAsset; 20 | } 21 | } 22 | 23 | [NonSerialized] private readonly string path; 24 | public string Path => path; 25 | 26 | public EditorAssetReference(string path) 27 | { 28 | this.path = path; 29 | } 30 | 31 | public static implicit operator T(EditorAssetReference editorAssetReference) 32 | { 33 | return editorAssetReference.Asset; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Editor/Utilities/EditorAssetReference.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ada72029bb66614d96e6b6231018552 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Utilities/EditorPreference.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Object = UnityEngine.Object; 3 | 4 | #if !UNITY_EDITOR 5 | using System; 6 | #endif // UNITY_EDITOR 7 | 8 | namespace RoyTheunissen.FMODSyntax 9 | { 10 | /// 11 | /// Utility class to simplify the workflow of using EditorPrefs-serialized fields. 12 | /// 13 | /// Now you can specify a field that is to be serialized in EditorPrefs as follows: 14 | /// 15 | /// private EditorPreferenceBool skipIntro = new EditorPreferenceBool("ProjectName/Skip Intro"); 16 | /// 17 | /// then you can get or set its value as follows: 18 | /// skipIntro.Value 19 | /// 20 | /// You can also draw the default editor field associated with it like so: 21 | /// skipIntro.DrawGUILayout(); 22 | /// 23 | public abstract class EditorPreference 24 | { 25 | #if !UNITY_EDITOR 26 | protected const string EditorOnlyExceptionMessage = "Can only be called in the Editor."; 27 | #endif // !UNITY_EDITOR 28 | 29 | private string path; 30 | public string Path => IsProjectSpecific ? ProjectPrefix + path : path; 31 | 32 | private bool isProjectSpecific = true; 33 | public bool IsProjectSpecific 34 | { 35 | get => isProjectSpecific; 36 | set => isProjectSpecific = value; 37 | } 38 | 39 | private static string cachedProjectPrefix; 40 | 41 | private static string ProjectPrefix 42 | { 43 | get 44 | { 45 | if (cachedProjectPrefix == null) 46 | { 47 | string assetsFolder = Application.dataPath; 48 | string projectsFolder = assetsFolder.Substring(0, assetsFolder.Length - "/Assets".Length); 49 | 50 | // NOTE: If you only want the name of the project checkout, you can do that here. That would 51 | // conflate save data from checkouts in different drives/parent folders with the same name though, 52 | // so I'm choosing to include the path up until the project name too. 53 | //string projectName = System.IO.Path.GetFileName(projectsFolder); 54 | 55 | // Prefix ends up something like C:/Git/YourProjectName/ so it's unique to your checkout. 56 | cachedProjectPrefix = projectsFolder + "/"; 57 | } 58 | return cachedProjectPrefix; 59 | } 60 | } 61 | 62 | private GUIContent label; 63 | protected GUIContent Label => label; 64 | 65 | public abstract object ObjectValue { get; } 66 | 67 | protected EditorPreference(string path) 68 | { 69 | this.path = path; 70 | 71 | string name = System.IO.Path.GetFileName(path).ToHumanReadable(); 72 | label = new GUIContent(name); 73 | } 74 | 75 | public abstract void DrawGUILayout(GUIContent label, params GUILayoutOption[] options); 76 | 77 | public void DrawGUILayout(params GUILayoutOption[] layoutOptions) 78 | { 79 | DrawGUILayout(Label, layoutOptions); 80 | } 81 | 82 | public void DrawGUILayout(string label, params GUILayoutOption[] options) 83 | { 84 | DrawGUILayout(new GUIContent(label), options); 85 | } 86 | 87 | public abstract void DrawGUI(Rect position, GUIContent label); 88 | 89 | public void DrawGUI(Rect position) 90 | { 91 | DrawGUI(position, Label); 92 | } 93 | 94 | public void DrawGUI(Rect position, string label) 95 | { 96 | DrawGUI(position, new GUIContent(label)); 97 | } 98 | } 99 | 100 | public abstract class EditorPreferenceGeneric 101 | : EditorPreference 102 | { 103 | public virtual ValueType Value 104 | { 105 | get => ValueRaw; 106 | set => ValueRaw = value; 107 | } 108 | 109 | protected ValueType ValueRaw 110 | { 111 | get 112 | { 113 | #if !UNITY_EDITOR 114 | return ValueRuntime; 115 | #else 116 | return !UnityEditor.EditorPrefs.HasKey(Path) ? defaultValue : UnityPrefsValue; 117 | #endif // UNITY_EDITOR 118 | } 119 | set 120 | { 121 | #if !UNITY_EDITOR 122 | // No need to set anything because we can't access the unity prefs anyway. 123 | #else 124 | UnityPrefsValue = value; 125 | #endif // UNITY_EDITOR 126 | } 127 | } 128 | 129 | protected virtual ValueType ValueRuntime => defaultValue; 130 | 131 | public override object ObjectValue => Value; 132 | 133 | protected abstract ValueType UnityPrefsValue { get; set; } 134 | 135 | private ValueType defaultValue; 136 | 137 | protected EditorPreferenceGeneric(string path, ValueType defaultValue = default(ValueType)) : base(path) 138 | { 139 | this.defaultValue = defaultValue; 140 | } 141 | } 142 | 143 | public class EditorPreferenceBool : EditorPreferenceGeneric 144 | { 145 | protected override bool UnityPrefsValue 146 | { 147 | get 148 | { 149 | #if UNITY_EDITOR 150 | return UnityEditor.EditorPrefs.GetBool(Path); 151 | #else 152 | throw new Exception(EditorOnlyExceptionMessage); 153 | #endif // !UNITY_EDITOR 154 | } 155 | set 156 | { 157 | #if UNITY_EDITOR 158 | UnityEditor.EditorPrefs.SetBool(Path, value); 159 | #else 160 | throw new Exception(EditorOnlyExceptionMessage); 161 | #endif // !UNITY_EDITOR 162 | } 163 | } 164 | 165 | public EditorPreferenceBool(string path, bool defaultValue = default(bool)) : base(path, defaultValue) 166 | { 167 | } 168 | 169 | public override void DrawGUILayout(GUIContent label, params GUILayoutOption[] options) 170 | { 171 | #if UNITY_EDITOR 172 | Value = UnityEditor.EditorGUILayout.Toggle(label, Value, options); 173 | #endif // UNITY_EDITOR 174 | } 175 | 176 | public void DrawGUILayoutLeft(params GUILayoutOption[] options) 177 | { 178 | DrawGUILayoutLeft(Label, options); 179 | } 180 | 181 | public void DrawGUILayoutLeft(GUIContent label, params GUILayoutOption[] options) 182 | { 183 | #if UNITY_EDITOR 184 | Value = UnityEditor.EditorGUILayout.ToggleLeft(label, Value, options); 185 | #endif // UNITY_EDITOR 186 | } 187 | 188 | public void DrawGUILayoutLeft(string label, params GUILayoutOption[] options) 189 | { 190 | DrawGUILayoutLeft(new GUIContent(label), options); 191 | } 192 | 193 | public override void DrawGUI(Rect position, GUIContent label) 194 | { 195 | #if UNITY_EDITOR 196 | Value = UnityEditor.EditorGUI.Toggle(position, label, Value); 197 | #endif // UNITY_EDITOR 198 | } 199 | 200 | public void DrawGUILeft(Rect position) 201 | { 202 | DrawGUILeft(position, Label); 203 | } 204 | 205 | public void DrawGUILeft(Rect position, GUIContent label) 206 | { 207 | #if UNITY_EDITOR 208 | Value = UnityEditor.EditorGUI.ToggleLeft(position, label, Value); 209 | #endif // UNITY_EDITOR 210 | } 211 | 212 | public void DrawGUILeft(Rect position, string label) 213 | { 214 | DrawGUILeft(position, new GUIContent(label)); 215 | } 216 | } 217 | 218 | public class EditorPreferenceString : EditorPreferenceGeneric 219 | { 220 | protected override string UnityPrefsValue 221 | { 222 | get 223 | { 224 | #if UNITY_EDITOR 225 | return UnityEditor.EditorPrefs.GetString(Path); 226 | #else 227 | throw new Exception(EditorOnlyExceptionMessage); 228 | #endif // !UNITY_EDITOR 229 | } 230 | set 231 | { 232 | #if UNITY_EDITOR 233 | UnityEditor.EditorPrefs.SetString(Path, value); 234 | #else 235 | throw new Exception(EditorOnlyExceptionMessage); 236 | #endif // !UNITY_EDITOR 237 | } 238 | } 239 | 240 | public EditorPreferenceString(string path, string defaultValue = default(string)) : base(path, defaultValue) 241 | { 242 | } 243 | 244 | public override void DrawGUILayout(GUIContent label, params GUILayoutOption[] options) 245 | { 246 | #if UNITY_EDITOR 247 | Value = UnityEditor.EditorGUILayout.TextField(label, Value, options); 248 | #endif // UNITY_EDITOR 249 | } 250 | 251 | public override void DrawGUI(Rect position, GUIContent label) 252 | { 253 | #if UNITY_EDITOR 254 | Value = UnityEditor.EditorGUI.TextField(position, label, Value); 255 | #endif // UNITY_EDITOR 256 | } 257 | } 258 | 259 | public class EditorPreferenceInt : EditorPreferenceGeneric 260 | { 261 | protected override int UnityPrefsValue 262 | { 263 | get 264 | { 265 | #if UNITY_EDITOR 266 | return UnityEditor.EditorPrefs.GetInt(Path); 267 | #else 268 | throw new Exception(EditorOnlyExceptionMessage); 269 | #endif // !UNITY_EDITOR 270 | } 271 | set 272 | { 273 | #if UNITY_EDITOR 274 | UnityEditor.EditorPrefs.SetInt(Path, value); 275 | #else 276 | throw new Exception(EditorOnlyExceptionMessage); 277 | #endif // !UNITY_EDITOR 278 | } 279 | } 280 | 281 | public EditorPreferenceInt(string path, int defaultValue = default(int)) : base(path, defaultValue) 282 | { 283 | } 284 | 285 | public override void DrawGUILayout(GUIContent label, params GUILayoutOption[] options) 286 | { 287 | #if UNITY_EDITOR 288 | Value = UnityEditor.EditorGUILayout.IntField(label, Value, options); 289 | #endif // UNITY_EDITOR 290 | } 291 | 292 | public override void DrawGUI(Rect position, GUIContent label) 293 | { 294 | #if UNITY_EDITOR 295 | Value = UnityEditor.EditorGUI.IntField(position, label, Value); 296 | #endif // UNITY_EDITOR 297 | } 298 | } 299 | 300 | public class EditorPreferenceFloat : EditorPreferenceGeneric 301 | { 302 | protected override float UnityPrefsValue 303 | { 304 | get 305 | { 306 | #if UNITY_EDITOR 307 | return UnityEditor.EditorPrefs.GetFloat(Path); 308 | #else 309 | throw new Exception(EditorOnlyExceptionMessage); 310 | #endif // !UNITY_EDITOR 311 | } 312 | set 313 | { 314 | #if UNITY_EDITOR 315 | UnityEditor.EditorPrefs.SetFloat(Path, value); 316 | #else 317 | throw new Exception(EditorOnlyExceptionMessage); 318 | #endif // !UNITY_EDITOR 319 | } 320 | } 321 | 322 | public EditorPreferenceFloat(string path, float defaultValue = default(float)) : base(path, defaultValue) 323 | { 324 | } 325 | 326 | public override void DrawGUILayout(GUIContent label, params GUILayoutOption[] options) 327 | { 328 | #if UNITY_EDITOR 329 | Value = UnityEditor.EditorGUILayout.FloatField(label, Value, options); 330 | #endif // UNITY_EDITOR 331 | } 332 | 333 | public override void DrawGUI(Rect position, GUIContent label) 334 | { 335 | #if UNITY_EDITOR 336 | Value = UnityEditor.EditorGUI.FloatField(position, label, Value); 337 | #endif // UNITY_EDITOR 338 | } 339 | } 340 | 341 | public class EditorPreferenceObject : EditorPreferenceGeneric 342 | where T : Object 343 | { 344 | private T cachedAsset; 345 | 346 | protected override T UnityPrefsValue 347 | { 348 | get 349 | { 350 | #if UNITY_EDITOR 351 | if (cachedAsset == null) 352 | { 353 | string assetPath = UnityEditor.EditorPrefs.GetString(Path); 354 | 355 | if (string.IsNullOrEmpty(assetPath)) 356 | return null; 357 | 358 | cachedAsset = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); 359 | } 360 | 361 | return cachedAsset; 362 | #else 363 | throw new Exception(EditorOnlyExceptionMessage); 364 | #endif // !UNITY_EDITOR 365 | } 366 | set 367 | { 368 | #if UNITY_EDITOR 369 | T previousValue = UnityPrefsValue; 370 | if (previousValue != value) 371 | { 372 | // Store the new asset's path. 373 | string path = value == null ? null : UnityEditor.AssetDatabase.GetAssetPath(value); 374 | UnityEditor.EditorPrefs.SetString(Path, path); 375 | 376 | // Invalidate the cached asset. 377 | cachedAsset = null; 378 | } 379 | #else 380 | throw new Exception(EditorOnlyExceptionMessage); 381 | #endif // !UNITY_EDITOR 382 | } 383 | } 384 | 385 | public EditorPreferenceObject(string path, T defaultValue = default(T)) : base(path, defaultValue) 386 | { 387 | } 388 | 389 | public override void DrawGUILayout(GUIContent label, params GUILayoutOption[] options) 390 | { 391 | #if UNITY_EDITOR 392 | Value = (T)UnityEditor.EditorGUILayout.ObjectField(label, Value, typeof(T), false, options); 393 | #endif // UNITY_EDITOR 394 | } 395 | 396 | public override void DrawGUI(Rect position, GUIContent label) 397 | { 398 | #if UNITY_EDITOR 399 | Value = (T)UnityEditor.EditorGUI.ObjectField(position, label, Value, typeof(T), false); 400 | #endif // UNITY_EDITOR 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /Editor/Utilities/EditorPreference.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 59c760fd241e131429d20eba823e8f8c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Roy Theunissen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8fd535d4937796b4da4b1648c12eed22 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Roy Theunissen](Documentation~/Github%20Header.jpg)](http://roytheunissen.com) 2 | [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](LICENSE.md) 3 | ![GitHub Follow](https://img.shields.io/github/followers/RoyTheunissen?label=RoyTheunissen&style=social) 4 | 5 | 6 | 7 | globe 8 | 9 | 10 | 11 | 12 | bluesky 13 | 14 | 15 | 16 | 17 | youtube 18 | 19 | 20 | 21 | 22 | tiktok 23 | 24 | 25 | _Generates code to allow invoking FMOD events with a strongly-typed syntax._ 26 | 27 | ## About the Project 28 | 29 | Out-of-the-box FMOD requires you to access events and parameters via inspector references or by name. 30 | Neither of these setups is ideal. 31 | 32 | Dispatching an event this way requires you to define a bunch of fields, assign things in the inspector, and then the syntax for setting parameters is weakly-typed. 33 | You can skip some of this by dispatching events/setting parameters by name but that has the drawback that you need to look up what the name is and it's very hard and error-prone to rename anything as it'll break references. 34 | 35 | For ease of use it's preferable if FMOD events and parameters are accessed in a strongly-typed way such that they're known at compile-time. Maybe something like this: 36 | 37 | ```cs 38 | AudioEvents.PlayerJump.Play(transform); 39 | ``` 40 | 41 | This would require a little bit of code generation, and that's where `FMOD-Syntax` comes in. With a simple setup wizard and one line of code in your audio service for culling expired events you can start dispatching FMOD events with parameters in as little as one line of code. 42 | 43 | ![Example](Documentation~/Generated%20Code%20Files.png) 44 | 45 | This setup allows events and parameters to be renamed gracefully as you can do it via your IDE, update your banks accordingly and then re-generate the code. If events are renamed in the banks and you still reference those events by their old names, warnings will be thrown to give you a chance to refactor without immediately getting compile errors. 46 | 47 | Overall this system significantly speeds up your audio implementation workflow and makes it more robust, at the expense of a little bit of boilerplate code that you won't even have to maintain yourself. 48 | 49 | ## Getting Started 50 | 51 | - Install the package to your Unity project 52 | - The Setup Wizard will pop up and allow you to specify where and how to create the settings file & save generated code files 53 | - Configure the system as desired and press Initialize 54 | - Use `FMOD > Generate FMOD Code` or `CTRL+ALT+G` to generate the FMOD code 55 | - Cull expired FMOD playback instances by calling `FmodSyntaxSystem.CullPlaybacks();` in an `Update` loop somewhere. I recommend putting this in your audio service. 56 | - You can now fire your FMOD events in a strongly typed way 57 | 58 | ## How to use 59 | 60 | ### One-offs 61 | ```cs 62 | // Parameterless one-off sound (global) 63 | AudioEvents.TestOneOff.Play(); 64 | 65 | // Parameterless one-off sound (spatialized) 66 | AudioEvents.TestOneOff.Play(transform); 67 | 68 | // Spatialized one-off with parameters 69 | AudioEvents.Footstep.Play(transform, FootstepPlayback.SurfaceValues.Generic); 70 | ``` 71 | 72 | ### Loops 73 | ```cs 74 | // Start a looping sound 75 | TestContinuousPlayback testContinuousPlayback = AudioEvents.TestContinuous.Play(transform); 76 | 77 | // Set the parameter on a looping sound 78 | float value = Mathf.Sin(Time.time * Mathf.PI * 1.0f).Map(-1, 1); 79 | testContinuousPlayback.Strength.Value = value; 80 | 81 | // Stop a looping sound 82 | testContinuousPlayback?.Stop(); 83 | 84 | // Cancel a looping sound in OnDestroy 85 | testContinuousPlayback?.Cleanup(); 86 | ``` 87 | 88 | ### Global Parameters 89 | ```cs 90 | // Setting global parameters 91 | AudioGlobalParameters.PlayerSpeed.Value = value; 92 | ``` 93 | 94 | ### Dispatching a parameterless event that is chosen via the inspector 95 | ```cs 96 | // Get a reference to the event that you can assign via the inspector 97 | [SerializeField] private AudioConfigReference parameterlessAudio; 98 | 99 | // Dispatch as per usual 100 | parameterlessAudio.Play(transform); 101 | ``` 102 | 103 | ### Labeled parameter enums 104 | In FMOD there are three types of parameters: "Continuous" (`int`), "Discrete" (`float`) and "Labeled" (`enum`). 105 | 106 | Labeled parameters are sent and received as integers, but they are associated with a name for convenience, exactly like enums in C#. 107 | 108 | ![image](https://github.com/RoyTheunissen/FMOD-Syntax/assets/3997055/280da27d-abde-4faf-b260-0a2591ea4d29) 109 | 110 | For convenience, FMOD Syntax generates an enum for every event with a labeled parameter, so auto-complete conveniently suggests all valid values when you are invoking the event. 111 | 112 | ```cs 113 | AudioEvents.Footstep.Play(transform, FootstepPlayback.SurfaceValues.Generic); 114 | ``` 115 | 116 | But let's say you have several events that share the same "Surface" parameter, for instance a Jump and a Footstep event. 117 | It could be inconvenient if you just want to determine the current surface type, and then fire jump and footstep events with that type, 118 | because you would have to cast it to the appropriate event-specific enum. 119 | 120 | ```cs 121 | AudioEvents.Footstep.Play(transform, FootstepPlayback.SurfaceValues.Generic); 122 | AudioEvents.Jump.Play(transform, JumpPlayback.SurfaceValues.Generic); 123 | ``` 124 | 125 | Furthermore, the enums in FMOD may actually represent an enum in your game. So it's inconvenient to have to map from that game enum to the FMOD enum. But there's a solution for that: **User-specified labeled parameter enums**. 126 | 127 | Simply tag your game enum with `[FmodLabelType]` and specify the names of the labeled parameters that it represents, and then when code is generated instead of generating event-specific enums, it uses the game enum you specified for those events. 128 | 129 | No more duplication, no more mapping. 130 | 131 | ```cs 132 | [FmodLabelType("Surface")] 133 | public enum SurfaceTypes 134 | { 135 | Generic, 136 | Dirt, 137 | Rock, 138 | } 139 | ``` 140 | ```cs 141 | AudioEvents.Footstep.Play(transform, SurfaceTypes.Generic); 142 | AudioEvents.Jump.Play(transform, SurfaceTypes.Generic); 143 | ``` 144 | #### Scriptable Object Collection Support 145 | 146 | The above 'Labeled parameter enums' feature now also works for the [Scriptable Object Collection](https://github.com/brunomikoski/ScriptableObjectCollection). Simply add the `[FmodLabelType]` attribute to the Scriptable Object Collection Item the same way you would with an enum. 147 | 148 | > [!IMPORTANT] 149 | > If you installed FMOD-Syntax or ScriptableObjectCollection to the `Assets` folder instead of via the Package Manager / in the Packages folder, a `SCRIPTABLE_OBJECT_COLLECTION` scripting define symbol will not be defined automatically and you will have to manually add this to the project settings for this feature to work._ 150 | 151 | 152 | 153 | ### Moving/renaming Events 154 | FMOD-Syntax has a two-tiered solution for allowing you to move/rename events in FMOD and update your code accordingly: 155 | - **Alias Generation** - When an event is detected as having been moved or renamed, 'aliases' are generated under the old name, tagged with an `[Obsolete]` attribute that informs you what the event is currently called. This causes the game to throw a compile warning everywhere that the old incorrect syntax is used, and you can copy/paste the correct name from the warning. This lets you manually migrate your event code without compile errors and with reminders for what the new syntax is. 156 | - **Auto-Refactoring** - When an event is detected has having been moved or renamed, you are also presented with the option to Auto-Refactor. If chosen, FMOD-Syntax will look through your .cs files, find any references to the old events and automatically refactor them to use the new event syntax. 157 | 158 | ### Syntax Formats 159 | FMOD-Syntax provides three syntax formats for defining events: 160 | - **Flat**: All events are defined in `AudioEvents`. Simplest / shortest syntax, but event names have to be unique.
161 | An event called `Player/Footstep` is invoked via `AudioEvents.Footstep.Play();` 162 | - **Flat (With Path Included In Name)**: Slightly longer names, but event names don't have to be unique. Requested by @AldeRoberge
163 | An event called `Player/Footstep` is invoked via `AudioEvents.Player_Footstep.Play();` 164 | - **Subclasses Per Folder**: Subclasses are generated inside the `AudioEvents` class for every folder holding their events. Longer syntax, but events are neatly organized and names don't have to be unique. 165 | An event called `Player/Footstep` is invoked via `AudioEvents.Player.Footstep.Play();` 166 | 167 | The default syntax is `Flat`, and you are recommended to use that and give unique names to your events. 168 | 169 | For example, you can use the following naming convention: `Object_Event`. That way you can have a footstep event for both a player and a monster without getting a name clash, and it doesn't matter if the event is in a folder called `Core/World1/Gameplay/Characters/Enemies/Monster/Monster_Footstep`, because including all those folders in the name is cumbersome. 170 | 171 | However, as you may be integrating FMOD-Syntax into an existing project and may not have the ability to affect the naming convention of events much, we recognize that different projects have different structures and you are free to choose whatever syntax suits your project best. 172 | 173 | Switching syntax formats supports all the same migration features as renaming or moving events. 174 | 175 | #### Miscellaneous Features 176 | 177 | - Code is also generated for playing Snapshots. Basically treated like events, though it's separated in its own `AudioSnapshots` class. 178 | - Code is also generated for Buses and VCA's, which can be accessed via an `AudioBuses` and `AudioVCAs` class respectively. 179 | 180 | ## Compatibility 181 | 182 | This system was developed for Unity 2021 and upwards, it's recommended that you use it for these versions. 183 | 184 | If you use an older version of Unity and are running into trouble, feel free to reach out and I'll see what I can do. 185 | 186 | ## Known Issues 187 | - There is a setup for automatically regenerating the code when the FMOD banks update, but this would require you to modify the FMOD Unity plugin so that feature is currently disabled. 188 | 189 | 190 | ## Installation 191 | 192 | ### Package Manager 193 | 194 | Go to `Edit > Project Settings > Package Manager`. Under 'Scoped Registries' make sure there is an OpenUPM entry. 195 | 196 | If you don't have one: click the `+` button and enter the following values: 197 | 198 | - Name: `OpenUPM`
199 | - URL: `https://package.openupm.com`
200 | 201 | Then under 'Scope(s)' press the `+` button and add `com.roytheunissen`. 202 | 203 | It should look something like this:
204 | ![image](https://user-images.githubusercontent.com/3997055/185363839-37b3bb3d-f70c-4dbd-b30d-cc8a93b592bb.png) 205 | 206 |
207 | All of my packages will now be available to you in the Package Manager in the 'My Registries' section and can be installed from there. 208 |
209 | 210 | 211 | ### Git Submodule 212 | 213 | You can check out this repository as a submodule into your project's Assets folder. This is recommended if you intend to contribute to the repository yourself. 214 | 215 | ### OpenUPM 216 | The package is available on the [openupm registry](https://openupm.com). It's recommended to install it via [openupm-cli](https://github.com/openupm/openupm-cli). 217 | 218 | ``` 219 | openupm add com.roytheunissen.fmod-syntax 220 | ``` 221 | 222 | ### Manifest 223 | You can also install via git URL by adding this entry in your **manifest.json** (make sure to end with a comma if you're adding this at the top) 224 | 225 | ``` 226 | "com.roytheunissen.fmod-syntax": "https://github.com/RoyTheunissen/FMOD-Syntax.git" 227 | ``` 228 | 229 | ### Unity Package Manager 230 | From Window->Package Manager, click on the + sign and Add from git: 231 | ``` 232 | https://github.com/RoyTheunissen/FMOD-Syntax.git 233 | ``` 234 | 235 | 236 | ## Contact 237 | [Roy Theunissen](https://roytheunissen.com) 238 | 239 | [roy.theunissen@live.nl](mailto:roy.theunissen@live.nl) 240 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: def502edc086a9d4cb2032786c011b8f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 15afd043ffab31b4e8f5ca59b9202fb4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/AudioReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using UnityEngine; 6 | 7 | namespace RoyTheunissen.FMODSyntax 8 | { 9 | /// 10 | /// Inspector reference to FMOD audio config. Can be used to get a simple parameterless audio config. 11 | /// 12 | [Serializable] 13 | public class AudioReference : IAudioConfig 14 | { 15 | [SerializeField] private string fmodEventGuid; 16 | 17 | [NonSerialized] private FmodParameterlessAudioConfig cachedFmodAudioConfig; 18 | [NonSerialized] private string guidFmodAudioConfigIsCachedFor; 19 | private FmodParameterlessAudioConfig FmodAudioConfig 20 | { 21 | get 22 | { 23 | if (guidFmodAudioConfigIsCachedFor != fmodEventGuid) 24 | { 25 | guidFmodAudioConfigIsCachedFor = fmodEventGuid; 26 | cachedFmodAudioConfig = GetParameterlessEventConfig(fmodEventGuid); 27 | if (!string.IsNullOrEmpty(fmodEventGuid) && cachedFmodAudioConfig == null) 28 | { 29 | Debug.LogError($"FMOD event was assigned to audio reference but its corresponding config could " + 30 | $"not be found at runtime. Did you forget to compile FMOD code?"); 31 | } 32 | } 33 | return cachedFmodAudioConfig; 34 | } 35 | } 36 | 37 | public bool IsAssigned => FmodAudioConfig != null; 38 | 39 | public string Name => IsAssigned ? FmodAudioConfig.Name : ""; 40 | public string Path => IsAssigned ? FmodAudioConfig.Path : ""; 41 | 42 | [NonSerialized] private static bool didCacheParameterlessEvents; 43 | private static readonly Dictionary parameterlessEventsByGuid = 44 | new Dictionary(); 45 | 46 | public FmodParameterlessAudioPlayback Play(Transform source = null) 47 | { 48 | return FmodAudioConfig.Play(source); 49 | } 50 | 51 | IAudioPlayback IAudioConfig.Play(Transform source) 52 | { 53 | return Play(source); 54 | } 55 | 56 | #if UNITY_EDITOR 57 | [UnityEditor.InitializeOnLoadMethod] 58 | private static void Initialize() 59 | { 60 | parameterlessEventsByGuid.Clear(); 61 | didCacheParameterlessEvents = false; 62 | } 63 | #endif // UNITY_EDITOR 64 | 65 | 66 | public static FmodParameterlessAudioConfig GetParameterlessEventConfig(string guid) 67 | { 68 | CacheParameterlessEventConfigs(); 69 | 70 | if (parameterlessEventsByGuid.TryGetValue(guid, out FmodParameterlessAudioConfig result)) 71 | return result; 72 | 73 | return null; 74 | } 75 | 76 | private static void CacheParameterlessEventConfigs() 77 | { 78 | if (didCacheParameterlessEvents) 79 | return; 80 | 81 | didCacheParameterlessEvents = true; 82 | 83 | parameterlessEventsByGuid.Clear(); 84 | 85 | const string containerClassName = "AudioParameterlessEvents"; 86 | IEnumerable containerTypes = AppDomain.CurrentDomain.GetAssemblies() 87 | .SelectMany(assembly => assembly.GetTypes()).Where(t => t.Name == containerClassName); 88 | 89 | foreach (Type containerType in containerTypes) 90 | { 91 | // Get the dictionary of parameter events by GUID. 92 | FieldInfo eventsByGuidDictionaryField = containerType.GetField( 93 | "EventsByGuid", BindingFlags.Public | BindingFlags.Static); 94 | Dictionary eventsByGuidDictionary = 95 | (Dictionary)eventsByGuidDictionaryField.GetValue(null); 96 | 97 | // Compile them all into one big dictionary. 98 | foreach (KeyValuePair kvp in eventsByGuidDictionary) 99 | { 100 | parameterlessEventsByGuid.Add(kvp.Key, kvp.Value); 101 | } 102 | } 103 | } 104 | 105 | public override string ToString() 106 | { 107 | FmodParameterlessAudioConfig config = FmodAudioConfig; 108 | return config == null ? "" : config.ToString(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Runtime/AudioReference.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: acdf7a8fe1d11dd4ba6217bde9680583 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Banks.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e12dd6d432c7de64b9deaa6c46bda6c0 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Banks/BankExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace FMOD.Studio 2 | { 3 | /// 4 | /// Extensions for bank to get the path without an out parameter because to make lambda functions easy to write. 5 | /// 6 | public static class BankExtensions 7 | { 8 | public static string getPath(this Bank bank) 9 | { 10 | bank.getPath(out string path); 11 | return path; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Runtime/Banks/BankExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 366b7beedb50c3c46b71596470091e65 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Buses.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 05bab732c1d23ab4f90737d2b8a5225d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Buses/Bus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FMODUnity; 3 | 4 | namespace RoyTheunissen.FMODSyntax 5 | { 6 | /// 7 | /// Utility for accessing the buses more conveniently. 8 | /// 9 | public class Bus 10 | { 11 | private readonly string path; 12 | 13 | [NonSerialized] private FMOD.Studio.Bus cachedFmodBus; 14 | [NonSerialized] private bool didCacheFmodBus; 15 | 16 | public FMOD.Studio.Bus FmodBus 17 | { 18 | get 19 | { 20 | if (!didCacheFmodBus) 21 | { 22 | didCacheFmodBus = true; 23 | cachedFmodBus = RuntimeManager.GetBus(path); 24 | } 25 | return cachedFmodBus; 26 | } 27 | } 28 | 29 | public float VolumeLinear 30 | { 31 | get => FMOD.Studio.BusExtensions.getVolume(FmodBus); 32 | set => FmodBus.setVolume(value); 33 | } 34 | 35 | public Bus(string path) 36 | { 37 | this.path = path; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Runtime/Buses/Bus.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4019062cc94a9b0478efbc97ad0b873b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Buses/BusExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace FMOD.Studio 2 | { 3 | /// 4 | /// Extensions for bus to get the path without an out parameter to make lambda functions easy to write. 5 | /// 6 | public static class BusExtensions 7 | { 8 | public static string getPath(this FMOD.Studio.Bus bus) 9 | { 10 | bus.getPath(out string path); 11 | return path; 12 | } 13 | 14 | public static float getVolume(this FMOD.Studio.Bus bus) 15 | { 16 | bus.getVolume(out float volume); 17 | return volume; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Runtime/Buses/BusExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6a849f795dac694780ce3a344ee4c73 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Callbacks.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6bfbaaba73d1e6f4e8cef65a6ef7c1de 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Callbacks/IOnFmodPlayback.cs: -------------------------------------------------------------------------------- 1 | namespace RoyTheunissen.FMODSyntax.Callbacks 2 | { 3 | /// 4 | /// Interface to use when you want a callback whenever an FMOD event playback is (un)registered. 5 | /// 6 | public interface IOnFmodPlayback 7 | { 8 | void OnFmodPlaybackRegistered(FmodAudioPlayback playback); 9 | void OnFmodPlaybackUnregistered(FmodAudioPlayback playback); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Runtime/Callbacks/IOnFmodPlayback.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ffe122c1a3f97764aa253a59b3602381 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Events.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ac1fadd7a67f8d24697e4b016458d0e5 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Events/FmodAudioConfig.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace RoyTheunissen.FMODSyntax 4 | { 5 | /// 6 | /// Non-generic base class for FmodAudioConfig to apply as a type constraint. 7 | /// 8 | public abstract class FmodAudioConfigBase : FmodPlayableConfig 9 | { 10 | protected FmodAudioConfigBase(string guid) : base(guid) 11 | { 12 | } 13 | } 14 | 15 | /// 16 | /// Config for a playable FMOD audio event. Returns an instance of the specified AudioFmodPlayback type so you can 17 | /// modify its parameters. Configs are specified in FmodEvents.AudioEvents 18 | /// 19 | public abstract class FmodAudioConfig : FmodAudioConfigBase, IAudioConfig 20 | where PlaybackType : FmodAudioPlayback 21 | { 22 | public FmodAudioConfig(string guid) : base(guid) 23 | { 24 | } 25 | 26 | public abstract PlaybackType Play(Transform source = null); 27 | 28 | IAudioPlayback IAudioConfig.Play(Transform source) 29 | { 30 | return Play(source); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Runtime/Events/FmodAudioConfig.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d6c822bf45292ed48b64d0ee86832454 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Events/FmodAudioFolder.cs: -------------------------------------------------------------------------------- 1 | namespace RoyTheunissen.FMODSyntax 2 | { 3 | /// 4 | /// Represents a folder in the FMOD bank. Helps organize events and prevent name conflicts. 5 | /// 6 | public abstract class FmodAudioFolder 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Runtime/Events/FmodAudioFolder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4d8770b2da4ab6849a12b4325f20a026 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Events/FmodAudioPlayback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FMOD; 4 | using FMOD.Studio; 5 | using FMODUnity; 6 | using RoyTheunissen.FMODSyntax.Callbacks; 7 | using UnityEngine; 8 | using Debug = UnityEngine.Debug; 9 | using STOP_MODE = FMOD.Studio.STOP_MODE; 10 | 11 | namespace RoyTheunissen.FMODSyntax 12 | { 13 | /// 14 | /// Non-generic base class for AudioFmodPlayback to apply as a type constraint. 15 | /// 16 | public abstract class FmodAudioPlaybackBase : FmodPlayablePlaybackBase 17 | { 18 | private bool isOneshot = false; 19 | public bool IsOneshot 20 | { 21 | get => isOneshot; 22 | protected set => isOneshot = value; 23 | } 24 | } 25 | 26 | /// 27 | /// Playback for a playable FMOD audio event. Allows you to update its parameters. 28 | /// Produced by calling Play() on an AudioFmodConfig, which are specified in FmodEvents.AudioEvents 29 | /// 30 | public abstract class FmodAudioPlayback : FmodAudioPlaybackBase, IAudioPlayback 31 | { 32 | public void Play(EventDescription eventDescription, Transform source) 33 | { 34 | eventDescription.getPath(out string path); 35 | 36 | if (!eventDescription.isValid()) 37 | { 38 | eventDescription.getID(out GUID guid); 39 | Debug.LogError($"Trying to play invalid FMOD Event guid: '{guid}' path:'{path}'"); 40 | return; 41 | } 42 | 43 | // Events are called something like event:/ but we want to get rid of any prefix like that. 44 | // Also every 'folder' along the way will be treated like a sort of 'tag' 45 | SearchKeywords = path.Substring(path.IndexOf("/", StringComparison.Ordinal) + 1).Replace('/', ','); 46 | 47 | Name = Path.GetFileName(path); 48 | 49 | EventDescription = eventDescription; 50 | eventDescription.createInstance(out EventInstance newInstance); 51 | Instance = newInstance; 52 | 53 | if (source != null) 54 | { 55 | Instance.set3DAttributes(RuntimeUtils.To3DAttributes(source)); 56 | RuntimeManager.AttachInstanceToGameObject(Instance, source); 57 | } 58 | 59 | // Cache properties. 60 | eventDescription.isOneshot(out bool isOneshotResult); 61 | IsOneshot = isOneshotResult; 62 | 63 | InitializeParameters(); 64 | 65 | Instance.start(); 66 | 67 | FmodSyntaxSystem.RegisterActiveEventPlayback(this); 68 | } 69 | 70 | public void Stop() 71 | { 72 | if (Instance.isValid()) 73 | Instance.stop(STOP_MODE.ALLOWFADEOUT); 74 | } 75 | 76 | public override void Cleanup() 77 | { 78 | if (Instance.isValid()) 79 | { 80 | Instance.stop(STOP_MODE.IMMEDIATE); 81 | 82 | RuntimeManager.DetachInstanceFromGameObject(Instance); 83 | if (EventDescription.isValid()) 84 | { 85 | Instance.release(); 86 | Instance.clearHandle(); 87 | } 88 | } 89 | 90 | FmodSyntaxSystem.UnregisterActiveEventPlayback(this); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Runtime/Events/FmodAudioPlayback.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f9422358271b6f47808272ac35a22c8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Events/FmodParameterlessAudio.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace RoyTheunissen.FMODSyntax 4 | { 5 | /// 6 | /// Playback of a simple FMOD event without any parameters. 7 | /// 8 | public sealed class FmodParameterlessAudioPlayback : FmodAudioPlayback 9 | { 10 | } 11 | 12 | /// 13 | /// Config of a simple FMOD event without any parameters. 14 | /// 15 | public sealed class FmodParameterlessAudioConfig : FmodAudioConfig, IAudioConfig 16 | { 17 | public FmodParameterlessAudioConfig(string guid) : base(guid) 18 | { 19 | } 20 | 21 | public override FmodParameterlessAudioPlayback Play(Transform source = null) 22 | { 23 | FmodParameterlessAudioPlayback instance = new FmodParameterlessAudioPlayback(); 24 | instance.Play(EventDescription, source); 25 | return instance; 26 | } 27 | 28 | IAudioPlayback IAudioConfig.Play(Transform source) 29 | { 30 | FmodParameterlessAudioPlayback instance = new FmodParameterlessAudioPlayback(); 31 | instance.Play(EventDescription, source); 32 | return instance; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Runtime/Events/FmodParameterlessAudio.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a38ea1c1583aa5a429a776f66e4d26e7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Events/FmodPlayableConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FMOD; 3 | using FMOD.Studio; 4 | using FMODUnity; 5 | 6 | namespace RoyTheunissen.FMODSyntax 7 | { 8 | /// 9 | /// Base class for configs of Playables (Events and Snapshots). 10 | /// 11 | public abstract class FmodPlayableConfig 12 | { 13 | [NonSerialized] private GUID id; 14 | public string Id => id.ToString(); 15 | 16 | /// 17 | /// NOTE: Seems like we can't cache this for some reason that's related to domain reloading. Not sure yet why. 18 | /// 19 | protected EventDescription EventDescription => RuntimeManager.GetEventDescription(id); 20 | 21 | public string Path 22 | { 23 | get 24 | { 25 | EventDescription.getPath(out string path); 26 | return path; 27 | } 28 | } 29 | 30 | public string Name => System.IO.Path.GetFileNameWithoutExtension(Path); 31 | 32 | public bool IsAssigned => !id.IsNull; 33 | 34 | public FmodPlayableConfig(string guid) 35 | { 36 | id = GUID.Parse(guid); 37 | } 38 | 39 | public void Preload() 40 | { 41 | EventDescription.loadSampleData(); 42 | } 43 | 44 | public void Unload() 45 | { 46 | EventDescription.unloadSampleData(); 47 | } 48 | 49 | public override string ToString() 50 | { 51 | return Name; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Runtime/Events/FmodPlayableConfig.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ee08425065d285c46a355a7cc6e94246 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Events/FmodPlayablePlaybackBase.cs: -------------------------------------------------------------------------------- 1 | using FMOD.Studio; 2 | using UnityEngine; 3 | 4 | namespace RoyTheunissen.FMODSyntax 5 | { 6 | /// 7 | /// Non-generic base class for the playback of Playables (Events / Snapshots). 8 | /// 9 | public abstract class FmodPlayablePlaybackBase 10 | { 11 | private EventInstance instance; 12 | protected EventInstance Instance 13 | { 14 | get => instance; 15 | set => instance = value; 16 | } 17 | 18 | private EventDescription eventDescription; 19 | protected EventDescription EventDescription 20 | { 21 | get => eventDescription; 22 | set => eventDescription = value; 23 | } 24 | 25 | public bool CanBeCleanedUp 26 | { 27 | get 28 | { 29 | if (!Instance.isValid()) 30 | return true; 31 | 32 | Instance.getPlaybackState(out PLAYBACK_STATE playbackState); 33 | return playbackState == PLAYBACK_STATE.STOPPED; 34 | } 35 | } 36 | 37 | private string name; 38 | public string Name 39 | { 40 | get => name; 41 | protected set => name = value; 42 | } 43 | 44 | private string searchKeywords; 45 | public string SearchKeywords 46 | { 47 | get => searchKeywords; 48 | protected set => searchKeywords = value; 49 | } 50 | 51 | public float NormalizedProgress 52 | { 53 | get 54 | { 55 | if (!eventDescription.isValid() || !instance.isValid()) 56 | return 0.0f; 57 | 58 | eventDescription.getLength(out int timelineDurationInMilliseconds); 59 | instance.getTimelinePosition(out int timelinePositionInMilliSeconds); 60 | return (float)(timelinePositionInMilliSeconds / (double)timelineDurationInMilliseconds); 61 | } 62 | } 63 | 64 | public float Volume 65 | { 66 | get 67 | { 68 | if (!eventDescription.isValid() || !instance.isValid()) 69 | return 1.0f; 70 | instance.getVolume(out float volume); 71 | return volume; 72 | } 73 | set 74 | { 75 | if (!eventDescription.isValid() || !instance.isValid()) 76 | return; 77 | 78 | instance.setVolume(Mathf.Max(value, 0.0f)); 79 | } 80 | } 81 | 82 | private float smoothDampVolumeVelocity; 83 | 84 | protected virtual void InitializeParameters() 85 | { 86 | // We need to pass our instance on to our parameters so we can set its values correctly. 87 | } 88 | 89 | public void MoveTowardsVolume(float target, float maxDelta) 90 | { 91 | Volume = Mathf.MoveTowards(Volume, target, maxDelta); 92 | } 93 | 94 | public void SmoothDampTowardsVolume(float target, float duration) 95 | { 96 | // Need to explicitly check for this or it can return NaN if Time.timeScale is 0. Yes, really. 97 | // https://discussions.unity.com/t/mathf-smoothdamp-assigns-nan/383635/13 98 | if (!Mathf.Approximately(Time.deltaTime, 0.0f)) 99 | Volume = Mathf.SmoothDamp(Volume, target, ref smoothDampVolumeVelocity, duration); 100 | } 101 | 102 | public abstract void Cleanup(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Runtime/Events/FmodPlayablePlaybackBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 92e9b5b4828aad546b9464a2d2ad9fe2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Events/Parameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FMOD.Studio; 3 | using FMODUnity; 4 | 5 | #if SCRIPTABLE_OBJECT_COLLECTION 6 | using BrunoMikoski.ScriptableObjectCollections; 7 | #endif // SCRIPTABLE_OBJECT_COLLECTION 8 | 9 | namespace RoyTheunissen.FMODSyntax 10 | { 11 | /// 12 | /// Utility for setting FMOD parameters from code. 13 | /// 14 | public abstract class Parameter 15 | { 16 | protected PARAMETER_ID id; 17 | protected bool isGlobal; 18 | 19 | protected EventInstance instance; 20 | 21 | public Parameter(PARAMETER_ID id, bool isGlobal) 22 | { 23 | this.id = id; 24 | this.isGlobal = isGlobal; 25 | } 26 | 27 | public void InitializeAsEventParameter(EventInstance instance) 28 | { 29 | this.instance = instance; 30 | } 31 | } 32 | 33 | public abstract class ParameterGeneric : Parameter 34 | { 35 | private float floatValue; 36 | public ValueType Value 37 | { 38 | get 39 | { 40 | if (isGlobal) 41 | RuntimeManager.StudioSystem.getParameterByID(id, out floatValue); 42 | else if (instance.isValid()) 43 | instance.getParameterByID(id, out floatValue); 44 | return GetValueFromFloat(floatValue); 45 | } 46 | set 47 | { 48 | floatValue = GetFloatFromValue(value); 49 | if (isGlobal) 50 | RuntimeManager.StudioSystem.setParameterByID(id, floatValue); 51 | else if (instance.isValid()) 52 | instance.setParameterByID(id, floatValue); 53 | } 54 | } 55 | 56 | protected abstract ValueType GetValueFromFloat(float value); 57 | protected abstract float GetFloatFromValue(ValueType value); 58 | 59 | protected ParameterGeneric(PARAMETER_ID id, bool isGlobal) 60 | : base(id, isGlobal) 61 | { 62 | } 63 | } 64 | 65 | public sealed class ParameterFloat : ParameterGeneric 66 | { 67 | public ParameterFloat(PARAMETER_ID id, bool isGlobal) : base(id, isGlobal) 68 | { 69 | } 70 | 71 | protected override float GetValueFromFloat(float value) 72 | { 73 | return value; 74 | } 75 | 76 | protected override float GetFloatFromValue(float value) 77 | { 78 | return value; 79 | } 80 | } 81 | 82 | public sealed class ParameterInt : ParameterGeneric 83 | { 84 | public ParameterInt(PARAMETER_ID id, bool isGlobal) : base(id, isGlobal) 85 | { 86 | } 87 | 88 | protected override int GetValueFromFloat(float value) 89 | { 90 | return (int)value; 91 | } 92 | 93 | protected override float GetFloatFromValue(int value) 94 | { 95 | return value; 96 | } 97 | } 98 | 99 | public sealed class ParameterEnum : ParameterGeneric 100 | where EnumType : Enum 101 | { 102 | public ParameterEnum(PARAMETER_ID id, bool isGlobal) : base(id, isGlobal) 103 | { 104 | } 105 | 106 | protected override EnumType GetValueFromFloat(float value) 107 | { 108 | return (EnumType)(object)(int)value; 109 | } 110 | 111 | protected override float GetFloatFromValue(EnumType value) 112 | { 113 | return (int)(object)value; 114 | } 115 | } 116 | 117 | #if SCRIPTABLE_OBJECT_COLLECTION 118 | public sealed class ParameterScriptableObjectCollectionItem : ParameterGeneric 119 | where ItemType : ScriptableObjectCollectionItem 120 | { 121 | public ParameterScriptableObjectCollectionItem(PARAMETER_ID id, bool isGlobal) : base(id, isGlobal) 122 | { 123 | } 124 | 125 | protected override ItemType GetValueFromFloat(float value) 126 | { 127 | return ScriptableObjectCollection.Values[(int)value]; 128 | } 129 | 130 | protected override float GetFloatFromValue(ItemType value) 131 | { 132 | return value.Index; 133 | } 134 | } 135 | #endif // SCRIPTABLE_OBJECT_COLLECTION 136 | 137 | public sealed class ParameterBool : ParameterGeneric 138 | { 139 | public ParameterBool(PARAMETER_ID id, bool isGlobal) : base(id, isGlobal) 140 | { 141 | } 142 | 143 | protected override bool GetValueFromFloat(float value) 144 | { 145 | return value > 0.5f; 146 | } 147 | 148 | protected override float GetFloatFromValue(bool value) 149 | { 150 | return value ? 1 : 0; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Runtime/Events/Parameter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd2d20f1cd1724448aaf3daed853352b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb33782e752ffb146a80df7c52b47528 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using UnityEngine; 4 | 5 | namespace RoyTheunissen.FMODSyntax 6 | { 7 | public static class StringExtensions 8 | { 9 | private const string HungarianPrefix = "m_"; 10 | private const char Underscore = '_'; 11 | public const char DefaultSeparator = ' '; 12 | 13 | private static readonly char[] DirectorySeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; 14 | 15 | private static bool IsExcludedSymbol(char symbol, char wordSeparator = DefaultSeparator) 16 | { 17 | return char.IsWhiteSpace(symbol) || char.IsPunctuation(symbol) || symbol == wordSeparator; 18 | } 19 | 20 | public static string ToCamelCasing(this string text, char wordSeparator = DefaultSeparator) 21 | { 22 | if (string.IsNullOrEmpty(text)) 23 | return text; 24 | 25 | // Split the text up into separate words first then fix spaces and change captialization. 26 | text = text.ToHumanReadable(wordSeparator); 27 | 28 | string camelText = string.Empty; 29 | for (int i = 0; i < text.Length; i++) 30 | { 31 | // Separators cause the next character to be capitalized. 32 | if (char.IsWhiteSpace(text[i]) || text[i] == wordSeparator) 33 | { 34 | // Non-whitespace separators are allowed through. 35 | if (!char.IsWhiteSpace(text[i])) 36 | camelText += text[i]; 37 | 38 | // If there is a character after the whitespace, add that as capitalized. 39 | if (i + 1 < text.Length) 40 | { 41 | i++; 42 | camelText += char.ToUpper(text[i]); 43 | } 44 | 45 | continue; 46 | } 47 | 48 | camelText += char.ToLower(text[i]); 49 | } 50 | 51 | return camelText; 52 | } 53 | 54 | public static string ToPascalCasing(this string text, char wordSeparator = DefaultSeparator) 55 | { 56 | if (string.IsNullOrEmpty(text)) 57 | return text; 58 | 59 | // Capitalize the first letter. 60 | string camelCasingText = text.ToCamelCasing(wordSeparator); 61 | if (camelCasingText.Length == 1) 62 | return camelCasingText.ToUpper(); 63 | 64 | return char.ToUpper(camelCasingText[0]) + camelCasingText.Substring(1); 65 | } 66 | 67 | /// 68 | /// Gets the human readable version of programmer text, like a variable name. 69 | /// 70 | /// The programmer text. 71 | /// The human readable equivalent of the programmer text. 72 | public static string ToHumanReadable( 73 | this string programmerText, 74 | char wordSeparator = DefaultSeparator) 75 | { 76 | if (string.IsNullOrEmpty(programmerText)) 77 | return programmerText; 78 | 79 | bool wasLetter = false; 80 | bool wasUpperCase = false; 81 | bool addedSpace = false; 82 | string result = ""; 83 | 84 | // First remove the m_ prefix if it exists. 85 | if (programmerText.StartsWith(HungarianPrefix)) 86 | programmerText = programmerText.Substring(HungarianPrefix.Length); 87 | 88 | // Deal with any miscellanneous spaces. 89 | if (wordSeparator != DefaultSeparator) 90 | programmerText = programmerText.Replace(DefaultSeparator, wordSeparator); 91 | 92 | // Deal with any miscellanneous underscores. 93 | if (wordSeparator != Underscore) 94 | programmerText = programmerText.Replace(Underscore, wordSeparator); 95 | 96 | // Go through the original string and copy it with some modifications. 97 | for (int i = 0; i < programmerText.Length; i++) 98 | { 99 | // If there was a change in caps add spaces. 100 | if ((wasUpperCase != char.IsUpper(programmerText[i]) 101 | || (wasLetter != char.IsLetter(programmerText[i]))) 102 | && i > 0 && !addedSpace 103 | && !(IsExcludedSymbol(programmerText[i], wordSeparator) || 104 | IsExcludedSymbol(programmerText[i - 1], wordSeparator))) 105 | { 106 | // Upper case to lower case. 107 | // I added this so that something like 'GUIItem' turns into 'GUI Item', but that 108 | // means we have to make sure that no symbols are involved. Also check that there 109 | // isn't already a space where we want to add a space. Don't want to double space. 110 | if (wasUpperCase && i > 1 && !IsExcludedSymbol(programmerText[i - 1], wordSeparator) 111 | && !IsExcludedSymbol(result[result.Length - 2], wordSeparator)) 112 | { 113 | // From letter to letter means we have to insert a space one character back. 114 | // Otherwise it's going from a letter to a symbol and we can just add a space. 115 | if (wasLetter && char.IsLetter(programmerText[i])) 116 | result = result.Insert(result.Length - 1, wordSeparator.ToString()); 117 | else 118 | result += wordSeparator; 119 | addedSpace = true; 120 | } 121 | 122 | // Lower case to upper case. 123 | if (!wasUpperCase) 124 | { 125 | result += wordSeparator; 126 | addedSpace = true; 127 | } 128 | } 129 | else 130 | { 131 | // No case change. 132 | addedSpace = false; 133 | } 134 | 135 | // Add the character. 136 | result += programmerText[i]; 137 | 138 | // Capitalize the first character. 139 | if (i == 0) 140 | result = result.ToUpper(); 141 | 142 | // Remember things about the previous letter. 143 | wasLetter = char.IsLetter(programmerText[i]); 144 | wasUpperCase = char.IsUpper(programmerText[i]); 145 | } 146 | 147 | return result; 148 | } 149 | 150 | public static string RemovePrefix(this string name, string prefix) 151 | { 152 | if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(prefix)) 153 | return name; 154 | 155 | if (!name.StartsWith(prefix)) 156 | return name; 157 | 158 | return name.Substring(prefix.Length); 159 | } 160 | 161 | public static string RemoveSuffix(this string name, string suffix) 162 | { 163 | if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(suffix)) 164 | return name; 165 | 166 | if (!name.EndsWith(suffix)) 167 | return name; 168 | 169 | return name.Substring(0, name.Length - suffix.Length); 170 | } 171 | 172 | /// 173 | /// Converts the slashes to be consistent. 174 | /// 175 | public static string ToUnityPath(this string name) 176 | { 177 | return name.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); 178 | } 179 | 180 | private const string AssetsFolder = "Assets"; 181 | 182 | public static string RemoveAssetsPrefix(this string path) 183 | { 184 | return path.RemovePrefix(AssetsFolder + Path.AltDirectorySeparatorChar); 185 | } 186 | 187 | public static string GetAbsolutePath(this string projectPath) 188 | { 189 | string absolutePath = projectPath.ToUnityPath().RemoveAssetsPrefix(); 190 | return Application.dataPath + Path.AltDirectorySeparatorChar + absolutePath; 191 | } 192 | 193 | public static string GetWhitespacePreceding(this string text, int index, bool includingNewLines) 194 | { 195 | string whitespacePreceding = string.Empty; 196 | if (index > 0) 197 | { 198 | for (int i = index - 1; i >= 0; i--) 199 | { 200 | char c = text[i]; 201 | if (c == '\n' && !includingNewLines) 202 | break; 203 | if (char.IsWhiteSpace(c)) 204 | whitespacePreceding = text[i] + whitespacePreceding; 205 | else 206 | break; 207 | } 208 | } 209 | 210 | return whitespacePreceding; 211 | } 212 | 213 | public static string GetSection(this string text, string from, string to) 214 | { 215 | if (string.IsNullOrEmpty(from) || string.IsNullOrEmpty(to)) 216 | return string.Empty; 217 | 218 | int fromIndex = text.IndexOf(from, StringComparison.Ordinal); 219 | if (fromIndex == -1) 220 | return string.Empty; 221 | fromIndex += from.Length; 222 | 223 | int toIndex = text.IndexOf(to, fromIndex, StringComparison.Ordinal); 224 | if (toIndex == -1) 225 | return string.Empty; 226 | 227 | return text.Substring(fromIndex, toIndex - fromIndex); 228 | } 229 | 230 | public static string GetParentDirectory(this string path) 231 | { 232 | int lastDirectorySeparator = path.LastIndexOfAny(DirectorySeparators); 233 | if (lastDirectorySeparator == -1) 234 | return path; 235 | 236 | return path.Substring(0, lastDirectorySeparator); 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /Runtime/Extensions/StringExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 647e8bc42dcac1a49ae16fbef3701d1f 3 | timeCreated: 1470404105 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Runtime/FmodLabelEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RoyTheunissen.FMODSyntax 4 | { 5 | /// 6 | /// Attribute to let the FMOD Syntax system know that a custom type is used to represent a labelled parameter. 7 | /// This is useful if for example you have a labelled parameter that is shared between various events, and you want 8 | /// to have only one enum represent it instead of having to map enums back-and-forth. Instead of an enum you can now 9 | /// also use a Scriptable Object Collection item. 10 | /// 11 | public class FmodLabelTypeAttribute : Attribute 12 | { 13 | private string[] labelledParameterNames; 14 | public string[] LabelledParameterNames => labelledParameterNames; 15 | 16 | public FmodLabelTypeAttribute(params string[] labelledParameterNames) 17 | { 18 | this.labelledParameterNames = labelledParameterNames; 19 | } 20 | } 21 | 22 | /// 23 | /// Basically here for backwards compatibility. FmodLabelTypeAttribute is a better name because these days it also 24 | /// supports linking Scriptable Object Collection item types to an FMOD Label parameter. I find it a bit harsh 25 | /// to mark this one as obsolete though, seems harmless to make this one just redirect to FmodLabelTypeAttribute. 26 | /// 27 | public sealed class FmodLabelEnumAttribute : FmodLabelTypeAttribute 28 | { 29 | public FmodLabelEnumAttribute(params string[] labelledParameterNames) 30 | : base(labelledParameterNames) 31 | { 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Runtime/FmodLabelEnum.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b11e62610b60a1440bfabab2cb9b4fdd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/FmodSyntaxSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using UnityEngine; 4 | using UnityEngine.Serialization; 5 | 6 | #if UNITY_EDITOR 7 | using UnityEditor; 8 | #endif // UNITY_EDITOR 9 | 10 | namespace RoyTheunissen.FMODSyntax 11 | { 12 | /// 13 | /// Scriptable object that holds all the settings for the FMOD Syntax system. 14 | /// 15 | public sealed class FmodSyntaxSettings : ScriptableObject 16 | { 17 | public enum SyntaxFormats 18 | { 19 | Flat = 0, 20 | FlatWithPathIncludedInName = 1, 21 | SubclassesPerFolder = 2, 22 | } 23 | 24 | [SerializeField] private string generatedScriptsFolderPath; 25 | public string GeneratedScriptsFolderPath => generatedScriptsFolderPath; 26 | 27 | [SerializeField] private string namespaceForGeneratedCode; 28 | public string NamespaceForGeneratedCode => namespaceForGeneratedCode; 29 | 30 | [SerializeField] private bool shouldGenerateAssemblyDefinition; 31 | public bool ShouldGenerateAssemblyDefinition => shouldGenerateAssemblyDefinition; 32 | 33 | [FormerlySerializedAs("generateFallbacksForMissingEvents")] 34 | [Tooltip("If specified, renamed or moved events will first generate an 'alias' so that any existing " + 35 | "references so you can update the references without getting compile errors.")] 36 | [SerializeField] private bool generateFallbacksForChangedEvents = true; 37 | public bool GenerateFallbacksForChangedEvents => generateFallbacksForChangedEvents; 38 | 39 | [Space] 40 | [Tooltip("How to format the events syntax. Different formats suit different use cases:\n\n" + 41 | "Flat - Simplest syntax. All events are inside a class called AudioEvents. Event names have to be unique.\n\n" + 42 | "Flat (With Path Included In Name) - Like Flat but an event called 'Player/Footstep' would generate " + 43 | "a field called 'Player_Footstep'. Keeps things very simple but does prevent name conflicts.\n\n" + 44 | "Subclasses Per Folder - Generates subclasses inside AudioEvents to represent the " + 45 | "folders in the FMOD project. An event called 'Player/Footstep' would be accessed via " + 46 | "'AudioEvents.Player.Footstep'. A very clear and organized way to prevent name clashes but does " + 47 | "require more typing.")] 48 | [SerializeField] private SyntaxFormats syntaxFormat = SyntaxFormats.Flat; 49 | public SyntaxFormats SyntaxFormat => syntaxFormat; 50 | 51 | [NonSerialized] private static FmodSyntaxSettings cachedInstance; 52 | [NonSerialized] private static bool didCacheInstance; 53 | public static FmodSyntaxSettings Instance 54 | { 55 | get 56 | { 57 | #if UNITY_EDITOR 58 | if (!didCacheInstance) 59 | { 60 | string[] guids = AssetDatabase.FindAssets($"t:{nameof(FmodSyntaxSettings)}"); 61 | if (guids.Length > 0) 62 | { 63 | string path = AssetDatabase.GUIDToAssetPath(guids[0]); 64 | cachedInstance = AssetDatabase.LoadAssetAtPath(path); 65 | didCacheInstance = cachedInstance != null; 66 | } 67 | } 68 | #endif // UNITY_EDITOR 69 | return cachedInstance; 70 | } 71 | } 72 | 73 | public void InitializeFromWizard( 74 | string generatedScriptsFolderPath, string namespaceForGeneratedCode, bool shouldGenerateAssemblyDefinition) 75 | { 76 | // Sanitize the generated scripts folder path. 77 | this.generatedScriptsFolderPath = generatedScriptsFolderPath.Replace( 78 | Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); 79 | if (!this.generatedScriptsFolderPath.EndsWith(Path.AltDirectorySeparatorChar)) 80 | this.generatedScriptsFolderPath += Path.AltDirectorySeparatorChar; 81 | 82 | this.namespaceForGeneratedCode = namespaceForGeneratedCode; 83 | 84 | this.shouldGenerateAssemblyDefinition = shouldGenerateAssemblyDefinition; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Runtime/FmodSyntaxSettings.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b66f732db3a804e4eb5ef4765f45f02f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/FmodSyntaxSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using RoyTheunissen.FMODSyntax.Callbacks; 4 | 5 | namespace RoyTheunissen.FMODSyntax 6 | { 7 | /// 8 | /// Class to use to expose information, help manage playback instances, that sort of thing. 9 | /// 10 | public static class FmodSyntaxSystem 11 | { 12 | private static readonly List activeEventPlaybacks = new List(); 13 | public static List ActiveEventPlaybacks => activeEventPlaybacks; 14 | 15 | private static readonly List onEventPlaybackCallbackReceivers = new List(); 16 | 17 | private static readonly List activeSnapshotPlaybacks = new List(); 18 | public static List ActiveSnapshotPlaybacks => activeSnapshotPlaybacks; 19 | 20 | #if UNITY_EDITOR 21 | [UnityEditor.InitializeOnLoadMethod] 22 | private static void Initialize() 23 | { 24 | activeEventPlaybacks.Clear(); 25 | onEventPlaybackCallbackReceivers.Clear(); 26 | } 27 | #endif // UNITY_EDITOR 28 | 29 | public static void StopAllActivePlaybacks() 30 | { 31 | StopAllActiveEventPlaybacks(); 32 | StopAllActiveSnapshotPlaybacks(); 33 | } 34 | 35 | public static void RegisterActiveEventPlayback(FmodAudioPlayback playback) 36 | { 37 | activeEventPlaybacks.Add(playback); 38 | 39 | for (int i = 0; i < onEventPlaybackCallbackReceivers.Count; i++) 40 | { 41 | onEventPlaybackCallbackReceivers[i].OnFmodPlaybackRegistered(playback); 42 | } 43 | } 44 | 45 | public static void UnregisterActiveEventPlayback(FmodAudioPlayback playback) 46 | { 47 | activeEventPlaybacks.Remove(playback); 48 | 49 | for (int i = 0; i < onEventPlaybackCallbackReceivers.Count; i++) 50 | { 51 | onEventPlaybackCallbackReceivers[i].OnFmodPlaybackUnregistered(playback); 52 | } 53 | } 54 | 55 | public static void StopAllActiveEventPlaybacks() 56 | { 57 | for (int i = activeEventPlaybacks.Count - 1; i >= 0; i--) 58 | { 59 | activeEventPlaybacks[i].Stop(); 60 | } 61 | } 62 | 63 | public static void RegisterActiveSnapshotPlayback(FmodSnapshotPlayback playback) 64 | { 65 | activeSnapshotPlaybacks.Add(playback); 66 | } 67 | 68 | public static void UnregisterActiveSnapshotPlayback(FmodSnapshotPlayback playback) 69 | { 70 | activeSnapshotPlaybacks.Remove(playback); 71 | } 72 | 73 | /// 74 | /// Culls playbacks that are no longer necessary. You should perform this logic continuously. 75 | /// You can either call this or use the ActivePlaybacks list / playback callbacks to do it in your own system. 76 | /// 77 | public static void CullPlaybacks() 78 | { 79 | // Cull any events that are ready to be cleaned up. 80 | for (int i = activeEventPlaybacks.Count - 1; i >= 0; i--) 81 | { 82 | IFmodPlayback activeEvent = activeEventPlaybacks[i]; 83 | if (activeEvent.CanBeCleanedUp) 84 | activeEvent.Cleanup(); 85 | } 86 | 87 | // Cull any snapshots that are ready to be cleaned up. 88 | for (int i = activeSnapshotPlaybacks.Count - 1; i >= 0; i--) 89 | { 90 | IFmodPlayback activeSnapshot = activeSnapshotPlaybacks[i]; 91 | if (activeSnapshot.CanBeCleanedUp) 92 | activeSnapshot.Cleanup(); 93 | } 94 | } 95 | 96 | public static void StopAllActiveSnapshotPlaybacks() 97 | { 98 | for (int i = activeSnapshotPlaybacks.Count - 1; i >= 0; i--) 99 | { 100 | activeSnapshotPlaybacks[i].Stop(); 101 | } 102 | } 103 | 104 | [Obsolete("This method is being renamed for disambiguation. " + 105 | "Please use RegisterEventPlaybackCallbackReceiver instead.")] 106 | public static void RegisterPlaybackCallbackReceiver(IOnFmodPlayback callbackReceiver) 107 | { 108 | RegisterEventPlaybackCallbackReceiver(callbackReceiver); 109 | } 110 | 111 | [Obsolete("This method is being renamed for disambiguation. " + 112 | "Please use UnregisterEventPlaybackCallbackReceiver instead.")] 113 | public static void UnregisterPlaybackCallbackReceiver(IOnFmodPlayback callbackReceiver) 114 | { 115 | UnregisterEventPlaybackCallbackReceiver(callbackReceiver); 116 | } 117 | 118 | public static void RegisterEventPlaybackCallbackReceiver(IOnFmodPlayback callbackReceiver) 119 | { 120 | onEventPlaybackCallbackReceivers.Add(callbackReceiver); 121 | } 122 | 123 | public static void UnregisterEventPlaybackCallbackReceiver(IOnFmodPlayback callbackReceiver) 124 | { 125 | onEventPlaybackCallbackReceivers.Remove(callbackReceiver); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Runtime/FmodSyntaxSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 57fd282cadc18f040afa9b8c86cfc243 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IAudioConfig.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace RoyTheunissen.FMODSyntax 4 | { 5 | public interface IAudioConfig 6 | { 7 | bool IsAssigned { get; } 8 | 9 | IAudioPlayback Play(Transform source = null); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Runtime/IAudioConfig.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70052d8a0d09c444899a37521f7bcd67 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/IAudioPlayback.cs: -------------------------------------------------------------------------------- 1 | namespace RoyTheunissen.FMODSyntax 2 | { 3 | public interface IFmodPlayback 4 | { 5 | bool CanBeCleanedUp { get; } 6 | 7 | /// 8 | /// Cleanup is responsible for finalizing playback and de-allocating whatever resources were used. 9 | /// 10 | void Cleanup(); 11 | 12 | void Stop(); 13 | } 14 | 15 | public interface IAudioPlayback : IFmodPlayback 16 | { 17 | string Name { get; } 18 | string SearchKeywords { get; } 19 | bool IsOneshot { get; } 20 | float NormalizedProgress { get; } 21 | float Volume { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Runtime/IAudioPlayback.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c99c6911d5e40db9dac78b9afcb37c2 3 | timeCreated: 1694637524 -------------------------------------------------------------------------------- /Runtime/RoyTheunissen.FMODSyntax.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RoyTheunissen.FMODSyntax", 3 | "rootNamespace": "RoyTheunissen.FMODSyntax", 4 | "references": [ 5 | "FMODUnity", 6 | "BrunoMikoski.ScriptableObjectCollection" 7 | ], 8 | "includePlatforms": [], 9 | "excludePlatforms": [], 10 | "allowUnsafeCode": false, 11 | "overrideReferences": false, 12 | "precompiledReferences": [], 13 | "autoReferenced": true, 14 | "defineConstraints": [], 15 | "versionDefines": [ 16 | { 17 | "name": "com.brunomikoski.scriptableobjectcollection", 18 | "expression": "2.2.1", 19 | "define": "SCRIPTABLE_OBJECT_COLLECTION" 20 | } 21 | ], 22 | "noEngineReferences": false 23 | } -------------------------------------------------------------------------------- /Runtime/RoyTheunissen.FMODSyntax.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f28b76e6ba53ea24ca2daf55c1581312 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Snapshots.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 636121fa99ff68b4fbba0031ca936e24 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Snapshots/FmodSnapshotConfig.cs: -------------------------------------------------------------------------------- 1 | namespace RoyTheunissen.FMODSyntax 2 | { 3 | /// 4 | /// Non-generic base class for FmodSnapshotConfig to apply as a type constraint. 5 | /// 6 | public abstract class FmodSnapshotConfigBase : FmodPlayableConfig 7 | { 8 | protected FmodSnapshotConfigBase(string guid) : base(guid) 9 | { 10 | } 11 | 12 | public abstract FmodSnapshotPlayback PlayGeneric(); 13 | } 14 | 15 | /// 16 | /// Config for a playable FMOD snapshot. Returns a playback instance of the specified snapshot so you can stop it 17 | /// from there. Very similar to an FMOD event, but represents a certain set of mixer property values. Note that 18 | /// unlike events, snapshots do not have any parameters. As such, we do not need to generate new types of snapshots, 19 | /// we just need to generate snapshots with the appropriate GUIDs. 20 | /// 21 | public abstract class FmodSnapshotConfig : FmodSnapshotConfigBase 22 | where PlaybackType : FmodSnapshotPlayback 23 | { 24 | public FmodSnapshotConfig(string guid) : base(guid) 25 | { 26 | } 27 | 28 | public abstract PlaybackType Play(); 29 | 30 | public override FmodSnapshotPlayback PlayGeneric() => Play(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Runtime/Snapshots/FmodSnapshotConfig.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bc7499d217b85de4989fe3be17581408 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Snapshots/FmodSnapshotPlayback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FMOD; 4 | using FMOD.Studio; 5 | using Debug = UnityEngine.Debug; 6 | 7 | namespace RoyTheunissen.FMODSyntax 8 | { 9 | /// 10 | /// Non-generic base class for FmodSnapshotPlayback to apply as a type constraint. 11 | /// 12 | public abstract class FmodSnapshotPlaybackBase : FmodPlayablePlaybackBase 13 | { 14 | private const string IntensityParameterName = "Intensity"; 15 | private const float IntensityParameterRange = 100; 16 | 17 | /// 18 | /// For snapshots it's very useful to define a parameter to control the Intensity setting of the snapshot and 19 | /// thus blend it in and out. This can easily be created by right-clicking the Intensity control in the right 20 | /// of the snapshot editor and choosing Expose As Parameter. 21 | /// NOTE: By default the intensity parameter's values go from 0 - 100 so we respect that. 22 | /// Use IntensityNormalized if you prefer to set it as number between 0 and 1. 23 | /// 24 | public float Intensity 25 | { 26 | get 27 | { 28 | RESULT result = Instance.getParameterByName(IntensityParameterName, out float value); 29 | if (result != RESULT.OK) 30 | { 31 | Debug.LogWarning($"Tried to get {IntensityParameterName} parameter of snapshot '{Name}' but no " + 32 | $"such parameter was found. Did you forget to set up an " + 33 | $"{IntensityParameterName} parameter in FMOD?"); 34 | } 35 | return value; 36 | } 37 | set 38 | { 39 | RESULT result = Instance.setParameterByName(IntensityParameterName, value); 40 | if (result != RESULT.OK) 41 | { 42 | Debug.LogWarning($"Tried to set {IntensityParameterName} parameter of snapshot '{Name}' but no " + 43 | $"such parameter was found. Did you forget to set up an " + 44 | $"{IntensityParameterName} parameter in FMOD?"); 45 | } 46 | } 47 | } 48 | 49 | public float IntensityNormalized 50 | { 51 | get => Intensity / IntensityParameterRange; 52 | set => Intensity = value * IntensityParameterRange; 53 | } 54 | } 55 | 56 | /// 57 | /// Playback of a snapshot. Sort of like an event, but simpler. 58 | /// 59 | public abstract class FmodSnapshotPlayback : FmodSnapshotPlaybackBase, IFmodPlayback 60 | { 61 | public void Play(EventDescription eventDescription) 62 | { 63 | eventDescription.getPath(out string path); 64 | 65 | if (!eventDescription.isValid()) 66 | { 67 | eventDescription.getID(out GUID guid); 68 | Debug.LogError($"Trying to play invalid FMOD Snapshot guid: '{guid}' path:'{path}'"); 69 | return; 70 | } 71 | 72 | // Events are called something like event:/ but we want to get rid of any prefix like that. 73 | // Also every 'folder' along the way will be treated like a sort of 'tag' 74 | SearchKeywords = path.Substring(path.IndexOf("/", StringComparison.Ordinal) + 1).Replace('/', ','); 75 | 76 | Name = Path.GetFileName(path); 77 | 78 | EventDescription = eventDescription; 79 | eventDescription.createInstance(out EventInstance newInstance); 80 | Instance = newInstance; 81 | 82 | InitializeParameters(); 83 | 84 | Instance.start(); 85 | 86 | FmodSyntaxSystem.RegisterActiveSnapshotPlayback(this); 87 | } 88 | 89 | public void Stop() 90 | { 91 | if (Instance.isValid()) 92 | Instance.stop(STOP_MODE.ALLOWFADEOUT); 93 | } 94 | 95 | public override void Cleanup() 96 | { 97 | if (Instance.isValid()) 98 | { 99 | Instance.stop(STOP_MODE.IMMEDIATE); 100 | 101 | if (EventDescription.isValid()) 102 | { 103 | Instance.release(); 104 | Instance.clearHandle(); 105 | } 106 | } 107 | 108 | FmodSyntaxSystem.UnregisterActiveSnapshotPlayback(this); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Runtime/Snapshots/FmodSnapshotPlayback.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7c7fbd29f2e74e1193881d2778de30ad 3 | timeCreated: 1698868389 -------------------------------------------------------------------------------- /Runtime/Snapshots/SnapshotReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using UnityEngine; 6 | 7 | namespace RoyTheunissen.FMODSyntax 8 | { 9 | /// 10 | /// Inspector reference to FMOD snapshot config. Can be used to specify via the inspector which snapshot to play. 11 | /// 12 | [Serializable] 13 | public class SnapshotReference 14 | { 15 | [SerializeField] private string fmodSnapshotGuid; 16 | 17 | [NonSerialized] private FmodSnapshotConfigBase cachedFmodSnapshotConfig; 18 | [NonSerialized] private string guidFmodSnapshotConfigIsCachedFor; 19 | private FmodSnapshotConfigBase FmodSnapshotConfig 20 | { 21 | get 22 | { 23 | if (guidFmodSnapshotConfigIsCachedFor != fmodSnapshotGuid) 24 | { 25 | guidFmodSnapshotConfigIsCachedFor = fmodSnapshotGuid; 26 | cachedFmodSnapshotConfig = GetSnapshotConfig(fmodSnapshotGuid); 27 | if (!string.IsNullOrEmpty(fmodSnapshotGuid) && cachedFmodSnapshotConfig == null) 28 | { 29 | Debug.LogError($"FMOD event was assigned to snapshot reference but its corresponding config " + 30 | $"could not be found at runtime. Did you forget to compile FMOD code?"); 31 | } 32 | } 33 | return cachedFmodSnapshotConfig; 34 | } 35 | } 36 | 37 | public bool IsAssigned => FmodSnapshotConfig != null; 38 | 39 | public string Name => IsAssigned ? FmodSnapshotConfig.Name : ""; 40 | public string Path => IsAssigned ? FmodSnapshotConfig.Path : ""; 41 | 42 | [NonSerialized] private static bool didCacheSnapshots; 43 | private static readonly Dictionary snapshotsByGuid = 44 | new Dictionary(); 45 | 46 | public FmodSnapshotPlayback Play() 47 | { 48 | return FmodSnapshotConfig.PlayGeneric(); 49 | } 50 | 51 | #if UNITY_EDITOR 52 | [UnityEditor.InitializeOnLoadMethod] 53 | private static void Initialize() 54 | { 55 | snapshotsByGuid.Clear(); 56 | didCacheSnapshots = false; 57 | } 58 | #endif // UNITY_EDITOR 59 | 60 | 61 | public static FmodSnapshotConfigBase GetSnapshotConfig(string guid) 62 | { 63 | CacheSnapshotConfigs(); 64 | 65 | if (snapshotsByGuid.TryGetValue(guid, out FmodSnapshotConfigBase result)) 66 | return result; 67 | 68 | return null; 69 | } 70 | 71 | private static void CacheSnapshotConfigs() 72 | { 73 | if (didCacheSnapshots) 74 | return; 75 | 76 | didCacheSnapshots = true; 77 | 78 | snapshotsByGuid.Clear(); 79 | 80 | // Find every class with name AudioSnapshots. 81 | const string containerClassName = "AudioSnapshots"; 82 | IEnumerable containerTypes = AppDomain.CurrentDomain.GetAssemblies() 83 | .SelectMany(assembly => assembly.GetTypes()).Where(t => t.Name == containerClassName); 84 | 85 | foreach (Type containerType in containerTypes) 86 | { 87 | // Find all the fields declared in those container classes. 88 | FieldInfo[] fields = containerType.GetFields(BindingFlags.Public | BindingFlags.Static); 89 | for (int i = 0; i < fields.Length; i++) 90 | { 91 | if (!typeof(FmodSnapshotConfigBase).IsAssignableFrom(fields[i].FieldType)) 92 | continue; 93 | 94 | // If the field is a valid snapshot config, add it to the dictionary. 95 | if (fields[i].GetValue(null) is FmodSnapshotConfigBase snapshotConfig) 96 | snapshotsByGuid.Add(snapshotConfig.Id, snapshotConfig); 97 | } 98 | } 99 | } 100 | 101 | public override string ToString() 102 | { 103 | FmodSnapshotConfigBase config = FmodSnapshotConfig; 104 | return config == null ? "" : config.ToString(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Runtime/Snapshots/SnapshotReference.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 609dd020242bedf43af1d71f0b213971 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/VCAs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 50f6273335e30624ba2c1b20db22a65b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/VCAs/VCA.cs: -------------------------------------------------------------------------------- 1 | using FMOD.Studio; 2 | using FMODUnity; 3 | 4 | namespace RoyTheunissen.FMODSyntax 5 | { 6 | /// 7 | /// Utility for accessing the VCAs more conveniently. 8 | /// 9 | public class VCA 10 | { 11 | private readonly string path; 12 | 13 | /// 14 | /// NOTE: Seems like we can't cache this for some reason that's related to domain reloading. Not sure yet why. 15 | /// 16 | private FMOD.Studio.VCA FmodVca => RuntimeManager.GetVCA(path); 17 | 18 | public float VolumeLinear 19 | { 20 | get => FmodVca.getVolume(); 21 | set => FmodVca.setVolume(value); 22 | } 23 | 24 | public VCA(string path) 25 | { 26 | this.path = path; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Runtime/VCAs/VCA.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4aec168eb379b7b40877c98385434dbe 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/VCAs/VCAExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace FMOD.Studio 2 | { 3 | /// 4 | /// Extensions for VCA to get the path without an out parameter to make lambda functions easy to write. 5 | /// 6 | public static class VCAExtensions 7 | { 8 | public static string getPath(this FMOD.Studio.VCA vca) 9 | { 10 | vca.getPath(out string path); 11 | return path; 12 | } 13 | 14 | public static float getVolume(this FMOD.Studio.VCA vca) 15 | { 16 | vca.getVolume(out float volume); 17 | return volume; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Runtime/VCAs/VCAExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 12322f111decb53458fa73a921bd3197 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.roytheunissen.fmod-syntax", 3 | "displayName": "FMOD Syntax", 4 | "version": "0.2.3", 5 | "unity": "2021.3", 6 | "description": "Generates code to allow invoking FMOD events with a strongly-typed syntax.", 7 | "keywords": [ 8 | "FMOD", 9 | "Audio", 10 | "Code Generation" 11 | ], 12 | "author": { 13 | "name": "Roy Theunissen", 14 | "url": "https://www.roytheunissen.com" 15 | }, 16 | "category": "audio", 17 | "homepage": "https://github.com/RoyTheunissen/FMOD-Syntax", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/RoyTheunissen/FMOD-Syntax" 21 | }, 22 | "bugs": "https://github.com/RoyTheunissen/FMOD-Syntax/issues" 23 | } 24 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9381d3b6ee2b4dc47a6dccb6aacaed4a 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------