1/* 2** Copyright (C) 2009-2017 Erik de Castro Lopo <erikd@mega-nerd.com> 3** 4** All rights reserved. 5** 6** Redistribution and use in source and binary forms, with or without 7** modification, are permitted provided that the following conditions are 8** met: 9** 10** * Redistributions of source code must retain the above copyright 11** notice, this list of conditions and the following disclaimer. 12** * Redistributions in binary form must reproduce the above copyright 13** notice, this list of conditions and the following disclaimer in 14** the documentation and/or other materials provided with the 15** distribution. 16** * Neither the author nor the names of any contributors may be used 17** to endorse or promote products derived from this software without 18** specific prior written permission. 19** 20** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 27** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 29** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 30** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*/ 32 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <sndfile.h> 37 38#include "common.h" 39 40#define BUFFER_LEN 4096 41#define MAX_CHANNELS 16 42 43 44typedef struct 45{ SNDFILE * infile ; 46 SNDFILE * outfile [MAX_CHANNELS] ; 47 48 union 49 { double d [MAX_CHANNELS * BUFFER_LEN] ; 50 int i [MAX_CHANNELS * BUFFER_LEN] ; 51 } din ; 52 53 union 54 { double d [BUFFER_LEN] ; 55 int i [BUFFER_LEN] ; 56 } dout ; 57 58 int channels ; 59} STATE ; 60 61static void usage_exit (void) ; 62 63static void deinterleave_int (STATE * state) ; 64static void deinterleave_double (STATE * state) ; 65 66int 67main (int argc, char **argv) 68{ STATE *state = NULL ; 69 SF_INFO sfinfo ; 70 char pathname [512], ext [32], *cptr ; 71 int ch, double_split, ret = 1 ; 72 73 if (argc != 2) 74 { if (argc != 1) 75 puts ("\nError : need a single input file.\n") ; 76 usage_exit () ; 77 goto cleanup ; 78 } ; 79 80 state = calloc (1, sizeof (*state)) ; 81 if (!state) 82 { printf ("\nError : Out of memory.\n") ; 83 goto cleanup ; 84 } ; 85 memset (&sfinfo, 0, sizeof (sfinfo)) ; 86 87 if ((state->infile = sf_open (argv [1], SFM_READ, &sfinfo)) == NULL) 88 { printf ("\nError : Not able to open input file '%s'\n%s\n", argv [1], sf_strerror (NULL)) ; 89 goto cleanup ; 90 } ; 91 92 if (sfinfo.channels < 2) 93 { printf ("\nError : Input file '%s' only has one channel.\n", argv [1]) ; 94 goto cleanup ; 95 } ; 96 97 if (sfinfo.channels > MAX_CHANNELS) 98 { printf ("\nError : Input file '%s' has too many (%d) channels. Limit is %d.\n", 99 argv [1], sfinfo.channels, MAX_CHANNELS) ; 100 goto cleanup ; 101 } ; 102 103 104 state->channels = sfinfo.channels ; 105 sfinfo.channels = 1 ; 106 107 if (snprintf (pathname, sizeof (pathname), "%s", argv [1]) > (int) sizeof (pathname)) 108 { printf ("\nError : Length of provided filename '%s' exceeds MAX_PATH (%d).\n", argv [1], (int) sizeof (pathname)) ; 109 goto cleanup ; 110 } ; 111 112 if ((cptr = strrchr (pathname, '.')) == NULL) 113 ext [0] = 0 ; 114 else 115 { snprintf (ext, sizeof (ext), "%s", cptr) ; 116 cptr [0] = 0 ; 117 } ; 118 119 printf ("Input file : %s\n", pathname) ; 120 puts ("Output files :") ; 121 122 for (ch = 0 ; ch < state->channels ; ch++) 123 { char filename [520] ; 124 size_t count ; 125 126 count = snprintf (filename, sizeof (filename), "%s_%02d%s", pathname, ch, ext) ; 127 128 if (count >= sizeof (filename)) 129 { printf ("File name truncated to %s\n", filename) ; 130 } ; 131 132 if ((state->outfile [ch] = sf_open (filename, SFM_WRITE, &sfinfo)) == NULL) 133 { printf ("Not able to open output file '%s'\n%s\n", filename, sf_strerror (NULL)) ; 134 goto cleanup ; 135 } ; 136 137 printf (" %s\n", filename) ; 138 } ; 139 140 switch (sfinfo.format & SF_FORMAT_SUBMASK) 141 { case SF_FORMAT_FLOAT : 142 case SF_FORMAT_DOUBLE : 143 case SF_FORMAT_VORBIS : 144 double_split = 1 ; 145 break ; 146 147 default : 148 double_split = 0 ; 149 break ; 150 } ; 151 152 if (double_split) 153 deinterleave_double (state) ; 154 else 155 deinterleave_int (state) ; 156 157 ret = 0 ; 158 159cleanup : 160 161 if (state != NULL) 162 { sf_close (state->infile) ; 163 for (ch = 0 ; ch < MAX_CHANNELS ; ch++) 164 if (state->outfile [ch] != NULL) 165 sf_close (state->outfile [ch]) ; 166 } ; 167 168 free (state) ; 169 170 return ret ; 171} /* main */ 172 173/*------------------------------------------------------------------------------ 174*/ 175 176static void 177usage_exit (void) 178{ puts ("\nUsage : sndfile-deinterleave <filename>\n") ; 179 puts ( 180 "Split a mutli-channel file into a set of mono files.\n" 181 "\n" 182 "If the input file is named 'a.wav', the output files will be named\n" 183 "a_00.wav, a_01.wav and so on.\n" 184 ) ; 185 printf ("Using %s.\n\n", sf_version_string ()) ; 186} /* usage_exit */ 187 188static void 189deinterleave_int (STATE * state) 190{ int read_len ; 191 int ch, k ; 192 193 do 194 { read_len = (int) sf_readf_int (state->infile, state->din.i, BUFFER_LEN) ; 195 196 for (ch = 0 ; ch < state->channels ; ch ++) 197 { for (k = 0 ; k < read_len ; k++) 198 state->dout.i [k] = state->din.i [k * state->channels + ch] ; 199 sf_write_int (state->outfile [ch], state->dout.i, read_len) ; 200 } ; 201 } 202 while (read_len > 0) ; 203 204} /* deinterleave_int */ 205 206static void 207deinterleave_double (STATE * state) 208{ int read_len ; 209 int ch, k ; 210 211 do 212 { read_len = (int) sf_readf_double (state->infile, state->din.d, BUFFER_LEN) ; 213 214 for (ch = 0 ; ch < state->channels ; ch ++) 215 { for (k = 0 ; k < read_len ; k++) 216 state->dout.d [k] = state->din.d [k * state->channels + ch] ; 217 sf_write_double (state->outfile [ch], state->dout.d, read_len) ; 218 } ; 219 } 220 while (read_len > 0) ; 221 222} /* deinterleave_double */ 223