jrosell's picture
update
52ac474
# remotes::install_github("jrosell/ambhtmx", force = TRUE)
library(ambhtmx)
page_title <- "ambhtmx tonejs example"
head_tags <- htmltools::tagList(
tags$link(href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css", rel = "stylesheet", integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH", crossorigin="anonymous"),
tags$script(src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js", integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz", crossorigin="anonymous"),
htmltools::HTML('<script src="https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js"></script><script src="https://cdn.jsdelivr.net/gh/gnat/css-scope-inline@main/script.js"></script>'),
htmltools::HTML('<script src="//unpkg.com/tone"></script>'),
htmltools::HTML('<style>input[type="radio"] {margin: 0 5px 0 20px}</style>')
)
amb_tonejs <- \(
id,
button_text,
pitch_name,
pitch_octave,
duration_value,
duration_unit,
duration_dotted = ""
) {
pitch <- paste0(pitch_name, pitch_octave, collapse = "")
dotted <- if (!is.null(duration_dotted)) { "." } else { "" }
duration <- paste0(duration_value, duration_unit, dotted, collapse = "")
script_chr <- paste0('
document.querySelector("#', id, '")?.addEventListener("click", async () => {
const synth = new Tone.Synth().toDestination();
synth.triggerAttackRelease("', pitch, '", "', duration, '");
await Tone.start();
console.log("audio is ready");
});',
collapse = ""
)
div(
class = paste0("amb_tonejs ", id),
style = "margin : 10px; float: left;",
button(id = id, button_text),
script(HTML(script_chr)),
a("x", style = "color: red; text-decoration: none", href = "#", hx_delete="/delete", hx_confirm = glue('Are you sure you want to delete "{button_text}"?'), hx_swap = "outerHTML", hx_target = glue(".{id}"))
)
}
#' Starting the app and defining the routes
app <- ambhtmx(host = "0.0.0.0", port = "7860")$app$
get("/", \(req, res){
div(
id = "page", style = "margin: 20px", h1(page_title),
div(id = "main",
div(
style ="margin-bottom: 10px",
span("Pitch name", div(
label(input(name = "pitch_name", type = "radio", value = "A", "A", checked = "checked")),
label(input(name = "pitch_name", type = "radio", value = "B", "B")),
label(input(name = "pitch_name", type = "radio", value = "C", "C")),
label(input(name = "pitch_name", type = "radio", value = "D", "D")),
label(input(name = "pitch_name", type = "radio", value = "E", "E")),
label(input(name = "pitch_name", type = "radio", value = "F", "F"))
))
),
div(
style ="margin-bottom: 10px",
label("Pitch octave", div(
input(name = "pitch_octave", type = "range", min = 0, max = 9, value = 4)
))
),
div(
style ="margin-bottom: 10px",
label("Duration value", div(
input(name = "duration_value", type = "range", min = 0.25, max = 8, step = 0.25, value = 0.25)
))
),
div(
style ="margin-bottom: 10px",
span("Duration unit", div(
label(input(name = "duration_unit", type = "radio", value = "n", "note", checked = "checked")),
label(input(name = "duration_unit", type = "radio", value = "t", "triplet")),
label(input(name = "duration_unit", type = "radio", value = "m", "measures"))
))
),
div(
style ="margin-bottom: 10px",
label(
input(name = "duration_dotted", type = "checkbox", value = "."),
"Duration dotted",
)
),
div(
style = "margin: 10px",
button(
"Add",
style = "margin-right: 20px",
hx_post = "/add",
hx_include = '[name="pitch_name"], [name="pitch_octave"], [name="duration_value"],[name="duration_unit"], [name="duration_dotted"]',
hx_vals = 'js:{id: document.querySelectorAll(".amb_tonejs").length + 1}',
hx_target = "#list",
hx_swap = "beforeend"
),
button(
"Reset",
hx_delete = "/delete",
hx_target = "#list",
hx_swap = "innerHTML",
hx_confirm = glue('Are you sure you want to reset all the notes?')
)
),
div(
id = "list",
amb_tonejs(
id = "amb_tonejs_1",
"Play c4 for 4n",
pitch_name = 'c',
pitch_octave = '4',
duration_value = 4,
duration_unit = 'n'
),
amb_tonejs(
id = "amb_tonejs_2",
"Play c5 for 8n",
pitch_name = 'c',
pitch_octave = '5',
duration_value = 8,
duration_unit = 'n'
)
)
)
) |>
send_page(res)
})$
post("/add", \(req, res) {
tryCatch(
{
params <- parse_multipart(req)
print(params)
amb_tonejs_id <- paste0("amb_tonejs_", params$id, collapse = "")
pitch <- paste0(params$pitch_name, params$pitch_octave, collapse = "")
dotted <- if (!is.null(params$duration_dotted)) { "." } else { "" }
duration <- paste0(params$duration_value, params$duration_unit, dotted, collapse = "")
text <- glue("Play {pitch} for {duration}")
html_tags <- amb_tonejs(
id = amb_tonejs_id,
glue("Play {pitch} for {duration}"),
pitch_name = params$pitch_name,
pitch_octave = params$pitch_octave,
duration_value = params$duration_value,
duration_unit = params$duration_unit,
duration_dotted = params$duration_dotted
) |>
send_tags(res)
},
error = \(e) {
print(e)
res$send("Error")
}
)
})$
delete("/delete", \(req, res) {
res$send("")
})
app$start(open = FALSE)