1 /* Sound_audio.cpp
2 *
3 * Copyright (C) 1992-2020 Paul Boersma
4 *
5 * This code 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 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the 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 work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <errno.h>
20
21 #ifdef linux
22 #define DEV_AUDIO "/dev/dsp"
23 #else
24 #define DEV_AUDIO "/dev/audio"
25 #endif
26
27 #include "Sound.h"
28 #include "Preferences.h"
29 #include "../external/portaudio/portaudio.h"
30
31 #if defined (macintosh)
32 #include "macport_on.h"
33 #include "pa_mac_core.h"
34 #include "macport_off.h"
35 #elif defined (_WIN32)
36 #include "winport_on.h"
37 #include <windows.h>
38 #include <mmsystem.h>
39 #include "winport_off.h"
40 #elif defined (linux)
41 #include <fcntl.h>
42 #if ! defined (NO_AUDIO)
43 #if defined (__OpenBSD__) || defined (__NetBSD__)
44 #include <soundcard.h>
45 #else
46 #include <sys/soundcard.h>
47 #endif
48 #endif
49 #include <sys/ioctl.h> /* ioctl */
50 #include <unistd.h> /* open write close read */
51 #else
52 #include <fcntl.h>
53 #endif
54
55 struct Sound_recordFixedTime_Info {
56 integer numberOfSamples, numberOfSamplesRead;
57 short *buffer;
58 };
getNumberOfSamplesRead(volatile struct Sound_recordFixedTime_Info * info)59 static integer getNumberOfSamplesRead (volatile struct Sound_recordFixedTime_Info *info) {
60 volatile integer numberOfSamplesRead = info -> numberOfSamplesRead;
61 return numberOfSamplesRead;
62 }
63
portaudioStreamCallback(const void * input,void *,unsigned long frameCount,const PaStreamCallbackTimeInfo *,PaStreamCallbackFlags,void * void_info)64 static int portaudioStreamCallback (
65 const void *input, void * /*output*/,
66 unsigned long frameCount,
67 const PaStreamCallbackTimeInfo * /*timeInfo*/,
68 PaStreamCallbackFlags /*statusFlags*/,
69 void *void_info)
70 {
71 struct Sound_recordFixedTime_Info *info = (struct Sound_recordFixedTime_Info *) void_info;
72 integer samplesLeft = info -> numberOfSamples - info -> numberOfSamplesRead;
73 if (samplesLeft > 0) {
74 integer dsamples = std::min (samplesLeft, uinteger_to_integer (frameCount));
75 memcpy (info -> buffer + info -> numberOfSamplesRead, input, integer_to_uinteger (2 * dsamples));
76 info -> numberOfSamplesRead += dsamples;
77 const short *input2 = (const short *) input;
78 //Melder_casual (U"read ", dsamples, U" samples: ", input2 [0], U", ", input2 [1], U", ", input2 [3], U"...");
79 if (info -> numberOfSamplesRead >= info -> numberOfSamples)
80 return paComplete;
81 } else /*if (info -> numberOfSamplesRead >= info -> numberOfSamples)*/ {
82 info -> numberOfSamplesRead = info -> numberOfSamples;
83 return paComplete;
84 }
85 return paContinue;
86 }
87
Sound_record_fixedTime(int inputSource,double gain,double balance,double sampleRate,double duration)88 autoSound Sound_record_fixedTime (int inputSource, double gain, double balance, double sampleRate, double duration) {
89 bool inputUsesPortAudio =
90 #if defined (_WIN32)
91 MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::MME_VIA_PORTAUDIO;
92 #elif defined (macintosh)
93 MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::COREAUDIO_VIA_PORTAUDIO;
94 #elif defined (raspberrypi)
95 MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::JACK_VIA_PORTAUDIO;
96 #else
97 MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::ALSA_VIA_PORTAUDIO;
98 #endif
99 PaStream *portaudioStream = nullptr;
100 #if defined (macintosh)
101 #elif defined (_WIN32)
102 HWAVEIN hWaveIn = 0;
103 #else
104 int fd = -1; // other systems use stream I/O with a file descriptor
105 int fd_mixer = -1;
106 #endif
107 try {
108 integer numberOfSamples, i;
109
110 /*
111 Declare platform-dependent data structures.
112 */
113 volatile struct Sound_recordFixedTime_Info info = { 0 };
114 PaStreamParameters streamParameters = { 0 };
115 #if defined (macintosh)
116 (void) gain;
117 (void) balance;
118 #elif defined (_WIN32)
119 WAVEFORMATEX waveFormat;
120 WAVEHDR waveHeader;
121 MMRESULT err;
122 (void) inputSource;
123 (void) gain;
124 (void) balance;
125 #elif defined (linux)
126 int dev_mask;
127 int val;
128 #endif
129
130 /*
131 Check representation of shorts.
132 */
133 if (sizeof (short) != 2)
134 Melder_throw (U"Cannot record a sound on this computer.");
135
136 /*
137 Check sampling frequency.
138 */
139 bool supportsSamplingFrequency = true;
140 if (inputUsesPortAudio) {
141 #if defined (macintosh)
142 if (sampleRate != 44100 && sampleRate != 48000 && sampleRate != 96000)
143 supportsSamplingFrequency = false;
144 #endif
145 } else {
146 #if defined (macintosh)
147 if (sampleRate != 44100)
148 supportsSamplingFrequency = false;
149 #elif defined (linux)
150 if (sampleRate != 8000 && sampleRate != 11025 &&
151 sampleRate != 16000 && sampleRate != 22050 &&
152 sampleRate != 32000 && sampleRate != 44100 &&
153 sampleRate != 48000)
154 supportsSamplingFrequency = false;
155 #elif defined (_WIN32)
156 if (sampleRate != 8000 && sampleRate != 11025 &&
157 sampleRate != 16000 && sampleRate != 22050 &&
158 sampleRate != 32000 && sampleRate != 44100 &&
159 sampleRate != 48000 && sampleRate != 96000)
160 supportsSamplingFrequency = false;
161 #endif
162 }
163 if (! supportsSamplingFrequency)
164 Melder_throw (U"Your audio hardware does not support a sampling frequency of ", sampleRate, U" Hz.");
165
166 /*
167 Open phase 1.
168 On some platforms, the info is filled in before the audio port is opened.
169 On other platforms, the info is filled in after the port is opened.
170 */
171 if (inputUsesPortAudio) {
172 if (! MelderAudio_hasBeenInitialized) {
173 PaError err = Pa_Initialize ();
174 if (err)
175 Melder_throw (U"Pa_Initialize: ", Melder_peek8to32 (Pa_GetErrorText (err)));
176 MelderAudio_hasBeenInitialized = true;
177 }
178 } else {
179 #if defined (macintosh)
180 #elif defined (_WIN32)
181 #elif ! defined (NO_AUDIO)
182 /*
183 We must open the port now, because we use an ioctl to set the info to an open port.
184 */
185 fd = open (DEV_AUDIO, O_RDONLY);
186 if (fd == -1) {
187 if (errno == EBUSY)
188 Melder_throw (U"Audio device in use by another program.");
189 else
190 #ifdef linux
191 Melder_throw (U"Cannot open audio device.\nPlease switch on PortAudio in the Sound Recording Preferences.");
192 #else
193 Melder_throw (U"Cannot open audio device.");
194 #endif
195 }
196 /*
197 The device immediately started recording into its buffer,
198 but probably at the wrong rate etc.
199 Pause and flush this rubbish.
200 */
201 #if defined (linux)
202 ioctl (fd, SNDCTL_DSP_RESET, nullptr);
203 #endif
204 #endif
205 }
206
207 /*
208 Set the input source; the default is the microphone.
209 */
210 if (inputUsesPortAudio) {
211 if (inputSource < 1 || inputSource > Pa_GetDeviceCount ())
212 Melder_throw (U"Unknown device #", inputSource, U".");
213 /*
214 Saying
215 streamParameters. device = inputSource - 1;
216 would presuppose that the input devices are listed before the output devices.
217 TODO: cycle through all devices, and determine which of them are input devices
218 */
219 streamParameters. device = Pa_GetDefaultInputDevice ();
220 Melder_casual (U"streamParameters. device: ", (integer) streamParameters. device);
221 const PaDeviceInfo *paDeviceInfo = Pa_GetDeviceInfo (streamParameters. device);
222 Melder_casual (U"Name: ", Melder_peek8to32 (paDeviceInfo -> name));
223 } else {
224 #if defined (macintosh)
225 #elif defined (linux) && ! defined (NO_AUDIO)
226 fd_mixer = open ("/dev/mixer", O_WRONLY);
227 if (fd_mixer == -1)
228 Melder_throw (U"Cannot open /dev/mixer.");
229 dev_mask = inputSource == 1 ? SOUND_MASK_MIC : SOUND_MASK_LINE;
230 if (ioctl (fd_mixer, SOUND_MIXER_WRITE_RECSRC, & dev_mask) == -1)
231 Melder_throw (U"Cannot set recording device in mixer");
232 #endif
233 }
234
235 /*
236 Set gain and balance.
237 */
238 if (inputUsesPortAudio) {
239 /* Taken from Audio Control Panel. */
240 } else {
241 #if defined (macintosh) || defined (_WIN32)
242 /* Taken from Audio Control Panel. */
243 #elif defined (linux) && ! defined (NO_AUDIO)
244 val = ( gain <= 0.0 ? 0 : gain >= 1.0 ? 100 : Melder_iround (gain * 100) );
245 balance = ( balance <= 0.0 ? 0 : balance >= 1 ? 1 : balance );
246 if (balance >= 0.5) {
247 val = (int)(((int)(val*balance/(1-balance)) << 8) | val);
248 } else {
249 val = (int)(val | ((int)(val*(1-balance)/balance) << 8));
250 }
251 val = (int)((std::min(2.0-2.0*balance,1.0))*val) | ((int)((std::min(2.0*balance,1.0))*val) << 8);
252 if (inputSource == 1) {
253 /* MIC */
254 if (ioctl (fd_mixer, MIXER_WRITE (SOUND_MIXER_MIC), & val) == -1)
255 Melder_throw (U"Cannot set gain and balance.");
256 } else {
257 /* LINE */
258 if (ioctl (fd_mixer, MIXER_WRITE (SOUND_MIXER_LINE), & val) == -1)
259 Melder_throw (U"Cannot set gain and balance.");
260 }
261 close (fd_mixer);
262 fd_mixer = -1;
263 #endif
264 }
265
266 /*
267 Set the sampling frequency.
268 */
269 if (inputUsesPortAudio) {
270 // Set while opening.
271 } else {
272 #if defined (macintosh)
273 #elif defined (linux) && ! defined (NO_AUDIO)
274 int sampleRate_int = (int) sampleRate;
275 if (ioctl (fd, SNDCTL_DSP_SPEED, & sampleRate_int) == -1)
276 Melder_throw (U"Cannot set sampling frequency to ", sampleRate, U" Hz.");
277 #elif defined (_WIN32)
278 waveFormat. nSamplesPerSec = sampleRate;
279 #endif
280 }
281
282 /*
283 Set the number of channels to 1 (mono), if possible.
284 */
285 if (inputUsesPortAudio) {
286 streamParameters. channelCount = 1;
287 } else {
288 #if defined (macintosh)
289 #elif defined (linux) && ! defined (NO_AUDIO)
290 val = 1;
291 if (ioctl (fd, SNDCTL_DSP_CHANNELS, & val) == -1)
292 Melder_throw (U"Cannot set to mono.");
293 #elif defined (_WIN32)
294 waveFormat. nChannels = 1;
295 #endif
296 }
297
298 /*
299 Set the encoding to 16-bit linear (or to 8-bit linear, if 16-bit is not available).
300 */
301 if (inputUsesPortAudio) {
302 streamParameters. sampleFormat = paInt16;
303 } else {
304 #if defined (macintosh)
305 #elif defined (linux) && ! defined (NO_AUDIO)
306 #if __BYTE_ORDER == __BIG_ENDIAN
307 val = AFMT_S16_BE;
308 #else
309 val = AFMT_S16_LE;
310 #endif
311 if (ioctl (fd, SNDCTL_DSP_SETFMT, & val) == -1)
312 Melder_throw (U"Cannot set 16-bit linear.");
313 #elif defined (_WIN32)
314 waveFormat. wFormatTag = WAVE_FORMAT_PCM;
315 waveFormat. wBitsPerSample = 16;
316 waveFormat. nBlockAlign = waveFormat. nChannels * waveFormat. wBitsPerSample / 8;
317 waveFormat. nAvgBytesPerSec = waveFormat. nBlockAlign * waveFormat. nSamplesPerSec;
318 #endif
319 }
320
321 /*
322 Create a buffer for recording, and the resulting sound.
323 */
324 numberOfSamples = Melder_iround (sampleRate * duration);
325 if (numberOfSamples < 1)
326 Melder_throw (U"Duration too short.");
327 autovector<short> buffer = newvectorzero <short> (numberOfSamples);
328 autoSound me = Sound_createSimple (1, numberOfSamples / sampleRate, sampleRate);
329 Melder_assert (my nx == numberOfSamples);
330
331 /*
332 Open phase 2.
333 This starts recording now.
334 */
335 if (inputUsesPortAudio) {
336 streamParameters. suggestedLatency = Pa_GetDeviceInfo (streamParameters. device) -> defaultLowInputLatency;
337 #if defined (macintosh)
338 PaMacCoreStreamInfo macCoreStreamInfo = { 0 };
339 macCoreStreamInfo. size = sizeof (PaMacCoreStreamInfo);
340 macCoreStreamInfo. hostApiType = paCoreAudio;
341 macCoreStreamInfo. version = 0x01;
342 macCoreStreamInfo. flags = paMacCoreChangeDeviceParameters | paMacCoreFailIfConversionRequired;
343 macCoreStreamInfo. channelMap = nullptr;
344 macCoreStreamInfo. channelMapSize = 0;
345 streamParameters. hostApiSpecificStreamInfo = & macCoreStreamInfo;
346 #endif
347 info. numberOfSamples = numberOfSamples;
348 info. numberOfSamplesRead = 0;
349 info. buffer = buffer.asArgumentToFunctionThatExpectsZeroBasedArray();
350 PaError err = Pa_OpenStream (& portaudioStream, & streamParameters, nullptr,
351 sampleRate,
352 0, // this gives the default of 64 samples per buffer on Paul's 2018 MacBook Pro (checked 20200813)
353 paNoFlag, portaudioStreamCallback, (void *) & info);
354 if (err)
355 Melder_throw (U"open ", Melder_peek8to32 (Pa_GetErrorText (err)));
356 Pa_StartStream (portaudioStream);
357 if (err)
358 Melder_throw (U"start ", Melder_peek8to32 (Pa_GetErrorText (err)));
359 } else {
360 #if defined (macintosh)
361 #elif defined (_WIN32)
362 waveFormat. cbSize = 0;
363 err = waveInOpen (& hWaveIn, WAVE_MAPPER, & waveFormat, 0, 0, CALLBACK_NULL);
364 if (err != MMSYSERR_NOERROR)
365 Melder_throw (U"Error ", err, U" while opening.");
366 #endif
367 }
368 for (i = 1; i <= numberOfSamples; i ++) trace (U"Started ", buffer [i]);
369
370 /*
371 Read the sound into the buffer.
372 */
373 if (inputUsesPortAudio) {
374 // The callback will do this. Just wait.
375 while (/*getNumberOfSamplesRead (& info)*/ info. numberOfSamplesRead < numberOfSamples) {
376 //Pa_Sleep (1);
377 trace (U"filled ", getNumberOfSamplesRead (& info), U"/", numberOfSamples);
378 }
379 for (i = 1; i <= numberOfSamples; i ++) trace (U"Recorded ", buffer [i]);
380 } else {
381 #if defined (macintosh)
382 #elif defined (_WIN32)
383 waveHeader. dwFlags = 0;
384 waveHeader. lpData = (char *) buffer.asArgumentToFunctionThatExpectsZeroBasedArray();
385 waveHeader. dwBufferLength = numberOfSamples * 2;
386 waveHeader. dwLoops = 0;
387 waveHeader. lpNext = nullptr;
388 waveHeader. reserved = 0;
389 err = waveInPrepareHeader (hWaveIn, & waveHeader, sizeof (WAVEHDR));
390 if (err != MMSYSERR_NOERROR)
391 Melder_throw (U"Error ", err, U" while preparing header.");
392 err = waveInAddBuffer (hWaveIn, & waveHeader, sizeof (WAVEHDR));
393 if (err != MMSYSERR_NOERROR)
394 Melder_throw (U"Error ", err, U" while listening.");
395 err = waveInStart (hWaveIn);
396 if (err != MMSYSERR_NOERROR)
397 Melder_throw (U"Error ", err, U" while starting.");
398 while (! (waveHeader. dwFlags & WHDR_DONE)) { Pa_Sleep (1); }
399 err = waveInUnprepareHeader (hWaveIn, & waveHeader, sizeof (WAVEHDR));
400 if (err != MMSYSERR_NOERROR)
401 Melder_throw (U"Error ", err, U" while unpreparing header.");
402 #else
403 integer bytesLeft = 2 * numberOfSamples, dbytes, bytesRead = 0;
404 while (bytesLeft) {
405 dbytes = read (fd, (char *) buffer.asArgumentToFunctionThatExpectsZeroBasedArray() + bytesRead, std::min (bytesLeft, 4000_integer));
406 if (dbytes <= 0)
407 break;
408 bytesLeft -= dbytes;
409 bytesRead += dbytes;
410 };
411 #endif
412 }
413
414 /*
415 Copy the buffered data to the sound object, and discard the buffer.
416 */
417 for (i = 1; i <= numberOfSamples; i ++)
418 my z [1] [i] = buffer [i] * (1.0 / 32768);
419
420 /*
421 Close the audio device.
422 */
423 if (inputUsesPortAudio) {
424 Pa_StopStream (portaudioStream);
425 Pa_CloseStream (portaudioStream);
426 } else {
427 #if defined (macintosh)
428 #elif defined (_WIN32)
429 err = waveInClose (hWaveIn);
430 if (err != MMSYSERR_NOERROR)
431 Melder_throw (U"Error ", err, U" while closing.");
432 #else
433 close (fd);
434 #endif
435 }
436
437 /*
438 Hand the resulting sound to the caller.
439 */
440 return me;
441 } catch (MelderError) {
442 if (inputUsesPortAudio) {
443 if (portaudioStream)
444 Pa_StopStream (portaudioStream);
445 if (portaudioStream)
446 Pa_CloseStream (portaudioStream);
447 } else {
448 #if defined (macintosh)
449 #elif defined (_WIN32)
450 if (hWaveIn != 0)
451 waveInClose (hWaveIn);
452 #else
453 if (fd_mixer != -1)
454 close (fd_mixer);
455 if (fd != -1)
456 close (fd);
457 #endif
458 }
459 Melder_throw (U"Sound not recorded.");
460 }
461 }
462
463 /********** PLAYING A SOUND **********/
464
465 static struct SoundPlay {
466 integer numberOfSamples, i1, i2, silenceBefore, silenceAfter;
467 double tmin, tmax, dt, t1;
468 Sound_PlayCallback callback;
469 Thing boss;
470 autovector <int16> outputBuffer;
471 } thePlayingSound;
472
melderPlayCallback(void * closure,integer samplesPlayed)473 static bool melderPlayCallback (void *closure, integer samplesPlayed) {
474 struct SoundPlay *me = (struct SoundPlay *) closure;
475 int phase = 2;
476 double t = ( samplesPlayed <= my silenceBefore ? my tmin :
477 samplesPlayed >= my silenceBefore + my numberOfSamples ? my tmax :
478 my t1 + (my i1 - 1.5 + samplesPlayed - my silenceBefore) * my dt );
479 if (! MelderAudio_isPlaying) {
480 my outputBuffer.reset(); // get a bit of privacy
481 phase = 3;
482 }
483 if (my callback)
484 return my callback (my boss, phase, my tmin, my tmax, t);
485 return true;
486 }
487
Sound_playPart(Sound me,double tmin,double tmax,Sound_PlayCallback callback,Thing boss)488 void Sound_playPart (Sound me, double tmin, double tmax, Sound_PlayCallback callback, Thing boss)
489 {
490 try {
491 integer ifsamp = Melder_iround (1.0 / my dx), bestSampleRate = MelderAudio_getOutputBestSampleRate (ifsamp);
492 if (ifsamp == bestSampleRate) {
493 struct SoundPlay *thee = (struct SoundPlay *) & thePlayingSound;
494 double *fromLeft = & my z [1] [0], *fromRight = ( my ny > 1 ? & my z [2] [0] : nullptr );
495 MelderAudio_stopPlaying (MelderAudio_IMPLICIT);
496 integer i1, i2;
497 if ((thy numberOfSamples = Matrix_getWindowSamplesX (me, tmin, tmax, & i1, & i2)) < 1)
498 return;
499 thy tmin = tmin;
500 thy tmax = tmax;
501 thy dt = my dx;
502 thy t1 = my x1;
503 thy callback = callback;
504 thy boss = boss;
505 thy silenceBefore = Melder_iroundTowardsZero (ifsamp * MelderAudio_getOutputSilenceBefore ());
506 thy silenceAfter = Melder_iroundTowardsZero (ifsamp * MelderAudio_getOutputSilenceAfter ());
507 integer numberOfChannels = my ny;
508 thy outputBuffer = newvectorzero <int16> ((i2 - i1 + 1 + thy silenceBefore + thy silenceAfter) * numberOfChannels);
509 thy i1 = i1;
510 thy i2 = i2;
511 int16 *to = & thy outputBuffer [0] + thy silenceBefore * numberOfChannels;
512 if (numberOfChannels > 2) {
513 for (integer i = i1; i <= i2; i ++) {
514 for (integer chan = 1; chan <= my ny; chan ++) {
515 integer value = Melder_iround_tieDown (my z [chan] [i] * 32768.0);
516 * ++ to = (int16) Melder_clipped (-32768_integer, value, +32767_integer);
517 }
518 }
519 } else if (numberOfChannels == 2) {
520 for (integer i = i1; i <= i2; i ++) {
521 integer valueLeft = Melder_iround_tieDown (fromLeft [i] * 32768.0);
522 * ++ to = (int16) Melder_clipped (-32768_integer, valueLeft, +32767_integer);
523 integer valueRight = Melder_iround_tieDown (fromRight [i] * 32768.0);
524 * ++ to = (int16) Melder_clipped (-32768_integer, valueRight, +32767_integer);
525 }
526 } else {
527 for (integer i = i1; i <= i2; i ++) {
528 integer value = Melder_iround_tieDown (fromLeft [i] * 32768.0);
529 * ++ to = (int16) Melder_clipped (-32768_integer, value, +32767_integer);
530 }
531 }
532 if (thy callback)
533 thy callback (thy boss, 1, tmin, tmax, tmin);
534 MelderAudio_play16 (thy outputBuffer.asArgumentToFunctionThatExpectsZeroBasedArray(), ifsamp,
535 thy silenceBefore + thy numberOfSamples + thy silenceAfter, numberOfChannels, melderPlayCallback, thee);
536 } else {
537 autoSound part = Sound_extractPart (me, tmin, tmax, kSound_windowShape::RECTANGULAR, 1.0, true);
538 autoSound resampled = Sound_resample (part.get(), bestSampleRate, 1);
539 Sound_playPart (resampled.get(), tmin, tmax, callback, boss); // recursively
540 }
541 } catch (MelderError) {
542 Melder_throw (me, U": not played.");
543 }
544 }
545
Sound_play(Sound me,Sound_PlayCallback playCallback,Thing playClosure)546 void Sound_play (Sound me, Sound_PlayCallback playCallback, Thing playClosure)
547 {
548 Sound_playPart (me, my xmin, my xmax, playCallback, playClosure);
549 }
550
551 /* End of file Sound_audio.cpp */
552