wavio.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. # wavio.py
  2. # Author: Warren Weckesser
  3. # License: BSD 3-Clause (http://opensource.org/licenses/BSD-3-Clause)
  4. # Synopsis: A Python module for reading and writing 24 bit WAV files.
  5. # Github: github.com/WarrenWeckesser/wavio
  6. import wave as _wave
  7. import numpy as _np
  8. def _wav2array(nchannels, sampwidth, data):
  9. """data must be the string containing the bytes from the wav file."""
  10. num_samples, remainder = divmod(len(data), sampwidth * nchannels)
  11. if remainder > 0:
  12. raise ValueError('The length of data is not a multiple of '
  13. 'sampwidth * num_channels.')
  14. if sampwidth > 4:
  15. raise ValueError("sampwidth must not be greater than 4.")
  16. if sampwidth == 3:
  17. a = _np.empty((num_samples, nchannels, 4), dtype=_np.uint8)
  18. raw_bytes = _np.fromstring(data, dtype=_np.uint8)
  19. a[:, :, :sampwidth] = raw_bytes.reshape(-1, nchannels, sampwidth)
  20. a[:, :, sampwidth:] = (a[:, :, sampwidth - 1:sampwidth] >> 7) * 255
  21. result = a.view('<i4').reshape(a.shape[:-1])
  22. else:
  23. # 8 bit samples are stored as unsigned ints; others as signed ints.
  24. dt_char = 'u' if sampwidth == 1 else 'i'
  25. a = _np.fromstring(data, dtype='<%s%d' % (dt_char, sampwidth))
  26. result = a.reshape(-1, nchannels)
  27. return result
  28. def readwav(file):
  29. """
  30. Read a WAV file.
  31. Parameters
  32. ----------
  33. file : string or file object
  34. Either the name of a file or an open file pointer.
  35. Return Values
  36. -------------
  37. rate : float
  38. The sampling frequency (i.e. frame rate)
  39. sampwidth : float
  40. The sample width, in bytes. E.g. for a 24 bit WAV file,
  41. sampwidth is 3.
  42. data : numpy array
  43. The array containing the data. The shape of the array is
  44. (num_samples, num_channels). num_channels is the number of
  45. audio channels (1 for mono, 2 for stereo).
  46. Notes
  47. -----
  48. This function uses the `wave` module of the Python standard libary
  49. to read the WAV file, so it has the same limitations as that library.
  50. In particular, the function does not read compressed WAV files.
  51. """
  52. wav = _wave.open(file)
  53. rate = wav.getframerate()
  54. nchannels = wav.getnchannels()
  55. sampwidth = wav.getsampwidth()
  56. nframes = wav.getnframes()
  57. data = wav.readframes(nframes)
  58. wav.close()
  59. array = _wav2array(nchannels, sampwidth, data)
  60. return rate, sampwidth, array
  61. def writewav24(filename, rate, data):
  62. """
  63. Create a 24 bit wav file.
  64. Parameters
  65. ----------
  66. filename : string
  67. Name of the file to create.
  68. rate : float
  69. The sampling frequency (i.e. frame rate) of the data.
  70. data : array-like collection of integer or floating point values
  71. data must be "array-like", either 1- or 2-dimensional. If it
  72. is 2-d, the rows are the frames (i.e. samples) and the columns
  73. are the channels.
  74. Notes
  75. -----
  76. The data is assumed to be signed, and the values are assumed to be
  77. within the range of a 24 bit integer. Floating point values are
  78. converted to integers. The data is not rescaled or normalized before
  79. writing it to the file.
  80. Example
  81. -------
  82. Create a 3 second 440 Hz sine wave.
  83. >>> rate = 22050 # samples per second
  84. >>> T = 3 # sample duration (seconds)
  85. >>> f = 440.0 # sound frequency (Hz)
  86. >>> t = np.linspace(0, T, T*rate, endpoint=False)
  87. >>> x = (2**23 - 1) * np.sin(2 * np.pi * f * t)
  88. >>> writewav24("sine24.wav", rate, x)
  89. """
  90. a32 = _np.asarray(data, dtype=_np.int32)
  91. if a32.ndim == 1:
  92. # Convert to a 2D array with a single column.
  93. a32.shape = a32.shape + (1,)
  94. # By shifting first 0 bits, then 8, then 16, the resulting output
  95. # is 24 bit little-endian.
  96. a8 = (a32.reshape(a32.shape + (1,)) >> _np.array([0, 8, 16])) & 255
  97. wavdata = a8.astype(_np.uint8).tostring()
  98. w = _wave.open(filename, 'wb')
  99. w.setnchannels(a32.shape[1])
  100. w.setsampwidth(3)
  101. w.setframerate(rate)
  102. w.writeframes(wavdata)
  103. w.close()