1

as a part of a shiny project I am working on, I need to play certain mp3 at specified times after the user presses an action button.

below is a simplified example. The app displays the current system time (and updates it as a clock), displays when is the specified first time point is (target1) and how much time is left till that. same for time point 2. For simplicity, the difference between these time points is 30 seconds.

In the last 2 pieces of code, I am trying to let shiny play a specified audio file when system time is equal to t1, then play another audio file when time reaches t2. But this part is not working.

do you have any suggestions?

thank you

An update:

looks like the action button here is causing some unnecessary complexity so I changed from observeEvent to just observe what I need now is just to make the audio files play when the t1 and t2 points are reached. This is the updated code based on this modifications and on the suggestions below but it is still not working (ie, I do not hear the sound playing when the timer reaches t1 or t2.

updated code:



library(shiny)



# Define UI for displaying current time ----
ui <- fluidPage(
  
  h2(textOutput("currentTime")),
  br(),
  br(),
  h2(textOutput("target1")),
  h2(textOutput("till1")), 
  br(),
  br(),
  h2(textOutput("target2")), 
  h2(textOutput("till2")), 
  uiOutput('my_audio1'), 
  uiOutput('my_audio2') 
)



server <- function(input, output, session) {
  
  
  
  output$currentTime <- renderText({
    invalidateLater(1000, session)
    paste("The current time is", Sys.time())
  })
  
  t1 <- isolate({
    Sys.time() + 5
  })
  
  t2 <- isolate({
    Sys.time() + 10
  })
  
  
  output$target1 <- renderText({
    
    paste("Target:1", t1)
  })
  
  output$target2 <- renderText({
    
    paste("Target:2", t2)
  })
  
  output$till1 <- renderText({
    invalidateLater(1000, session)
    
    paste("Seconds till target 1:", t1-Sys.time())
  })
  
  output$till2 <- renderText({
    invalidateLater(1000, session)
    
    paste("Seconds till target 2:", t2-Sys.time())
  })
  
  
  
  observe({
    if(Sys.time() >= t1 & Sys.time() <= t2) {
      
      output$my_audio1 <-renderUI(tags$audio(src = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3", type = "audio/mp3", autoplay = NA, controls = NA, style="display:none;"))
      
    }
  })
  
  
  observe({
    if(Sys.time() >= t2){
      
      output$my_audio2 <-renderUI(tags$audio(src = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-7.mp3", type = "audio/mp3", autoplay = NA, controls = NA, style="display:none;"))
      
    }
  })
  
  
}

# Create Shiny app ----
shinyApp(ui, server)

Previous code

library(shiny)
 


# Define UI for displaying current time ----
ui <- fluidPage(
  
  h2(textOutput("currentTime")),
  br(),
  br(),
  h2(textOutput("target1")),
  h2(textOutput("till1")), 
  br(),
  br(),
  h2(textOutput("target2")), 
  h2(textOutput("till2")), 
  
  actionButton("play", "Start"), 
  br(),
  br()
  
)


 
server <- function(input, output, session) {
  
  
 
  output$currentTime <- renderText({
    invalidateLater(1000, session)
    paste("The current time is", Sys.time())
  })
  
  t1 <- isolate({
    Sys.time() + 30
  })
  
  t2 <- isolate({
    Sys.time() + 60
  })
  
    
  output$target1 <- renderText({
 
      paste("Target:1", t1)
    })
  
  output$target2 <- renderText({
 
      paste("Target:2", t2)
    })
  
  output$till1 <- renderText({
    invalidateLater(1000, session)
 
    paste("Seconds till target 1:", t1-Sys.time())
  })
   
  output$till2 <- renderText({
    invalidateLater(1000, session)
 
    paste("Seconds till target 2:", t2-Sys.time())
  })
  
 
  
  observeEvent(input$play, {
    if(Sys.time() == t1){
      
      insertUI(selector = "#play",
               where = "afterEnd",
               ui = tags$audio(src = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3", type = "audio/mp3", autoplay = NA, controls = NA, style="display:none;")  
      ) 
    }
  })
  
  
  observeEvent(input$play, {
    if(Sys.time() == t2){
      
      insertUI(selector = "#play",
               where = "afterEnd",
               ui = tags$audio(src = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-7.mp3", type = "audio/mp3", autoplay = NA, controls = NA, style="display:none;")  
      ) 
    }
  })
  
  
}

# Create Shiny app ----
shinyApp(ui, server)
Bahi8482
  • 489
  • 5
  • 15
  • 1
    I would try to use the `delay` function of the 'shinyjs' package. – Stéphane Laurent Apr 24 '21 at 13:29
  • @StéphaneLaurent appreciate the guidance. will give it a try and post an update. – Bahi8482 Apr 24 '21 at 18:00
  • @StéphaneLaurent I tried but I can not specify a certain time point (eg 12:30:00). the function only supports using msecs. I tried to use some math (eg substract) but did not work. do you have any additional guidance? there is not many examples online for the delay function. thank you – Bahi8482 Apr 24 '21 at 20:16

1 Answers1

1

I think the problem is with Sys.time() == t1

If you notice your countdowns never hit 0 (which is when Sys.time() would be equal to t1) and so the button is hard to click at precisely the right moment.

Not sure what your goal is, but you might fix it with Sys.time() >= t1 which would let you play the audio after the time had passed.

You could do Sys.time() >= t1 & Sys.time() <=t2 to only have it available between the two times.

If you want the button clicked close to the "precise" time, you could give a 1 second window with Sys.time() >= t1 - 30 & Sys.time() <= t1 + 30.

You could use any of these in combination with the delay() function in shinyjs as mentioned in the comments for more options.

Matt Watkins
  • 206
  • 1
  • 3
  • Thanks for these suggestions. This makes complete sense but for some reason I still can not make it work. I added another modification in the question stem by removing the `action button` for simplicity. If you can please have a look. I just want the audio to play when `Sys.time()` is equal to `t1` or `t2`. – Bahi8482 Apr 27 '21 at 00:28