LongSound_extensions.cpp 9.4 KB

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