【问题标题】:Efficiently split a large audio file in R在 R 中有效地分割一个大的音频文件
【发布时间】:2013-12-20 23:22:53
【问题描述】:


之前我问过this question on SO about splitting an audio file。我从@Jean V. Adams 那里得到的答案相对来说(缺点:输入是立体声,输出是单声道,而不是立体声)对于小型声音对象来说效果很好:

library(seewave)

# your audio file (using example file from seewave package)
data(tico)
audio <- tico # this is an S4 class object
# the frequency of your audio file
freq <- 22050
# the length and duration of your audio file
totlen <- length(audio)
totsec <- totlen/freq

# the duration that you want to chop the file into
seglen <- 0.5

# defining the break points
breaks <- unique(c(seq(0, totsec, seglen), totsec))
index <- 1:(length(breaks)-1)
# a list of all the segments
subsamps <- lapply(index, function(i) cutw(audio, f=freq, from=breaks[i], to=breaks[i+1]))

我将此解决方案应用于我正在准备分析的文件中的一个(大约 300 个)(约 150 MB),我的计算机在它上面工作了(> 5 个小时),但我最终关闭了会议结束前。

有没有人有任何想法或解决方案来有效地使用 R 将大型音频文件(特别是 S4 类 Wave 对象)拆分成更小的部分?我希望大大减少从这些大文件中制作小文件所需的时间,并且我希望使用 R。但是,如果我不能让 R 有效地完成任务,我将不胜感激这项工作的其他工具。上面的示例数据是单声道的,但我的数据是立体声的。示例数据可以使用:

tico@stereo <- TRUE
tico@right <- tico@left

更新

我在第一个解决方案的基础上确定了另一个解决方案:

lapply(index, function(i) audio[(breaks[i]*freq):(breaks[i+1]*freq)])

比较三种解决方案的性能:

# Solution suggested by @Jean V. Adams
system.time(replicate(100,lapply(index, function(i) cutw(audio, f=freq, from=breaks[i], to=breaks[i+1], output="Wave"))))
user  system elapsed 
1.19    0.00    1.19 
# my modification of the previous solution
system.time(replicate(100,lapply(index, function(i) audio[(breaks[i]*freq):(breaks[i+1]*freq)])))
user  system elapsed 
0.86    0.00    0.85 

# solution suggested by @CarlWitthoft 
audiomod <- audio[(freq*breaks[1]):(freq*breaks[length(breaks)-1])] # remove unequal part at end
system.time(replicate(100,matrix(audiomod@left,ncol=length(breaks))))+
system.time(replicate(100,matrix(audiomod@right,ncol=length(breaks))))
user  system elapsed 
0.25    0.00    0.26 

使用索引的方法(即[)似乎更快(3-4x)。 @CarlWitthoft 的解决方案更快,缺点是它将数据放入矩阵而不是多个 Wave 对象,我将使用 writeWave 保存。据推测,如果我正确理解如何创建这种类型的 S4 对象,从矩阵格式转换为单独的 Wave 对象将相对简单。有没有改进的余地?

【问题讨论】:

  • 也许 foo&lt;-matrix(audio,ncol=X) 其中 X 是每个 breaks[i]*freq:breaks[i+1]*freq 的长度。然后矩阵的每一行都是您的样本之一。这确实取决于具有相同长度的样本。
  • @CarlWitthoft 我尝试了这种方法。 audio 是一个 S4 对象。所以,我收到了这个错误Error in as.vector(data) : no method for coercing this S4 class to a vector。我几乎没有使用 S4 对象的经验,这意味着我有一些阅读工作要做。我猜我可能必须分别使用 audio@leftaudio@right 才能使用您建议的方法。
  • 是的,听起来(对不起!)是对的。从audio槽中拉出向量数据。
  • @CarlWitthoft 您的解决方案明显更快,但这确实意味着我需要使用生成的矩阵来创建新的Wave 对象,我之前没有将其指定为所需的输出。如果您想将其发布为答案,我很乐意至少给它一个 +1。
  • @PaulHiemstra 感谢您的建议。我最终发布了我实际用于当前问题的方法。只是试图添加到该网站,我希望我不会惹恼任何羽毛。关于元堆栈溢出的礼仪有很多争论,我尽量不做任何可疑的事情,但如果我做错了什么/没有帮助,请告诉我。

标签: r performance audio file-io split


【解决方案1】:

我最终使用的方法基于@CarlWitthoft 和@JeanV.Adams 提供的解决方案。与我使用的其他技术相比,它的速度相当快,而且它允许我在几小时而不是几天内拆分大量文件。

以下是使用小 Wave 对象的整个过程(我当前的音频文件大小可达 150 MB,但将来我可能会收到更大的文件(即涵盖 12-24 小时录音的声音文件) ) 内存管理将变得更加重要):

library(seewave)
library(tuneR)

data(tico)

# force to stereo
tico@stereo <- TRUE
tico@right <- tico@left    
audio <- tico # this is an S4 class object


# the frequency of your audio file
freq <- 22050
# the length and duration of your audio file
totlen <- length(audio)
totsec <- totlen/freq 

# the duration that you want to chop the file into (in seconds)
seglen <- 0.5

# defining the break points
breaks <- unique(c(seq(0, totsec, seglen), totsec))
index <- 1:(length(breaks)-1)

# the split
leftmat<-matrix(audio@left, ncol=(length(breaks)-2), nrow=seglen*freq) 
rightmat<-matrix(audio@right, ncol=(length(breaks)-2), nrow=seglen*freq)
# the warnings are nothing to worry about here... 

# convert to list of Wave objects.
subsamps0409_180629 <- lapply(1:ncol(leftmat), function(x)Wave(left=leftmat[,x],
         right=rightmat[,x], samp.rate=d@samp.rate,bit=d@bit)) 


# get the last part of the audio file.  the part that is < seglen
lastbitleft <- d@left[(breaks[length(breaks)-1]*freq):length(d)]
lastbitright <- d@right[(breaks[length(breaks)-1]*freq):length(d)]

# convert and add the last bit to the list of Wave objects
subsamps0409_180629[[length(subsamps0409_180629)+1]] <- 
     Wave(left=lastbitleft, right=lastbitright, samp.rate=d@samp.rate, bit=d@bit)

这不是我最初的问题的一部分,但我的最终目标是保存这些新的、更小的 Wave 对象。

# finally, save the Wave objects
setwd("C:/Users/Whatever/Wave_object_folder")

# I had some memory management issues on my computer when doing this
# process with large (~ 130-150 MB) audio files so I used rm() and gc(),
# which seemed to resolve the problems I had with allocating memory.
rm("breaks","audio","freq","index","lastbitleft","lastbitright","leftmat",
  "rightmat","seglen","totlen","totsec")

gc()

filenames <- paste("audio","_split",1:(length(breaks)-1),".wav",sep="")

# Save the files
sapply(1:length(subsamps0409_180629),
       function(x)writeWave(subsamps0409_180629[[x]], 
       filename=filenames[x]))

这里唯一真正的缺点是输出文件非常大。例如,我放入一个 130 MB 的文件并将其拆分为 18 个文件,每个文件大约 50 MB。我认为这是因为我的输入文件是 .mp3 而输出是 .wav。我将这个答案发布到我自己的问题中,以便用我用来解决它的完整解决方案来结束我遇到的问题,但其他答案值得赞赏,我将花时间查看每个解决方案并评估它们提供的内容。我确信有更好的方法来完成这项任务,以及可以更好地处理非常大的音频文件的方法。在解决这个问题时,我几乎没有触及到处理内存管理的皮毛。

【讨论】:

    【解决方案2】:

    根据 Frank 的要求,这是一种可能的方法。 提取 audio@leftaudio@right 插槽的声音数据向量,然后一步将它们分成等长的部分,如下所示:

    leftsong<-audio@left
    leftmat<-matrix(leftsong, ncol=(seglen*freq)
    

    我假设 seglenbreaks[i]breaks[i+1] 之间的距离。 然后可以从leftmatrightmat 中的匹配行创建和处理新的wave 对象。

    【讨论】:

    • 感谢您的解决方案。我在这个想法上应用了一个变体来解决我的问题(请参阅我发布的答案),并且它在完全合理的时间内起作用。我没有接受我自己的答案,因为我不确定这方面的礼仪,而且我现在不能接受你的答案,因为代码需要一些工作。我尝试编辑不完整的行,但我的编辑未被接受。 leftmat 行需要填写。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 1970-01-01
    • 2011-08-04
    • 2018-01-13
    • 1970-01-01
    相关资源
    最近更新 更多