Giter VIP home page Giter VIP logo

howler's Introduction

howler's People

Contributors

ashbaldry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

howler's Issues

Filenames are not always track titles

Using the filenames as track titles doesn't always work well. For example, I have a use case with long URLs with numerical ids as filenames.

It'd be nicer if we could pass track titles alongside the track files. A natural way to do it would be as the names of the files vector - defaulting to the current behaviour if the files are unnamed. This would be analogous to e.g. the choices argument of shiny::selectInput.

Incorrect track names when using a single track

Hi Ashley - I've been testing out the new htmlwidget implementation and it's really good!

There's a small bug with the track name when creating a howler with a single track. Basically the track name doesn't get stored in an array in the JS when there's only one track. This means that track_names[current_track] returns the first letter instead of the first track title! Also it breaks addTrack because track_names.push() causes an error.

This is down to this bit of JS:

        if (Array.isArray(x.tracks)) {
          tracks = x.tracks;
          track_names = x.names;
          if (x.formats) {
            track_formats = x.formats;
          }
        } else if (x.tracks) {
          tracks = [x.tracks];
          track_names = [x.names];
          if (x.formats) {
            track_formats = [x.formats];
          }
        } else {
          tracks = [];
          track_names = [];
        }

It turns out that when there's a single track, the JS receives options like: tracks: Array(1), names: 'test', .... So tracks is an array but names isn't.

I am using jsonlite 1.8.0, maybe this is version-sensitive.

howlerPlayer cannot be added dynamically

Because of the way the howlerPlayers are detected by the JS code when shiny first connects, any player added later (e.g. through UI injection) won't reveive the messages from shiny.

One easy way to fix this would be to add a refreshPlayers() function which would cause the howlerPlayers to be re-detected in JS. One would have to call that function after adding a player dynamically. It's a bit clunky though.

(Side note: I don't actually need that feature so I probably won't address it in my fork.)

Add support to play sound without having to define a UI element

Borrowing from daattali/shinyjs#110

It would be great if the following piece of code:

library(shiny)
library(howler)

ui <- fluidPage(
  howler(tracks = NULL, elementId = "sound"),
  actionButton("play", "Play sound")
)

server <- function(input, output, session) {
  observeEvent(input$play, {
    addTrack("sound", <file_to_add>, play_track = TRUE)
  })
}

shinyApp(ui = ui, server = server)

Could be reduced to the following:

library(shiny)
library(howler)

ui <- fluidPage(
  actionButton("play", "Play sound")
)

server <- function(input, output, session) {
  observeEvent(input$play, {
    playSound(<file_to_add>)
  })
}

shinyApp(ui = ui, server = server)

Is this something you'd support?

Howler cannot handle URLs with query parameters

Thanks for sharing your package (MIT license is โค๏ธ), it's going to be super useful!

I need to make a number of changes to make it work for my use case, which I'll implement in my fork. I'm going to open a few issues just to document what I intend to change.

Currently, HTTP URLs with query parameters are not handled correctly and cannot be played.

These are URLs like:

http://www.example.com/files/audio.mp3?a=1&b=2

Use case: tracks in a private AWS S3 bucket are accessible using presigned URLs which have signature details in such query parameters.

Add ability to delete track

Create a function synonymous to addTrack where the user can delete a track from a howler player from the server-side.

Validation will be needed (possibly some feedback) whether or not the track exists, and if playing should the player stop or skip to next track?

New release for v0.2.1

Thanks a lot for all the changes you've implemented recently - as far as I can tell, everything is working great!

Could you please make a github release (or at least just a tag v0.1.2) to simplify deployment? This way we can install the package from the "known good" ashbaldry/[email protected].

Are you planning a CRAN release soon? It'd be great but I appreciate it's a bit of work - happy to help with it if needed :)

Feature req: server-side function to toggle play

We have a server-side function to play and pause, we could also have one to toggle play.

#' @rdname howlerServer
#' @export
togglePlayHowl <- function(id, session = getDefaultReactiveDomain()) {
  message_name <- paste0("togglePlayHowler_", session$ns(id))
  session$sendCustomMessage(message_name, id)
}
Shiny.addCustomMessageHandler(`togglePlayHowler_${el.id}`, function(id) {
  sound.playing() ? sound.pause() : sound.play();
});

Just for convenience!

renderHowler doesn't work when not initalised in UI

The following doesn't work in {howler}

library(shiny)
library(howler)

audio_files_dir <- system.file("examples/_audio", package = "howler")
addResourcePath("sample_audio", audio_files_dir)
audio_files <- file.path("sample_audio", list.files(audio_files_dir, ".mp3$"))

ui <- fluidPage(
  title = "howler Basic Example",
  h1("howler Basic Example"),
  howlerOutput("howlX"),
  actionButton("button", "Play")
)

server <- function(input, output, session) {
  output$howlX <- renderHowler({
    req(input$button)
    howler::howler(audio_files[1L], options = list(autoplay = TRUE))
  })
}

shinyApp(ui, server)

but including a dummy howler() in the UI allows it to work. Looks like the custom widget tag is causing issues.

Base64 Don't Work in Applications

Trying to use {howler} for Way to stop any previously playing sounds (base64) in javascript before playing next sound and it didn't work, however howler.js does work with a solution. Need to enable this to work.

Example sound:
"data:audio/wav;base64,UklGRhwMAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAZGF0Ya4LAACAgICAgICAgICAgICAgICAgICAgICAgICAf3hxeH+AfXZ1eHx6dnR5fYGFgoOKi42aloubq6GOjI2Op7ythXJ0eYF5aV1AOFFib32HmZSHhpCalIiYi4SRkZaLfnhxaWptb21qaWBea2BRYmZTVmFgWFNXVVVhaGdbYGhZbXh1gXZ1goeIlot1k6yxtKaOkaWhq7KonKCZoaCjoKWuqqmurK6ztrO7tbTAvru/vb68vbW6vLGqsLOfm5yal5KKhoyBeHt2dXBnbmljVlJWUEBBPDw9Mi4zKRwhIBYaGRQcHBURGB0XFxwhGxocJSstMjg6PTc6PUxVV1lWV2JqaXN0coCHhIyPjpOenqWppK6xu72yxMu9us7Pw83Wy9nY29ve6OPr6uvs6ezu6ejk6erm3uPj3dbT1sjBzdDFuMHAt7m1r7W6qaCupJOTkpWPgHqAd3JrbGlnY1peX1hTUk9PTFRKR0RFQkRBRUVEQkdBPjs9Pzo6NT04Njs+PTxAPzo/Ojk6PEA5PUJAQD04PkRCREZLUk1KT1BRUVdXU1VRV1tZV1xgXltcXF9hXl9eY2VmZmlna3J0b3F3eHyBfX+JgIWJiouTlZCTmpybnqSgnqyrqrO3srK2uL2/u7jAwMLFxsfEv8XLzcrIy83JzcrP0s3M0dTP0drY1dPR1dzc19za19XX2dnU1NjU0dXPzdHQy8rMysfGxMLBvLu3ta+sraeioJ2YlI+MioeFfX55cnJsaWVjXVlbVE5RTktHRUVAPDw3NC8uLyknKSIiJiUdHiEeGx4eHRwZHB8cHiAfHh8eHSEhISMoJyMnKisrLCszNy8yOTg9QEJFRUVITVFOTlJVWltaXmNfX2ZqZ21xb3R3eHqAhoeJkZKTlZmhpJ6kqKeur6yxtLW1trW4t6+us7axrbK2tLa6ury7u7u9u7vCwb+/vr7Ev7y9v8G8vby6vru4uLq+tri8ubi5t7W4uLW5uLKxs7G0tLGwt7Wvs7avr7O0tLW4trS4uLO1trW1trm1tLm0r7Kyr66wramsqaKlp52bmpeWl5KQkImEhIB8fXh3eHJrbW5mYGNcWFhUUE1LRENDQUI9ODcxLy8vMCsqLCgoKCgpKScoKCYoKygpKyssLi0sLi0uMDIwMTIuLzQ0Njg4Njc8ODlBQ0A/RUdGSU5RUVFUV1pdXWFjZGdpbG1vcXJ2eXh6fICAgIWIio2OkJGSlJWanJqbnZ2cn6Kkp6enq62srbCysrO1uLy4uL+/vL7CwMHAvb/Cvbq9vLm5uba2t7Sysq+urqyqqaalpqShoJ+enZuamZqXlZWTkpGSkpCNjpCMioqLioiHhoeGhYSGg4GDhoKDg4GBg4GBgoGBgoOChISChISChIWDg4WEgoSEgYODgYGCgYGAgICAgX99f398fX18e3p6e3t7enp7fHx4e3x6e3x7fHx9fX59fn1+fX19fH19fnx9fn19fX18fHx7fHx6fH18fXx8fHx7fH1+fXx+f319fn19fn1+gH9+f4B/fn+AgICAgH+AgICAgIGAgICAgH9+f4B+f35+fn58e3t8e3p5eXh4d3Z1dHRzcXBvb21sbmxqaWhlZmVjYmFfX2BfXV1cXFxaWVlaWVlYV1hYV1hYWVhZWFlaWllbXFpbXV5fX15fYWJhYmNiYWJhYWJjZGVmZ2hqbG1ub3Fxc3V3dnd6e3t8e3x+f3+AgICAgoGBgoKDhISFh4aHiYqKi4uMjYyOj4+QkZKUlZWXmJmbm52enqCioqSlpqeoqaqrrK2ur7CxsrGys7O0tbW2tba3t7i3uLe4t7a3t7i3tre2tba1tLSzsrKysbCvrq2sq6qop6alo6OioJ+dnJqZmJeWlJKSkI+OjoyLioiIh4WEg4GBgH9+fXt6eXh3d3V0c3JxcG9ubWxsamppaWhnZmVlZGRjYmNiYWBhYGBfYF9fXl5fXl1dXVxdXF1dXF1cXF1cXF1dXV5dXV5fXl9eX19gYGFgYWJhYmFiY2NiY2RjZGNkZWRlZGVmZmVmZmVmZ2dmZ2hnaGhnaGloZ2hpaWhpamlqaWpqa2pra2xtbGxtbm1ubm5vcG9wcXBxcnFycnN0c3N0dXV2d3d4eHh5ent6e3x9fn5/f4CAgIGCg4SEhYaGh4iIiYqLi4uMjY2Oj5CQkZGSk5OUlJWWlpeYl5iZmZqbm5ybnJ2cnZ6en56fn6ChoKChoqGio6KjpKOko6SjpKWkpaSkpKSlpKWkpaSlpKSlpKOkpKOko6KioaKhoaCfoJ+enp2dnJybmpmZmJeXlpWUk5STkZGQj4+OjYyLioqJh4eGhYSEgoKBgIB/fn59fHt7enl5eHd3dnZ1dHRzc3JycXBxcG9vbm5tbWxrbGxraWppaWhpaGdnZ2dmZ2ZlZmVmZWRlZGVkY2RjZGNkZGRkZGRkZGRkZGRjZGRkY2RjZGNkZWRlZGVmZWZmZ2ZnZ2doaWhpaWpra2xsbW5tbm9ub29wcXFycnNzdHV1dXZ2d3d4eXl6enp7fHx9fX5+f4CAgIGAgYGCgoOEhISFhoWGhoeIh4iJiImKiYqLiouLjI2MjI2OjY6Pj46PkI+QkZCRkJGQkZGSkZKRkpGSkZGRkZKRkpKRkpGSkZKRkpGSkZKRkpGSkZCRkZCRkI+Qj5CPkI+Pjo+OjY6Njo2MjYyLjIuMi4qLioqJiomJiImIh4iHh4aHhoaFhoWFhIWEg4SDg4KDgoKBgoGAgYCBgICAgICAf4CAf39+f35/fn1+fX59fHx9fH18e3x7fHt6e3p7ent6e3p5enl6enl6eXp5eXl4eXh5eHl4eXh5eHl4eXh5eHh3eHh4d3h4d3h3d3h4d3l4eHd4d3h3eHd4d3h3eHh4eXh5eHl4eHl4eXh5enl6eXp5enl6eXp5ent6ent6e3x7fHx9fH18fX19fn1+fX5/fn9+f4B/gH+Af4CAgICAgIGAgYCBgoGCgYKCgoKDgoOEg4OEg4SFhIWEhYSFhoWGhYaHhoeHhoeGh4iHiIiHiImIiImKiYqJiYqJiouKi4qLiouKi4qLiouKi4qLiouKi4qLi4qLiouKi4qLiomJiomIiYiJiImIh4iIh4iHhoeGhYWGhYaFhIWEg4OEg4KDgoOCgYKBgIGAgICAgH+Af39+f359fn18fX19fHx8e3t6e3p7enl6eXp5enl6enl5eXh5eHh5eHl4eXh5eHl4eHd5eHd3eHl4d3h3eHd4d3h3eHh4d3h4d3h3d3h5eHl4eXh5eHl5eXp5enl6eXp7ent6e3p7e3t7fHt8e3x8fHx9fH1+fX59fn9+f35/gH+AgICAgICAgYGAgYKBgoGCgoKDgoOEg4SEhIWFhIWFhoWGhYaGhoaHhoeGh4aHhoeIh4iHiIeHiIeIh4iHiIeIiIiHiIeIh4iHiIiHiIeIh4iHiIeIh4eIh4eIh4aHh4aHhoeGh4aHhoWGhYaFhoWFhIWEhYSFhIWEhISDhIOEg4OCg4OCg4KDgYKCgYKCgYCBgIGAgYCBgICAgICAgICAf4B/f4B/gH+Af35/fn9+f35/fn1+fn19fn1+fX59fn19fX19fH18fXx9fH18fXx9fH18fXx8fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x8e3x7fHt8e3x7fHx8fXx9fH18fX5+fX59fn9+f35+f35/gH+Af4B/gICAgICAgICAgICAgYCBgIGAgIGAgYGBgoGCgYKBgoGCgYKBgoGCgoKDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KCgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGBgYCBgIGAgYCBgIGAgYCBgIGAgYCBgIGAgYCBgIGAgYCAgICBgIGAgYCBgIGAgYCBgIGAgYCBgExJU1RCAAAASU5GT0lDUkQMAAAAMjAwOC0wOS0yMQAASUVORwMAAAAgAAABSVNGVBYAAABTb255IFNvdW5kIEZvcmdlIDguMAAA"

Adjust playback speed outside of `tags$audio`

First of all, great package! It's been quite helpful for understanding how to manipulate the playback of audio files.

I noticed that the tags$audio option had the additional controls of downloading the audio and altering the playback speed:

audio$tags Default Preview audio$tags Options
image image

I was looking into modifying the widget to access these controls, but just wanted to reach out and see if you had any plans to add the playback speed feature to the main howler widget?

Create empty howlerPlayer

In various cases, it'd be useful to create a player with an empty playlist. In my use case, the playlist is constructed later and I have no tracks when the app starts.

One option might be to create the player later, once the first track comes in, but this is is not nice for 2 reasons:

  • I'd rather have the UI of the player all along, even with no tracks
  • It wouldn't work anyway, given that the JS detects the howlerPlayers only once when shiny connects

For these reasons, we should be able to create a howlerPlayer without files (files = NULL or files = list() or files = character(0)).

It might not be straightforward since we can't create a howl without a track, so effectively the player element of the howlerPlayer would have to be empty, and that is sure to cause some issues with play, plause, etc...

In the meantime, I'm using files = " " (not an empty string as that wouldn't work) to get the player started with a dummy track and "invisible" title and that works ok. This empty track stays in the playlist though which is not ideal.

Player Restarts from Beginning After Pause/Start

Only seems to happen before changing tracks, unable to find why it returns to the start.

Simple reprex:

library(shiny)
library(howler)

howler_example_dir <- system.file("examples/www/audio", package = "howler")
addResourcePath("audio_files", howler_example_dir)

howler_example_file <- list.files(howler_example_dir, ".mp3$")[1]

ui <- fluidPage(
  useHowlerJS(),
  howlerPlayer("sound", file.path("audio_files", howler_example_file)),
  playPauseButton("sound"),
  textOutput("seek")
)

server <- function(input, output, session) { 
  output$seek <- renderText(input$sound_seek)
}

shinyApp(ui, server)

changeTrack() file argument doesn't match howlerPlayer files

changeTrack() has a peculiar behaviour: it expects a file name rather than a file path.

I don't know if it's a bug or if it was on purpose. Perhaps it's for dealing with the case where multiple files are provided for the same track? But these files probably have different extensions, and the extension is part of the file name so it wouldn't help.

This is a bit strange because the howlerPlayer is constructed with file paths, and the internal playlist is composed of file paths. It also means that changeTrack cannot handle files with the same name in different directories, since we have to strip the directory structure to use it.

Here's an illustration.

library(shiny)
library(howler)

audio_files_dir <- system.file("examples/_audio", package = "howler")
addResourcePath("sample_audio", audio_files_dir)
audio_files <- file.path("sample_audio", list.files(audio_files_dir, ".mp3$"))

ui <- fluidPage(
  useHowlerJS(),

  h3("howler changeTrack example"),

  howlerPlayer("sound", audio_files),
  howlerPlayPauseButton("sound"),
  howlerVolumeSlider("sound"),
  howlerCurrentTrack("sound"),
  tags$br(),

  selectInput("selected_track", label = "Select a track", choices = audio_files)
)

server <- function(input, output, session) {

  observeEvent(input$selected_track, {
    # basename is required here, otherwise it won't work
    changeTrack(session, "sound", basename(input$selected_track))
  })
}

shinyApp(ui, server)

The example raises an interesting question: what should be the unique identifier of a track? Using the full file path to the audio file makes sense, but it's not necessarily user friendly - and there's always the case where multiple audio files are provided for the same track...

Perhaps changeTrack could take either a file (full file path, matching the internal playlist) or an index (1-based in R, converted to 0-based in JS) so it's easy to move through a playlist.

`add_track` doesn't work if track has a name

Hey Ash - I'm back :)

Adding a track with a name doesn't work because Howl expects a string URL and not an object with a name.

We just need to strip the name in R when calling addHolwerTrack_id.

Currently the call is session$sendCustomMessage(message_name, list(track = track, track_name = track_name, play = play_track)) where track is a named string.

Simply adding unname (and as.list for good measure) fixed the problem:

addTrack <- function(id, track, play_track = FALSE, session = getDefaultReactiveDomain()) {
  if (is.null(names(track))) {
    track_name <- sub("\\.[^\\.]+$", "", basename(track[1]))
  } else {
    track_name <- names(track)
  }

  message_name <- paste0("addHowlerTrack_", session$ns(id))
  track_info <- list(track = as.list(unname(track)),
                     track_name = as.list(track_name),
                     play = play_track)
  session$sendCustomMessage(message_name, track_info)
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.