Paint 'Dream' of Vladimir Matveitcev

 

'... and orchestra did not play, or even strike up, or even bang away at, but precisely, in the cat's loathsome expression, hacked out some incredible march of an unheard-of brashness.'

M.A. Bulgakov 'The Master and Margarita'

Ask a good tailor about a key element of men's clothing, and for sure, you will hear - button. So, sound is one of the main characteristics of a doorbell, about development of which I continue to talk. At the same time, reading on the Internet the descriptions of doorbells that reproduce up to 30 pleasant melodies, I got an association with the phrase, which is set as epigraph to this article. Blasphemy, but not a problem to "teach" the doorbell sing something glamorous with magic voice of Sarah Brightman, but at 3am this tune will likely cause us deep burrow into the blanket, hoping to see continued a magic sleep, which sees the person on the same named picture of Vladimir Matveytsev. But in the very this moment, the neighbors who live one floor below, obscene persistently tight button of your melodious doorbell, wanting to talk on the topic "What is the purpose of taps in the bathroom?". Surely you have your own stories to this case, but I think you'll agree that the good march on raucous battle trumpet, is indispensable in this case.

Conclusion: the sound of low quality is quite suitable in this application

A Brief History of music synthesizers

As you will see in the next article, where we will deal with "hardware" circuit and its programming, playing of sound is a simple task for Microcomputing Units (MCU). But, because this series of articles is intended to describe the entire process of production, then let's talk about the main thing, namely, the generation of melodies. Agree, they did not fell directly from the heaven into the electronic brains of our doorbell. And because we want to give a visitor of our online store an opportunity to choose the melodies, it is desirable to learn how to create their description, understandable to the MCU. Shamelessly cutting pieces from the concerts of Pink Floyd and stuffing them into a programmable sound chips is definitely not our way. I think it is difficult to find someone who has not heard the concert of that musicians "The Dark Side of the Moon" or "Animals", where electronic synthesizer clearly presented. Before we delve into our work, let's recall father of all music synthesizers, a man with a remarkable biography, artillery colonel Yevgeny Murzin, unjustly forgotten today. Back in 1941, being a student he made the first drawings of the photoelectronic optical sound synthesizer, the idea of which produce for obvious reasons was not supported in those hard years of war. The first device was made only in 1958, which he named ANS in honor of the composer A.N. Scriabin. For comparison and understanding of how simple our task, I note that Murzin's synthesizer had 72 sound in an octave. We will work with the standard seven notes in an octave.

The task and the tools

Because we are about to start programming, then it is time to choose a language and development tools. Even the most cursory glance at the subject will reveal a great variety of languages and tools for writing and translating texts of programs to arrays of bits, understandable for the selected MCU. Among these are "grandparents" sort of Fortran and Ada, already inveterate Java, Python, C and C++, as well as young Rust. Of course a matter of taste, but the MCU ports can be controlled even in PHP, for example, by PiPHP for Raspberry PI.

Perhaps many will disagree, but I dare say that the profession of a programmer in its present form will change dramatically in the next 10-15 years. For example, until recently, the creation of Graphical User Interface (GUI) has been a quite hard challenge for programmer, but now some of the tools allow you to easily describe it with simple text structures in XML, QML, or graphical tools, provided by numerous IDE. But that's not the point. The standard human generational turnover calculated in 25-th years, hardly can change significantly, because directly dependent on the human physiology. On the other hand, the cycle of technology change already shorter of human generational turnover, and probably will reach 5 years soon. In this regard, to remain being in trend, the programmer will face the need of continuous learning new languages ​​and tools. At the same time, he is obliged to do the current job at the high quality level. Ask yourself about the duration of the most productive period in the life of mid-statistical programmer? Is constantly increasing stress can be a reason it will get shorter? In my opinion we are already close to saturation and programming will move to a higher system level, the duration of which activity will be even shorter. In the article 'The Churn' you also will find an interesting dialogue about 'need' to have such a great variety of programming languages.

Therefore, I propose a simple rule - select a relatively long-term direction of work, which requires knowledge of three to five programming languages, and devote free time to more important business, such as studying the "Book of Changes". In most cases, the end user does not care you chose Erlang or BASIC to solve the problem. Important just that the product's real value of usability and operate reliably for years without repair and developer intervention. Although even this logical demand already changed because of other laws, not directly related to the work of engineers. In any case, previous generation of home refrigerators and washing machines designed for longer-term work.

So, we have to create a program to play simple monophonic melody on the Personal Computer (PC) speaker. Text representation of music pieces must be easily read by a person not familiar with musical notation, and does not require specialized editors for writing music. The program must have a visual means for modification of the melody pattern and saving it to the original file of primary descriptor. The main purpose of the program is transformation of textual representation of the final edited melody to standard configuration file (in case of C++ it is a header file with extension ".hh"), to be included at the compilation phase of the MCU embedded software, taking into account the frequency of its clock. Thus, each key parameters such as an octave, the pitch, note and pause duration should be placed together in one byte (8 bits).

Therefore, the program is of the type of tools for human creation, is not a link in a chain of the custmer's order automatic processing for the manufacturing of products, and by the reason of relatively large number of parameters, it must have GUI. Because we'll build our doorbell on a very cheap MCU, plyaback to PC speaker chosen because the PC hardware and methods of sound generation there are very similar.

C++ chosen as programming language and Code :: Blocks as IDE. This free cross-platform tool allows you to create a wide range of applications, including embedded (for different types of MCU):

CodeBlocks GUI
Thus, in many future cases, mastering the skills of work with this software will allow you to avoid purchasing of other same purpose instruments, and do not waste time on them studying. As before, I'm using Linux OS, but no problem to run Code::Blocks, for example, in Windows.

Create primary music file

At the beginning, an inquisitive engineer will learn some standards regarding music synthesis, for example, MIDI and SoundFont. Then will openin Audacity something like "Fly me to the Moon" by Oscar Peterson, and explore the melody for wrong, by his point of view, passages and formants. And only then, he will remove all "unnecessary" and "sew" something invigorating from it, using only single-tone patterns.

Melody Edit Audacity
By the reason of the very small MCU's resources, as has been repeatedly mentioned, we'll simplify the task and will not look for ready-made solutions or think about developing of OCR-scanner to process the melody primary descriptor, presented in a standard form:

Tea for two notes
For the same reason, we'll not load the MCU with processing of complex MIDI-structures. So, it makes no sense to complicate said program with processing primary text of musical composition. Moreover, the real melody, designed to play by the simplest musical synthesizer, usually broken down into parts for different instruments. We need a simple melody recording format, sufficient to solve the problem accordingly the conditions imposed. Therefore, if you as I do not understand musical notation, then arm with own 100% hearing and play desired melody by one finger, eg, in the application Virtual MIDI Piano Keyboard:

Virtual MIDI Piano Keyboard
As a result, you'll get something like that mentioned "Tea for two", as shown in the next couple of lines, where each note represented in the American Standard Notation System (STN), which is also called scientific notation:

B3,G3#,A3#,G3#,B3,G3#,A3#,A3#
A3#,F3#,G3#,F3#,A3#,A3#,G3#

As you can see, there is an indication of the octave (3) and the pitch of the notes (B, G, ...), but no data about the duration of notes and pauses. The duration of notes usually are not varies in a piece of music, especially in the case of ringtones, acceptable for our application. But pause... It is a great invention in human relations! The following picture shows a fragment of a movie, where the person's statuses against each other changes in a few minutes from a dependent to boss, and vice versa. Excellent actors so ably shows these metamorphoses, that it is not necessary to delve into the meaning of the words, and even look at the screen - just listen to pauses to determine the current status of the person.

Boss and Staff
And since music is designed to reflect the life, we'll expand the range of pause descriptor to the four states. However, in some cases, this rule could be simplified - a year-old child always have the same benevolent pause.

So, let's create musical notation grammar for our simple application:

  • each note always represented by at least two characters in scientific notation, with the possible addition of a third symbol of alternation '#'
  • it supports 2 types of duration of the note - regular and long. Long-term note marked by the font type 'bold'
  • It supports 4 types of pause duration, marked by different font colors: absent, short, regular, long
  • note descriptors can be separated from each other by symbol ','

Thus, our specialized musical grammar allows to represent simple melodies in a more or less convenient form for human reading. Now its time to decide which instrument to use for primary descriptors writing. You can take advantage of ample opportunities of wxWidgets and wxSmith, allowing to expand our program with the field of text editor, which support font management, respectively of our grammar requirements. On the other hand, because the it is not commercial and rarely to be used application, then probably you'll agree that it is easier to give the user the ability to use any text editor familiar to him.

Therefore, it's time to choose the format of primary melody file descriptor, which our program should transform to a header file, "friendly" to the compiler for the MCU selected. Copy the above example of melody to the editor 'Microsoft Word' or 'LibreOffice Writer' and save it in different formats. Try, for example, ODT, RTF, XML, HTML, and consider their contents in the simple text editor. Probably you'll be surprised a lot by the volume and variety of service information and maybe will deem HTML as the most appropriate for parser, which you will create in the developed program. You can also try using some Markdown Editor to create a primary descriptor melodies. In general, choose the appropriate format and, of course, read the description of its standard.

Just wondering, what kind of simplified grammar would you suggest to support the 72 sounds in an octave for Murzin's synthesizer?

The software description

In this section we will take a look at the source melody file and structure of our software part, which will play it. Following the rules of our grammar, the melody presented earlier can be written as follows:

B3,G3#,A3#,G3#,B3,G3#,A3#,A3#
A3#,F3#,G3#,F3#,A3#,A3#,G3#

The following figure shows a GUI of our program while debugging in the Code :: Blocks:

Melody Header File Gen Software
As you can see it supports the setting of sound and pause duration, announced in our grammar. Additionally, you can specify the MCU's type and its clock frequency. Because melody sound on the speaker is different from that which we have heard while strumming it in Virtual MIDI Piano Keyboard, the possibility of shifting the original melody to the desired number of octaves can be useful. At the same time, the program should take into account the specified MCU's clock frequency and report, if necessary, about impossibility of playing the selected melody with given specific parameters. Finally, you can specify the number of notes to play on a battery-powered doorbell (radio button). The melody should still be recognizable, so you can not specify some rough number of 5 for all occasions.

Over time, each programmer usually following own style of code writing, but it is desirable to get acquainted with the rules of famous people or companies like, for example, NASA. Because the main task of our application is to create a header file of melody descriptor to be included while automatic processing of the customer order (recall the description of our SKU) and compilation of the program for the appropriate MCU, then I'm adding one more to these rules:

  • try to make the interrupt handlers in the MCU's program as short as possible. In most cases, it is sufficient only to set a specific flag, which will be processed in the main loop of the program, or in the procedure of MCU awake from a sleep state. If it is desirable to still take some actions in an interrupt handler, they should be as simple as possible.

In this regard, let us once again turn our attention to the previously given formulation of the final tasks:

Thus, each key parameters such as an octave, the pitch, note and pause duration should be placed together in one byte (8 bits).

It is time now to define the function by which we'll control the PC's speaker.

PC speaker control - version №1

In the example below, the sound is generated by a frequency of 2 kHz and a duration of 1 second.

int freq = 2000; // Note frequency in Hz
int dur = 1000; // Note duration in ms

int fd = open("/dev/console", O_WRONLY);
ioctl(fd, KIOCSOUND, (int)(1193180/freq));
usleep(1000 * dur);
ioctl(fd, KIOCSOUND, 0);
close(fd);

But we'll hear it only if run the program by sudo. Therefore, this option is not suitable for our purpose.

PC speaker control - version №2

We'll use the function "beep", which has analog in Windows. It fully meet our grammar, and allow us to set the frequency of a note (pitch), its duration, and the duration of the pause. In Linux, it is quite possible that 'beep' support disabled by default. Installation and getting work can be done in the following sequence:

  • Install the 'beep':
    sudo apt-get install beep
  • In the file '/etc/modprobe.d/blacklist.conf' uncomment next line:
    blacklist pcspkr
    and restart the computer.
  • Check the 'beep' command in terminal, for example:
    beep -f 1000 -r 2 -d 1000 -l 400
  • If there is no sound, then enter the command in the terminal:
    alsamixer
    A dialog opens, as shown at the following picture, where you have to enable the 'Beep' channel:

ALSAmixer
NOTE:
if your computer does not have a speaker, then you can use the command 'speaker-test' to play on external speakers. For example:

speaker-test -t sine -b 1000 -p 1000 -f 1000

However, since sound of external speakers is radically different from the sound on PC's speaker, I do not use this method here.

Functions of the GUI elements

The figure above shows the state of GUI after user has selected "File" in the menu and opened the file of desired melody primary descriptor. If the structure of its content complies with our grammar, it will be allowed to activate the button "Generate". Click on it will create an array, containing a frequency corresponding to each letter in the melody descriptor, duration of notes and pauses, and some additional checks will be made. Data about the frequency according to the note symbol is easy to find on the Internet and, for example, for 3-rd octave it can be presented in the following table:

C3 = 130.813
C3# = 138.591
D3 = 146.832
D3# = 155.563
E3 = 164.814
F3 = 174.614
F3# = 184.997
G3 = 195.998
G3# = 207.652
A3 = 220.000
A3# = 233.082
B3 = 246.942

If there is no problem, then program will disable the button "Generate", but enable buttons "Play" and "Save". When you click on "Save", the original primary melody descriptor will be updated, respectively to the changes of parameters that user has made through the GUI, and the header file with the extension ".hh" will be created or updated, corresponding to the selected MCU and its clock frequency. The button "Generate" will be automatically enabled in any case of change in operational parameters, presented in the GUI. At the same time, playing of music stops and button "Save" disables.

Implementation of the functions described herein is not particularly interesting, therefore, let's focus on the button "Play". Although in the GUI, built on the basis of wxWidgets can be used function wxExecute, which enables to perform synchronous and asynchronous calls, but we will consider other options for playing melody.

Synchronous playback of music

Schematic handler of button "Play" click:

for (i = 0; i < melody_notes_cntr; i++) {
   // Create command to play each note
   cur_note_cmd = 'beep ';
   ......
   cur_note_cmd += '-D ' + cur_duration_value;

   // Example of command: 'beep -f 1000 -l 400 -D 1000'
   system(cur_note_cmd); // Execute command
}

This is working version, but we'll have to wait for the whole melody playback and GUI will freeze with button "Play" down. But we need to be able to stop playing when changing any of the settings, and then try to play a modified version.

Asynchronous playing of melody - adding Processes

Create the following variables:

pid_t child_pid, first_child_pid;
volatile sig_atomic_t player_ready = 1;

Schematic handler of button "Play" click:

first_child_pid = fork(); // Create child process to scan notes

if (first_child_pid == 0) {
   player_ready = 1;

   for (i = 0; i < melody_notes_cntr; i++) {
      // Fill in array 'arg_list' of current note parameters
      ......

      // Example of array content:
      // arg_list[1] = '-f 1000';
      // arg_list[2] = '-l 400;
      // arg_list[3] = '-D 1000';

      // Play note
      if (player_ready == 1) {
         player_ready = 0;

         // Create child process for command 'beep'
         if ((child_pid = fork()) == 0) {
            execvp ("beep", arg_list);
            return;
          }

          // Wait to the end of current note play
          while (player_ready == 0) {}
      }
   }
}

In order to catch the moment of each note sound completion and notify the parent loop of melody scan, we have to create handler of signal from the child process:

void player_signal_handler(int sig_num) {
   switch(sig_num) {
   case SIGCHLD:
      player_ready = 1;
      break;
   default:
      break;
   }
}

To stop playing music, we must close the relevant processes. So add, for example, the following function:

void stop_player(bool play_only = false) {
   if (child_pid > 0) {
      kill(child_pid, SIGTERM);
      child_pid = -1;
   }

   if (first_child_pid > 0) {
      kill(first_child_pid, SIGTERM);
      first_child_pid = -1;
   }
}

Finally, we must add the call of 'stop_player' function in a handler of GUI parameters. For example, for MCU type list, the code can be:

void MelodyHeaderFileGenFrame::OnchoMCUSelect(wxCommandEvent& event){
   stop_player();
}

Although now we can interrupt playback at any time, but the code is large and we unnecessarily waste PC resources by creating additional processes.

Asynchronous playing of melody - Threads

The threads may be created within the current process. In addition it is easier to manage and synchronize them.
Create the following variable:

pthread_t thread_player_id;

We also need a function inside the thread to scan melody, which is almost the same as function, that we have described considering the synchronous playback of music:

void* thread_player(void *args) {
   for (i = 0; i < melody_notes_cntr; i++) {
      // Create command to play each note
      cur_note_cmd = 'beep ';
      ......
      cur_note_cmd += '-D ' + cur_duration_value;

      // Example of command: 'beep -f 1000 -l 400 -D 1000'
      system(cur_note_cmd); // Execute the command
   }
}

Edit the function 'stop_player' as following:

void stop_player(bool play_only = false) {
   pthread_cancel(thread_player_id);
}

Modify the schematic handler of button "Play" click in the next way:

pthread_create(&thread_player_id, NULL, thread_player, NULL);

And of course, add the call of function 'stop_player' into the handlers of GUI parameters. For example:

void MelodyHeaderFileGenFrame::OnspnNotesOnBatteryChange(wxSpinEvent& event) {
   melody_len_on_battery = spnNotesOnBattery->GetValue();
   stop_player(true);
}

Header file for MCU

Finally, a few words about the file with extension '.hh', for which this whole story was started and our program will automatically create and fill, respectively to the primary melody descriptor and parameters, set by GUI. Here is the content:

#define MELODY_BASE_PRESCALER 4
#define MELODY_LEN_ON_BATTERY 8
const unsigned char melody[15] PROGMEM = {123,8,74,8,27,72,10,106,106,6,72,6,10,10,104};

I do not like unnecessary frills, but if needed, we can allocate 150-200 bytes per dozen simple melodies even in the MCU with only 1000 bytes of program memory.

NOTE: please note that all definitions in the file do not provide features, uniquely identifying a melody. There is also no definition of duration of notes and pauses, which are standardized for this application, and stored in a separate header file. This is done in order to save MCU resources and imposes restrictions on the choice of the type of melodies that should have cheerful rhythm. Otherwise, these parameters should be determined in each such header file. Thus, if the doorbell must support more than one melody, the scanner of customer order will automatically process all the required music files and create a single, to be included to the rest of the project files for appropriate MCU.

Tools and documentation

Programming languages: С++ (https://isocpp.org/get-started)
Software: Code::Blocks (http://www.codeblocks.org/)
Virtual MIDI Piano Keyboard (https://sourceforge.net/projects/vmpk/)
Additional reading:

MIDI (https://www.midi.org/specifications)
SoundFont (http://freepats.zenvoid.org/sf2/sfspec24.pdf)

JavaScript (https://www.javascript.com/)
MIDI.js (https://mudcu.be/midi-js/)
Python (https://www.python.org/)
XML (https://www.w3.org/XML/)
JSON (http://www.json.org/)

Qt (https://www.qt.io/)
PyCharm (https://www.jetbrains.com/pycharm/)
Kivy (https://kivy.org/#home)
Git (https://git-scm.com/)
GitKraken (https://www.gitkraken.com/)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

To be continued ...