C++代写:ECE3574MusicSynthesizer


代写一个音乐合成器,计算音律,生成wav文件。

Introduction

Music synthesizers are programs that take music in notational form and produce
the digital signals required to play the sound. Thus, they are similar to a
compiler for music. For this milestone the synthesizer will use a very simple
notation for the music, a sequence of pure tones with a specified amplitude
and duration. Consider a middle C note which has a frequency of 261.63 Hz,
lasting 3 seconds and amplitude F. This can be written as a sinusoidal
function:
f(t) = F sin( 2pif*t )
with f = 261.63 and t E (0, 3). To represent this continuous function
digitally we need to sample the sinusoid in time, using a sampling rate t, and
quantize the amplitude into a fixed range by choosing a quantization level, Q
in bits. This is referred to as pulse code modulated data. In this milestone
we will use a fixed sampling rate of t = 1/44100 and Q = 16 bits, that is each
sample is of type int16_t ( http://en.cppreference.com/w/cpp/types/integer ). The fixed-width integer
types (int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, etc.) are
defined in the header cstdint (
http://en.cppreference.com/w/cpp/types/integer
). You should always use
them instead of the types int , long , etc. when you need to depend on the
memory size of the type.
You will write a program to read a song represented as a sequence of tones
with a specified amplitude and duration and produce a sampled signal in WAVE
format suitable for playing on a computer. The program should perform strict
error checking of input, printing a warning message and skipping any malformed
(non-blank) line of input. The number of lines and hence the length of the
song is arbitrary.
The program should read the input file and create a sampled signal
representing the sound, scaling the peak amplitude to the allowable range of a
16 bit (short) type if required, and save it as a binary file in a simplified
WAVE format as specified below.

Input File Specification

The input file is a text file in comma separated value format with three
columns. Each line contains a single tone with the number of lines being
arbitrary and indicating the number of tones in the song. On each line, the
first column contains one of seven notes written as one of: C (261.63 Hz), D
(293.66 Hz), E (329.63 Hz) , F (349.63 Hz), G (392.0 Hz), A (440.0 Hz), B
(493.88 Hz). The second column is a positive floating point number
representing the amplitude of the note in arbitrary units, and the third
column, a strictly positive floating point number representing the duration in
seconds.
For example the following file specifies 3 notes,
C, 10.0, 2.5
G, 20.8, 5.0
C, 10.0, 3.0
Each note should be played-back-to back, with no gaps in between, using the
sin function as specified above, where t=0 when the note starts.

Output File Specification

This milestone will use a simplified version of the WAVE format. A WAVE file
is a binary file that consists of a header section followed by the sampled
signal data. The header consists of 14 fields written consecutively with a bit
widths and values as follows:
int8_t ChunkID[4]; // “RIFF”
int32_t ChunkSize; // 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
int8_t Format[4]; // “WAVE”
int8_t Subchunk1ID[4]; // “fmt “
int32_t Subchunk1Size; // 16
int16_t AudioFormat; // 1
int16_t NumChannels; // Mono = 1
int32_t SampleRate; // samples per second, 44100
int32_t ByteRate; // SampleRate * NumChannels * BitsPerSample/8
int16_t BlockAlign; // NumChannels * BitsPerSample/8
int16_t BitsPerSample; // 8 bits = 8, 16 bits = 16
int8_t Subchunk2ID[4]; // “data”
int32_t Subchunk2Size; // NumSamples * NumChannels * BitsPerSample/8
int16_t Data[NUMSAMPLES]; // the Pulse Code Modulated data
—|—
Each field should be written to the binary output file in little-endian order.
The output wav file can be played with almost any music player, for example
VLC ( http://www.videolan.org/vlc/index.html ).

Hints

Recall that to write a file byte-by-byte in C++ it should be opened in binary
mode. Reading and writing from/to a binary file is different than when using
text files. First, you have to open the file in binary mode, e.g.
std::ofstream outstream(“output_binary.file”, std::ios::binary);
—|—
To write a binary value you use the write method, which takes a pointer to a
memory location as a std::fstream::char_type * (a pointer to the type fstreams
use to hold characters) and the number of bytes to read. The pointer must be
cast using a reinterpret cast. For example to write an unsigned 32 bit integer
one would do
uint32_t value = 0;
outstream.write(reinterpret_caststd::fstream::char_type*(&value), sizeof value);
—|—
When reading and writing binary files it is often usefull to have a Hex Editor
https://en.wikipedia.org/wiki/Hex_editor to aid with debugging. There are
many availble for free
https://en.wikipedia.org/wiki/Comparison_of_hex_editors
for various
platforms. The reference environment specified in the Vagrantfile provided
includes a command-line tool xxd to dump a hex representation of a file.

Setup

Accept the GitHub invitation above and then clone the git repository to your
local machine. Implement your program in a source file named synth.cpp . You
should use git to commit versions of your program source (only) as you go
along, demonstrating good incremental programming technique.

Correctness

The file names to read and write are specified as command line argument
(whatever the user types on the command line after the executable). If you
need a primer on how to use command line arguments, see below.
The following program invocation reads the file twinkle.csv and converts it to
the file twinkle.wav (Assuming Windows platform, $ as the command line prompt)
$ .\synth.exe twinkle.csv twinkle.wav
If no file names are specified, or the specified files cannot be opened, the
program should print an appropriate error message to standard error and return
EXIT_FAILURE . If the input file is invalid, then an error message should be
printed to standard error and the progeram should return EXIT_FAILURE .
Otherwise it should convert the file and return EXIT_SUCCESS.

Testing

The initial git repository has a sub-directory called test which has several
examples of input files (ending in .csv) and corresponding expected output
(ending in .wav). The included CMakeLists.txt file sets up these tests for
you. Just configure the build, run the build, and then run the tests.
Correctness means that the program reads the filename from the command line
and write the output per the specification. Code quality will be assessed in
this assignment by ensuring your code compiles cleanly, with no warnings,
using the g++ flag -Wall -Wextra in the reference environment, as
specified above. That is in the VM typing g++ -std=c++11 -Wall -Wextra /vagrant/synth.cpp should produce no errors or warnings. You should also
have made more than 2 commits to your repository before submission

A Primer on Command Line Arguments

Recall the primary ways to get user input into a program, standard input (
std::cin ) and reading from files. Another very convenient one is to use
command line arguments. When your program is run, usually from another program
(the operating system or a shell program), it starts executing in the function
main . This is called the entry point.
int main() {
//code here
return 0;
}
—|—
There is another form of this entry point with two arguments. The first
(traditionally named argc ) is the number of string arguments to the program,
the second (traditionally named argv ) is an array of “argc” C-style strings
(pointers to null-terminated memory) with the arguments themselves.
int main(int argc, charargv[]) {
//code here return 0;
}
—|—
You can specify the command line arguments when you run the program from a
text shell (e.g. powershell or bash), from a graphical shell (like when you
click on an icon), or from a script.
It is easy to convert the more C-style arguments to a more modern C++ style as
follows
#include
#include
int main(int argc, char
argv[])
{
std::vectorstd::string arguments;
for(int i = 0; i < argc; ++i) arguments.push_back(argv[i]);
// code can use arguments as a C++ vector of C++ strings
return 0;
}
—|—
A related question to the previous is why does main return an int anyway?
Well, when your program is run to completion it can indicate to the running
process if it succeeded or if an error occurred by returning this int. Success
is defined as zero, which is why most simple examples returns that. The
following are defined in the header “cstdlib”: “EXIT_SUCCESS” and
“EXIT_FAILURE”, and can be used when returning from main.


文章作者: SafePoker
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SafePoker !
  目录