Skip to content

Commit 1a665a5

Browse files
author
twojstaryzdomu
committed
Added support for splitting WAV files into individual tracks
1 parent ecd586c commit 1a665a5

File tree

4 files changed

+127
-20
lines changed

4 files changed

+127
-20
lines changed

ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,8 @@
7474
Sealed various memory leaks. Refactored exit statements.
7575

7676
Abort further processing if output file already exists.
77+
78+
Added support for ## as track # replacement token in the basename
79+
parameter.
80+
81+
Added support for splitting WAV files into individual tracks.

README

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
binchunker converts a CD image in a ".bin / .cue" format
6363
(sometimes ".raw / .cue") to a set of .iso and .cdr tracks.
6464

65+
It also allows to split uncompressed PCM audio files in the
66+
WAV format into individual tracks.
67+
6568
The bin/cue format is used by some non-Unix cd-writing
6669
software, but is not supported on most other cd-writing
6770
programs.
@@ -118,17 +121,19 @@
118121
How to use this stuff:
119122

120123
bchunk [-t] [-v] [-p (PSX)] [-r (raw)] [-w (wav)] [-s (swabaudio)]
121-
<image.bin | track # | '*'> <image.cue> [ <basename> ]
124+
<image.bin | image.wav | track # | '*'> <image.cue> [ <basename> ]
122125

123-
image.bin is a source raw cd image file listed in the
126+
image.bin is a source raw cd image file and image.wav is an
127+
uncompressed PCM audio file, either listed in the
124128
cue sheet file as a FILE. track # identifies the track listed
125129
in the cue sheet as a TRACK. The wildcard '*' (quoted)
126130
selects all tracks for conversion. image.cue is the cue sheet
127131
file containing filenames, track types and offsets. The optional
128132
basename is used for the leading part of the output filenames.
129133
By default, the output files are now named after the basename
130134
of the source image file or the track #, ending with the
131-
appropriate format extension.
135+
appropriate format extension. basename may contain the track
136+
token ## which will be replaced by the track number.
132137

133138
The -t flag adds the track # in the output filename between
134139
the basename & the extension, as in versions prior to 1.2.3.

bchunk.1

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
.TH BCHUNK 1 "v1.2.3 30 Jun 2022" "Heikki Hannikainen"
22
.SH NAME
33
bchunk \- CD image format conversion from bin/cue to iso/cdr
4+
and and WAV file splitter
45
.SH SYNOPSIS
56
.B bchunk [-t] [-v] [-p] [-r] [-w] [-s]
6-
<image.bin | track # | '*'> <image.cue> [ <basename> ]
7+
<image.bin | image.wav | track # | '*'> <image.cue> [ <basename> ]
78
.SH DESCRIPTION
89
.LP
910
.B bchunk
@@ -14,13 +15,18 @@ The bin/cue format is used by some non-Unix cd-writing
1415
software, but is not supported on most other cd-writing
1516
programs.
1617
.LP
17-
image.bin is a raw cd image file listed in the cue sheet as a FILE.
18+
It also allows to split uncompressed PCM audio files in the
19+
WAV format into individual tracks.
20+
.LP
21+
image.bin is a raw cd image file and image.wav is an uncompressed
22+
PCM audio file, either listed in the cue sheet file as a FILE.
1823
track # is the track number listed in the cue sheet as a TRACK.
1924
track # may omit the leading 0s. The wildcard '*' (single quoted to
2025
prevent shell expansion) selects all tracks from a cue sheet for
2126
conversion. image.cue is the cue sheet file containing track types
2227
and offsets. basename is optionally used for the leading part of
23-
output filenames.
28+
output filenames. basename may contain the track token '##'
29+
which will be replaced by the track number.
2430
.LP
2531
The produced .iso track contains an ISO file system, which can be
2632
mounted through a loop device on Linux systems, or
@@ -73,6 +79,10 @@ raw format.
7379
.B image.bin
7480
Raw CD image file, listed in the cue sheet as a FILE.
7581
.TP 5
82+
.B image.wav
83+
WAV file with uncompressed PCM audio, listed in the cue sheet
84+
as a FILE.
85+
.TP 5
7686
.B image.cue
7787
Cue sheet, TOC (Track index, Table Of Contents) file.
7888
.TP 5

bchunk.c

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@
2828

2929
#define VERSION "1.2.3"
3030
#define USAGE "Usage: bchunk [-t] [-v] [-r] [-p (PSX)] [-w (wav)] [-s (swabaudio)]\n" \
31-
" <image.bin | track # | '*'> <image.cue> [ <basename> ]\n" \
31+
" <image.bin | image.wav | track # | '*'> <image.cue> [ <basename> ]\n" \
3232
"Example: bchunk foo.bin foo.cue foo\n" \
3333
" bchunk foo.bin foo.cue\n" \
3434
" bchunk 2 foo.cue foo\n" \
3535
" -t Insert track # in between the basename and the format extension\n" \
3636
" -v Verbose mode\n" \
37+
" -d Debug mode\n" \
3738
" -r Raw mode for MODE2/2352: write all 2352 bytes from offset 0 (VCD/MPEG)\n" \
3839
" -p PSX mode for MODE2/2352: write 2336 bytes from offset 24\n" \
3940
" (default MODE2/2352 mode writes 2048 bytes from offset 24)\n"\
@@ -57,7 +58,12 @@
5758
#define WAV_RIFF_HLEN 12
5859
#define WAV_FORMAT_HLEN 24
5960
#define WAV_DATA_HLEN 8
61+
#define WAV_DATA_FOURCC "data"
62+
#define WAV_DATA_FLEN sizeof(WAV_DATA_FOURCC) - 1
6063
#define WAV_HEADER_LEN WAV_RIFF_HLEN + WAV_FORMAT_HLEN + WAV_DATA_HLEN
64+
#define WAV_HEADER_MAX WAV_HEADER_LEN + 100
65+
66+
#define TRACK_TOKEN "##"
6167

6268
/*
6369
* Ugly way to convert integers to little-endian format.
@@ -93,6 +99,7 @@ struct track_t {
9399
char *extension;
94100
int bstart;
95101
int bsize;
102+
long dataoffs;
96103
long startsect;
97104
long stopsect;
98105
long start;
@@ -106,6 +113,7 @@ char *cuefile = NULL;
106113
char *bname = NULL;
107114
char *file = NULL;
108115
int verbose = 0;
116+
int debug = 0;
109117
int psxtruncate = 0;
110118
int raw = 0;
111119
int swabaudio = 0;
@@ -170,14 +178,17 @@ void parse_args(int argc, char *argv[])
170178
{
171179
int s;
172180

173-
while ((s = getopt(argc, argv, "swvp?hrt")) != -1) {
181+
while ((s = getopt(argc, argv, "swvp?hrtd")) != -1) {
174182
switch (s) {
175183
case 'r':
176184
raw = 1;
177185
break;
178186
case 'v':
179187
verbose = 1;
180188
break;
189+
case 'd':
190+
debug = 1;
191+
break;
181192
case 'w':
182193
towav = 1;
183194
break;
@@ -242,6 +253,44 @@ long time2frames(char *s)
242253
return 75 * (mins * 60 + secs) + frames;
243254
}
244255

256+
/*
257+
* Check WAV file name extension
258+
*/
259+
260+
int is_wav_file(struct track_t *track) {
261+
static char ext_wav[] = ".wav";
262+
return !strcasecmp(track->file + strlen(track->file) - strlen(ext_wav), ext_wav);
263+
}
264+
265+
/*
266+
* Set WAV data header offset
267+
*/
268+
269+
int set_wav_data_offset(FILE *bf, struct track_t *track) {
270+
char s[WAV_DATA_FLEN+1];
271+
s[WAV_DATA_FLEN] = '\0';
272+
for (int offset = WAV_HEADER_LEN; offset < WAV_HEADER_MAX; offset++) {
273+
if (fseek(binf, offset, SEEK_SET) == -1)
274+
die_format(4, " Could not fseek to offset %d: %s\n", offset, strerror(errno));
275+
fread(s, WAV_DATA_FLEN, 1, binf);
276+
if (strcmp(WAV_DATA_FOURCC, s) == 0) {
277+
track->dataoffs = offset + WAV_DATA_HLEN;
278+
if (debug) {
279+
fseek(binf, offset + WAV_DATA_FLEN, SEEK_SET);
280+
uint32_t w;
281+
fread(&w, WAV_DATA_FLEN, 1, binf);
282+
fprintf(stderr, " data header at %d\n"
283+
" WAV data at %ld\n"
284+
" WAV data bytes %u\n",
285+
offset, track->dataoffs, w);
286+
}
287+
return 1;
288+
}
289+
}
290+
printf("No WAV data header found in %s for track %d\n", track->file, track->num);
291+
return 0;
292+
}
293+
245294
/*
246295
* Parse the mode string
247296
*/
@@ -284,7 +333,7 @@ void gettrackmode(struct track_t *track, char *modes)
284333
track->bsize = 2336;
285334
track->extension = ext_iso;
286335

287-
} else if (!strcasecmp(modes, "AUDIO")) {
336+
} else if (!strcasecmp(modes, "AUDIO") || is_wav_file(track)) {
288337
track->bstart = 0;
289338
track->bsize = 2352;
290339
track->audio = 1;
@@ -345,7 +394,7 @@ int writetrack(FILE *bf, struct track_t *track)
345394

346395
printf("%2d: %s ", track->num, track->output);
347396

348-
if (fseek(bf, track->start, SEEK_SET))
397+
if (fseek(bf, track->start + track->dataoffs, SEEK_SET))
349398
die_format(4, " Could not fseek to track location: %s\n", strerror(errno));
350399

351400
reallen = (track->stopsect - track->startsect + 1) * track->bsize;
@@ -385,7 +434,7 @@ int writetrack(FILE *bf, struct track_t *track)
385434
i = htoles(2*8); // bits per channel
386435
fwrite(&i, 2, 1, f);
387436
// DATA header
388-
fputs("data", f);
437+
fputs(WAV_DATA_FOURCC, f);
389438
l = htolel(reallen);
390439
fwrite(&l, 4, 1, f);
391440
}
@@ -394,6 +443,7 @@ int writetrack(FILE *bf, struct track_t *track)
394443
sz = track->start;
395444
sect = track->startsect;
396445
fl = 0;
446+
397447
while ((sect <= track->stopsect) && (fread(buf, SECTLEN, 1, bf) > 0)) {
398448
if (track->audio) {
399449
if (swabaudio) {
@@ -439,7 +489,7 @@ int writetrack(FILE *bf, struct track_t *track)
439489
*/
440490

441491
int set_output(struct track_t *track, char *binfile, char *basefile, int trackadd) {
442-
char *t;
492+
char *e, *s, *t;
443493
if (strchr(binfile, '*')) {
444494
if (!basefile)
445495
bname = prune_ext(track->file);
@@ -460,6 +510,19 @@ int set_output(struct track_t *track, char *binfile, char *basefile, int trackad
460510
if (asprintf(&bname, "%s", basefile) == -1)
461511
die(4, "set_output(): asprintf() failed, out of memory\n");
462512
}
513+
if (basefile)
514+
if ((e = strstr(basefile, TRACK_TOKEN))) {
515+
int l = e - basefile;
516+
if (!(s = calloc(l + 1, 1)))
517+
die(4, "set_output(): calloc() failed, out of memory\n");
518+
strncpy(s, basefile, l);
519+
if (bname)
520+
t = bname;
521+
if (asprintf(&bname, "%s%.2d%s", s, track->num, e + strlen(TRACK_TOKEN)) == -1)
522+
die(4, "set_output(): asprintf() failed, out of memory\n");
523+
free(s);
524+
free(t);
525+
}
463526
if (trackadd) {
464527
if (bname)
465528
t = bname;
@@ -484,7 +547,7 @@ int set_output(struct track_t *track, char *binfile, char *basefile, int trackad
484547
int main(int argc, char **argv)
485548
{
486549
char s[CUELLEN+1];
487-
char *b, *e, *p, *t;
550+
char *b, *e, *i, *p, *t, *u;
488551
struct track_t *prevtrack = NULL;
489552
struct track_t **prevp = &tracks;
490553

@@ -551,6 +614,7 @@ int main(int argc, char **argv)
551614
track->output = NULL;
552615
track->mode = 0;
553616
track->audio = 0;
617+
track->dataoffs = 0;
554618
track->bsize = track->bstart = -1;
555619
track->bsize = -1;
556620
track->startsect = track->stopsect = -1;
@@ -566,13 +630,33 @@ int main(int argc, char **argv)
566630
*t = '\0';
567631
t++;
568632
printf(" %s %s", p, t);
569-
track->startsect = time2frames(t);
570-
track->start = track->startsect * SECTLEN;
633+
long index = strtol(p, &i, 10);
634+
if (strlen(i))
635+
die_format(3, "... ouch, non-digit found in INDEX %s: %s\n", p, i);
636+
u = strdupa(t);
637+
int ss;
638+
// handle unordered pregap, ignore subtracks
639+
for (ss = time2frames(t); ss > track->startsect && index < 2;) {
640+
track->startsect = ss;
641+
track->start = track->startsect * SECTLEN;
642+
}
643+
if (debug)
644+
fprintf(stderr, "INDEX %s%s: %s startsect %d\n",
645+
p, track->startsect + 1 ? "" : " updating",
646+
u, ss);
571647
if (verbose)
572-
printf(" (startsect %ld ofs %ld)", track->startsect, track->start);
573-
if ((prevtrack) && (prevtrack->stopsect < 0) && !(strcmp(prevtrack->file,track->file))) { // only when files match
574-
prevtrack->stopsect = track->startsect - 1;
575-
prevtrack->stop = track->start - 1;
648+
printf(" (%sstartsect %ld ofs %ld)",
649+
index == 0 ? "pregap " : "",
650+
track->startsect, track->start);
651+
if (prevtrack) {
652+
// only when files match, ignore subtracks
653+
if (!(strcmp(prevtrack->file,track->file)) && index < 2) {
654+
prevtrack->stopsect = track->startsect - 1;
655+
prevtrack->stop = track->start - 1;
656+
}
657+
if (debug)
658+
fprintf(stderr, "TRACK %.2d: stopsect %ld\n",
659+
prevtrack->num, prevtrack->stopsect);
576660
}
577661
}
578662
}
@@ -586,10 +670,13 @@ int main(int argc, char **argv)
586670
fprintf(stderr, "Could not open BIN %s: %s\n", track->file, strerror(errno));
587671
continue;
588672
}
673+
if (is_wav_file(track))
674+
if (!set_wav_data_offset(binf, track))
675+
continue;
589676
if (track->stopsect < 0) { // if not set yet
590677
if (fseek(binf, 0, SEEK_END) == -1)
591678
die_format(4, "main(): fseek failure in %s\n", track->file);
592-
track->stop = ftell(binf) - 1;
679+
track->stop = ftell(binf) - track->dataoffs - 1;
593680
track->stopsect = track->stop / SECTLEN;
594681
}
595682
writetrack(binf, track);

0 commit comments

Comments
 (0)