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!

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

LikeLike

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

LikeLiked by 1 person

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!

LikeLike

// 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;

}

}

LikeLike

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.

LikeLike

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

LikeLike

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!

LikeLike

Hi Jason,

Wondering if you could explain something to me. When I ran my program, the frequency calculation was not working for the note or sharp/flat. I had typed my code with numbers in an integer format and not decimal format (ie, (pow(2, (1 / 12))) and not (pow(2.0, (1.0 / 12.0))) ). I made that one change, and then it worked. Because I was using a double type variable, am I not able to modify that with plain old integers? It seemed strange to me so I just wanted to understand what the issue was, as I am new to coding.

Thanks,

Callie

LikeLike

Putting the 0 after the decimal places sets the variable as a float rather than an int. So, if you did not put the decimal place it would be doing integer calculations and cutting off the decimal place. So, you were probably encountering a rounding error.

LikeLike

amazing

thank you very much

keep going

LikeLike

hi there :

i did it but i couldn’t submit it . how can i do that ?

please help

LikeLike

Look at the instructions in CS50. There should be something on there about how to do the submit50 action.

LikeLike

Hi Jason,

I dont think this line of code is correct for getting the octave:

int octave = note[strlen(note) – 1] – ‘0’;

strlen(note) will not work because the parameter note which is passed to the frequency function is not a string but instead a pointer variable to the address of the first character of note. Therefore the function strlen will not work with the variable note since it is not a string or an array of characters, but instead a pointer to the address.

Would love to hear your thoughts.

Thanks!

LikeLike

Thanks for the input. If you look at the function for frequency, you will notice that we are declaring the argument note as a string. A string in C is really just a one dimensional array of chars. Thus we can use functions such as strlen as shown here. Then we use the -‘0’ to convert the char to an int as discussed here.

Thanks again and good luck.

LikeLike