# HaaS EDU scene application learning - retro music box

Overall introduction of HaaS EDU scenario application

# 1. Experimental introduction

Chiptune is a childhood memory of many post-80s and 90s. It is said that the name of Chiptune should be unfamiliar to many people, but it has another name: 8-bit. The so-called Chiptune is a song written by the sound emitted by the chips of old home computers, video game consoles and arcades (also known as CHIP). Strictly speaking, Chiptune is not only 8bit, but also a low bit rate that pursues the sense of retro particles.

In this experiment, we also come to realize a retro "eight tone" box.

# 2. Knowledge points involved

• Music score coding
• PWM and buzzer

# 3. Software and hardware environment preparation

## 3.1 hardware

One computer for development

One HAAS EDU K1 development board (built-in buzzer)

One USB2TypeC data cable

## 3.2 software

"Retro music box" function has been included in edu_ The demo is applied and included in the release version.

### 3.2.1 firmware version

Firmware version: v1.0 zero

### 3.2.2 code path

git clone https://gitee.com/alios-things/AliOS-Things.git -b dev_3.1.0_haas

cd AliOS-Things/application/example/edu_demo/k1_apps/musicbox

### 3.2.3 compilation

Enter the top-level directory of the code, such as Alios things, for compilation. Directly compile the edu in the application/example / directory_ Demo application.

Two methods to compile

Command line mode

aos make distclean

aos make edu_demo@haaseduk1 -c config

aos make

AliOS Studio IDE mode

### 3.2.4 burning

See the development environment section

# 4. Buzzer

The buzzer is a very simple sounding device. Unlike the loudspeaker used for playing, the buzzer can only play a relatively simple frequency.

From the driving principle, buzzer can be divided into passive buzzer and active buzzer. The "source" here refers to whether there is a driving source. Passive buzzer, as the name suggests, does not have its own built-in drive source. Only when the voice coil is connected with alternating current, the electromagnet inside the voice coil and the permanent magnet attract or repel each other to push the diaphragm to make sound. After the direct current is connected, the diaphragm can only be pushed continuously without sound, and the sound can only be generated when it is connected or disconnected. On the contrary, as long as the DC power is connected to the active driver, the internal driving source will drive the diaphragm at a fixed frequency and make sound directly.

In this experiment, it is recommended to use a passive buzzer, because it is only driven by PWM, and the sound will be more crisp and pure. A similar effect can be achieved when using the active buzzer, but the sound will be slightly noisy due to the superposition of its own vibration frequency.

## 4.1. Driving circuit

The first end of the buzzer is connected to the VCC and the second end is connected to the triode. The triode here is driven by PWM0 to determine whether the two ends of the buzzer are connected with GND, and then cause an oscillation. By continuously turning the IO port, the buzzer can be driven to sound.

## 4.2 driver code

In order to flip the IO port at a specific frequency, we can use the PWM (pulse width modulation) function. Refer to Chapter 3 for details on PWM resources.

In this experiment, we implemented two methods: tone and noTone. Among them, the tone method is used to drive the buzzer to emit a sound of a specific frequency, that is, "tone". The noTone method is used to turn off the buzzer.

It is worth noting that in the tone method, the duty cycle of pwm is fixed at 0.5, which means that in a vibration cycle, the diaphragm of the buzzer is always on the top half of the time and on the bottom half of the time. Changing the duty cycle here will not change the power of the buzzer, so the volume will not change.

// application/example/edu_demo/k1_apps/musicbox/musicbox.c

void tone(uint16_t port, uint16_t frequency, uint16_t duration)

{

pwm_dev_t pwm = {port, {0.5, frequency}, NULL}; // Set pwm frequency as set frequency

if (frequency > 0)                              // pwm can only be initialized if the frequency value is legal

{

hal_pwm_init(&pwm);

hal_pwm_start(&pwm);

}

if (duration != 0)

{

aos_msleep(duration);

}

if (frequency > 0 && duration > 0)      // If duration is set, playback stops after this delay

{

hal_pwm_stop(&pwm);

hal_pwm_finalize(&pwm);

}

}

void noTone(uint16_t port)

{

pwm_dev_t pwm = {port, {0.5, 1}, NULL}; // Turn off the pwm output of the corresponding port

hal_pwm_stop(&pwm);

hal_pwm_finalize(&pwm);

}



# 5. From tone to music

After completing the drive of the buzzer, we can make the buzzer sound at the frequency we want. Next, what we need to do is combine these frequencies to form music.

## 5.1. Define tone

At present, we can only specify the frequency of sound, but we don't know how the frequency corresponds to the tone. Only by following the tone can we splice the music. If we regard the buzzer as the device we want to drive, the corresponding relationship between frequency and tone is the communication protocol, and music is the ideal device output.

We use the commonly used musical formula at present—— twelvetone equal temperament . Using the definition of Wikipedia, it can be calculated as follows:

Set the tonic tone to a1(440Hz) to calculate the frequency of all tones. The results are as follows (in order to make the calculation process clearer, the score is not reduced):

 Interval name Interval halftone number Multiple of twelve mean law frequency Purity degree (A1) 0 {\displaystyle 2^{0}=1\,} {\displaystyle 440\times 1=440\,} One degree increase / two degrees decrease (A ♯ 1/B ♭ 1) 1 {\displaystyle {\sqrt[{12}]{2}}=2^{\frac {1}{12}}\approx 1.0594630943592952645618252949463} {\displaystyle 440\times 2^{\frac {1}{12}}\approx 466.1637615180899164072031297762} Large second degree (B1) 2 {\displaystyle {\sqrt[{6}]{2}}=2^{\frac {2}{12}}\approx 1.1224620483093729814335330496792} {\displaystyle 440\times 2^{\frac {2}{12}}\approx 493.8833012561241118307545418586} Minor third degree (C) 3 {\displaystyle {\sqrt[{4}]{2}}=2^{\frac {3}{12}}\approx 1.1892071150027210667174999705605} {\displaystyle 440\times 2^{\frac {3}{12}}\approx 523.2511306011972693556999870466} Third degree (C ♯) 4 {\displaystyle {\sqrt[{3}]{2}}=2^{\frac {4}{12}}\approx 1.2599210498948731647672106072782} {\displaystyle 440\times 2^{\frac {4}{12}}\approx 554.3652619537441924975726672023} Pure fourth degree (D) 5 {\displaystyle {\sqrt[{12}]{32}}=2^{\frac {5}{12}}\approx 1.3348398541700343648308318811845} {\displaystyle 440\times 2^{\frac {5}{12}}\approx 587.3295358348151205255660277209} Increase by four degrees / decrease by five degrees (D#/E ♭) 6 {\displaystyle {\sqrt {2}}=2^{\frac {6}{12}}\approx 1.4142135623730950488016887242097} {\displaystyle 440\times 2^{\frac {6}{12}}\approx 622.2539674441618214727430386522} Pure five degrees (E) 7 {\displaystyle {\sqrt[{12}]{128}}=2^{\frac {7}{12}}\approx 1.4983070768766814987992807320298} {\displaystyle 440\times 2^{\frac {7}{12}}\approx 659.2551138257398594716835220930} Minor six degrees (F) 8 {\displaystyle {\sqrt[{3}]{4}}=2^{\frac {8}{12}}\approx 1.5874010519681994747517056392723} {\displaystyle 440\times 2^{\frac {8}{12}}\approx 698.4564628660077688907504812795} Big six degrees (F#) 9 {\displaystyle {\sqrt[{4}]{8}}=2^{\frac {9}{12}}\approx 1.6817928305074290860622509524664} {\displaystyle 440\times 2^{\frac {9}{12}}\approx 739.9888454232687978673904190852} Minor seven degrees (G) 10 {\displaystyle {\sqrt[{6}]{32}}=2^{\frac {10}{12}}\approx 1.781797436280678609480452411181} {\displaystyle 440\times 2^{\frac {10}{12}}\approx 783.9908719634985881713990609195} Big seven degrees (G#) 11 {\displaystyle {\sqrt[{12}]{2048}}=2^{\frac {11}{12}}\approx 1.8877486253633869932838263133351} {\displaystyle 440\times 2^{\frac {11}{12}}\approx 830.6093951598902770448835778670} Pure octave (A) 12 {\displaystyle 2^{1}=2\,} {\displaystyle 440\times 2=880\,}

In this way, we get the relationship between frequency and tone, and we record it in the header file.

// application/example/edu_demo/k1_apps/musicbox/pitches.h

#define NOTE_B0  31

#define NOTE_C1  33

#define NOTE_CS1 35

#define NOTE_D1  37

#define NOTE_DS1 39

... ...

#define NOTE_B7  3951

#define NOTE_C8  4186

#define NOTE_CS8 4435

#define NOTE_D8  4699

#define NOTE_DS8 4978

In this way, we can adopt tone Method to emit the corresponding tone.

tone(0, NOTE_B7, 100)

// Use pwm0 corresponding buzzer to play NOTE_B7 lasts for 100ms



## 5.2. Generate music score

Next, we can start composing music. Here we choose a very simple children's Song - "two tigers" to show you how to compose music.

Our tone method has two parameters that need attention: frequency determines the playing tone, and duration determines the playing time of the tone, that is, the beat. Therefore, we also need to pay attention to these two parameters when reading the simplified spectrum.

Interested students can refer to some basic knowledge of simplified music wikipedia - simple score . This experiment will only use very simple methods, so you can also read directly.

Take "two tigers" as an example.

### 5.2.1 notes

Notes are represented by numbers 1 to 7. These seven numbers are equal to the natural scale in major.

1 = C in the upper left corner represents the key sign, which represents this simple score. If you use C major and add the sound name, it will be like this:

 1 = C Scale C D E F G A B roll call do re mi fa sol la Si number 1 2 3 4 5 6 7 code NOTE_C4 NOTE_D4 NOTE_E4 NOTE_F4 NOTE_G4 NOTE_A4 NOTE_B4

If the definition in the upper left corner is 1 = D, then re label it from D, as shown in the following table:

 1 = D Scale D E F G A B C roll call do re mi fa sol la Si number 1 2 3 4 5 6 7 code NOTE_D4 NOTE_E4 NOTE_F4 NOTE_G4 NOTE_A4 NOTE_B4 NOTE_C4

### 5.2.2 octave

If it is an octave higher, it will add a point above the number. If it is an octave lower, a point will be added below the number. You don't need to add anything in the middle octave. If you want to increase another octave, add two points vertically above (for example:)； To lower another octave, add two points vertically below (for example:), and so on.

 1 = C Major scale number 5 code NOTE_G7 NOTE_G6 NOTE_G5 NOTE_G4 NOTE_G3 NOTE_G2 NOTE_G1

 1 = C natural minor number 5 code NOTE_GS7 NOTE_GS6 NOTE_GS5 NOTE_GS4 NOTE_GS3 NOTE_GS2 NOTE_GS1

After knowing the notes and octaves, we can start filling in the tone array. Each element in this array corresponds to the frequency parameter of the tone method.

static int liang_zhi_lao_hu_Notes[] = {

NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4,

//Two old tigers two old tigers

NOTE_E4, NOTE_F4, NOTE_G4, NOTE_E4, NOTE_F4, NOTE_G4,

//♪ run fast ♪

NOTE_G4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_C4,

//One has no eyes

NOTE_G4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_C4,

//One has no tail

NOTE_D4, NOTE_G3, NOTE_C4, 0,

//That's weird

NOTE_D4, NOTE_G3, NOTE_C4, 0};

//That's weird

### 5.2.3, time sign and sound length

The 2 / 4 in the upper left corner indicates the time sign. Here, 4 means that the quarter note is a beat, and 2 means that there are two beats in each bar.

Usually there are only numbers Quarter note . Adding a horizontal line under the number can halve the length of the quarter note Quaver ； Two horizontal lines can halve the length of an octave to become Sixteenth note , and so on; The horizontal line after the number extends the note, one for each additional horizontal line Quarter note The length of the.

Therefore, we can get the beat array. Each element in this array corresponds to the duration parameter of the tone method.

static int liang_zhi_lao_hu_NoteDurations[] = {

8, 8, 8, 8, 8, 8, 8, 8,

8, 8, 4, 8, 8, 4,

16, 16, 16, 16, 4, 4,

16, 16, 16, 16, 4, 4,

8, 8, 4, 4,

8, 8, 4, 4};

### 5.2.4 definition of structure

Next, we fill the obtained score information into the structure.

// application/example/edu_demo/k1_apps/musicbox/musicbox.c

typedef struct

{

char *name;                 // The name of music

int *notes;                 // Note array

int *noteDurations;         // Beat array

unsigned int noteLength;    // Number of notes

unsigned int musicTime;     // The total duration of music is processed by the player and used for interface display. Users don't need to care

} music_t;                      // Musical structure

typedef struct

{

music_t **music_list;           // Music List

unsigned int music_list_len;    // Length of Music List

int cur_music_index;            // What is the current music

unsigned int cur_music_note;    // Which note of the current music

unsigned int cur_music_time;    // The current playback duration is handled by the player and used for interface display. Users do not need to care

unsigned int isPlaying;         // Whether the music is played / paused is handled by the player, and users do not need to care

} player_t;

static music_t liang_zhi_lao_hu = {

"liang_zhi_lao_hu",

liang_zhi_lao_hu_Notes,

liang_zhi_lao_hu_NoteDurations,

34

};

music_t *music_list[] = {

&liang_zhi_lao_hu_Notes,        // Insert music into the music list

};

player_t musicbox_player = {music_list, 1, 0, 0, 0, 0}; // Initialize music player

# 6. Realize playing music

while (1)

{

// If the current tone subscript is less than the total tone of the music, it has not been played

if (musicbox_player.cur_music_note < cur_music->noteLength)

{

// Calculate the delay of 1000ms / n diaeresis required by the current note through the beat

int noteDuration = 1000 / cur_music->noteDurations[musicbox_player.cur_music_note];

// For the notes with dots, we use the reading to mark. After adding a dot, the sound length of the note is half longer than its original sound length, that is, 1.5 times of the original sound length.

noteDuration = (noteDuration < 0) ? (-noteDuration * 1.5) : noteDuration;

// Get the current tone

int note = cur_music->notes[musicbox_player.cur_music_note];

// Play tones using the tone method

tone(0, note, noteDuration);

// Delay for a period of time to make the tone conversion clearer

aos_msleep((int)(noteDuration * NOTE_SPACE_RATIO));

// Calculate the current playback time

musicbox_player.cur_music_time += (noteDuration + (int)(noteDuration * NOTE_SPACE_RATIO));

// Ready to play the next tone

musicbox_player.cur_music_note++;

}

}

# 7. Draw player

As a developer with ideals and pursuit, just playing music can't meet our creative desire. So let's implement another player, which can pause / play, previous / next, and display the song name and progress bar.

To realize the required information, we have completed the relevant definitions in the structure, and only need to complete the corresponding music playback control according to the key operation.

void musicbox_task()

{

while (1)

{

// Remove the residue of the last painting

OLED_Clear();

// Gets the pointer to the current music

music_t *cur_music = musicbox_player.music_list[musicbox_player.cur_music_index];

// Get the name of the current music and draw it

char show_song_name[14] = {0};

sprintf(show_song_name, "%-13.13s", cur_music->name);

OLED_Show_String(14, 4, show_song_name, 16, 1);

// If the current player is not paused (playing)

if (musicbox_player.isPlaying)

{

// If it's not finished

if (musicbox_player.cur_music_note < cur_music->noteLength)

{

int noteDuration = 1000 / cur_music->noteDurations[musicbox_player.cur_music_note];

noteDuration = (noteDuration < 0) ? (-noteDuration * 1.5) : noteDuration;

printf("note[%d] = %d\t delay %d ms\n", musicbox_player.cur_music_note, cur_music->noteDurations[musicbox_player.cur_music_note], noteDuration);

int note = cur_music->notes[musicbox_player.cur_music_note];

tone(0, note, noteDuration);

aos_msleep((int)(noteDuration * NOTE_SPACE_RATIO));

musicbox_player.cur_music_time += (noteDuration + (int)(noteDuration * NOTE_SPACE_RATIO));

musicbox_player.cur_music_note++;

}

// If you finish playing, switch to the next song

else

{

noTone(0);

aos_msleep(1000);

next_song();    // musicbox_player.cur_music_index + + player points to the next music

}

OLED_Icon_Draw(54, 36, &icon_pause_24_24, 1);   // Draw a pause icon when the player is playing

}

else

{

OLED_Icon_Draw(54, 36, &icon_resume_24_24, 1);  // Draw the play icon when the player is paused

aos_msleep(500);

}

// Draw a straight line to represent the progress bar. The length of the straight line is 99.0 (the maximum length of the paintable area) * (musicbox_player.cur_music_time) / cur_ Music - > MusicTime

OLED_DrawLine(16, 27, (int)(16 + 99.0 * (musicbox_player.cur_music_time * 1.0 / cur_music->musicTime)), 27, 1);

// Draw icons for previous song and next song

OLED_Icon_Draw(94, 36, &icon_next_song_24_24, 1);

OLED_Icon_Draw(14, 36, &icon_previous_song_24_24, 1);

// Displays the drawn information on the screen

OLED_Refresh_GRAM();

}

}

# 8. Developer technical support

For more technical support, you can add nail developers or pay attention to WeChat official account.

For more information on technologies and solutions, please visit Alibaba cloud AIoT homepage https://iot.aliyun.com/

Tags: IoT HaaS

Posted by Nightslyr on Thu, 14 Apr 2022 20:47:26 +0930