The second simple music converter / generator
2022-08-24
In the past I posted an article about my little project of generating music and converting from music sheets to audio files. But that version is quite unsatisfying and has several problems that I would like to solve. Then comes this second project, where I try to solve the previous problems.
Below I shall explain some of the differences from the previous version.
Rust
The most noticeable change is probably that this is now written in another programming language called Rust. The main reason I made the decision to change the language was the safety.
Safety
The primary reason is safety.
When I was writing a C program, there were countless memory related bugs, like segmentation faults, double free erros, or un-freed memories. And it is hard to exactly locate the place where the error originated. Furthermore, there are numerous occasions where I messed up with my program logic as well. There are even more difficult to fix than the memory-related ones, since it appeared as normal programs at first sight. And Rust helps with solving these problems.
Memory-related problems
How does Rust help solving memory-related problems? To explain this we first look at what memory-related problems are.
Roughly speaking there are three types of memory problems:
- Try to access memory location that is not allocated.
- Try to free memory location that is not allocated.
- Forgot to free the allocated memory.
- Accessing unallocated memory
There are various sub-types of this problem, including accessing an array at an index that is outside the range of the array, e.g. accessing
arr[10]
whenarr
points to a memory location where you only allocated 9 elements. Another example is to try to access the pointer after one freed the pointer.When writing a C program, the programmers have to behave themselves to ensure they don't make these types of mistakes. In Rust, the compiler will try to make sure that no such error can occur. To be more specific, the compiler will "insert" the
free
statements automatically for us, so that we don't have to do that ourselves. As a consequence, the compiler has to know exactly at which point it should insert thefree
statements. In other words, the compiler has to understand the programs to a certain degree, to make the decision to put afree
statement somewhere automatically. And the compiler achieves this goal by enforcing a model on the programs.To summarize, we sacrifice some flexibility in the program design to endow the compiler the power to do some things for us.
For technicalities on the model that the compiler enforces, the borrow checker, I would recommend the official documentation, see the relevant chapter in the documentation.
- Freeing unallocated memory
Rust compiler tries to solve these two types of memory problems again by the enforced model. This type is directly solved by the compiler inserting
free
statements automatically: the compiler will itself check that it only frees the allocated memory, so we don't have to worry about it. - Forgot to free allocated memory
Again this is solved by the compiler: it should not forget to free the allocated memory. And in this regard the compiler is much more reliable than me, in my experiences. :-P
Program Logic problems
It seems too magical at first sight that Rust compiler can help fix the program logic.
Rust compiler achieves this, similarly to the reason above, by a deeper understanding of the programs. But this time it does not enforce some particular model on the programmers. Instead it just proposes some warnings to inform the programmer that there are some strange things going on in the program logic. For instance, if a variable is assigned a value but that value is never used, the compiler will generate a warning to urge the programmer to look at that piece of code again, either to ignore the warning, or to find the proper place to use that variable, somehow.
A better parser
The previous version of this project had a poor music sheet format: one has to input -9 to represent the C4 tone: I chose to denote the "A4" tone as zero, as it is the standard pitch.
For those who know nothing about music, including myself before I started this project, "A4" and "C4" are denoted using the scientific pitch notation.
For example, the first few notes of Für Elise were denoted as follows:
6 0.25 7 0.25 6 0.25 7 0.25
Here 6 denotes the tone "F5", and 0.25 means a duration of 1/4 beats.
Of course this notation is really inconvenient to use. I am quite amazed that I could tolerate this notation and transcribed several sheets with it.
Previously I was thinking to use another of my projects: REP, which is a parser generator, to write a parser for my music converter project.
However, I am stuck in the porject currently: I have to fix a fundamental error in my design of the implementation of the algorithm, in order for it to run at an acceptable speed, and I feel tired in working on that project for the time being. Thus I decide to take a break and figure out the parser for this music project first.
Now we can write such sheets as follows; what an improvement! :D
o4 e4 a a a a2 a4 b > c c < a2 a > c o4 b4 b g g g2 b > c < a a1
This is a segment of the song "Ievan Polkka".
The notation is similar to that of alda, by the way.
Functionality differences?
Is there any difference in terms of the generated music? For example, can it generate the timber of violin now? Unfortunately, it still does not offer such functionalities yet.
The reason is simple: I am still new to the area of audio processing, and I am exploring it by myself only, without the shoulder of the giants to step upon, sort of.
So my plan is of course to dig into this area, and see what I can do. For example, I am trying to implement some short-time Fourier transform algorithms to analyse some real violin sounds, in order to see if I can simulate its timbre.
Surely I know there are well-written libraries out there for doing such transforms, like fourier and rustfft. But my major is Mathematics, and I always get excited to turn my Math knowledge into something that I can really "feel". So how can I miss the chance to play with this interesting subject? Besides, I am thinking to apply some of my research ideas to the topic of audio processing as well. :D
Some more songs
Though the essential feature of this second version of the project is not much different from its ancestor, I manage to transcribe more songs with it, thanks to the improved parser. So let's hear some of the new songs then. :)
Ievan Polkka: Click here for audio.
The song comes from this sheet.
Wellerman: Click here for audio.
The song comes from this sheet.
You who love 105°C: Click here for audio.
This comes from the sheet in this video.
Some more words
So I am still working on the improvements of this project. Anyone interested can have a look at its repository.
The above sheets can be found in the directory songs of the repository.