在R中有效地分割大型音频文件

时间:2013-12-20 23:22:54

标签: r performance audio file-io split


以前我问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对象将是相对微不足道的。还有进一步改进的空间吗?

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:2)

根据弗兰克的要求,这是一种可行的方法。 提取声音数据的audio@leftaudio@right槽'向量,然后在一步中将每个向上分成等长的部分,如:

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

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