OSDN Git Service

Add fseek between intermixed read/write.
[wavtool-pl/wavtool-pl.git] / src / wfd.c
1 /*
2     wavtool-pl
3     Copyright (C) 2011 Ying-Chun Liu (PaulLiu) <paulliu@debian.org>
4
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <sndfile.h>
24
25 #include "wfh.h"
26 #include "wfd.h"
27
28 /**
29  * Create an empty dat file
30  *
31  * @param filename the filename of dat file
32  */
33 void wfd_init(const char *filename) {
34   FILE *file1;
35   file1 = fopen(filename,"w");
36   if (file1) {
37     fclose(file1);
38   }
39 }
40
41 /**
42  * convert micro seconds to samples
43  *
44  * @param time time in ms
45  * @return samples
46  */
47 int wfd_ms2samples(double time) {
48   double samples_f;
49   int samples;
50   samples_f = ((double)wfh_samplerate) * time / 1000.0;
51   samples = (int)samples_f;
52   return samples;
53 }
54
55 /**
56  * skip the file by microseconds
57  *
58  * @param inputfile the file descriptor
59  * @param time time in ms
60  */
61 void wfd_skip(FILE *inputfile, double time) {
62   int samples;
63   samples = wfd_ms2samples(time);
64   fseek(inputfile,SEEK_CUR,samples*wfh_channels*(wfh_bits/8));
65 }
66
67 /**
68  * Get volume factor by frame number
69  *
70  * @param frame Current frame number
71  * @param p the p vector, 7 elements, sort by time (ms)
72  * @param v the v vector, 7 elements, sort based on p (%)
73  * @return the volume factor for frame. (%)
74  */
75 double wfd_append_linear_volume(int frame, const int *p, const double *v) {
76   double ret=0.0;
77   int i;
78   for (i=0; i<6; i++) {
79     if (p[i] <= frame && frame < p[i+1]) {
80       ret = v[i] + (v[i+1]-v[i]) * (((double)(frame-p[i])) / (((double)(p[i+1]-p[i]))));
81       break;
82     }
83   }
84   return ret;
85 }
86
87 /**
88  * mix sample A with sample B
89  *
90  * @param a sample A
91  * @param b sample B
92  * @return sample of A mix B
93  */
94 short wfd_mix(short a, short b) {
95   int a_t;
96   int b_t;
97   int r1;
98   short ret=0.0;
99
100   a_t = ((int)a)+32768;
101   b_t = ((int)b)+32768;
102
103   if (a_t <= 32768 && b_t <= 32768) {
104     r1 = (a_t * b_t) / 32768;
105   } else {
106     r1 = 2*(a_t+b_t) - (a_t*b_t)/32768 - 65536;
107     if (r1>=65536) {
108       r1 = 65535;
109     }
110   }
111   r1 = r1 - 32768;
112   ret = ((short)r1);
113   return ret;
114 }
115
116
117 /**
118  * append data to dat file
119  *
120  * @param outputfilename the filename of output file
121  * @param inputfilename the filename of input file
122  * @param offset skip the ms in input file
123  * @param length the time in ms have to be handled
124  * @param ovr the overlap of output file and input file (ms)
125  * @param p the p position array
126  * @param v the v volume array
127  * @return 0 - ok, nonzero - error
128  */
129 int wfd_append(const char *outputfilename, const char *inputfilename,
130                 double offset, double length,
131                 double ovr, const double *p, const double *v) {
132   int ret=0;
133   FILE *outfile=NULL;
134   SNDFILE *inputfile=NULL;
135   SF_INFO inputfileinfo;
136   short *buf=NULL;
137   int p_f[7];
138   double v_f[7];
139   int currentFrame=0;
140   int ovrFrames=0;
141   int outputFrames=0;
142   int i;
143   short sum;
144   int c1,c2;
145
146   memset(&inputfileinfo,0,sizeof(SF_INFO));
147   outfile = fopen(outputfilename,"r+");
148   fseek(outfile,0,SEEK_END);
149   if (inputfilename) {
150     inputfile = sf_open(inputfilename,SFM_READ,&inputfileinfo);
151     sf_command(inputfile,SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
152   } else {
153     inputfile=NULL;
154   }
155   outputFrames = wfd_ms2samples(length);
156
157   /* handle offset */
158   if (wfd_ms2samples(offset)>0) {
159     if (inputfile) {
160       sf_seek(inputfile,wfd_ms2samples(offset),SEEK_SET);
161     }
162   }
163
164   /* pre-calculate volume */
165   p_f[0]=0;
166   for (i=0; i<2; i++) {
167     p_f[i+1] = wfd_ms2samples(p[i]) + p_f[i];
168   }
169   p_f[3] = wfd_ms2samples(p[4]) + p_f[2];
170   p_f[6] = outputFrames;
171   p_f[5] = p_f[6] - wfd_ms2samples(p[3]);
172   p_f[4] = p_f[5] - wfd_ms2samples(p[2]);
173
174   v_f[0]=0.0;
175   for (i=0;i<2; i++) {
176     v_f[i+1] = v[i];
177   }
178   v_f[3]=v[4];
179   v_f[4]=v[2];
180   v_f[5]=v[3];
181   v_f[6]=0.0;
182
183   if (p_f[1]==p_f[2]) {
184     v_f[1]=v_f[2];
185   }
186   if (p_f[0]==p_f[1]) {
187     v_f[0]=v_f[1];
188   }
189   if (p_f[5]==p_f[4]) {
190     v_f[5]=v_f[4];
191   }
192   if (p_f[6]==p_f[5]) {
193     v_f[6]=v_f[5];
194   }
195
196   ovrFrames = wfd_ms2samples(ovr);
197   if (ovrFrames > 0) {
198     fseek(outfile,(-1)*wfh_channels*(wfh_bits/8)*ovrFrames,SEEK_END);
199   } else if (ovr < 0.0) {
200     /* output blank samples */
201     int ovrSamples=0;
202     int i,j,k;
203     ovrSamples = wfd_ms2samples(-ovr);
204     for (i=0; i<ovrSamples; i++) {
205       for (j=0; j<wfh_channels; j++) {
206         for (k=0; k<(wfh_bits/8); k++) {
207           fwrite("\0",1,1,outfile);
208         }
209       }
210     }
211     fseek(outfile,0,SEEK_CUR);
212     ovr=0.0;
213     ovrFrames=0;
214   }
215
216   /* output */
217   buf = (short *)malloc(sizeof(short)*(inputfileinfo.channels));
218   memset(buf,0,sizeof(short)*(inputfileinfo.channels));
219
220   currentFrame = 0;
221   for ( ; outputFrames > 0; outputFrames--) {
222     if (inputfile) {
223       int result1;
224       result1 = sf_readf_short(inputfile,buf,1);
225       if (result1 < 1) {
226         memset(buf,0,sizeof(short)*(inputfileinfo.channels));
227         sf_close(inputfile);
228         inputfile=NULL;
229       }
230     }
231     /* simple mix if there are multi-channels */
232     sum=0;
233     for (i=0; i<inputfileinfo.channels; i++) {
234       sum += buf[i];
235     }
236     /* modify the volume */
237     if (inputfileinfo.channels>0) {
238       double vf;
239       sum = sum/inputfileinfo.channels;
240       vf = wfd_append_linear_volume(currentFrame,p_f,v_f);
241       sum = (short)(((double)sum)*(vf/100.0));
242     } else {
243       sum=0;
244     }
245     if (ovrFrames>0) {
246       short d,r;
247       c1 = fgetc(outfile);
248       if (c1 == EOF) {
249         ovrFrames=0;
250         goto wfd_append_normal;
251       }
252       c2 = fgetc(outfile);
253       if (c2 == EOF) {
254         ovrFrames=0;
255         goto wfd_append_normal;
256       }
257       fseek(outfile,-2,SEEK_CUR);
258       d = (c1 & (0x00ff)) | (((c2 & 0x00ff) << 8) & 0xff00);
259       r = wfd_mix(sum,d);
260       fputc( (char)(r & (0x00ff)), outfile);
261       fputc( (char)((r>>8) & 0x00ff), outfile);
262       fseek(outfile,0,SEEK_CUR);
263       ovrFrames--;
264     } else {
265     wfd_append_normal:
266       fputc( (char)(sum & (0x00ff)), outfile);
267       fputc( (char)((sum>>8) & 0x00ff), outfile);
268       fseek(outfile,0,SEEK_CUR);
269     }
270     currentFrame++;
271   }
272
273   if (!inputfile) {
274     sf_close(inputfile);
275     inputfile=NULL;
276   }
277   if (!buf) {
278     free(buf);
279     buf=NULL;
280   }
281   ret = ftell(outfile);
282   fclose(outfile);
283   outfile=NULL;
284   return ret;
285 }