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:
-
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 andreactive
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. - 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,