Learning C# Notes - Part IV: Methods

Here are my notes on eveyrthing related to methods.

Methods Basics

  • We can think of methods as the different actions that our classes can perform so they are the basic building blocks that determine how a class behaves.

  • Sometimes they are called ‘Functions’.

  • We first define a method using paretheses () after the name. Method names usually start with a capital letter.

  • After defining the method, we can call it from somewhere else.

  • See an example below. We first define a method and then it is called from some other method definition.

class Program { void FirstMethod() { // code to be executed } void OtherMethod() { //Some More code FirstMethod(); } }

Return Types & Passing Parameters

  • Methods can return a variable after they run, which needs to be specified when we define the method.

  • If a method returns no value, it will be of type void.

  • While return types are like outputs, we can also input information into a method. These inputs are called parameters.

  • For a method to take parameters, we must define them when we define the method.

  • A method can take multiple parameters.

  • We can give parameters a default value by using the equals sign when defining the method.

  • The ‘params’ keyword can also be used to make a parameter optional.

    • To be precise, it allows you to pass any amount of elements in an argument's array and if you pass 0 elements this is in practice like making the argument optional.

    • The params keyword can only be used once per method and must be the last argument.

int SumNumbers(int num1, int num2) { return num1 + num2; } void MyMethod(string country = "Norway") { Console.WriteLine(country); }

Method Overloading

  • Multiple methods can have the same name and vary in how many parameters they take and even their return types.

  • This is useful when you need a method that can perform generally the same function but using different types of information.

  • We just define and call them as normal using the same method name.

  • See the example below. Both methods will just sum numbers but they can take different type of variables as values.

int SumNumbers(int num1, int num2) { return num1 + num2; } float SumNumbers(float num1, float num2) { return num1 + num2; }

Calling Method from other classes & Static Methods

  • When working in Unity with C#, most of the time, each class lives in its own script file.

  • Is very common for a class to call another class’ method.

  • This is usually done by typing the name of the class followed by a full stop and then the name of the method, ending in paretheses.

  • If we want to call a method within a class without instatiating that said class, a possible solution could be to make the method static when defining it.

Unity Methods

  • Unity includes some important methods that we can use on Monobehaviour type scripts.

  • Awake() is the first method called and it should usually be used for initializing things. Is only called once.

  • Start() is also called once after Awake() but inly if the script component is enabled.

  • Update() is called on every frame that the script component is active.

  • LateUpdate() is called every frame but only after everything on Update() has finished executing.

  • FixedUpdate() is the same as above but it has the frequency of the physics system.

  • OnEnable() is called when the gameobject containing the script is enabled and becomes active.

  • OnDisable() is called when the gameobject containing the script is disabled or destroyed.

Learning C# Notes - Part III: Selection & Operation Statements

So up until now I’ve left my notes on classes, which work as functional units in c# and on variables which store all sorts of data.

Next thing would be to use that data residing within classes and do something with it. Selection statements allow us to so different thing depending on the data itself while operation statements gives you the ability of parsing through collections of data to read or modify them.

Conditional Statements (If-else)

  • This is the most basic selection statement.

  • We analyze a condition and determine what to do if it is true.

  • Since a condition can only be either true or false, we must always analyze a boolean variable or any method that returns a bool.

  • We can use any logic expression that would result in a boolean like 3 > number.

  • We can also determine what we want to happen if the condition is not true with the else keyword.

  • Additionally, we can check further conditions with the else if keyword.

  • See examples:

int health;

if (health < 10) 
{
    //Player is hurt.  
}
else if (health < 50) 
{
    //Player is low health.  
}
else 
{
    //Player is healthy. 
}

Switch Statement

  • The conditional structure explained above is good when you just have a few possible options but sometimes you have a bunch of cases and you want to do something different for each one.

  • We can also include a default case which will be used if the expression passed is not found.

  • The syntaxis is a bit different that conditional statements, see below:

enum playerState; //This struct contains a list of states the player can be in.

switch (playerState) 
{
  case playerState.poisoned
    //We play a poisend snapshot.
    break;
  case playerState.burning
    //We play a burning snapshot.
    break;
  case playerState.confused
    //We play a confused snapshot.
    break;
  case playerState.dead
    //We play a death snapshot.
    break;
  case default
    //We play the normal snapshot.
    break;
}

Logical Operators

  • If we want to do more complex statements we can use logical operators to add certain conditions and logic.

  • We use “&&” as an AND statement when we want to check that two or more conditions are true.

  • We use “||” as an OR statement if we want to do something when either condition is true. Only one needs to be true.

  • We use “!” as a NOT statement to signify that we want to check the opposite of a condition. In other words, the “!” operator inverts a bool, turning a true into false.

  • When checking that something is bigger or smaller that our condition, we use “<“ and “>”.

  • We can also use “<=” to check is something is smaller or equal and “>=” to check if it is bigger or equal.

  • We use “==” to check if a variable equals another.

  • See these in action below:

// NOT
bool result = true;
if (!result)
{
    Console.WriteLine("The condition is true (result is false).");
}
else
{
    Console.WriteLine("The condition is false (result is true).");
}

// Short-circuit AND
int m = 9;
int n = 7;
int p = 5;
if (m >= n && m >= p)
{
    Console.WriteLine("Nothing is larger than m.");
}

// AND and NOT
if (m >= n && !(p > m))
{
    Console.WriteLine("Nothing is larger than m.");
}

// Short-circuit OR
if (m > n || m > p)
{
    Console.WriteLine("m isn't the smallest.");
}

// NOT and OR
m = 4;
if (!(m >= n || m >= p))
{
    Console.WriteLine("Now m is the smallest.");
}

Iteration Statements: foreach

  • We use this iterator to go through all the values on a list, dictionary or other collection.

  • We use the “in” keyword to specify the type of element to be examined and where it comes from.

  • Is important to note that “foreach” creates a copy of the collection when iterating through it so we can’t alter it directly.

  • If we want to alter a collection as we loop through it we need to use a “for” loop (see below).

  • "foreach” is more readable and clean but performs worst than a for loop.

var fibNumbers = new List<int> { 0, 1, 1, 2, 3, 5, 8, 13 };
int count = 0;
foreach (int element in fibNumbers)
{
    Console.WriteLine($"Element #: ");
    count++;
}
Console.WriteLine($"Number of elements: ");

Iteration Statements: for

  • This will execute the code inside as long as the condition evaluated is true.

  • We initialize an iterator value and alter it on each cycle.

  • It is very useful for checking all the elements in a collection or do something a certain number of times.

  • It allows you to directly alter the values of the collection that you are examining.

  • Is important to note that we first check the condition and then execute the code between { }.

int[] Numbers = {1,2,3,4,5,6}
contant someConstantValue;

for (int i = 0; Numbers.Length; i++) 
{
    Numbers[i] = Numbers[i]*someConstantValue;    
}

Iteration Statements: do while

  • This basically works like a for loop but the main difference is that the code is executed first and then the condition is checked.

  • So in other words a “do while” statement will always execute the code between { } at least once.

  • You can use the keyword “break” to exit the loop at anytime.

int n = 0;
do
{
    Console.WriteLine(n);
    n++;
} while (n < 5);

Iteration Statements: while

  • “while” can also be used by itself to to something while a certain condition is true.

int n = 0;
while (n < 5)
{
    Console.WriteLine(n);
    n++;
}

Learning C# Notes - Part II: Variables & Collections

After seeing classes in part 1, here are my notes on variables, which we will use to hold different data that we want to use somehow. Collections are just different ways of grouping variables.

Variable Declaration & Definition

  • Variables must be declared. This is simply “anouncing” which type the variable uses and giving it a name.

  • Optionally, we can also define a variable, that is, we give it an specific value.

int number; //We declare a variable.
string greeting = "Hello"; //We declare and define a value.

Basic Variables Types

  • Int -> Stores whole numbers (can be negative).

  • float -> Stores numbers with decimals. An “f” is written after the number.

  • double -> Same as a float but can hold twice as many decimals. A “D” is written after the number.

  • string -> Stores a chain of characters and is always surrounded by double quotes.

  • char -> Stores a single character and uses single quotes.

  • bool -> Only holds two posiible states: true and false.

int aNumber = 45;
float aFloatNumber = 0.3f;
double aDoubleNumer = 3.59D;
string aText = "Hello";
char aSingleLetter = 'J';
bool aBooleanVariable = true;
  • Remember that string variables can look like a number as any number is just a chain of characters but this doesn’t mean you can do mathematical operations with them. For this, you need to convert them to an int or float.

  • Sometimes, the variable type is implicit by context and you can just use “var” as the type.

  • We say a variable is local when is declared and used within a method, so the rest of the class has no notion of it.

  • There are a few rules when naming variables:

    • You should start with a lowcase letter.

    • You can then use letters, numbers and underscores but NO spaces.

    • Names are case sensitive.

    • A usual convention is to use camelCase.

Constants

  • You can add the keyword “const” to make any variable contant.

  • A constant hold a value that never changes, like Pi or G

  • If anything then tries to change the value of a constant an error will be produced.

const int number = 5;

Enums

  • Enums are a special type of variable. Well, they are actually a class but you can think of them as variable type.

  • They hold a group of constants that, once you define them, won’t change.

  • Enums really just hold intigers (whole numbers) but they give you the option of labeling each of them with a meaningful name.

  • So basically Enums offer more ease of use and readibility than just using numbers.

  • A classic example is that, instead of using 0, 1, 2, 3 to represent the coordinates in a compass, you use North, West, South, East.

enum Coordinates
{
  North,    // 0
  West = 1,   // 1
  South,      // 2
  East = 23,      // 23
}
  • We declare an enum starting with a capital letter (since they are really classes).

  • We then declare the elements within the curly braces, using a comma after each one.

  • See how, by default, a value of 0 is given to the first element, 1 to the second and so on.

  • We can also define our own values if we prefer.

  • If you use Fmod, an enum would correspond to a “Labeled” type paramter.

Value Types VS Reference Types

  • Something that confused me at the start is that many things that you can do with a variable, you can also do with a class. This is because both classes and variables are “types”.

  • So you can store classes in collections and pass them through methods, for example.

  • The main difference between the two, is that a variable is a “Value Type” while classes are a “Reference Type”.

  • Value types pass a copy of the value inside them while reference types pass the type instance itself.

  • If we want to pass a variable as a reference type, we can use the keyword “ref” before the variable.

  • This means that changes made to the variable by the method will affect the original that was passed in.

  • So an example below.

  • See more: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-types

class DoMath 
{
    int num = 8;

    int DoSomething(int number) 
    {
      //Something is done...
    }

    DoSomething(num); // Here we are passing hte 8 value.
    DoSomething(ref num); //Here we are passing num itself.
}

Structs

  • Think about them as a custom data type. They are like a container for any number of variables. So for example, we could have a struct called Car which contains a string variable for the model, an int for the year and a bool for availability.

  • So structs are basically a variable that can contains more variables. In a way, they are like a light class as they can use variables, methods and constructors.

  • They can be useful when you want to manage data in certain ways (queues, dictionaries...) and want to refer to a few different variables in one go.

  • Struct can't have defaults on their constructors and their variables can't be initialized.

struct House
{
  public int Elevation;
  public bool Ocupied;
  public string StreetName;
  
  public CalculateDistance(int elevation) 
  {
    //We do some maths...
  }
}

Collections

  • Collections simply hold a number of variables at the same time.

  • This is useful because you can look through them and do dynamic operations like you can see below.

Arrays

  • They hold multiple variables of the same type.

  • When defining the array, you must define its size and this can’t change dynamically.

  • Is possible to create multi-dimensional arrays (arrays of arrays).

//We can declare an array by directly defining its contents:
int[] numberArray = [1, 2, 3, 4, 5];

//Or we can just specify its size:
int[] numberArray = new int[5];

//We can access any variable using its index.
//The first elements always uses 0.
numberArray[0];

//We can also check the size with:
numberArray.Lenght;

Lists

  • They function like arrays but they can be dynamically sized so you can add and substract elements during run time.

  • Lists are a generic type class and we must instantiate them so the sintax is a bit different as you can see below.

  • Lists can contain nulls.

  • Lists can do many other things: https://www.tutorialsteacher.com/csharp/csharp-list

//We declare a new list. No need to specify the size.
List<int> myNumberList = new List<int>();

//We add elements by using the Add method.
myNumberList.Add(17);

//We can remove an specific element like so.
//This woud remove the first 5 found in the list.
myNumberList.Remove(5);

//Alternatively, we can also remove using the index.
//So this would remove the third element in the list:
myNumberList.RemoveAt(3);

Dictionaries

  • Similarly to lists, they can be dynamically sized but they always contain a pair of variables which can be of any type.

  • The first type is called the Key, while the second is the Value.

  • We use a dictionary in a similar way as a List since they are also clases.

  • An important difference is that dictionaries don’t use indexes since the key value acts as the index.

  • When iterating through a dictionary with a for or foreach loop (more on this in future posts), you should not remove entries directly or you will create errors. You can store whatever you want to remove in a list, for example.

//For example, we can declare a new dictionary which will contain
//an int as the key and a string as the value.
Dictionary<int, string> numberNames = new Dictionary<int, string>();

//We add value in a similar way to a list, but we need to specify both values.
numberNames.Add(1,"One");
numberNames.Add(2,"Two");

//To remove, we only specify the key.
numberNames.Remove(2);

//Use TryGetValue() when you are not sure if a certain key 
//is in the dictionary. This will be false if there is no such key value.
numberNames.TryGetValue(2);

Queues

  • They store any number of items and these item can be of any type.

  • Queues work with a FIFO rule, so the first element to enter the queue is also the first to exit.

  • Queues are classes so they use a similar syntax as lists and dictionaries.

//We declare a queue 
Queue<int> callerIds = new Queue<int>();

//We use the Enqueue() method to add to it.
callerIds.Enqueue(1);
callerIds.Enqueue(2);
callerIds.Enqueue(3);
callerIds.Enqueue(4);

//And Dequeue() to will always remove the first element.
callerIds.Dequeue(); //This would remove the 1 value.

//We can use Peek() to see the first element without removing it.
calledIds.Peek();

Hashsets

  • This is a collection of unique elements (no repetition) that is particularly high performance.

  • Is useful to store a set of unique data so you can search it quickly.

  • You delcare them, add and remove from them is the same way as lists.

Learning C# Notes - Part I: Classes

If you work on Game Audio, sooner or later you will need at least some basic knowledge about programming since implementing audio usually needs logic and math. Game engines and specially middleware like Wwise or Fmod does a lot of that that work for us but there are many occasons where you need some extra customization and for that, nothing beats programming.

I would also say that programming is very satisfying… when things work. Well mybe not when things work but when you understand how things work. At least in my case, I like logic and math so builiding a system and seeing that it all works when you push a button is very rewarding but it takes time to get there.

My only previous contact with coding was some high school classes too many years ago and some failed attempts at Code Academy. I never really had the need or drive to learn so I didn’t. Now that I work on the games industry as a sound designer, I found the perfect oportunity to learn so I went all in.

If you want to learn C#, there are great resources in Youtube and documentation online so maybe this post is not the best place to start. But later on, once you have some experience, this could be a good way to learn things from another perspective and at the same time refresh some concepts.

So here are my notes as I have been learning C# with some examples to show syntaxis. When I finish this series of posts I will compile all the information in one long mega-post to have a sort of C# bible.

Classes

  • Most fundamental functional unit in C#.

  • They work as blueprints which define objects.

  • These objects contain data and instructions.

  • C# is an object oriented language: these objects act as self contained functional units that can talk to each other.

  • See example below of a class that describes the notion of a car.

public class MyCar
{
    //Car Attributes
    int doors = 5;
    int year = 1967;
    string model = "Alhambra";

    //Car Actions
    public void Drive()
    {
        //The car drives.
    }

    public void Rust()
    {
        //The car rusts
    }
}

Class Instantiation & Constructors

  • Classes can be instatiated. This is like grabbing the blueprint and building a unit of the thing that the blueprint defines.

  • Once you create an instance, it can contain its own values and be independent from other instances.

  • You can use a class without creating an instance if it is a static class. (More on this later)

  • Constructors are used when we instatiate a class, even if you don’t explicitely have one on your class.

  • Constructors will create the instance using default values unless you create a custom constructor which can take different values and apply them when instatiating (see example below).

public class MyCar
{
    //Car Attributes
    int doors = 5;
    int year = 1967;
    string model = "Alhambra";

    //Constructor
    public MyCar(int carDoors, string carModel)
    {
        year = carDoors;
        model = carModel;
    }
}
  • To create a constructor, we create a method inside the class that has the same name as the class and no return value. (see above)

  • If we want the ability to input certain values when creating the class instance instead of using default values, we add these values as parameters to the method that we created (the constructor).

Class Inheritance & Abstraction

  • Classes can be independent from each other or they can have a parent-child relationship.

  • When a class is child of another, it inheriths all of it functionality and values.

  • It can be very convenient to have base classes to build from. For example, our car class could inherit from a parent “vehicle” class so we get its functionalities and variables.

  • Sometimes, we know that a base class is only going to act as a parent that will never be instantiated and in those cases we can add the modifier “abstract” to it.

  • So an abstract class can never be instantiated and it can have incomplete information (like empty methods) that its children must contain and complete.

  • Inheritance is indicated with a colon after the class name. See example below.

public class MyCar : Vehicle
{
}

Classes & Unity

  • In Unity, usually each class is contained in its own script file but sometimes a single file can contain several classes.

  • When we create a new c# script in Unity, you will see that we are always creating a new class which inherits from Monobehaviour.

  • Monobehaviour is a parent class that gives you access to functions related to runtime like Start() or Update(). So these scripts usually deal with things that work in real time.

  • Scripts inheriting from Monobehaviour also allow you to add them to any game object as component. So you can think about them as "custom unity components". See how the scripts below, which inherit from Monobehaviour, act as components.


  • Another Unity parent class that is usually used is ScriptableObject. In contrast with Monobehaviour, scripts that inherit from ScriptableObject don't have runtime related functions and can't be added as components.

  • Nevertheless, these scripts can be created as custom data objects to contain information about any concept in a game like enemies, weapons, sounds or shaders and reside on our project as an asset file.

  • Scriptable objects can be instatiated via code but the most usual and useful way to create them is on Unity as a project asset. To do this, we use the "CreateAssetMenu" keyword when we define them. See example:

[CreateAssetMenu(menuName = "YourGame/Audio/Data/Weapon")]
public class WeaponAudioData : AudioData
{
}
  • If you think about it, scriptable objects are a way to separate funcionality and data. The first will stay on Monobehaviour scripts running in real time while the data will reside on scriptable objects and access when needed. This makes code much more scalable and easy to debug.

  • Lastly, keep in mind that Unity can also contain classes that just don’t inherit from either Monobehaviour or ScriptableObject.

Pro Tools Batch Rename & Regular Expressions

Batch renaming was introduced into Pro Tools at the end of 2017, with the 12.8.2 version. Since then, I haven’t had much of a chance to use this feature since most of my work has been mixing and sound design. Nevertheless, after some recent days of voice acting recording and all the editing associated, I have been looking a bit into this feature.

So this is a quick summary of what you can do with it with some tips and examples.

Operations

There are two batch rename windows in Pro Tools, one for clips and another for tracks. They are, for the most part, identical. You can open each of them with the following shortcuts:

  • Clips: CTRL + SHIFT + R

  • Tracks: OPTION + SHIFT + R

Both windows also have a preset manager which is great to have.

As you can see, there are four different operations you can do: Replace, Trim, Add and Numbering. As far as I can tell, the different operations are always executed from top to bottom, so keep that in mind when designing a preset. Let’s see each of them in more detail:

Replace (CMD + R) allows you to search for any combination of letters and/or numbers and replace with a different one. The “Clear Existing Name” checkbox allows you to completely remove any previous name the track or clip had. This option makes sense when you want to start from scratch and use any of the other operations (add and numbering) afterwards.

For example, let’s say you don’t like when Pro Tools adds that ugly “dup1” to your track name when duplicating them. You could use a formula like this:

Original names New names

FX 1.dup1 FX 1 Copy
FX 2.dup1 FX 2 Copy
FX 3.dup1 FX 3 Copy

You may realise that this would only work with the first copy of a track. Further copies of the same track will be named “…dup2, ...dup3” so the replace won’t work. There is a way to fix that with the last checkbox, “Regular Expressions”. This allows you to create complex and advanced functions and is where the true power of batch renaming resides. More about it later.

Trim (CMD + T) is useful when you want to shave off a known amount of characters from the beginning or end of the name. You can even use the range option to remove characters right in the middle. This of course makes the most sense when you have a consistent name length, since any difference in size will screw up the process.

So, for example, if you have the following structure and you want to remove the date, you can use the following operation:

Original names New names

Show_EP001_Line001_280819_v01 Show_EP001_Line001_v01
Show_EP001_Line002_280819_v03 Show_EP001_Line002_v03
Show_EP001_Line003_280819_v02 Show_EP001_Line003_v02

Add (CMD + D) lets you insert prefixes and suffixes, pretty much doing the opposite of Trim. You can also insert any text at a certain index in the middle of the name.

We can add to the previous example a suffix to mark the takes that are approved. It would look like this:


Original names New names

Show_EP001_Line001_v01 Show_EP001_Line001_v01_Approved
Show_EP001_Line002_v03 Show_EP001_Line002_v03_Approved
Show_EP001_Line003_v02 Show_EP001_Line003_v02_Approved

Finally, Numbering (CMD + N) is a very useful operation that allows you to add any sequence of numbers or even letters at any index. You can choose the starting number or letter and the increment value. As far as I can tell, this increment value can’t be negative. If you want to use a sequence of letters, you need to check the box “Use A..Z” and in that case the starting number 1 will correspond with the letter “A”.

If we are dealing with different layers for a sound, we could use this function to label them like so:

Original names New names

Plasma_Blaster Plasma_Blaster_A
Plasma_Blaster Plasma_Blaster_B
Plasma_Blaster Plasma_Blaster_C

As you can see, in this case, we are using letters instead of numbers and and underscore to separate them form the name. Also, you can see that in the case of clips, you can choose wether the order comes from the timeline itself of from the clip list.

Regular Expressions

Regular expressions (or regex) are kind of an unified language or syntax used in software to search, replace and validate data. As I was saying this is where the true power of batch renaming is. In fact, it may be a bit overkill for Pro Tools but let’s see some formulas and tips to use regular expressions in Pro Tools.

This stuff gets tricky fast, so you can follow along trying out the examples in Pro Tools or using https://regex101.com/.

Defining searches

First off, you need to decide what do you want to find in order to replace it or delete it (replace with nothing). For this, of course you can search for any term like “Take” or “001” but obviously, you don’t need regex for that. Regex shines when you need to find more general things like any 4 digit number or the word “Mic” followed by optional numbers. Let’s see how we can do all this with some commands and syntax:

[…] Anything between brackets is a character set. You can use “-” to describe a range. For example, “[gjk]” would search for either g, i or k, while [1-6] means any number from 1 to 6. We could use “Take[0-9]“ to search for the word “Take” followed by any 1 digit number.

Curly brackets are used to specify how many times we want to find a certain character set. For example ”[0-9]” would look for any combination of numbers that is 5 digits long. This could be useful to remove or replace a set of numbers like a date which is always constant. You can also use ”[0-9]” to search for any number which is between 5 and 8 digits. Additionally, ”[0-9]” would look for any number longer than 5 digits.

There are also certain special instructions to search for specific sets of charaqcters. “\d” looks for any digit (number) type character, while “\w” would match any letter, digit or underscore character. “\s” finds any whitespace character (normal spaces or tabs).

Modifiers

When defining searches, you can use some modifiers to add extra meaning. Here are some of the most useful:

. (dot or full stop) Matches any character. So, “Take_.” would match any character that comes after the underscore.
+ (plus sign) Any number of characters. We could use “Take_.+” to match any number of character coming after the underscore.
^ (caret) When used within a character set means “everything but whatever is after this character:. So “[^a-d]” would match any character that is not a, b, c or d.
? (question mark) Makes a search optional. So for example, “Mic\d?“ would match the word Mic by itself and also if it has any 1 digit number after it.
* (Asterisk) Also makes a search optional but allowing multiple instances of said search. In a way, is a combination of + and ?. So for example, ”Mic\d*” would match “Mic” by itself, “Mic6” but also “Mic456” and, in general, the word Mic with any number of digits after it.
| (vertical bar) Is used to expressed the boolean “or”. So for example, “Approved|Aproved” would search for either of these options and apply the same processing to both if they are found.

Managing multiple regex in the same preset

You sometimes want to process several sections of a name and replace them with different things, regardless of their position and the content around them. To achieve this, you could create a regex preset for each section but is also possible to have several regex formulas in just one. Let´s see how we can do this.

In the “Find:” section, we need to use (…) (parenthesis). Each section encompased between parenthesis is called a group. A group is just a set of instructions that is processed as a separated entity. So if we want to search for “Track” and also for a 3 digit number we could use a search like this one “(Track)(\d)“. Now, it is important to be careful with what we use between the two groups depending of our goals. With nothing in between, Pro Tools would strictly search for the word track, followed by a 3 digit number. We may want this but tipically what we want is to find those terms wherever in the name and in whichever order. For this, we could use a vertical bar (|) in between the two groups like so: “(Track)|(\d)“ which is telling Pro Tools: hey, search for this or for this and then replace any for whatever.

But what if you want to replace each group for an specific different thing? This is easily done by also using groups in the ¨Replace¨section. You need to indentify each of them with “?1”, “?2” and so on. So the example on the right would search for the word “Track” anywhere in the name and replace ti with “NewTrack” and then it would search for any 3 digit number and replace it with “NewNumbers”

Here is a more complex example, involving 4 different groups. If you have a look at the original names, you will see this structure: “Show_EpisodeNumber_Character_LineNumber”. We would want to change the character and show to the proper names. We are also using a “v” character after the line number to indicate that this is the approved take by the client, it could be nice if we could transform this into the string “Approved”. Finally, Pro Tools adds a dash (-) and some numbers after you edit any clip and we would want to get rid of all of this. If you have a look at our regex, you would see that we can solve all of this in one go. Also, notice how the group order is not important since we are using vertical bars to separate them. You will see that in the third group, I’m searching for anything that comes after a dash and replacing it with just nothing (ie, deleting it), which can be very handy sometimes. So the clip names will change like so:

Original names New names

Show_045_Character_023-01 Treasure_Island_045_Hero_023
Show_045_Character_026v-03 Treasure_Island_045_Hero_026_Approved
Show_045_Character_045v-034 Treasure_Island_045_Hero_045_Approved

Other regex functions that I want to learn in the future

I didn´t have time to learn or figure out everything that I have been thinking regular expressions could do, so here is a list of things I would like to reasearch in the future. Maybe some of them are impossible for now. If you are also interested in achieving some of these things, leave a comment or send me an email and I could have a look in the future.

  • Command that adds the current date with a certain format.

  • Commands that add meta information like type of file, timecode stamp and such.

  • Syntax that allows you to search for a string of characters, process them in some way, and them use it in the replace section.

  • Deal with case sensitivity.

  • Capitalize or uncapitalize characters.

  • Conditional syntax. (If you find some string do A, if you don´t, do B).

Regex Resources:

https://regex101.com/
https://www.cheatography.com/davechild/cheat-sheets/regular-expressions/
https://www.youtube.com/playlist?list=PL4cUxeGkcC9g6m_6Sld9Q4jzqdqHd2HiD

Conclusion

I hope you now have a better understanding of how powerful batch renaming can be. With regular expressions I just wanted to give you some basic principles to build upon and have some knowledge to start building more complex presets that can save you a lot of time.