GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
audiodevinfo.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2013-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cstdint>
31 
32 #include <algorithm>
33 #include <ostream>
34 #include <string>
35 #include <vector>
36 
37 #include "Matrix.h"
38 #include "mach-info.h"
39 #include "oct-locbuf.h"
40 #include "quit.h"
41 #include "unwind-prot.h"
42 
43 #include "Cell.h"
44 #include "defun-dld.h"
45 #include "error.h"
46 #include "errwarn.h"
47 #include "interpreter.h"
48 #include "oct-map.h"
49 #include "ov-int32.h"
50 #include "ov.h"
51 #include "ovl.h"
52 #include "parse.h"
53 
54 #if defined (HAVE_PORTAUDIO)
55 
56 #include <portaudio.h>
57 
58 PaSampleFormat
59 bits_to_format (int bits)
60 {
61  if (bits == 8)
62  return paInt8;
63  else if (bits == 16)
64  return paInt16;
65  else if (bits == 24)
66  return paInt24;
67  else if (bits == 32)
68  return paInt32;
69  else if (bits == -1)
70  return paFloat32;
71  else
72  return 0;
73 }
74 
75 #endif
76 
77 DEFUN_DLD (audiodevinfo, args, ,
78  doc: /* -*- texinfo -*-
79 @deftypefn {} {@var{devinfo} =} audiodevinfo ()
80 
81 @deftypefnx {} {@var{devs} =} audiodevinfo (@var{io})
82 @deftypefnx {} {@var{name} =} audiodevinfo (@var{io}, @var{id})
83 @deftypefnx {} {@var{id} =} audiodevinfo (@var{io}, @var{name})
84 @deftypefnx {} {@var{driverversion} =} audiodevinfo (@var{io}, @var{id}, "DriverVersion")
85 @deftypefnx {} {@var{id} =} audiodevinfo (@var{io}, @var{rate}, @var{bits}, @var{chans})
86 @deftypefnx {} {@var{supports} =} audiodevinfo (@var{io}, @var{id}, @var{rate}, @var{bits}, @var{chans})
87 
88 Return a structure describing the available audio input and output devices.
89 
90 The @var{devinfo} structure has two fields @qcode{"input"} and
91 @qcode{"output"}. The value of each field is a structure array with fields
92 @qcode{"Name"}, @nospell{@qcode{"DriverVersion"}} and @qcode{"ID"} describing
93 an audio device.
94 
95 If the optional argument @var{io} is 1, return information about input devices
96 only. If it is 0, return information about output devices only. If @var{io}
97 is the only argument supplied, return the number of input or output devices
98 available.
99 
100 If the optional argument @var{id} is provided, return information about the
101 corresponding device.
102 
103 If the optional argument @var{name} is provided, return the ID of the named
104 device.
105 
106 If the optional argument @nospell{@qcode{"DriverVersion"}} is given, return the
107 name of the driver for the specified device.
108 
109 Given a sampling rate, bits per sample, and number of channels for an input or
110 output device, return the ID of the first device that supports playback or
111 recording using the specified parameters.
112 
113 If also given a device ID, return true if the device supports playback or
114 recording using those parameters.
115 @end deftypefn */)
116 {
117 #if defined (HAVE_PORTAUDIO)
118 
119  int nargin = args.length ();
120 
121  if (nargin > 5)
122  print_usage ();
123 
124  octave_scalar_map devinfo;
126  octave_value_list output;
127 
128  PaError err = Pa_Initialize ();
129 
130  if (err != paNoError)
131  error ("audiodevinfo: PortAudio initialization failed");
132 
133  int num_devices = Pa_GetDeviceCount ();
134 
135  if (num_devices < 0)
136  num_devices = 0;
137 
138  octave_idx_type numinput, numoutput;
139  numinput = numoutput = 0;
140  for (int i = 0; i < num_devices; i++)
141  {
142  const PaDeviceInfo *device_info = Pa_GetDeviceInfo (i);
143 
144  if (! device_info)
145  {
146  warning_with_id ("Octave:invalid-audio-device",
147  "invalid audio device ID = %d", i);
148  continue;
149  }
150 
151  if (device_info->maxInputChannels != 0)
152  numinput++;
153 
154  if (device_info->maxOutputChannels != 0)
155  numoutput++;
156  }
157 
158  Cell input_name (dim_vector (1, numinput));
159  Cell input_driver_version (dim_vector (1, numinput));
160  Cell input_id (dim_vector (1, numinput));
161  Cell output_name (dim_vector (1, numoutput));
162  Cell output_driver_version (dim_vector (1, numoutput));
163  Cell output_id (dim_vector (1, numoutput));
164 
165  octave_idx_type idx_i, idx_o;
166  idx_i = idx_o = 0;
167  for (int i = 0; i < num_devices; i++)
168  {
169  const PaDeviceInfo *device_info = Pa_GetDeviceInfo (i);
170 
171  if (! device_info)
172  {
173  warning_with_id ("Octave:invalid-audio-device",
174  "invalid audio device ID = %d", i);
175  continue;
176  }
177 
178  const PaHostApiInfo *api_info = Pa_GetHostApiInfo (device_info->hostApi);
179 
180  const char *driver = (api_info ? api_info->name : "");
181 
182  char name[128];
183  sprintf (name, "%s (%s)", device_info->name, driver);
184 
185  if (device_info->maxInputChannels != 0)
186  {
187  input_name(idx_i) = name;
188  input_driver_version(idx_i) = driver;
189  input_id(idx_i) = i;
190  idx_i++;
191  }
192 
193  if (device_info->maxOutputChannels != 0)
194  {
195  output_name(idx_o) = name;
196  output_driver_version(idx_o) = driver;
197  output_id(idx_o) = i;
198  idx_o++;
199  }
200  }
201 
202  octave_map inputdev, outputdev;
203  inputdev.setfield ("Name", input_name);
204  inputdev.setfield ("DriverVersion", input_driver_version);
205  inputdev.setfield ("ID", input_id);
206  outputdev.setfield ("Name", output_name);
207  outputdev.setfield ("DriverVersion", output_driver_version);
208  outputdev.setfield ("ID", output_id);
209  devinfo.setfield ("input", inputdev);
210  devinfo.setfield ("output", outputdev);
211 
213 
214  // Return information about input & output audio devices and their properties.
215  if (nargin == 0)
216  {
217  retval = devinfo;
218  }
219  // Return the number of input or output devices
220  else if (nargin == 1)
221  {
222  if (args(0).int_value () == 0)
223  retval = numoutput;
224  else if (args(0).int_value () == 1)
225  retval = numinput;
226  else
227  error ("audiodevinfo: specify 0 for output and 1 for input devices");
228  }
229  // Return device name when given id or id when given device name.
230  else if (nargin == 2)
231  {
232  bool found = false;
233  int outin = args(0).int_value ();
234  if (args(1).is_string ())
235  {
236  std::string name = args(1).string_value ();
237  if (outin == 0)
238  {
239  for (int i = 0; i < numoutput; i++)
240  {
241  if (output_name(i).string_value () == name)
242  {
243  retval = output_id(i);
244  found = true;
245  break;
246  }
247  }
248  }
249  else if (outin == 1)
250  {
251  for (int i = 0; i < numinput; i++)
252  {
253  if (input_name(i).string_value () == name)
254  {
255  retval = input_id(i);
256  found = true;
257  break;
258  }
259  }
260  }
261  else
262  error ("audiodevinfo: specify 0 for output and 1 for input devices");
263  }
264  else
265  {
266  if (outin == 0)
267  {
268  for (int i = 0; i < numoutput; i++)
269  {
270  if (output_id(i).int_value () == args(1).int_value ())
271  {
272  retval = output_name(i);
273  found = true;
274  break;
275  }
276  }
277  }
278  else if (outin == 1)
279  {
280  for (int i = 0; i < numinput; i++)
281  {
282  if (input_id(i).int_value () == args(1).int_value ())
283  {
284  retval = input_name(i);
285  found = true;
286  break;
287  }
288  }
289  }
290  else
291  error ("audiodevinfo: specify 0 for output and 1 for input devices");
292  }
293 
294  if (! found)
295  error ("audiodevinfo: no device found for the specified criteria");
296  }
297  // Return the DriverVersion (really, name of driver) of the specified device
298  else if (nargin == 3)
299  {
300  bool found = false;
301  int outin = args(0).int_value ();
302  int id = args(1).int_value ();
303 
304  std::string arg3 = args(2).string_value ();
305  std::transform (arg3.begin (), arg3.end (), arg3.begin (), tolower);
306  if (arg3 != "driverversion")
307  error ("audiodevinfo: third argument must be \"DriverVersion\"");
308 
309  if (outin == 0)
310  {
311  for (int i = 0; i < numoutput; i++)
312  {
313  if (output_id(i).int_value () == id)
314  {
315  found = true;
316  retval = output_driver_version(i);
317  break;
318  }
319  }
320  }
321  else if (outin == 1)
322  {
323  for (int i = 0; i < numinput; i++)
324  {
325  if (input_id(i).int_value () == id)
326  {
327  found = true;
328  retval = input_driver_version(i);
329  break;
330  }
331  }
332  }
333  else
334  error ("audiodevinfo: specify 0 for output and 1 for input devices");
335 
336  if (! found)
337  error ("audiodevinfo: no device found for the specified criteria");
338  }
339  // Return the ID of the first device meeting specified criteria.
340  else if (nargin == 4)
341  {
342  int io = args(0).int_value ();
343  int rate = args(1).int_value ();
344  int bits = args(2).int_value ();
345  int chans = args(3).int_value ();
346 
347  for (int i = 0; i < num_devices; i++)
348  {
349  PaStreamParameters stream_parameters;
350  stream_parameters.device = i;
351  stream_parameters.channelCount = chans;
352  PaSampleFormat format = bits_to_format (bits);
353 
354  if (format != 0)
355  stream_parameters.sampleFormat = format;
356  else
357  error ("audiodevinfo: invalid bits per sample format");
358 
359  const PaDeviceInfo *device_info = Pa_GetDeviceInfo (i);
360 
361  if (! device_info)
362  {
363  warning_with_id ("Octave:invalid-audio-device",
364  "invalid audio device ID = %d", i);
365  continue;
366  }
367 
368  stream_parameters.suggestedLatency
369  = device_info->defaultLowInputLatency;
370 
371  stream_parameters.hostApiSpecificStreamInfo = nullptr;
372 
373  if (io == 0)
374  {
375  if (device_info->maxOutputChannels < chans)
376  continue;
377 
378  err = Pa_IsFormatSupported (nullptr, &stream_parameters, rate);
379 
380  if (err == paFormatIsSupported)
381  {
382  retval = i;
383  return retval;
384  }
385  }
386  else if (io == 1)
387  {
388  if (device_info->maxInputChannels < chans)
389  continue;
390 
391  err = Pa_IsFormatSupported (&stream_parameters, nullptr, rate);
392  if (err == paFormatIsSupported)
393  {
394  retval = i;
395  return retval;
396  }
397  }
398  }
399  retval = -1;
400  }
401  // Check if given device supports specified playback or recording modes.
402  else if (nargin == 5)
403  {
404  int io = args(0).int_value ();
405  int id = args(1).int_value ();
406  int rate = args(2).int_value ();
407  int bits = args(3).int_value ();
408  int chans = args(4).int_value ();
409  PaStreamParameters stream_parameters;
410  stream_parameters.device = id;
411  stream_parameters.channelCount = chans;
412  PaSampleFormat format = bits_to_format (bits);
413  if (format != 0)
414  stream_parameters.sampleFormat = format;
415  else
416  error ("audiodevinfo: invalid bits per sample format");
417 
418  const PaDeviceInfo *device_info = Pa_GetDeviceInfo (id);
419 
420  if (! device_info)
421  error ("audiodevinfo: invalid audio device ID = %d", id);
422 
423  stream_parameters.suggestedLatency
424  = device_info->defaultLowInputLatency;
425 
426  stream_parameters.hostApiSpecificStreamInfo = nullptr;
427  if (io == 0)
428  {
429  if (device_info->maxOutputChannels < chans)
430  {
431  retval = false;
432  return retval;
433  }
434  err = Pa_IsFormatSupported (nullptr, &stream_parameters, rate);
435  if (err == paFormatIsSupported)
436  {
437  retval = true;
438  return retval;
439  }
440  }
441  else if (io == 1)
442  {
443  if (device_info->maxInputChannels < chans)
444  {
445  retval = false;
446  return retval;
447  }
448  err = Pa_IsFormatSupported (&stream_parameters, nullptr, rate);
449  if (err == paFormatIsSupported)
450  {
451  retval = true;
452  return retval;
453  }
454  }
455  else
456  error ("audiodevinfo: specify 0 for output and 1 for input devices");
457 
458  retval = false;
459  }
460 
461  return retval;
462 
463 #else
464  octave_unused_parameter (args);
465 
466  err_disabled_feature ("audiodevinfo",
467  "audio playback and recording through PortAudio");
468 #endif
469 }
470 
471 /*
472 %!testif HAVE_PORTAUDIO
473 %! devinfo = audiodevinfo;
474 %! assert (rows (devinfo.input), 1);
475 %! assert (rows (devinfo.output), 1);
476 
477 %!testif HAVE_PORTAUDIO
478 %! devinfo = audiodevinfo;
479 %! nout = audiodevinfo (0);
480 %! nin = audiodevinfo (1);
481 %! assert (columns (devinfo.output), nout);
482 %! assert (columns (devinfo.input), nin);
483 
484 %!testif HAVE_PORTAUDIO
485 %! devinfo = audiodevinfo;
486 %! nout = audiodevinfo (0);
487 %! nin = audiodevinfo (1);
488 %! for i = 1:nout
489 %! assert (devinfo.output(i).Name, audiodevinfo (0, devinfo.output(i).ID));
490 %! endfor
491 %! for i=1:nin
492 %! assert (devinfo.input(i).Name, audiodevinfo (1, devinfo.input(i).ID));
493 %! endfor
494 
495 %!testif HAVE_PORTAUDIO
496 %! devinfo = audiodevinfo;
497 %! nout = audiodevinfo (0);
498 %! nin = audiodevinfo (1);
499 %! for i = 1:nout
500 %! assert (devinfo.output(i).ID, audiodevinfo (0, devinfo.output(i).Name));
501 %! endfor
502 %! for i = 1:nin
503 %! assert (devinfo.input(i).ID, audiodevinfo (1, devinfo.input(i).Name));
504 %! endfor
505 */
506 
507 #if defined (HAVE_PORTAUDIO)
508 
509 enum audio_type { TYPE_INT8, TYPE_UINT8, TYPE_UINT16, TYPE_DOUBLE };
510 
511 class audioplayer : public octave_base_value
512 {
513 public:
514  audioplayer (void);
515  ~audioplayer (void);
516 
517  // Overloaded base functions
518  double player_value (void) const { return 0; }
519  virtual double scalar_value (bool = false) const { return 0; }
520  void print (std::ostream& os, bool pr_as_read_syntax = false);
521  void print_raw (std::ostream& os, bool pr_as_read_syntax) const;
522 
523  // Properties
524  bool is_constant (void) const { return true; }
525  bool is_defined (void) const { return true; }
526  bool print_as_scalar (void) const { return true; }
527 
528  void init (void);
529  void init_fn (void);
530  void set_y (const octave_value& y);
531  void set_y (octave_function *fn);
532  void set_y (std::string fn);
533  Matrix& get_y (void);
534  RowVector get_left (void) const;
535  RowVector get_right (void) const;
536  void set_fs (int fs);
537  int get_fs (void);
538  void set_nbits (int nbits);
539  int get_nbits (void);
540  void set_id (int id);
541  int get_id (void);
542  int get_channels (void);
543  audio_type get_type (void);
544 
545  void set_sample_number (unsigned int sample);
546  unsigned int get_sample_number (void);
547  unsigned int get_total_samples (void);
548  void set_end_sample (unsigned int sample);
549  unsigned int get_end_sample (void);
550  void reset_end_sample (void);
551  void set_tag (const charMatrix& tag);
552  charMatrix get_tag (void);
553  void set_userdata (const octave_value& userdata);
554  octave_value get_userdata (void);
555  PaStream * get_stream (void);
556 
557  void playblocking (void);
558  void play (void);
559  void pause (void);
560  void resume (void);
561  void stop (void);
562  bool isplaying (void);
563 
564  octave_function *octave_callback_function;
565 
566 private:
567  int id;
568  int fs;
569  int nbits;
570  int channels;
571  unsigned int sample_number;
572  unsigned int end_sample;
573  charMatrix tag;
574  Matrix y;
575  octave_value userdata;
576  RowVector left;
577  RowVector right;
578  PaStream *stream;
579  PaStreamParameters output_parameters;
580  audio_type type;
581 
583 };
584 
585 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (audioplayer, "audioplayer", "audioplayer");
586 
587 static int
588 octave_play_callback (const void *, void *output, unsigned long frames,
589  const PaStreamCallbackTimeInfo *,
590  PaStreamCallbackFlags, void *data)
591 {
592  audioplayer *player = static_cast<audioplayer *> (data);
593 
594  if (! player)
595  error ("audio player callback function called without player");
596 
598  = octave::feval (player->octave_callback_function,
599  ovl (static_cast<double> (frames)), 1);
600 
601  if (retval.length () < 2)
602  error ("audio player callback function failed");
603 
604  const Matrix sound = retval(0).matrix_value ();
605  int return_status = retval(1).int_value ();
606 
607  if (frames - sound.rows () != 0 || sound.columns () < 1
608  || sound.columns () > 2)
609  error ("audio player callback function failed");
610 
611  // Don't multiply the audio data by scale_factor here. Although it
612  // does move the operation outside of the loops below, it also causes
613  // a second copy of the data array to be made.
614 
615  const ColumnVector sound_l = sound.column (0);
616  const ColumnVector sound_r = (sound.columns () == 1
617  ? sound_l : sound.column (1));
618 
619  const double *p_l = sound_l.data ();
620  const double *p_r = sound_r.data ();
621 
622  switch (player->get_nbits ())
623  {
624  case 8:
625  {
626  static double scale_factor = std::pow (2.0, 7) - 1.0;
627 
628  int8_t *buffer = static_cast<int8_t *> (output);
629 
630  for (unsigned long i = 0; i < frames; i++)
631  {
632  buffer[2*i] = p_l[i] * scale_factor;
633  buffer[2*i+1] = p_r[i] * scale_factor;
634  }
635  }
636  break;
637 
638  case 16:
639  {
640  static double scale_factor = std::pow (2.0, 15) - 1.0;
641 
642  int16_t *buffer = static_cast<int16_t *> (output);
643 
644  for (unsigned long i = 0; i < frames; i++)
645  {
646  buffer[2*i] = p_l[i] * scale_factor;
647  buffer[2*i+1] = p_r[i] * scale_factor;
648  }
649  }
650  break;
651 
652  case 24:
653  {
654  static double scale_factor = std::pow (2.0, 23) - 1.0;
655 
656  static int big_endian = octave::mach_info::words_big_endian ();
657 
658  uint8_t *buffer = static_cast<uint8_t *> (output);
659 
660  for (unsigned long i = 0; i < frames; i++)
661  {
662  int32_t sample_l = p_l[i];
663  int32_t sample_r = p_r[i];
664 
665  sample_l &= 0x00ffffff;
666  sample_r &= 0x00ffffff;
667 
668  uint8_t *_sample_l = reinterpret_cast<uint8_t *> (&sample_l);
669  uint8_t *_sample_r = reinterpret_cast<uint8_t *> (&sample_r);
670 
671  unsigned long offset = i * 6;
672 
673  buffer[offset+0] = _sample_l[0+big_endian] * scale_factor;
674  buffer[offset+1] = _sample_l[1+big_endian] * scale_factor;
675  buffer[offset+2] = _sample_l[2+big_endian] * scale_factor;
676 
677  buffer[offset+3] = _sample_r[0+big_endian] * scale_factor;
678  buffer[offset+4] = _sample_r[1+big_endian] * scale_factor;
679  buffer[offset+5] = _sample_r[2+big_endian] * scale_factor;
680  }
681  }
682  break;
683 
684  default:
685  error ("invalid player bit depth in callback function");
686  }
687 
688  return return_status;
689 }
690 
691 static int
692 portaudio_play_callback (const void *, void *output, unsigned long frames,
693  const PaStreamCallbackTimeInfo*,
694  PaStreamCallbackFlags, void *data)
695 {
696  audioplayer *player = static_cast<audioplayer *> (data);
697 
698  if (! player)
699  error ("audio player callback function called without player");
700 
701  // Don't multiply the audio data by scale_factor here. Although it would
702  // move the operation outside of the loops below, it also causes a second
703  // copy of the *entire* data array to be made when only a small portion
704  // (buffer_size elements) is usually needed for this callback.
705 
706  const RowVector sound_l = player->get_left ();
707  const RowVector sound_r = player->get_right ();
708 
709  const double *pl = sound_l.data ();
710  const double *pr = sound_r.data ();
711 
712  if (player->get_type () == TYPE_DOUBLE)
713  {
714  switch (player->get_nbits ())
715  {
716  case 8:
717  {
718  static double scale_factor = std::pow (2.0, 7) - 1.0;
719 
720  int8_t *buffer = static_cast<int8_t *> (output);
721 
722  for (unsigned long j = 0; j < frames; j++)
723  {
724  unsigned int sample_number = player->get_sample_number ();
725 
726  if (sample_number >= player->get_end_sample ())
727  return paComplete;
728 
729  unsigned long offset = j * 2;
730 
731  buffer[offset+0] = pl[sample_number] * scale_factor;
732  buffer[offset+1] = pr[sample_number] * scale_factor;
733 
734  player->set_sample_number (sample_number + 1);
735  }
736  }
737  break;
738 
739  case 16:
740  {
741  static double scale_factor = std::pow (2.0, 15) - 1.0;
742 
743  int16_t *buffer = static_cast<int16_t *> (output);
744 
745  for (unsigned long j = 0; j < frames; j++)
746  {
747  unsigned int sample_number = player->get_sample_number ();
748 
749  if (sample_number >= player->get_end_sample ())
750  return paComplete;
751 
752  unsigned long offset = j * 2;
753 
754  buffer[offset+0] = pl[sample_number] * scale_factor;
755  buffer[offset+1] = pr[sample_number] * scale_factor;
756 
757  player->set_sample_number (sample_number + 1);
758  }
759  }
760  break;
761 
762  case 24:
763  {
764  static double scale_factor = std::pow (2.0, 23) - 1.0;
765 
766  static int big_endian = octave::mach_info::words_big_endian ();
767 
768  uint8_t *buffer = static_cast<uint8_t *> (output);
769 
770  for (unsigned long j = 0; j < frames; j++)
771  {
772  unsigned int sample_number = player->get_sample_number ();
773 
774  if (sample_number >= player->get_end_sample ())
775  return paComplete;
776 
777  int32_t sample_l = pl[sample_number] * scale_factor;
778  int32_t sample_r = pr[sample_number] * scale_factor;
779 
780  sample_l &= 0x00ffffff;
781  sample_r &= 0x00ffffff;
782 
783  uint8_t *_sample_l = reinterpret_cast<uint8_t *> (&sample_l);
784  uint8_t *_sample_r = reinterpret_cast<uint8_t *> (&sample_r);
785 
786  unsigned long offset = j * 6;
787 
788  buffer[offset+0] = _sample_l[0+big_endian];
789  buffer[offset+1] = _sample_l[1+big_endian];
790  buffer[offset+2] = _sample_l[2+big_endian];
791 
792  buffer[offset+3] = _sample_r[0+big_endian];
793  buffer[offset+4] = _sample_r[1+big_endian];
794  buffer[offset+5] = _sample_r[2+big_endian];
795 
796  player->set_sample_number (sample_number + 1);
797  }
798  }
799  break;
800 
801  default:
802  error ("invalid player bit depth in callback function");
803  }
804  }
805  else if (player->get_type () == TYPE_INT8)
806  {
807  int8_t *buffer = static_cast<int8_t *> (output);
808 
809  for (unsigned long j = 0; j < frames; j++)
810  {
811  unsigned int sample_number = player->get_sample_number ();
812 
813  if (sample_number >= player->get_end_sample ())
814  return paComplete;
815 
816  unsigned long offset = j * 2;
817 
818  buffer[offset+0] = pl[sample_number];
819  buffer[offset+1] = pr[sample_number];
820 
821  player->set_sample_number (sample_number + 1);
822  }
823  }
824  else if (player->get_type () == TYPE_UINT8)
825  {
826  uint8_t *buffer = static_cast<uint8_t *> (output);
827 
828  for (unsigned long j = 0; j < frames; j++)
829  {
830  unsigned int sample_number = player->get_sample_number ();
831 
832  if (sample_number >= player->get_end_sample ())
833  return paComplete;
834 
835  unsigned long offset = j * 2;
836 
837  buffer[offset+0] = pl[sample_number];
838  buffer[offset+1] = pr[sample_number];
839 
840  player->set_sample_number (sample_number + 1);
841  }
842  }
843  else if (player->get_type () == TYPE_UINT16)
844  {
845  int16_t *buffer = static_cast<int16_t *> (output);
846 
847  for (unsigned long j = 0; j < frames; j++)
848  {
849  unsigned int sample_number = player->get_sample_number ();
850 
851  if (sample_number >= player->get_end_sample ())
852  return paComplete;
853 
854  unsigned long offset = j * 2;
855 
856  buffer[offset+0] = pl[sample_number];
857  buffer[offset+1] = pr[sample_number];
858 
859  player->set_sample_number (sample_number + 1);
860  }
861  }
862 
863  return paContinue;
864 }
865 
866 static void
867 safe_audioplayer_stop (audioplayer *player)
868 {
869  player->stop ();
870 }
871 
872 audioplayer::audioplayer (void)
873  : octave_callback_function (nullptr),
874  id (-1), fs (0), nbits (16), channels (0), sample_number (0),
875  end_sample (-1), tag (""), y (), userdata (Matrix ()),
876  left (), right (), stream (nullptr), output_parameters (), type ()
877 { }
878 
879 audioplayer::~audioplayer (void)
880 {
881  if (isplaying ())
882  {
883  warning_with_id ("Octave:audio-interrupt",
884  "interrupting playing audioplayer");
885  stop ();
886  }
887 }
888 
889 void
890 audioplayer::print (std::ostream& os, bool pr_as_read_syntax)
891 {
892  print_raw (os, pr_as_read_syntax);
893  newline (os);
894 }
895 
896 void
897 audioplayer::print_raw (std::ostream& os, bool) const
898 {
899  os << 0;
900 }
901 
902 void
903 audioplayer::init_fn (void)
904 {
905  if (Pa_Initialize () != paNoError)
906  error ("audioplayer: initialization error");
907 
908  if (Pa_GetDeviceCount () < 1)
909  error ("audioplayer: no audio devices found or available");
910 
911  int device = get_id ();
912 
913  if (device == -1)
914  device = Pa_GetDefaultOutputDevice ();
915 
916  output_parameters.device = device;
917  output_parameters.channelCount = 2;
918  output_parameters.sampleFormat = bits_to_format (get_nbits ());
919 
920  const PaDeviceInfo *device_info = Pa_GetDeviceInfo (device);
921 
922  if (! device_info)
923  warning_with_id ("Octave:invalid-default-audio-device",
924  "invalid default audio device ID = %d", device);
925 
926  output_parameters.suggestedLatency
927  = (device_info ? device_info->defaultHighOutputLatency : -1);
928 
929  output_parameters.hostApiSpecificStreamInfo = nullptr;
930 }
931 
932 void
933 audioplayer::init (void)
934 {
935  // FIXME: Both of these variables are unused.
936  // Should they be eliminated or is something not yet implemented?
937  //
938  // int channels = y.rows ();
939  // RowVector *sound_l = get_left ();
940 
941  if (Pa_Initialize () != paNoError)
942  error ("audioplayer: initialization error");
943 
944  if (Pa_GetDeviceCount () < 1)
945  error ("audioplayer: no audio devices found or available");
946 
947  int device = get_id ();
948 
949  if (device == -1)
950  device = Pa_GetDefaultOutputDevice ();
951 
952  output_parameters.device = device;
953  output_parameters.channelCount = 2;
954 
955  if (type == TYPE_DOUBLE)
956  output_parameters.sampleFormat = bits_to_format (get_nbits ());
957  else if (type == TYPE_INT8)
958  output_parameters.sampleFormat = paInt8;
959  else if (type == TYPE_UINT8)
960  output_parameters.sampleFormat = paUInt8;
961  else if (type == TYPE_UINT16)
962  output_parameters.sampleFormat = paInt16;
963 
964  const PaDeviceInfo *device_info = Pa_GetDeviceInfo (device);
965 
966  if (! device_info)
967  warning_with_id ("Octave:invalid-default-audio-device",
968  "invalid default audio device ID = %d", device);
969 
970  output_parameters.suggestedLatency
971  = (device_info ? device_info->defaultHighOutputLatency : -1);
972 
973  output_parameters.hostApiSpecificStreamInfo = nullptr;
974 }
975 
976 void
977 audioplayer::set_y (const octave_value& y_arg)
978 {
979  if (y_arg.is_int8_type ())
980  type = TYPE_INT8;
981  else if (y_arg.is_uint8_type ())
982  type = TYPE_UINT8;
983  else if (y_arg.is_int16_type ())
984  type = TYPE_UINT16;
985  else
986  type = TYPE_DOUBLE;
987 
988  y = y_arg.matrix_value ();
989 
990  if (y.rows () > 2)
991  y = y.transpose ();
992 
993  channels = y.rows ();
994  left = y.row (0);
995 
996  if (channels == 2)
997  right = y.row (1);
998 
999  reset_end_sample ();
1000 }
1001 
1002 void
1003 audioplayer::set_y (octave_function *fn)
1004 {
1005  octave_callback_function = fn;
1006  channels = 2;
1007  reset_end_sample ();
1008 }
1009 
1010 Matrix&
1011 audioplayer::get_y (void)
1012 {
1013  return y;
1014 }
1015 
1016 RowVector
1017 audioplayer::get_left (void) const
1018 {
1019  return left;
1020 }
1021 
1022 RowVector
1023 audioplayer::get_right (void) const
1024 {
1025  return channels == 1 ? left : right;
1026 }
1027 
1028 void
1029 audioplayer::set_fs (int fs_arg)
1030 {
1031  fs = fs_arg;
1032 }
1033 
1034 int
1035 audioplayer::get_fs (void)
1036 {
1037  return fs;
1038 }
1039 
1040 void
1041 audioplayer::set_nbits (int nbits_arg)
1042 {
1043  nbits = nbits_arg;
1044 }
1045 
1046 int
1047 audioplayer::get_nbits (void)
1048 {
1049  return nbits;
1050 }
1051 
1052 void
1053 audioplayer::set_id (int id_arg)
1054 {
1055  id = id_arg;
1056 }
1057 
1058 int
1059 audioplayer::get_id (void)
1060 {
1061  return id;
1062 }
1063 
1064 int
1065 audioplayer::get_channels (void)
1066 {
1067  return channels;
1068 }
1069 
1070 audio_type
1071 audioplayer::get_type (void)
1072 {
1073  return type;
1074 }
1075 
1076 void
1077 audioplayer::set_sample_number (unsigned int sample_number_arg)
1078 {
1079  sample_number = sample_number_arg;
1080 }
1081 
1082 unsigned int
1083 audioplayer::get_sample_number (void)
1084 {
1085  return sample_number;
1086 }
1087 
1088 unsigned int
1089 audioplayer::get_total_samples (void)
1090 {
1091  return left.numel ();
1092 }
1093 
1094 void
1095 audioplayer::set_end_sample (unsigned int end_sample_arg)
1096 {
1097  end_sample = end_sample_arg;
1098 }
1099 
1100 unsigned int
1101 audioplayer::get_end_sample (void)
1102 {
1103  return end_sample;
1104 }
1105 
1106 void
1107 audioplayer::reset_end_sample (void)
1108 {
1109  set_end_sample (left.numel ());
1110 }
1111 
1112 void
1113 audioplayer::set_tag (const charMatrix& tag_arg)
1114 {
1115  tag = tag_arg;
1116 }
1117 
1118 charMatrix
1119 audioplayer::get_tag (void)
1120 {
1121  return tag;
1122 }
1123 
1124 void
1125 audioplayer::set_userdata (const octave_value& userdata_arg)
1126 {
1127  userdata = userdata_arg;
1128 }
1129 
1131 audioplayer::get_userdata (void)
1132 {
1133  return userdata;
1134 }
1135 
1136 void
1137 audioplayer::playblocking (void)
1138 {
1139  if (get_stream ())
1140  stop ();
1141 
1142  const unsigned int buffer_size = get_fs () / 20;
1143  OCTAVE_LOCAL_BUFFER (uint32_t, buffer, buffer_size * 2);
1144 
1145  PaError err;
1146  err = Pa_OpenStream (&stream, nullptr, &(output_parameters), get_fs (),
1147  buffer_size, paClipOff, nullptr, nullptr);
1148  if (err != paNoError)
1149  error ("audioplayer: unable to open audio playback stream");
1150 
1151  err = Pa_StartStream (stream);
1152  if (err != paNoError)
1153  error ("audioplayer: unable to start audio playback stream");
1154 
1155  unsigned int start, end;
1156  start = get_sample_number ();
1157  end = get_end_sample ();
1158 
1159  octave::unwind_protect frame;
1160 
1161  frame.add_fcn (safe_audioplayer_stop, this);
1162 
1163  for (unsigned int i = start; i < end; i += buffer_size)
1164  {
1165  octave_quit ();
1166 
1167  if (octave_callback_function != nullptr)
1168  octave_play_callback (nullptr, buffer, buffer_size, nullptr, 0, this);
1169  else
1170  portaudio_play_callback (nullptr, buffer, buffer_size, nullptr, 0, this);
1171 
1172  err = Pa_WriteStream (stream, buffer, buffer_size);
1173  }
1174 }
1175 
1176 void
1177 audioplayer::play (void)
1178 {
1179  if (get_stream ())
1180  stop ();
1181 
1182  const unsigned int buffer_size = get_fs () / 20;
1183 
1184  PaError err;
1185  if (octave_callback_function != nullptr)
1186  err = Pa_OpenStream (&stream, nullptr, &(output_parameters),
1187  get_fs (), buffer_size, paClipOff,
1188  octave_play_callback, this);
1189  else
1190  err = Pa_OpenStream (&stream, nullptr, &(output_parameters),
1191  get_fs (), buffer_size, paClipOff,
1192  portaudio_play_callback, this);
1193 
1194  if (err != paNoError)
1195  error ("audioplayer: failed to open audio playback stream");
1196 
1197  err = Pa_StartStream (stream);
1198  if (err != paNoError)
1199  error ("audioplayer: failed to start audio playback stream");
1200 }
1201 
1202 void
1203 audioplayer::pause (void)
1204 {
1205  if (get_stream () == nullptr)
1206  return;
1207 
1208  PaError err;
1209  err = Pa_StopStream (stream);
1210  if (err != paNoError)
1211  error ("audioplayer: failed to stop audio playback stream");
1212 }
1213 
1214 void
1215 audioplayer::resume (void)
1216 {
1217  if (get_stream () == nullptr)
1218  return;
1219 
1220  PaError err;
1221  err = Pa_StartStream (stream);
1222  if (err != paNoError)
1223  error ("audioplayer: failed to start audio playback stream");
1224 }
1225 
1226 PaStream *
1227 audioplayer::get_stream (void)
1228 {
1229  return stream;
1230 }
1231 
1232 void
1233 audioplayer::stop (void)
1234 {
1235  if (get_stream () == nullptr)
1236  return;
1237 
1238  PaError err;
1239  set_sample_number (0);
1240  reset_end_sample ();
1241  if (! Pa_IsStreamStopped (get_stream ()))
1242  {
1243  err = Pa_AbortStream (get_stream ());
1244  if (err != paNoError)
1245  error ("audioplayer: failed to stop audio playback stream");
1246  }
1247 
1248  err = Pa_CloseStream (get_stream ());
1249  if (err != paNoError)
1250  error ("audioplayer: failed to close audio playback stream");
1251 
1252  stream = nullptr;
1253 }
1254 
1255 bool
1256 audioplayer::isplaying (void)
1257 {
1258  if (get_stream () == nullptr)
1259  return false;
1260 
1261  PaError err;
1262  err = Pa_IsStreamActive (stream);
1263  if (err != 0 && err != 1)
1264  error ("audioplayer: checking stream activity status failed");
1265 
1266  return (err == 1);
1267 }
1268 
1269 class audiorecorder : public octave_base_value
1270 {
1271 public:
1272  audiorecorder (void);
1273  ~audiorecorder (void);
1274 
1275  // Overloaded base functions
1276  double player_value (void) const { return 0; }
1277  virtual double scalar_value (bool = false) const { return 0; }
1278  void print (std::ostream& os, bool pr_as_read_syntax = false);
1279  void print_raw (std::ostream& os, bool pr_as_read_syntax) const;
1280 
1281  // Properties
1282  bool is_constant (void) const { return true; }
1283  bool is_defined (void) const { return true; }
1284  bool print_as_scalar (void) const { return true; }
1285 
1286  void init (void);
1287  void set_fs (int fs);
1288  int get_fs (void);
1289  void set_nbits (int nbits);
1290  int get_nbits (void);
1291  PaSampleFormat get_sampleFormat (void);
1292  void set_id (int id);
1293  int get_id (void);
1294  void set_channels (int channels);
1295  int get_channels (void);
1296  audio_type get_type (void);
1297 
1298  void set_sample_number (unsigned int sample);
1299  unsigned int get_sample_number (void);
1300  unsigned int get_total_samples (void);
1301  void set_end_sample (unsigned int sample);
1302  unsigned int get_end_sample (void);
1303  void reset_end_sample (void);
1304  void set_tag (const charMatrix& tag);
1305  charMatrix get_tag (void);
1306  void set_userdata (const octave_value& userdata);
1307  octave_value get_userdata (void);
1308  PaStream * get_stream (void);
1309 
1310  octave_value getaudiodata (void);
1311  audioplayer * getplayer (void);
1312  bool isrecording (void);
1313  audioplayer play (void);
1314  void record (void);
1315  void recordblocking (float seconds);
1316  void pause (void);
1317  void resume (void);
1318  void stop (void);
1319  void append (float sample_l, float sample_r);
1320 
1321  octave_function *octave_callback_function;
1322 
1323 private:
1324  int id;
1325  int fs;
1326  int nbits;
1327  int channels;
1328  unsigned int sample_number;
1329  unsigned int end_sample;
1330  charMatrix tag;
1331  Matrix y;
1332  octave_value userdata;
1333  std::vector<float> left;
1334  std::vector<float> right;
1335  PaStream *stream;
1336  PaStreamParameters input_parameters;
1337  audio_type type;
1338 
1340 };
1341 
1342 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (audiorecorder, "audiorecorder", "audiorecorder");
1343 
1344 static int
1345 octave_record_callback (const void *input, void *, unsigned long frames,
1346  const PaStreamCallbackTimeInfo *,
1347  PaStreamCallbackFlags, void *data)
1348 {
1349  audiorecorder *recorder = static_cast<audiorecorder *> (data);
1350 
1351  if (! recorder)
1352  error ("audio recorder callback function called without recorder");
1353 
1354  int channels = recorder->get_channels ();
1355 
1356  Matrix sound (frames, 2);
1357  sound.resize (frames, 2);
1358 
1359  if (recorder->get_sampleFormat () == bits_to_format (8))
1360  {
1361  static double scale_factor = std::pow (2.0, 7) - 1.0;
1362 
1363  const int8_t *input8 = static_cast<const int8_t *> (input);
1364 
1365  for (unsigned long i = 0; i < frames; i++)
1366  {
1367  float sample_l = input8[i*channels] / scale_factor;
1368  float sample_r = input8[i*channels + (channels - 1)] / scale_factor;
1369 
1370  sound(i,0) = sample_l;
1371  sound(i,1) = sample_r;
1372  }
1373  }
1374  // FIXME: This is a workaround for a bug in PortAudio affecting 8-Bit
1375  // recording (see Octave bug #44305).
1376  // Remove this clause once the bug in PortAudio has been fixed.
1377  else if (recorder->get_sampleFormat () == bits_to_format (16)
1378  && recorder->get_nbits () == 8)
1379  {
1380  static double scale_factor = std::pow (2.0, 7) - 1.0;
1381 
1382  const int16_t *input16 = static_cast<const int16_t *> (input);
1383 
1384  for (unsigned long i = 0; i < frames; i++)
1385  {
1386  float sample_l = (input16[i*channels] >> 8) / scale_factor;
1387  float sample_r = (input16[i*channels + (channels - 1)] >> 8)
1388  / scale_factor;
1389 
1390  sound(i,0) = sample_l;
1391  sound(i,1) = sample_r;
1392  }
1393  }
1394  else if (recorder->get_sampleFormat () == bits_to_format (16))
1395  {
1396  static double scale_factor = std::pow (2.0, 15) - 1.0;
1397 
1398  const int16_t *input16 = static_cast<const int16_t *> (input);
1399 
1400  for (unsigned long i = 0; i < frames; i++)
1401  {
1402  float sample_l = input16[i*channels] / scale_factor;
1403  float sample_r = input16[i*channels + (channels - 1)] / scale_factor;
1404 
1405  sound(i,0) = sample_l;
1406  sound(i,1) = sample_r;
1407  }
1408  }
1409  else if (recorder->get_sampleFormat () == bits_to_format (24))
1410  {
1411  static double scale_factor = std::pow (2.0, 23);
1412 
1413  // FIXME: Is there a better way?
1414  // Could use union of int32_t, uint8_t[3:0]? (12/31/19)
1415  const uint8_t *input24 = static_cast<const uint8_t *> (input);
1416 
1417  int32_t sample_l32, sample_r32;
1418 
1419  uint8_t *sample_l = reinterpret_cast<uint8_t *> (&sample_l32);
1420  uint8_t *sample_r = reinterpret_cast<uint8_t *> (&sample_r32);
1421 
1422  for (unsigned long i = 0; i < frames; i++)
1423  {
1424  sample_l32 = sample_r32 = 0;
1425  for (int j = 0; j < 3; j++)
1426  {
1427  sample_l[j] = input24[i*channels*3 + j];
1428  sample_r[j] = input24[i*channels*3 + (channels - 1)*3 + j];
1429  }
1430 
1431  if (sample_l32 & 0x00800000)
1432  sample_l32 |= 0xff000000;
1433 
1434  if (sample_r32 & 0x00800000)
1435  sample_r32 |= 0xff000000;
1436 
1437  sound(i,0) = sample_l32 / scale_factor;
1438  sound(i,1) = sample_r32 / scale_factor;
1439  }
1440  }
1441 
1443  = octave::feval (recorder->octave_callback_function, ovl (sound), 1);
1444 
1445  return retval(0).int_value ();
1446 }
1447 
1448 static int
1449 portaudio_record_callback (const void *input, void *, unsigned long frames,
1450  const PaStreamCallbackTimeInfo *,
1451  PaStreamCallbackFlags, void *data)
1452 {
1453  audiorecorder *recorder = static_cast<audiorecorder *> (data);
1454 
1455  if (! recorder)
1456  error ("audio recorder callback function called without recorder");
1457 
1458  int channels = recorder->get_channels ();
1459 
1460  if (recorder->get_sampleFormat () == bits_to_format (8))
1461  {
1462  static float scale_factor = std::pow (2.0f, 7) - 1.0f;
1463 
1464  const int8_t *input8 = static_cast<const int8_t *> (input);
1465 
1466  for (unsigned long i = 0; i < frames; i++)
1467  {
1468  float sample_l = input8[i*channels] / scale_factor;
1469  float sample_r = input8[i*channels + (channels - 1)] / scale_factor;
1470 
1471  recorder->append (sample_l, sample_r);
1472  }
1473  }
1474  // FIXME: This is a workaround for a bug in PortAudio affecting 8-Bit
1475  // recording (see Octave bug #44305).
1476  // Remove this clause once the bug in PortAudio has been fixed.
1477  else if (recorder->get_sampleFormat () == bits_to_format (16)
1478  && recorder->get_nbits () == 8)
1479  {
1480  static double scale_factor = std::pow (2.0, 7) - 1.0;
1481 
1482  const int16_t *input16 = static_cast<const int16_t *> (input);
1483 
1484  for (unsigned long i = 0; i < frames; i++)
1485  {
1486  float sample_l = (input16[i*channels] >> 8) / scale_factor;
1487  float sample_r = (input16[i*channels + (channels - 1)] >> 8)
1488  / scale_factor;
1489 
1490  recorder->append (sample_l, sample_r);
1491  }
1492  }
1493  else if (recorder->get_sampleFormat () == bits_to_format (16))
1494  {
1495  static float scale_factor = std::pow (2.0f, 15) - 1.0f;
1496 
1497  const int16_t *input16 = static_cast<const int16_t *> (input);
1498 
1499  for (unsigned long i = 0; i < frames; i++)
1500  {
1501  float sample_l = input16[i*channels] / scale_factor;
1502  float sample_r = input16[i*channels + (channels - 1)] / scale_factor;
1503 
1504  recorder->append (sample_l, sample_r);
1505  }
1506  }
1507  else if (recorder->get_sampleFormat () == bits_to_format (24))
1508  {
1509  static float scale_factor = std::pow (2.0f, 23);
1510 
1511  // FIXME: Is there a better way?
1512  // Could use union of int32_t, uint8_t[3:0]? (12/31/19)
1513  const uint8_t *input24 = static_cast<const uint8_t *> (input);
1514 
1515  int32_t sample_l32, sample_r32;
1516 
1517  uint8_t *sample_l = reinterpret_cast<uint8_t *> (&sample_l32);
1518  uint8_t *sample_r = reinterpret_cast<uint8_t *> (&sample_r32);
1519 
1520  for (unsigned long i = 0; i < frames; i++)
1521  {
1522  sample_l32 = sample_r32 = 0;
1523  for (int j = 0; j < 3; j++)
1524  {
1525  sample_l[j] = input24[i*channels*3 + j];
1526  sample_r[j] = input24[i*channels*3 + (channels - 1)*3 + j];
1527  }
1528 
1529  if (sample_l32 & 0x00800000)
1530  sample_l32 |= 0xff000000;
1531 
1532  if (sample_r32 & 0x00800000)
1533  sample_r32 |= 0xff000000;
1534 
1535  recorder->append (sample_l32 / scale_factor,
1536  sample_r32 / scale_factor);
1537  }
1538  }
1539 
1540  if (recorder->get_sample_number () >= recorder->get_end_sample ())
1541  return paComplete;
1542 
1543  return paContinue;
1544 }
1545 
1546 static void
1547 safe_audiorecorder_stop (audiorecorder *recorder)
1548 {
1549  recorder->stop ();
1550 }
1551 
1552 audiorecorder::audiorecorder (void)
1553  : octave_callback_function (nullptr),
1554  id (-1), fs (8000), nbits (8), channels (1), sample_number (0),
1555  end_sample (-1), tag (""), y (), userdata (Matrix ()),
1556  left (), right (), stream (nullptr), input_parameters (), type ()
1557 { }
1558 
1559 audiorecorder::~audiorecorder (void)
1560 {
1561  if (isrecording ())
1562  {
1563  warning_with_id ("Octave:audio-interrupt",
1564  "interrupting recording audiorecorder");
1565  stop ();
1566  }
1567 }
1568 
1569 void
1570 audiorecorder::print (std::ostream& os, bool pr_as_read_syntax)
1571 {
1572  print_raw (os, pr_as_read_syntax);
1573  newline (os);
1574 }
1575 
1576 void
1577 audiorecorder::print_raw (std::ostream& os, bool) const
1578 {
1579  os << 0;
1580 }
1581 
1582 void
1583 audiorecorder::init (void)
1584 {
1585  if (Pa_Initialize () != paNoError)
1586  error ("audiorecorder: initialization error");
1587 
1588  if (Pa_GetDeviceCount () < 1)
1589  error ("audiorecorder: no audio devices found or available");
1590 
1591  int device = get_id ();
1592 
1593  if (device == -1)
1594  device = Pa_GetDefaultInputDevice ();
1595 
1596  input_parameters.device = device;
1597  input_parameters.channelCount = get_channels ();
1598  input_parameters.sampleFormat = bits_to_format (get_nbits ());
1599 
1600  // FIXME: This is a workaround for a bug in PortAudio affecting 8-Bit
1601  // recording (see Octave bug #44305).
1602  // Remove this clause once the bug in PortAudio has been fixed.
1603  if (get_nbits () == 8)
1604  input_parameters.sampleFormat = bits_to_format (16);
1605 
1606  const PaDeviceInfo *device_info = Pa_GetDeviceInfo (device);
1607 
1608  if (! device_info)
1609  warning_with_id ("Octave:invalid-default-audio-device",
1610  "invalid default audio device ID = %d", device);
1611 
1612  input_parameters.suggestedLatency
1613  = (device_info ? device_info->defaultHighInputLatency : -1);
1614 
1615  input_parameters.hostApiSpecificStreamInfo = nullptr;
1616 }
1617 
1618 void
1619 audiorecorder::set_fs (int fs_arg)
1620 {
1621  fs = fs_arg;
1622 }
1623 
1624 int
1625 audiorecorder::get_fs (void)
1626 {
1627  return fs;
1628 }
1629 
1630 void
1631 audiorecorder::set_nbits (int nbits_arg)
1632 {
1633  nbits = nbits_arg;
1634 }
1635 
1636 int
1637 audiorecorder::get_nbits (void)
1638 {
1639  return nbits;
1640 }
1641 
1642 PaSampleFormat
1643 audiorecorder::get_sampleFormat (void)
1644 {
1645  return input_parameters.sampleFormat;
1646 }
1647 
1648 void
1649 audiorecorder::set_id (int id_arg)
1650 {
1651  id = id_arg;
1652 }
1653 
1654 int
1655 audiorecorder::get_id (void)
1656 {
1657  return id;
1658 }
1659 
1660 void
1661 audiorecorder::set_channels (int channels_arg)
1662 {
1663  assert (channels_arg == 1 || channels_arg == 2);
1664  channels = channels_arg;
1665 }
1666 
1667 int
1668 audiorecorder::get_channels (void)
1669 {
1670  return channels;
1671 }
1672 
1673 audio_type
1674 audiorecorder::get_type (void)
1675 {
1676  return type;
1677 }
1678 
1679 void
1680 audiorecorder::set_sample_number (unsigned int sample_number_arg)
1681 {
1682  sample_number = sample_number_arg;
1683 }
1684 
1685 unsigned int
1686 audiorecorder::get_sample_number (void)
1687 {
1688  return sample_number;
1689 }
1690 
1691 unsigned int
1692 audiorecorder::get_total_samples (void)
1693 {
1694  return left.size ();
1695 }
1696 
1697 void
1698 audiorecorder::set_end_sample (unsigned int end_sample_arg)
1699 {
1700  end_sample = end_sample_arg;
1701 }
1702 
1703 unsigned int
1704 audiorecorder::get_end_sample (void)
1705 {
1706  return end_sample;
1707 }
1708 
1709 void
1710 audiorecorder::reset_end_sample (void)
1711 {
1712  set_end_sample (left.size ());
1713 }
1714 
1715 void
1716 audiorecorder::set_tag (const charMatrix& tag_arg)
1717 {
1718  tag = tag_arg;
1719 }
1720 
1721 charMatrix
1722 audiorecorder::get_tag (void)
1723 {
1724  return tag;
1725 }
1726 
1727 void
1728 audiorecorder::set_userdata (const octave_value& userdata_arg)
1729 {
1730  userdata = userdata_arg;
1731 }
1732 
1734 audiorecorder::get_userdata (void)
1735 {
1736  return userdata;
1737 }
1738 
1740 audiorecorder::getaudiodata (void)
1741 {
1742  // Must get size before entering loop as the value of left.size() may change
1743  // during loop with simultaneous recording and playback (bug #50674).
1744  unsigned int ls = left.size ();
1745  Matrix audio (2, ls);
1746 
1747  for (unsigned int i = 0; i < ls; i++)
1748  {
1749  audio(0,i) = left[i];
1750  audio(1,i) = right[i];
1751  }
1752 
1753  return audio;
1754 }
1755 
1756 audioplayer *
1757 audiorecorder::getplayer (void)
1758 {
1759  audioplayer *player = new audioplayer ();
1760 
1761  player->set_y (getaudiodata ());
1762  player->set_fs (get_fs ());
1763  player->set_nbits (get_nbits ());
1764  player->init ();
1765 
1766  return player;
1767 }
1768 
1769 bool
1770 audiorecorder::isrecording (void)
1771 {
1772  if (get_stream () == nullptr)
1773  return false;
1774 
1775  PaError err;
1776  err = Pa_IsStreamActive (stream);
1777  if (err != 0 && err != 1)
1778  error ("audiorecorder: checking stream activity status failed");
1779 
1780  return (err == 1);
1781 }
1782 
1783 void
1784 audiorecorder::record (void)
1785 {
1786  if (get_stream ())
1787  stop ();
1788 
1789  left.clear ();
1790  right.clear ();
1791 
1792  const unsigned int buffer_size = get_fs () / 20;
1793 
1794  PaError err;
1795  if (octave_callback_function != nullptr)
1796  {
1797  err = Pa_OpenStream (&stream, &(input_parameters), nullptr,
1798  get_fs (), buffer_size, paClipOff,
1799  octave_record_callback, this);
1800  }
1801  else
1802  {
1803  err = Pa_OpenStream (&stream, &(input_parameters), nullptr,
1804  get_fs (), buffer_size, paClipOff,
1805  portaudio_record_callback, this);
1806  }
1807  if (err != paNoError)
1808  error ("audiorecorder: unable to open audio recording stream");
1809 
1810  err = Pa_StartStream (stream);
1811  if (err != paNoError)
1812  error ("audiorecorder: unable to start audio recording stream");
1813 }
1814 
1815 void
1816 audiorecorder::recordblocking (float seconds)
1817 {
1818  if (get_stream ())
1819  stop ();
1820 
1821  left.clear ();
1822  right.clear ();
1823 
1824  const unsigned int buffer_size = get_fs () / 20;
1825  OCTAVE_LOCAL_BUFFER (uint8_t, buffer, buffer_size * 2 * 3);
1826 
1827  PaError err;
1828  err = Pa_OpenStream (&stream, &(input_parameters), nullptr,
1829  get_fs (), buffer_size, paClipOff, nullptr, this);
1830  if (err != paNoError)
1831  error ("audiorecorder: unable to open audio recording stream");
1832 
1833  err = Pa_StartStream (stream);
1834  if (err != paNoError)
1835  error ("audiorecorder: unable to start audio recording stream");
1836 
1837  unsigned int frames = seconds * get_fs ();
1838 
1839  octave::unwind_protect frame;
1840 
1841  frame.add_fcn (safe_audiorecorder_stop, this);
1842 
1843  for (unsigned int i = 0; i < frames; i += buffer_size)
1844  {
1845  octave_quit ();
1846 
1847  Pa_ReadStream (get_stream (), buffer, buffer_size);
1848 
1849  if (octave_callback_function != nullptr)
1850  octave_record_callback (buffer, nullptr, buffer_size, nullptr, 0, this);
1851  else
1852  portaudio_record_callback (buffer, nullptr, buffer_size, nullptr, 0, this);
1853  }
1854 }
1855 
1856 void
1857 audiorecorder::pause (void)
1858 {
1859  if (get_stream () == nullptr)
1860  return;
1861 
1862  PaError err;
1863  err = Pa_StopStream (stream);
1864  if (err != paNoError)
1865  error ("audiorecorder: unable to stop audio recording stream");
1866 }
1867 
1868 void
1869 audiorecorder::resume (void)
1870 {
1871  if (get_stream () == nullptr)
1872  return;
1873 
1874  PaError err;
1875  err = Pa_StartStream (stream);
1876  if (err != paNoError)
1877  error ("audiorecorder: unable to start audio recording stream");
1878 }
1879 
1880 void
1881 audiorecorder::stop (void)
1882 {
1883  if (get_stream () == nullptr)
1884  return;
1885 
1886  PaError err;
1887  if (! Pa_IsStreamStopped (get_stream ()))
1888  {
1889  err = Pa_AbortStream (get_stream ());
1890  if (err != paNoError)
1891  error ("audioplayer: unable to stop audio playback stream");
1892  }
1893 
1894  err = Pa_CloseStream (stream);
1895  if (err != paNoError)
1896  error ("audiorecorder: unable to close audio recording stream");
1897 
1898  set_sample_number (0);
1899  reset_end_sample ();
1900  stream = nullptr;
1901 }
1902 
1903 void
1904 audiorecorder::append (float sample_l, float sample_r)
1905 {
1906  left.push_back (sample_l);
1907  right.push_back (sample_r);
1908  set_sample_number (get_sample_number () + 1);
1909 }
1910 
1911 PaStream *
1912 audiorecorder::get_stream (void)
1913 {
1914  return stream;
1915 }
1916 
1917 #endif
1918 
1919 DEFMETHOD_DLD (__recorder_audiorecorder__, interp, args, ,
1920  doc: /* -*- texinfo -*-
1921 @deftypefn {} {@var{recorder} =} __recorder_audiorecorder__ (@var{fs}, @var{nbits}, @var{channels})
1922 @deftypefnx {} {@var{recorder} =} __recorder_audiorecorder__ (@var{fs}, @var{nbits}, @var{channels}, @var{id})
1923 @deftypefnx {} {@var{recorder} =} __recorder_audiorecorder__ (@var{fcn}, @dots{})
1924 Undocumented internal function.
1925 @end deftypefn */)
1926 {
1928 
1929 #if defined (HAVE_PORTAUDIO)
1930 
1931  interp.mlock ();
1932 
1933  int nargin = args.length ();
1934 
1935  audiorecorder *recorder = new audiorecorder ();
1936 
1937  if (nargin > 0)
1938  {
1939  bool is_function = (args(0).is_string () || args(0).is_function_handle ()
1940  || args(0).is_inline_function ());
1941 
1942  if (is_function)
1943  error ("audiorecorder: callbacks not yet implemented");
1944  }
1945 
1946  if (nargin >= 3)
1947  {
1948  recorder->set_fs (args(0).int_value ());
1949  recorder->set_nbits (args(1).int_value ());
1950  recorder->set_channels (args(2).int_value ());
1951  }
1952 
1953  if (nargin == 4)
1954  {
1955  recorder->set_id (args(3).int_value ());
1956  }
1957 
1958  recorder->init ();
1959  retval = recorder;
1960 
1961 #else
1962  octave_unused_parameter (interp);
1963  octave_unused_parameter (args);
1964 
1965  err_disabled_feature ("__recorder_audiorecorder__",
1966  "audio playback and recording through PortAudio");
1967 #endif
1968 
1969  return retval;
1970 }
1971 
1972 #if defined (HAVE_PORTAUDIO)
1973 
1974 static audiorecorder *
1975 get_recorder (octave::interpreter& interp, const octave_value& ov)
1976 {
1977  interp.mlock ();
1978 
1979  const octave_base_value& rep = ov.get_rep ();
1980 
1981  octave_base_value *ncrep = const_cast<octave_base_value *> (&rep);
1982 
1983  audiorecorder *rec = dynamic_cast<audiorecorder *> (ncrep);
1984  if (! rec)
1985  error ("audiodevinfo.cc (get_recorder): dynamic_cast to audiorecorder failed");
1986 
1987  return rec;
1988 }
1989 
1990 #endif
1991 
1992 DEFMETHOD_DLD (__recorder_getaudiodata__, interp, args, ,
1993  doc: /* -*- texinfo -*-
1994 @deftypefn {} {@var{data} =} __recorder_getaudiodata__ (@var{recorder})
1995 Undocumented internal function.
1996 @end deftypefn */)
1997 {
1999 
2000 #if defined (HAVE_PORTAUDIO)
2001  retval = get_recorder (interp, args(0))->getaudiodata ();
2002 #else
2003  octave_unused_parameter (interp);
2004  octave_unused_parameter (args);
2005 
2006  err_disabled_feature ("__recorder_getaudiodata__",
2007  "audio playback and recording through PortAudio");
2008 #endif
2009 
2010  return retval;
2011 }
2012 
2013 DEFMETHOD_DLD (__recorder_get_channels__, interp, args, ,
2014  doc: /* -*- texinfo -*-
2015 @deftypefn {} {@var{n} =} __recorder_get_channels__ (@var{recorder})
2016 Undocumented internal function.
2017 @end deftypefn */)
2018 {
2020 
2021 #if defined (HAVE_PORTAUDIO)
2022  retval = get_recorder (interp, args(0))->get_channels ();
2023 #else
2024  octave_unused_parameter (interp);
2025  octave_unused_parameter (args);
2026 
2027  err_disabled_feature ("__recorder_get_channels__",
2028  "audio playback and recording through PortAudio");
2029 #endif
2030 
2031  return retval;
2032 }
2033 
2034 DEFMETHOD_DLD (__recorder_get_fs__, interp, args, ,
2035  doc: /* -*- texinfo -*-
2036 @deftypefn {} {@var{fs} =} __recorder_get_fs__ (@var{recorder})
2037 Undocumented internal function.
2038 @end deftypefn */)
2039 {
2041 
2042 #if defined (HAVE_PORTAUDIO)
2043  retval = get_recorder (interp, args(0))->get_fs ();
2044 #else
2045  octave_unused_parameter (interp);
2046  octave_unused_parameter (args);
2047 
2048  err_disabled_feature ("__recorder_get_fs__",
2049  "audio playback and recording through PortAudio");
2050 #endif
2051 
2052  return retval;
2053 }
2054 
2055 DEFMETHOD_DLD (__recorder_get_id__, interp, args, ,
2056  doc: /* -*- texinfo -*-
2057 @deftypefn {} {@var{id} =} __recorder_get_id__ (@var{recorder})
2058 Undocumented internal function.
2059 @end deftypefn */)
2060 {
2062 
2063 #if defined (HAVE_PORTAUDIO)
2064  retval = get_recorder (interp, args(0))->get_id ();
2065 #else
2066  octave_unused_parameter (interp);
2067  octave_unused_parameter (args);
2068 
2069  err_disabled_feature ("__recorder_get_id__",
2070  "audio playback and recording through PortAudio");
2071 #endif
2072 
2073  return retval;
2074 }
2075 
2076 DEFMETHOD_DLD (__recorder_get_nbits__, interp, args, ,
2077  doc: /* -*- texinfo -*-
2078 @deftypefn {} {@var{nbits} =} __recorder_get_nbits__ (@var{recorder})
2079 Undocumented internal function.
2080 @end deftypefn */)
2081 {
2083 
2084 #if defined (HAVE_PORTAUDIO)
2085  retval = get_recorder (interp, args(0))->get_nbits ();
2086 #else
2087  octave_unused_parameter (interp);
2088  octave_unused_parameter (args);
2089 
2090  err_disabled_feature ("__recorder_get_nbits__",
2091  "audio playback and recording through PortAudio");
2092 #endif
2093 
2094  return retval;
2095 }
2096 
2097 DEFMETHOD_DLD (__recorder_get_sample_number__, interp, args, ,
2098  doc: /* -*- texinfo -*-
2099 @deftypefn {} {@var{n} =} __recorder_get_sample_number__ (@var{recorder})
2100 Undocumented internal function.
2101 @end deftypefn */)
2102 {
2104 
2105 #if defined (HAVE_PORTAUDIO)
2106  retval = get_recorder (interp, args(0))->get_sample_number ();
2107 #else
2108  octave_unused_parameter (interp);
2109  octave_unused_parameter (args);
2110 
2111  err_disabled_feature ("__recorder_get_sample_number__",
2112  "audio playback and recording through PortAudio");
2113 #endif
2114 
2115  return retval;
2116 }
2117 
2118 DEFMETHOD_DLD (__recorder_get_tag__, interp, args, ,
2119  doc: /* -*- texinfo -*-
2120 @deftypefn {} {@var{tag} =} __recorder_get_tag__ (@var{recorder})
2121 Undocumented internal function.
2122 @end deftypefn */)
2123 {
2125 
2126 #if defined (HAVE_PORTAUDIO)
2127  retval = get_recorder (interp, args(0))->get_tag ();
2128 #else
2129  octave_unused_parameter (interp);
2130  octave_unused_parameter (args);
2131 
2132  err_disabled_feature ("__recorder_get_tag__",
2133  "audio playback and recording through PortAudio");
2134 #endif
2135 
2136  return retval;
2137 }
2138 
2139 DEFMETHOD_DLD (__recorder_get_total_samples__, interp, args, ,
2140  doc: /* -*- texinfo -*-
2141 @deftypefn {} {@var{n} =} __recorder_get_total_samples__ (@var{recorder})
2142 Undocumented internal function.
2143 @end deftypefn */)
2144 {
2146 
2147 #if defined (HAVE_PORTAUDIO)
2148  retval = get_recorder (interp, args(0))->get_total_samples ();
2149 #else
2150  octave_unused_parameter (interp);
2151  octave_unused_parameter (args);
2152 
2153  err_disabled_feature ("__recorder_get_total_samples__",
2154  "audio playback and recording through PortAudio");
2155 #endif
2156 
2157  return retval;
2158 }
2159 
2160 DEFMETHOD_DLD (__recorder_get_userdata__, interp, args, ,
2161  doc: /* -*- texinfo -*-
2162 @deftypefn {} {@var{data} =} __recorder_get_userdata__ (@var{recorder})
2163 Undocumented internal function.
2164 @end deftypefn */)
2165 {
2167 
2168 #if defined (HAVE_PORTAUDIO)
2169  retval = get_recorder (interp, args(0))->get_userdata ();
2170 #else
2171  octave_unused_parameter (interp);
2172  octave_unused_parameter (args);
2173 
2174  err_disabled_feature ("__recorder_get_userdata__",
2175  "audio playback and recording through PortAudio");
2176 #endif
2177 
2178  return retval;
2179 }
2180 
2181 DEFMETHOD_DLD (__recorder_isrecording__, interp, args, ,
2182  doc: /* -*- texinfo -*-
2183 @deftypefn {} {} __recorder_isrecording__ (@var{recorder})
2184 Undocumented internal function.
2185 @end deftypefn */)
2186 {
2188 
2189 #if defined (HAVE_PORTAUDIO)
2190  retval = get_recorder (interp, args(0))->isrecording ();
2191 #else
2192  octave_unused_parameter (interp);
2193  octave_unused_parameter (args);
2194 
2195  err_disabled_feature ("__recorder_isrecording__",
2196  "audio playback and recording through PortAudio");
2197 #endif
2198 
2199  return retval;
2200 }
2201 
2202 DEFMETHOD_DLD (__recorder_pause__, interp, args, ,
2203  doc: /* -*- texinfo -*-
2204 @deftypefn {} {} __recorder_pause__ (@var{recorder})
2205 Undocumented internal function.
2206 @end deftypefn */)
2207 {
2208 #if defined (HAVE_PORTAUDIO)
2209  get_recorder (interp, args(0))->pause ();
2210  return ovl ();
2211 #else
2212  octave_unused_parameter (interp);
2213  octave_unused_parameter (args);
2214 
2215  err_disabled_feature ("__recorder_pause__",
2216  "audio playback and recording through PortAudio");
2217 #endif
2218 }
2219 
2220 DEFMETHOD_DLD (__recorder_recordblocking__, interp, args, ,
2221  doc: /* -*- texinfo -*-
2222 @deftypefn {} {} __recorder_recordblocking__ (@var{recorder}, @var{seconds})
2223 Undocumented internal function.
2224 @end deftypefn */)
2225 {
2226 #if defined (HAVE_PORTAUDIO)
2227  float seconds = args(1).float_value ();
2228  get_recorder (interp, args(0))->recordblocking (seconds);
2229  return ovl ();
2230 #else
2231  octave_unused_parameter (interp);
2232  octave_unused_parameter (args);
2233 
2234  err_disabled_feature ("__recorder_recordblocking__",
2235  "audio playback and recording through PortAudio");
2236 #endif
2237 }
2238 
2239 DEFMETHOD_DLD (__recorder_record__, interp, args, ,
2240  doc: /* -*- texinfo -*-
2241 @deftypefn {} {} __recorder_record__ (@var{recorder})
2242 @deftypefnx {} {} __recorder_record__ (@var{recorder}, @var{seconds})
2243 Undocumented internal function.
2244 @end deftypefn */)
2245 {
2246 #if defined (HAVE_PORTAUDIO)
2247  audiorecorder *recorder = get_recorder (interp, args(0));
2248 
2249  if (args.length () == 2)
2250  recorder->set_end_sample (args(1).int_value () * recorder->get_fs ());
2251 
2252  recorder->record ();
2253  return ovl ();
2254 #else
2255  octave_unused_parameter (interp);
2256  octave_unused_parameter (args);
2257 
2258  err_disabled_feature ("__recorder_record__",
2259  "audio playback and recording through PortAudio");
2260 #endif
2261 }
2262 
2263 DEFMETHOD_DLD (__recorder_resume__, interp, args, ,
2264  doc: /* -*- texinfo -*-
2265 @deftypefn {} {} __recorder_resume__ (@var{recorder})
2266 Undocumented internal function.
2267 @end deftypefn */)
2268 {
2269 #if defined (HAVE_PORTAUDIO)
2270  if (args.length () == 1)
2271  get_recorder (interp, args(0))->resume ();
2272  return ovl ();
2273 #else
2274  octave_unused_parameter (interp);
2275  octave_unused_parameter (args);
2276 
2277  err_disabled_feature ("__recorder_resume__",
2278  "audio playback and recording through PortAudio");
2279 #endif
2280 }
2281 
2282 DEFMETHOD_DLD (__recorder_set_fs__, interp, args, ,
2283  doc: /* -*- texinfo -*-
2284 @deftypefn {} {} __recorder_set_fs__ (@var{recorder}, @var{fs})
2285 Undocumented internal function.
2286 @end deftypefn */)
2287 {
2288 #if defined (HAVE_PORTAUDIO)
2289  if (args.length () == 2)
2290  get_recorder (interp, args(0))->set_fs (args(1).int_value ());
2291  return ovl ();
2292 #else
2293  octave_unused_parameter (interp);
2294  octave_unused_parameter (args);
2295 
2296  err_disabled_feature ("__recorder_set_fs__",
2297  "audio playback and recording through PortAudio");
2298 #endif
2299 }
2300 
2301 DEFMETHOD_DLD (__recorder_set_tag__, interp, args, ,
2302  doc: /* -*- texinfo -*-
2303 @deftypefn {} {} __recorder_set_tag__ (@var{recorder}, @var{tag})
2304 Undocumented internal function.
2305 @end deftypefn */)
2306 {
2307 #if defined (HAVE_PORTAUDIO)
2308  if (args.length () == 2)
2309  get_recorder (interp, args(0))->set_tag (args(1).char_matrix_value ());
2310  return ovl ();
2311 #else
2312  octave_unused_parameter (interp);
2313  octave_unused_parameter (args);
2314 
2315  err_disabled_feature ("__recorder_set_tag__",
2316  "audio playback and recording through PortAudio");
2317 #endif
2318 }
2319 
2320 DEFMETHOD_DLD (__recorder_set_userdata__, interp, args, ,
2321  doc: /* -*- texinfo -*-
2322 @deftypefn {} {} __recorder_set_userdata__ (@var{recorder}, @var{data})
2323 Undocumented internal function.
2324 @end deftypefn */)
2325 {
2326 #if defined (HAVE_PORTAUDIO)
2327  if (args.length () == 2)
2328  get_recorder (interp, args(0))->set_userdata (args(1));
2329  return ovl ();
2330 #else
2331  octave_unused_parameter (interp);
2332  octave_unused_parameter (args);
2333 
2334  err_disabled_feature ("__recorder_set_userdata__",
2335  "audio playback and recording through PortAudio");
2336 #endif
2337 }
2338 
2339 DEFMETHOD_DLD (__recorder_stop__, interp, args, ,
2340  doc: /* -*- texinfo -*-
2341 @deftypefn {} {} __recorder_stop__ (@var{recorder})
2342 Undocumented internal function.
2343 @end deftypefn */)
2344 {
2345 #if defined (HAVE_PORTAUDIO)
2346  if (args.length () == 1)
2347  get_recorder (interp, args(0))->stop ();
2348  return ovl ();
2349 #else
2350  octave_unused_parameter (interp);
2351  octave_unused_parameter (args);
2352 
2353  err_disabled_feature ("__recorder_stop__",
2354  "audio playback and recording through PortAudio");
2355 #endif
2356 }
2357 
2358 DEFMETHOD_DLD (__player_audioplayer__, interp, args, ,
2359  doc: /* -*- texinfo -*-
2360 @deftypefn {} {@var{player} =} __player_audioplayer__ (@var{y}, @var{fs})
2361 @deftypefnx {} {@var{player} =} __player_audioplayer__ (@var{y}, @var{fs}, @var{nbits})
2362 @deftypefnx {} {@var{player} =} __player_audioplayer__ (@var{y}, @var{fs}, @var{nbits}, @var{id})
2363 Undocumented internal function.
2364 @end deftypefn */)
2365 {
2367 
2368 #if defined (HAVE_PORTAUDIO)
2369  interp.mlock ();
2370 
2371  audioplayer *recorder = new audioplayer ();
2372 
2373  bool is_function = (args(0).is_string () || args(0).is_function_handle ()
2374  || args(0).is_inline_function ());
2375 
2376  if (is_function)
2377  error ("audioplayer: callbacks not yet implemented");
2378 
2379  recorder->set_y (args(0));
2380  recorder->set_fs (args(1).int_value ());
2381 
2382  if (args.length () > 2)
2383  {
2384  // FIXME: Should be able to support 32-bit streams (bug #57939)
2385  int nbits = args(2).int_value ();
2386  if (nbits != 8 && nbits != 16 && nbits != 24)
2387  error ("audioplayer: NBITS must be 8, 16, or 24");
2388 
2389  switch (args.length ())
2390  {
2391  case 3:
2392  recorder->set_nbits (nbits);
2393  break;
2394 
2395  case 4:
2396  recorder->set_nbits (nbits);
2397  recorder->set_id (args(3).int_value ());
2398  break;
2399  }
2400  }
2401 
2402  if (is_function)
2403  recorder->init_fn ();
2404  else
2405  recorder->init ();
2406 
2407  retval = recorder;
2408 #else
2409  octave_unused_parameter (interp);
2410  octave_unused_parameter (args);
2411 
2412  err_disabled_feature ("__player_audioplayer__",
2413  "audio playback and recording through PortAudio");
2414 #endif
2415 
2416  return retval;
2417 }
2418 
2419 #if defined (HAVE_PORTAUDIO)
2420 
2421 static audioplayer *
2422 get_player (octave::interpreter& interp, const octave_value& ov)
2423 {
2424  interp.mlock ();
2425 
2426  const octave_base_value& rep = ov.get_rep ();
2427 
2428  octave_base_value *ncrep = const_cast<octave_base_value *> (&rep);
2429 
2430  audioplayer *pl = dynamic_cast<audioplayer *> (ncrep);
2431  if (! pl)
2432  error ("audiodevinfo.cc get_player: dynamic_cast to audioplayer failed");
2433 
2434  return pl;
2435 }
2436 
2437 #endif
2438 
2439 DEFMETHOD_DLD (__player_get_channels__, interp, args, ,
2440  doc: /* -*- texinfo -*-
2441 @deftypefn {} {@var{n} =} __player_get_channels__ (@var{player})
2442 Undocumented internal function.
2443 @end deftypefn */)
2444 {
2446 
2447 #if defined (HAVE_PORTAUDIO)
2448  if (args.length () == 1)
2449  retval = get_player (interp, args(0))->get_channels ();
2450 #else
2451  octave_unused_parameter (interp);
2452  octave_unused_parameter (args);
2453 
2454  err_disabled_feature ("__player_get_channels__",
2455  "audio playback and recording through PortAudio");
2456 #endif
2457 
2458  return retval;
2459 }
2460 
2461 DEFMETHOD_DLD (__player_get_fs__, interp, args, ,
2462  doc: /* -*- texinfo -*-
2463 @deftypefn {} {@var{fs} =} __player_get_fs__ (@var{player})
2464 Undocumented internal function.
2465 @end deftypefn */)
2466 {
2468 
2469 #if defined (HAVE_PORTAUDIO)
2470  if (args.length () == 1)
2471  retval = get_player (interp, args(0))->get_fs ();
2472 #else
2473  octave_unused_parameter (interp);
2474  octave_unused_parameter (args);
2475 
2476  err_disabled_feature ("__player_get_fs__",
2477  "audio playback and recording through PortAudio");
2478 #endif
2479 
2480  return retval;
2481 }
2482 
2483 DEFMETHOD_DLD (__player_get_id__, interp, args, ,
2484  doc: /* -*- texinfo -*-
2485 @deftypefn {} {@var{id} =} __player_get_id__ (@var{player})
2486 Undocumented internal function.
2487 @end deftypefn */)
2488 {
2490 
2491 #if defined (HAVE_PORTAUDIO)
2492  if (args.length () == 1)
2493  retval = get_player (interp, args(0))->get_id ();
2494 #else
2495  octave_unused_parameter (interp);
2496  octave_unused_parameter (args);
2497 
2498  err_disabled_feature ("__player_get_id__",
2499  "audio playback and recording through PortAudio");
2500 #endif
2501 
2502  return retval;
2503 }
2504 
2505 DEFMETHOD_DLD (__player_get_nbits__, interp, args, ,
2506  doc: /* -*- texinfo -*-
2507 @deftypefn {} {@var{nbits} =} __player_get_nbits__ (@var{player})
2508 Undocumented internal function.
2509 @end deftypefn */)
2510 {
2512 
2513 #if defined (HAVE_PORTAUDIO)
2514  if (args.length () == 1)
2515  retval = get_player (interp, args(0))->get_nbits ();
2516 #else
2517  octave_unused_parameter (interp);
2518  octave_unused_parameter (args);
2519 
2520  err_disabled_feature ("__player_get_nbits__",
2521  "audio playback and recording through PortAudio");
2522 #endif
2523 
2524  return retval;
2525 }
2526 
2527 DEFMETHOD_DLD (__player_get_sample_number__, interp, args, ,
2528  doc: /* -*- texinfo -*-
2529 @deftypefn {} {@var{n} =} __player_get_sample_number__ (@var{player})
2530 Undocumented internal function.
2531 @end deftypefn */)
2532 {
2534 
2535 #if defined (HAVE_PORTAUDIO)
2536  if (args.length () == 1)
2537  retval = get_player (interp, args(0))->get_sample_number ();
2538 #else
2539  octave_unused_parameter (interp);
2540  octave_unused_parameter (args);
2541 
2542  err_disabled_feature ("__player_get_sample_number__",
2543  "audio playback and recording through PortAudio");
2544 #endif
2545 
2546  return retval;
2547 }
2548 
2549 DEFMETHOD_DLD (__player_get_tag__, interp, args, ,
2550  doc: /* -*- texinfo -*-
2551 @deftypefn {} {@var{tag} =} __player_get_tag__ (@var{player})
2552 Undocumented internal function.
2553 @end deftypefn */)
2554 {
2556 
2557 #if defined (HAVE_PORTAUDIO)
2558  if (args.length () == 1)
2559  retval = get_player (interp, args(0))->get_tag ();
2560 #else
2561  octave_unused_parameter (interp);
2562  octave_unused_parameter (args);
2563 
2564  err_disabled_feature ("__player_get_tag__",
2565  "audio playback and recording through PortAudio");
2566 #endif
2567 
2568  return retval;
2569 }
2570 
2571 DEFMETHOD_DLD (__player_get_total_samples__, interp, args, ,
2572  doc: /* -*- texinfo -*-
2573 @deftypefn {} {@var{n} =} __player_get_total_samples__ (@var{player})
2574 Undocumented internal function.
2575 @end deftypefn */)
2576 {
2578 
2579 #if defined (HAVE_PORTAUDIO)
2580  if (args.length () == 1)
2581  retval = get_player (interp, args(0))->get_total_samples ();
2582 #else
2583  octave_unused_parameter (interp);
2584  octave_unused_parameter (args);
2585 
2586  err_disabled_feature ("__player_get_total_samples__",
2587  "audio playback and recording through PortAudio");
2588 #endif
2589 
2590  return retval;
2591 }
2592 
2593 DEFMETHOD_DLD (__player_get_userdata__, interp, args, ,
2594  doc: /* -*- texinfo -*-
2595 @deftypefn {} {@var{data} =} __player_get_userdata__ (@var{player})
2596 Undocumented internal function.
2597 @end deftypefn */)
2598 {
2600 
2601 #if defined (HAVE_PORTAUDIO)
2602  if (args.length () == 1)
2603  retval = get_player (interp, args(0))->get_userdata ();
2604 #else
2605  octave_unused_parameter (interp);
2606  octave_unused_parameter (args);
2607 
2608  err_disabled_feature ("__player_get_userdata__",
2609  "audio playback and recording through PortAudio");
2610 #endif
2611 
2612  return retval;
2613 }
2614 
2615 DEFMETHOD_DLD (__player_isplaying__, interp, args, ,
2616  doc: /* -*- texinfo -*-
2617 @deftypefn {} {} __player_isplaying__ (@var{player})
2618 Undocumented internal function.
2619 @end deftypefn */)
2620 {
2622 
2623 #if defined (HAVE_PORTAUDIO)
2624  if (args.length () == 1)
2625  retval = get_player (interp, args(0))->isplaying ();
2626 #else
2627  octave_unused_parameter (interp);
2628  octave_unused_parameter (args);
2629 
2630  err_disabled_feature ("__player_isplaying__",
2631  "audio playback and recording through PortAudio");
2632 #endif
2633 
2634  return retval;
2635 }
2636 
2637 DEFMETHOD_DLD (__player_pause__, interp, args, ,
2638  doc: /* -*- texinfo -*-
2639 @deftypefn {} {} __player_pause__ (@var{player})
2640 Undocumented internal function.
2641 @end deftypefn */)
2642 {
2643 #if defined (HAVE_PORTAUDIO)
2644  if (args.length () == 1)
2645  get_player (interp, args(0))->pause ();
2646  return ovl ();
2647 #else
2648  octave_unused_parameter (interp);
2649  octave_unused_parameter (args);
2650 
2651  err_disabled_feature ("__player_pause__",
2652  "audio playback and recording through PortAudio");
2653 #endif
2654 }
2655 
2656 DEFMETHOD_DLD (__player_playblocking__, interp, args, ,
2657  doc: /* -*- texinfo -*-
2658 @deftypefn {} {} __player_playblocking__ (@var{player})
2659 @deftypefnx {} {} __player_playblocking__ (@var{player}, @var{start})
2660 @deftypefnx {} {} __player_playblocking__ (@var{player}, [@var{start}, @var{end}])
2661 Undocumented internal function.
2662 @end deftypefn */)
2663 {
2664 #if defined (HAVE_PORTAUDIO)
2665 
2666  audioplayer *player = get_player (interp, args(0));
2667 
2668  if (args.length () == 1)
2669  {
2670  player->playblocking ();
2671  }
2672  else if (args.length () == 2)
2673  {
2674  if (args(1).is_matrix_type ())
2675  {
2676  RowVector range = args(1).row_vector_value ();
2677 
2678  unsigned int start = range.elem (0) - 1;
2679  unsigned int end = range.elem (1) - 1;
2680 
2681  if (start > player->get_total_samples ()
2682  || start > end || end > player->get_total_samples ())
2683  error ("audioplayer: invalid range specified for playback");
2684 
2685  player->set_sample_number (start);
2686  player->set_end_sample (end);
2687  }
2688  else
2689  {
2690  unsigned int start = args(1).int_value () - 1;
2691 
2692  if (start > player->get_total_samples ())
2693  error ("audioplayer: invalid range specified for playback");
2694 
2695  player->set_sample_number (start);
2696  }
2697 
2698  player->playblocking ();
2699  }
2700 
2701  return ovl ();
2702 #else
2703  octave_unused_parameter (interp);
2704  octave_unused_parameter (args);
2705 
2706  err_disabled_feature ("__player_playblocking__",
2707  "audio playback and recording through PortAudio");
2708 #endif
2709 }
2710 
2711 DEFMETHOD_DLD (__player_play__, interp, args, ,
2712  doc: /* -*- texinfo -*-
2713 @deftypefn {} {} __player_play__ (@var{player})
2714 @deftypefnx {} {} __player_play__ (@var{player}, @var{start})
2715 @deftypefnx {} {} __player_play__ (@var{player}, [@var{start}, @var{end}])
2716 Undocumented internal function.
2717 @end deftypefn */)
2718 {
2719 #if defined (HAVE_PORTAUDIO)
2720 
2721  if (args.length () == 1)
2722  {
2723  get_player (interp, args(0))->play ();
2724  }
2725  else if (args.length () == 2)
2726  {
2727  audioplayer *player = get_player (interp, args(0));
2728 
2729  if (args(1).is_matrix_type ())
2730  {
2731  RowVector range = args(1).row_vector_value ();
2732 
2733  unsigned int start = range.elem (0) - 1;
2734  unsigned int end = range.elem (1) - 1;
2735 
2736  if (start > player->get_total_samples ()
2737  || start > end || end > player->get_total_samples ())
2738  error ("audioplayer: invalid range specified for playback");
2739 
2740  player->set_sample_number (start);
2741  player->set_end_sample (end);
2742  }
2743  else
2744  {
2745  unsigned int start = args(1).int_value () - 1;
2746 
2747  if (start > player->get_total_samples ())
2748  error ("audioplayer: invalid range specified for playback");
2749 
2750  player->set_sample_number (start);
2751  }
2752 
2753  player->play ();
2754  }
2755 
2756  return ovl ();
2757 #else
2758  octave_unused_parameter (interp);
2759  octave_unused_parameter (args);
2760 
2761  err_disabled_feature ("__player_play__",
2762  "audio playback and recording through PortAudio");
2763 #endif
2764 }
2765 
2766 DEFMETHOD_DLD (__player_resume__, interp, args, ,
2767  doc: /* -*- texinfo -*-
2768 @deftypefn {} {} __player_resume__ (@var{player})
2769 Undocumented internal function.
2770 @end deftypefn */)
2771 {
2772 #if defined (HAVE_PORTAUDIO)
2773  if (args.length () == 1)
2774  get_player (interp, args(0))->resume ();
2775  return ovl ();
2776 #else
2777  octave_unused_parameter (interp);
2778  octave_unused_parameter (args);
2779 
2780  err_disabled_feature ("__player_resume__",
2781  "audio playback and recording through PortAudio");
2782 #endif
2783 }
2784 
2785 DEFMETHOD_DLD (__player_set_fs__, interp, args, ,
2786  doc: /* -*- texinfo -*-
2787 @deftypefn {} {} __player_set_fs__ (@var{player}, @var{fs})
2788 Undocumented internal function.
2789 @end deftypefn */)
2790 {
2791 #if defined (HAVE_PORTAUDIO)
2792  if (args.length () == 2)
2793  get_player (interp, args(0))->set_fs (args(1).int_value ());
2794  return ovl ();
2795 #else
2796  octave_unused_parameter (interp);
2797  octave_unused_parameter (args);
2798 
2799  err_disabled_feature ("__player_set_fs__",
2800  "audio playback and recording through PortAudio");
2801 #endif
2802 }
2803 
2804 DEFMETHOD_DLD (__player_set_tag__, interp, args, ,
2805  doc: /* -*- texinfo -*-
2806 @deftypefn {} {} __player_set_tag__ (@var{player}, @var{tag})
2807 Undocumented internal function.
2808 @end deftypefn */)
2809 {
2810 #if defined (HAVE_PORTAUDIO)
2811  if (args.length () == 2)
2812  get_player (interp, args(0))->set_tag (args(1).char_matrix_value ());
2813  return ovl ();
2814 #else
2815  octave_unused_parameter (interp);
2816  octave_unused_parameter (args);
2817 
2818  err_disabled_feature ("__player_set_tag__",
2819  "audio playback and recording through PortAudio");
2820 #endif
2821 }
2822 
2823 DEFMETHOD_DLD (__player_set_userdata__, interp, args, ,
2824  doc: /* -*- texinfo -*-
2825 @deftypefn {} {} __player_set_userdata__ (@var{player}, @var{data})
2826 Undocumented internal function.
2827 @end deftypefn */)
2828 {
2829 #if defined (HAVE_PORTAUDIO)
2830  if (args.length () == 2)
2831  get_player (interp, args(0))->set_userdata (args(1));
2832  return ovl ();
2833 #else
2834  octave_unused_parameter (interp);
2835  octave_unused_parameter (args);
2836 
2837  err_disabled_feature ("__player_set_userdata__",
2838  "audio playback and recording through PortAudio");
2839 #endif
2840 }
2841 
2842 DEFMETHOD_DLD (__player_stop__, interp, args, ,
2843  doc: /* -*- texinfo -*-
2844 @deftypefn {} {} __player_stop__ (@var{player})
2845 Undocumented internal function.
2846 @end deftypefn */)
2847 {
2848 #if defined (HAVE_PORTAUDIO)
2849  if (args.length () == 1)
2850  get_player (interp, args(0))->stop ();
2851  return ovl ();
2852 #else
2853  octave_unused_parameter (interp);
2854  octave_unused_parameter (args);
2855 
2856  err_disabled_feature ("__player_stop__",
2857  "audio playback and recording through PortAudio");
2858 #endif
2859 }
octave_idx_type columns(void) const
Definition: Array.h:424
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:499
const T * data(void) const
Size of the specified dimension.
Definition: Array.h:581
octave_idx_type rows(void) const
Definition: Array.h:415
Definition: Cell.h:43
Definition: dMatrix.h:42
Matrix transpose(void) const
Definition: dMatrix.h:135
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:151
ColumnVector column(octave_idx_type i) const
Definition: dMatrix.cc:422
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
void add_fcn(void(*fcn)(Params...), Args &&... args)
void mlock(bool skip_first=false) const
virtual bool is_constant(void) const
Definition: ov-base.h:459
virtual bool print_as_scalar(void) const
Definition: ov-base.h:647
virtual void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
Definition: ov-base.cc:409
virtual double scalar_value(bool frc_str_conv=false) const
Definition: ov-base.h:505
virtual void print(std::ostream &os, bool pr_as_read_syntax=false)
Definition: ov-base.cc:403
virtual bool is_defined(void) const
Definition: ov-base.h:355
void setfield(const std::string &key, const Cell &val)
Definition: oct-map.cc:282
void setfield(const std::string &key, const octave_value &val)
Definition: oct-map.cc:190
const octave_base_value & get_rep(void) const
Definition: ov.h:1292
bool is_int8_type(void) const
Definition: ov.h:659
bool is_int16_type(void) const
Definition: ov.h:662
bool is_uint8_type(void) const
Definition: ov.h:671
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:806
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin function.
Definition: defun-dld.h:61
#define DEFMETHOD_DLD(name, interp_name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin method.
Definition: defun-dld.h:99
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1065
void error(const char *fmt,...)
Definition: error.cc:968
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5814
QString name
bool words_big_endian(void)
Definition: mach-info.cc:72
static int left
Definition: randmtzig.cc:191
octave_value_list feval(const char *name, const octave_value_list &args, int nargout)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
Definition: oct-parse.cc:9580
size_t format(std::ostream &os, const char *fmt,...)
Definition: utils.cc:1329
octave_int< T > pow(const octave_int< T > &a, const octave_int< T > &b)
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
static int input(yyscan_t yyscanner)
#define DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
Definition: ov-base.h:158
#define DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(t, n, c)
Definition: ov-base.h:180
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211