There is no need for floating-point arithmetic in music
Virgus wrote:I'd like to store midi notes/ durations sequences in the smallest format possible
that is to say 7bit for midi notes and 8 bit for durations (considering 256 ms multiplied
by 10 that makes for each note a maximum duration possible of 2.5 seconds)
and then translate them to frequency in the function that reads the array (or EEPROM
location) and plays it back...
Thats only 2 bytes per symbol (1 for note and 1 for duration).
Virgus wrote:it would be
great to have one byte only for both notes and durations and have them in pairs sequence...
If your goal is to use the same indexing for notes and durations then you can use two char arrays (1 for notes and 1 for durations) or one int array (e.g. high byte for note low byte for duration). The second one is more compact.
Virgus wrote:One solution I'm thinking about could be to store a table with A frequency values for each octave
and then multiply each time one of that values by 2^(1/12) constant the number of times necessary
to get to the correct note frequency. That would be a maximum of 11 multiplication possible for each note...
Wrong approach and counter intuitive to the definition of an octave (you are assuming that an octave is even tempered). If you store the note frequencies belonging to the same octave in one table, then higher/lower notes can be calculated by doubling/halfing those frequencies. This is the same as shifting integers left/right, i.e. not computationally intensive compared to f-p operations.
Here's what I would do:
1. First we need to determine the limits. Let's say those are C0 (16.35 Hz) and B9 (15804.264 Hz).
2. To get rid of floating-point values, we convert everything to milihertz: C0 = 16350 mHz, B9 = 15804264 mHz.
3. To store that kind of values we need to use 4-byte integers (unsigned long)
4. The frequency table will look like this:
- table.png (20.66 KiB) Viewed 6717 times
5. This table requires 12 * 10 * 4 = 480 bytes of ROM space. We can make it 10 times smaller if we store only the 0-octave and calculate note frequencies for the others by left shifting (doubling) the 0-note frequency n times:
Code: Select all
unsigned long oct[12] = {16350, 17320, 18350, 19450, 20600, 21830,
23120, 24500, 25960, 27500, 29140, 30870};
6. To store a note we use only one byte (high nibble for column and low nibble for row of the frequency table)
Let's say we want to encode sequence: C2, E5, F3. If we assign indexes to the columns then C=0, C#=1, D=2, ..., B=11
Our sequence becomes: 0x02, 0x45, 0x53
Our frequencies are:
Code: Select all
(oct[0] << 2), (oct[3] << 5), (oct[5] << 3) =
= (16350 << 2), (19450 << 5), (21830 << 3) =
= 65400, 659200, 174640 =
= 65.4 Hz, 659.2 Hz, 174.64 Hz
Example:
Code: Select all
#include "built_in.h"
const unsigned long oct[12] = {16350, 17320, 18350, 19450, 20600, 21830,
23120, 24500, 25960, 27500, 29140, 30870};
// C2 = 0x020A; // Duration: 0x0A = 10 * 10 ms = 100 ms
// E5 = 0x450F; // Duration: 0x0F = 15 * 10 ms = 150 ms
// F3 = 0x5314; // Duration: 0x14 = 20 * 10 ms = 200 ms
unsigned int seq[] = {0x020A, 0x450F, 0x5314}; // C2, E5, F3 sequence
void PlaySound(unsigned long freq, unsigned int duration) {
// your code for playing sounds
}
void main() {
int i;
unsigned long freq;
// play sequence
for (i = 0; i < 3; i++) {
// High-byte high-nibble High-byte low-nibble
freq = oct[Hi(seq[i]) >> 4] << (Hi(seq[i]) & 0x0F);
PlaySound(freq, Lo(seq[i]));
}
}
or if you wish to separate notes and durations:
Code: Select all
#include "built_in.h"
const unsigned long oct[12] = {16350, 17320, 18350, 19450, 20600, 21830,
23120, 24500, 25960, 27500, 29140, 30870};
// C2 = 0x020A; // Duration: 0x0A = 10 * 10 ms = 100 ms
// E5 = 0x450F; // Duration: 0x0F = 15 * 10 ms = 150 ms
// F3 = 0x6314; // Duration: 0x14 = 20 * 10 ms = 200 ms
unsigned char seq_note[] = {0x02, 0x45, 0x53}; // C2, E5, F3 sequence
unsigned char seq_duration[] = {0x0A, 0x0F, 0x14}; // 10, 15, 20 ms duration
void PlaySound(unsigned long freq, unsigned int duration) {
// your code for playing sounds
}
void main() {
int i;
unsigned long freq;
// play sequence
for (i = 0; i < 3; i++) {
// high-nibble low-nibble
freq = oct[seq_note[i] >> 4] << (seq_note[i] & 0x0F);
PlaySound(freq, seq_duration[i]);
}
}
Regards