composing: note duration issues

So I am attempting to transcribe a bunch of NES songs into arduino compatible tunes and am starting with my favourite: Bloody Tears from Castlevania II.

I am having some issues with note durations though. The majority of the song is in 16ths and so the majority of my code was 16 for the associated notes. But once I played it sounded like 1/32ths or faster. I have put them at 8ths for he time being so i can continue on trying to make this, but they are not 1/8ths either. So I do not understand how note duration works in this case and i am doing this based off of “toneMelody” if that isn’t obvious.

tldr: how do i get notes such as 1, 1/4, 1/8, 1/16, etc within the framework of the following code?

Thank you for any assistance.

CODE:

#include “pitches.h”

int melody = {
NOTE_AS5, NOTE_F5, NOTE_F6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_D6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_AS5, NOTE_F5,
NOTE_C6, NOTE_D6, NOTE_F6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_D6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_AS5, NOTE_F5,
NOTE_AS5, NOTE_F5, NOTE_F6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_D6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_AS5, NOTE_F5,
NOTE_C6, NOTE_D6, NOTE_F6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_D6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_DS6, NOTE_F5, NOTE_C6, NOTE_F5, NOTE_AS5, NOTE_F5, NOTE_C5, NOTE_DS5, NOTE_D5, NOTE_D5, NOTE_C4, NOTE_B3, NOTE_C4, NOTE_D4, NOTE_DS4, NOTE_F4, NOTE_DS5
};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations = {
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
4, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4,

};

void setup() {
// iterate over the notes of the melody:
for (int thisNote = 0; thisNote <75; thisNote++) {

//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int noteDuration = 1000 / noteDurations[thisNote];
tone(8, melody[thisNote], noteDuration);

// to distinguish the notes, set a minimum time between them.
// the note’s duration + 30% seems to work well:
int pauseBetweenNotes = noteDuration * 1.00;
delay(pauseBetweenNotes);
// stop the tone playing:
noTone(75);
}
}

void loop() {
// no need to repeat the melody.
}

am having some issues with note durations though. The majority of the song is in 16ths and so the majority of my code was 16 for the associated notes. But once I played it sounded like 1/32ths or faster.

The duration of note depends on the tempo. i.e. If you play a song at twice the tempo, 1/4 notes are half the duration.

// to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.00;

The comment doesn’t agree with the code. :wink:

And, I think the logic is wrong… For example if you add a 30% delay to a whole note, that’s longer than a 1/4 note. That’s going to foul-up the timing. If you add 100% to a whole note, that’s a whole measure rest added-in before the next note.

I think you need to subtract the pause from the sound-duration, so that - Total Note Duration = Sound Duration + Pause. That way, the next note will start at the right time.

cart:
So I am attempting to transcribe a bunch of NES songs into arduino compatible tunes

Did you ever think of using the "Nokia ringtone format RTTTL for melody encoding?

The RTTTL ringtone format was commonly used with Nokia mobile phones during the 1990s for coding monophonic ringtones.

http://www.mobilefish.com/tutorials/rtttl/rtttl_quickguide_specification.html

But not only Nokia mobiles of the 1990s can play such melodies. Arduino can do, too!

Perhaps give that code a try :

I googled the melody you asked for.

Does it play using this code?

// A fun sketch to demonstrate the use of the tone() function written by Brett Hagman.

// This plays RTTTL (RingTone Text Transfer Language) songs using the
// now built-in tone() command in Wiring and Arduino.
// Written by Brett Hagman
// http://www.roguerobotics.com/



// To play the output on a small speaker (i.e. 8 Ohms or higher), simply use
// a 1K Ohm resistor from the output pin to the speaker, and connect the other
// side of the speaker to ground.


const int tonePin = 8;  // arbitrary for arduino boards, set this to whatever you want

#define OCTAVE_OFFSET 0

// These values can also be found as constants in the Tone library (Tone.h)
int notes[] = { 0,
262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951
};

void setup(void)
{
  Serial.begin(9600);
char *song= "btears:d=4,o=5,b=125:16a#,16f,16f6,16f,16d#6,16f,16c#6,16f,16c6,16f,16c#6,16f,16c6,16f,16a#,16f,16c6,16f,16c#6,16f,16d#6,16f,16c#6,16f,16c6,16f,16g#,16f,16c6,16f,16a#,16f,16a#,16f,16f6,16f,16d#6,16f,16c#6,16f,16c6,16f,16c#6,16f,16c6,16f,16a#,16f,16c6,16f,16c#6,16f,16d#6,16f,16c#6,16f,16c6,16f,16g#,16f,16c6,16f,16a#,16f,8d#,16g#,2f,8d#,8c#,8d#.,8g#.,f.,8d#,8c#,8d#,16g#,2f,8d#,8f,8f#.,g#.,8f.,f#";
play_rtttl(song);
}

void loop()
{
  
}




#define isdigit(n) (n >= '0' && n <= '9')

void play_rtttl(char *p)
{
  // Absolutely no error checking in here

  byte default_dur = 4;
  byte default_oct = 6;
  int bpm = 63;
  int num;
  long wholenote;
  long duration;
  byte note;
  byte scale;

  // format: d=N,o=N,b=NNN:
  // find the start (skip name, etc)

  while(*p != ':') p++;    // ignore name
  p++;                     // skip ':'

  // get default duration
  if(*p == 'd')
  {
    p++; p++;              // skip "d="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    if(num > 0) default_dur = num;
    p++;                   // skip comma
  }

  Serial.print("ddur: "); Serial.println(default_dur, 10);

  // get default octave
  if(*p == 'o')
  {
    p++; p++;              // skip "o="
    num = *p++ - '0';
    if(num >= 3 && num <=7) default_oct = num;
    p++;                   // skip comma
  }

  Serial.print("doct: "); Serial.println(default_oct, 10);

  // get BPM
  if(*p == 'b')
  {
    p++; p++;              // skip "b="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    bpm = num;
    p++;                   // skip colon
  }

  Serial.print("tempo BPM: "); Serial.println(bpm, 10);

  // BPM usually expresses the number of quarter notes per minute
  wholenote = (60 * 1000L / bpm) * 4;  // this is the time for whole note (in milliseconds)

  Serial.print("wn: "); Serial.println(wholenote, 10);


  // now begin note loop
  while(*p)
  {
    // first, get note duration, if available
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    
    if(num) duration = wholenote / num;
    else duration = wholenote / default_dur;  // we will need to check if we are a dotted note after

    // now get the note
    note = 0;

    switch(*p)
    {
      case 'c':
        note = 1;
        break;
      case 'd':
        note = 3;
        break;
      case 'e':
        note = 5;
        break;
      case 'f':
        note = 6;
        break;
      case 'g':
        note = 8;
        break;
      case 'a':
        note = 10;
        break;
      case 'b':
        note = 12;
        break;
      case 'p':
      default:
        note = 0;
    }
    p++;

    // now, get optional '#' sharp
    if(*p == '#')
    {
      note++;
      p++;
    }

    // now, get optional '.' dotted note
    if(*p == '.')
    {
      duration += duration/2;
      p++;
    }
  
    // now, get scale
    if(isdigit(*p))
    {
      scale = *p - '0';
      p++;
    }
    else
    {
      scale = default_oct;
    }

    scale += OCTAVE_OFFSET;

    if(*p == ',')
      p++;       // skip comma for next note (or we may be at the end)

    // now play the note

    if(note)
    {
      Serial.print("Playing: ");
      Serial.print(scale, 10); Serial.print(' ');
      Serial.print(note, 10); Serial.print(" (");
      Serial.print(notes[(scale - 4) * 12 + note], 10);
      Serial.print(") ");
      Serial.println(duration, 10);
      tone(tonePin, notes[(scale - 4) * 12 + note]);
      delay(duration);
      noTone(tonePin);
    }
    else
    {
      Serial.print("Pausing: ");
      Serial.println(duration, 10);
      delay(duration);
    }
  }
}

P.S.: Playing speed can easily be modified by setting different BEAT PER MINUTE Tempo in the prefix section of the song (b=).