在R中高效拆分大型音频文件




之前我在SO上问过这个关于拆分音频文件的问题。我从@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对象将相对简单。还有改进的余地吗?

我最终使用的方法是在@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。我发布了我自己问题的答案,以便用我用来解决它的完整解决方案来总结我遇到的问题,但我们很感激其他答案,我会花时间查看每个解决方案,并评估它们提供的内容。我相信有更好的方法来完成这项任务,并且这些方法将更好地处理非常大的音频文件。在解决这个问题时,我几乎没有触及内存管理的表面。

根据Frank的请求,这里有一种可能的方法。提取audio@leftaudio@right时隙的声音数据矢量,然后在一步中将每个时隙分解为相等长度的部分,类似于:

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

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

最新更新