YARP
Yet Another Robot Platform
SoundFileWav.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
8 
9 #include <yarp/conf/system.h>
10 
11 #include <yarp/os/NetInt16.h>
12 #include <yarp/os/NetInt32.h>
13 #include <yarp/os/ManagedBytes.h>
14 #include <yarp/os/Vocab.h>
15 
16 #include <yarp/sig/Sound.h>
17 #include <yarp/os/Log.h>
18 #include <yarp/os/LogStream.h>
19 
20 #include <cstdio>
21 #include <cstring>
22 
23 using namespace yarp::os;
24 using namespace yarp::sig;
25 using namespace yarp::sig::file;
26 
27 namespace
28 {
29  YARP_LOG_COMPONENT(SOUNDFILE_WAV, "yarp.sig.SoundFileWav")
30 }
31 
33 class PcmWavHeader {
34 public:
40 
41  struct {
48  } pcm;
50 
54 
57 
58  void setup_to_write(const Sound& sound, FILE *fp);
59  bool parse_from_file(FILE *fp);
60 };
62 
64 {
65  yCTrace(SOUNDFILE_WAV, "bool PcmWavHeader::parse_from_file(FILE *fp)\n");
66 
67  size_t ret;
68 
69  ret = fread(&wavHeader, sizeof(wavHeader), 1, fp);
70  if (ret != 1) {
71  yCError(SOUNDFILE_WAV, "failed to read .wav file");
72  return false;
73  }
74 
75  ret = fread(&wavLength, sizeof(wavLength), 1, fp);
76  if (ret != 1) {
77  yCError(SOUNDFILE_WAV, "failed to read .wav file");
78  return false;
79  }
80 
81  ret = fread(&formatHeader1, sizeof(formatHeader1), 1, fp);
82  if (ret != 1) {
83  yCError(SOUNDFILE_WAV, "failed to read .wav file");
84  return false;
85  }
86 
87  ret = fread(&formatHeader2, sizeof(formatHeader2), 1, fp);
88  if (ret != 1) {
89  yCError(SOUNDFILE_WAV, "failed to read .wav file");
90  return false;
91  }
92 
93  ret = fread(&formatLength, sizeof(formatLength), 1, fp);
94  if (ret != 1) {
95  yCError(SOUNDFILE_WAV, "failed to read .wav file");
96  return false;
97  }
98 
99  ret = fread(&pcm.pcmFormatTag, sizeof(pcm.pcmFormatTag), 1, fp);
100  if (ret != 1) {
101  yCError(SOUNDFILE_WAV, "failed to read .wav file");
102  return false;
103  }
104 
105  ret = fread(&pcm.pcmChannels, sizeof(pcm.pcmChannels), 1, fp);
106  if (ret != 1) {
107  yCError(SOUNDFILE_WAV, "failed to read .wav file");
108  return false;
109  }
110 
111  ret = fread(&pcm.pcmSamplesPerSecond, sizeof(pcm.pcmSamplesPerSecond), 1, fp);
112  if (ret != 1) {
113  yCError(SOUNDFILE_WAV, "failed to read .wav file");
114  return false;
115  }
116 
117  ret = fread(&pcm.pcmBytesPerSecond, sizeof(pcm.pcmBytesPerSecond), 1, fp);
118  if (ret != 1) {
119  yCError(SOUNDFILE_WAV, "failed to read .wav file");
120  return false;
121  }
122 
123  ret = fread(&pcm.pcmBlockAlign, sizeof(pcm.pcmBlockAlign), 1, fp);
124  if (ret != 1) {
125  yCError(SOUNDFILE_WAV, "failed to read .wav file");
126  return false;
127  }
128 
129  ret = fread(&pcm.pcmBitsPerSample, sizeof(pcm.pcmBitsPerSample), 1, fp);
130  if (ret != 1) {
131  yCError(SOUNDFILE_WAV, "failed to read .wav file");
132  return false;
133  }
134  if (pcm.pcmBitsPerSample != 16)
135  {
136  yCError(SOUNDFILE_WAV, "sorry, lousy .wav read code only does 16-bit ints\n");
137  return false;
138  }
139 
140  //extra bytes in pcm chuck
141  int extra_size = formatLength - sizeof(pcm);
142  if (extra_size != 0)
143  {
144  yCError(SOUNDFILE_WAV, "extra_size = %d\n", extra_size);
145  pcmExtraData.allocate(extra_size);
146  ret = fread(&pcmExtraData, extra_size, 1, fp);
147  if (ret != 1) {
148  yCError(SOUNDFILE_WAV, "failed to read .wav file");
149  return false;
150  }
151  }
152 
153  //extra chunks
154  ret = fread(&dummyHeader, sizeof(dummyHeader), 1, fp);
155  if (ret != 1) {
156  yCError(SOUNDFILE_WAV, "failed to read .wav file");
157  return false;
158  }
159 
160  while (dummyHeader != yarp::os::createVocab32('d', 'a', 't', 'a'))
161  {
162  ret = fread(&dummyLength, sizeof(dummyLength), 1, fp);
163  if (ret != 1) {
164  yCError(SOUNDFILE_WAV, "failed to read .wav file");
165  return false;
166  }
167  dummyData.clear();
168  dummyData.allocate(dummyLength);
169  ret = fread(&dummyData, dummyLength, 1, fp);
170  if (ret != 1) {
171  yCError(SOUNDFILE_WAV, "failed to read .wav file");
172  return false;
173  }
174  ret = fread(&dummyHeader, sizeof(dummyHeader), 1, fp);
175  if (ret != 1) {
176  yCError(SOUNDFILE_WAV, "failed to read .wav file");
177  return false;
178  }
179  }
180 
181  dataHeader = dummyHeader;
182  ret = fread(&dataLength, sizeof(dataLength), 1, fp);
183  if (ret != 1) {
184  yCError(SOUNDFILE_WAV, "failed to read .wav file");
185  return false;
186  }
187 
188  return true;
189 }
190 
191 void PcmWavHeader::setup_to_write(const Sound& src, FILE *fp)
192 {
193  int bitsPerSample = 16;
194  size_t channels = src.getChannels();
195  size_t bytes = channels*src.getSamples()*2;
196  int align = channels*((bitsPerSample+7)/8);
197 
198  wavHeader = yarp::os::createVocab32('R','I','F','F');
199  wavLength = bytes + sizeof(PcmWavHeader) - 2*sizeof(NetInt32);
200  formatHeader1 = yarp::os::createVocab32('W','A','V','E');
201  formatHeader2 = yarp::os::createVocab32('f','m','t',' ');
202  formatLength = sizeof(pcm);
203 
204  pcm.pcmFormatTag = 1; /* PCM! */
205  pcm.pcmChannels = channels;
206  pcm.pcmSamplesPerSecond = (int)src.getFrequency();
207  pcm.pcmBytesPerSecond = align*pcm.pcmSamplesPerSecond;
208  pcm.pcmBlockAlign = align;
209  pcm.pcmBitsPerSample = bitsPerSample;
210 
211  dataHeader = yarp::os::createVocab32('d','a','t','a');
212  dataLength = bytes;
213 
214 
215  fwrite(&wavHeader,sizeof(wavHeader),1,fp);
216  fwrite(&wavLength,sizeof(wavLength),1,fp);
217  fwrite(&formatHeader1,sizeof(formatHeader1),1,fp);
218 
219  fwrite(&formatHeader2,sizeof(formatHeader2),1,fp);
220  fwrite(&formatLength,sizeof(formatLength),1,fp);
221 
222  fwrite(&pcm.pcmFormatTag,sizeof(pcm.pcmFormatTag),1,fp);
223  fwrite(&pcm.pcmChannels,sizeof(pcm.pcmChannels),1,fp);
224  fwrite(&pcm.pcmSamplesPerSecond,sizeof(pcm.pcmSamplesPerSecond),1,fp);
225  fwrite(&pcm.pcmBytesPerSecond,sizeof(pcm.pcmBytesPerSecond),1,fp);
226  fwrite(&pcm.pcmBlockAlign,sizeof(pcm.pcmBlockAlign),1,fp);
227  fwrite(&pcm.pcmBitsPerSample,sizeof(pcm.pcmBitsPerSample),1,fp);
228 
229  fwrite(&dataHeader,sizeof(dataHeader),1,fp);
230  fwrite(&dataLength,sizeof(dataLength),1,fp);
231 
232 }
233 
234 //#######################################################################################################
235 
236 bool yarp::sig::file::read_wav_file(Sound& sound_data, const char * filename)
237 {
238  FILE *fp = fopen(filename, "rb");
239  if (!fp) {
240  yCError(SOUNDFILE_WAV, "cannot open file %s for reading\n", filename);
241  return false;
242  }
243 
244  PcmWavHeader header;
245  if (!header.parse_from_file(fp))
246  {
247  yCError(SOUNDFILE_WAV, "error parsing header of file %s\n", filename);
248  fclose(fp);
249  return false;
250  };
251 
252 
253  int freq = header.pcm.pcmSamplesPerSecond;
254  int channels = header.pcm.pcmChannels;
255  int bits = header.pcm.pcmBitsPerSample;
256  int samples = header.dataLength/(bits/8)/channels;
257  sound_data.resize(samples,channels);
258  sound_data.setFrequency(freq);
259  ManagedBytes bytes(header.dataLength);
260  yCDebug(SOUNDFILE_WAV, "%d channels %d samples %d frequency\n", channels, samples, freq);
261 
262  size_t result;
263  result = fread(bytes.get(),bytes.length(),1,fp);
264  YARP_UNUSED(result);
265 
266  auto* data = reinterpret_cast<NetInt16*>(bytes.get());
267  int ct = 0;
268  for (int i=0; i<samples; i++) {
269  for (int j=0; j<channels; j++) {
270  sound_data.set(data[ct],i,j);
271  ct++;
272  }
273  }
274 
275  fclose(fp);
276  return true;
277 }
278 
279 bool yarp::sig::file::read_wav_bytestream(Sound& data, const char* bytestream)
280 {
281  yCError(SOUNDFILE_WAV, "read_wav_bytestream() Not yet implemented");
282  return true;
283 }
284 
285 bool yarp::sig::file::write_wav_file(const Sound& sound_data, const char * filename)
286 {
287  FILE *fp = fopen(filename, "wb");
288  if (!fp) {
289  yCError(SOUNDFILE_WAV, "cannot open file %s for writing\n", filename);
290  return false;
291  }
292 
293  PcmWavHeader header;
294  header.setup_to_write(sound_data, fp);
295 
296  ManagedBytes bytes(header.dataLength);
297  auto* data = reinterpret_cast<NetInt16*>(bytes.get());
298  int ct = 0;
299  size_t samples = sound_data.getSamples();
300  size_t channels = sound_data.getChannels();
301  for (size_t i=0; i<samples; i++) {
302  for (size_t j=0; j<channels; j++) {
303  int v = sound_data.get(i,j);
304  data[ct] = v;
305  ct++;
306  }
307  }
308  fwrite(bytes.get(),bytes.length(),1,fp);
309 
310  fclose(fp);
311  return true;
312 }
int16_t * samples
bool ret
NetInt32 formatLength
NetInt32 pcmSamplesPerSecond
NetInt32 dummyHeader
NetInt32 dummyLength
ManagedBytes pcmExtraData
NetInt32 wavLength
void setup_to_write(const Sound &sound, FILE *fp)
NetInt16 pcmFormatTag
NetInt32 dataHeader
NetInt32 pcmBytesPerSecond
NetInt16 pcmChannels
NetInt32 dataLength
NetInt16 pcmBlockAlign
struct PcmWavHeader::@86 pcm
ManagedBytes dummyData
NetInt32 formatHeader2
NetInt32 formatHeader1
NetInt16 pcmBitsPerSample
bool parse_from_file(FILE *fp)
NetInt32 wavHeader
An abstraction for a block of bytes, with optional responsibility for allocating/destroying that bloc...
Definition: ManagedBytes.h:22
const char * get() const
Class for storing sounds See Audio in YARP for additional documentation on YARP audio.
Definition: Sound.h:26
void setFrequency(int freq)
Set the frequency of the sound (i.e.
Definition: Sound.cpp:229
size_t getChannels() const
Get the number of channels of the sound.
Definition: Sound.cpp:424
void resize(size_t samples, size_t channels=1)
Set the sound size.
Definition: Sound.cpp:168
int getFrequency() const
Get the frequency of the sound (i.e.
Definition: Sound.cpp:224
audio_sample get(size_t sample, size_t channel=0) const
Definition: Sound.cpp:175
void set(audio_sample value, size_t sample, size_t channel=0)
Definition: Sound.cpp:209
size_t getSamples() const
Get the number of samples contained in the sound.
Definition: Sound.cpp:419
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCTrace(component,...)
Definition: LogComponent.h:85
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:77
An interface to the operating system, including Port based communication.
std::int16_t NetInt16
Definition of the NetInt16 type.
Definition: NetInt16.h:30
std::int32_t NetInt32
Definition of the NetInt32 type.
Definition: NetInt32.h:30
constexpr yarp::conf::vocab32_t createVocab32(char a, char b=0, char c=0, char d=0)
Create a vocab from chars.
Definition: Vocab.h:28
Image file operations.
Definition: ImageFile.h:21
bool read_wav_file(Sound &data, const char *filename)
Read a sound from a .wav audio file.
bool write_wav_file(const Sound &data, const char *filename)
Write a sound to a .wav file.
bool read_wav_bytestream(Sound &data, const char *bytestream)
Read a sound from a byte array.
Signal processing.
Definition: Image.h:22
#define YARP_END_PACK
Ends 1 byte packing for structs/classes.
Definition: system.h:191
#define YARP_BEGIN_PACK
Starts 1 byte packing for structs/classes.
Definition: system.h:190
#define YARP_UNUSED(var)
Definition: api.h:162