In this post I will go through the solution to Problem Set 3 in CS50. In this Problem Set, we start by writing out a text file that we will use to eventually play the happy birthday song. This part is one of the easier of this case. The first character designates the letter associated with that note (i.e. A, B, C, D, etc.). The next character designates the octave of that note. The @ separates the note from the length of that note. If the note is a sharp, we designate that with a # after the letter. Next, all you have to do is make sure that your notes correspond with the ones given for the happy birthday song.

Now we can actually get to some code. In this exercise, we are tasked with finishing the functions that will allow our text to be converted into a .wav file. The first of these that I will cover is the duration. Now, we know from the above text that our duration is represented by a fraction. However, we need to know how many eighths there are in each fraction. The math for this is very simple. First we get the 0th character from our string which will be the numerator. The 2nd character will then be our denominator. Therefore, if we take the number 8 and divide it by the denominator, and then multiply that by the numerator, we will have our number of eighths.

I am going to skip frequency for now and cover that implementation last. For the is_rest function, we are only concerned with knowing whether we have come across a line that is empty. We learned that a string, even one that does not contain a value, ends with the null terminator “\0”. So, with this knowledge we just have to check to see if the 0th character in the string is in fact the null terminator. If it is, we return true, else we return false.

Now for the most complex part of the solution, which is the frequency. The first variable I have set up will get the octave from the string. We know that the octave will always be last, so we can get the string length minus one and this will give us the position of the octave. Next, we declare a double precision variable for the frequency and set it equal to 0.0. The next series of lines may look a little confusing, but this is just a multi-way if statement that will return the frequency based on the letter of the note. You could also implement this as a switch case statement and land in the same place.

The next set of statements loops through the octaves to adjust the frequency. Remember that we are told that A4 is 440 htz, and that our frequency will adjust whether the octave is lower or higher. In this solution, we loop through the octave applying an adjustment for the frequency based on whether it is higher or lower than A4. The final step is to adjust the frequency one last time if there is a flat or sharp designation. We simply look at the 1st element in the string which is where these symbols will be and apply the math that adjusts the frequency. Our final step is to round the frequency (remember it is a double) and return it from the function.

And that is it for Music. I hope that this helped you come up with another solution to this Problem Set. If you found this information useful, please remember to share this post on your favorite social media by clicking on the buttons below. As always…Happy Programming!

7 comments

  1. Hi Jason,

    How does – ‘0’ give an integer? For example: int numerator = fraction[0] – ‘0’;

    Forgive my ignorance, but I’m just learning to code with CS50 via edx.

    Thanks,
    Chris

    Like

    1. No worries Chris. So, the fraction that is being passed into the function is a string. Now we know a string is simply an array of chars. Thus, with a fraction of 1/8, the 0th index, or fraction[0], will equal ‘1’. Remember, this is not a number, but actually a char. In ASCII, the character ‘1’ is represented by the number 49 and the character ‘0’ is represented by the number 48. So, 49 minus 48 equals 1 (the number). Check out this thread on StackOverflow for a more in depth discussion.

      https://stackoverflow.com/questions/15598698/why-does-subtracting-0-in-c-result-in-the-number-that-the-char-is-representing

      Thanks for participating in the comments. Feel free to share any of my content if it helps you getting through the CS50 course.
      Good luck,
      Jason

      Like

  2. HI JASON, thanks for sharing this, I was in a bucle until I see your resolution. I´m new at this. Well I want to share my code too. Can you tell what do you think? PD. Sorry for my bad English.

    // Helper functions for music

    #include
    #include
    #include
    #include
    #include “helpers.h”

    // Converts a fraction formatted as X/Y to duration
    int duration(string fraction)
    {
    // converts numerator into duration
    int n = strlen(fraction);
    int numerador = atoi(&fraction[0]); *If someone see this, can you tell me we do I need to write &?
    int denominador = atoi(&fraction[(n – 1)]);
    return ((8 / denominador) * numerador);
    }

    // Calculates frequency (in Hz) of a note
    int frequency(string note)
    {
    // calculamos la frecuencia segun la OCATAVA // calculate frecuency of OCTAVE
    int octave = note[strlen(note) – 1] – ‘0’;
    int dif = octave – 4;
    int f;

    if(dif TRUE, else FALSE
    if (s == NULL)
    {
    return 0;
    }
    else
    {
    return 1;
    }
    }

    thanks for reading!

    Like

    1. // Helper functions for music

      #include
      #include
      #include
      #include
      #include “helpers.h”

      // Converts a fraction formatted as X/Y to eighths
      int duration(string fraction)
      {
      // converts numerator into duration
      int n = strlen(fraction);
      int numerador = atoi(&fraction[0]);
      int denominador = atoi(&fraction[(n – 1)]);
      return ((8 / denominador) * numerador);
      }

      // Calculates frequency (in Hz) of a note
      int frequency(string note)
      {
      // calculamos la frecuencia segun la OCATAVA
      int octave = note[strlen(note) – 1] – ‘0’;
      int dif = octave – 4;
      int f;

      if(dif < 0)
      {
      f = 440 / 2 ^ (dif);
      }
      else
      {
      f = 440 * 2 ^ (dif);
      }

      //calculamos la frecuencia segun la LETRA

      double freq = 440.0;

      if (note[0] == 'B')
      {
      freq *= (pow(2.0, (2.0 / 12.0)));
      }
      else if (note[0] == 'C')
      {
      freq /= (pow(2.0, (9.0 / 12.0)));
      }
      else if (note[0] == 'D')
      {
      freq /= (pow(2.0, (7.0 / 12.0)));
      }
      else if (note[0] == 'E')
      {
      freq /= (pow(2.0, (5.0 / 12.0)));
      }
      else if (note[0] == 'F')
      {
      freq /= (pow(2.0, (4.0 / 12.0)));
      }
      else if (note[0] == 'G')
      {
      freq /= (pow(2.0, (2.0 / 12.0)));
      }

      //calcula la frecuencia segun los SEMITONOS

      if (note[1] == '#')
      {
      freq *= (pow(2.0, (1 / 12)));
      }
      else if (note[1] == 'b')
      {
      freq /= (pow(2.0, (1 / 12)));
      }

      //SUMA todas las frecuencias

      return round(freq) + f;
      }

      // Determines whether a string represents a rest
      bool is_rest(string s)
      {
      // Cuando el string es NULL retorna TRUE, caso contrario FALSE
      if (s == NULL)
      {
      return 0;
      }
      else
      {
      return 1;
      }
      }

      Like

      1. Thanks Adrian. Sorry it took me a little while to reply. I am glad that I was able to help. That is my whole goal with this website. I glanced over your code, and it all looks good to me. The key with this is that your functions do what they intend them to do. If you’re passing the check50 test, than you are all good. Everyone is going to come up with different solutions to the problem. What I suggest, is after you have completed some additional problem sets, go back and refactor the ones that you has done before. This will help you put what you learn into practice on your own code. Keep at it, and you will get there. Please let me know if there is anything else I can do to help.

        Like

  3. Hi Jason,

    İ am also very new to cs 50 course and trying to understand the concept even though i am having difficulties on psets. İ did not understand some parts of it and wanted to ask you. Why do we get fraction[0] as a numerator and fraction[2] as a denominator. Would you also recommend any online exercise for c? İ just started 2 weeks ago and i am kind of unable to put my thoughts into code and having really hard time. Even though i understand everything in the course while watching in the problem sets i struggle.
    Thanks
    emir

    Like

    1. Hello Emir,
      So, let’s look at the fraction 1/8. If our user types this in, what we are really getting is the string “1/8”. Now, the string is actually an array of characters. The first character is “1”, the second is “/” , and the third is “8”. So, think of our fraction as the the array fraction[“1”, “/”, “8”]. Now you want to get the first and last indexes of the array, which is where the “1” and “8” are sitting. However, array indexes are 0 based. So, the 0th index (i.e. first element) of the fraction array will return “1”. Thus, fraction[0] will return the “1”, fraction[1] will return the “/”, and fraction[2] will return the “8”. The key with the first few weeks of CS50 is making sure you understand the concepts. Unless you plan to code in C, which is a very low level language. Once you start digging into the Python portion you will see how much the higher level languages do under the hood. I gives you a greater appreciation for more modern day languages. Stick with it and keep plugging away. Good luck!

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s