Need Help with Playing Audio Files and Exporting CSV on shinyapps.io

Hello Community,

I'm having trouble with a project I'm working on with shinyapps.io. In my project, I need to load an audio file, extract some features, and then export a CSV file to be saved in a directory on my local machine. The idea is for users to listen to the audio file, have the app process the audio, and then save the results in a CSV file.

When I run the app locally, it works perfectly fine - I can load the audio file, play it, process the audio, and export the CSV file. But when I deploy the app to shinyapps . io, I'm facing two main issues:

  1. Audio File Playback: I can load the audio file, but it does not play. I'm using the tags$audio function to add the audio control in the UI and reactive to load the audio file. The audio control shows up on the page but no sound is coming out. I confirmed that the audio file is being loaded properly because I can download it from the app and it plays perfectly fine on my local machine.
  2. Exporting CSV File: Another issue is exporting the results as a CSV file. The CSV file should be saved to the user's local machine, but it doesn't seem to work on shinyapps . io.

here the link to the shinyapps . io:
https://ieysimurra.shinyapps.io/Downloads_2304/

Below is a simplified version of the code:

# Note: this code assumes that the input WAV file has a single channel. 
# If the input file has multiple channels, you may need to modify the code 
# to select a specific channel for analysis.
#for testing, use the same audio
## /media/ieysimurra/e8239ce2-7832-440f-8396-da2dee271807/ieysimurra/Documentos/iADS-E/IADS-E sound stimuli (IADS-2 is not included)/Breaking/0793_2.wav
## /media/ieysimurra/e8239ce2-7832-440f-8396-da2dee271807/ieysimurra/SuperCollider/sounds/a11wlk01.wav
#load package
{
  library("shiny")
  library("shinyFiles")
  library("tuneR")
  library("seewave")
  library("soundgen") 
  library("readr")
  library("ggplot2") 
  library("clusterSim") 
  library("zoo") 
  library("psych") 
  library("Hmisc") 
  library("audio")
  library("sound") 
  library("reshape") 
  library("htmlwidgets")
  # load.library <- {c("shiny", "tuneR", "seewave", "tuneR", "soundgen", "readr",
  #                    "ggplot2", "clusterSim", "zoo", "psych", "Hmisc", "audio",
  #                    "tuneR", "sound", "reshape", "htmlwidgets")}
  # new.packages <- load.library[!(load.library %in% installed.packages()[,"Package"])]
  # if(length(new.packages)) install.packages(new.packages)
  # lapply(load.library, require, character.only = TRUE)
} # to read packages (verify missing libraries and install them beforehand)
#Global variables
{
  out <- data.frame()
  wl <- 1024 / 44100 * 1000
  stp <- wl * 1
  # Define specific acoustic features by setting TRUE or FALSE
  spectralMomentum      <- TRUE 
  r_ratio500            <- TRUE
  spectralIrregularity  <- TRUE
  spectralSmoothness    <- TRUE
  RMSEnergy             <- TRUE
  spectralFlatness      <- TRUE
  spectralFluxSeewave   <- FALSE
  spectralFluxPrime     <- TRUE
  spectralFluxCrN       <- TRUE
  spectralFluxLog       <- TRUE
  zcrFunction           <- FALSE
  domFreq               <- FALSE    
  setWavPlayer("aplay")  # Set the WAV file player
  audio_features <- reactiveVal(NULL) # function to store the analyzed data
  export_data <- list() # to store the selected acoustic features' dataframes
}
#Ui structure
{
  ui <- navbarPage(
  title = "Análise de Arquivos de Áudio via descritores de áudio - 0.01",
  
  tabPanel(
    "1 - Audio Input",
    sidebarLayout(
      sidebarPanel(
        fileInput("file", "Select a WAV file"),      
        actionButton("play", "Play")
      ),
      mainPanel(
        tags$audio(id = "audio", controls = NA, style = "height:50px;"),  # HTML audio tag
        plotOutput("plot")
      )
    )
  ),
  tabPanel(
    "2 - Acoustic Descriptors and Plots",
    sidebarLayout(
      sidebarPanel(
        selectInput(
          inputId = "acousticFeatures",
          label = "Choose an audio descriptor:",
          choices = c("Spectral Centroid", "Spectral Standard Deviation", 
                      "Spectral Variance", "Spectral Skewness",
                      "Spectral Kurtosis", "Energy Below 500 Hz",
                      "Spectral Irregularity", "Spectral Smoothness", 
                      "RMS Energy", "Spectral Flatness", 
                      # "Spectral Flux - Seewave", 
                      "Spectral Flux - Prime", 
                      "Spectral Flux - CrN", "Spectral Flux - Log"
                      # "Zero Crossing Rate", "Dominant Frequency"
                      ),
                           multiple = FALSE),
        actionButton("analyze", "Analyze"),
        shinyDirButton('folder', 'Select a folder', 'Please select a folder', FALSE),
        # Export to CSV button
        actionButton("exportBtn", "Export to CSV")
      ),
      mainPanel(
        plotOutput("plotGGPLOT2"),
        textOutput("time_Duration"),
        verbatimTextOutput("summary_stats")
      )
    )
  )
)
}

server <- function(input, output) {
  audio_features <- reactiveVal(NULL)
  audio_handle <- reactiveVal(NULL)
  out <- NULL  # Define 'out' outside the reactive expression

  destination_dir <- reactiveVal(NULL) # Variable to store the selected destination directory
  
  observeEvent(input$folder, {
    shinyDirChoose(input, 'folder', roots=getVolumes()(), filetypes=c('', 'csv'))
    folder_path <- parseDirPath(roots=getVolumes()(), input$folder)
    if (!is.null(input$folder)) {
      destination_dir(dirname(folder_path))
    }
  })
  
  observeEvent(input$file, {
    showNotification("Starting analysis. Please, be patient! :-)")
    
    file <- input$file$datapath
    output$plot <- renderPlot({
      # file <- "/home/ieysimurra/Downloads/a11wlk01.wav"
      signal <- readWave(file)      
      time <- time(signal) / 44100
      spectrogram <- spectrogram(signal, samplingRate = 44100)
      plot(time, signal@left, type="l", xlab="Time (sec)", ylab="Amplitude")
      
      ##perform acoustic descriptors computational analysis
      # 
      # file <- input$file$datapath
      # for test sound without shiny only
      # 
      # 
      all.spgm <<- {
        spectrogram(file, samplingRate = 44100, 
                    windowLength = wl, step = stp, 
                    wn = "hanning", output = "original", 
                    plot = FALSE, osc = FALSE)
      }    
      # calculate the length of the input file (audio duration in seconds)
      time_Duration <<- round(length(signal@left) / signal@samp.rate, 3)
      
      #######################################
      ## run this line to perform audio features calculations
      ### preparing data table
      {
        # run 'out' object to create the feature dataframe 
        out <<- data.frame(row.names = 1:ncol(all.spgm))
        # please run again this block
        {
          if(spectralMomentum == TRUE){
            out$specCentroid = NA
            out$specVariance = NA
            out$specStdDev = NA
            out$specSkewness = NA
            out$specKurtosis = NA
          }
          if(r_ratio500 == TRUE){
            out$ratio500 = NA
          }
          if(spectralIrregularity == TRUE){
            out$irregularity = NA
          }
          if(spectralSmoothness == TRUE){
            out$specSmoothness = NA
          }
          if(RMSEnergy == TRUE){
            out$rms = NA
          }
          if(spectralFlatness == TRUE){
            out$specFlatness = NA
          }
          if(spectralFluxSeewave == TRUE){
            out$spectralFluxSeewave = NA
          }
          if(spectralFluxPrime == TRUE){
            out$specFluxPrime = NA
          }
          if(spectralFluxCrN == TRUE){
            out$specFluxCrN = NA
          }
          if(spectralFluxLog == TRUE){
            out$specFluxLog = NA
          }
          if(zcrFunction == TRUE){
            out$zcrFunction = NA
          }
          if(domFreq == TRUE){
            out$domFreq = NA
          }
        }
        # Run this to perform calculation
        for (i in 1:ncol(all.spgm)) {
          # Absolute spectrum for this frame
          df = data.frame(
            freq = as.numeric(rownames(all.spgm)),  # frequency (kHz)
            d = all.spgm[, i]                       # density
          )
          #####  centroid (see https://en.wikipedia.org/wiki/Central_moment)  
          if(spectralMomentum == TRUE){ 
            # out$specCentroid[i] = sum(df$freq * df$d)
            out$specCentroid[i] = (1 / sum(df$d)) * sum(df$freq * df$d)
            m2 <- sum(out$specCentroid[i])
            # Spread (see https://www.mathworks.com/help/audio/ug/spectral-descriptors.html
            out$specVariance[i] = sum((df$freq - m2)^2 * df$d) / sum(df$d)  
            m3 <- sum(sqrt(out$specVariance[i]))
            #####  Spectral Standard Deviation (https://www.mathworks.com/help/audio/ug/spectral-descriptors.html#SpectralDescriptorsExample-2)
            out$specStdDev[i] = sqrt(sum((df$freq - m2)^2 * df$d) / sum(df$d))
            #####  Skewness (see https://en.wikipedia.org/wiki/Central_moment)   
            out$specSkewness[i] = sum((df$freq - m2)^3 * df$d) / m3^3
            #####  Kurtosis (see https://www.mathworks.com/help/audio/ug/spectral-descriptors.html#SpectralDescriptorsExample-2)
            out$specKurtosis[i] = (sum((df$freq - m2)^4 * df$d) / m3^4) -3
          }
          #####  Energy above/below 500 Hz
          if(r_ratio500 == TRUE){
            out$ratio500[i] = sum(df$d[df$freq >= .5]) / sum(df$d[df$freq < .5])
          }
          #####  Irregularity
          if(spectralIrregularity == TRUE){
            out$irregularity[i] = sum(abs(df$d - rollapply(df$d, 3, sum) / 3))
          }
          #####  Spectral Smoothness
          if(spectralSmoothness == TRUE){
            out$specSmoothness[i] = sum(abs(20 * log(df$d, 10) - rollapply(
              20 * log(df$d, 10), 3, sum) / 3))
          }
          #####  RMS
          if(RMSEnergy == TRUE){  
            out$rms[i] = sqrt(sum(df$d^2) / ncol(all.spgm))
          }
          #####  Flatness
          if(spectralFlatness == TRUE){
            out$specFlatness[i] = geometric.mean(df$d, na.rm=TRUE) / (sum(df$d) / 
                                                                        ncol(all.spgm))
          }
          ### Spectral Flux from Seewave
          # if(spectralFluxSeewave == TRUE){
          #   spectralFluxSeewave<-dfreq(signal, wl = 1024, ovlp = 0.6, 
          #                              wn = "hanning", flim = NULL, norm = FALSE,  
          #                              p = 2, plot = FALSE)
          #   spectralFluxSeewave<-as.data.frame(spectralFluxSeewave[, -1])
          #   for (i in seq_along(specFlux)){colnames(specFlux[[i]]) <- "specFlux"}
          #   out$spectralFluxSeewave <- spectralFluxSeewave
          # }
          # Spectral Flux First Version
          if(spectralFluxPrime == TRUE){
            out$specFluxPrime[i] = sqrt(sum((abs(df$d[i]) - abs(df$d[i-1]))^2))
          }
          # Spectral Flux Correlação normalizada
          if(spectralFluxCrN == TRUE){
            out$specFluxCrN[i] = 1 - sum(abs(df$d[i]) * abs(df$d[i-1])) / sqrt(sum(abs(df$d[i])^2)) * sqrt(sum(abs(df$d[i-1])^2))
          }
          # Spectral Flux Logarítmica
          if(spectralFluxLog == TRUE){
            out$specFluxLog[i] <- sum(rollapply(log(df$d), 2, diff))^2
          }
          # Zero-Crossing Rate
          if(zcrFunction == TRUE){
            zcrFunction <- zcr(signal, wl = 1024, ovlp = 0.6, plot = FALSE)
            zcrFunction <- as.data.frame(zcrFunction[, -1])
            out$zcrFunction <- zcrFunction
          }
          ### Dominant Frequency
          if(domFreq == TRUE){
            domFreq<-dfreq(signal, wl = 1024, ovlp = 0.6, plot = FALSE)
            domFreq<-as.data.frame(domFreq[, -1])
            colnames(domFreq) <- "domFreq"
            out$domFreq <- domFreq
          }
        }
        out$time <- (seq.int(nrow(as.data.frame(out$specCentroid))) * wl) /1000
        out<<-data.frame(out)
        showNotification("Analysis done!")
      } # run this (CTRL + Return)      
    })
    
    observeEvent(input$play, {
      if (!is.null(audio_handle())) {
        stop(audio_handle())
      }
      audio_handle(play(input$file$datapath))
    })
 
})

  observeEvent(input$analyze, {
    # Update the reactive value with the calculated features
    audio_features(out)
    
# Return the requested dataset ----
    datasetInput <- reactive({

        switch(input$acousticFeatures,
               "Spectral Centroid"           = out$specCentroid, 
               "Spectral Variance"           = out$specVariance, 
               "Spectral Standard Deviation" = out$specStdDev, 
               "Spectral Skewness"           = out$specSkewness, 
               "Spectral Kurtosis"           = out$specKurtosis,
               "Energy Below 500 Hz"         = out$ratio500,
               "Spectral Irregularity"       = out$irregularity,
               "Spectral Smoothness"         = out$specSmoothness,
               "RMS Energy"                  = out$rms,
               "Spectral Flatness"           = out$specFlatness,
               # "Spectral Flux - Seewave"     = out$spectralFluxSeewave,
               "Spectral Flux - Prime"       = out$specFluxPrime,
               "Spectral Flux - CrN"         = out$specFluxCrN,
               "Spectral Flux - Log"         = out$specFluxLog
               # "Zero Crossing Rate"          = out$zcrFunction,
               # "Dominant Frequency"          = out$domFreq
        )

    })
    
    # Create ggplot2 time series plot
    output$plotGGPLOT2 <- renderPlot({
      acousticFeatures <- datasetInput()
      if (is.null(acousticFeatures)) {
        return(NULL)
      }
      ggplot(out, aes_string(x = out$time, y = acousticFeatures)) +
        geom_line() +
        #      xlab(input$xvar) +
        #      ylab(input$yvar) +
        ggtitle("Time Series Plot")
    })  
    
    
    #######################################
    #######################################
    # Calculate summary statistics
    summary_stats <- function(feature) {
      mean_val <- mean(feature, na.rm = TRUE)
      sd_val <- sd(feature, na.rm = TRUE)
      skewness_val <- psych::skew(feature, na.rm = TRUE)
      kurtosis_val <- psych::describe(feature, type = 3)$kurtosis
      ci <- Hmisc::wtd.quantile(feature, probs = c(0.025, 0.975), weights = NULL)
      
      c(mean = mean_val, sd = sd_val, skewness = skewness_val, kurtosis = kurtosis_val, ci = ci)
    }
    
    # Select the acoustic feature
    acoustic_feature <- switch(
      input$acousticFeatures,
      "Spectral Centroid"           = out$specCentroid, 
      "Spectral Variance"           = out$specVariance, 
      "Spectral Standard Deviation" = out$specStdDev, 
      "Spectral Skewness"           = out$specSkewness, 
      "Energy Below 500 Hz"         = out$ratio500,      
      "Spectral Kurtosis"           = out$specKurtosis,
      "Spectral Irregularity"       = out$irregularity,
      "Spectral Smoothness"         = out$specSmoothness,
      "RMS Energy"                  = out$rms,
      "Spectral Flatness"           = out$specFlatness,
      # "Spectral Flux - Seewave"     = out$spectralFluxSeewave,
      "Spectral Flux - Prime"       = out$specFluxPrime,
      "Spectral Flux - CrN"         = out$specFluxCrN,
      "Spectral Flux - Log"         = out$specFluxLog
      # "Zero Crossing Rate"          = out$zcrFunction,
      # "Dominant Frequency"          = out$domFreq
    )    
    
    # Compute summary statistics
    summary <- summary_stats(acoustic_feature)
    
    # Print the summary statistics
    output$summary_stats <- renderPrint({
      summary
    })
    
    selectedFeature <- reactive({
      switch(input$acousticFeatures,
             "Spectral Centroid"           = out$specCentroid, 
             "Spectral Variance"           = out$specVariance, 
             "Spectral Standard Deviation" = out$specStdDev, 
             "Spectral Skewness"           = out$specSkewness, 
             "Spectral Kurtosis"           = out$specKurtosis,
             "Energy Below 500 Hz"         = out$ratio500,             
             "Spectral Irregularity"       = out$irregularity,
             "Spectral Smoothness"         = out$specSmoothness,
             "RMS Energy"                  = out$rms,
             "Spectral Flatness"           = out$specFlatness,
             # "Spectral Flux - Seewave"     = out$spectralFluxSeewave,
             "Spectral Flux - Prime"       = out$specFluxPrime,
             "Spectral Flux - CrN"         = out$specFluxCrN,
             "Spectral Flux - Log"         = out$specFluxLog
             # "Zero Crossing Rate"          = out$zcrFunction,
             # "Dominant Frequency"          = out$domFreq
      )
    })
    
    # Add the selected acoustic features' dataframes to the export_data list
    export_data[[input$acousticFeatures]] <<- datasetInput()    
    
    # Export button observer
    observeEvent(input$exportBtn, {
      
      if (!is.null(destination_dir())) {
        # export_data <- audio_features()  # Get the analyzed data
      if (length(export_data) > 0) {
        # Choose a file name for the CSV export
        titlename <- "acoustic_descriptors"
        titlenameM <- "melted_version"        
        # # Create a data frame with time and feature values
        # featureData <- data.frame(time = out$time, value = feature)
        # Append .csv extension to the filename
        # filename <- paste0(dirname(rstudioapi::getSourceEditorContext()$path), "/", titlename, ".csv")
        filename <- file.path(destination_dir(), paste0(titlename, ".csv"))
        filenameM <- file.path(destination_dir(), paste0(titlenameM, ".csv"))
        # filenameM <- paste0(dirname(rstudioapi::getSourceEditorContext()$path), "/", titlenameM, ".csv")        
        # Combine all the dataframes in the export_data list into a single dataframe
        combined_df <- do.call(cbind, export_data)
        # Write the combined dataframe to a CSV file
        write.csv(data.frame(time = out$time, combined_df), file = filename, row.names = FALSE)
        write.csv(melt(combined_df), file = filenameM, row.names = FALSE)        
        cat("Data exported to", filename, "\n")
      } else {
        cat("No data to export.\n")
      }
      }
      
     # Show a confirmation message
      showModal(modalDialog(
        title = "Export Successful",
        "All the files have been exported to ",
        paste0(dirname(rstudioapi::getSourceEditorContext()$path), "/", "."),
        " You can now close this window. Thank You!",
        easyClose = TRUE
      ))      
  
    })
    
    ### to normalize results
    out_norm<-lapply(out, function(x){
      data.Normalization(as.data.frame(x), type="n4", normalization="column", 
                         na.rm = TRUE)})
    #######################################
  })
  
  #######################################
  #######################################
  ### to plot frequency chart from ggplot2  
#  ggplot(out,aes(x=time,y=specCentroid))+
#    geom_line()
  
}

shinyApp(ui, server)

If you have any suggestions on how to resolve these issues, or if you can point me to any resources, it would be greatly appreciated. Thank you in advance for your time and help!

Best,