简体   繁体   中英

Plot blocking in RStudio?

I am trying to execute a function which plots some data then plays some audio (corresponding to that data) in RStudio. However, it seems that the audio playback blocks the plotting. Why is this?

plot.prod.df <- function(df, row) {
  # given a row number and a df, plot the prod df for that trial
  # create data
  print(paste0("plotting production data. trial: ", row))
  if(is.null(df$prod[[row]])){
    stop("Invalid trial")
  } 
  else {
    
    # get relevant production data
    prod.df <- data.frame("onset" = df$prod[[row]]$onset * 1000, 
                          "pitch" = df$prod[[row]]$pitch,
                          "error" = df$prod[[row]]$error_boolean)
    
    prod.df$error <- as.factor(prod.df$error)
    
    # get target notes
    target.notes <- str.mel.to.vector(df$stimuli.pitch[[row]], "-")
    
    # check if there were playbacktimes
    print(df$playback.times[[row]])
    if (any(is.na(df$playback.times[[row]]))) {
      print("no playback times on this trial")
      # plot
      ggplot(prod.df, aes(x=onset, y=pitch, color = error)) +
        theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
              panel.background = element_blank(), axis.line = element_line(colour = "black")) +
        geom_hline(yintercept = target.notes, color = magma.colors[4], size = 2, alpha = 0.7) + 
        geom_point() +
        scale_color_manual(values=c(magma.colors[1], magma.colors[2]))
    }
    else {
      playback.times <-  fromJSON(df$playback.times[[row]])[2:length(fromJSON(df$playback.times[[row]]))] # remove the default original playback
      print(playback.times)
      # plot
      ggplot(prod.df, aes(x=onset, y=pitch, color = error)) +
        theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
              panel.background = element_blank(), axis.line = element_line(colour = "black")) +
        geom_hline(yintercept = target.notes, color = magma.colors[4], size = 2, alpha = 0.7) + 
        geom_vline(xintercept = playback.times, color = magma.colors[5], size = 2, alpha = 0.7) + 
        geom_point() +
        scale_color_manual(values=c(magma.colors[1], magma.colors[2]))
    
    }
    
  }
  
}


# set up WavPlayer
sound::setWavPlayer("./sox/play")


play.seq <- function(midi_notes, length_of_each_note) {
  #print("playing sequence user heard")

  freqs <- midi_to_freq(midi_notes)
  sines <- lapply(freqs, sound::Sine, length_of_each_note)
  
  for (i in seq_along(1:length(sines))) {
    Sys.sleep(length_of_each_note)
    sound::play(cutSampleEnds(sines[[i]])) # cutSampleEnds removes cracks
  }
  
}


play.and.plot.trial <- function(df, row = NULL) {
  
  # if a row isn't specified, randomly select a row
  if (is.null(row)) {
    sampled_trial <- sample(1:nrow(df), 1)
  }
  else {
    sampled_trial <- row
  }
  
  print(paste0("no. trial to plot: ", sampled_trial))

  plot.prod.df(df, sampled_trial)
  play.seq(str.mel.to.vector(df$stimuli.pitch[[sampled_trial]], "-"), 0.25)
  
}

play.and.plot.trial(dat)

I can confirm that if I comment out the following line and run play.and.plot.trial(dat), it plots:

play.seq(str.mel.to.vector(df$stimuli.pitch[[sampled_trial]], "-"), 0.25)

I am not sure why this stops the plotting happening; it only prints to console, I think.

I also tried adding Sys.sleep(5) before the audio playback and this does not help.

TL;DR

print the grob:

play.and.plot.trial <- function(df, row = NULL) {
  # ...
  print(plot.prod.df(df, sampled_trial))
  play.seq(str.mel.to.vector(df$stimuli.pitch[[sampled_trial]], "-"), 0.25)
}

Rationale

Calls to ggplot(...) + geom_*(...) do not actually render into a graphic device. The rendering is done by ggplot2:::print.ggplot . Try this:

gg <- ggplot(mtcars, aes(mpg, disp)) + geom_path()

Notice how it does not plot to the viewing pane? Now, if you type in gg on the console, it renders. This is because R will try to figure out the most appropriate way to "print" this type of object. Using S3 method dispatch in R, it uses class(gg) to look for either print.gg (not found) or print.ggplot (found within the ggplot2 package). Since it finds the latter, it uses that.

(If neither were found, it would use print.default .)

This is also applicable in for loops, where for instance you want to make multiple plots.

The answer in any event is to explicitly render it. Because of the S3 method dispatch, print(gg) will still call ggplot2:::print.ggplot because of how S3 works.

(If you're ever in a situation where you have a gg object that needs rendering but you have not loaded the ggplot2 namespace with library(ggplot2) , you can use ggplot2:::print.ggplot(gg) explicitly. Or you can use it explicitly just because you want your code to be "declarative" and unambiguous, since not knowing how S3 dispatch works might make print(gg) look magical.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM