Assignment #1 : Digital Audio

Data Structure and Algorithms EEE2020

Written by SukJoon On, 2018142216.

Objectives

This assignment’s objective is to fill the given createWave and createWaveForOneNote functions. The necessary recipe is given in advance.

  • createWave: generates a sine wave array for “do-re-mi-fa-sol-ra-ti-do” Here, use a loop and call createWaveForOneNote in the loop.
  • createWaveForOneNote: generates a part of the wave array that plays each note.
  • printer.h and printer.cpp: generate a wav file given a wave array.
  • main.cpp: creates a wave array that contains the “do-re-mi-fa-sol-rati-do” sequence, and creates the sample wav file.

wav File

Link given below describes the wav file format. The header is defined in Printer class.

WAVE PCM soundfile format

unsigned char wavFileHeader[44] = {
		0x52, 0x49, 0x46, 0x46, // ChunkID, "RIFF"
		0x00, 0x00, 0x00, 0x00, 
			// ChunkSize, 36 + SubChunk2Size
		0x57, 0x41, 0x56, 0x45, // Format "WAVE"
		0x66, 0x6d, 0x74, 0x20, // Subchunk1ID, "FMT"
		0x10, 0x00, 0x00, 0x00, // Subchunk1Size, 16
		0x01, 0x00, // AudioFormat, PCM = 1
		0x01, 0x00, // Channel #, 1
		0x44, 0xac, 0x00, 0x00, // Sample Rate, 44100
		0x88, 0x58, 0x01, 0x00, // Byte rate, 88200,  
            // == SampleRate * NumChannels * BitsPerSample/8
		0x02, 0x00, // Block Align
		0x10, 0x00, // Bits per sample, 16, 2 byte(short)
		0x64, 0x61, 0x74, 0x61, // Subchunk2ID, "DATA"
		0x00, 0x00, 0x00, 0x00 }; // Subchunk2Size
			// NumSamples * NumChannels * BitsPerSample/8
			// This is the number of bytes in the data.

The 4-byte Chunksize and 4-byte Subchunk2Size is initialized to zeros, since it will be determined after generating music signals. The Printer::print funcion will decide the data.

void Printer::print(ofstream &f, short *music, size_t size) {
	fileheader.Subchunk2Size = 2 * size; // size must be 1 byte unit.
	fileheader.ChunkSize = 2 * size + 36;
	f.write(reinterpret_cast<char *>(&fileheader), 44);
	f.write(reinterpret_cast<char *>(music), 2 * size);
}

Application

musicSize must hold the data size, since Printer::print needs when setting Chunksize and Subchunk2Size to the header. It should hold the size of,

second * noteSize * sampleRate.

so that the function can allocate the array.

The createWave function must call createWaveForOneNote function eight times, to represent “do-re-mi-fa-sol-ra-ti-do”.

// Create a wave array for Do, Re, Mi, Fa, Sol, La, Ti and Do
// 1. set musicSize
// 2. Allocate the music array
// 3. Fill the music array by calling the createWaveForOneNote function multiple times
void createWave(short* &music, size_t &musicSize) {
	// Fill Me

	// author: SukJoon Oh, 2018142216
	// 04/12/2020
	size_t& data = musicSize;
	data = (1/*sec*/ * noteSize * sampleRate);
	music = new short[data]; // alloc

	for(int i = 0; i < noteSize; i++)
		createWaveForOneNote(&music[i * sampleRate], i);
}

The createWaveForOneNote gets the array and index as the input. Thus the function should generate values by putting in the right order, which is given by,

each_note * sampleRate + index

where each_note * sampleRate term plays a role of base.

// Create a wave array for a note
void createWaveForOneNote(short *music, int index) {
	// Fill Me

	// author: SukJoon Oh, 2018142216
	// 04/12/2020
	const int vol = 3000;

	for (int i = 0; i < sampleRate ; i++) {
		music[i] =
			static_cast<short>(
				vol * sin((2 * M_PI * i * frequency[index]) / sampleRate)
				);
	}
}

Then, by simply calling the function createWave in main, the file is generated.

// Send an empty music array
createWave(music, musicSize);

To compile the program, command used is given below.

$ make

The makefile configuration is listed below.

CC=g++
SRCS = $(shell ls *.cpp)
OBJS = $(SRCS:.cpp=.o)
HDRS = $(shell ls *.h)
TARGET = createWave
$(TARGET) : $(OBJS)
	$(CC) $(OBJS) -std=c++1y -lc -o $@

%.o : %.cpp
	$(CC) -c $<

$(OBJS) : $(HDRS)

all :$(TARGET)

clean :
	rm -f $(OBJS) $(TARGET) sample.wav