Skip to content

Commit

Permalink
Merge pull request #18 from ImSkully/dev
Browse files Browse the repository at this point in the history
v1.0.3 Release
  • Loading branch information
ImSkully authored Nov 11, 2024
2 parents 46cb736 + 5c146e5 commit 7c4d1e0
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 86 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ The following diagram is an example representation of a good basic circuit that

Note that the actual circuit will vary depending on your controller:
- For ESP8266 boards, the software PWM output pins available are any in the range `GPIO0` - `GPIO16`
- For ESP32 boards, the [LEDC PWM channel](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html) can acts as output [*(diagram)*](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/_images/ledc-api-settings.jpg) and you can normally use any pin in the range `GPIO0` - `GPIO33`
- For ESP32 boards, the [LEDC PWM channel](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html) can acts as output and you can normally use any pin in the range `GPIO0` - `GPIO33`

## Code
If your microcontroller is compatible with ESPHome then you can fortunately make use of the existing RTTTL component to easily play the RTTTL strings composed on the web application directly on the ESP device, see the [RTTTL Component](https://esphome.io/components/buzzer.html#rtttl-component) documentation for more information, the source for the underlying RTTTL library can be found [here](https://esphome.io/api/rtttl_8h).
If your microcontroller is compatible with ESPHome then you can fortunately make use of the existing RTTTL component to easily play the RTTTL strings composed on the web application directly on the ESP device, see the [RTTTL Component](https://esphome.io/components/rtttl.html) documentation for more information, the source for the underlying RTTTL library can be found [here](https://esphome.io/api/rtttl_8h).

```yaml
# Buzzer (ESP32 controller)
Expand Down
91 changes: 47 additions & 44 deletions routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default function Home() {
</div>
</div>

<div class="card mt-3">
<div class="card my-3">
<div class="card-body">
{/* Controls Row */}
<div class="row">
Expand All @@ -77,7 +77,7 @@ export default function Home() {
<input
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Use left slider to adjust"
title="Use slider to adjust"
type="text"
class="form-control disabled"
id="bpm-input"
Expand Down Expand Up @@ -118,55 +118,58 @@ export default function Home() {
</div>

{/* RTTTL Output Text Area */}
<div class="row">
<div class="col-8">
<div class="my-3">
<label class="form-label" for="rtttl-output-textbox">RTTTL Output</label>
<div class="input-icon mb-3">
<span class="input-icon-addon">
<i class="icon ti ti-music"></i>
</span>
<input type="text" class="form-control font-terminal" id="rtttl-output-textbox" placeholder="song_name:d=4,o=5,b=140:..." />
</div>
<div class="row mt-3">
<div class="col-6">
<label class="form-label" for="rtttl-output-textbox">RTTTL Output</label>
<div class="input-icon mb-3">
<span class="input-icon-addon">
<i class="icon ti ti-music"></i>
</span>
<input type="text" class="form-control font-terminal" id="rtttl-output-textbox" placeholder="song_name:d=4,o=5,b=140:..." />
</div>
</div>
<div class="col-2">
{/* Dropdown Example RTTTL Selector */}
<label class="form-label" for="rtttl-examples-selector">Example Ringtones</label>
<select id="rtttl-examples-selector" class="form-select">
<option selected disabled hidden>(select a tone)</option>
</select>
</div>

{/* Playback */}
<div class="col-4">
<div class="my-3">
<div class="form-label">
Playback <kbd>Spacebar</kbd>
</div>
<div class="btn-group w-100" role="group">
{/* Play Button */}
<input type="radio" class="btn-check" name="btn-radio-toolbar" id="radio-toolbar-play" autocomplete="off" />
<label for="radio-toolbar-play" class="btn btn-success">
<i class="icon ti ti-player-play"></i>
Play
</label>
{/* Stop Button */}
<input type="radio" class="btn-check" name="btn-radio-toolbar" id="radio-toolbar-stop" autocomplete="off" disabled />
<label for="radio-toolbar-stop" class="btn btn-danger">
<i class="icon ti ti-player-stop"></i>
Stop
</label>
<div class="form-label">
Playback <kbd>Spacebar</kbd>
</div>
<div class="btn-group w-100" role="group">
{/* Play Button */}
<input type="radio" class="btn-check" name="btn-radio-toolbar" id="radio-toolbar-play" autocomplete="off" />
<label for="radio-toolbar-play" class="btn btn-success">
<i class="icon ti ti-player-play"></i>
Play
</label>
{/* Stop Button */}
<input type="radio" class="btn-check" name="btn-radio-toolbar" id="radio-toolbar-stop" autocomplete="off" disabled />
<label for="radio-toolbar-stop" class="btn btn-danger">
<i class="icon ti ti-player-stop"></i>
Stop
</label>

{/* Load From Text Area Button */}
<input type="radio" class="btn-check" name="btn-radio-toolbar" id="radio-toolbar-load-from-textarea" autocomplete="off" />
<label for="radio-toolbar-load-from-textarea" class="btn btn-info">
<i class="icon ti ti-book-upload"></i>
Load From Text Area
</label>
{/* Load From Text Area Button */}
<input type="radio" class="btn-check" name="btn-radio-toolbar" id="radio-toolbar-load-from-textarea" autocomplete="off" />
<label for="radio-toolbar-load-from-textarea" class="btn btn-info">
<i class="icon ti ti-book-upload"></i>
Load From Text Area
</label>

{/* Volume Button */}
<button class="btn btn-icon bg-azure-lt" name="btn-radio-toolbar" data-bs-toggle="dropdown" aria-label="volume-button" autocomplete="off">
<i class="icon ti ti-volume"></i>
<div class="dropdown-menu p-2">
<label class="form-label text-center" id="radio-toolbar-slider-volume-text" for="radio-toolbar-slider-volume">20%</label>
<input type="range" class="form-range" value="20" min="0" max="100" id="radio-toolbar-slider-volume" />
</div>
</button>
</div>
{/* Volume Button */}
<button class="btn btn-icon bg-azure-lt" name="btn-radio-toolbar" data-bs-toggle="dropdown" aria-label="volume-button" autocomplete="off">
<i class="icon ti ti-volume"></i>
<div class="dropdown-menu p-2">
<label class="form-label text-center" id="radio-toolbar-slider-volume-text" for="radio-toolbar-slider-volume">20%</label>
<input type="range" class="form-range" value="20" min="0" max="100" id="radio-toolbar-slider-volume" />
</div>
</button>
</div>
</div>
</div>
Expand Down
13 changes: 5 additions & 8 deletions routes/rtttl_specification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ export default async function RTTTLSpecification() {
<p>
The name of the ringtone, this is normally not used by the player but is useful for identifying the ringtone. According to the official
specification, the name must not exceed 11 characters in length though this is arbritary and many implementation of RTTTL are generally capable of
handling names with much greater lengths. Whilst no character restrictions are specified, it is recommended to use only alphanumeric characters and
spaces, with the semicolon <code>:</code> character.
handling names with much greater lengths. Whilst no character restrictions are specified, it is recommended to use only alphanumeric characters.
</p>

<h2 id="part-2-control-section" class="mt-4">
Expand Down Expand Up @@ -143,10 +142,8 @@ export default async function RTTTLSpecification() {
<div class="card-body">
<p>
The <a href="https://en.wikipedia.org/wiki/Duration_(music)" target="_blank" class="fw-bold">duration</a>{" "}
of the note which is measured relative to other notes using a system of whole notes.
<strong>
<u>It is not measured in time (seconds or minutes)</u>
</strong>.
of the note which is measured relative to other notes using a system of whole notes &mdash;{" "}
<u class="fw-bold">It is not measured in time (seconds or minutes)</u>.
</p>
<div class="col-4">
<table class="table table-vcenter card-table">
Expand Down Expand Up @@ -386,8 +383,8 @@ export default async function RTTTLSpecification() {
<div class="hr-text" id="references">
<span>References</span>
</div>
<div class="alert alert-secondary mt-3">
<ol>
<div class="alert alert-info mt-3">
<ol start={2}>
<li>
<a href="https://web.archive.org/web/20000615010005/http://www.binet.lv/personal/nokia/note_syntax_1-1.txt" target="_blank">
Web Archive: binet.lv/personal/nokia/note_syntax_1-1.txt
Expand Down
41 changes: 10 additions & 31 deletions routes/specs/rtttl_1.0.0.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
;
; Ringing Tones text transfer language (RTTTL)
;
; Version 1.1 / 13-Aug-1999
; - Updated by The TDD for strict conformity
; with Nokia Smart Messaging 2.0 Specification
;
; http://www.binet.lv/personal/nokia/note_syntax_1-1.txt
;
; Version 1.0 / 29-July-1998
; - First version by John Mostelo
;
Expand All @@ -16,46 +10,34 @@
<ringing-tones-text-transfer-language> :=
<name> <sep> [<defaults>] <sep> <note-command>+

<name> := <char>+ ; maximum name length 11 characters
<name> := <char>+ ; maximum name length 10 characters

<sep> := ":"

<defaults> :=
<def-note-duration> |
<def-note-scale> |
<def-beats> |
<def-volume> |
<def-style>
<def-beats>

<def-note-duration> := "d=" <duration>

<def-note-scale> := "o=" <scale>

<def-beats> := "b=" <beats-per-minute>

<def-volume> := "v=" <volume>

<def-style> := "s=" <style>

<beats-per-minute> := 25,28,...,900 ; decimal value

<volume> := 0..15 ; 0 - no tone, 15 - maximal volume, decimal value

<style> := 1 | ; normal
2 | ; continuous
3 ; staccato

; If not specified, defaults are
;
; 4 = duration
; 5 = scale
; 6 = scale
; 63 = beats-per-minute
; 7 = volume
; 1 = style


<note-command> :=
[<duration>] <note> [<scale>] [<special-duration>] <delimiter>


<duration> :=
"1" | ; Full 1/1 note
"2" | ; 1/2 note
Expand All @@ -77,19 +59,16 @@
"G#" |
"A" |
"A#" |
"B" |
"H"

<scale> :=
"4" | ; Note A is 440Hz
"5" | ; Note A is 880Hz
"6" | ; Note A is 1.76 kHz
"7" ; Note A is 3.52 kHz
"5" | ; Note A is 440Hz
"6" | ; Note A is 880Hz
"7" | ; Note A is 1.76 kHz
"8" ; Note A is 3.52 kHz

<special-duration> :=
"." | ; Dotted note
";" | ; Double dotted note
"&" ; 2/3 length
"." ; Dotted note

<delimiter> := ","

Expand Down
38 changes: 37 additions & 1 deletion static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const ELEMENTS = {
OCTAVE_SELECTOR: $("#octave-selector"),
BPM_SLIDER: $("#bpm-slider"),
DURATION_SELECTOR: $("#duration-selector"),
EXAMPLES_SELECTOR: $("#rtttl-examples-selector"),
BPM_INPUT: $("#bpm-input"),
VOLUME_TEXT: $("#radio-toolbar-slider-volume-text"),
BUTTON: {
Expand Down Expand Up @@ -54,6 +55,25 @@ const GLOBALS = {

/** RTTTL note labels. */
NOTE_LABELS: ["b", "a#", "a", "g#", "g", "f#", "f", "e", "d#", "d", "c#", "c"], // Must match exactly as it appears in frontend table.

/** List of example RTTTL ringtones that can be selected in the example selector. */
RTTTL_EXAMPLES: [
"Black Bear:d=4,o=5,b=180:d#,d#,8g.,16d#,8a#.,16g,d#,d#,8g.,16d#,8a#.,16g,f,8c.,16b4,c,8f.,16d#,8d.,16d#,8c.,16d,8a#4.,16c,8d.,16a#4,d#,d#,8g.,16d#,8a#.,16g,d#,d#,8g.,16d#,8a#.,16g,f,f,f,8g.,16f,d#,g,2d#",
"Batman:d=8,o=5,b=180:d,d,c#,c#,c,c,c#,c#,d,d,c#,c#,c,c,c#,c#,d,d#,c,c#,c,c,c#,c#,f,p,4f",
"Barbie Girl:d=8,o=6,b=125:g#,e,g#,c#6,4a,4p,f#,d#,f#,b,4g#,f#,e,4p,e,c#,4f#,4c#,4p,f#,e,4g#,4f#",
"Bethoven:d=4,o=5,b=160:c,e,c,g,c,c6,8b,8a,8g,8a,8g,8f,8e,8f,8e,8d,c,e,g,e,c6,g.",
"Digimon:d=8,o=5,b=112:c,g,f#,p,16c,16c,g,f#,g,16c,16c,g,f#,g,a#,a#,4p,c,g,f#,p,16c,16c,g,f#,g,16c,16c,g,f#,d#,4c",
"Flintstones:d=8,o=5,b=200:g#,4c#,p,4c#6,a#,4g#,4c#,p,4g#,f#,f,f,f#,g#,4c#,4d#,2f,2p,4g#,4c#,p,4c#6,a#,4g#,4c#,p,4g#,f#,f,f,f#,g#,4c#,4d#,2c#",
"Flute:d=8,o=5,b=160:16a,16g,16a,16a#,c6,c6,c6,c6,c6,c6,c6,c6,4f.,4p,32f,16e,16f,16g,a,a,a,a,a,a,a,a,4d.,4p,16d,16c,16d,16e,f,f,f,c,g,g,g,c,a,f,a,c6,f6,c6,d6,a#,c6,f,a,c6,f6,c6,d6,a#,4c6,4p,4f.,f,4a4,4p,4e,4p,f,g,f,a,a#,a,f,g,f,d,e,d,c#,d,c#,a4,b4,a4,c#,d,c#,e,f,e,f,g,f,a,a#,a,f,g,f,d,e.",
"Funky Town:d=8,o=4,b=125:c6,c6,a#5,c6,p,g5,p,g5,c6,f6,e6,c6,2p,c6,c6,a#5,c6,p,g5,p,g5,c6,f6,e6,c6",
"Halloween:d=8,o=5,b=180:d6,g,g,d6,g,g,d6,g,d#6,g,d6,g,g,d6,g,g,d6,g,d#6,g,c#6,f#,f#,c#6,f#,f#,c#6,f#,d6,f#,c#6,f#,f#,c#6,f#,f#,c#6,f#,d6,f#",
"James Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6",
"Pager:d=8,o=5,b=160:d6,16p,2d6,16p,d6,16p,2d6,16p,d6,16p,2d6.",
"Rugrats:d=8,o=7,b=100:c,d,e,f.,g.,a,p,a,g,f,e.,d.,c,p,c,d,e,d.,c.,b6,p,b6,c,d,c.,b6.,a6,p,c,d,e,f.,g.,a,p,a,g,f,e.,d.,c,p,c,d,e,d.,c.,b6",
"Star Wars:d=8,o=6,b=180:f5,f5,f5,2a#5.,2f.,d#,d,c,2a#.,4f.,d#,d,c,2a#.,4f.,d#,d,d#,2c,4p,f5,f5,f5,2a#5.,2f.,d#,d,c,2a#.,4f.,d#,d,c,2a#.,4f.,d#,d,d#,2c",
"The Simpsons:d=4,o=5,b=160:2c6,e6,f#6,8a6,g6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#,8c6,8c6,8c6,c6",
"Trim Phone:d=16,o=5,b=355:a,b,a,b,a,b,a,4p,a,b,a,b,a,b,a,b,a.",
],
};

/*
Expand Down Expand Up @@ -252,6 +272,12 @@ function createComposerTable() {
${createDropdownRow(dropdownOptions.octave)}
</tbody>
`);

// Load all example RTTTL strings into the examples selector.
GLOBALS.RTTTL_EXAMPLES.forEach((example) => {
const toneName = example.split(":")[0];
ELEMENTS.EXAMPLES_SELECTOR.append(`<option value="${example}">${toneName}</option>`);
});
}

/**
Expand All @@ -261,6 +287,9 @@ function resetComposerTable() {
ELEMENTS.COMPOSER_TABLE.find(".tone-enabled").removeClass("tone-enabled"); // Clear all enabled note cells.
if (RTTTL.AudioPlayer.isPlaying()) RTTTL.AudioPlayer.stop(); // If playback is active, stop it.

// Clear all existing composed notes.
COMPOSED_NOTES.length = 0;

$('[id^="duration-note-"]').prop("selectedIndex", 0); // Reset all note duration dropdowns.
$('[id^="octave-note-"]').prop("selectedIndex", 0); // Reset all note octave dropdowns.
}
Expand Down Expand Up @@ -469,7 +498,7 @@ function toggleNote(noteCell, skipGenerate = false) {
const row = +noteCell.attr("row"); // Vertical position (0-11).
const isEnabled = noteCell.hasClass("tone-enabled");

// If the note is being disabled.
// If the note is being enabled.
if (isEnabled) {
const note = GLOBALS.NOTE_LABELS[row];
const duration = +$(`#duration-note-${column}`).val();
Expand Down Expand Up @@ -547,6 +576,13 @@ $(() => {
ELEMENTS.VOLUME_TEXT.text(`${newVolume}%`);
});

// RTTTL Example selector.
ELEMENTS.EXAMPLES_SELECTOR.on("change", function () {
console.debug(`Loading example RTTTL ringtone: '${$(this)?.val().split(":")[0]}'`);
ELEMENTS.RTTL_OUTPUT_BOX.val($(this).val());
loadFromTextArea();
});

/*========================================================================
# Keybind Event Listeners #
========================================================================*/
Expand Down

0 comments on commit 7c4d1e0

Please sign in to comment.