Crashes when using audiorecorder with multiple channels

Hi
I’m having trouble trying to record 3 channels of audio.

If I run the below code with 1 channel (the thirds arguments of audiorecorder) everything is fine,
If I do it with 2, Octave ramps the fans up and stops responding
If I do it with 3, Octave crashes completely

Stepping through it, it’s the second line where these effects take place

AudioIn = audiorecorder(48000,24,3,1);
recordblocking(AudioIn,3);
signal = abs(getaudiodata(AudioIn));

If I change it to what’s below then it’s happy in the 2 channel case but still crashes on 3

AudioIn = audiorecorder(48000,24,2,1);
record(AudioIn,3);
while (isrecording(AudioIn)==true)
  pause(0.1)
endwhile
signal = abs(getaudiodata(AudioIn));

It’s a class compliant audio interface and other software is happy to see as many channels as it wants. Any ideas would be greatly appreciated

My system

  • macOS Monterey 12.2.1
  • Octave 7.2.0 (from homebrew)
  • Audio Device - Focusrite Scarlett 18i8 3rd gen

Can you try 3 channels but reduce the bitrate and number of bits per sample? Maybe 8000 Hz and 8-bit for 3 channels. If that works, but it crashes for 48000 Hz at 24-bit, we can start localizing why.

This is what I see on linux (octave 7.3):

octave:1> AudioIn = audiorecorder(48000,24,1,1);
ALSA lib pcm_dsnoop.c:566:(snd_pcm_dsnoop_open) unable to open slave
ALSA lib pcm_dmix.c:999:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_dmix.c:999:(snd_pcm_dmix_open) unable to open slave
octave:2> AudioIn = audiorecorder(48000,24,1,1);
octave:3> AudioIn = audiorecorder(48000,24,2,1);
octave:4> AudioIn = audiorecorder(48000,24,2,1);
octave:5> AudioIn = audiorecorder(48000,24,3,1);
octave-gui: libinterp/dldfcn/audiodevinfo.cc:1658: void octave::audiorecorder::set_channels(int): Assertion `channels_arg == 1 || channels_arg == 2' failed.
fatal: caught signal Aborted -- stopping myself...
Aborted (core dumped)

This is my advice as well. The @audiorecorder invocation is calling for 48,000 samples / second * 3 bytes / sample * 3 channels = 432,000 bytes / sec. It may be that this is too high a data transfer rate for one of the internal buses in your computer. Alternatively, it could be something with the use of blocking versus non-blocking I/O. At these processing rates you may need to run the Octave process with real-time priority or at least use nice to get it to be serviced more frequently by the OS scheduler.

A hard assertion for not going beyond 2 channels? Should that be changed to error_unless?

EDIT: Loading that file, it already says error not assert:

  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	}

Where is that assertion coming from?

(EDIT from @rik: It comes from version 7.2.0 which the original reporter is using. The development branch correctly does more input validation. So that is the first point to make, using more than 2 channels in Octave should never work.)

(I’m not able to replicate the original error or crash because the Linux desktop I use to develop for Octave does not have any audio input devices).

It should not crash in any case, but fail more gracefully.

Yes, this is, of course, true. Good code doesn’t crash. Still, I think the first steps suggested by Arun are the right way to begin debugging this.

On Octave 8 I see:

octave:1> AudioIn = audiorecorder(48000,24,1,1);
ALSA lib pcm_dsnoop.c:566:(snd_pcm_dsnoop_open) unable to open slave
ALSA lib pcm_dmix.c:999:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_dmix.c:999:(snd_pcm_dmix_open) unable to open slave
octave:2> AudioIn = audiorecorder(48000,24,1,1);
octave:3> AudioIn = audiorecorder(48000,24,2,1);
octave:4> AudioIn = audiorecorder(48000,24,3,1);
error: audiorecorder: number of channels must be 1 or 2
error: called from
    audiorecorder at line 89 column 21

p.s. Still do not understand why first call fails, but the second succeeds

This was the change from assert to error:

changeset:   30575:ff6e74a8f7ba
user:        Rik <rik@octave.org>
date:        Fri Dec 31 07:31:53 2021 -0800
files:       libinterp/dldfcn/audiodevinfo.cc
description:
Replace assert() with error() to avoid aborts in audiorecorder.

* audiodevinfo.cc (~audioplayer, ~audiorecorder): Reword warning_with_id()
message about stopping stream for clarity.
* audiodevinfo.cc (audiorecorder::set_channels): Replace C++ assert() with call
to Octave error() function.
* audiodevinfo.cc (F__recorder_audiorecorder__, F__player_audioplayer__):
Reword error message about callback functions for clarity.

So it will be in Octave 8 but not Octave 7.

But this raises a bigger question: why can’t we go beyond two channels like @Emma is trying to do?

Octave uses PortAudio library internally. I don’t know what the restrictions are for this library.

Currently PortAudio can handle all input channels, but perhaps it was only 2 back in the day when octave function was originally written.

@dasergatskov : Since you can replicate the error and I cannot, could you remove the two lines from audiodevinfo.cc and see if it works for 3 channels?

  1659	  if (channels_arg != 1 && channels_arg != 2)
  1660	    error ("audiorecorder: number of channels must be 1 or 2");

Here is what I have hardware-wise:

$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 1: Generic [HD-Audio Generic], device 0: ALC887-VD Analog [ALC887-VD Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Generic [HD-Audio Generic], device 2: ALC887-VD Alt Analog [ALC887-VD Alt Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

(USB Audio is an USB headset w/ mic; HD-Audio Generic is
Audio device: Advanced Micro Devices, Inc. [AMD] Starship/Matisse HD Audio Controller)
So audiorecorder(48000,24,3,1) would not be a legal call; technically I have only 2 ch per
device)

I am not sure about working, but if I disable this check I would not get an error:

octave:15> AudioIn = audiorecorder(48000,24,1,10)
AudioIn =

audiorecorder object with properties:

  BitsPerSample = 24
  CurrentSample = 0
  DeviceID = 10
  NumberOfChannels = 1
  Running = off
  SampleRate = 48000
  TotalSamples = 0
  Tag = 
  Type = audiorecorder
  UserData = [](0x0)

octave:16> AudioIn = audiorecorder(48000,24,1,100)
warning: invalid default audio device ID = 100
warning: called from
    audiorecorder at line 89 column 21

AudioIn =

audiorecorder object with properties:

  BitsPerSample = 24
  CurrentSample = 0
  DeviceID = 100
  NumberOfChannels = 1
  Running = off
  SampleRate = 48000
  TotalSamples = 0
  Tag = 
  Type = audiorecorder
  UserData = [](0x0)

octave:17> AudioIn = audiorecorder(48000,24,3,1)
AudioIn =

audiorecorder object with properties:

  BitsPerSample = 24
  CurrentSample = 0
  DeviceID = 1
  NumberOfChannels = 3
  Running = off
  SampleRate = 48000
  TotalSamples = 0
  Tag = 
  Type = audiorecorder
  UserData = [](0x0)

Apparently it thinks I have 18 audiodevices:

AudioIn = audiorecorder(48000,24,3,18)
warning: invalid default audio device ID = 18
warning: called from
    audiorecorder at line 89 column 21
...
AudioIn = audiorecorder(48000,24,3,17)
AudioIn =

audiorecorder object with properties:

  BitsPerSample = 24
  CurrentSample = 0
  DeviceID = 17
  NumberOfChannels = 3
  Running = off
  SampleRate = 48000
  TotalSamples = 0
  Tag = 
  Type = audiorecorder
  UserData = [](0x0)

Doesn’t look like there is an intrinsic limitation but the whole c++ code behind audiorecorder is written with the concept of left and right channels so testing more channels looks like much more involved than just removing the channel number check…

1 Like

Tried this (although it looks like you’ve already got past that). It fixes the lock up when using recordblocking with 2 channels but the 3 channel crash still remains.

Thanks everyone for tracking this down. I’ll find a workaround for now.
I’ve never got into the Octave source code before but I might try and fix it when I have the time (unless someone else wants to have a go at it)