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