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