Generalise the timing

main
Michael Hayes 15 years ago
parent be813dcb55
commit 5c0c1fba18

@ -68,45 +68,76 @@
* sixteenth notes? Perhaps A,, * sixteenth notes? Perhaps A,,
* Perhaps lower case for eighth notes. * Perhaps lower case for eighth notes?
* Triplets and other irrational tuplets? A triplet quarter note
has 2 / 3 the duration of a quarter note while a triplet eighth note
has 2 / 3 the duration of of an eighth note. This can be achieved
using *6 or *12.
*/ */
enum {MMELODY_SCALE_SIZE = 12}; enum {MMELODY_SCALE_SIZE = 12};
/* Assume each beat is a quarter note. */
enum {MMELODY_BEAT_FRACTION = 4};
/* Scale factor for fraction of a symbol timing. */
enum {MMELODY_DURATION_FRACTION = 4};
/* How long to release note before sounding the next one. */
#define MMELODY_RELEASE_MS 50
static void static void
mmelody_ticker_set (mmelody_t mmelody) mmelody_ticker_set (mmelody_t mmelody)
{ {
uint16_t speed; /* The duration of a beat varies with the time signature:
2/2 : minum (half note)
4/4 : crotchet (quarter note)
6/8, 9/8, 12/8 : dotted crotchet (one and a half quarter notes)
Currently, there is no time signature support so we assume 4/4.
This means a beat is a quarter note and a bar is four quarter
notes.
ticks_per_beat = poll_rate * 60 / speed
symbols_per_beat = symbol_fraction / MMELODY_BEAT_FRACTION
ticks_per_symbol = ticks_per_beat / symbols_per_beat
ticks_per_unit = ticks_per_symbol / MMELODY_DURATION_FRACTION
/* Notes per four minutes. */ ticks_per_note = ticks_per_beat * MMELODY_BEAT_FRACTION
speed = mmelody->speed * mmelody->note_fraction; = ticks_per_unit * symbol_fraction
* MMELODY_DURATION_FRACTION
/* This is the duration of an eighth-note. The duration is specified as the actual duration scaled by
MMELODY_DURATION_FRACTION so that it can specify half periods.
With note_fraction = 4, speed = 50, and poll_rate of 200 this With symbol_fraction = 4, speed = 50, and poll_rate of 200 this
gives a result of 120. */ gives a result of 60. */
mmelody->note_ticks = mmelody->poll_rate * 60 * 2 / speed;
mmelody->unit_ticks = (mmelody->poll_rate * 60
* MMELODY_BEAT_FRACTION)
/ (mmelody->speed * mmelody->symbol_fraction
* MMELODY_DURATION_FRACTION);
} }
static void static void
mmelody_note_on (mmelody_t mmelody, mmelody_note_t note, uint8_t duration) mmelody_note_on (mmelody_t mmelody, mmelody_note_t note, uint8_t duration)
{ {
/* The duration is in terms of eighth notes. */ /* The duration is in terms of the half the duration of a symbol. */
mmelody->play_callback (mmelody->play_callback_data, note, mmelody->play_callback (mmelody->play_callback_data, note,
mmelody->volume); mmelody->volume);
mmelody->note = note; mmelody->note = note;
/* Determine ticks between sounding notes (this needs to be a /* Determine ticks between sounding notes (this needs to be a
minimum of 1). */ minimum of 1). If we assume a release period of 50 ms,
mmelody->ticks2 = mmelody->note_ticks / 16; this will give a count of 10 ticks at 200 Hz sampling. */
if (!mmelody->ticks2) mmelody->ticks2 = mmelody->release_ticks;
mmelody->ticks2 = 1;
/* Determine ticks before turning the note off. */ /* Determine ticks before turning the note off. */
mmelody->ticks1 = mmelody->note_ticks * duration - mmelody->ticks2; mmelody->ticks1 = mmelody->unit_ticks * duration - mmelody->ticks2;
} }
@ -122,9 +153,9 @@ mmelody_note_off (mmelody_t mmelody)
/* Specify the default note length in fractions of a measure (bar). /* Specify the default note length in fractions of a measure (bar).
A value of 4 is the default which makes each note a quarter note. */ A value of 4 is the default which makes each note a quarter note. */
static void static void
mmelody_note_fraction_set (mmelody_t mmelody, uint8_t note_fraction) mmelody_symbol_fraction_set (mmelody_t mmelody, uint8_t symbol_fraction)
{ {
mmelody->note_fraction = note_fraction; mmelody->symbol_fraction = symbol_fraction;
mmelody_ticker_set (mmelody); mmelody_ticker_set (mmelody);
} }
@ -222,7 +253,7 @@ mmelody_scan (mmelody_t mmelody, const char *str)
case '*': case '*':
if (num) if (num)
mmelody_note_fraction_set (mmelody, num); mmelody_symbol_fraction_set (mmelody, num);
continue; continue;
case '@': case '@':
@ -262,22 +293,20 @@ mmelody_scan (mmelody_t mmelody, const char *str)
duration++; duration++;
str++; str++;
} }
duration = duration * MMELODY_DURATION_FRACTION;
if (*str == '.') if (*str == '.')
{ {
/* Dotted quarter note. */ /* Dotted quarter note. */
duration += duration * 2; duration += duration / 2;
str++; str++;
} }
else if (*str == ',') else if (*str == ',')
{ {
/* Eighth note. */ /* Eighth note. */
str++; str++;
/* What about dotted eighth notes? */ duration = duration / 2;
}
else
{
/* Quarter note. */
duration *= 2;
} }
mmelody_note_on (mmelody, note, duration); mmelody_note_on (mmelody, note, duration);
@ -301,7 +330,7 @@ mmelody_play (mmelody_t mmelody, const char *str)
mmelody->loop_count = 0; mmelody->loop_count = 0;
mmelody->octave = MMELODY_OCTAVE_DEFAULT; mmelody->octave = MMELODY_OCTAVE_DEFAULT;
/* Default to quarter notes. */ /* Default to quarter notes. */
mmelody_note_fraction_set (mmelody, 4); mmelody_symbol_fraction_set (mmelody, 4);
/* Stop what is currently sounding. */ /* Stop what is currently sounding. */
mmelody_note_off (mmelody); mmelody_note_off (mmelody);
@ -317,16 +346,8 @@ mmelody_speed_set (mmelody_t mmelody, mmelody_speed_t speed)
/* With 8 bits for the tempo (in bpm), the max tempo is 255 bpm. /* With 8 bits for the tempo (in bpm), the max tempo is 255 bpm.
This corresponds to 4.25 beats per second. If the minimum time This corresponds to 4.25 beats per second. If the minimum time
to release a note is 1 / 8 of a quarter note, then we need to to release a note is 1 / 8 of a quarter note, then we need to
poll at a rate of at least 4.25 * 8 = 34 times per second. poll at a rate of at least 4.25 * 8 = 34 times per second. */
The duration of a beat varies with the time signature:
2/2 : minum (half note)
4/4 : crotchet (quarter note)
6/8, 9/8, 12/8 : dotted crotchet (one and a half quarter notes)
Currently, there is no time signature support so we assume 4/4.
This means a beat is quarter note and a bar is four quarter notes.
*/
mmelody->speed = speed; mmelody->speed = speed;
mmelody_ticker_set (mmelody); mmelody_ticker_set (mmelody);
} }
@ -384,8 +405,9 @@ mmelody_init (mmelody_obj_t *mmelody,
mmelody->note = 0; mmelody->note = 0;
mmelody->ticks1 = 0; mmelody->ticks1 = 0;
mmelody->ticks2 = 0; mmelody->ticks2 = 0;
mmelody->release_ticks = (poll_rate * MMELODY_RELEASE_MS) / 1000;
mmelody_speed_set (mmelody, MMELODY_SPEED_DEFAULT); mmelody_speed_set (mmelody, MMELODY_SPEED_DEFAULT);
mmelody_note_fraction_set (mmelody, 4); mmelody_symbol_fraction_set (mmelody, 4);
return mmelody; return mmelody;
} }

@ -22,15 +22,17 @@ typedef struct
{ {
uint16_t ticks1; uint16_t ticks1;
uint8_t ticks2; uint8_t ticks2;
/* Duration of an eighth note. */ uint8_t unit_ticks;
uint8_t note_ticks; uint8_t release_ticks;
/* Pointer to current position in string. */ /* Pointer to current position in string. */
const char *cur; const char *cur;
/* Pointer to start of string. */ /* Pointer to start of string. */
const char *start; const char *start;
const char *loop_start; const char *loop_start;
int8_t loop_count; int8_t loop_count;
uint8_t note_fraction; /* Fraction of a whole note, e.g., 4 for quarter note. */
uint8_t symbol_fraction;
/* Last note emitted. */
uint8_t note; uint8_t note;
/* Tempo in beats per minute. */ /* Tempo in beats per minute. */
mmelody_speed_t speed; mmelody_speed_t speed;

Loading…
Cancel
Save