briba
briba

Reputation: 2987

Split audio file into pieces

I´m trying to split an audio file in some pieces.

The fact is: I have a byte array and I would like do split the wav file into some random pieces (3 for example).

Of course, I know that I can´t do something like this. But does anyone have an idea on how to do it?

byte[] result = stream.ToArray();
byte[] testing = new byte[44];

for (int ix = 0; ix < testing.Length; ix++)
{
    testing[ix] = result[ix];
}

System.IO.File.WriteAllBytes("yourfilepath_" + System.Guid.NewGuid() + ".wav", testing);

I would like do build this solution in C# but I heard that there is a lib called Sox and I can split with silence gap like this:

sox in.wav out.wav silence 1 0.5 1% 1 5.0 1% : newfile : restart

But everytime I run this command, only one file is generated. (audio file lasts 5 seconds, and each splitted file must have something aroung 1 second).

What is the best way to do this?

Thank you very much!

Upvotes: 3

Views: 5792

Answers (1)

aybe
aybe

Reputation: 16662

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).

Upvotes: 5

Related Questions