1 /* LongSound_extensions.c
2 *
3 * Copyright (C) 1993-2017 David Weenink
4 *
5 * This code 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 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 djmw 20020627 GPL header
21 djmw 20030913 changed 'f' to 'file' as argument in Melder_checkSoundFile
22 djmw 20060206 Set errno = 0: "An application that needs to examine the value
23 of errno to determine the error should set it to 0 before a function call,
24 then inspect it before a subsequent function call."
25 djmw 20061213 MelderFile_truncate also works for MacOS X
26 djmw 20061212 Header unistd.h for MacOS X added.
27 djmw 20070129 Sounds may be multichannel
28 djmw 20071030 MelderFile->wpath to MelderFile->path
29 */
30
31 #include "LongSound_extensions.h"
32
33 #if defined (_WIN32)
34 #include "winport_on.h"
35 #include <windows.h>
36 #include "winport_off.h"
37 #elif defined(linux)
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <string.h>
41 #elif defined (macintosh)
42 #include <unistd.h>
43 #endif
44
45 #include "NUM2.h"
46 #include <errno.h>
47
48 /*
49 Precondition: size (my buffer) >= nbuf
50 */
_LongSound_to_multichannel_buffer(LongSound me,short * buffer,integer nbuf,int nchannels,int ichannel,integer ibuf)51 static void _LongSound_to_multichannel_buffer (LongSound me, short *buffer, integer nbuf, int nchannels, int ichannel, integer ibuf) {
52 const integer numberOfReads = (my nx - 1) / nbuf + 1;
53 integer n_to_read = 0;
54
55 if (ibuf <= numberOfReads) {
56 n_to_read = ( ibuf == numberOfReads ? (my nx - 1) % nbuf + 1 : nbuf );
57 const integer imin = (ibuf - 1) * nbuf + 1;
58 my invalidateBuffer();
59 LongSound_readAudioToShort (me, my buffer.asArgumentToFunctionThatExpectsZeroBasedArray(), imin, n_to_read);
60
61 for (integer i = 1; i <= n_to_read; i ++)
62 buffer [nchannels * (i - 1) + ichannel] = my buffer [i];
63 }
64 if (ibuf >= numberOfReads)
65 for (integer i = n_to_read + 1; i <= nbuf; i ++)
66 buffer [nchannels * (i - 1) + ichannel] = 0;
67 }
68
LongSounds_writeToStereoAudioFile16(LongSound me,LongSound thee,int audioFileType,MelderFile file)69 void LongSounds_writeToStereoAudioFile16 (LongSound me, LongSound thee, int audioFileType, MelderFile file) {
70 try {
71 const integer nbuf = std::min (my nmax, thy nmax);
72 const integer nx = std::max (my nx, thy nx);
73 const integer numberOfReads = (nx - 1) / nbuf + 1, numberOfBitsPerSamplePoint = 16;
74
75 Melder_require (thy numberOfChannels == my numberOfChannels && my numberOfChannels == 1,
76 U"The two LongSounds should be mono.");
77 Melder_require (my sampleRate == thy sampleRate,
78 U"The two sampling frequencies should be equal.");
79 /*
80 Allocate a stereo buffer of size (2 * the smallest)!
81 WE SUPPOSE THAT SMALL IS LARGE ENOUGH.
82 Read the same number of samples from both files, despite
83 potential differences in internal buffer size.
84 */
85
86 const integer nchannels = 2;
87 autovector <short> buffer = newvectorzero <short> (nchannels * nbuf);
88
89 autoMelderFile f = MelderFile_create (file);
90 MelderFile_writeAudioFileHeader (file, audioFileType, Melder_ifloor (my sampleRate), nx, nchannels, numberOfBitsPerSamplePoint);
91
92 for (integer i = 1; i <= numberOfReads; i ++) {
93 const integer n_to_write = ( i == numberOfReads ? (nx - 1) % nbuf + 1 : nbuf );
94 _LongSound_to_multichannel_buffer (me, buffer.asArgumentToFunctionThatExpectsOneBasedArray(), nbuf, nchannels, 1, i);
95 _LongSound_to_multichannel_buffer (thee, buffer.asArgumentToFunctionThatExpectsOneBasedArray(), nbuf, nchannels, 2, i);
96 MelderFile_writeShortToAudio (file, nchannels, Melder_defaultAudioFileEncoding (audioFileType,
97 numberOfBitsPerSamplePoint), & buffer [1], n_to_write);
98 }
99 MelderFile_writeAudioFileTrailer (file, audioFileType, Melder_ifloor (my sampleRate), nx, nchannels, numberOfBitsPerSamplePoint);
100 f.close ();
101 } catch (MelderError) {
102 Melder_throw (me, U": no stereo audio file created.");
103 }
104 }
105
106
107 /*
108 BSD systems provide ftruncate, several others supply chsize, and a few
109 may provide a (possibly undocumented) fcntl option F_FREESP. Under MS-DOS,
110 you can sometimes use write(fd, "", 0). However, there is no portable
111 solution, nor a way to delete blocks at the beginning.
112 */
MelderFile_truncate(MelderFile me,integer size)113 static void MelderFile_truncate (MelderFile me, integer size) {
114 #if defined (_WIN32)
115 HANDLE hFile;
116 DWORD fdwAccess = GENERIC_READ | GENERIC_WRITE, fPos;
117 DWORD fdwShareMode = 0; // file cannot be shared
118 LPSECURITY_ATTRIBUTES lpsa = nullptr;
119 DWORD fdwCreate = OPEN_EXISTING;
120 LARGE_INTEGER fileSize;
121
122 MelderFile_close (me);
123
124 hFile = CreateFileW (Melder_peek32toW_fileSystem (my path),
125 fdwAccess, fdwShareMode, lpsa, fdwCreate, FILE_ATTRIBUTE_NORMAL, nullptr);
126
127 Melder_require (hFile != INVALID_HANDLE_VALUE,
128 U"Cannot open file ", me, U".");
129
130 fileSize.LowPart = size; // Set current file pointer to position 'size'
131 fileSize.HighPart = 0; // Limit the file size to 2^32 - 2 bytes
132 fPos = SetFilePointer (hFile, fileSize.LowPart, & fileSize.HighPart, FILE_BEGIN);
133 Melder_require (fPos != 0xFFFF'FFFF,
134 U"Can't set the position at size ", size, U" for file ", me, U".");
135
136 // Limit the file size as the current position of the file pointer.
137
138 SetEndOfFile (hFile);
139 CloseHandle (hFile);
140 #elif defined (linux) || defined (macintosh)
141 MelderFile_close (me);
142 const int succes = truncate (Melder_peek32to8 (my path), size);
143 Melder_require (succes == 0,
144 U"Truncating failed for file ", me, U" (", Melder_peek8to32 (strerror (errno)), U").");
145 #else
146 Melder_throw (U"Don't know what to do.");
147 #endif
148 }
149
writePartToOpenFile16(LongSound me,int audioFileType,integer imin,integer n,MelderFile file)150 static void writePartToOpenFile16 (LongSound me, int audioFileType, integer imin, integer n, MelderFile file) {
151 const integer numberOfBuffers = (n - 1) / my nmax + 1, numberOfBitsPerSamplePoint = 16;
152 const integer numberOfSamplesInLastBuffer = (n - 1) % my nmax + 1;
153 integer offset = imin;
154 if (file -> filePointer) {
155 for (integer ibuffer = 1; ibuffer <= numberOfBuffers; ibuffer ++) {
156 const integer numberOfSamplesToCopy = ( ibuffer < numberOfBuffers ? my nmax : numberOfSamplesInLastBuffer );
157 my invalidateBuffer();
158 LongSound_readAudioToShort (me, my buffer.asArgumentToFunctionThatExpectsZeroBasedArray(), offset, numberOfSamplesToCopy);
159 offset += numberOfSamplesToCopy;
160 MelderFile_writeShortToAudio (file, my numberOfChannels, Melder_defaultAudioFileEncoding (audioFileType, numberOfBitsPerSamplePoint), my buffer.asArgumentToFunctionThatExpectsZeroBasedArray(), numberOfSamplesToCopy);
161 }
162 }
163 }
164
LongSounds_appendToExistingSoundFile(OrderedOf<structSampled> * me,MelderFile file)165 void LongSounds_appendToExistingSoundFile (OrderedOf<structSampled>* me, MelderFile file) {
166 integer pre_append_endpos = 0, numberOfBitsPerSamplePoint = 16;
167 try {
168 Melder_require (my size > 0,
169 U"No Sound or LongSound objects to append.");
170 /*
171 We have to open with "r+" mode because this will position the stream
172 at the beginning of the file. The "a" mode does not allow us to
173 seek before the end-of-file.
174
175 For Linux: If the file is already opened (e.g. by a LongSound) object we
176 should deny access!
177 Under Windows deny access is default?!
178 */
179
180 autofile f = Melder_fopen (file, "r+b");
181 file -> filePointer = f; // essential !!
182 double sampleRate_d;
183 integer startOfData, numberOfSamples, numberOfChannels;
184 int encoding;
185 const int audioFileType = MelderFile_checkSoundFile (file, & numberOfChannels, & encoding, & sampleRate_d, & startOfData, & numberOfSamples);
186 Melder_require (audioFileType > 0,
187 U"Not a sound file.");
188 /*
189 Check whether all the sampling frequencies and channels match.
190 */
191 integer sampleRate = Melder_ifloor (sampleRate_d);
192 for (integer i = 1; i <= my size; i ++) {
193 bool sampleRatesMatch, numbersOfChannelsMatch;
194 const Sampled data = my at [i];
195 if (data -> classInfo == classSound) {
196 Sound sound = (Sound) data;
197 sampleRatesMatch = Melder_iround (1.0 / sound -> dx) == sampleRate;
198 numbersOfChannelsMatch = sound -> ny == numberOfChannels;
199 numberOfSamples += sound -> nx;
200 } else {
201 LongSound longSound = (LongSound) data;
202 sampleRatesMatch = longSound -> sampleRate == sampleRate;
203 numbersOfChannelsMatch = longSound -> numberOfChannels == numberOfChannels;
204 numberOfSamples += longSound -> nx;
205 }
206 Melder_require (sampleRatesMatch,
207 U"Sampling frequencies should match.");
208 Melder_require (numbersOfChannelsMatch,
209 U"The number of channels should match.");
210 }
211 /*
212 Search the end of the file, count the number of bytes and append.
213 */
214 MelderFile_seek (file, 0, SEEK_END);
215 pre_append_endpos = MelderFile_tell (file);
216
217 errno = 0;
218 for (integer i = 1; i <= my size; i ++) {
219 const Sampled data = my at [i];
220 if (data -> classInfo == classSound) {
221 Sound sound = (Sound) data;
222 MelderFile_writeFloatToAudio (file, sound -> z.get(),
223 Melder_defaultAudioFileEncoding (audioFileType, numberOfBitsPerSamplePoint), true);
224 } else {
225 LongSound longSound = (LongSound) data;
226 writePartToOpenFile16 (longSound, audioFileType, 1, longSound -> nx, file);
227 }
228 Melder_require (errno == 0,
229 U"Error during writing.");
230 }
231 /*
232 Update header
233 */
234 MelderFile_rewind (file);
235 MelderFile_writeAudioFileHeader (file, audioFileType, sampleRate, numberOfSamples, numberOfChannels, numberOfBitsPerSamplePoint);
236 MelderFile_writeAudioFileTrailer (file, audioFileType, sampleRate, numberOfSamples, numberOfChannels, numberOfBitsPerSamplePoint);
237 f.close (file);
238 return;
239 } catch (MelderError) {
240 if (errno != 0 && pre_append_endpos > 0) {
241 // Restore file at original size
242 int error = errno;
243 MelderFile_truncate (file, pre_append_endpos);
244 Melder_throw (U"File ", MelderFile_messageName (file), U" restored to original size (", Melder_peek8to32 (strerror (error)), U").");
245 } throw;
246 }
247 }
248
249 /* End of file LongSound_extensions.cpp */
250