├── .gitignore ├── LICENSE.md ├── readme.md ├── reaper ├── Audio │ ├── Ambient.ogg │ └── Gunshot 2.ogg └── ReaperSession.RPP └── src ├── LICENSE.md ├── ReaperNode.cs ├── ReaperParser.cs ├── ReaperParserComponent.cs └── SampleScene.unity /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Bb]uilds/ 6 | Assets/AssetStoreTools* 7 | 8 | # Visual Studio cache directory 9 | .vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | *.opendb 26 | 27 | # Unity3D generated meta files 28 | *.pidb.meta 29 | *.pdb.meta 30 | 31 | # Unity3D Generated File On Crash Reports 32 | sysinfo.txt 33 | 34 | # Builds 35 | *.apk 36 | *.unitypackage 37 | 38 | # meta 39 | *.meta 40 | 41 | # reaper 42 | *.reapeaks 43 | *.RPP-bak 44 | *.rpp-bak 45 | 46 | # mac os 47 | .DS_Store -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2018 David Hill 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 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # UnityReaperParser 2 | 3 | UnityReaperParser is a tool for Unity written in C# to load and parse Reaper Project Files (.rpp/.RPP). This can be used to build your own tool to use Reaper as a kind of middleware to implement audio in video games. 4 | 5 | ## Getting Started 6 | 7 | ### Installing 8 | 9 | Download the repository and import the src folder and the SampleScene into your project. 10 | 11 | ##### Set the correct path 12 | 13 | Open the SampleScene in Unity. In the hierarchy, click the "Main Camera" object. In the inspector find the "Reaper Parser Component" and paste the absolute path to the reaper file in the downloaded "Path To Reaper File". 14 | 15 | ## How does it work 16 | 17 | 18 | ### The Reaper File .rpp 19 | 20 | To use the UnityReaperParser it is necessary to understand how reaper stores it's state. UnityReaperParser reads the .rpp like a .txt. You can open this file in a text editor of your choice. In this file we can find all information about our reaper session, the tracks, the regions, the automation etc. and use it in our video games. 21 | 22 | ``` 23 | Options -> Ripple edit per-track and save the session. The line should now be ```RIPPLE 1```. 30 | 31 | Inside the ```REAPER_PROJECT``` we find other children like tracks: 32 | 33 | ``` 34 | tempo = rpp.GetNode("TEMPO").values; 69 | Debug.Log("Tempo information: " + string.Join(", ", tempo.ToArray())); 70 | 71 | ``` 72 | The second part demonstrate how to get the position of the cursor or information about the global bpm settings. This is why you need to understand how the .rpp file works. For now you get all the information you need by calling ```GetNode("NODE_TYPE")``` You need to be sure that there is a node with the passed type otherwise GetNode returns ```null```. 73 | 74 | As a last example on how to dig through the rpp file: 75 | 76 | ``` 77 | // e.g. get the name of the first item on the last track 78 | string itemName = rpp.GetLastNode("TRACK").GetNode("ITEM").GetNode("NAME").value; 79 | Debug.Log("name of the first item on the last track: " + itemName); 80 | ``` 81 | 82 | ### Get an idea how to use this 83 | 84 | In our SampleScene in Unity there are to objects with SoundSources – "Ambient" and "Gunshot". In ReaperParserComponent.cs you can find the IEnumerator LoadAudio(). In this example we use UnityReaperParser to iterate through all the tracks in the reaper file. We load the first item on the track from disk and assign it to the corresponding Unity Objects. 85 | 86 | This is an example how to create your own middleware using UnityReaperParser. Here we create new GameObjects for our tracks in Reaper and assign the first item of each track as an AudioClip to the AudioSource. This one is aasy, but there are no limits to your creativity. 87 | 88 | ``` 89 | foreach (var t in tracks) 90 | { 91 | var name_ = t.GetNode("NAME").value; 92 | var item_ = t.GetNode("ITEM"); 93 | 94 | var g = new GameObject(name_); 95 | g.transform.parent = reaperObject.transform; 96 | g.AddComponent(); 97 | 98 | //var g = GameObject.Find(name_); 99 | 100 | if (item_ == null || g == null) 101 | continue; 102 | 103 | var src_ = g.GetComponent(); 104 | 105 | if (src_ == null) 106 | continue; 107 | 108 | var container = new ReaperParser.Container(); 109 | yield return ReaperParser.LoadAudioFromDisk(item_, container); 110 | 111 | src_.clip = container.t; 112 | src_.loop = item_.GetNode("LOOP").value == "1" ? true : false; 113 | src_.Play(); 114 | } 115 | ``` 116 | 117 | To really use this tool as a middle ware there is far more work to do but it's a start. 118 | 119 | ## Authors 120 | 121 | * **David Hill** - *Initial work* - [davemod](https://github.com/davemod) 122 | 123 | ## License 124 | 125 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details -------------------------------------------------------------------------------- /reaper/Audio/Ambient.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemod/UnityReaperParser/ea82a825b51f1da40b4d81465838dd900dcb8bbe/reaper/Audio/Ambient.ogg -------------------------------------------------------------------------------- /reaper/Audio/Gunshot 2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davemod/UnityReaperParser/ea82a825b51f1da40b4d81465838dd900dcb8bbe/reaper/Audio/Gunshot 2.ogg -------------------------------------------------------------------------------- /reaper/ReaperSession.RPP: -------------------------------------------------------------------------------- 1 | 29 | 31 | RENDER_FILE "" 32 | RENDER_PATTERN "" 33 | RENDER_FMT 0 2 0 34 | RENDER_1X 0 35 | RENDER_RANGE 1 0 0 18 1000 36 | RENDER_RESAMPLE 3 0 1 37 | RENDER_ADDTOPROJ 0 38 | RENDER_STEMS 0 39 | RENDER_DITHER 0 40 | TIMELOCKMODE 1 41 | TEMPOENVLOCKMODE 1 42 | ITEMMIX 0 43 | DEFPITCHMODE 589824 0 44 | TAKELANE 1 45 | SAMPLERATE 44100 0 0 46 | 48 | LOCK 16385 49 | 56 | GLOBAL_AUTO -1 57 | TEMPO 120 4 4 58 | PLAYRATE 1 0 0.25 4 59 | SELECTION 0 0 60 | SELECTION2 0 0 61 | MASTERAUTOMODE 0 62 | MASTERTRACKHEIGHT 0 0 63 | MASTERPEAKCOL 16576 64 | MASTERMUTESOLO 0 65 | MASTERTRACKVIEW 0 0.6667 0.5 0.5 0 0 0 66 | MASTERHWOUT 0 0 1 0 0 0 0 -1 67 | MASTER_NCH 2 2 68 | MASTER_VOLUME 1 0 -1 -1 1 69 | MASTER_FX 1 70 | MASTER_SEL 0 71 | 78 | 85 | 87 | 116 | 137 | > 138 | > 139 | 183 | > 184 | > 185 | > 186 | -------------------------------------------------------------------------------- /src/LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2018 David Hill 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 | -------------------------------------------------------------------------------- /src/ReaperNode.cs: -------------------------------------------------------------------------------- 1 | /** @file ReaperNode.css 2 | * @brief Implementation of the class ReaperNode 3 | * @author David Hil 4 | * 5 | * Copyright (c) 2019 David Hill 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | using UnityEngine; 27 | using System.Collections; 28 | using System.Collections.Generic; 29 | using System.Text.RegularExpressions; 30 | using System.Linq; 31 | using System; 32 | 33 | namespace UnityReaperParser 34 | { 35 | 36 | public partial class ReaperNode 37 | { 38 | 39 | // public: 40 | 41 | /// 42 | /// Initializes a new instance of the class. 43 | /// 44 | /// The current line as a string from the reaper file. 45 | /// The parent node. Pass null if there is no parent. 46 | /// The parsing instance of ReaperParser. 47 | public ReaperNode(string text, ReaperNode parent, ReaperParser parser) 48 | { 49 | this.children = new List(); 50 | this.parser = parser; 51 | this.parent = parent; 52 | text = text.Trim(); 53 | 54 | var matches = Regex.Matches(text, "(\"[^\"]*\"|[\\S]+)"); // split the values 55 | var matchList = matches.Cast().Select(match => match.Value).ToList(); // convert matches to string list 56 | matchList = matchList.Select(s => s.Trim(new char[] { ' ', '\"', '<', '>' })).ToList(); 57 | 58 | 59 | 60 | /* 61 | * Try to parse the first element of matches as a float. If it is a 62 | * number, the node has no type but only values. if it is not a 63 | * number, the first element is the type of the node. 64 | */ 65 | 66 | try 67 | { 68 | var num = float.Parse(matchList[0]); 69 | type = ""; 70 | values = matchList; 71 | } 72 | catch 73 | { 74 | if (matchList.Count > 0) 75 | { 76 | type = matchList[0]; 77 | matchList.RemoveAt(0); 78 | values = matchList; 79 | } 80 | } 81 | } 82 | 83 | /// 84 | /// Adds a child. 85 | /// 86 | /// O. 87 | public void addChild(ReaperNode o) 88 | { 89 | children.Add(o); 90 | } 91 | 92 | 93 | 94 | /// 95 | /// The Reaper Parser that parsed the reaper file. That's the way to get the directory of the reaper session file. 96 | /// 97 | public readonly ReaperParser parser; 98 | 99 | /// 100 | /// The parent ReaperNode. If null, this node is the main ReaperSession Node. 101 | /// 102 | public readonly ReaperNode parent; 103 | 104 | /// 105 | /// The type of this Node as a string. 106 | /// 107 | public readonly string type; 108 | 109 | /// 110 | /// The values of this Node. 111 | /// 112 | public readonly List values; 113 | 114 | /// 115 | /// Gets the first value of this nodes values. 116 | /// 117 | /// The first value of this nodes values. 118 | public string value { get { return values.Count > 0 ? values.First() : ""; } } 119 | 120 | /// 121 | /// All Children of this ReaperNode as a instances of ReaperNode. 122 | /// A child can be a 123 | /// - TRACK 124 | /// - ITEM (a region on a track) 125 | /// - POSITION (which might be the position of an item on a track), 126 | /// - FADEIN (which might be an array of numbers) or 127 | /// - PT (which is a point of an envelope) 128 | /// - etc. 129 | /// There are many other properties and values. Have a look into 130 | /// the .rpp file (open in any text editor). 131 | /// 132 | /// The child nodes of this node. 133 | public List children { get; private set; } 134 | 135 | 136 | 137 | /// 138 | /// Gets all children of the given type with the given name 139 | /// 140 | /// All children of given type with the given name. 141 | /// Type. 142 | /// Name. 143 | /// Search child nodes too. False by default. 144 | public List GetNodesByTypeAndName(string type, string name, bool recursive = false) 145 | { 146 | List nodes_ = new List(); 147 | 148 | foreach (var n_ in GetNodes(type,recursive)) 149 | { 150 | 151 | if (n_.GetNode("NAME").value == name) 152 | nodes_.Add(n_); 153 | } 154 | 155 | return nodes_; 156 | } 157 | 158 | /// 159 | /// Find a node by it's type and name. 160 | /// 161 | /// The node by type and name. 162 | /// Type. 163 | /// Name. 164 | public ReaperNode GetNodeByTypeAndName(string type, string name) 165 | { 166 | List nodes_ = new List(); 167 | 168 | foreach (var n_ in GetNodesRecursive(type)) 169 | { 170 | 171 | if (n_.GetNode("NAME").value == name) 172 | return n_; 173 | } 174 | 175 | return null; 176 | } 177 | 178 | /// 179 | /// Find a node by it's type. 180 | /// 181 | /// The first child node with type type. 182 | /// Type. 183 | public ReaperNode GetNode(string type) 184 | { 185 | return children.Find(x => x.type == type); 186 | } 187 | 188 | /// 189 | /// Gets the last child node with type type. 190 | /// 191 | /// The last child node with type type. 192 | /// Type. 193 | public ReaperNode GetLastNode(string type) 194 | { 195 | return children.FindLast(x => x.type == type); 196 | } 197 | 198 | /// 199 | /// Gets all child nodes with type type. 200 | /// 201 | /// The child nodes with type type. 202 | /// Type. 203 | /// If true search child nodes too. False by default. 204 | public List GetNodes(string type, bool recursive = false){ 205 | return !recursive ? children.FindAll(x => x.type == type) : GetNodesRecursive(type); 206 | } 207 | 208 | 209 | // private: 210 | 211 | /// 212 | /// Search all children recursively. Finds children in children too and so on... 213 | /// 214 | /// All children of type type. 215 | /// Type. 216 | private List GetNodesRecursive(string type){ 217 | List found = new List(); 218 | GetAllChildrenRecursive(ref found, type); 219 | return found; 220 | } 221 | 222 | private void GetAllChildrenRecursive(ref List found, string type){ 223 | List _children = GetNodes(type); 224 | found.AddRange(_children); 225 | foreach (var _c in _children){ 226 | _c.GetAllChildrenRecursive(ref found, type); 227 | } 228 | } 229 | } 230 | } -------------------------------------------------------------------------------- /src/ReaperParser.cs: -------------------------------------------------------------------------------- 1 | /** @file ReaperParser.css 2 | * @brief Implementation of the class ReaperParser to read and parse an .rpp file. 3 | * @author David Hil 4 | * 5 | * Copyright (c) 2019 David Hill 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | 27 | using System; 28 | using System.IO; 29 | using System.Collections; 30 | using System.Collections.Generic; 31 | using UnityEngine; 32 | 33 | namespace UnityReaperParser 34 | { 35 | public partial class ReaperParser 36 | { 37 | public ReaperNode rpp; 38 | 39 | private string content; 40 | public string path { get; private set; } 41 | public string directory { get; private set; } 42 | public bool isValid { get; private set; } 43 | 44 | 45 | private string[] contentLines; 46 | private int currentLine = 0; 47 | private ReaperNode currentNode; 48 | 49 | 50 | /// 51 | /// Initializes a new instance of the class. 52 | /// 53 | /// The Absolute Path to the Reaper File .rpp. 54 | public ReaperParser(string path) 55 | { 56 | // Read File and store it to member content 57 | try 58 | { 59 | StreamReader sf = new StreamReader(path); 60 | this.content = sf.ReadToEnd(); 61 | this.contentLines = content.Split('\n'); 62 | rpp = Parse(null, this); 63 | this.path = path; 64 | this.directory = Path.GetDirectoryName(path); 65 | 66 | isValid = true; 67 | } 68 | catch (Exception e) 69 | { 70 | isValid = false; 71 | Debug.LogError(e.ToString()); 72 | } 73 | } 74 | 75 | /// 76 | /// Parse the reaper file. 77 | /// 78 | /// The top node. 79 | /// Parent. 80 | /// The parsing ReaperParser. 81 | private ReaperNode Parse(ReaperNode parent, ReaperParser parser){ 82 | 83 | string cl = contentLines[currentLine]; 84 | 85 | if (cl.Contains("<")){ 86 | var newNode = new ReaperNode(cl, parent,parser); // Create a new Node 87 | if (currentNode!=null) // only happens for the first line ")){ 91 | currentNode = currentNode.parent != null ? currentNode.parent : currentNode; 92 | } else if (currentNode != null){ 93 | currentNode.addChild(new ReaperNode(cl, currentNode,parser)); 94 | } 95 | 96 | currentLine++; 97 | 98 | if (currentLine < contentLines.Length) 99 | Parse(currentNode,this); 100 | 101 | return currentNode; 102 | } 103 | } 104 | 105 | 106 | 107 | 108 | public partial class ReaperParser 109 | { 110 | /// 111 | /// Container to pass certain data types to IEnumerators as reference. 112 | /// 113 | public class Container 114 | { 115 | public T t; 116 | } 117 | 118 | /// 119 | /// Loads the audio from disk using the WWW class. Adds the loaded file 120 | /// as an AudioClip to the Container. 121 | /// 122 | /// The ReaperNode instance with type "ITEM". 123 | /// An instance of ClipContainer. 124 | public static IEnumerator LoadAudioFromDisk(ReaperNode item, Container clipContainer) 125 | { 126 | if (item.type == "ITEM") 127 | { 128 | var source_wave = item.GetNode("SOURCE"); 129 | var source_path = source_wave.GetNode("FILE").value; 130 | 131 | if (source_path[0] != '/') // is relative path 132 | source_path = item.parser.directory + "/" + source_path; 133 | 134 | string url = "file:///" + source_path; 135 | WWW www = new WWW(url); 136 | 137 | clipContainer.t = www.GetAudioClip(); 138 | 139 | while (!clipContainer.t.isReadyToPlay) 140 | yield return null; 141 | } 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /src/ReaperParserComponent.cs: -------------------------------------------------------------------------------- 1 | /** @file ReaperParserComponent.css 2 | * @brief Demonstration of how to use an instance of ReaperParser 3 | * @author David Hil 4 | * 5 | * Copyright (c) 2019 David Hill 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | using System.Collections; 26 | using System.Collections.Generic; 27 | using System.Linq; 28 | using UnityEngine; 29 | 30 | namespace UnityReaperParser 31 | { 32 | public class ReaperParserComponent : MonoBehaviour 33 | { 34 | public string pathToReaperFile; 35 | 36 | public ReaperParser parser; 37 | 38 | public ReaperNode rpp; 39 | 40 | public AudioSource ambientSource; 41 | public AudioSource gunshotSource; 42 | 43 | // Use this for initialization 44 | void Start() 45 | { 46 | Example(); 47 | } 48 | 49 | private void Update() 50 | { 51 | if (Input.GetKeyDown(KeyCode.Space)) 52 | { 53 | gunshotSource = GameObject.Find("Gunshot").GetComponent(); 54 | if (gunshotSource) 55 | gunshotSource.Play(); 56 | } 57 | } 58 | 59 | /// 60 | /// Parse the rpp file. 61 | /// 62 | private void Example(){ 63 | 64 | // open the file and parse it 65 | parser = new ReaperParser(pathToReaperFile); 66 | 67 | if (!parser.isValid) 68 | { 69 | Debug.LogError("The ReaperParser was not initialized."); 70 | return; 71 | } 72 | 73 | // get the parsed main ReaperNode object 74 | rpp = parser.rpp; 75 | 76 | // e.g. get the position of the cursor. There is only one value. Value returns always the first of all values. 77 | string cursorPosition = rpp.GetNode("CURSOR").value; 78 | Debug.Log("The cursor position: " + cursorPosition); 79 | 80 | // e.g. get the main tempo information of the session. 81 | List tempo = rpp.GetNode("TEMPO").values; 82 | Debug.Log("Tempo information: " + string.Join(", ", tempo.ToArray())); 83 | 84 | // e.g. get the name of the first item on the last track 85 | string itemName = rpp.GetLastNode("TRACK").GetNode("ITEM").GetNode("NAME").value; 86 | Debug.Log("name of the first item on the last track: " + itemName); 87 | 88 | // load the audio in a coroutine to be sure the audioclip is loaded before assigned to a audiosource 89 | StartCoroutine(LoadAudio()); 90 | 91 | } 92 | 93 | IEnumerator LoadAudio() 94 | { 95 | 96 | var tracks = rpp.GetNodes("TRACK"); 97 | 98 | /* 99 | * Iterate through all the tracks in the reaper file. Then 100 | * find the corresponding Gameobject in the Unity Scene. Load the 101 | * first item on the track and assign it to the corresponding 102 | * GameObject in the scene. 103 | * 104 | * This is an example how to create your own middleware using 105 | * UnityReaperParser. Here we use the names of the GameObjects 106 | * and the names of the tracks in reaper to assign audio to 107 | * AudioSources. This one is aasy, but there are no limits to 108 | * your creativity. 109 | */ 110 | 111 | var reaperObject = GameObject.Find("Reaper"); 112 | 113 | foreach (var t in tracks) 114 | { 115 | 116 | var name_ = t.GetNode("NAME").value; 117 | var item_ = t.GetNode("ITEM"); 118 | 119 | var g = new GameObject(name_); 120 | g.transform.parent = reaperObject.transform; 121 | g.AddComponent(); 122 | 123 | //var g = GameObject.Find(name_); 124 | 125 | if (item_ == null || g == null) 126 | continue; 127 | 128 | var src_ = g.GetComponent(); 129 | 130 | if (src_ == null) 131 | continue; 132 | 133 | var container = new ReaperParser.Container(); 134 | yield return ReaperParser.LoadAudioFromDisk(item_, container); 135 | 136 | src_.clip = container.t; 137 | src_.loop = item_.GetNode("LOOP").value == "1" ? true : false; 138 | src_.Play(); 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/SampleScene.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 0} 41 | m_IndirectSpecularColor: {r: 0.44657838, g: 0.49641234, b: 0.57481676, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 11 47 | m_GIWorkflowMode: 0 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_TemporalCoherenceThreshold: 1 54 | m_EnvironmentLightingMode: 0 55 | m_EnableBakedLightmaps: 1 56 | m_EnableRealtimeLightmaps: 0 57 | m_LightmapEditorSettings: 58 | serializedVersion: 10 59 | m_Resolution: 2 60 | m_BakeResolution: 10 61 | m_AtlasSize: 512 62 | m_AO: 0 63 | m_AOMaxDistance: 1 64 | m_CompAOExponent: 1 65 | m_CompAOExponentDirect: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 1 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 2 75 | m_BakeBackend: 1 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 256 79 | m_PVRBounces: 2 80 | m_PVRFilterTypeDirect: 0 81 | m_PVRFilterTypeIndirect: 0 82 | m_PVRFilterTypeAO: 0 83 | m_PVRFilteringMode: 1 84 | m_PVRCulling: 1 85 | m_PVRFilteringGaussRadiusDirect: 1 86 | m_PVRFilteringGaussRadiusIndirect: 5 87 | m_PVRFilteringGaussRadiusAO: 2 88 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 89 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 90 | m_PVRFilteringAtrousPositionSigmaAO: 1 91 | m_ShowResolutionOverlay: 1 92 | m_LightingDataAsset: {fileID: 0} 93 | m_UseShadowmask: 1 94 | --- !u!196 &4 95 | NavMeshSettings: 96 | serializedVersion: 2 97 | m_ObjectHideFlags: 0 98 | m_BuildSettings: 99 | serializedVersion: 2 100 | agentTypeID: 0 101 | agentRadius: 0.5 102 | agentHeight: 2 103 | agentSlope: 45 104 | agentClimb: 0.4 105 | ledgeDropHeight: 0 106 | maxJumpAcrossDistance: 0 107 | minRegionArea: 2 108 | manualCellSize: 0 109 | cellSize: 0.16666667 110 | manualTileSize: 0 111 | tileSize: 256 112 | accuratePlacement: 0 113 | debug: 114 | m_Flags: 0 115 | m_NavMeshData: {fileID: 0} 116 | --- !u!1 &170076733 117 | GameObject: 118 | m_ObjectHideFlags: 0 119 | m_PrefabParentObject: {fileID: 0} 120 | m_PrefabInternal: {fileID: 0} 121 | serializedVersion: 5 122 | m_Component: 123 | - component: {fileID: 170076735} 124 | - component: {fileID: 170076734} 125 | m_Layer: 0 126 | m_Name: Directional Light 127 | m_TagString: Untagged 128 | m_Icon: {fileID: 0} 129 | m_NavMeshLayer: 0 130 | m_StaticEditorFlags: 0 131 | m_IsActive: 1 132 | --- !u!108 &170076734 133 | Light: 134 | m_ObjectHideFlags: 0 135 | m_PrefabParentObject: {fileID: 0} 136 | m_PrefabInternal: {fileID: 0} 137 | m_GameObject: {fileID: 170076733} 138 | m_Enabled: 1 139 | serializedVersion: 8 140 | m_Type: 1 141 | m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} 142 | m_Intensity: 1 143 | m_Range: 10 144 | m_SpotAngle: 30 145 | m_CookieSize: 10 146 | m_Shadows: 147 | m_Type: 2 148 | m_Resolution: -1 149 | m_CustomResolution: -1 150 | m_Strength: 1 151 | m_Bias: 0.05 152 | m_NormalBias: 0.4 153 | m_NearPlane: 0.2 154 | m_Cookie: {fileID: 0} 155 | m_DrawHalo: 0 156 | m_Flare: {fileID: 0} 157 | m_RenderMode: 0 158 | m_CullingMask: 159 | serializedVersion: 2 160 | m_Bits: 4294967295 161 | m_Lightmapping: 1 162 | m_AreaSize: {x: 1, y: 1} 163 | m_BounceIntensity: 1 164 | m_ColorTemperature: 6570 165 | m_UseColorTemperature: 0 166 | m_ShadowRadius: 0 167 | m_ShadowAngle: 0 168 | --- !u!4 &170076735 169 | Transform: 170 | m_ObjectHideFlags: 0 171 | m_PrefabParentObject: {fileID: 0} 172 | m_PrefabInternal: {fileID: 0} 173 | m_GameObject: {fileID: 170076733} 174 | m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} 175 | m_LocalPosition: {x: 0, y: 3, z: 0} 176 | m_LocalScale: {x: 1, y: 1, z: 1} 177 | m_Children: [] 178 | m_Father: {fileID: 0} 179 | m_RootOrder: 1 180 | m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} 181 | --- !u!1 &282840810 182 | GameObject: 183 | m_ObjectHideFlags: 0 184 | m_PrefabParentObject: {fileID: 0} 185 | m_PrefabInternal: {fileID: 0} 186 | serializedVersion: 5 187 | m_Component: 188 | - component: {fileID: 282840814} 189 | - component: {fileID: 282840813} 190 | - component: {fileID: 282840811} 191 | - component: {fileID: 282840812} 192 | m_Layer: 0 193 | m_Name: Main Camera 194 | m_TagString: MainCamera 195 | m_Icon: {fileID: 0} 196 | m_NavMeshLayer: 0 197 | m_StaticEditorFlags: 0 198 | m_IsActive: 1 199 | --- !u!81 &282840811 200 | AudioListener: 201 | m_ObjectHideFlags: 0 202 | m_PrefabParentObject: {fileID: 0} 203 | m_PrefabInternal: {fileID: 0} 204 | m_GameObject: {fileID: 282840810} 205 | m_Enabled: 1 206 | --- !u!114 &282840812 207 | MonoBehaviour: 208 | m_ObjectHideFlags: 0 209 | m_PrefabParentObject: {fileID: 0} 210 | m_PrefabInternal: {fileID: 0} 211 | m_GameObject: {fileID: 282840810} 212 | m_Enabled: 1 213 | m_EditorHideFlags: 0 214 | m_Script: {fileID: 11500000, guid: 63016a23f5a3a424982b84fe7d3cd065, type: 3} 215 | m_Name: 216 | m_EditorClassIdentifier: 217 | pathToReaperFile: /absolute/path/to/reapersession.tpp 218 | ambientSource: {fileID: 0} 219 | gunshotSource: {fileID: 0} 220 | --- !u!20 &282840813 221 | Camera: 222 | m_ObjectHideFlags: 0 223 | m_PrefabParentObject: {fileID: 0} 224 | m_PrefabInternal: {fileID: 0} 225 | m_GameObject: {fileID: 282840810} 226 | m_Enabled: 1 227 | serializedVersion: 2 228 | m_ClearFlags: 1 229 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 230 | m_NormalizedViewPortRect: 231 | serializedVersion: 2 232 | x: 0 233 | y: 0 234 | width: 1 235 | height: 1 236 | near clip plane: 0.3 237 | far clip plane: 1000 238 | field of view: 60 239 | orthographic: 0 240 | orthographic size: 5 241 | m_Depth: -1 242 | m_CullingMask: 243 | serializedVersion: 2 244 | m_Bits: 4294967295 245 | m_RenderingPath: -1 246 | m_TargetTexture: {fileID: 0} 247 | m_TargetDisplay: 0 248 | m_TargetEye: 3 249 | m_HDR: 1 250 | m_AllowMSAA: 1 251 | m_AllowDynamicResolution: 0 252 | m_ForceIntoRT: 1 253 | m_OcclusionCulling: 1 254 | m_StereoConvergence: 10 255 | m_StereoSeparation: 0.022 256 | --- !u!4 &282840814 257 | Transform: 258 | m_ObjectHideFlags: 0 259 | m_PrefabParentObject: {fileID: 0} 260 | m_PrefabInternal: {fileID: 0} 261 | m_GameObject: {fileID: 282840810} 262 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 263 | m_LocalPosition: {x: 0, y: 1, z: -10} 264 | m_LocalScale: {x: 1, y: 1, z: 1} 265 | m_Children: [] 266 | m_Father: {fileID: 0} 267 | m_RootOrder: 0 268 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 269 | --- !u!1 &1591796044 270 | GameObject: 271 | m_ObjectHideFlags: 0 272 | m_PrefabParentObject: {fileID: 0} 273 | m_PrefabInternal: {fileID: 0} 274 | serializedVersion: 5 275 | m_Component: 276 | - component: {fileID: 1591796045} 277 | m_Layer: 0 278 | m_Name: Reaper 279 | m_TagString: Untagged 280 | m_Icon: {fileID: 0} 281 | m_NavMeshLayer: 0 282 | m_StaticEditorFlags: 0 283 | m_IsActive: 1 284 | --- !u!4 &1591796045 285 | Transform: 286 | m_ObjectHideFlags: 0 287 | m_PrefabParentObject: {fileID: 0} 288 | m_PrefabInternal: {fileID: 0} 289 | m_GameObject: {fileID: 1591796044} 290 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 291 | m_LocalPosition: {x: 0, y: 0, z: 0} 292 | m_LocalScale: {x: 1, y: 1, z: 1} 293 | m_Children: [] 294 | m_Father: {fileID: 0} 295 | m_RootOrder: 2 296 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 297 | --------------------------------------------------------------------------------