Split an Audio File into Pieces of an Arbitrary Size

Split an audio file into pieces of an arbitrary size

I don't have any experience working with audio files in R, but I was able to come up with an approach that might help you. Check out the code below.

library(seewave)

# your audio file (using example file from seewave package)
data(tico)
audio <- tico
# 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]))

Split audio file into several files, each below a size threshold

There are two parts to your question.

  • Convert existing FLAC audio file to some other format like wav
  • Split converted wav file into chunk of specific size.

Obviously, there are more than one way to do this. However, pydub provides easier methods to accomplish above.
details on pydub documentation can be found here.

1) Convert existing FLAC audio file to some other format like wav

Using pydub you can read FLAC audio format and then convert to wav as below

flac_audio = AudioSegment.from_file("sample.flac", "flac")
flac_audio.export("audio.wav", format="wav")

2) Split converted wav file into chunk of specific size.

Again, there are various ways to do this. The way I did this was to determine total length and size of the converted wavfile and then approximate that to desired chunk size.

The sample wav file used was of 101,612 KB size and about 589 sec or little over 9 minutes.

Wav File size by observation :

Stereo frame_rate 44.1KHz audio files are approximately 10 Mb per a minute. 48K would be a little larger.That means that the corresponding mono file would be 5 megs per minute

The approximation holds good for our sample file with about10 Mb per minute

Wav file size by math:

Co relation between wav file size and duration is given by

wav_file_size_in_bytes = (sample rate (44100) * bit rate (16-bit) * number of channels (2 for stereo) * number of seconds) / 8 (8 bits = 1 byte)

Source : http://manual.audacityteam.org/o/man/digital_audio.html

The formula I used to calculate chunks of audio file:

Get chunk size by following method

for duration_in_sec (X) we get wav_file_size (Y)

So whats duration in sec (K) given file size of 10Mb

This gives K = X * 10Mb / Y

pydub.utils has method make_chunks that can make chunks of specific duration (in milliseconds). We determine duration for desired size using above formula.

We use that to create chunks of 10Mb (or near 10Mb) and export each chunk separately. Last chunk may be smaller depending upon size.

Here is a working code.

from pydub import AudioSegment
#from pydub.utils import mediainfo
from pydub.utils import make_chunks
import math

flac_audio = AudioSegment.from_file("sample.flac", "flac")
flac_audio.export("audio.wav", format="wav")
myaudio = AudioSegment.from_file("audio.wav" , "wav")
channel_count = myaudio.channels #Get channels
sample_width = myaudio.sample_width #Get sample width
duration_in_sec = len(myaudio) / 1000#Length of audio in sec
sample_rate = myaudio.frame_rate

print "sample_width=", sample_width
print "channel_count=", channel_count
print "duration_in_sec=", duration_in_sec
print "frame_rate=", sample_rate
bit_rate =16 #assumption , you can extract from mediainfo("test.wav") dynamically


wav_file_size = (sample_rate * bit_rate * channel_count * duration_in_sec) / 8
print "wav_file_size = ",wav_file_size


file_split_size = 10000000 # 10Mb OR 10, 000, 000 bytes
total_chunks = wav_file_size // file_split_size

#Get chunk size by following method #There are more than one ofcourse
#for duration_in_sec (X) --> wav_file_size (Y)
#So whats duration in sec (K) --> for file size of 10Mb
# K = X * 10Mb / Y

chunk_length_in_sec = math.ceil((duration_in_sec * 10000000 ) /wav_file_size) #in sec
chunk_length_ms = chunk_length_in_sec * 1000
chunks = make_chunks(myaudio, chunk_length_ms)

#Export all of the individual chunks as wav files

for i, chunk in enumerate(chunks):
chunk_name = "chunk{0}.wav".format(i)
print "exporting", chunk_name
chunk.export(chunk_name, format="wav")

Output:

Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
sample_width= 2
channel_count= 2
duration_in_sec= 589
frame_rate= 44100
wav_file_size = 103899600
exporting chunk0.wav
exporting chunk1.wav
exporting chunk2.wav
exporting chunk3.wav
exporting chunk4.wav
exporting chunk5.wav
exporting chunk6.wav
exporting chunk7.wav
exporting chunk8.wav
exporting chunk9.wav
exporting chunk10.wav
>>>

Splitting an Audio File Into Equal-Lenght Segments Using FFmpeg

You can use the segment muxer. Basic example:

ffmpeg -i input.wav -f segment -segment_time 2 output_%03d.wav
  • -f segment indicates that the segment muxer should be used for the output.
  • -segment_time 2 makes each segment 2 seconds long.
  • output_%03d.wav is the output file name pattern which will result iin output_000.wav, output_001.wav, output_002.wav, and so on.

Split audio file into pieces

EDIT

With SOX:

string sox = @"C:\Program Files (x86)\sox-14-4-1\sox.exe";
string inputFile = @"D:\Brothers Vibe - Rainforest.mp3";
string outputDirectory = @"D:\splittest";
string outputPrefix = "split";

int[] segments = { 10, 15, 30 };

IEnumerable<string> enumerable = segments.Select(s => "trim 0 " + s.ToString(CultureInfo.InvariantCulture));
string @join = string.Join(" : newfile : ", enumerable);
string cmdline = string.Format("\"{0}\" \"{1}%1n.wav" + "\" {2}", inputFile,
Path.Combine(outputDirectory, outputPrefix), @join);

var processStartInfo = new ProcessStartInfo(sox, cmdline);
Process start = System.Diagnostics.Process.Start(processStartInfo);

If SOX complains about libmad (for MP3) : copy DLLs next to it, see here

Alternatively you can use FFMPEG in the same manner :

ffmpeg -ss 0 -t 30 -i "Brothers Vibe - Rainforest.mp3" "Brothers Vibe - Rainforest.wav"

(see the docs for all the details)


You can do that easily with BASS.NET :

For the code below you pass in :

  • input file name
  • desired duration for each segment
  • output directory
  • prefix to use for each segment file

The method will check whether the file is long enough for the specified segments, if yes then it will cut the file to WAVs with the same sample rate, channels, bit depth.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Un4seen.Bass;
using Un4seen.Bass.Misc;

namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
if (!Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero))
throw new InvalidOperationException("Couldn't initialize BASS");

string fileName = @"D:\Brothers Vibe - Rainforest.mp3";
var segments = new double[] {30, 15, 20};
string[] splitAudio = SplitAudio(fileName, segments, "output", @"D:\split");
}

private static string[] SplitAudio(string fileName, double[] segments, string prefix, string outputDirectory)
{
if (fileName == null) throw new ArgumentNullException("fileName");
if (segments == null) throw new ArgumentNullException("segments");
if (prefix == null) throw new ArgumentNullException("prefix");
if (outputDirectory == null) throw new ArgumentNullException("outputDirectory");
int i = Bass.BASS_StreamCreateFile(fileName, 0, 0,
BASSFlag.BASS_STREAM_PRESCAN | BASSFlag.BASS_STREAM_DECODE);
if (i == 0)
throw new InvalidOperationException("Couldn't create stream");

double sum = segments.Sum();

long length = Bass.BASS_ChannelGetLength(i);
double seconds = Bass.BASS_ChannelBytes2Seconds(i, length);
if (sum > seconds)
throw new ArgumentOutOfRangeException("segments", "Required segments exceed file duration");
BASS_CHANNELINFO info = Bass.BASS_ChannelGetInfo(i);

if (!Directory.Exists(outputDirectory)) Directory.CreateDirectory(outputDirectory);

int index = 0;
var list = new List<string>();
foreach (double segment in segments)
{
double d = segment;
long seconds2Bytes = Bass.BASS_ChannelSeconds2Bytes(i, d);
var buffer = new byte[seconds2Bytes];
int getData = Bass.BASS_ChannelGetData(i, buffer, buffer.Length);
string name = string.Format("{0}_{1}.wav", prefix, index);
string combine = Path.Combine(outputDirectory, name);
int bitsPerSample = info.Is8bit ? 8 : info.Is32bit ? 32 : 16;
var waveWriter = new WaveWriter(combine, info.chans, info.freq, bitsPerSample, true);
waveWriter.WriteNoConvert(buffer, buffer.Length);
waveWriter.Close();
list.Add(combine);
index++;
}
bool free = Bass.BASS_StreamFree(i);

return list.ToArray();
}
}
}

TODO

The extraction is not optimized, if you are concerned with memory usage, then the function should be enhanced to grab parts of a segments and write them progressively to the WaveWriter.

Notes

BASS.NET has a nag screen, but you can request for a free registration serial at their website.

Note, install BASS.NET then make sure to copy bass.dll from the base package next to your EXE. Also, you can use pretty much any audio formats, see their website for formats plugins and how to load them (BASS_PluginLoad).

Efficiently split a large audio file in R

The approach I ended up using builds off of the solutions offered by @CarlWitthoft and @JeanV.Adams. It is quite fast compared to the other techniques I was using, and it has allowed me to split a large number of my files in a matter of hours, rather than days.

Here is the whole process using a small Wave object for example (my current audio files range up to 150 MB in size, but in the future, I may receive much larger files (i.e. sound files covering 12-24 hours of recording) where memory management will become more important):

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)

This wasn't part of my original question, but my ultimate goal was to save these new, smaller Wave objects.

# 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]))

The only real downside here is that the output files are pretty big. For example, I put in a 130 MB file and split it into 18 files each approximately 50 MB. I think this is because my input file is .mp3 and the output is .wav. I posted this answer to my own question in order to wrap up the problem I was having with the full solution I used to solve it, but other answers are appreciated and I will take the time to look at each solution and evaluate what they offer. I am sure there are better ways to accomplish this task, and methods that will work better with very large audio files. In solving this problem, I barely scratched the surface on dealing with memory management.

Fast way for splitting large .wav file using R

If you're loading all of these into memory at the same time, rather than sequential variable names you should be using a list.

tr1 = list()
duration = 0.5
start_times = seq(0, 2, by = duration)

for (i in seq_along(start_times)) {
tr1[[i]] = readWave('TR1_edit.WAV',
from = start_times[i],
to = start_times[i] + duration,
units = 'minutes')
}

This is the same principle as why you should use a list of data frames rather than sequentially named data frames.

You could easily wrap this into a function that takes the name of a WAV file as input, gets its length from the metadata, and imports it in 30-second (or a parameterized argument) segments, and returns the list of segments.

How to split an audio file into 1 second long chunks of audio files in Matlab?

You can use audioread to read the file in chunks instead of reading the entire file in all at once. The code below might be helpful.

info = audioinfo('handel.wav');

Fs = info.SampleRate;
chunkDuration = 1; % 1 sec
numSamplesPerChunk = chunkDuration*Fs;

chunkCnt = 1;
for startLoc = 1:numSamplesPerChunk:info.TotalSamples
endLoc = min(startLoc + numSamplesPerChunk - 1, info.TotalSamples);

y = audioread('handel.wav', [startLoc endLoc]);
outFileName = sprintf('outfile%03d.wav', chunkCnt);
audiowrite(outFileName, y, Fs);
chunkCnt = chunkCnt + 1;
end

Hope this helps.

Dinesh

How to split a file in java into multiple same size parts using bytestream

You can rewrite your main method as :

public static void main(String[] args) throws IOException {

File file = new File("song.mp3");

FileInputStream fIn = new FileInputStream("song.mp3");
FileOutputStream fOut = new FileOutputStream("song_0.mp3");
int chunk_size = 1024 * 100;
byte[] buff = new byte[chunk_size]; // 100KB file
int i = 0;
String file_name = file.getName();
String file_name_base = file_name.substring(0, file_name.lastIndexOf("."));
while (fIn.read() != -1) {

fIn.read(buff);
int total_read = 0;
total_read += chunk_size;
long read_next_chunk = total_read;
String file_name_new = file_name_base + "_" + i + ".mp3";
File file_new = new File(file_name_base);
i++;
fOut = new FileOutputStream(file_name_new);
fOut.write(buff);

fIn.skip(total_read);// skip the total read part

} // end of while loop

fIn.close();
fOut.close();

}
  1. The incremental variable declaration (int i = 0) was needed to create outside the while loop.
  2. Don't null the variable buff as it will throw Null-pointer in next iteration.
  3. File's base name needed to be extracted in order to create the file parts with names you want. E.g. song_0.mp3, song_1.mp3,...


Related Topics



Leave a reply



Submit