1# ---------------------------------------------------------------------------- 2# 3# Copyright (C) 2008-2014 Fons Adriaensen <fons@linuxaudio.org> 4# 5# This program 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 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http:#www.gnu.org/licenses/>. 17# 18# ---------------------------------------------------------------------------- 19 20 21import numpy as np 22from audiotools import audiofile_ext 23 24 25class AudioFile() : 26 27 """ 28 Read/write audio file data to/from numpy arrays. 29 30 Data type of the arrays must be 32-bit float, 31 with sample values in the range (-1,+1). 32 """ 33 34 default_maxread = 1000000 35 default_sampfreq = 48000 36 default_filetype = 'wav,24bit' 37 38 MODE_RD = 1 39 MODE_WR = 2 40 41 def __init__(self): 42 """ 43 Create a new AudioFile object. 44 45 An AudioFile object has no public data members. 46 All acces should be via the function members only. 47 """ 48 self._capsule = audiofile_ext.create (self) 49 self._channels = None 50 self._sampfreq = None 51 self._filesize = None 52 self._filename = None 53 self._filemode = None 54 self._filetype = None 55 56 57 def open_read (self, filename): 58 """ 59 Open an existing audio file for reading. 60 61 Any currently open file is closed first. Returns MODE_RD, 62 or None if opening the file fails. 63 """ 64 self.close () 65 audiofile_ext.open_read (self._capsule, filename) 66 if self.info (): self._filename = filename 67 return self._filemode 68 69 70 def open_write (self, filename, channels, sampfreq, filetype = None): 71 """ 72 Create and open a new audio file for writing. 73 74 The filetype argument should be a string consisting of the 75 words listed below, comma separated and without spaces. 76 Dither is applied to 16-bit files only. 77 78 File types: caf, wav, amb, aiff, flac. 79 Sample formats: 16bit, 24bit, 32bit, float. 80 Dither type: none, rec, tri, lips. 81 82 Any currently open file is closed first. Returns MODE_WR, 83 or None if opening the file fails. 84 """ 85 self.close () 86 audiofile_ext.open_write (self._capsule, filename, channels, sampfreq, filetype) 87 if self.info (): self._filename = filename 88 return self._filemode 89 90 91 def info (self): 92 """ 93 Read parameters of the current file to local copies. 94 95 For internal use only, called by the open* members. 96 Returns the current access mode or 0. 97 """ 98 R = audiofile_ext.info (self._capsule) 99 if R [0] > 0: 100 self._filemode = R [0] 101 self._channels = R [1] 102 self._sampfreq = R [2] 103 self._filesize = R [3] 104 self._filetype = R [4] + "," + R [5] 105 return R [0] 106 107 108 def close (self): 109 """ 110 Close the current audio file. 111 """ 112 self._channels = None 113 self._sampfreq = None 114 self._filesize = None 115 self._filename = None 116 self._filemode = None 117 self._filetype = None 118 return audiofile_ext.close(self._capsule) 119 120 121 def seek (self, posit, mode = 0): 122 """ 123 Seek to new position, in frames. 124 """ 125 return audiofile_ext.seek(self._capsule, posit, mode) 126 127 128 def read (self, data): 129 """ 130 Read audio frames from file into a numpy array 'data'. 131 132 The 'data' argument can be an array or an array view 133 created by slicing or reshaping. The first dimension 134 determines the number of frames, the second dimension 135 must be equal to the number of channels in the audio 136 file. For a single channel file a 1-dimensional array 137 or view, or a python array is accepted as well. 138 139 Note that if the slicing operation results in a copy, 140 no data will be returned as the copy will be discarded 141 after being filled in. Basic slicing and reshaping 142 always create valid view. 143 """ 144 return audiofile_ext.read(self._capsule, data) 145 146 147 def write (self, data): 148 """ 149 Write audio frames from numpy array 'data' to file. 150 151 See read() for more info on the 'data' argument. 152 """ 153 return audiofile_ext.write(self._capsule, data) 154 155 156 def channels (self): 157 """ 158 Return the number of channels in the current file. 159 """ 160 return self._channels 161 162 163 def sampfreq (self): 164 """ 165 Return the sample frequency of the current file. 166 """ 167 return self._sampfreq 168 169 170 def filesize (self): 171 """ 172 Return the size of the current file, in frames. 173 """ 174 return self._filesize 175 176 177 def filename (self): 178 """ 179 Return the current filename. 180 """ 181 return self._filename 182 183 184 def filemode (self): 185 """ 186 Return the current access mode. 187 """ 188 return self._filemode 189 190 191 def filetype (self): 192 """ 193 Return a string describing the file type and format. 194 """ 195 return self._filetype 196 197 198 199def read_audio (filename, maxread = AudioFile.default_maxread): 200 """ 201 Commodity function. Read an entire file and return a 202 2-D array containing the audio data, the sample rate 203 and the file type. The optional second parameter puts 204 a limit on the number of frames read and returned. 205 """ 206 F = AudioFile () 207 F.open_read (filename) 208 ns = min (F.filesize (), maxread) 209 sf = F.sampfreq () 210 ft = F.filetype () 211 A = np.empty ([ns, F.channels ()], dtype = np.float32) 212 F.read (A) 213 F.close () 214 return (A, sf, ft) 215 216 217def write_audio (A, filename, sampfreq = AudioFile.default_sampfreq, filetype = None): 218 """ 219 Commodity fuction. Writes the vector or 2-D array A to 220 an audio file with the given sample rate and type. The 221 type options are the same as for write(). 222 """ 223 F = AudioFile () 224 if A.ndim == 1: nc = 1 225 elif A.ndim == 2: nc = A.shape [1] 226 else: raise TypeError ("Array dimension must be 1 or 2") 227 F.open_write (filename, nc, sampfreq, filetype) 228 F.write (A) 229 F.close () 230