Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

753
LINES

< > BotCompany Repo | #1027001 // GSWavFile - helper class for reading/making WAV files

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (778L/5K).

1  
// Wav file IO class
2  
// A.Greensted
3  
// http://www.labbookpages.co.uk
4  
5  
// File format is based on the information from
6  
// http://www.sonicspot.com/guide/wavefiles.html
7  
// http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/wave.htm
8  
9  
// Version 1.0
10  
11  
sclass GSWavFile implements AutoCloseable { // GS for Greensted...
12  
  private enum IOState {READING, WRITING, CLOSED};
13  
  private final static int BUFFER_SIZE = 4096;
14  
15  
  private final static int FMT_CHUNK_ID = 0x20746D66;
16  
  private final static int DATA_CHUNK_ID = 0x61746164;
17  
  private final static int RIFF_CHUNK_ID = 0x46464952;
18  
  private final static int RIFF_TYPE_ID = 0x45564157;
19  
20  
  private File file;            // File that will be read from or written to
21  
  private IOState ioState;        // Specifies the IO State of the Wav File (used for snaity checking)
22  
  private int bytesPerSample;     // Number of bytes required to store a single sample
23  
  private long numFrames;         // Number of frames within the data section
24  
  private OutputStream oStream; // Output stream used for writting data
25  
  private FileInputStream iStream;    // Input stream used for reading data
26  
  private double floatScale;        // Scaling factor used for int <-> float conversion       
27  
  private double floatOffset;     // Offset factor used for int <-> float conversion        
28  
  private boolean wordAlignAdjust;    // Specify if an extra byte at the end of the data chunk is required for word alignment
29  
30  
  // Wav Header
31  
  private int numChannels;        // 2 bytes unsigned, 0x0001 (1) to 0xFFFF (65,535)
32  
  private long sampleRate;        // 4 bytes unsigned, 0x00000001 (1) to 0xFFFFFFFF (4,294,967,295)
33  
                          // Although a java int is 4 bytes, it is signed, so need to use a long
34  
  private int blockAlign;         // 2 bytes unsigned, 0x0001 (1) to 0xFFFF (65,535)
35  
  private int validBits;          // 2 bytes unsigned, 0x0002 (2) to 0xFFFF (65,535)
36  
37  
  // Buffering
38  
  private byte[] buffer;          // Local buffer used for IO
39  
  private int bufferPointer;        // Points to the current position in local buffer
40  
  private int bytesRead;          // Bytes read after last read into local buffer
41  
  private long frameCounter;        // Current number of frames read or written
42  
43  
  // Cannot instantiate GSWavFile directly, must either use newWavFile() or openWavFile()
44  
  private GSWavFile()
45  
  {
46  
    buffer = new byte[BUFFER_SIZE];
47  
  }
48  
49  
  public int getNumChannels()
50  
  {
51  
    return numChannels;
52  
  }
53  
54  
  public long getNumFrames()
55  
  {
56  
    return numFrames;
57  
  }
58  
59  
  public long getFramesRemaining()
60  
  {
61  
    return numFrames - frameCounter;
62  
  }
63  
64  
  public long getSampleRate()
65  
  {
66  
    return sampleRate;
67  
  }
68  
69  
  public int getValidBits()
70  
  {
71  
    return validBits;
72  
  }
73  
74  
  public static GSWavFile newWavFile(File file, int numChannels, long numFrames, int validBits, long sampleRate) throws IOException {
75  
    ret newWavFile(null, file, numChannels, numFrames, validBits, sampleRate);
76  
  }
77  
  
78  
  public static GSWavFile newWavFile(OutputStream oStream, File file, int numChannels, long numFrames, int validBits, long sampleRate) throws IOException {
79  
    // Instantiate new Wavfile and initialise
80  
    GSWavFile wavFile = new GSWavFile();
81  
    wavFile.file = file;
82  
    wavFile.numChannels = numChannels;
83  
    wavFile.numFrames = numFrames;
84  
    wavFile.sampleRate = sampleRate;
85  
    wavFile.bytesPerSample = (validBits + 7) / 8;
86  
    wavFile.blockAlign = wavFile.bytesPerSample * numChannels;
87  
    wavFile.validBits = validBits;
88  
89  
    // Sanity check arguments
90  
    if (numChannels < 1 || numChannels > 65535) throw new RuntimeException("Illegal number of channels, valid range 1 to 65536");
91  
    if (numFrames < 0) throw new RuntimeException("Number of frames must be positive");
92  
    if (validBits < 2 || validBits > 65535) throw new RuntimeException("Illegal number of valid bits, valid range 2 to 65536");
93  
    if (sampleRate < 0) throw new RuntimeException("Sample rate must be positive");
94  
95  
    // Create output stream for writing data
96  
    wavFile.oStream = oStream != null ? oStream : new FileOutputStream(file);
97  
98  
    // Calculate the chunk sizes
99  
    long dataChunkSize = wavFile.blockAlign * numFrames;
100  
    long mainChunkSize =  4 + // Riff Type
101  
                  8 + // Format ID and size
102  
                  16 +  // Format data
103  
                  8 +   // Data ID and size
104  
                  dataChunkSize;
105  
106  
    // Chunks must be word aligned, so if odd number of audio data bytes
107  
    // adjust the main chunk size
108  
    if (dataChunkSize % 2 == 1) {
109  
      mainChunkSize += 1;
110  
      wavFile.wordAlignAdjust = true;
111  
    }
112  
    else {
113  
      wavFile.wordAlignAdjust = false;
114  
    }
115  
116  
    // Set the main chunk size
117  
    putLE(RIFF_CHUNK_ID,  wavFile.buffer, 0, 4);
118  
    putLE(mainChunkSize,  wavFile.buffer, 4, 4);
119  
    putLE(RIFF_TYPE_ID, wavFile.buffer, 8, 4);
120  
121  
    // Write out the header
122  
    wavFile.oStream.write(wavFile.buffer, 0, 12);
123  
124  
    // Put format data in buffer
125  
    long averageBytesPerSecond = sampleRate * wavFile.blockAlign;
126  
127  
    putLE(FMT_CHUNK_ID,       wavFile.buffer, 0, 4);    // Chunk ID
128  
    putLE(16,             wavFile.buffer, 4, 4);    // Chunk Data Size
129  
    putLE(1,                wavFile.buffer, 8, 2);    // Compression Code (Uncompressed)
130  
    putLE(numChannels,        wavFile.buffer, 10, 2);   // Number of channels
131  
    putLE(sampleRate,         wavFile.buffer, 12, 4);   // Sample Rate
132  
    putLE(averageBytesPerSecond,  wavFile.buffer, 16, 4);   // Average Bytes Per Second
133  
    putLE(wavFile.blockAlign,   wavFile.buffer, 20, 2);   // Block Align
134  
    putLE(validBits,          wavFile.buffer, 22, 2);   // Valid Bits
135  
136  
    // Write Format Chunk
137  
    wavFile.oStream.write(wavFile.buffer, 0, 24);
138  
139  
    // Start Data Chunk
140  
    putLE(DATA_CHUNK_ID,        wavFile.buffer, 0, 4);    // Chunk ID
141  
    putLE(dataChunkSize,        wavFile.buffer, 4, 4);    // Chunk Data Size
142  
143  
    // Write Format Chunk
144  
    wavFile.oStream.write(wavFile.buffer, 0, 8);
145  
146  
    // Calculate the scaling factor for converting to a normalised double
147  
    if (wavFile.validBits > 8)
148  
    {
149  
      // If more than 8 validBits, data is signed
150  
      // Conversion required multiplying by magnitude of max positive value
151  
      wavFile.floatOffset = 0;
152  
      wavFile.floatScale = Long.MAX_VALUE >> (64 - wavFile.validBits);
153  
    }
154  
    else
155  
    {
156  
      // Else if 8 or less validBits, data is unsigned
157  
      // Conversion required dividing by max positive value
158  
      wavFile.floatOffset = 1;
159  
      wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1);
160  
    }
161  
162  
    // Finally, set the IO State
163  
    wavFile.bufferPointer = 0;
164  
    wavFile.bytesRead = 0;
165  
    wavFile.frameCounter = 0;
166  
    wavFile.ioState = IOState.WRITING;
167  
168  
    return wavFile;
169  
  }
170  
171  
  public static GSWavFile openWavFile(File file) throws IOException, RuntimeException
172  
  {
173  
    // Instantiate new Wavfile and store the file reference
174  
    GSWavFile wavFile = new GSWavFile();
175  
    wavFile.file = file;
176  
177  
    // Create a new file input stream for reading file data
178  
    wavFile.iStream = new FileInputStream(file);
179  
180  
    // Read the first 12 bytes of the file
181  
    int bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 12);
182  
    if (bytesRead != 12) throw new RuntimeException("Not enough wav file bytes for header");
183  
184  
    // Extract parts from the header
185  
    long riffChunkID = getLE(wavFile.buffer, 0, 4);
186  
    long chunkSize = getLE(wavFile.buffer, 4, 4);
187  
    long riffTypeID = getLE(wavFile.buffer, 8, 4);
188  
189  
    // Check the header bytes contains the correct signature
190  
    if (riffChunkID != RIFF_CHUNK_ID) throw new RuntimeException("Invalid Wav Header data, incorrect riff chunk ID");
191  
    if (riffTypeID != RIFF_TYPE_ID) throw new RuntimeException("Invalid Wav Header data, incorrect riff type ID");
192  
193  
    // Check that the file size matches the number of bytes listed in header
194  
    if (file.length() != chunkSize+8) {
195  
      throw new RuntimeException("Header chunk size (" + chunkSize + ") does not match file size (" + file.length() + ")");
196  
    }
197  
198  
    boolean foundFormat = false;
199  
    boolean foundData = false;
200  
201  
    // Search for the Format and Data Chunks
202  
    while (true)
203  
    {
204  
      // Read the first 8 bytes of the chunk (ID and chunk size)
205  
      bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 8);
206  
      if (bytesRead == -1) throw new RuntimeException("Reached end of file without finding format chunk");
207  
      if (bytesRead != 8) throw new RuntimeException("Could not read chunk header");
208  
209  
      // Extract the chunk ID and Size
210  
      long chunkID = getLE(wavFile.buffer, 0, 4);
211  
      chunkSize = getLE(wavFile.buffer, 4, 4);
212  
213  
      // Word align the chunk size
214  
      // chunkSize specifies the number of bytes holding data. However,
215  
      // the data should be word aligned (2 bytes) so we need to calculate
216  
      // the actual number of bytes in the chunk
217  
      long numChunkBytes = (chunkSize%2 == 1) ? chunkSize+1 : chunkSize;
218  
219  
      if (chunkID == FMT_CHUNK_ID)
220  
      {
221  
        // Flag that the format chunk has been found
222  
        foundFormat = true;
223  
224  
        // Read in the header info
225  
        bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 16);
226  
227  
        // Check this is uncompressed data
228  
        int compressionCode = (int) getLE(wavFile.buffer, 0, 2);
229  
        if (compressionCode != 1) throw new RuntimeException("Compression Code " + compressionCode + " not supported");
230  
231  
        // Extract the format information
232  
        wavFile.numChannels = (int) getLE(wavFile.buffer, 2, 2);
233  
        wavFile.sampleRate = getLE(wavFile.buffer, 4, 4);
234  
        wavFile.blockAlign = (int) getLE(wavFile.buffer, 12, 2);
235  
        wavFile.validBits = (int) getLE(wavFile.buffer, 14, 2);
236  
237  
        if (wavFile.numChannels == 0) throw new RuntimeException("Number of channels specified in header is equal to zero");
238  
        if (wavFile.blockAlign == 0) throw new RuntimeException("Block Align specified in header is equal to zero");
239  
        if (wavFile.validBits < 2) throw new RuntimeException("Valid Bits specified in header is less than 2");
240  
        if (wavFile.validBits > 64) throw new RuntimeException("Valid Bits specified in header is greater than 64, this is greater than a long can hold");
241  
242  
        // Calculate the number of bytes required to hold 1 sample
243  
        wavFile.bytesPerSample = (wavFile.validBits + 7) / 8;
244  
        if (wavFile.bytesPerSample * wavFile.numChannels != wavFile.blockAlign)
245  
          throw new RuntimeException("Block Align does not agree with bytes required for validBits and number of channels");
246  
247  
        // Account for number of format bytes and then skip over
248  
        // any extra format bytes
249  
        numChunkBytes -= 16;
250  
        if (numChunkBytes > 0) wavFile.iStream.skip(numChunkBytes);
251  
      }
252  
      else if (chunkID == DATA_CHUNK_ID)
253  
      {
254  
        // Check if we've found the format chunk,
255  
        // If not, throw an exception as we need the format information
256  
        // before we can read the data chunk
257  
        if (foundFormat == false) throw new RuntimeException("Data chunk found before Format chunk");
258  
259  
        // Check that the chunkSize (wav data length) is a multiple of the
260  
        // block align (bytes per frame)
261  
        if (chunkSize % wavFile.blockAlign != 0) throw new RuntimeException("Data Chunk size is not multiple of Block Align");
262  
263  
        // Calculate the number of frames
264  
        wavFile.numFrames = chunkSize / wavFile.blockAlign;
265  
        
266  
        // Flag that we've found the wave data chunk
267  
        foundData = true;
268  
269  
        break;
270  
      }
271  
      else
272  
      {
273  
        // If an unknown chunk ID is found, just skip over the chunk data
274  
        wavFile.iStream.skip(numChunkBytes);
275  
      }
276  
    }
277  
278  
    // Throw an exception if no data chunk has been found
279  
    if (foundData == false) throw new RuntimeException("Did not find a data chunk");
280  
281  
    // Calculate the scaling factor for converting to a normalised double
282  
    if (wavFile.validBits > 8)
283  
    {
284  
      // If more than 8 validBits, data is signed
285  
      // Conversion required dividing by magnitude of max negative value
286  
      wavFile.floatOffset = 0;
287  
      wavFile.floatScale = 1 << (wavFile.validBits - 1);
288  
    }
289  
    else
290  
    {
291  
      // Else if 8 or less validBits, data is unsigned
292  
      // Conversion required dividing by max positive value
293  
      wavFile.floatOffset = -1;
294  
      wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1);
295  
    }
296  
297  
    wavFile.bufferPointer = 0;
298  
    wavFile.bytesRead = 0;
299  
    wavFile.frameCounter = 0;
300  
    wavFile.ioState = IOState.READING;
301  
302  
    return wavFile;
303  
  }
304  
305  
  // Get and Put little endian data from local buffer
306  
  // ------------------------------------------------
307  
  private static long getLE(byte[] buffer, int pos, int numBytes)
308  
  {
309  
    numBytes --;
310  
    pos += numBytes;
311  
312  
    long val = buffer[pos] & 0xFF;
313  
    for (int b=0 ; b<numBytes ; b++) val = (val << 8) + (buffer[--pos] & 0xFF);
314  
315  
    return val;
316  
  }
317  
318  
  private static void putLE(long val, byte[] buffer, int pos, int numBytes)
319  
  {
320  
    for (int b=0 ; b<numBytes ; b++)
321  
    {
322  
      buffer[pos] = (byte) (val & 0xFF);
323  
      val >>= 8;
324  
      pos ++;
325  
    }
326  
  }
327  
328  
  // Sample Writing and Reading
329  
  // --------------------------
330  
  private void writeSample(long val) throws IOException
331  
  {
332  
    for (int b=0 ; b<bytesPerSample ; b++)
333  
    {
334  
      if (bufferPointer == BUFFER_SIZE)
335  
      {
336  
        oStream.write(buffer, 0, BUFFER_SIZE);
337  
        bufferPointer = 0;
338  
      }
339  
340  
      buffer[bufferPointer] = (byte) (val & 0xFF);
341  
      val >>= 8;
342  
      bufferPointer ++;
343  
    }
344  
  }
345  
346  
  private long readSample() throws IOException, RuntimeException
347  
  {
348  
    long val = 0;
349  
350  
    for (int b=0 ; b<bytesPerSample ; b++)
351  
    {
352  
      if (bufferPointer == bytesRead) 
353  
      {
354  
        int read = iStream.read(buffer, 0, BUFFER_SIZE);
355  
        if (read == -1) throw new RuntimeException("Not enough data available");
356  
        bytesRead = read;
357  
        bufferPointer = 0;
358  
      }
359  
360  
      int v = buffer[bufferPointer];
361  
      if (b < bytesPerSample-1 || bytesPerSample == 1) v &= 0xFF;
362  
      val += v << (b * 8);
363  
364  
      bufferPointer ++;
365  
    }
366  
367  
    return val;
368  
  }
369  
370  
  // Integer
371  
  // -------
372  
  public int readFrames(int[] sampleBuffer, int numFramesToRead) throws IOException, RuntimeException
373  
  {
374  
    return readFrames(sampleBuffer, 0, numFramesToRead);
375  
  }
376  
377  
  public int readFrames(int[] sampleBuffer, int offset, int numFramesToRead) throws IOException, RuntimeException
378  
  {
379  
    if (ioState != IOState.READING) throw new IOException("Cannot read from GSWavFile instance");
380  
381  
    for (int f=0 ; f<numFramesToRead ; f++)
382  
    {
383  
      if (frameCounter == numFrames) return f;
384  
385  
      for (int c=0 ; c<numChannels ; c++)
386  
      {
387  
        sampleBuffer[offset] = (int) readSample();
388  
        offset ++;
389  
      }
390  
391  
      frameCounter ++;
392  
    }
393  
394  
    return numFramesToRead;
395  
  }
396  
397  
  public int readFrames(int[][] sampleBuffer, int numFramesToRead) throws IOException, RuntimeException
398  
  {
399  
    return readFrames(sampleBuffer, 0, numFramesToRead);
400  
  }
401  
402  
  public int readFrames(int[][] sampleBuffer, int offset, int numFramesToRead) throws IOException, RuntimeException
403  
  {
404  
    if (ioState != IOState.READING) throw new IOException("Cannot read from GSWavFile instance");
405  
406  
    for (int f=0 ; f<numFramesToRead ; f++)
407  
    {
408  
      if (frameCounter == numFrames) return f;
409  
410  
      for (int c=0 ; c<numChannels ; c++) sampleBuffer[c][offset] = (int) readSample();
411  
412  
      offset ++;
413  
      frameCounter ++;
414  
    }
415  
416  
    return numFramesToRead;
417  
  }
418  
419  
  public int writeFrames(int[] sampleBuffer, int numFramesToWrite) throws IOException, RuntimeException
420  
  {
421  
    return writeFrames(sampleBuffer, 0, numFramesToWrite);
422  
  }
423  
424  
  public int writeFrames(int[] sampleBuffer, int offset, int numFramesToWrite) throws IOException, RuntimeException
425  
  {
426  
    if (ioState != IOState.WRITING) throw new IOException("Cannot write to GSWavFile instance");
427  
428  
    for (int f=0 ; f<numFramesToWrite ; f++)
429  
    {
430  
      if (frameCounter == numFrames) return f;
431  
432  
      for (int c=0 ; c<numChannels ; c++)
433  
      {
434  
        writeSample(sampleBuffer[offset]);
435  
        offset ++;
436  
      }
437  
438  
      frameCounter ++;
439  
    }
440  
441  
    return numFramesToWrite;
442  
  }
443  
444  
  public int writeFrames(int[][] sampleBuffer, int numFramesToWrite) throws IOException, RuntimeException
445  
  {
446  
    return writeFrames(sampleBuffer, 0, numFramesToWrite);
447  
  }
448  
449  
  public int writeFrames(int[][] sampleBuffer, int offset, int numFramesToWrite) throws IOException, RuntimeException
450  
  {
451  
    if (ioState != IOState.WRITING) throw new IOException("Cannot write to GSWavFile instance");
452  
453  
    for (int f=0 ; f<numFramesToWrite ; f++)
454  
    {
455  
      if (frameCounter == numFrames) return f;
456  
457  
      for (int c=0 ; c<numChannels ; c++) writeSample(sampleBuffer[c][offset]);
458  
459  
      offset ++;
460  
      frameCounter ++;
461  
    }
462  
463  
    return numFramesToWrite;
464  
  }
465  
466  
  // Long
467  
  // ----
468  
  public int readFrames(long[] sampleBuffer, int numFramesToRead) throws IOException, RuntimeException
469  
  {
470  
    return readFrames(sampleBuffer, 0, numFramesToRead);
471  
  }
472  
473  
  public int readFrames(long[] sampleBuffer, int offset, int numFramesToRead) throws IOException, RuntimeException
474  
  {
475  
    if (ioState != IOState.READING) throw new IOException("Cannot read from GSWavFile instance");
476  
477  
    for (int f=0 ; f<numFramesToRead ; f++)
478  
    {
479  
      if (frameCounter == numFrames) return f;
480  
481  
      for (int c=0 ; c<numChannels ; c++)
482  
      {
483  
        sampleBuffer[offset] = readSample();
484  
        offset ++;
485  
      }
486  
487  
      frameCounter ++;
488  
    }
489  
490  
    return numFramesToRead;
491  
  }
492  
493  
  public int readFrames(long[][] sampleBuffer, int numFramesToRead) throws IOException, RuntimeException
494  
  {
495  
    return readFrames(sampleBuffer, 0, numFramesToRead);
496  
  }
497  
498  
  public int readFrames(long[][] sampleBuffer, int offset, int numFramesToRead) throws IOException, RuntimeException
499  
  {
500  
    if (ioState != IOState.READING) throw new IOException("Cannot read from GSWavFile instance");
501  
502  
    for (int f=0 ; f<numFramesToRead ; f++)
503  
    {
504  
      if (frameCounter == numFrames) return f;
505  
506  
      for (int c=0 ; c<numChannels ; c++) sampleBuffer[c][offset] = readSample();
507  
508  
      offset ++;
509  
      frameCounter ++;
510  
    }
511  
512  
    return numFramesToRead;
513  
  }
514  
515  
  public int writeFrames(long[] sampleBuffer, int numFramesToWrite) throws IOException, RuntimeException
516  
  {
517  
    return writeFrames(sampleBuffer, 0, numFramesToWrite);
518  
  }
519  
520  
  public int writeFrames(long[] sampleBuffer, int offset, int numFramesToWrite) throws IOException, RuntimeException
521  
  {
522  
    if (ioState != IOState.WRITING) throw new IOException("Cannot write to GSWavFile instance");
523  
524  
    for (int f=0 ; f<numFramesToWrite ; f++)
525  
    {
526  
      if (frameCounter == numFrames) return f;
527  
528  
      for (int c=0 ; c<numChannels ; c++)
529  
      {
530  
        writeSample(sampleBuffer[offset]);
531  
        offset ++;
532  
      }
533  
534  
      frameCounter ++;
535  
    }
536  
537  
    return numFramesToWrite;
538  
  }
539  
540  
  public int writeFrames(long[][] sampleBuffer, int numFramesToWrite) throws IOException, RuntimeException
541  
  {
542  
    return writeFrames(sampleBuffer, 0, numFramesToWrite);
543  
  }
544  
545  
  public int writeFrames(long[][] sampleBuffer, int offset, int numFramesToWrite) throws IOException, RuntimeException
546  
  {
547  
    if (ioState != IOState.WRITING) throw new IOException("Cannot write to GSWavFile instance");
548  
549  
    for (int f=0 ; f<numFramesToWrite ; f++)
550  
    {
551  
      if (frameCounter == numFrames) return f;
552  
553  
      for (int c=0 ; c<numChannels ; c++) writeSample(sampleBuffer[c][offset]);
554  
555  
      offset ++;
556  
      frameCounter ++;
557  
    }
558  
559  
    return numFramesToWrite;
560  
  }
561  
562  
  // Double
563  
  // ------
564  
  public int readFrames(double[] sampleBuffer, int numFramesToRead) throws IOException, RuntimeException
565  
  {
566  
    return readFrames(sampleBuffer, 0, numFramesToRead);
567  
  }
568  
569  
  public int readFrames(double[] sampleBuffer, int offset, int numFramesToRead) throws IOException, RuntimeException
570  
  {
571  
    if (ioState != IOState.READING) throw new IOException("Cannot read from GSWavFile instance");
572  
573  
    for (int f=0 ; f<numFramesToRead ; f++)
574  
    {
575  
      if (frameCounter == numFrames) return f;
576  
577  
      for (int c=0 ; c<numChannels ; c++)
578  
      {
579  
        sampleBuffer[offset] = floatOffset + (double) readSample() / floatScale;
580  
        offset ++;
581  
      }
582  
583  
      frameCounter ++;
584  
    }
585  
586  
    return numFramesToRead;
587  
  }
588  
589  
  public int readFrames(double[][] sampleBuffer, int numFramesToRead) throws IOException, RuntimeException
590  
  {
591  
    return readFrames(sampleBuffer, 0, numFramesToRead);
592  
  }
593  
594  
  public int readFrames(double[][] sampleBuffer, int offset, int numFramesToRead) throws IOException, RuntimeException
595  
  {
596  
    if (ioState != IOState.READING) throw new IOException("Cannot read from GSWavFile instance");
597  
598  
    for (int f=0 ; f<numFramesToRead ; f++)
599  
    {
600  
      if (frameCounter == numFrames) return f;
601  
602  
      for (int c=0 ; c<numChannels ; c++) sampleBuffer[c][offset] = floatOffset + (double) readSample() / floatScale;
603  
604  
      offset ++;
605  
      frameCounter ++;
606  
    }
607  
608  
    return numFramesToRead;
609  
  }
610  
611  
  public int writeFrames(double[] sampleBuffer, int numFramesToWrite) throws IOException, RuntimeException
612  
  {
613  
    return writeFrames(sampleBuffer, 0, numFramesToWrite);
614  
  }
615  
616  
  public int writeFrames(double[] sampleBuffer, int offset, int numFramesToWrite) throws IOException, RuntimeException
617  
  {
618  
    if (ioState != IOState.WRITING) throw new IOException("Cannot write to GSWavFile instance");
619  
620  
    for (int f=0 ; f<numFramesToWrite ; f++)
621  
    {
622  
      if (frameCounter == numFrames) return f;
623  
624  
      for (int c=0 ; c<numChannels ; c++)
625  
      {
626  
        writeSample((long) (floatScale * (floatOffset + sampleBuffer[offset])));
627  
        offset ++;
628  
      }
629  
630  
      frameCounter ++;
631  
    }
632  
633  
    return numFramesToWrite;
634  
  }
635  
636  
  public int writeFrames(double[][] sampleBuffer, int numFramesToWrite) throws IOException, RuntimeException
637  
  {
638  
    return writeFrames(sampleBuffer, 0, numFramesToWrite);
639  
  }
640  
641  
  public int writeFrames(double[][] sampleBuffer, int offset, int numFramesToWrite) throws IOException, RuntimeException
642  
  {
643  
    if (ioState != IOState.WRITING) throw new IOException("Cannot write to GSWavFile instance");
644  
645  
    for (int f=0 ; f<numFramesToWrite ; f++)
646  
    {
647  
      if (frameCounter == numFrames) return f;
648  
649  
      for (int c=0 ; c<numChannels ; c++) writeSample((long) (floatScale * (floatOffset + sampleBuffer[c][offset])));
650  
651  
      offset ++;
652  
      frameCounter ++;
653  
    }
654  
655  
    return numFramesToWrite;
656  
  }
657  
658  
659  
  public void close() throws IOException
660  
  {
661  
    // Close the input stream and set to null
662  
    if (iStream != null)
663  
    {
664  
      iStream.close();
665  
      iStream = null;
666  
    }
667  
668  
    if (oStream != null) 
669  
    {
670  
      // Write out anything still in the local buffer
671  
      if (bufferPointer > 0) oStream.write(buffer, 0, bufferPointer);
672  
673  
      // If an extra byte is required for word alignment, add it to the end
674  
      if (wordAlignAdjust) oStream.write(0);
675  
676  
      // Close the stream and set to null
677  
      oStream.close();
678  
      oStream = null;
679  
    }
680  
681  
    // Flag that the stream is closed
682  
    ioState = IOState.CLOSED;
683  
  }
684  
685  
  public void display()
686  
  {
687  
    display(System.out);
688  
  }
689  
690  
  public void display(PrintStream out)
691  
  {
692  
    out.printf("File: %s\n", file);
693  
    out.printf("Channels: %d, Frames: %d\n", numChannels, numFrames);
694  
    out.printf("IO State: %s\n", ioState);
695  
    out.printf("Sample Rate: %d, Block Align: %d\n", sampleRate, blockAlign);
696  
    out.printf("Valid Bits: %d, Bytes per sample: %d\n", validBits, bytesPerSample);
697  
  }
698  
699  
  public static void main(String[] args)
700  
  {
701  
    if (args.length < 1)
702  
    {
703  
      System.err.println("Must supply filename");
704  
      System.exit(1);
705  
    }
706  
707  
    try
708  
    {
709  
      for (String filename : args)
710  
      {
711  
        GSWavFile readWavFile = openWavFile(new File(filename));
712  
        readWavFile.display();
713  
714  
        long numFrames = readWavFile.getNumFrames();
715  
        int numChannels = readWavFile.getNumChannels();
716  
        int validBits = readWavFile.getValidBits();
717  
        long sampleRate = readWavFile.getSampleRate();
718  
719  
        GSWavFile writeWavFile = newWavFile(new File("out.wav"), numChannels, numFrames, validBits, sampleRate);
720  
721  
        final int BUF_SIZE = 5001;
722  
723  
//        int[] buffer = new int[BUF_SIZE * numChannels];
724  
//        long[] buffer = new long[BUF_SIZE * numChannels];
725  
        double[] buffer = new double[BUF_SIZE * numChannels];
726  
727  
        int framesRead = 0;
728  
        int framesWritten = 0;
729  
730  
        do
731  
        {
732  
          framesRead = readWavFile.readFrames(buffer, BUF_SIZE);
733  
          framesWritten = writeWavFile.writeFrames(buffer, BUF_SIZE);
734  
          System.out.printf("%d %d\n", framesRead, framesWritten);
735  
        }
736  
        while (framesRead != 0);
737  
        
738  
        readWavFile.close();
739  
        writeWavFile.close();
740  
      }
741  
742  
      GSWavFile writeWavFile = newWavFile(new File("out2.wav"), 1, 10, 23, 44100);
743  
      double[] buffer = new double[10];
744  
      writeWavFile.writeFrames(buffer, 10);
745  
      writeWavFile.close();
746  
    }
747  
    catch (Exception e)
748  
    {
749  
      System.err.println(e);
750  
      e.printStackTrace();
751  
    }
752  
  }
753  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1027001
Snippet name: GSWavFile - helper class for reading/making WAV files
Eternal ID of this version: #1027001/6
Text MD5: d6eb43522a2f5e9281fd5e543d980f9e
Transpilation MD5: 552d93fdc1fd8910c7706495b4bdb8eb
Author: stefan
Category: javax / audio
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-02-06 18:04:50
Source code size: 25502 bytes / 753 lines
Pitched / IR pitched: No / No
Views / Downloads: 215 / 565
Version history: 5 change(s)
Referenced in: [show references]