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