1 /*
2  *   pcmfade.c
3  *
4  *   Oliver Fromme  <olli@fromme.com>
5  *
6  *   Copyright (C) 1997,1998,1999
7  *        Oliver Fromme.  All rights reserved.
8  *
9  *   Redistribution and use in source and binary forms, with or without
10  *   modification, are permitted provided that the following conditions
11  *   are met:
12  *   1. Redistributions of source code must retain the above copyright
13  *      notice, this list of conditions and the following disclaimer.
14  *   2. Redistributions in binary form must reproduce the above copyright
15  *      notice, this list of conditions and the following disclaimer in the
16  *      documentation and/or other materials provided with the distribution.
17  *   3. Neither the name of the author nor the names of any co-contributors
18  *      may be used to endorse or promote products derived from this software
19  *      without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY OLIVER FROMME AND CONTRIBUTORS ``AS IS'' AND
22  *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  *   ARE DISCLAIMED.  IN NO EVENT SHALL OLIVER FROMME OR CONTRIBUTORS BE LIABLE
25  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  *   SUCH DAMAGE.
32  *
33  *   @(#)$Id: pcmfade.c,v 1.3 1999/01/01 23:31:52 olli Exp $
34  */
35 
36 static const char cvsid[]
37     = "@(#)$Id: pcmfade.c,v 1.3 1999/01/01 23:31:52 olli Exp $";
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <limits.h>
44 #include <fcntl.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 
48 #include "utils.h"
49 
usage(char * dummy)50 void usage (char *dummy)
51 {
52 	fprintf (stderr, "Usage:  %s <0%% offset> <100%% offset> <PCM file>\n", me);
53 	fprintf (stderr, "The following formats can be used for offsets:\n");
54 	fprintf (stderr, "   [-][<minutes>]:<seconds>\n");
55 	fprintf (stderr, "   [-]<samples>\n");
56 	fprintf (stderr, "<minutes> is an integer value, default is 0.\n");
57 	fprintf (stderr, "<seconds> is an integer OR floating point value (can be >= 60).\n");
58 	fprintf (stderr, "<samples> is an integer value (1 sample = 4 bytes).\n");
59 	fprintf (stderr, "Negative values are relative to the end of the file.\n");
60 	fprintf (stderr, "if <0%% offset>  <  <100%% offset>, a fade-in is created.\n");
61 	fprintf (stderr, "if <0%% offset>  >  <100%% offset>, a fade-out is created.\n");
62 	fprintf (stderr, "Note: the samples are assumed to be 44.1 kHz signed 16 bit stereo:\n");
63 	fprintf (stderr, "      1 second = 44100 samples = 176400 bytes.\n");
64 	fprintf (stderr, "WARNING: the <PCM file> is modified in place, so make a backup copy!\n");
65 	exit (0);
66 }
67 
samples2time(long samples)68 char *samples2time (long samples)
69 {
70 	static char time[24];
71 	long mins, secs;
72 
73 	secs = samples / 44100;
74 	samples -= secs * 44100;
75 	mins = secs / 60;
76 	secs -= mins * 60;
77 	samples = (samples * 2000 + 441) / 882;
78 	sprintf (time, "%2ld:%02ld.%05ld", mins, secs, samples);
79 	return (time);
80 }
81 
get_filesize(int fd)82 long get_filesize (int fd)
83 {
84 	struct stat fdstat;
85 
86 	if (fstat(fd, &fdstat))
87 		return (-1);
88 	return (fdstat.st_size);
89 }
90 
skip_whitespace(char ** c)91 void skip_whitespace (char **c)
92 {
93 	*c += strspn(*c, " \t\r\n");
94 }
95 
get_offset(char * s,long samples)96 long get_offset (char *s, long samples)
97 {
98 	char *ends, *dpp;
99 	int neg = FALSE;
100 	long off;
101 
102 	skip_whitespace (&s);
103 	switch (*s) {
104 		case '-':
105 			neg = TRUE;
106 		case '+':
107 			s++;
108 			skip_whitespace (&s);
109 	}
110 	if (!*s)
111 		return (-1);
112 	if ((dpp = strchr(s, ':'))) {
113 		long mins;
114 		double secs;
115 
116 		if (dpp++ == s)
117 			mins = 0;
118 		else {
119 			mins = strtoul(s, &ends, 10);
120 			skip_whitespace (&ends);
121 			if (*ends != ':')
122 				return (-1);
123 		}
124 		skip_whitespace (&dpp);
125 		if (*dpp) {
126 			secs = strtod(dpp, &ends);
127 			skip_whitespace (&ends);
128 			if (*ends)
129 				return (-1);
130 		}
131 		else
132 			secs = 0.0;
133 		off = (mins * 60 + secs) * 44100 + 0.5;
134 	}
135 	else {
136 		off = strtoul(s, &ends, 0);
137 		skip_whitespace (&ends);
138 		if (*ends || off >= samples || (neg && !off))
139 			return (-1);
140 	}
141 	return (neg ? samples - off : off);
142 }
143 
144 #define BUFFERSAMPLES 16384
145 #define BUFFERSHORTS (BUFFERSAMPLES * 2)
146 #define BUFFERSIZE (BUFFERSHORTS * sizeof(short))
147 
fade(int fd,long maxlevel,long ostart,int lstart,long oend,int lend)148 void fade (int fd, long maxlevel, long ostart, int lstart, long oend, int lend)
149 {
150 	long off, olen = oend - ostart, olen2 = olen >> 1;
151 	int level = lstart, llen = lend - lstart;
152 	int inc, bres = 0;
153 
154 	short *buffer;
155 	long bindex = 0;
156 
157 	buffer = tmalloc(BUFFERSIZE);
158 	if (lseek(fd, ostart * 4, SEEK_SET) == -1)
159 		die ("lseek()");
160 	if (read(fd, buffer, BUFFERSIZE) == -1)
161 		die ("read()");
162 	for (off = ostart; off <= oend; off++) {
163 		buffer[bindex] = ((long) buffer[bindex] * level) / maxlevel;
164 		bindex++;
165 		buffer[bindex] = ((long) buffer[bindex] * level) / maxlevel;
166 		bindex++;
167 		if (bindex >= BUFFERSHORTS) {
168 			if (lseek(fd, ostart * 4, SEEK_SET) == -1)
169 				die ("lseek()");
170 			if (write(fd, buffer, BUFFERSIZE) == -1)
171 				die ("write()");
172 			ostart += BUFFERSAMPLES;
173 			if (read(fd, buffer, BUFFERSIZE) == -1)
174 				die ("read()");
175 			bindex = 0;
176 		}
177 		bres += llen;
178 		if (abs(bres) + olen2 >= olen) {
179 			if (bres > 0)
180 				inc = (bres + olen2) / olen;
181 			else
182 				inc = (bres - olen2) / olen;
183 			level += inc;
184 			bres -= (inc * olen);
185 		}
186 	}
187 	if (bindex) {
188 		if (lseek(fd, ostart * 4, SEEK_SET) == -1)
189 			die ("lseek()");
190 		if (write(fd, buffer, bindex * sizeof(short)) == -1)
191 			die ("write()");
192 	}
193 	free (buffer);
194 }
195 
196 #define MAXLEVEL 0x10000
197 
main(int argc,char * argv[])198 int main (int argc, char *argv[])
199 {
200 	int fd;
201 	int lstart, lend;
202 	long filesize;
203 	long s0, s100, ostart, oend;
204 
205 	utils_init (argv[0]);
206 	if (argc != 4)
207 		usage (NULL);
208 	if ((fd = open(argv[3], O_RDWR, 0)) == -1)
209 		die (argv[3]);
210 	if ((filesize = get_filesize(fd)) == -1)
211 		die (argv[3]);
212 	if (filesize % 3)
213 		fprintf (stderr,
214 			"%s: Warning: file size is not a multiple of 4.\n",
215 			me);
216 	filesize >>= 2;
217 	if ((s0 = get_offset(argv[1], filesize)) == -1
218 			|| (s100 = get_offset(argv[2], filesize)) == -1) {
219 		fprintf (stderr,
220 			"%s: Offset specification invalid or out of range.\n",
221 			me);
222 		exit (1);
223 	}
224 	if (s0 < s100) {
225 		ostart = s0;
226 		oend = s100;
227 		lstart = 0;
228 		lend = MAXLEVEL;
229 	}
230 	else if (s0 > s100) {
231 		ostart = s100;
232 		oend = s0;
233 		lstart = MAXLEVEL;
234 		lend = 0;
235 	}
236 	else {
237 		fprintf (stderr,
238 			"%s: start offset and end offset must be different.\n",
239 			me);
240 		close (fd);
241 		exit (1);
242 	}
243 	printf ("File:  %s\n", argv[3]);
244 	printf ("Size (in samples):  %8ld / %s\n", filesize, samples2time(filesize));
245 	printf ("Fading from %3d%% at %8ld / %s\n",
246 		lstart ? 100 : 0, ostart, samples2time(ostart));
247 	printf ("         to %3d%% at %8ld / %s\n",
248 		lend ? 100 : 0, oend, samples2time(oend));
249 	printf ("Duration of fade:   %8ld / %s\n",
250 		oend - ostart, samples2time(oend - ostart));
251 	fade (fd, MAXLEVEL, ostart, lstart, oend, lend);
252 	close (fd);
253 	exit (0);
254 }
255 
256 /* EOF */
257