Advertisement
UME14

CS50/PSET3

Jan 3rd, 2019
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.89 KB | None | 0 0
  1. PSET3 ||
  2. ========
  3.  
  4. =====>>helpers.c=====
  5. // Helper functions for music
  6.  
  7. #include <cs50.h>
  8.  
  9. #include "helpers.h"
  10.  
  11. // Converts a fraction formatted as X/Y to eighths
  12. int duration(string fraction)
  13. {
  14. // TODO
  15. }
  16.  
  17. // Calculates frequency (in Hz) of a note
  18. int frequency(string note)
  19. {
  20. // TODO
  21. }
  22.  
  23. // Determines whether a string represents a rest
  24. bool is_rest(string s)
  25. {
  26. // TODO
  27. }
  28.  
  29. =====>>helpers.h=====
  30. // Helper functions for music
  31.  
  32. #include <cs50.h>
  33.  
  34. // Converts a fraction to eighths
  35. int duration(string fraction);
  36.  
  37. // Calculates frequency (in Hz) of a note formatted as XY,
  38. // where X is any of A through G and Y is any of 0 through 8,
  39. // or formatted as XYZ, where X is any of A through G, Y is # or b,
  40. // and Z is any of 0 through 8
  41. int frequency(string note);
  42.  
  43. // Determines whether a string represents a rest
  44. bool is_rest(string s);
  45.  
  46. =====>>Makefile=====
  47. CC ?= clang
  48. CFLAGS ?= -fsanitize=integer -fsanitize=undefined -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wshadow
  49. LDLIBS ?= -lcs50 -lm
  50.  
  51. .PHONY: all
  52. all: notes synthesize
  53.  
  54. notes: helpers.c helpers.h notes.c wav.c wav.h
  55. $(CC) $(CFLAGS) -o notes helpers.c notes.c wav.c $(LDLIBS)
  56.  
  57. synthesize: synthesize.c wav.c wav.h helpers.c helpers.h
  58. $(CC) $(CFLAGS) -o synthesize helpers.c synthesize.c wav.c $(LDLIBS)
  59.  
  60. .PHONY: clean
  61. clean:
  62. rm -f notes synthesize *.wav
  63.  
  64. =====>>notes.c=====
  65. // Prints frequencies of and outputs WAV file with all notes in an octave
  66.  
  67. #include <cs50.h>
  68. #include <stdio.h>
  69. #include <string.h>
  70.  
  71. #include "helpers.h"
  72. #include "wav.h"
  73.  
  74. // Notes in an octave
  75. const string NOTES[] = {"C", "C#", "D", "D#", "E", "F",
  76. "F#", "G", "G#", "A", "A#", "B"
  77. };
  78.  
  79. // Default octave
  80. #define OCTAVE 4
  81.  
  82. int main(int argc, string argv[])
  83. {
  84. // Override default octave if specified at command line
  85. int octave = OCTAVE;
  86. if (argc == 2)
  87. {
  88. octave = atoi(argv[1]);
  89. if (octave < 0 || octave > 8)
  90. {
  91. fprintf(stderr, "Invalid octave\n");
  92. return 1;
  93. }
  94. }
  95. else if (argc > 2)
  96. {
  97. fprintf(stderr, "Usage: notes [OCTAVE]\n");
  98. return 1;
  99. }
  100.  
  101. // Open file for writing
  102. song s = song_open("notes.wav");
  103.  
  104. // Add each semitone
  105. for (int i = 0, n = sizeof(NOTES) / sizeof(string); i < n; i++)
  106. {
  107. // Append octave to note
  108. char note[4];
  109. sprintf(note, "%s%i", NOTES[i], octave);
  110.  
  111. // Calculate frequency of note
  112. int f = frequency(note);
  113.  
  114. // Print note to screen
  115. printf("%3s: %i\n", note, f);
  116.  
  117. // Write (eighth) note to file
  118. note_write(s, f, 1);
  119. }
  120.  
  121. // Close file
  122. song_close(s);
  123. }
  124.  
  125. =====>>synthesize.c=====
  126.  
  127. // Prompts user for a sequence of notes with which to synthesize a song
  128.  
  129. #include <cs50.h>
  130. #include <stdio.h>
  131. #include <string.h>
  132.  
  133. #include "helpers.h"
  134. #include "wav.h"
  135.  
  136. int main(int argc, string argv[])
  137. {
  138. // Check command line arguments
  139. if (argc != 2)
  140. {
  141. fprintf(stderr, "Usage: synthesize FILE\n");
  142. return 1;
  143. }
  144. string filename = argv[1];
  145.  
  146. // Open file for writing
  147. song s = song_open(filename);
  148.  
  149. // Expect notes from user until EOF
  150. while (true)
  151. {
  152. // Expect note
  153. string line = get_string("");
  154.  
  155. // Check for EOF
  156. if (line == NULL)
  157. {
  158. break;
  159. }
  160.  
  161. // Check if line is rest
  162. if (is_rest(line))
  163. {
  164. rest_write(s, 1);
  165. }
  166. else
  167. {
  168. // Parse line into note and duration
  169. string note = strtok(line, "@");
  170. string fraction = strtok(NULL, "@");
  171.  
  172. // Write note to song
  173. note_write(s, frequency(note), duration(fraction));
  174. }
  175. }
  176.  
  177. // Close file
  178. song_close(s);
  179. }
  180.  
  181. =====>>wav.c=====
  182.  
  183. // A simple sound library adapted from Douglas Thain's ([email protected])
  184. // wavfile library for CSE 20211 made available under
  185. // the Creative Commons Attribution license.
  186. // https://creativecommons.org/licenses/by/4.0/
  187.  
  188. #include <cs50.h>
  189. #include <errno.h>
  190. #include <math.h>
  191. #include <stdio.h>
  192. #include <stdlib.h>
  193. #include <string.h>
  194. #include <time.h>
  195.  
  196. #include "wav.h"
  197.  
  198. #define WAV_SAMPLES_PER_SECOND 44100
  199. #define BEAT_LEN (WAV_SAMPLES_PER_SECOND / 4)
  200. #define DECAY_DURATION BEAT_LEN / 2
  201. #define DECAY_FACTOR -5
  202. #define SILENCE_DURATION 5
  203. #define VOLUME 32000
  204.  
  205. #ifndef M_PI
  206. #define M_PI 3.14159265358979323846
  207. #endif
  208. #ifndef M_E
  209. #define M_E 2.71828182845904523536
  210. #endif
  211.  
  212. // Represents a WAV header
  213. struct wav_header
  214. {
  215. char riff_tag[4]; // "RIFF"
  216. int32_t riff_length; // Size of RIFF header
  217. char wave_tag[4]; // "WAVE"
  218. char fmt_tag[4]; // "fmt ", start of format data
  219. int32_t fmt_length; // Size of fmt subchunk
  220. int16_t audio_format; // Audio compression format
  221. int16_t num_channels; // Number of audio channels
  222. int32_t sample_rate; // Samples of audio per second
  223. int32_t byte_rate; // Bytes per second
  224. int16_t block_align; // Bytes for one sample
  225. int16_t bits_per_sample; // Bits for one sample
  226. char data_tag[4]; // "data", start of sound data
  227. int32_t data_length; // Size of sound data
  228. };
  229.  
  230. // Opens a new file and populates it with WAV header
  231. static FILE *wav_open(const char *filename)
  232. {
  233. struct wav_header header;
  234. int samples_per_second = WAV_SAMPLES_PER_SECOND;
  235. int bits_per_sample = 16;
  236. strncpy(header.riff_tag, "RIFF", 4);
  237. strncpy(header.wave_tag, "WAVE", 4);
  238. strncpy(header.fmt_tag, "fmt ", 4);
  239. strncpy(header.data_tag, "data", 4);
  240. header.riff_length = 0;
  241. header.fmt_length = 16;
  242. header.audio_format = 1;
  243. header.num_channels = 1;
  244. header.sample_rate = samples_per_second;
  245. header.byte_rate = samples_per_second * (bits_per_sample / 8);
  246. header.block_align = bits_per_sample / 8;
  247. header.bits_per_sample = bits_per_sample;
  248. header.data_length = 0;
  249. FILE *file = fopen(filename, "w+");
  250. if (!file)
  251. {
  252. return NULL;
  253. }
  254. fwrite(&header, sizeof(header), 1, file);
  255. fflush(file);
  256. return file;
  257. }
  258.  
  259. // Outputs the contents of a WAV file to disk
  260. static bool wav_write(FILE *file, short data[], size_t length)
  261. {
  262. size_t items = fwrite(data, sizeof(short), length, file);
  263. return items == length;
  264. }
  265.  
  266. // Updates the WAV file header and closes the file
  267. static bool wav_close(FILE *file)
  268. {
  269. int file_length = ftell(file);
  270. int data_length = file_length - sizeof(struct wav_header);
  271. fseek(file, sizeof(struct wav_header) - sizeof(int), SEEK_SET);
  272. fwrite(&data_length, sizeof(data_length), 1, file);
  273. int riff_length = file_length - 8;
  274. fseek(file, 4, SEEK_SET);
  275. size_t items = fwrite(&riff_length, sizeof(riff_length), 1, file);
  276. fclose(file);
  277. return items == 1;
  278. }
  279.  
  280. // Allocate memory for an empty song, set initial capacity to 16 notes
  281. song song_open(string filename)
  282. {
  283. // Allocate memory for empty song
  284. song s = calloc(1, sizeof(struct song));
  285. if (!s)
  286. {
  287. return NULL;
  288. }
  289.  
  290. // Give the song an initial capacity of 16 notes
  291. s->filename = filename;
  292. s->capacity = 16;
  293. s->notes = calloc(s->capacity, sizeof(*s->notes));
  294. if (!s->notes)
  295. {
  296. free(s);
  297. return NULL;
  298. }
  299. s->size = 0;
  300. return s;
  301. }
  302.  
  303. // Append note to end of array of notes in song
  304. // Each duration unit is an eighth note at 120bpm
  305. bool note_write(song s, int frequency, int duration)
  306. {
  307. // Increase size of song if necessary
  308. if (s->size == s->capacity)
  309. {
  310. if (s->capacity > SIZE_MAX / sizeof(*s->notes) / 2)
  311. {
  312. return false;
  313. }
  314. s->capacity *= 2;
  315. note **temp = realloc(s->notes, sizeof(*s->notes) * s->capacity);
  316. if (!temp)
  317. {
  318. return false;
  319. }
  320. s->notes = temp;
  321. }
  322.  
  323. // Create a new note
  324. note *n = calloc(1, sizeof(note));
  325. if (!n)
  326. {
  327. return false;
  328. }
  329. n->frequency = frequency;
  330. n->duration = duration;
  331.  
  332. // Add note to song
  333. s->notes[s->size] = n;
  334. s->duration += duration;
  335. s->size++;
  336.  
  337. return true;
  338. }
  339.  
  340. // Add a frequency of 0 to represent a rest in the song
  341. bool rest_write(song s, int duration)
  342. {
  343. return note_write(s, 0, duration);
  344. }
  345.  
  346. // Compute wavforms based on note frequency and save to disk
  347. bool song_close(song s)
  348. {
  349. if (!s || !s->size)
  350. {
  351. return false;
  352. }
  353. FILE *f = wav_open(s->filename);
  354. if (!f)
  355. {
  356. return false;
  357. }
  358. short *waveform = calloc(s->duration * BEAT_LEN, sizeof(short));
  359. if (!waveform)
  360. {
  361. return false;
  362. }
  363. short *current_sample = waveform;
  364.  
  365. // Iterate over notes in the song
  366. for (size_t i = 0; i < s->size; i++)
  367. {
  368. // Compute waveform for note
  369. note *n = s->notes[i];
  370. double phase = 0.0;
  371. double phase_step = n->frequency * 2.0 * M_PI / WAV_SAMPLES_PER_SECOND;
  372. short *note_end = current_sample + n->duration * BEAT_LEN - SILENCE_DURATION;
  373. short *decay_start = note_end - DECAY_DURATION;
  374. for (; current_sample != decay_start; phase += phase_step)
  375. {
  376. *current_sample++ = round(VOLUME * sin(phase));
  377. }
  378. for (; current_sample != note_end; phase += phase_step)
  379. {
  380. double t = (double) (current_sample - decay_start) / BEAT_LEN;
  381. *current_sample++ = round(VOLUME * pow(M_E, t * DECAY_FACTOR) * sin(phase));
  382. }
  383.  
  384. // Skip over silence at end of note
  385. current_sample += SILENCE_DURATION;
  386.  
  387. // Done with note, free it
  388. free(n);
  389. }
  390.  
  391. // Output WAV data to file
  392. bool success = wav_write(f, waveform, s->duration * BEAT_LEN);
  393. wav_close(f);
  394.  
  395. // Free song
  396. free(s->notes);
  397. free(s);
  398. free(waveform);
  399. return success;
  400. }
  401.  
  402. =====>>wav.h=====
  403.  
  404. // A simple sound library adapted from Douglas Thain's ([email protected])
  405. // wavfile library for CSE 20211 made available under the
  406. // Creative Commons Attribution license.
  407. // https://creativecommons.org/licenses/by/4.0/
  408.  
  409. #include <cs50.h>
  410. #include <stdio.h>
  411. #include <inttypes.h>
  412.  
  413. // Representation of a note
  414. typedef struct note
  415. {
  416. int frequency;
  417. int duration;
  418. }
  419. note;
  420.  
  421. // Representation of a song
  422. struct song
  423. {
  424. string filename;
  425. note **notes;
  426. size_t capacity;
  427. size_t size;
  428. int duration;
  429. };
  430. typedef struct song *song;
  431.  
  432. // Adds a note to a song for a given duration (in eighths)
  433. bool note_write(song s, int frequency, int duration);
  434.  
  435. // Adds a rest to a song for a given duration (in eighths)
  436. bool rest_write(song s, int duration);
  437.  
  438. // Saves a song to disk
  439. bool song_close(song s);
  440.  
  441. // Creates a new song
  442. song song_open(string filename);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement