Skip to content

Commit

Permalink
Support multi-melody Rtttl files
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronkirschen committed Jul 31, 2022
1 parent 6ce865e commit bc1bd1f
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/melody_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class MelodyFactoryClass {
/**
* Load melody from file in RTTTL format.
*/
Melody loadRtttlFile(String filePath, FS& fs = SPIFFS);
Melody loadRtttlFile(String filepath, String title = "", FS& fs = SPIFFS);

/**
* Load melody from string in RTTTL format.
Expand Down Expand Up @@ -149,7 +149,7 @@ class MelodyFactoryClass {
bool loadNote(String token);

// Enable debug messages over serial port
static const bool debug = false;
static const bool debug = true;
};

extern MelodyFactoryClass MelodyFactory;
Expand Down
34 changes: 31 additions & 3 deletions src/melody_factory_rtttl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const uint16_t sourceNotes[] = {
};
// clang-format on

Melody MelodyFactoryClass::loadRtttlFile(String filepath, FS& fs) {
Melody MelodyFactoryClass::loadRtttlFile(String filepath, String title, FS& fs) {
File f = fs.open(filepath, "r");
f.setTimeout(0);

Expand All @@ -99,7 +99,18 @@ Melody MelodyFactoryClass::loadRtttlFile(String filepath, FS& fs) {
return Melody();
}

String title = f.readStringUntil(':');
if (title.length() == 0) {
if (debug) Serial.println("No title parameter was passed, operating in single-line mode");
title = f.readStringUntil(':');
} else {
if (debug) Serial.println("Title paramter was passed, operating in multi-line mode");
if (!f.find(title.c_str())) {
if (debug) Serial.println("Unable to find melody with title: " + String(title));
return Melody();
}
f.readStringUntil(':');
}

title.trim();
if (debug) Serial.println(String("Title:") + title);
if (title.length() == 0) { return Melody(); }
Expand All @@ -114,11 +125,28 @@ Melody MelodyFactoryClass::loadRtttlFile(String filepath, FS& fs) {
// 32 because it is the shortest note!
int timeUnit = 60 * 1000 * 4 / beat / 32;

unsigned long position = f.position();
unsigned long bytesUntilNewLine = f.readStringUntil('\n').length();
f.seek(position);

notes = std::make_shared<std::vector<NoteDuration>>();
bool result = true;
while (f.available() && notes->size() < maxLength && result) {
bool lastNoteBeforeNewLine = false;
while ((f.available()) && (notes->size() < maxLength) && (result) && (!lastNoteBeforeNewLine)) {
String s = f.readStringUntil(',');

if (s.length() > bytesUntilNewLine) {
lastNoteBeforeNewLine = true;
f.seek(position);
s = f.readStringUntil('\n');
}
s.trim();

position = f.position();
bytesUntilNewLine = f.readStringUntil('\n').length();
f.seek(position);

if (debug) Serial.println(String("Found note:") + s);
result = parseRtttlNote(s);
}
if (result && notes->size() > 0) { return Melody(title, timeUnit, notes, false); }
Expand Down

7 comments on commit bc1bd1f

@aaronkirschen
Copy link
Contributor Author

@aaronkirschen aaronkirschen commented on bc1bd1f Jul 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Melodies are separated by new line (\n).
loadRtttlFile(filepath, "example") would load the melody with title="example".

@fabianoriccardi
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great addition, I'm considering it! Do you know if RTTTL specs say if the melody can be split over multiple lines?
Since your update limits a melody to the line.

@aaronkirschen
Copy link
Contributor Author

@aaronkirschen aaronkirschen commented on bc1bd1f Jul 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had found a document (http://mines.lumpylumpy.com/Electronics/Computers/Software/Cpp/MFC/RingTones.RTTTL) with a lot of ringtone RTTTLs that were separated by new lines, and I wanted to be able to upload just that one file and select RTTTLs from it so I wrote this based on that file.

The official spec (I found it here https://panuworld.net/nuukiaworld/download/nokix/rtttl.htm) states that reader applications must ignore whitespace, which I think would include ignoring newlines. So, it seems like RTTTL was intended to support ringtones spanning multiple lines.

There are a few options for changing this so that it will continue to support multi-line melodies if you want to keep supporting those.

I think that having multiple ringtones in one file may already break away from the spec though, in which case using whitespace new line to separate ringtones may not matter so much other than perhaps decreasing human readability by not allowing each ringtone to be put on multiple lines

@fabianoriccardi
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the references. The specs are very clear:

An RTTTL file is a text file, containing the ringtone name, a control section and a section containing a comma separated sequence of ring tone commands. White space must be ignored by any reader application.

So just one ringtone per file. However, I understand your point, and basically you are building a "database" of melodies within a single file. I propose to create a different method to load a file containing multiple melodies, one per single line. The prototype may be:

Melody loadRtttlDB(String filename, String melody, FS& fs);

and for simplicity, I will constrain each melody to a single line. What do you think?

@aaronkirschen
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would work well. It will keep the original method compliant with the RTTTL reader specification, while still allowing for the convenience of loading melodies from this type of non-compliant file containing multiple melodies.

@fabianoriccardi
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly! I can't promise when, but I will integrate this idea as soon as possible

@fabianoriccardi
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HI @aaronkirschen, I have just pushed the modification. May you give it a try?

Please sign in to comment.