Real-time sonification: Difference between revisions
David Sousa (talk | contribs) |
David Sousa (talk | contribs) |
||
(27 intermediate revisions by 3 users not shown) | |||
Line 76: | Line 76: | ||
'''Detailed explanation of the code:''' | '''Detailed explanation of the code:''' | ||
The blocks are evaluated sequentially from the top to the bottom | The blocks are evaluated sequentially from the top to the bottom within the loop block '''forever''' which repeats the following evaluation sequence until something stops the program: | ||
# Set the variable '''X''' to the button state ('''true''' or '''false''' whether the button is pressed by the time of the pink block '''button A is pressed''' evaluation) | # Set the variable '''X''' to the button state ('''true''' or '''false''' whether the button is pressed by the time of the pink block '''button A is pressed''' evaluation) | ||
# '''If''' the variable/condition '''X''' holds '''true''' (the button was pressed), '''ring tone (Hz) Middle C''', else, '''ring tone (Hz) Middle E''' | # '''If''' the variable/condition '''X''' holds '''true''' (the button was pressed), '''ring tone (Hz) Middle C''', else, '''ring tone (Hz) Middle E''' | ||
=== Sonification of a range of values (using | === Sonification of a range of values (using input sensors) === | ||
Most sensors provide a range of values, not just 0 or 1, in which case we must first find out what the lowest and highest possible values are before defining the mapping for sonification. This variable input from the sensor can originate from the light level sensor, the accelerometer, the magnetometer, the intensity of the sound captured by the microphone, or other sensors connected to the micro:bit through the pins. This data can easily be collected by the microcontroller. | Most sensors provide a range of values, not just 0 or 1, in which case we must first find out what the lowest and highest possible values are before defining the mapping for sonification. This variable input from the sensor can originate from the light level sensor, the accelerometer, the magnetometer, the intensity of the sound captured by the microphone, or other sensors connected to the micro:bit through the pins. This data can easily be collected by the microcontroller. | ||
Line 91: | Line 91: | ||
<HTML> | <HTML> | ||
<div style="position:relative;height:0;padding-bottom: | <div style="position:relative;height:0;padding-bottom:70%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:S29417-89547-25165-22076" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div> | ||
</html> | </html> | ||
'''Detailed explanation of the code''' | '''Detailed explanation of the code:''' | ||
The blocks | The blocks within the '''on start''' block are evaluated sequentially before anything else in the program when the micro:bit is turned on. | ||
# Set the '''x-Min''' variable to the light level lowest possible measured value '''0'''. | # Set the '''x-Min''' variable to the light level lowest possible measured value '''0'''. | ||
# Set the '''x-Max''' variable to the light level highest possible measured value '''255'''. | # Set the '''x-Max''' variable to the light level highest possible measured value '''255'''. | ||
The blocks | The blocks within the block '''forever''' are evaluated sequentially in a loop from top to bottom after the '''on start''' sequence: | ||
# Set the '''x''' variable to the measured '''light level''' | # Set the '''x''' variable to the measured '''light level''' | ||
Line 113: | Line 113: | ||
<HTML> | <HTML> | ||
<div style="position:relative;height:0;padding-bottom: | <div style="position:relative;height:0;padding-bottom:70%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:_F4g6Y9Fd6WRW" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div> | ||
</html> | </html> | ||
'''Detailed explanation of the code''' | '''Detailed explanation of the code:''' | ||
The blocks | The blocks within the '''on start''' block are evaluated sequentially before anything else in the program when the micro:bit is turned on. | ||
# Set the '''x-Min''' variable to the light level lowest possible measured value '''0'''. | # Set the '''x-Min''' variable to the light level lowest possible measured value '''0'''. | ||
# Set the '''x-Max''' variable to the light level highest possible measured value '''255'''. | # Set the '''x-Max''' variable to the light level highest possible measured value '''255'''. | ||
The blocks | The blocks within the block '''forever''' are evaluated sequentially in a loop from top to bottom after the '''on start''' sequence: | ||
# Set the '''x''' variable to the measured '''light level''' | # Set the '''x''' variable to the measured '''light level''' | ||
Line 131: | Line 131: | ||
'''Reminder:''' You can replace the '''light level''' input block with any other micro:bit sensor [https://makecode.microbit.org/reference/input input block] (or any other sensors connected to the micro:bit through the pins) that provide a range of values. Just be sure, to redefine the '''x-Min''' and '''x-Max''' values accordingly, as the [https://makecode.microbit.org/reference/input/acceleration accelerometer] and the [https://makecode.microbit.org/reference/input/compass-heading compass] heading, for instance, work on a different range. | '''Reminder:''' You can replace the '''light level''' input block with any other micro:bit sensor [https://makecode.microbit.org/reference/input input block] (or any other sensors connected to the micro:bit through the pins) that provide a range of values. Just be sure, to redefine the '''x-Min''' and '''x-Max''' values accordingly, as the [https://makecode.microbit.org/reference/input/acceleration accelerometer] and the [https://makecode.microbit.org/reference/input/compass-heading compass] heading, for instance, work on a different range. | ||
==== Using external input sensors ==== | |||
To use an external digital/analog sensor on a micro pin or using for instance the I2C protocol (all of these blocks can be found under the advanced categories) you can use the same programs but simply replace the '''light level''' input block with the corresponding block as follows: | |||
[[File:Soundscapes realtime digitalread.png|350 px|center|Digital read pin]] | |||
[[File:Soundscapes realtime analogread.png|350 px|center|Analog read pin]] | |||
[[File:Soudnscapes realtime i2c.png|700 px|center|i2c]] | |||
'''Attention to the pin number or the i2c address!''' | |||
=== Multiple inputs mapped to a single sound === | === Multiple inputs mapped to a single sound === | ||
Line 142: | Line 158: | ||
<div style="position:relative;height:0;padding-bottom:40%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:_4w40bdb7LTjV" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div> | <div style="position:relative;height:0;padding-bottom:40%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:_4w40bdb7LTjV" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div> | ||
</html> | </html> | ||
=== The SoundScapes sonification extension for micro:bit === | |||
In all the previous examples, numbers were mapped to a continuous range of frequencies, which is great! But does it sound appealing? To enhance the auditory experience, you can map numbers to a musical scale. The [https://makecode.microbit.org/pkg/davidnsousa/sonification SoundScapes sonification extension for MakeCode micro:bit] makes this type of mapping easy and accessible. | |||
The following shows how to install the extension: | |||
[[File:Install-sonification-extension.gif |600 px|center|Install datalogger extension]] | |||
==== Map and play directly from a micro:bit sensor ==== | |||
To map and play directly from a micro:bit sensor you can use the following block with a dropdown menu for choosing the sensor. The input range is automatically selected to match the minimum and maximum values that can be obtained from the micro:bit sensors. | |||
<HTML><div style="position:relative;height:0;padding-bottom:40%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:S46900-26939-17526-29927" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div></HTML> | |||
Although the hard work is behind the curtains, this makes it more challenging for you to innovate in sonification :) | |||
This example is equivalent to the real-time sonification example using the sonification '''map''' function for single value as follows. | |||
==== Map and play a single value on a music scale ==== | |||
The '''map''' function returns an integer number from mapping a number on a certain range [low, high] to a specified music scale on a specified number of octaves. For instance, the following example maps the '''light level''' value on the range [0,255] to Middle C Major on 1 octave and plays it for 500 ms forever: | |||
<HTML><div style="position:relative;height:0;padding-bottom:40%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:S52170-83121-65302-12966" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div></HTML> | |||
Other sensors (including external sensors connected through pins to the micro:bit) and different input ranges can be used as well. This is useful for real-time sonification, when you sonify the data at the same time you collect it. | |||
For instance, the following example maps the light level value on the range [0,255] to Middle C Major on 1 octave and plays it for 500 ms forever: | |||
<HTML><div style="position:relative;height:0;padding-bottom:40%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:S52170-83121-65302-12966" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div></HTML> | |||
Other sensors (including external sensors connected through pins to the micro:bit) and different input ranges can be used as well. This is useful for real-time sonification, when you sonify the data at the same time you collect it. | |||
==== Map and play on a custom scale ==== | |||
You can easily create your own music scales with arrays and serve them as input to the map functions to map and play any number value on your custom scale. The input array must contain the frequency ratios relative to the root frequency. | |||
For instance, the following maps the '''light level''' value on the range [0,255] to Middle C harmonic on 1 octave and plays it for 500 ms: | |||
<HTML><div style="position:relative;height:0;padding-bottom:70%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:S52429-19885-94270-50677" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div></html> | |||
where '''harmonic''' is an array of numbers containing the frequency ratios of the harmonic scale. Since each tone in the harmonic scale is exactly one octave apart from the previous tone, changing the octave number in this particular case will just expand the range of the harmonic series. | |||
=== Sonification via MIDI (The micro:bit as a MIDI instrument) === | === Sonification via MIDI (The micro:bit as a MIDI instrument) === | ||
Line 190: | Line 256: | ||
</html> | </html> | ||
'''Detailed explanation of the code:''' | |||
The blocks inside the '''on start''' block are evaluated sequentially before anything else in the program when the micro:bit is turned on. | The blocks inside the '''on start''' block are evaluated sequentially before anything else in the program when the micro:bit is turned on. | ||
Line 197: | Line 265: | ||
# '''midi use raw serial''' is what will get the micro:bit to "talk" to the MIDI output device. | # '''midi use raw serial''' is what will get the micro:bit to "talk" to the MIDI output device. | ||
The blocks | The blocks within the block '''forever''' are evaluated sequentially in a loop from top to bottom after the '''on start''' sequence: | ||
# Set the '''Note''' variable to a MIDI note by mapping the '''light level''' range of possible values to the chosen MIDI range 40 to 85 (within 0 and 128) using the '''map''' block. | # Set the '''Note''' variable to a MIDI note by mapping the '''light level''' range of possible values to the chosen MIDI range 40 to 85 (within 0 and 128) using the '''map''' block. | ||
Line 203: | Line 271: | ||
# Play MIDI note '''Note''' (measured light level mapped to MIDI) with '''Instrument_1''' (on MIDI channel 1). | # Play MIDI note '''Note''' (measured light level mapped to MIDI) with '''Instrument_1''' (on MIDI channel 1). | ||
# Pause for 250 ms. | # Pause for 250 ms. | ||
# Stop playing the MIDI '''Note'''. | # Stop playing the MIDI note '''Note'''. | ||
# Pause for 100 ms. | # Pause for 100 ms. | ||
==== Using multiple channels ==== | ==== Using multiple MIDI channels ==== | ||
This example maps the '''light level''' to MIDI and uses multiple MIDI channels allowing one to choose to play the notes either with a button or by shaking the micro:bit <ref name="code" group="Note"/>. | |||
<HTML> | |||
<div style="position:relative;height:0;padding-bottom:75%;overflow:hidden;"><iframe style="position:absolute;top:0;left:0;width:100%;height:100%;" src="https://makecode.microbit.org/#pub:_it6bszWsMeyq" frameborder="0" sandbox="allow-popups allow-forms allow-scripts allow-same-origin"></iframe></div> | |||
</html> | |||
'''Detailed explanation of the code:''' | |||
The logic behind this example is very similar to the previous one. However, an extra MIDI channel 10 (it could have been any other number between 1 and 16) is set '''on start''' as variable '''Instrument_2'''. Thus, any changes on this variable are actions on the MIDI channel 10. The mapping of the light level to MIDI is still set within the loop, but the '''Instrument_1''' related blocks and '''pauses''' were moved to the input block '''on button B pressed'''. The input block '''on shake''' just repeats the same code for '''Instrument_2'''. Note, that when you play a note, irrespectively of the instrument chosen, a musical note appears and disappears from the LED screen. | |||
==Notes== | ==Notes== | ||
<references group="Note" /> | <references group="Note" /> |
Latest revision as of 10:17, 4 October 2024
Real-time sonification is an exciting technique that can strongly promote students' engagement in STEAM fields. Real-time sonification means that we are not able to perceive the time interval between the acquisition of the data and the respective sound produced by our sonification device because of the speed of the process. Moreover, the methods for creating sound representations of the data are defined simultaneously with data collection (in "real-time").
Before starting, we want to emphasize that the quality of the sound, which is subjective and therefore depends on the user's taste, must be such that at least it does not disturb the user. On the contrary, if it were appealing enough to attract their attention it would be better. On the other hand, when trying to do something "pleasant" there is a risk of generating sound results that do not fulfill the objective of describing the behavior of the input data well. It is therefore necessary to find a compromise: the sound must be sufficiently pleasant as well as exhaustively informative
Real-time sonification devices
To create a real-time sonification device it is useful to use a microcontroller. These are like "small and simple computers" with a single processor unit. They are not computers though. Their architecture is much simpler and they cannot run an operating system. Still, they can be programmed to execute a single program at a time, which can perform multiple tasks but sequentially, according to the order of the instructions listed in the program. There are several types of microcontrollers, the Arduino (arduino.cc) being the most popular.
To begin with, the SoundScapes project suggests using the BBC micro:bit microcontroller. This tool is very simple to use, versatile, and includes several embedded sensors readily available to use, eliminating the requirement to build a specific electrical circuit for operation. The micro:bit can be programmed online with Makecode (using the Chrome browser for better compatibility) in python, javascript, or blocks.
Sonification with micro:bit
Before diving into sonification with the micro:bit you must first get familiarized with the Makecode programming environment. On the main page, there are various tutorials, like the "Flashing Heart", the "Name Tag", etc, between which you can choose to get started. If you sign up on the platform, your projects will be saved on your account and you can access them from any device as long as you sign in. Otherwise, they are anyway saved as cookies, however, you can loose them if you clear your browser cache.
Sound notions in micro:bit
In the Makecode editor, there is a useful and attractive library dedicated to music, especially for young students. This music library offers several commands/blocks that facilitate the generation of sounds and the creation of melodies. There are many blocks and combinations of blocks you can use to generate different kinds of sounds. Here we introduce you to the most basic bocks and progress to more complex examples. It is a good exercise to play with the different blocks and hear what happens to get familiar with them.
Generate a single tone
The following code generates a single tone with a pre-specified frequency Middle C and duration 1 beat when button A is pressed, or a continuous Middle E ring when button B is pressed. It is possible to change the frequency of the tones by clicking the white input fields with values "Middle C" and "Middle E". From the drop-down menu arrows, it is also possible to change the beat duration of the "Middle C" tone and whether the sound is played sequentially with other command blocks, in the background, or in loop [Note 1].
Play a melody
To play a melody use the following block and click on it to create the melody:
The following example code plays two melodies with different bpm values for buttons A and B and stops all sounds when A and B are pressed simultaneously. It is possible to change the melodies by clicking the white input fields with the colorful music notes. As in the previous example, it is also possible to change the beat duration and whether the sound is played sequentially with other command blocks, in the background, or in loop [Note 1].
Manipulate frequency change, waveform, volume and duration
It is also possible to generate more complex sounds by manipulating frequency change, waveform, volume, and duration with the following block:
The following example plays two complex sounds sequentially forever [Note 1]:
Sonification of a Boolean
In computer science, a Boolean, or logical, data type is a fundamental primitive that can hold one of two possible values: true or false, often represented as 1 or 0. To illustrate this concept, we will sonify the simplest data type, the Boolean. Common examples of sensors that produce Boolean data include presence sensors, contact sensors, switches, and buttons.
The following implements the sonification of a Boolean sensor using the micro:Bit, specifically focusing on button A. When the button is pressed, we will hear the note C, and when it is released, the note will change to F. This auditory feedback provides a clear representation of the button's state, enhancing our understanding of Boolean data in a practical context [Note 1].
Detailed explanation of the code:
The blocks are evaluated sequentially from the top to the bottom within the loop block forever which repeats the following evaluation sequence until something stops the program:
- Set the variable X to the button state (true or false whether the button is pressed by the time of the pink block button A is pressed evaluation)
- If the variable/condition X holds true (the button was pressed), ring tone (Hz) Middle C, else, ring tone (Hz) Middle E
Sonification of a range of values (using input sensors)
Most sensors provide a range of values, not just 0 or 1, in which case we must first find out what the lowest and highest possible values are before defining the mapping for sonification. This variable input from the sensor can originate from the light level sensor, the accelerometer, the magnetometer, the intensity of the sound captured by the microphone, or other sensors connected to the micro:bit through the pins. This data can easily be collected by the microcontroller.
Change pith with fixed rhythm
In this example, we show how to map the light level to a frequency range. The internal light sensor of the micro:bit provides a value between 0 (dark) and 255 (very bright). We call this input value variable x. We also define the variables x-Min and x-Max with the minimum and maximum values of our sensor. For the purpose of sonifying the measured light level, we will map the value of the light level to a pitch between 200 Hz (minimum value) and 2000 Hz (maximum value), played at a fixed rhythm [Note 1].
Detailed explanation of the code:
The blocks within the on start block are evaluated sequentially before anything else in the program when the micro:bit is turned on.
- Set the x-Min variable to the light level lowest possible measured value 0.
- Set the x-Max variable to the light level highest possible measured value 255.
The blocks within the block forever are evaluated sequentially in a loop from top to bottom after the on start sequence:
- Set the x variable to the measured light level
- Play a one 1 beat tone with a frequency resulting from mapping the x value (in the x-Min to x-Max range) to the chosen frequency range in the map block.
Change rhythm with fixed pitch
Another option is to maintain a fixed pitch while varying the rhythm based on the light level. We can achieve this by playing a short-duration note and introducing pauses that vary in length, ranging from 1000 ms (for dark conditions) to 20 ms (for very bright conditions). This approach allows for a dynamic auditory representation of the changing light levels [Note 1].
Detailed explanation of the code:
The blocks within the on start block are evaluated sequentially before anything else in the program when the micro:bit is turned on.
- Set the x-Min variable to the light level lowest possible measured value 0.
- Set the x-Max variable to the light level highest possible measured value 255.
The blocks within the block forever are evaluated sequentially in a loop from top to bottom after the on start sequence:
- Set the x variable to the measured light level
- Play a one 1 beat High D tone.
- Pause for a period calculated from mapping the x value (in the x-Min to x-Max range) to the chosen time range in the map block.
Reminder: You can replace the light level input block with any other micro:bit sensor input block (or any other sensors connected to the micro:bit through the pins) that provide a range of values. Just be sure, to redefine the x-Min and x-Max values accordingly, as the accelerometer and the compass heading, for instance, work on a different range.
Using external input sensors
To use an external digital/analog sensor on a micro pin or using for instance the I2C protocol (all of these blocks can be found under the advanced categories) you can use the same programs but simply replace the light level input block with the corresponding block as follows:
Attention to the pin number or the i2c address!
Multiple inputs mapped to a single sound
Sonification systems often serve to provide more than one piece of information. We can map as many variables as the amount of sound parameters we can control. As long as the sound does not become confusing due to the multiple sound layers playing simultaneously. If we consider that a philharmonic orchestra can have over one hundred elements we have some room for overlaying several sounds. Opposite to the visual stimuli where we cannot exceed a certain number, usually inferior to that of audio stimuli. Finally, like in the orchestra, the sounds have to be carefully arranged together in case of large numbers.
The following sonifies the light level mapped to pith with a pause detefined by the compass heading mapped to milliseconds [Note 1].
The SoundScapes sonification extension for micro:bit
In all the previous examples, numbers were mapped to a continuous range of frequencies, which is great! But does it sound appealing? To enhance the auditory experience, you can map numbers to a musical scale. The SoundScapes sonification extension for MakeCode micro:bit makes this type of mapping easy and accessible.
The following shows how to install the extension:
Map and play directly from a micro:bit sensor
To map and play directly from a micro:bit sensor you can use the following block with a dropdown menu for choosing the sensor. The input range is automatically selected to match the minimum and maximum values that can be obtained from the micro:bit sensors.
Although the hard work is behind the curtains, this makes it more challenging for you to innovate in sonification :)
This example is equivalent to the real-time sonification example using the sonification map function for single value as follows.
Map and play a single value on a music scale
The map function returns an integer number from mapping a number on a certain range [low, high] to a specified music scale on a specified number of octaves. For instance, the following example maps the light level value on the range [0,255] to Middle C Major on 1 octave and plays it for 500 ms forever:
Other sensors (including external sensors connected through pins to the micro:bit) and different input ranges can be used as well. This is useful for real-time sonification, when you sonify the data at the same time you collect it.
For instance, the following example maps the light level value on the range [0,255] to Middle C Major on 1 octave and plays it for 500 ms forever:
Other sensors (including external sensors connected through pins to the micro:bit) and different input ranges can be used as well. This is useful for real-time sonification, when you sonify the data at the same time you collect it.
Map and play on a custom scale
You can easily create your own music scales with arrays and serve them as input to the map functions to map and play any number value on your custom scale. The input array must contain the frequency ratios relative to the root frequency.
For instance, the following maps the light level value on the range [0,255] to Middle C harmonic on 1 octave and plays it for 500 ms:
where harmonic is an array of numbers containing the frequency ratios of the harmonic scale. Since each tone in the harmonic scale is exactly one octave apart from the previous tone, changing the octave number in this particular case will just expand the range of the harmonic series.
Sonification via MIDI (The micro:bit as a MIDI instrument)
The sound produced by the speaker (buzzer) of the micro:bit has little power and does not play low frequencies. The micro:bit is also very limited in its capacity to generate multiple sounds simultaneously and sounds with more complex timbres. In the last example, we used a "trick" to sonify values of multiple inputs. We used the pause (duration of silence between consequent sounds) as a sonification output. Smart but what we would really enjoy would be several sounds simultaneously playing and expressing several layers of data. We can obtain better sound quality and play more instruments at the same time using the midi protocol.
MIDI is a protocol that facilitates real-time communication between electronic musical instruments. MIDI stands for Musical Instrument Digital Interface and it was developed in the early ’80s for storing, editing, processing, and reproducing sequences of digital events connected to sound-producing electronic instruments, especially those using the 88-note chromatic compass of a piano-keyboard. We can roughly, but easily, understand MIDI as the advanced successor of the “piano rolls”, which, more than a century ago, were perforated papers or pinned cylinders, in which music performances were either recorded (in real-time) or notated (in step time). These paper-rolls were then played automatically by specially designed mechanical instruments, the mechanical pianos (pianolas) or music machines, using them as their “program”.
Setup the MIDI
The following video explains in detail how to connect the micro:bit to your DAW (Digital Audio Workstation) or digital synthesizer through MIDI on Windows:
Step-by-step instructions (see the video):
- Install the MIDI Extension for Makecode.
- Create a very basic program using the MIDI extension to test your setup.
- Install Hairless MIDI, open it, and from serial port drop-down menu select the com port (USB port) to which the micro:bit is connected to.
- Install loopMIDI, open it, and click the + button at the bottom-left corner to create a new virtual port.
- Go back to the HairlessMIDI window and on the MIDI out drop-down menu select loopMIDI port
- You might need to unplug and plug in the micro:bit again for it to work.
- You are ready to play!
How it works: The micro:bit sends MIDI messages through serial communication. These messages are then received by Hairless MIDI, which forwards them to LoopMIDI. Acting as a virtual MIDI port, LoopMIDI makes the MIDI messages accessible to computer software/web apps (like DAWs or digital synthesizers) that receive these messages and generate the corresponding sounds, completing the connection.
There are plenty of free (and some open-source, cross-platform) DAW stations like LMMS that you can download and configure to play MIDI input. The easiest method is to play directly from the browser through a web app such as midi.city, the Online Sequencer and many others to discover online. In principle, web apps such as midi.city will readily detect your midi instrument (the micro:bit in this case) and you are ready to play after giving the browser permissions to access your device (which you will be asked to do).
MIDI is a powerful tool for sonification because it allows you to control a wide range of sound parameters, such as pitch, volume, and timbre. This setup allows for multiple Microbits to send MIDI data to a single synthesizer, enabling synchronized sonification of multiple data streams. It also allows a single micro:bit to send MIDI data over multiple MIDI chanels.
Note: On Linux install ttymidi instead of hairlesMIDI and loopMIDI.
Sensor data over MIDI
Previous examples using sensor data can be adapted to send data over MIDI with the Makecode MIDI extension, meaning that the sounds will play not on the micro:bit but through a properly configured computer software/web application. The following example maps the light level to MIDI notes and sends them through MIDI channel 1 [Note 1].
Detailed explanation of the code:
The blocks inside the on start block are evaluated sequentially before anything else in the program when the micro:bit is turned on.
- Show a fancy musical note icon on the LED screen just to make it nicer.
- Set the Instrument_1 variable to midi channel 1. Thus any changes to the variable Instrument_1 are actions on the MIDI channel 1.
- midi use raw serial is what will get the micro:bit to "talk" to the MIDI output device.
The blocks within the block forever are evaluated sequentially in a loop from top to bottom after the on start sequence:
- Set the Note variable to a MIDI note by mapping the light level range of possible values to the chosen MIDI range 40 to 85 (within 0 and 128) using the map block.
- Set the sound volume of Instrument_1 (on MIDI channel 1) to 100.
- Play MIDI note Note (measured light level mapped to MIDI) with Instrument_1 (on MIDI channel 1).
- Pause for 250 ms.
- Stop playing the MIDI note Note.
- Pause for 100 ms.
Using multiple MIDI channels
This example maps the light level to MIDI and uses multiple MIDI channels allowing one to choose to play the notes either with a button or by shaking the micro:bit [Note 1].
Detailed explanation of the code:
The logic behind this example is very similar to the previous one. However, an extra MIDI channel 10 (it could have been any other number between 1 and 16) is set on start as variable Instrument_2. Thus, any changes on this variable are actions on the MIDI channel 10. The mapping of the light level to MIDI is still set within the loop, but the Instrument_1 related blocks and pauses were moved to the input block on button B pressed. The input block on shake just repeats the same code for Instrument_2. Note, that when you play a note, irrespectively of the instrument chosen, a musical note appears and disappears from the LED screen.