30using System.Collections.Generic;
31using System.Collections;
33using System.Globalization;
34using UnityEngine.Networking;
38#if UNITY_EDITOR || UNITY_STANDALONE
39using MYFLT = System.Double;
40#elif UNITY_ANDROID || UNITY_IOS
41using MYFLT = System.Single;
52 [SerializeField]
public string type =
"";
53 [SerializeField]
public string channel =
"";
54 [SerializeField]
public string text =
"";
55 [SerializeField]
public string caption =
"";
56 [SerializeField]
public float min;
57 [SerializeField]
public float max;
58 [SerializeField]
public float value;
59 [SerializeField]
public float skew;
63 public void SetRange(
float uMin,
float uMax,
float uValue = 0f,
float uSkew = 1f,
float uIncrement = 0.01f)
92 [SerializeField]
public string suffix;
93 [Tooltip(
"Utility bool to store if the drawn property is foldout or not")]
103 var path =
string.Empty;
108 path = GetPersistentDataPath(
platform, runtime);
111 path = GetStreamingAssetsPath(
platform, runtime);
114 path = GetPluginsPath(
platform, runtime);
120 path = Path.Combine(path,
suffix);
128 private string GetPersistentDataPath(
SupportedPlatform supportedPlatform,
bool runtime)
130 var res = Application.persistentDataPath;
131 switch (supportedPlatform)
135 $
"~/Library/Application Support/{Application.companyName}/{Application.productName}" :
136 Application.persistentDataPath;
140 $
"%userprofile%\\AppData\\LocalLow\\{Application.companyName}\\{Application.productName}" :
141 Application.persistentDataPath;
145 $
"/storage/emulated/0/Android/data/{Application.identifier}/files" : Application.persistentDataPath;
148 res = runtime ? $
"/var/mobile/Containers/Data/Application/{Application.identifier}/Documents" : Application.persistentDataPath;
157 private string GetStreamingAssetsPath(
SupportedPlatform supportedPlatform,
bool runtime)
159 var res = Application.streamingAssetsPath;
160 switch (supportedPlatform)
163 res = runtime ? $
"<path to player app bundle>/Contents/Resources/Data/StreamingAssets" : Application.streamingAssetsPath;
166 res = runtime ? $
"<path to executablename_Data folder>" : Application.streamingAssetsPath;
169 res = runtime ? $
"jar:file://storage/emulated/0/Android/data/{Application.identifier}/!/assets" : Application.streamingAssetsPath;
172 res = runtime ? $
"/var/mobile/Containers/Data/Application/{Application.identifier}/Raw/" : Application.streamingAssetsPath;
181 var res = Path.Combine(Application.dataPath,
"Plugins");
182 switch (supportedPlatform)
185 res = runtime ? $
"<path to player app bundle>/Contents/Resources/Data/Plugins" : res;
188 res = runtime ? $
"<path to executablename_Data folder>/Managed" : Path.Combine(Application.dataPath,
"Managed");
191#if UNITY_ANDROID && !UNITY_EDITOR
193 res = GetAndroidNativeLibraryDir();
195 res = runtime ? $
"/data/app/<random chars>/{Application.identifier}-<random chars>/lib/arm64-v8a" : res;
199 res = runtime ? $
"/var/mobile/Containers/Data/Application/{Application.identifier}/Plugins" : res;
206#if UNITY_ANDROID && !UNITY_EDITOR
207 public static AndroidJavaObject GetUnityActivity()
209 using (var unityPlayer =
new AndroidJavaClass(
"com.unity3d.player.UnityPlayer"))
211 return unityPlayer.GetStatic<AndroidJavaObject>(
"currentActivity");
215 public static AndroidJavaObject GetUnityContext()
217 var activity = GetUnityActivity();
219 return activity.Call<AndroidJavaObject>(
"getApplicationContext");
222 public static AndroidJavaObject GetApplicationInfo()
224 var context = GetUnityContext();
226 return GetUnityContext().Call<AndroidJavaObject>(
"getApplicationInfo");
229 public static string GetAndroidNativeLibraryDir()
231 var info = GetApplicationInfo();
233 return info.Get<
string>(
"nativeLibraryDir");
239 return type.ToString();
249 return $
"[{GetPlatformString()}]:[{GetTypeString()}]: {GetPath(runtime)}";
262#endregion PUBLIC_CLASSES
267[AddComponentMenu(
"Audio/CsoundUnity")]
269[RequireComponent(typeof(AudioSource))]
272 #region PUBLIC_FIELDS
306 public DefaultAsset csoundAsset {
get => _csoundAsset; }
320 [HideInInspector]
public bool mute =
false;
343 public List<CsoundChannelController>
channels {
get => _channels; }
389 #endregion PUBLIC_FIELDS
391 #region PRIVATE_FIELDS
402 [HideInInspector] [SerializeField]
private string _csoundFileGUID;
403 [HideInInspector] [SerializeField]
private string _csoundString;
404 [HideInInspector] [SerializeField]
private string _csoundFileName;
406 [HideInInspector] [SerializeField]
private DefaultAsset _csoundAsset;
408 [HideInInspector] [SerializeField]
private List<CsoundChannelController> _channels =
new List<CsoundChannelController>();
412 private Dictionary<string, int> _channelsIndexDict =
new Dictionary<string, int>();
413 [HideInInspector] [SerializeField]
private List<string> _availableAudioChannels =
new List<string>();
417#pragma warning disable 414
418 [HideInInspector] [SerializeField]
private bool _drawCsoundString =
false;
419 [HideInInspector] [SerializeField]
private bool _drawTestScore =
false;
420 [HideInInspector] [SerializeField]
private bool _drawSettings =
false;
421 [HideInInspector] [SerializeField]
private bool _drawChannels =
false;
422 [HideInInspector] [SerializeField]
private bool _drawAudioChannels =
false;
423 [HideInInspector] [SerializeField]
private bool _drawPresets =
false;
424 [HideInInspector] [SerializeField]
private bool _drawPresetsLoad =
false;
425 [HideInInspector] [SerializeField]
private bool _drawPresetsSave =
false;
426 [HideInInspector] [SerializeField]
private bool _drawPresetsImport =
false;
432 [HideInInspector] [SerializeField]
private bool _showRuntimeEnvironmentPath =
false;
433 [HideInInspector] [SerializeField]
private string _currentPreset;
434 [HideInInspector] [SerializeField]
private string _currentPresetSaveFolder;
435 [HideInInspector] [SerializeField]
private string _currentPresetLoadFolder;
439#pragma warning restore 414
441 private bool initialized =
false;
442 private uint ksmps = 32;
443 private uint ksmpsIndex = 0;
444 private float zerdbfs = 1;
445 private bool compiledOk =
false;
446 private bool performanceFinished;
447 private AudioSource audioSource;
448 private Coroutine LoggingCoroutine;
449 int bufferSize, numBuffers;
451 private const string GLOBAL_TAG =
"(GLOBAL)";
456 private Dictionary<string, MYFLT[]> namedAudioChannelTempBufferDict =
new Dictionary<string, MYFLT[]>();
471 AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers);
473 audioSource = GetComponent<AudioSource>();
474 audioSource.spatializePostEffects =
true;
479 var ac = AudioClip.Create(
"CsoundUnitySpatializerClip", 32, 1, AudioSettings.outputSampleRate,
false);
480 var data =
new float[32];
481 for (var i = 0; i < data.Length; i++) data[i] = 1;
484 audioSource.clip = ac;
485 audioSource.loop =
true;
498 for (
int i = 0; i <
channels.Count; i++)
500 if (
channels[i].type.Contains(
"combobox"))
505 if (!_channelsIndexDict.ContainsKey(
channels[i].channel))
506 _channelsIndexDict.Add(
channels[i].channel, i);
514 namedAudioChannelTempBufferDict.Add(name,
new MYFLT[ksmps]);
519 LoggingCoroutine = StartCoroutine(Logging(.01f));
527 Debug.Log($
"Csound zerdbfs: {zerdbfs}");
535 Debug.Log(
"Error creating Csound object");
539 Debug.Log($
"CsoundUnity done init, compiledOk? {compiledOk}");
542 #region PUBLIC_METHODS
544 #region INSTANTIATION
583 #endregion INSTANTIATION
596 if (
string.IsNullOrWhiteSpace(guid) || !Guid.TryParse(guid, out Guid guidResult))
598 Debug.LogWarning($
"GUID NOT VALID, Resetting fields");
603 var fileName = AssetDatabase.GUIDToAssetPath(guid);
605 if (
string.IsNullOrWhiteSpace(fileName) ||
606 fileName.Length < 4 ||
607 Path.GetFileName(fileName).Length < 4 ||
608 !Path.GetFileName(fileName).EndsWith(
".csd"))
610 Debug.LogWarning(
"FILENAME not valid, Resetting fields");
615 this._csoundFileGUID = guid;
616 this._csoundFileName = Path.GetFileName(fileName);
617 var csoundFilePath = Path.GetFullPath(fileName);
618 this._csoundAsset = (DefaultAsset)(AssetDatabase.LoadAssetAtPath(fileName, typeof(DefaultAsset)));
619 this._csoundString = File.ReadAllText(csoundFilePath);
622 foreach (var chan
in this._channels)
623 if (!_channelsIndexDict.ContainsKey(chan.channel))
624 _channelsIndexDict.Add(chan.channel, count++);
629 if (
string.IsNullOrWhiteSpace(name))
continue;
634 namedAudioChannelTempBufferDict.Add(name,
new MYFLT[ksmps]);
697 return csound.
GetSr();
706 return csound.
GetKr();
727 #endregion PERFORMANCE
738 if (!File.Exists(filename))
return null;
740 string[] fullCsdText = File.ReadAllLines(filename);
741 if (fullCsdText.Length < 1)
return null;
743 List<string> locaAudioChannels =
new List<string>();
745 foreach (
string line
in fullCsdText)
747 var trimmd = line.TrimStart();
748 if (!trimmd.Contains(
"chnset"))
continue;
749 if (trimmd.StartsWith(
";"))
continue;
750 var lndx = trimmd.IndexOf(
"chnset");
751 var chnsetEnd = lndx +
"chnset".Length + 1;
752 var prms = trimmd.Substring(chnsetEnd, trimmd.Length - chnsetEnd);
753 var split = prms.Split(
',');
754 if (!split[0].StartsWith(
"a") && !split[0].StartsWith(
"ga"))
757 var ach = split[1].Replace(
'\\',
' ').Replace(
'\"',
' ').Trim();
758 if (!locaAudioChannels.Contains(ach))
759 locaAudioChannels.Add(ach);
761 return locaAudioChannels;
769 public static List<CsoundChannelController>
ParseCsdFile(
string filename)
771 if (!File.Exists(filename))
return null;
773 string[] fullCsdText = File.ReadAllLines(filename);
774 if (fullCsdText.Length < 1)
return null;
776 List<CsoundChannelController> locaChannelControllers;
777 locaChannelControllers =
new List<CsoundChannelController>();
779 foreach (
string line
in fullCsdText)
782 if (line.Contains(
"</"))
785 var trimmd = line.TrimStart();
787 if (trimmd.StartsWith(
";"))
792 var control = trimmd.Substring(0, trimmd.IndexOf(
" ") > -1 ? trimmd.IndexOf(
" ") : 0);
793 if (control.Contains(
"slider") || control.Contains(
"button") || control.Contains(
"checkbox")
794 || control.Contains(
"groupbox") || control.Contains(
"form") || control.Contains(
"combobox"))
797 controller.
type = control;
798 if (trimmd.IndexOf(
"caption(") > -1)
800 string infoText = trimmd.Substring(trimmd.IndexOf(
"caption(") + 9);
801 infoText = infoText.Substring(0, infoText.IndexOf(
")") - 1);
805 if (trimmd.IndexOf(
"text(") > -1)
807 string text = trimmd.Substring(trimmd.IndexOf(
"text(") + 6);
808 text = text.Substring(0, text.IndexOf(
")") - 1);
809 text = text.Replace(
"\"",
"");
810 text = text.Replace(
'"',
new char());
811 controller.
text = text;
812 if (controller.
type ==
"combobox")
814 char[] delimiterChars = {
',' };
815 string[] tokens = text.Split(delimiterChars);
816 controller.
SetRange(1, tokens.Length, 0);
818 for (var o = 0; o < tokens.Length; o++)
820 tokens[o] =
string.Join(
"", tokens[o].Split(
default(
string[]), System.StringSplitOptions.RemoveEmptyEntries));
826 if (trimmd.IndexOf(
"items(") > -1)
828 string text = trimmd.Substring(trimmd.IndexOf(
"items(") + 7);
829 text = text.Substring(0, text.IndexOf(
")") - 1);
831 text = text.Replace(
"\"",
"");
832 text = text.Replace(
'"',
new char());
833 if (controller.
type ==
"combobox")
835 char[] delimiterChars = {
',' };
836 string[] tokens = text.Split(delimiterChars);
837 controller.
SetRange(1, tokens.Length, 0);
839 for (var o = 0; o < tokens.Length; o++)
841 tokens[o] =
string.Join(
"", tokens[o].Split(
default(
string[]), System.StringSplitOptions.RemoveEmptyEntries));
847 if (trimmd.IndexOf(
"channel(") > -1)
849 string channel = trimmd.Substring(trimmd.IndexOf(
"channel(") + 9);
850 channel = channel.Substring(0, channel.IndexOf(
")") - 1);
854 if (trimmd.IndexOf(
"range(") > -1)
856 int rangeAt = trimmd.IndexOf(
"range(");
859 string range = trimmd.Substring(rangeAt + 6);
860 range = range.Substring(0, range.IndexOf(
")"));
861 char[] delimiterChars = {
',' };
862 string[] tokens = range.Split(delimiterChars);
863 for (var i = 0; i < tokens.Length; i++)
865 tokens[i] =
string.Join(
"", tokens[i].Split(
default(
string[]), StringSplitOptions.RemoveEmptyEntries));
866 if (tokens[i].StartsWith(
"."))
868 tokens[i] =
"0" + tokens[i];
870 if (tokens[i].StartsWith(
"-."))
872 tokens[i] =
"-0" + tokens[i].Substring(2, tokens[i].Length - 2);
875 var min =
float.Parse(tokens[0], CultureInfo.InvariantCulture);
876 var max =
float.Parse(tokens[1], CultureInfo.InvariantCulture);
881 if (tokens.Length > 2)
883 val =
float.Parse(tokens[2], CultureInfo.InvariantCulture);
885 if (tokens.Length > 3)
887 skew =
float.Parse(tokens[3], CultureInfo.InvariantCulture);
889 if (tokens.Length > 4)
891 increment =
float.Parse(tokens[4], CultureInfo.InvariantCulture);
894 controller.
SetRange(min, max, val, skew, increment);
898 if (line.IndexOf(
"value(") > -1)
900 string value = trimmd.Substring(trimmd.IndexOf(
"value(") + 6);
901 value = value.Substring(0, value.IndexOf(
")"));
902 value = value.Replace(
"\"",
"");
903 controller.
value = value.Length > 0 ?
float.Parse(value, CultureInfo.InvariantCulture) : 0;
904 if (control.Contains(
"combobox"))
911 locaChannelControllers.Add(controller);
914 return locaChannelControllers;
953 Debug.Log(
"clear spin");
987 #endregion IO_BUFFERS
989 #region CONTROL_CHANNELS
1003 if (_channelsIndexDict.ContainsKey(channel))
1005 if (
channels[_channelsIndexDict[channel]].type.Contains(
"combobox"))
1009 channels[_channelsIndexDict[channel]].value = (float)val;
1019 if (_channelsIndexDict.ContainsKey(channelController.
channel))
1020 channels[_channelsIndexDict[channelController.
channel]] = channelController;
1021 if (csound ==
null)
return;
1030 public void SetChannels(List<CsoundChannelController> channelControllers,
bool excludeButtons =
true)
1032 for (var i = 0; i < channelControllers.Count; i++)
1034 if (excludeButtons && channelControllers[i].type.Contains(
"button"))
continue;
1067 if (!_channelsIndexDict.ContainsKey(channel))
return null;
1068 var indx = _channelsIndexDict[channel];
1069 return this._channels[indx];
1084 #endregion CONTROL_CHANNELS
1086 #region AUDIO_CHANNELS
1098 #endregion AUDIO_CHANNELS
1124 if (samples.Length < 1)
return -1;
1144 string createTableInstrument = String.Format(
@"gisampletable{0} ftgen {0}, 0, {1}, -7, 0, 0", tableNumber, -tableLength );
1167 return csound.
GetTable(tableNumber, index);
1177 public int GetTable(out MYFLT[] tableValues,
int numTable)
1179 return csound.
GetTable(out tableValues, numTable);
1202 public void SetTable(
int table,
int index, MYFLT value)
1204 csound.
SetTable(table, index, value);
1302 public string GetFilePath()
1304 return Path.Combine(Application.dataPath.Substring(0, Application.dataPath.Length -
"Assets".Length), AssetDatabase.GUIDToAssetPath(
csoundFileGUID));
1340 return csound.
GetEnv(envType.ToString());
1410 public static float Remap(
float value,
float from1,
float to1,
float from2,
float to2,
bool clamp =
false)
1412 float retValue = (value - from1) / (to1 - from1) * (to2 - from2) + from2;
1413 if (
float.IsNaN(retValue))
return from2;
1414 if (
float.IsPositiveInfinity(retValue))
return to2;
1415 if (
float.IsNegativeInfinity(retValue))
return from2;
1416 return clamp ? Mathf.Clamp(retValue, from2, to2) : retValue;
1429 public static float RemapTo0to1(
float value,
float from,
float to,
float skew = 1f)
1431 if ((to - from) == 0)
return 0;
1433 var proportion = Mathf.Clamp01((value - from) / (to - from));
1438 return Mathf.Pow(proportion, skew);
1452 public static float RemapFrom0to1(
float value,
float from,
float to,
float skew = 1f)
1454 if (skew == 0)
return to;
1456 var proportion = Mathf.Clamp01(value);
1458 if (skew != 1 && proportion > 0)
1459 proportion = Mathf.Exp(Mathf.Log(proportion) / skew);
1461 return from + (to - from) * proportion;
1471 if (samples ==
null || samples.Length == 0)
return new MYFLT[0];
1472 var myFLT =
new MYFLT[samples.Length];
1473 for (var i = 0; i < myFLT.Length; i++)
1475 myFLT[i] = (MYFLT)samples[i];
1487 if (samples ==
null || samples.Length == 0)
return new float[0];
1488 var flt =
new float[samples.Length];
1489 for (var i = 0; i < flt.Length; i++)
1491 flt[i] = (float)samples[i];
1529 return GetSamples(source, channelNumber,
false);
1557 public static MYFLT[]
GetSamples(
string source,
int channelNumber = 1,
bool writeChannelData =
false)
1559 MYFLT[] res =
new MYFLT[0];
1561 var src = Resources.Load<AudioClip>(source);
1564 Debug.LogError($
"Couldn't load samples from AudioClip {source}");
1568 var data =
new float[src.samples * src.channels];
1569 src.GetData(data, 0);
1571 if (writeChannelData)
1573 res =
new MYFLT[src.samples * src.channels + 1];
1574 res[0] = src.channels;
1576 for (var i = 0; i < data.Length; i++)
1585 res =
new MYFLT[src.samples];
1587 for (var i = 0; i < data.Length; i += src.channels, s++)
1589 res[s] = data[i + (channelNumber - 1)];
1603 public static float[]
GetFloatSamples(
string source,
int channelNumber = 1,
bool writeChannelData =
false)
1630 var req = Resources.LoadAsync<AudioClip>(source);
1636 var samples = ((AudioClip)req.asset).samples;
1639 onSamplesLoaded?.Invoke(
null);
1642 onSamplesLoaded?.Invoke(
GetSamples((AudioClip)req.asset));
1645 var path = Path.Combine(Application.streamingAssetsPath, source);
1646 yield
return LoadingClip(path, (clip) =>
1652 yield
return LoadingClip(source, (clip) =>
1667 var data =
new float[audioClip.samples * audioClip.channels];
1668 audioClip.GetData(data, 0);
1669 MYFLT[] res =
new MYFLT[data.Length];
1671 foreach (var d
in data)
1679 static IEnumerator LoadingClip(
string path, Action<AudioClip> onEnd)
1681 var ext = Path.GetExtension(path);
1687 case "MP3": type = AudioType.MPEG;
break;
1689 case "OGG": type = AudioType.OGGVORBIS;
break;
1692 default: type = AudioType.WAV;
break;
1696 path =
"file://" + path;
1698 path =
"file:///" + path;
1701 using (var req = UnityWebRequestMultimedia.GetAudioClip(path, type))
1703 yield
return req.SendWebRequest();
1705#if UNITY_2020_1_OR_NEWER
1706 if (req.result == UnityWebRequest.Result.ConnectionError ||
1707 req.result == UnityWebRequest.Result.DataProcessingError ||
1708 req.result == UnityWebRequest.Result.ProtocolError)
1710 Debug.LogError($
"Couldn't load file at path: {path} \n{req.error}");
1711 onEnd?.Invoke(
null);
1715 if (req.isHttpError || req.isNetworkError)
1717 Debug.LogError($
"Couldn't load file at path: {path} \n{req.error}");
1718 onEnd?.Invoke(
null);
1722 var clip = DownloadHandlerAudioClip.GetContent(req);
1726 Debug.LogError(
"The loaded clip is null!");
1730 clip.name = Path.GetFileName(path);
1731 onEnd?.Invoke(clip);
1766 preset.name = preset.
presetName =
string.IsNullOrWhiteSpace(presetName) ?
"CsoundUnityPreset" : presetName;
1768 preset.channels =
new List<CsoundChannelController>();
1771 var newChan = chan.Clone();
1772 preset.channels.Add(newChan);
1795 JsonUtility.FromJsonOverwrite(presetData, preset);
1797 catch (ArgumentException ex)
1799 Debug.LogError($
"Couldn't set Preset {presetName}, {ex.Message}");
1802 preset.name = preset.
presetName =
string.IsNullOrWhiteSpace(presetName) ?
1803 string.IsNullOrWhiteSpace(preset.presetName) ?
1804 "CsoundUnityPreset" : preset.presetName : presetName;
1819 if (
string.IsNullOrWhiteSpace(path) || !path.Contains(
"Assets"))
1821 Debug.LogWarning(
"CsoundUnityPreset scriptable object cannot be created outside of the project folder, " +
1822 "defaulting to 'Assets'. Use the JSON format to save it outside of the Application.dataPath folder.");
1823 path = Application.dataPath;
1827 var assetsIndex = path.IndexOf(
"Assets");
1829 if (assetsIndex <
"Assets".Length)
1831 Debug.LogError(
"Error, couldn't find the Assets folder!");
1835 path = path.Substring(assetsIndex, path.Length - assetsIndex);
1837 if (!File.Exists(path))
1839 Directory.CreateDirectory(path);
1842 var fullPath = Path.Combine(path, $
"{preset.presetName}.asset");
1845 var assetLength =
".asset".Length;
1846 var basePath = $
"{fullPath.Substring(0, fullPath.Length - assetLength)}";
1848 var overwriteAction =
new Action(() =>
1850 AssetDatabase.DeleteAsset(fullPath);
1851 AssetDatabase.CreateAsset(preset, fullPath);
1852 Debug.Log($
"Overwriting CsoundUnityPreset at path {fullPath}");
1854 var renameAction =
new Action(() =>
1859 preset.presetName = $
"{baseName}_{count}";
1860 fullPath = $
"{basePath}_{count}.asset";
1864 Debug.Log($
"Saving CsoundUnityPreset at path {fullPath}");
1865 AssetDatabase.CreateAsset(preset, fullPath);
1868 var message = $
"CsoundUnityPreset at {fullPath} already exists, overwrite or rename?";
1869 var res = EditorUtility.DisplayDialogComplex(
"Overwrite?", message,
"Overwrite",
"Cancel",
"Rename");
1873 overwriteAction.Invoke();
1879 renameAction.Invoke();
1885 Debug.Log($
"Creating new CsoundUnityPreset at {fullPath}");
1886 AssetDatabase.CreateAsset(preset, fullPath);
1888 AssetDatabase.SaveAssets();
1889 AssetDatabase.Refresh();
1917 var fullPath = CheckPathForExistence(path, preset.
presetName, overwriteIfExisting);
1918 var presetData = JsonUtility.ToJson(preset,
true);
1921 Debug.Log($
"Saving JSON preset at {fullPath}");
1922 File.WriteAllText($
"{fullPath}", presetData);
1924 catch (IOException ex)
1926 Debug.Log(ex.Message);
1929 AssetDatabase.Refresh();
1946 presetName =
string.IsNullOrWhiteSpace(presetName) ?
"CsoundUnityPreset" : presetName;
1947 preset.presetName = presetName;
1959 public void SavePresetAsJSON(
string presetName,
string path =
null,
bool overwriteIfExisting =
false)
1963 presetName =
string.IsNullOrWhiteSpace(presetName) ?
"CsoundUnityPreset" : presetName;
1964 preset.presetName = presetName;
1974 public void SaveGlobalPreset(
string presetName,
string path =
null,
bool overwriteIfExisting =
false)
1976 var presetData = JsonUtility.ToJson(
this,
true);
1979 var name = $
"{presetName} {GLOBAL_TAG}";
1980 var fullPath = CheckPathForExistence(path, name, overwriteIfExisting);
1981 Debug.Log($
"Saving global preset at {fullPath}");
1982 File.WriteAllText(fullPath, presetData);
1984 catch (IOException ex)
1986 Debug.Log(ex.Message);
1989 AssetDatabase.Refresh();
2053 Debug.LogError($
"Couldn't set preset {preset.presetName} to this CsoundUnity instance {this.name}, " +
2054 $
"this instance uses csd: {this.csoundFileName}, the preset was saved with csd: {preset.csoundFileName} instead");
2059 Debug.LogError(
"Couldn't load a null CsoundUnityPreset!");
2079 JsonUtility.FromJsonOverwrite(presetData,
this);
2082 var current =
string.IsNullOrWhiteSpace(presetName) ? this._currentPreset : presetName;
2083 _currentPreset = $
"{current} {GLOBAL_TAG}";
2105 public void LoadPreset(
string path, Action<CsoundUnityPreset> onPresetLoaded =
null)
2107 var presetName = Path.ChangeExtension(Path.GetFileName(path),
null);
2109#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
2110 StartCoroutine(LoadingData(path, (d) =>
2114 onPresetLoaded?.Invoke(
null);
2117 onPresetLoaded?.Invoke(preset);
2120 if (!File.Exists(path))
2122 Debug.LogError($
"Preset JSON not found at path {path}");
2125 var data = File.ReadAllText(path);
2129 Debug.LogError(
"Couldn't create preset from path: {path}");
2130 onPresetLoaded?.Invoke(
null);
2133 onPresetLoaded?.Invoke(preset);
2147 var presetName = Path.GetFileName(path);
2149#if UNITY_ANDROID || UNITY_IOS
2150 StartCoroutine(LoadingData(path, (d) =>
2155 if (!File.Exists(path))
2157 Debug.LogError($
"Global Preset JSON not found at path {path}");
2160 var data = File.ReadAllText(path);
2171 public static List<CsoundUnityPreset>
ParseSnap(
string csdPath,
string snapPath)
2175 var snap = File.ReadAllText(snapPath);
2176 var snapStart = snap.IndexOf(
"{");
2177 var snapEnd = snap.LastIndexOf(
"}");
2178 var presets = snap.Substring(snapStart + 1, snapEnd - snapStart - 2);
2179 var csdName = Path.GetFileName(csdPath);
2182 var parsedPresets = ParsePresets(csdName, presets);
2184 if (originalChannels ==
null || originalChannels.Count == 0)
2186 if (originalChannels ==
null || originalChannels.Count == 0)
2188 Debug.LogWarning($
"Couldn't fix preset channels for snap {snapPath}, csd path: {csdPath}, preset channels will not be visible on Editor, " +
2189 $
"but you should still be able to use them. Be aware that Comboboxes will be broken. " +
2190 $
"Please ensure that a '.csd' file with the same name of the '.snaps' file is present at the same location.");
2191 return parsedPresets;
2194 foreach (var preset
in parsedPresets)
2196 FixPresetChannels(originalChannels, preset.channels);
2198 Debug.Log($
"originalChannels: {originalChannels.Count}");
2199 return parsedPresets;
2202 private static List<CsoundUnityPreset> ParsePresets(
string snapName,
string presets)
2204 var parsedPresets =
new List<CsoundUnityPreset>();
2205 var splitPresets = presets.Split(
new string[] {
"}," }, StringSplitOptions.None);
2207 foreach (var preset
in splitPresets)
2210 parsedPresets.Add(ParsePreset(snapName, preset));
2221 return parsedPresets;
2226 var presetNameStart = preset.IndexOf(
"\"");
2227 var subPreset = preset.Substring(presetNameStart + 1, preset.Length - presetNameStart - 1);
2229 var presetNameEnd = subPreset.IndexOf(
"\"");
2230 var presetName = subPreset.Substring(0, presetNameEnd);
2232 var presetContentStart = preset.IndexOf(
"{");
2233 var presetContent = preset.Substring(presetContentStart + 1, preset.Length - presetContentStart - 1);
2235 var splitPresetContent = presetContent.Split(
new string[] {
"," }, StringSplitOptions.None);
2236 var presetChannels =
new List<CsoundChannelController>();
2237 foreach (var chan
in splitPresetContent)
2240 presetChannels.Add(ParseChannel(chan).Clone());
2243 return CreatePreset(presetName, snapName, presetChannels);
2248 var split = chan.Split(
new string[] {
":" }, StringSplitOptions.None);
2249 var chanName = split[0];
2250 var chanValue = split[1];
2252 var cleanChanName = chanName.Replace(
"\"",
"").Trim();
2253 float.TryParse(chanValue, out
float chanValueFloat);
2257 channel = cleanChanName,
2258 value = chanValueFloat
2264 private static void FixPresetChannels(List<CsoundChannelController> originalChannels, List<CsoundChannelController> channelsToFix)
2266 if (originalChannels ==
null || originalChannels.Count == 0)
2268 Debug.LogError(
"Couldn't fix preset channels, aborting");
2272 foreach (var chanToFix
in channelsToFix)
2274 foreach (var chan
in originalChannels)
2276 if (chan.channel == chanToFix.channel)
2281 chanToFix.
caption = chan.caption;
2282 chanToFix.increment = chan.increment;
2283 chanToFix.max = chan.max;
2284 chanToFix.min = chan.min;
2285 chanToFix.options = chan.options;
2286 chanToFix.skew = chan.skew;
2287 chanToFix.text = chan.text;
2288 chanToFix.type = chan.type;
2291 if (chanToFix.type.Equals(
"combobox"))
2293 chanToFix.value -= 1;
2300 static IEnumerator LoadingData(
string path, Action<string> onDataLoaded)
2302 Debug.Log($
"Loading JSON data from path: {path}");
2303 using (var request = UnityWebRequest.Get(path))
2305 request.downloadHandler =
new DownloadHandlerBuffer();
2306 yield
return request.SendWebRequest();
2307 if (request.isNetworkError || request.isHttpError)
2309 Debug.Log($
"Couldn't load data at path: {path}: {request.error}");
2310 onDataLoaded?.Invoke(
null);
2313 var data = request.downloadHandler.text;
2314 onDataLoaded?.Invoke(data);
2318 private static string CheckPathForExistence(
string path,
string presetName,
bool overwriteIfExisting)
2320 path =
string.IsNullOrWhiteSpace(path) ?
2321 Application.persistentDataPath
2323 if (!
char.IsSeparator(path[path.Length - 1]))
2328 if (!Directory.Exists(path))
2332 Directory.CreateDirectory(path);
2334 catch (IOException ex)
2336 Debug.LogError($
"Couldn't create folder at: {path}, defaulting to {Application.persistentDataPath} {ex.Message}");
2337 path = Application.persistentDataPath;
2341 var fullPath = Path.Combine(path, presetName +
".json");
2344 while (File.Exists(fullPath) && !overwriteIfExisting)
2346 fullPath = Path.Combine(path, $
"{presetName}_{count++}.json");
2353 #endregion UTILITIES
2355 #endregion PUBLIC_METHODS
2471 #region PRIVATE_METHODS
2473 void OnAudioFilterRead(
float[] data,
int channels)
2486 private void ProcessBlock(
float[] samples,
int numChannels)
2488 if (compiledOk && initialized && !_quitting)
2490 for (
int i = 0; i < samples.Length; i += numChannels, ksmpsIndex++)
2492 for (uint channel = 0; channel < numChannels; channel++)
2496 if (_quitting)
return;
2499 samples[i + channel] = 0.0f;
2505 performanceFinished = res == 1;
2510 if (!namedAudioChannelTempBufferDict.ContainsKey(chanName))
continue;
2511 namedAudioChannelTempBufferDict[chanName] =
GetAudioChannel(chanName);
2517 SetInputSample((
int)ksmpsIndex, (
int)channel, samples[i + channel] * zerdbfs);
2522 var output = (float)
GetOutputSample((
int)ksmpsIndex, (int)outputSampleChannel) / zerdbfs;
2525 samples[i + channel] =
processClipAudio ? output : samples[i + channel] * output;
2529 samples[i + channel] = 0.0f;
2530 Debug.LogWarning(
"Volume is too high! Clearing output");
2539 if (!
namedAudioChannelDataDict.ContainsKey(chanName) || !namedAudioChannelTempBufferDict.ContainsKey(chanName))
continue;
2550 void LogCsoundMessages()
2562 IEnumerator Logging(
float interval)
2566 while (this.logCsoundOutput)
2570 if (this.logCsoundOutput)
2576 yield
return new WaitForSeconds(interval);
2586 private void ResetFields()
2589 this._csoundAsset =
null;
2592 this._csoundFileName =
null;
2593 this._csoundString =
null;
2594 this._csoundFileGUID =
string.Empty;
2596 this._channels.Clear();
2597 this._availableAudioChannels.Clear();
2599 this.namedAudioChannelDataDict.Clear();
2600 this.namedAudioChannelTempBufferDict.Clear();
2603 private bool _quitting =
false;
2607 void OnApplicationQuit()
2610 if (LoggingCoroutine !=
null)
2611 StopCoroutine(LoggingCoroutine);
2619 #endregion PRIVATE_METHODS
EnvironmentPathOrigin
The base folder where to set the Environment Variables
Utility class for controller and channels
void SetRange(float uMin, float uMax, float uValue=0f, float uSkew=1f, float uIncrement=0.01f)
CsoundChannelController Clone()
Defines a class to hold out and in types, and flags
int CompileOrc(string orchStr)
void SetTable(int table, int index, MYFLT value)
Sets the value of a slot in a function table. The table number and index are assumed to be valid.
IDictionary< string, int > GetNamedGens()
Returns a Dictionary keyed by the names of all named table generators. Each name is paired with its i...
long GetCurrentTimeSamples()
uint GetNchnls()
Get number of input channels
void SetSpinSample(int frame, int channel, MYFLT sample)
Set a sample from Csound's audio output buffer
IDictionary< string, IList< OpcodeArgumentTypes > > GetOpcodeList()
Returns a sorted Dictionary keyed by all opcodes which are active in the current instance of csound....
void GetNamedGEN(int num, out string name, int len)
Gets the GEN name from a number num, if this is a named GEN The final parameter is the max len of the...
MYFLT[] GetAudioChannel(string name)
int GetTableArgs(out MYFLT[] args, int index)
Stores the arguments used to generate function table 'tableNum' in args, and returns the number of ar...
void ClearSpin()
Clears the input buffer (spin).
uint GetNchnlsInput()
Get number of input channels
MYFLT GetTable(int table, int index)
Returns the value of a slot in a function table. The table number and index are assumed to be valid.
void TableCopyIn(int table, MYFLT[] source)
Copy the contents of an array source into a given function table The table number is assumed to be va...
void SendScoreEvent(string scoreEvent)
void SetParams(CSOUND_PARAMS parms)
Transfers the contents of the provided raw CSOUND_PARAMS object into csound's internal data structues...
void TableCopyOutAsync(int table, out MYFLT[] dest)
Asynchronous version of tableCopyOut()
void SetStringChannel(string channel, string value)
int GetCsoundMessageCount()
string GetCsoundMessage()
void AddSpinSample(int frame, int channel, MYFLT sample)
CSOUND_PARAMS GetParams()
Fills in a provided raw CSOUND_PARAMS object with csounds current parameter settings....
string GetEnv(string key)
Gets a string value from csound's environment values. Meaningful values include the contents of Windo...
int LoadPlugins(string dir)
IDictionary< string, ChannelInfo > GetChannelList()
Provides a dictionary of all currently defined channels resulting from compilation of an orchestra co...
bool CompiledWithoutError()
MYFLT GetSpoutSample(int frame, int channel)
Get a sample from Csound's audio output buffer
MYFLT GetChannel(string channel)
Get a a control channel
void TableCopyInAsync(int table, MYFLT[] source)
Asynchronous version of csoundTableCopyIn()
int SetGlobalEnv(string name, string value)
Set the global value of environment variable 'name' to 'value', or delete variable if 'value' is NULL...
int IsNamedGEN(int num)
Checks if a given GEN number num is a named GEN if so, it returns the string length (excluding termin...
void CsoundSetScoreOffsetSeconds(MYFLT value)
void TableCopyOut(int table, out MYFLT[] dest)
Copy the contents of a function table into a supplied array dest The table number is assumed to be va...
void SetChannel(string channel, MYFLT value)
int TableLength(int table)
Returns the length of a function table (not including the guard point), or -1 if the table does not e...
MYFLT[] GetSpout()
Returns the Csound audio output working buffer (spout) as a MYFLT array. Enables external software to...
MYFLT[] GetSpin()
Returns the Csound audio input working buffer (spin) as a MYFLT array. Enables external software to w...
int CreateTable(int tableNumber, MYFLT[] samples)
Creates a table with the supplied samples. Can be called during performance.
string csoundFileName
The file CsoundUnity will try to load. You can only load one file with each instance of CsoundUnity,...
CsoundInitialized OnCsoundInitialized
An event that will be executed when Csound is initialized
int CreateTableInstrument(int tableNumber, int tableLength)
Creates an empty table, to be filled with samples later. Please note that trying to read the samples ...
readonly Dictionary< string, MYFLT[]> namedAudioChannelDataDict
public named audio Channels shown in CsoundUnityChild inspector
void CopyTableOutAsync(int table, out MYFLT[] dest)
Asynchronous version of CopyTableOut.
long GetCurrentTimeSamples()
Returns the current performance time in samples
uint GetNchnls()
Get the number of output channels
MYFLT[] GetSpin()
Get Csound's audio input buffer
string csoundString
a string to hold all the csoundFile content
void LoadGlobalPreset(string path)
Load a global preset on this CsoundUnity instance from a JSON file found at path. The JSON file shoul...
List< CsoundChannelController > channels
list to hold channel data
void SetStringChannel(string channel, string val)
Sets a string channel in Csound. Used in connection with a chnget opcode in your Csound instrument.
static float[] GetFloatSamples(string source, int channelNumber=1, bool writeChannelData=false)
Same as GetSamples but it will return a float array.
int GetTableArgs(out MYFLT[] args, int index)
Stores the arguments used to generate function table 'tableNum' in args, and returns the number of ar...
void ConvertPresetToScriptableObject(string path, string destination)
Convert a JSON preset into a Scriptable Object preset to be written at the specified path....
static MYFLT[] GetSamples(string source, int channelNumber=1, bool writeChannelData=false)
Get Samples from a "Resources" path. This will return an interleaved array of samples,...
CsoundUnityPreset SetPreset(string presetData)
Set a CsoundUnityPreset to this CsoundUnity instance using presetData.
CsoundUnityPreset SetPreset(string presetName, string presetData)
Set a CsoundUnityPreset to this CsoundUnity instance using a presetName and presetData....
static MYFLT[] ConvertToMYFLT(float[] samples)
Utility method to create an array of MYFLTs from an array of floats
int PerformKsmps()
Process a ksmps-sized block of samples
void SetChannel(string channel, MYFLT val)
Sets a Csound channel. Used in connection with a chnget opcode in your Csound instrument.
CsoundUnity SetGlobalPreset(string presetName, string presetData)
Set a global preset to this CsoundUnity instance using a presetName and presetData.
void SetCsd(string guid)
Sets the csd file
static List< CsoundChannelController > ParseCsdFile(string filename)
Parse the csd file
MYFLT[] GetSpout()
Get Csound's audio output buffer
static MYFLT[] GetSamples(AudioClip audioClip)
Get samples from an AudioClip as a MYFLT array.
bool loudVolumeWarning
If true it will print warnings in the console when the output volume is too high, and mute all the sa...
static CsoundUnityPreset CreatePreset(string presetName, string csoundFileName, List< CsoundChannelController > channels)
Create a CsoundUnityPreset from a presetName, csoundFileName and a list of CsoundChannelControllers
uint GetKsmps()
Get the number of audio sample frames per control sample.
uint GetNchnlsInputs()
Get the number of input channels
static float RemapFrom0to1(float value, float from, float to, float skew=1f)
Remap a normalized (0-1) value to a value in another range, specifying its "from" and "to" values,...
int GetAPIVersion()
Returns the Csound API version number times 100 (1.00 = 100).
SamplesOrigin
Where the samples to load come from:
static MYFLT[] GetMonoSamples(string source, int channelNumber)
Get an array of MYFLTs from the AudioClip source from the Resources folder. No information about the ...
void SetPreset(CsoundUnityPreset preset)
Set a CsoundUnityPreset to this CsoundUnity instance.
bool processClipAudio
If true Csound uses as an input the AudioClip attached to this AudioSource If false,...
static float RemapTo0to1(float value, float from, float to, float skew=1f)
Remap a value to a normalized (0-1) value specifying its expected "from" and "to" values,...
static void SavePresetAsJSON(List< CsoundChannelController > channels, string csoundFileName, string presetName, string path=null, bool overwriteIfExisting=false)
Save a preset as JSON from a list of CsoundChannelController, specifying the related CsoundFileName a...
void SetTable(int table, int index, MYFLT value)
Sets the value of a slot in a function table. The table number and index are assumed to be valid.
void SendScoreEvent(string scoreEvent)
Send a score event to Csound in the form of "i1 0 10 ....
void AddInputSample(int frame, int channel, MYFLT sample)
Adds the indicated sample into the audio input working buffer (spin); this only ever makes sense befo...
CsoundChannelController GetChannelController(string channel)
Get a serialized CsoundChannelController
MYFLT Get0dbfs()
Get 0 dbfs
int GetTable(out MYFLT[] tableValues, int numTable)
Stores values to function table 'numTable' in tableValues, and returns the table length (not includin...
void SavePresetAsJSON(string presetName, string path=null, bool overwriteIfExisting=false)
Save a preset as JSON using CsoundChannelControllers and CsoundFileName from this CsoundUnity instanc...
static float[] GetStereoFloatSamples(string source)
Get an array of floats from the AudioClip source from the Resources folder. The first index in the re...
void ClearSpin()
Clears the input buffer (spin).
void LoadPreset(string path, Action< CsoundUnityPreset > onPresetLoaded=null)
Loads a preset from a JSON file. The JSON file should represent a CsoundUnityPreset.
MYFLT GetSr()
Get the current sample rate
float loudWarningThreshold
The volume threshold at which a warning is output to the console, and audio output is filtered
List< EnvironmentSettings > environmentSettings
The list of the Environment Settings that will be set on start, using csoundSetGlobalEnv....
IDictionary< string, IList< CsoundUnityBridge.OpcodeArgumentTypes > > GetOpcodeList()
Get the Opcode List, blocking
void CopyTableInAsync(int table, MYFLT[] source)
Asynchronous version of CopyTableIn
string csoundFileGUID
the unique guid of the csd file
bool logCsoundOutput
If true, all Csound output messages will be sent to the Unity output console. Note that this can slow...
int SetGlobalEnv(string name, string value)
Set the global value of environment variable 'name' to 'value', or delete variable if 'value' is NULL...
void RewindScore()
Rewinds a compiled Csound score to the time specified with SetScoreOffsetSeconds().
bool mute
If true no audio is sent to output
void GetNamedGEN(int num, out string name, int len)
Gets the GEN name from a number num, if this is a named GEN The final parameter is the max len of the...
void CopyFloatTableIn(int table, float[] source)
Same as CopyTableIn but passing a float array.
static float Remap(float value, float from1, float to1, float from2, float to2, bool clamp=false)
Linear remap floats within one range to another
void SaveGlobalPreset(string presetName, string path=null, bool overwriteIfExisting=false)
Save a serialized copy of this CsoundUnity instance. Similar behaviour as saving a Unity Preset from ...
int GetTableLength(int table)
Returns the length of a function table (not including the guard point), or -1 if the table does not e...
static float[] GetMonoFloatSamples(string source, int channelNumber)
Get an array of floats from the AudioClip source from the Resources folder. No information about the ...
string CurrentPreset
The current preset name. If empty, no preset has been set.
const string packageVersion
The version of this package
void SetChannel(CsoundChannelController channelController)
Sets a Csound channel. Useful for setting presets at runtime. Used in connection with a chnget opcode...
static void SavePresetAsJSON(CsoundUnityPreset preset, string path=null, bool overwriteIfExisting=false)
Save the specified CsoundUnityPreset as JSON, at the specified path. If a file exists at the specifie...
MYFLT GetOutputSample(int frame, int channel)
Get a sample from Csound's audio output buffer
void CopyTableOut(int table, out MYFLT[] dest)
Copy the contents of a function table into a supplied array dest The table number is assumed to be va...
void SavePresetAsScriptableObject(string presetName, string path=null)
Save a CsoundUnityPreset of this CsoundUnity instance at the specified path, using the specified pres...
static void WritePreset(CsoundUnityPreset preset, string path)
Write a CsoundUnityPreset at the specified path inside the Assets folder. You can pass a full path,...
int LoadPlugins(string dir)
Loads all plugins from a given directory
EnvType
The enum representing the Csound Environment Variables
static CsoundUnityPreset CreatePreset(string presetName, string presetData)
Create a CsoundUnityPreset from a presetName and presetData.
void SetInputSample(int frame, int channel, MYFLT sample)
Set a sample in Csound's input buffer
delegate void CsoundInitialized()
The delegate of the event OnCsoundInitialized
static List< CsoundUnityPreset > ParseSnap(string csdPath, string snapPath)
Parse a Cabbage Snap and return a list of CsoundUnityPresets.
IDictionary< string, CsoundUnityBridge.ChannelInfo > GetChannelList()
Blocking method to get a list of the channels from Csound, not from the serialized list of this insta...
MYFLT GetTableSample(int tableNumber, int index)
Retrieves a single sample from a Csound function table.
string csoundScore
the score to send via editor
void Cleanup()
Prints information about the end of a performance, and closes audio and MIDI devices....
int IsNamedGEN(int num)
Checks if a given GEN number num is a named GEN if so, it returns the string length (excluding termin...
bool CompiledWithoutError()
Returns true if the csd file was compiled without errors.
void SetChannels(List< CsoundChannelController > channelControllers, bool excludeButtons=true)
Sets a list of Csound channels.
void SetParams(CsoundUnityBridge.CSOUND_PARAMS parms)
Transfers the contents of the provided raw CSOUND_PARAMS object into csound's internal data structues...
void CsoundReset()
Resets all internal memory and state in preparation for a new performance. Enables external software ...
MYFLT GetChannel(string channel)
Gets a Csound channel. Used in connection with a chnset opcode in your Csound instrument.
List< string > availableAudioChannels
list to hold available audioChannels names
int CompileOrc(string orcStr)
Parse, and compile the given orchestra from an ASCII string, also evaluating any global space code (i...
bool IsInitialized
Is Csound initialized?
static List< string > ParseCsdFileForAudioChannels(string filename)
Parse the csd and returns available audio channels (set in csd via:
string GetEnv(EnvType envType)
Get Environment path.
void CopyTableIn(int table, MYFLT[] source)
Copy the contents of a supplied array into a function table The table number is assumed to be valid,...
const string packageName
The name of this package
IDictionary< string, int > GetNamedGens()
Returns a Dictionary keyed by the names of all named table generators. Each name is paired with its i...
int GetVersion()
Returns the Csound version number times 1000 (5.00.0 = 5000).
static IEnumerator GetSamples(string source, SamplesOrigin origin, Action< MYFLT[]> onSamplesLoaded)
Async version of GetSamples
MYFLT GetKr()
Get the current control rate
static MYFLT[] GetStereoSamples(string source)
Get an array of MYFLTs in from the AudioClip source from the Resources folder. The first index in the...
int CreateFloatTable(int tableNumber, float[] samples)
Creates a table with the supplied float samples. Can be called during performance.
void SetScoreOffsetSeconds(MYFLT value)
Csound score events prior to the specified time are not performed, and performance begins immediately...
MYFLT[] GetAudioChannel(string channel)
Gets a Csound Audio channel. Used in connection with a chnset opcode in your Csound instrument.
static float[] ConvertToFloat(MYFLT[] samples)
Utility method to create an array of floats from an array of MYFLTs
CsoundUnityBridge.CSOUND_PARAMS GetParams()
Fills in a provided raw CSOUND_PARAMS object with csounds current parameter settings....
List< CsoundChannelController > channels
This class describes a setting that is meant to be used to set Csound's Global Environment Variables
string GetPath(bool runtime=false)
Set the runtime bool true on Editor for debug purposes only, let Unity detect the correct path with A...
SupportedPlatform platform
EnvironmentPathOrigin baseFolder
string GetPathDescriptor(bool runtime)
string GetPlatformString()