BGC Tools
Public Types | Static Public Member Functions | Private Member Functions | Static Private Member Functions | Private Attributes
BGC.Audio.WaveEncoding Class Reference

Operations for Loading and Saving WAV files Some documentation on the WAV format is available here: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html More...

Collaboration diagram for BGC.Audio.WaveEncoding:
Collaboration graph
[legend]

Public Types

enum  Format : ushort {
  Format.UNDEFINED = 0x0000, Format.PCM = 0x0001, Format.IEEE_Float = 0x0003, Format.A_Law = 0x0006,
  Format.u_Law = 0x0007, Format.Extensible = 0xFFFE
}
 

Static Public Member Functions

static bool LoadBGCStream (string filepath, out IBGCStream stream)
 Loads a WAV file from the filepath as a SimpleAudioClip, returns success. More...
 
static bool LoadBGCSimple (string filepath, out SimpleAudioClip simpleAudioClip)
 Loads a WAV file from the filepath as a SimpleAudioClip, returns success. More...
 
static bool LoadBGCStereo (string filepath, out InterlacingAudioClip interlacingAudioClip)
 Loads a WAV file from the filepath as a InterlacingAudioClip, returns success. More...
 
static bool LoadFile (string filepath, out int channels, out float[] samples)
 Loads a WAV file from the filepath as a float array, returns success. More...
 
static bool LoadStereoFile (string filepath, out float[] leftSamples, out float[] rightSamples)
 Loads a Stereo WAV file from the filepath as two float arrays, returns success. More...
 
static bool LoadPCMFile (string filepath, out int channels, out short[] samples)
 Loads a WAV file from the filepath as a PCM short array, returns success. More...
 
static bool SaveStream (string filepath, IBGCStream stream, bool overwrite=false)
 Save the samples passed in as a WAVE file with the specified filepath. Returns success. More...
 
static bool SaveFile (string filepath, int channels, float[] samples, bool overwrite=false)
 Save the samples passed in as a WAVE file with the specified filepath. Returns success. More...
 
static bool SaveFile (string filepath, int channels, short[] samples, bool overwrite=false)
 Save the samples passed in as a WAVE file with the specified filepath. Returns success. More...
 
static bool SaveFile (string filepath, int channels, float[] leftSamples, float[] rightSamples, bool overwrite=false)
 Save the samples passed in as a WAVE file with the specified filepath. Returns success. More...
 

Private Member Functions

delegate bool DataParser< T > (byte[] rawData, Format format, int channels, out T parsedData)
 
delegate T DataResampler< T > (T inputData, double inputSamplingRate, double outputSamplingRate, int channels)
 
delegate bool DataWriter< T > (string filepath, T data, int channels)
 

Static Private Member Functions

static bool ReadFile< T > (string filepath, DataParser< T > dataParser, DataResampler< T > dataResampler, out T parsedData, out int channels)
 Internal method to load a WAV file an parse the data More...
 
static bool ReadFMT (string filepath, byte[] buffer, out int channels, out int samplingRate, out Format format)
 Read in Format Chunk. Returns success More...
 
static FileStream CreateFile (string filepath, int samples, int channels=2)
 
static bool ReadPCMDATA (byte[] buffer, Format format, int channels, out short[] samples)
 Read in DATA chunk. Returns success. More...
 
static bool SimpleReadDATA (byte[] buffer, Format format, int channels, out float[] samples)
 Read in DATA chunk. Returns success. More...
 
static bool ReadStereoDATA (byte[] buffer, Format format, int channels, out(float[] leftSamples, float[] rightSamples) samples)
 Read in Stereo DATA chunk. Returns success. More...
 
static bool ReadStereoDATA (byte[] buffer, Format format, int channels, out float[] leftSamples, out float[] rightSamples)
 Read in Stereo DATA chunk. Returns success. More...
 
static bool WriteFile< T > (string filepath, T data, int channels, DataWriter< T > dataWriter, bool overwrite=false)
 Save the samples passed in as a WAVE file with the specified filepath. Returns success. More...
 
static bool SimpleWriter (string filepath, float[] samples, int channels)
 
static bool StreamWriter (string filepath, IBGCStream stream, int channels)
 
static bool SimpleWriter (string filepath, short[] samples, int channels)
 
static bool StereoWriter (string filepath,(float[] left, float[] right) samples, int channels)
 

Private Attributes

const float toInt16Factor = 32767f
 
const float toFloatFactor = 1f / 32767f
 

Detailed Description

Operations for Loading and Saving WAV files Some documentation on the WAV format is available here: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html

Definition at line 14 of file WaveEncoding.cs.

Member Enumeration Documentation

◆ Format

enum BGC.Audio.WaveEncoding.Format : ushort
strong
Enumerator
UNDEFINED 
PCM 
IEEE_Float 
A_Law 
u_Law 
Extensible 

Definition at line 16 of file WaveEncoding.cs.

16  : ushort
17  {
18  UNDEFINED = 0x0000,
19  PCM = 0x0001,
20  IEEE_Float = 0x0003,
21  A_Law = 0x0006,
22  u_Law = 0x0007,
23  Extensible = 0xFFFE
24  }

Member Function Documentation

◆ CreateFile()

static FileStream BGC.Audio.WaveEncoding.CreateFile ( string  filepath,
int  samples,
int  channels = 2 
)
inlinestaticprivate

Definition at line 452 of file WaveEncoding.cs.

453  {
454  const int hz = 44100;
455 
456  const int waveIDSize = 4;
457  const int chunkHeaderSize = 8;
458  const int fmtSize = 18;
459  const int factSize = 4;
460  int dataSize = 2 * samples;
461 
462  FileStream fileStream = new FileStream(filepath, FileMode.Create);
463 
464  //The size of the file chunk is the "WAVE" label, plus the sizes of each chunk and each
465  //of their headers
466  int fileSize = waveIDSize + fmtSize + factSize + dataSize + 3 * chunkHeaderSize;
467 
468  fileStream.Seek(0, SeekOrigin.Begin);
469 
470  //
471  //File Chunk Header
472  //
473  //Chunk ID - 4 bytes
474  fileStream.Write(Encoding.UTF8.GetBytes("RIFF"), 0, 4);
475  //Chunk Size - 4 bytes
476  fileStream.Write(BitConverter.GetBytes(fileSize), 0, 4);
477 
478  //
479  //File Chunk
480  //
481  //Wave ID - 4 bytes
482  fileStream.Write(Encoding.UTF8.GetBytes("WAVE"), 0, 4);
483 
484  //
485  //Format Chunk Header
486  //
487  //Chunk ID - 4 bytes
488  fileStream.Write(Encoding.UTF8.GetBytes("fmt "), 0, 4);
489  //Chunk Size - 4 bytes (18 bytes of data)
490  fileStream.Write(BitConverter.GetBytes(fmtSize), 0, 4);
491 
492  //
493  //Format Chunk
494  //
495  //Format Tag - 2 bytes (PCM)
496  fileStream.Write(BitConverter.GetBytes((short)1), 0, 2);
497  //Number of Channels - 2 bytes
498  fileStream.Write(BitConverter.GetBytes((short)channels), 0, 2);
499  //Sampling Rate - 4 bytes
500  fileStream.Write(BitConverter.GetBytes(hz), 0, 4);
501  //Average Bytes Per Second - 4 bytes
502  fileStream.Write(BitConverter.GetBytes(hz * channels * 2), 0, 4);
503  //Block Align - 2 bytes
504  fileStream.Write(BitConverter.GetBytes((short)(channels * 2)), 0, 2);
505  //Bits Per Sample - 2 bytes
506  fileStream.Write(BitConverter.GetBytes((short)16), 0, 2);
507  //Extension Size - 2 bytes (0, no extension provided)
508  fileStream.Write(BitConverter.GetBytes((short)0), 0, 2);
509 
510  //
511  //Fact Chunk Header
512  //
513  //Chunk ID - 4 bytes
514  fileStream.Write(Encoding.UTF8.GetBytes("fact"), 0, 4);
515  //Chunk Size - 4 bytes
516  fileStream.Write(BitConverter.GetBytes(factSize), 0, 4);
517 
518  //
519  //Fact Chunk
520  //
521  //Sample Length - 4 bytes
522  fileStream.Write(BitConverter.GetBytes(samples / channels), 0, 4);
523 
524  //
525  //Data Chunk Header
526  //
527  //Chunk ID - 4 bytes
528  fileStream.Write(Encoding.UTF8.GetBytes("data"), 0, 4);
529  //Chunk Size - 4 bytes
530  fileStream.Write(BitConverter.GetBytes(samples * 2), 0, 4);
531 
532  return fileStream;
533  }

◆ DataParser< T >()

delegate bool BGC.Audio.WaveEncoding.DataParser< T > ( byte []  rawData,
Format  format,
int  channels,
out T  parsedData 
)
private

◆ DataResampler< T >()

delegate T BGC.Audio.WaveEncoding.DataResampler< T > ( inputData,
double  inputSamplingRate,
double  outputSamplingRate,
int  channels 
)
private

◆ DataWriter< T >()

delegate bool BGC.Audio.WaveEncoding.DataWriter< T > ( string  filepath,
data,
int  channels 
)
private

◆ LoadBGCSimple()

static bool BGC.Audio.WaveEncoding.LoadBGCSimple ( string  filepath,
out SimpleAudioClip  simpleAudioClip 
)
inlinestatic

Loads a WAV file from the filepath as a SimpleAudioClip, returns success.

Definition at line 54 of file WaveEncoding.cs.

References BGC.Audio.LinearInterpolation.Resample().

Referenced by BGC.Tests.TestOverlapAdd.TestCarlileShuffler(), and BGC.Tests.TestOverlapAdd.TestPhaseVocoding().

57  {
58  if (ReadFile(
59  filepath: filepath,
60  dataParser: SimpleReadDATA,
61  dataResampler: LinearInterpolation.Resample,
62  parsedData: out float[] samples,
63  channels: out int channels))
64  {
65  simpleAudioClip = new SimpleAudioClip(samples, channels);
66  return true;
67  }
68 
69  simpleAudioClip = null;
70  return false;
71  }
static bool SimpleReadDATA(byte[] buffer, Format format, int channels, out float[] samples)
Read in DATA chunk. Returns success.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LoadBGCStereo()

static bool BGC.Audio.WaveEncoding.LoadBGCStereo ( string  filepath,
out InterlacingAudioClip  interlacingAudioClip 
)
inlinestatic

Loads a WAV file from the filepath as a InterlacingAudioClip, returns success.

Definition at line 76 of file WaveEncoding.cs.

79  {
80  if (LoadStereoFile(
81  filepath: filepath,
82  leftSamples: out float[] leftSamples,
83  rightSamples: out float[] rightSamples))
84  {
85  interlacingAudioClip = new InterlacingAudioClip(leftSamples, rightSamples);
86  return true;
87  }
88 
89  interlacingAudioClip = null;
90  return false;
91  }
static bool LoadStereoFile(string filepath, out float[] leftSamples, out float[] rightSamples)
Loads a Stereo WAV file from the filepath as two float arrays, returns success.

◆ LoadBGCStream()

static bool BGC.Audio.WaveEncoding.LoadBGCStream ( string  filepath,
out IBGCStream  stream 
)
inlinestatic

Loads a WAV file from the filepath as a SimpleAudioClip, returns success.

Definition at line 32 of file WaveEncoding.cs.

References BGC.Audio.LinearInterpolation.Resample().

Referenced by BGC.Audio.Spatial.GetFilter(), BGC.Tests.SynthesisTests.TestAllPassFilterSpeech(), BGC.Tests.SynthesisTests.TestFMFilterVoice(), BGC.Tests.TestOverlapAdd.TestNewConvolution(), and BGC.Tests.TestOverlapAdd.TestNewSpatialization().

35  {
36  if (ReadFile(
37  filepath: filepath,
38  dataParser: SimpleReadDATA,
39  dataResampler: LinearInterpolation.Resample,
40  parsedData: out float[] samples,
41  channels: out int channels))
42  {
43  stream = new SimpleAudioClip(samples, channels);
44  return true;
45  }
46 
47  stream = null;
48  return false;
49  }
static bool SimpleReadDATA(byte[] buffer, Format format, int channels, out float[] samples)
Read in DATA chunk. Returns success.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LoadFile()

static bool BGC.Audio.WaveEncoding.LoadFile ( string  filepath,
out int  channels,
out float []  samples 
)
inlinestatic

Loads a WAV file from the filepath as a float array, returns success.

Definition at line 97 of file WaveEncoding.cs.

References BGC.Audio.LinearInterpolation.Resample().

Referenced by BGC.Tests.WAVEncodingTests.TestDualChannelWave(), and BGC.Tests.WAVEncodingTests.TestSingleChannelWave().

101  {
102  return ReadFile(
103  filepath: filepath,
104  dataParser: SimpleReadDATA,
105  dataResampler: LinearInterpolation.Resample,
106  parsedData: out samples,
107  channels: out channels);
108  }
static bool SimpleReadDATA(byte[] buffer, Format format, int channels, out float[] samples)
Read in DATA chunk. Returns success.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LoadPCMFile()

static bool BGC.Audio.WaveEncoding.LoadPCMFile ( string  filepath,
out int  channels,
out short []  samples 
)
inlinestatic

Loads a WAV file from the filepath as a PCM short array, returns success.

Definition at line 134 of file WaveEncoding.cs.

References BGC.Audio.LinearInterpolation.Resample().

138  {
139  return ReadFile(
140  filepath: filepath,
141  dataParser: ReadPCMDATA,
142  dataResampler: LinearInterpolation.Resample,
143  parsedData: out samples,
144  channels: out channels);
145  }
static bool ReadPCMDATA(byte[] buffer, Format format, int channels, out short[] samples)
Read in DATA chunk. Returns success.
Here is the call graph for this function:

◆ LoadStereoFile()

static bool BGC.Audio.WaveEncoding.LoadStereoFile ( string  filepath,
out float []  leftSamples,
out float []  rightSamples 
)
inlinestatic

Loads a Stereo WAV file from the filepath as two float arrays, returns success.

Definition at line 113 of file WaveEncoding.cs.

References BGC.Audio.LinearInterpolation.Resample().

117  {
118  bool success = ReadFile(
119  filepath: filepath,
120  dataParser: ReadStereoDATA,
121  dataResampler: LinearInterpolation.Resample,
122  parsedData: out (float[] leftSamples, float[] rightSamples) samples,
123  channels: out int channels);
124 
125  leftSamples = samples.leftSamples;
126  rightSamples = samples.rightSamples;
127 
128  return success;
129  }
static bool ReadStereoDATA(byte[] buffer, Format format, int channels, out(float[] leftSamples, float[] rightSamples) samples)
Read in Stereo DATA chunk. Returns success.
Here is the call graph for this function:

◆ ReadFile< T >()

static bool BGC.Audio.WaveEncoding.ReadFile< T > ( string  filepath,
DataParser< T >  dataParser,
DataResampler< T >  dataResampler,
out T  parsedData,
out int  channels 
)
inlinestaticprivate

Internal method to load a WAV file an parse the data

Definition at line 218 of file WaveEncoding.cs.

224  {
225  parsedData = default;
226  channels = 0;
227 
228  if (!File.Exists(filepath))
229  {
230  Debug.LogError($"File {filepath} does not exist.");
231  return false;
232  }
233 
234  using (FileStream fileStream = File.OpenRead(filepath))
235  {
236  fileStream.Seek(0, SeekOrigin.Begin);
237  byte[] smallBuffer = new byte[12];
238 
239  fileStream.Read(smallBuffer, 0, 12);
240  //RIFF
241  string header = Encoding.UTF8.GetString(smallBuffer, 0, 4);
242  Assert.IsTrue(header == "RIFF", $"Unexpected initial chunkID: {header}");
243 
244  //Chunk Size
245  int headerSize = BitConverter.ToInt32(smallBuffer, 4);
246 
247  //WAVE ID
248  string waveID = Encoding.UTF8.GetString(smallBuffer, 8, 4);
249  Assert.IsTrue(waveID == "WAVE", $"Unexpected WaveID: {waveID}");
250 
251  bool readFormat = false;
252  bool readData = false;
253 
254  int samplingRate = 44100;
255  Format format = Format.UNDEFINED;
256 
257  //Now Read Chunks
258  //Each chunk starts with an 8 byte header, so we probably reached padding if
259  //there is less than that remaining
260  while (fileStream.CanRead && (fileStream.Length - fileStream.Position) >= 8)
261  {
262  fileStream.Read(smallBuffer, 0, 8);
263  string chunkID = Encoding.UTF8.GetString(smallBuffer, 0, 4).Trim();
264  int chunkSize = BitConverter.ToInt32(smallBuffer, 4);
265 
266  //Read chunk
267  byte[] tempBuffer = new byte[chunkSize];
268  fileStream.Read(tempBuffer, 0, chunkSize);
269 
270  switch (chunkID)
271  {
272  case "fmt":
273  readFormat = ReadFMT(
274  filepath: filepath,
275  buffer: tempBuffer,
276  channels: out channels,
277  samplingRate: out samplingRate,
278  format: out format);
279  break;
280 
281  case "fact":
282  //Fact chunks report the number of samples per channel and are optional
283  //Do nothing with it
284  break;
285 
286  case "minf":
287  case "elm1":
288  case "regn":
289  case "umid":
290  case "DGDA":
291  case "JUNK":
292  case "bext":
293  case "cue":
294  case "LIST":
295  case "_PMX":
296  //Some chunks we have seen and identified as unneeded.
297  break;
298 
299  case "data":
300  if (readFormat == false)
301  {
302  Debug.LogError($"fmt chunk not found in {filepath} before data.");
303  channels = 0;
304  return false;
305  }
306 
307  if (readData)
308  {
309  Debug.LogError($"Found a second data chunk. Not built to handle that.");
310  parsedData = default;
311  channels = 0;
312  return false;
313  }
314 
315  readData = dataParser(
316  rawData: tempBuffer,
317  format: format,
318  channels: channels,
319  parsedData: out parsedData);
320  break;
321 
322  default:
323  Debug.Log($"Skipping unexpected Chunk in File {filepath}: {chunkID}.");
324  //Do nothing with it
325  break;
326  }
327  }
328 
329 
330  if (parsedData != default && samplingRate != 44100)
331  {
332  parsedData = dataResampler(
333  inputData: parsedData,
334  inputSamplingRate: samplingRate,
335  outputSamplingRate: 44100,
336  channels: channels);
337  }
338 
339  return readData;
340  }
341  }
static bool ReadFMT(string filepath, byte[] buffer, out int channels, out int samplingRate, out Format format)
Read in Format Chunk. Returns success

◆ ReadFMT()

static bool BGC.Audio.WaveEncoding.ReadFMT ( string  filepath,
byte []  buffer,
out int  channels,
out int  samplingRate,
out Format  format 
)
inlinestaticprivate

Read in Format Chunk. Returns success

Definition at line 346 of file WaveEncoding.cs.

352  {
353  Assert.IsTrue(buffer.Length >= 16, $"Unexpected fmt ChunkSize: {buffer.Length}");
354 
355  //Format Tag - 2 bytes (PCM)
356  format = (Format)BitConverter.ToUInt16(buffer, 0);
357 
358  switch (format)
359  {
360  case Format.PCM:
361  case Format.IEEE_Float:
362  case Format.Extensible:
363  case Format.u_Law:
364  //Supported
365  break;
366 
367  case Format.A_Law:
368  Debug.LogError($"Unsupported Format: ({format}) on file {filepath}");
369  channels = 0;
370  samplingRate = 0;
371  return false;
372 
373  default:
374  Debug.LogError($"Unrecognized Format: ({format}) on file {filepath}");
375  channels = 0;
376  samplingRate = 0;
377  return false;
378  }
379 
380 
381  //Number of Channels - 2 bytes
382  channels = BitConverter.ToInt16(buffer, 2);
383  Assert.IsTrue(channels == 1 || channels == 2, $"Unsupported Channels: {channels}");
384 
385  //Sampling Rate - 4 bytes
386  samplingRate = BitConverter.ToInt32(buffer, 4);
387  if (samplingRate != 44100)
388  {
389  Debug.LogWarning($"Sampling rate ({samplingRate}) not 44100. Resampling {filepath}");
390  }
391 
392  //Average Bytes Per Second - 4 bytes
393  int byteRate = BitConverter.ToInt32(buffer, 8);
394  int bytesPerSample = byteRate / (samplingRate * channels);
395  Assert.IsTrue(bytesPerSample == 2 || bytesPerSample == 4, $"Unexpected ByteRate: {byteRate}");
396 
397  //Block Align - 2 bytes
398  short blockAlign = BitConverter.ToInt16(buffer, 12);
399  Assert.IsTrue(blockAlign == channels * bytesPerSample, $"Unexpected BlockAlign: {blockAlign}");
400 
401  //Bits Per Sample - 2 bytes
402  short bps = BitConverter.ToInt16(buffer, 14);
403  Assert.IsTrue(bps == 8 * bytesPerSample, $"Unexpected BitsPerSample: {bps}");
404 
405  //Extension Size - 2 bytes
406  //(We don't care about the extension)
407  //short extensionSize = BitConverter.ToInt16(buffer, 16);
408 
409  if (format == Format.Extensible)
410  {
411  short validBitsPerSample = BitConverter.ToInt16(buffer, 18);
412  if (validBitsPerSample != 16)
413  {
414  Debug.LogError($"Unexpected ValidBitsPerSample: {validBitsPerSample}. Continuing.");
415  }
416 
417  //Real format code
418  format = (Format)BitConverter.ToUInt16(buffer, 24);
419 
420  switch (format)
421  {
422  case Format.PCM:
423  //Supported
424  break;
425 
426  case Format.IEEE_Float:
427  //Supported
428  break;
429 
430  case Format.u_Law:
431  //Supported
432  break;
433 
434  case Format.A_Law:
435  case Format.Extensible:
436  Debug.LogError($"Unexpected Data Format Code: ({format}) on file {filepath}");
437  channels = 0;
438  samplingRate = 0;
439  return false;
440 
441  default:
442  Debug.LogError($"Unrecognized Data Format Code: ({format}) on file {filepath}");
443  channels = 0;
444  samplingRate = 0;
445  return false;
446  }
447  }
448 
449  return true;
450  }

◆ ReadPCMDATA()

static bool BGC.Audio.WaveEncoding.ReadPCMDATA ( byte []  buffer,
Format  format,
int  channels,
out short []  samples 
)
inlinestaticprivate

Read in DATA chunk. Returns success.

Definition at line 553 of file WaveEncoding.cs.

558  {
559  int sampleCount;
560 
561  switch (format)
562  {
563  case Format.PCM:
564  //2 Bytes per sample
565  sampleCount = buffer.Length / 2;
566  samples = new short[sampleCount];
567  for (int i = 0; i < sampleCount; i++)
568  {
569  samples[i] = BitConverter.ToInt16(buffer, 2 * i);
570  }
571  return true;
572 
573  case Format.u_Law:
574  //2 Bytes per sample
575  sampleCount = buffer.Length / 2;
576  samples = new short[sampleCount];
577  {
578  int sampleIn;
579  int sign;
580  int mantissa;
581  int exponent;
582  int segment;
583  int step;
584 
585  for (int i = 0; i < sampleCount; i++)
586  {
587  sampleIn = BitConverter.ToInt16(buffer, 2 * i);
588  sign = sampleIn < 0b1000_0000 ? -1 : 1;
589  mantissa = ~sampleIn;
590  exponent = (mantissa >> 4) & 0b0000_0111;
591  segment = exponent + 1;
592  mantissa &= 0b0000_1111;
593  step = 4 << segment;
594  samples[i] = (short)(sign * (((0b1000_0000) << exponent) + step * mantissa + step / 2 - 4 * 33));
595  }
596 
597  }
598  return true;
599 
600  case Format.IEEE_Float:
601  case Format.UNDEFINED:
602  case Format.A_Law:
603  case Format.Extensible:
604  Debug.LogError($"Unsupported Format: {format}");
605  break;
606 
607  default:
608  Debug.LogError($"Unrecognized Format: {format}");
609  break;
610  }
611 
612 
613  samples = null;
614  return false;
615  }

◆ ReadStereoDATA() [1/2]

static bool BGC.Audio.WaveEncoding.ReadStereoDATA ( byte []  buffer,
Format  format,
int  channels,
out(float[] leftSamples, float[] rightSamples)  samples 
)
inlinestaticprivate

Read in Stereo DATA chunk. Returns success.

Definition at line 698 of file WaveEncoding.cs.

703  {
704  if (channels == 1)
705  {
706  Debug.LogError("Expected 2 channels, found 1.");
707  samples.leftSamples = null;
708  samples.rightSamples = null;
709  return false;
710  }
711 
712  int sampleCount;
713 
714  switch (format)
715  {
716  case Format.PCM:
717  //2 Channels at 2 Bytes per sample
718  sampleCount = buffer.Length / 4;
719  samples.leftSamples = new float[sampleCount];
720  samples.rightSamples = new float[sampleCount];
721 
722  for (int i = 0; i < sampleCount; i++)
723  {
724  samples.leftSamples[i] = toFloatFactor * BitConverter.ToInt16(buffer, 4 * i);
725  samples.rightSamples[i] = toFloatFactor * BitConverter.ToInt16(buffer, 4 * i + 2);
726  }
727  return true;
728 
729  case Format.IEEE_Float:
730  //2 Channels at 4 Bytes per sample
731  sampleCount = buffer.Length / 8;
732  samples.leftSamples = new float[sampleCount];
733  samples.rightSamples = new float[sampleCount];
734 
735  for (int i = 0; i < sampleCount; i++)
736  {
737  samples.leftSamples[i] = BitConverter.ToSingle(buffer, 8 * i);
738  samples.rightSamples[i] = BitConverter.ToSingle(buffer, 8 * i + 4);
739  }
740  return true;
741 
742  case Format.u_Law:
743  //2 Channels at 2 Bytes per sample
744  sampleCount = buffer.Length / 4;
745  samples.leftSamples = new float[sampleCount];
746  samples.rightSamples = new float[sampleCount];
747  {
748  int sampleIn;
749  int sampleOut;
750  int sign;
751  int mantissa;
752  int exponent;
753  int segment;
754  int step;
755 
756  for (int i = 0; i < sampleCount; i++)
757  {
758  sampleIn = BitConverter.ToInt16(buffer, 4 * i);
759  sign = sampleIn < 0b1000_0000 ? -1 : 1;
760  mantissa = ~sampleIn;
761  exponent = (mantissa >> 4) & 0b0000_0111;
762  segment = exponent + 1;
763  mantissa &= 0b0000_1111;
764  step = 4 << segment;
765  sampleOut = (sign * (((0b1000_0000) << exponent) + step * mantissa + step / 2 - 4 * 33));
766  samples.leftSamples[i] = toFloatFactor * sampleOut;
767 
768  sampleIn = BitConverter.ToInt16(buffer, 4 * i + 2);
769  sign = sampleIn < 0b1000_0000 ? -1 : 1;
770  mantissa = ~sampleIn;
771  exponent = (mantissa >> 4) & 0b0000_0111;
772  segment = exponent + 1;
773  mantissa &= 0b0000_1111;
774  step = 4 << segment;
775  sampleOut = (sign * (((0b1000_0000) << exponent) + step * mantissa + step / 2 - 4 * 33));
776  samples.rightSamples[i] = toFloatFactor * sampleOut;
777  }
778  }
779  return true;
780 
781 
782 
783 
784  case Format.UNDEFINED:
785  case Format.A_Law:
786  case Format.Extensible:
787  Debug.LogError($"Unsupported Format: {format}");
788  break;
789 
790  default:
791  Debug.LogError($"Unrecognized Format: {format}");
792  break;
793  }
794 
795  samples.leftSamples = null;
796  samples.rightSamples = null;
797  return false;
798  }
const float toFloatFactor
Definition: WaveEncoding.cs:27

◆ ReadStereoDATA() [2/2]

static bool BGC.Audio.WaveEncoding.ReadStereoDATA ( byte []  buffer,
Format  format,
int  channels,
out float []  leftSamples,
out float []  rightSamples 
)
inlinestaticprivate

Read in Stereo DATA chunk. Returns success.

Definition at line 803 of file WaveEncoding.cs.

809  {
810  int sampleCount;
811 
812  switch (format)
813  {
814  case Format.PCM:
815  //2 Channels at 2 Bytes per sample
816  sampleCount = buffer.Length / 4;
817  leftSamples = new float[sampleCount];
818  rightSamples = new float[sampleCount];
819 
820  for (int i = 0; i < sampleCount; i++)
821  {
822  leftSamples[i] = toFloatFactor * BitConverter.ToInt16(buffer, 4 * i);
823  rightSamples[i] = toFloatFactor * BitConverter.ToInt16(buffer, 4 * i + 2);
824  }
825  return true;
826 
827  case Format.IEEE_Float:
828  //2 Channels at 4 Bytes per sample
829  sampleCount = buffer.Length / 8;
830  leftSamples = new float[sampleCount];
831  rightSamples = new float[sampleCount];
832 
833  for (int i = 0; i < sampleCount; i++)
834  {
835  leftSamples[i] = BitConverter.ToSingle(buffer, 8 * i);
836  rightSamples[i] = BitConverter.ToSingle(buffer, 8 * i + 4);
837  }
838  return true;
839 
840  case Format.u_Law:
841  //2 Channels at 2 Bytes per sample
842  sampleCount = buffer.Length / 4;
843  leftSamples = new float[sampleCount];
844  rightSamples = new float[sampleCount];
845  {
846  int sampleIn;
847  int sampleOut;
848  int sign;
849  int mantissa;
850  int exponent;
851  int segment;
852  int step;
853 
854  for (int i = 0; i < sampleCount; i++)
855  {
856  sampleIn = BitConverter.ToInt16(buffer, 4 * i);
857  sign = sampleIn < 0b1000_0000 ? -1 : 1;
858  mantissa = ~sampleIn;
859  exponent = (mantissa >> 4) & 0b0000_0111;
860  segment = exponent + 1;
861  mantissa &= 0b0000_1111;
862  step = 4 << segment;
863  sampleOut = (sign * (((0b1000_0000) << exponent) + step * mantissa + step / 2 - 4 * 33));
864  leftSamples[i] = toFloatFactor * sampleOut;
865 
866  sampleIn = BitConverter.ToInt16(buffer, 4 * i + 2);
867  sign = sampleIn < 0b1000_0000 ? -1 : 1;
868  mantissa = ~sampleIn;
869  exponent = (mantissa >> 4) & 0b0000_0111;
870  segment = exponent + 1;
871  mantissa &= 0b0000_1111;
872  step = 4 << segment;
873  sampleOut = (sign * (((0b1000_0000) << exponent) + step * mantissa + step / 2 - 4 * 33));
874  rightSamples[i] = toFloatFactor * sampleOut;
875  }
876  }
877  return true;
878 
879  case Format.UNDEFINED:
880  case Format.A_Law:
881  case Format.Extensible:
882  Debug.LogError($"Unsupported Format: {format}");
883  break;
884 
885  default:
886  Debug.LogError($"Unrecognized Format: {format}");
887  break;
888  }
889 
890  leftSamples = null;
891  rightSamples = null;
892  return false;
893  }
const float toFloatFactor
Definition: WaveEncoding.cs:27

◆ SaveFile() [1/3]

static bool BGC.Audio.WaveEncoding.SaveFile ( string  filepath,
int  channels,
float []  samples,
bool  overwrite = false 
)
inlinestatic

Save the samples passed in as a WAVE file with the specified filepath. Returns success.

Definition at line 166 of file WaveEncoding.cs.

Referenced by BGC.Tests.TestOverlapAdd.TestCarlileShuffler(), BGC.Tests.WAVEncodingTests.TestDualChannelWave(), BGC.Tests.TestOverlapAdd.TestPhaseVocoding(), and BGC.Tests.WAVEncodingTests.TestSingleChannelWave().

171  {
172  return WriteFile(
173  filepath: filepath,
174  data: samples,
175  channels: channels,
176  dataWriter: SimpleWriter,
177  overwrite: overwrite);
178  }
static bool SimpleWriter(string filepath, float[] samples, int channels)
Here is the caller graph for this function:

◆ SaveFile() [2/3]

static bool BGC.Audio.WaveEncoding.SaveFile ( string  filepath,
int  channels,
short []  samples,
bool  overwrite = false 
)
inlinestatic

Save the samples passed in as a WAVE file with the specified filepath. Returns success.

Definition at line 183 of file WaveEncoding.cs.

188  {
189  return WriteFile(
190  filepath: filepath,
191  data: samples,
192  channels: channels,
193  dataWriter: SimpleWriter,
194  overwrite: overwrite);
195  }
static bool SimpleWriter(string filepath, float[] samples, int channels)

◆ SaveFile() [3/3]

static bool BGC.Audio.WaveEncoding.SaveFile ( string  filepath,
int  channels,
float []  leftSamples,
float []  rightSamples,
bool  overwrite = false 
)
inlinestatic

Save the samples passed in as a WAVE file with the specified filepath. Returns success.

Definition at line 200 of file WaveEncoding.cs.

206  {
207  return WriteFile(
208  filepath: filepath,
209  data: (leftSamples, rightSamples),
210  channels: channels,
211  dataWriter: StereoWriter,
212  overwrite: overwrite);
213  }
static bool StereoWriter(string filepath,(float[] left, float[] right) samples, int channels)

◆ SaveStream()

static bool BGC.Audio.WaveEncoding.SaveStream ( string  filepath,
IBGCStream  stream,
bool  overwrite = false 
)
inlinestatic

Save the samples passed in as a WAVE file with the specified filepath. Returns success.

Definition at line 150 of file WaveEncoding.cs.

References BGC.Audio.IBGCStream.Channels.

Referenced by BGC.Tests.SynthesisTests.FMTestBell(), BGC.Tests.SynthesisTests.FMTestBrass(), BGC.Tests.SynthesisTests.FMTestDrum(), BGC.Tests.SynthesisTests.FMTestOvertones(), BGC.Tests.SynthesisTests.FMTestPiano(), BGC.Tests.SynthesisTests.FMTestTrianglePiano(), BGC.Tests.SynthesisTests.SmallAnalyticFMTest(), BGC.Tests.SynthesisTests.SmallSineFMTest(), BGC.Tests.SynthesisTests.SynthTestSnare(), BGC.Tests.SynthesisTests.TestBiQuadFilters(), BGC.Tests.SynthesisTests.TestCarrierModifiedFakeVoices(), BGC.Tests.MidiEncodingTests.TestContinuousFilter(), BGC.Tests.SynthesisTests.TestFakeVoice(), BGC.Tests.SynthesisTests.TestFDComposer(), BGC.Tests.SynthesisTests.TestFDComposerPure(), BGC.Tests.MidiEncodingTests.TestFlute(), BGC.Tests.SynthesisTests.TestFrequencyShifter(), BGC.Tests.SynthesisTests.TestFunFakeVoice(), BGC.Tests.MidiEncodingTests.TestGuitar(), BGC.Tests.MidiEncodingTests.TestHiHat(), BGC.Tests.TestOverlapAdd.TestNewConvolution(), BGC.Tests.TestOverlapAdd.TestNewSpatialization(), BGC.Tests.MidiEncodingTests.TestOrgan(), BGC.Tests.SynthesisTests.TestPhaseReEncoder(), BGC.Tests.MidiEncodingTests.TestPulses(), BGC.Tests.MidiEncodingTests.TestRenderMidi(), BGC.Tests.MidiEncodingTests.TestRenderToccataMidi(), BGC.Tests.SynthesisTests.TestSawtoothWave(), BGC.Tests.SynthesisTests.TestSineWave(), BGC.Tests.MidiEncodingTests.TestSnare(), BGC.Tests.SynthesisTests.TestSquareWave(), BGC.Tests.SynthesisTests.TestSTM(), BGC.Tests.SynthesisTests.TestTriangleWave(), BGC.Tests.SynthesisTests.TryAllPassFilter(), BGC.Tests.SynthesisTests.TryFMFilter(), and BGC.Tests.WAVEncodingTests.UpScalingTest().

154  {
155  return WriteFile(
156  filepath: filepath,
157  data: stream,
158  channels: stream.Channels,
159  dataWriter: StreamWriter,
160  overwrite: overwrite);
161  }
static bool StreamWriter(string filepath, IBGCStream stream, int channels)
Here is the caller graph for this function:

◆ SimpleReadDATA()

static bool BGC.Audio.WaveEncoding.SimpleReadDATA ( byte []  buffer,
Format  format,
int  channels,
out float []  samples 
)
inlinestaticprivate

Read in DATA chunk. Returns success.

Definition at line 620 of file WaveEncoding.cs.

625  {
626  int sampleCount;
627 
628  switch (format)
629  {
630  case Format.PCM:
631  //2 Bytes per sample
632  sampleCount = buffer.Length / 2;
633  samples = new float[sampleCount];
634  for (int i = 0; i < sampleCount; i++)
635  {
636  samples[i] = toFloatFactor * BitConverter.ToInt16(buffer, 2 * i);
637  }
638  return true;
639 
640  case Format.IEEE_Float:
641  //4 Bytes per sample
642  sampleCount = buffer.Length / 4;
643  samples = new float[sampleCount];
644  for (int i = 0; i < sampleCount; i++)
645  {
646  samples[i] = BitConverter.ToSingle(buffer, 4 * i);
647  }
648  return true;
649 
650  case Format.u_Law:
651  //2 Bytes per sample
652  sampleCount = buffer.Length / 2;
653  samples = new float[sampleCount];
654  {
655  int sampleIn;
656  int sampleOut;
657  int sign;
658  int mantissa;
659  int exponent;
660  int segment;
661  int step;
662 
663  for (int i = 0; i < sampleCount; i++)
664  {
665  sampleIn = BitConverter.ToInt16(buffer, 2 * i);
666  sign = sampleIn < 0b1000_0000 ? -1 : 1;
667  mantissa = ~sampleIn;
668  exponent = (mantissa >> 4) & 0b0000_0111;
669  segment = exponent + 1;
670  mantissa &= 0b0000_1111;
671  step = 4 << segment;
672  sampleOut = (sign * (((0b1000_0000) << exponent) + step * mantissa + step / 2 - 4 * 33));
673  samples[i] = toFloatFactor * sampleOut;
674  }
675 
676  }
677  return true;
678 
679  case Format.UNDEFINED:
680  case Format.A_Law:
681  case Format.Extensible:
682  Debug.LogError($"Unsupported Format: {format}");
683  break;
684 
685  default:
686  Debug.LogError($"Unrecognized Format: {format}");
687  break;
688  }
689 
690 
691  samples = null;
692  return false;
693  }
const float toFloatFactor
Definition: WaveEncoding.cs:27

◆ SimpleWriter() [1/2]

static bool BGC.Audio.WaveEncoding.SimpleWriter ( string  filepath,
float []  samples,
int  channels 
)
inlinestaticprivate

Definition at line 936 of file WaveEncoding.cs.

937  {
938  using (FileStream fileStream = CreateFile(filepath, samples.Length, channels))
939  {
940  //converting in 2 float[] steps to Int16[], //then Int16[] to Byte[]
941  short[] intData = new short[samples.Length];
942 
943  //bytesData array is twice the size of dataSource array because Int16 is 2 bytes.
944  byte[] bytesData = new byte[samples.Length * 2];
945 
946  for (int i = 0; i < samples.Length; i++)
947  {
948  intData[i] = (short)(samples[i] * toInt16Factor);
949  }
950  Buffer.BlockCopy(intData, 0, bytesData, 0, bytesData.Length);
951  fileStream.Write(bytesData, 0, bytesData.Length);
952  }
953 
954  return true;
955  }
const float toInt16Factor
Definition: WaveEncoding.cs:26
static FileStream CreateFile(string filepath, int samples, int channels=2)

◆ SimpleWriter() [2/2]

static bool BGC.Audio.WaveEncoding.SimpleWriter ( string  filepath,
short []  samples,
int  channels 
)
inlinestaticprivate

Definition at line 990 of file WaveEncoding.cs.

991  {
992  using (FileStream fileStream = CreateFile(filepath, samples.Length, channels))
993  {
994  //bytesData array is twice the size of dataSource array because Int16 is 2 bytes.
995  byte[] bytesData = new byte[samples.Length * 2];
996  Buffer.BlockCopy(samples, 0, bytesData, 0, bytesData.Length);
997  fileStream.Write(bytesData, 0, bytesData.Length);
998  }
999 
1000  return true;
1001  }
static FileStream CreateFile(string filepath, int samples, int channels=2)

◆ StereoWriter()

static bool BGC.Audio.WaveEncoding.StereoWriter ( string  filepath,
(float[] left, float[] right)  samples,
int  channels 
)
inlinestaticprivate

Definition at line 1003 of file WaveEncoding.cs.

1004  {
1005  if (samples.left.Length != samples.right.Length)
1006  {
1007  Debug.LogError("Left and Right channels must be the same length");
1008  return false;
1009  }
1010 
1011  using (FileStream fileStream = CreateFile(filepath, 2 * samples.left.Length, channels))
1012  {
1013  //converting in 2 float[] steps to Int16[], //then Int16[] to Byte[]
1014  short[] intData = new short[2 * samples.left.Length];
1015 
1016  //bytesData array is twice the size of dataSource array because Int16 is 2 bytes.
1017  byte[] bytesData = new byte[4 * samples.left.Length];
1018 
1019  for (int i = 0; i < samples.left.Length; i++)
1020  {
1021  intData[2 * i] = (short)(samples.left[i] * toInt16Factor);
1022  intData[2 * i + 1] = (short)(samples.right[i] * toInt16Factor);
1023  }
1024  Buffer.BlockCopy(intData, 0, bytesData, 0, bytesData.Length);
1025  fileStream.Write(bytesData, 0, bytesData.Length);
1026  }
1027 
1028  return true;
1029  }
const float toInt16Factor
Definition: WaveEncoding.cs:26
static FileStream CreateFile(string filepath, int samples, int channels=2)

◆ StreamWriter()

static bool BGC.Audio.WaveEncoding.StreamWriter ( string  filepath,
IBGCStream  stream,
int  channels 
)
inlinestaticprivate

Definition at line 957 of file WaveEncoding.cs.

References BGC.Audio.IBGCStream.Read(), BGC.Audio.IBGCStream.Reset(), and BGC.Audio.IBGCStream.TotalSamples.

958  {
959  using (FileStream fileStream = CreateFile(filepath, stream.TotalSamples, channels))
960  {
961  stream.Reset();
962  const int BUFFER_SIZE = 512;
963  float[] buffer = new float[BUFFER_SIZE];
964 
965  //converting in 2 float[] steps to Int16[], //then Int16[] to Byte[]
966  short[] intData = new short[BUFFER_SIZE];
967  //bytesData array is twice the size of dataSource array because Int16 is 2 bytes.
968  byte[] bytesData = new byte[BUFFER_SIZE * 2];
969 
970  int readSamples;
971 
972  do
973  {
974  readSamples = stream.Read(buffer, 0, BUFFER_SIZE);
975  for (int i = 0; i < readSamples; i++)
976  {
977  intData[i] = (short)(buffer[i] * toInt16Factor);
978  }
979  Buffer.BlockCopy(intData, 0, bytesData, 0, 2 * readSamples);
980  fileStream.Write(bytesData, 0, 2 * readSamples);
981 
982  }
983  while (readSamples == BUFFER_SIZE);
984  }
985  stream.Reset();
986 
987  return true;
988  }
const float toInt16Factor
Definition: WaveEncoding.cs:26
static FileStream CreateFile(string filepath, int samples, int channels=2)
Here is the call graph for this function:

◆ WriteFile< T >()

static bool BGC.Audio.WaveEncoding.WriteFile< T > ( string  filepath,
data,
int  channels,
DataWriter< T >  dataWriter,
bool  overwrite = false 
)
inlinestaticprivate

Save the samples passed in as a WAVE file with the specified filepath. Returns success.

Definition at line 906 of file WaveEncoding.cs.

912  {
913  try
914  {
915  if (File.Exists(filepath))
916  {
917  if (overwrite)
918  {
919  File.Delete(filepath);
920  }
921  else
922  {
923  filepath = IO.DataManagement.NextAvailableFilePath(filepath);
924  }
925  }
926 
927  return dataWriter(filepath, data, channels);
928  }
929  catch (IOException excp)
930  {
931  Debug.LogException(excp);
932  return false;
933  }
934  }

Field Documentation

◆ toFloatFactor

const float BGC.Audio.WaveEncoding.toFloatFactor = 1f / 32767f
private

Definition at line 27 of file WaveEncoding.cs.

◆ toInt16Factor

const float BGC.Audio.WaveEncoding.toInt16Factor = 32767f
private

Definition at line 26 of file WaveEncoding.cs.


The documentation for this class was generated from the following file: