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.

Sound Design Job Interview Questions

Hi, there. After going through the process of doing a bunch of interviews for sound designer positions, I thought it could be useful to compile the most usual questions I encountered.

Take them as a guide to know what to expect. I personally like to have a rough idea of how I want to answer each of these, but I mostly leave it to improvisation in terms of what to actually say. I prefer the conversation to flow naturally as much as possible.

I left out a bunch of generic HR questions because they are just everywhere, although I left some in just to give some comments on them. Also, you will probably have at least 2-3 different interviews with different people and these questions are just taken from different interview layers.

About you / Personal

Tell me a bit about yourself. // Classic opener. Preare a summary in your mind, to the point. Quality before quantity.

What do you do on your spare time apart from video games? // You are maybe temtped to say “record audio libraries” here and that’s fine but I would prefer to show a different side of myself that complements the professional side.

What are you playing right now?

Favourite game ever?

Favourite game ever for sound? // This and the above questions are a nice chance to show your taste a bit more. You can taylor the answer to the interviewer or company a bit but I would pretty much answer honestly.

Who is your Hero? // Interesting one, think of something cool.

Career / HR

What is your dream job? // Could be a tricky one but nice to show ambition here.

Where do you see yourself in five years // Similar to the previous one but maybe if you get this one after the previous one you can talk more about your place in life in a more personal way.

What is your greatest achievement?

What is your greatest failure?

What is your best stregth?

What is your worst weakness? // HR bread and butter. Prepare reasonable answers and move on to the interesting stuff.

What hard or tricky problem have you found and solved? // A chance to show you are resilient.

Do you have experience working under pressure? // Use a real example here.

What has been the biggest disappointment in your career to date?

Why do you wish to leave your current position? // I prefer an answer here that points towards career growth if possible.

Describe a personal project you have worked on lately. // Good to show you are creative and have initiative which is probably the case since you want a sound designer job.

Questions for the interviewer? // This one does usually come up, at every level interview. I like to ask about the company culture and vibe, I usually ask them if they like working there. You can then ask more specific questions about the position itself and finally about the selection process.

Position Specific

What 3 things you think you need to thrive in your position? // I got this one and didn’t have anything prepared.. Your answer should show a nice range of things here. One of my mine was: “People around who know much more than me”, which was the first thing I thought.

Team player? // There is usually a “team” oriented question. I feel stupid just saying “of course, I love working with my collegues!” so what I usually do is prepare a real example of teamwork epicness on my previous jobs. In general, is always good to prepare an example for each thing that you claim. The more tangible the example is, the better.

Do you have experience working with clients? // Good to keep in mind an answer for this. As said above, prepare an example.

What interested you about our company? // Another classic, you should try to focus on positives about the games the company creates, if you know them or play them, much better.

Salary expectations? // There are many strategies out there for this one but my advice is that you try to get a range from them first but this doesn’t always happen. In my case, I like to do some research online about the company and area/city so I can have a reasonable range in my mind. Be confident.

How much notice do you need to give your current employer?

Willing to re-locate?

Audio Specific

DAW, Middleware, Game Engine, Plugins, Audio Library Software // You will get generic questions about whichever piece of software the company uses. Prepare answers that show how much you know them and try to give as many examples as possible. Don’t stretch the truth here.

Do you have experience using Middleware? Which one? // Even if you don’t have experience with the middleware the company uses is still good to talk about your experience with others.

Which DAW do you know how to use? Which one you like the best? // I usually give my honest opinion here but I like to stress that all modern DAWs nowadays are pretty good and capable.

Do you have experience using any audio library software like Soundly or Soundminer?

Your favourite audio library or your most used one?

What is your favourite sound generation tool or synth that you use?

What did you use to do (insert project name)? // Give as much info and details here as possible, this is a great question to get.

How do you balance quality and deadlines? // Like this, of course.

What is your favourite tool, technique, process or software? // Cool one to have prepared with that tool or plugin you can’t live without. It is ideal to describe what you do with it and then give examples from real world projects where you apply it.

What is an understimated area of audio?

Describe your process of creating an SFX from the idea to implementation. // Give a general idea of your pipeline (with examples) but is also good to show hints of flexibility to work with other pieces of software.

Which SFXs have you created recently that you are very satisfied with?

Do you have experience doing field recordings?

Do you keep notes during recording sessions? How do you do this?

Do you have experience creating audio libraries?

Which game engine have you used professionaly?

Do you know how to code? Which langaugaes do you know? // You have to be careful with this one as programming is a deep subject, try to give an accurate idea of what you are capable of.

What audio system have you coded that you are proud of?

Have you used Git or SVN?

Do you have experience recording/composing/producing/mixing/mastering music? // It all depends on the position offered. I only went for pure sound design jobs and still got asked about this so you will need to preapre answers on music.

What is the area within audio that you are less confortable with?

What gear do you use? What are your 3 must haves? // Be specific here, talk about pieces of equipment you like.

What motivates you as a sound designer? What realy excites you about your job?

Additional Advice


Quick & early iteration for game audio

When your job is creative and open ended sometimes is hard to feels things are finished. Even if you design, export and implement a particular SFX on your project, is hard to be sure things are done. Art is not finished, is abandoned. This sometimes produces a kind of “paralysis by analysis” situation where if you try to really make sure everything is perfect you would never finish in time.

That’s why I’m advocating for a diffferent approach on this post: Quick and early iteration. Don’t worry about things being perfect, not even “finished”, just put ideas together and throw them into the real world as fast as possible. There are a few advantages to this approach.

Context

For the most part, audio is never going to be heard in isolation so why bother making a SFX super detailed and interesting if its context is never going to allow it to shine? This is analogous to the mixing engineer trying to make the kick drum sound awesone for hours, only to realize that it doesn’t work in the context of the song.

You don’t always know how much spectral space do you have. You don’t always know how much time you have. If you try to get these things perfect you will probably be too slow on a project with hundreds of sounds.

Instead, make your best guess, put some audio together and get it into the game. As more and more audio gets in, you will start to get a sense of how the sounds work together. Later on, is when you can start thinking about teaking: with context.

Implementation

Getting audio into the game quick also lets you know sooner rather than later how an audio event should work functionally. This is important because it can affect how it should be designed. Should it be a loop? Use a speed parameter? Should you bake many different sound into a single file or have many small sounds played from a scattered instrument?

If you actually implement it, you will be faced with these decisions in a real way that will always be better than just planning. Don’t get me wrong, you should plan your work of course but that is never going to tell you if things are going to actually work. For that, you need to get your hands dirty.

On the code side of things, an early simple implementation will give you insights about your current audio tech. You will know if your audio scripts are sufficient or if you need to add new functionallities or ask a programmer to help you out. In terms of project planning, knowing this soon is a big advantage and producers will appreciate it.

Lastly, somehing nice about early implementation is that once you get feedback on the audio, you just need to swap or tweak the assets and future iteration will be quick and painless.

Focused Priorities

It would be tricky to know what is important if you don’t have a clear idea of what is going to be there. Throwing audio to the game soon gives you a more clear idea of what you need to focus on. Your time is not infinite so you need to pick your battles and add detail and love to the content that most will benefit from it.

Once a respectable amount of audio is implemented, it will be easier to decide what is best to focus on and you will get a better sense of what is important.

Earlier Unknowns

Game development is usually about solving problems and these problems are often hard to predict. You can sit all day and plan how you are going to do things but it would be impossible to take some of these future issues into account. The next best thing is to know about them as early as possible.

That way, you can tackle them with more time and have a better chance getting the resources you need.

Optimization

Something to also consider is how efficient you are in terms of memory and CPU. This is hard to determine in the earlier phases of the project and if you go too slow on the first stages of the game development, it may be too late to have enough time to react.

Building the audio earlier would give more information about how things perform and you will be able to make better strategic decisions on how to do things. In turn, this will also influence how audio is built and designed.

80% is enough

Quick iteration has an additional advantage: if anything goes wrong or new time constrains come, you know you will be 80% of the way there and that could be enough to save the day.

This is something that goes against all our instintcs as creative, detail-oriented people. We always strive for more emotive, interesting and inspiring audio but that can only happen if we have the time to do it and sometimes we just don’t. If worst comes to worst, at least we know things are functional and all the main aural information is already conveyed to the player. Maybe is not pretty but it works and sometimes, in an emergency, that could be just enough.

Iteration

So that’s pretty much the idea. As you get more and more audio in place, always trying to be fast rather than perfect, you will get a better idea of what to spend more time on and that’s when iteration comes into focus. As a project reaches maturity, you will find yourself working less on new content and more on iteration.

You will be surprised to see that most things will just need tweaking while only a few will need to be re-done from scratch. On the long run, I think you save time working this way and the result will be better. Another nice advantage is the peace of mind that comes from knowing that most things are in place and now is just a question of iteration and refinement.

Lastly, as you get more experience working this way, you will find that your initial quick, dirty work gets closer and closer to be good enough quality sometimes even high quality as you sharpen your instincts and workflows.