Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio on organelle #6

Open
ssssam opened this issue Nov 15, 2021 · 11 comments
Open

Audio on organelle #6

ssssam opened this issue Nov 15, 2021 · 11 comments

Comments

@ssssam
Copy link
Owner

ssssam commented Nov 15, 2021

Currently audio on Organelle using 'cpal' does not work.

The issue seems to be that audio_host.input_devices() returns an empty list; even though arecord -L does not.

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

The alsa-rs package has synth-example, this does not work on device on Organelle OS 4.0:

# /sdcard/synth-example 0 44100 3000
Opened audio output "hw:0" with parameters: HwParams { channels: Ok(2), rate: "Ok(44100) Hz", format: Ok(S16LE), access: Ok(MMapInterleaved), period_size: "Ok(750) frames", buffer_size: "Ok(3000) frames" }, SwParams(avail_min: Ok(750) frames, start_threshold: Ok(2250) frames, stop_threshold: Ok(3000) frames)
Starting audio output stream
Recovering from ALSA function 'snd_pcm_avail_update' failed with error 'EPIPE: Broken pipe'
Starting audio output stream
Recovering from ALSA function 'snd_pcm_avail_update' failed with error 'EPIPE: Broken pipe'

The errors repeat and no audio seems to come out.

Error comes from snd_pcm_avail_update. The synth-example calls snd_pcm_recover but the problem repeats each time.

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

I am trying to find an example that actually works.

  1. soundprogramming.net.

This works:

Audio device opened successfully.
Audio device parameters have been set successfully.
Init: Buffer size = 16384 frames.
Init: Significant bits for linear samples = 16
Audio device has been prepared for use.
Audio device has been uninitialized.

This calls: snd_pcm_open(), snd_pcm_hw_params_malloc(), snd_pcm_hw_params_any(), snd_pcm_hw_params_set_rate_resample(), snd_pcm_hw_params_set_access(), snd_pcm_hw_params_set_format(), snd_pcm_hw_params_set_channels(), snd_pcm_hw_params_set_rate_near(), snd_pcm_hw_params(), snd_pcm_hw_params_get_buffer_size(), snd_pcm_hw_params_get_sbits(), snd_pcm_prepare().

It doesn't try to play or capture any audio.

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

  1. saunalahti.fi - from 2005 (linked from StackOverflow)

This required some fixing up to work. Source attached: t2.c.txt

I have got to a point where it mostly runs and should generate noise, but no sound comes out. (aplay /dev/random does produce noise).

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

  1. miniFMsynth.c from http://alsamodular.sourceforge.net/alsa_programming_howto.html#sect06

I hacked this to play all the notes at start instead of waiting for the sequencer. It produced a sound!!

This one does not use snd_pcm_start() at all, nor snd_pcm_avail_update().

This suggests the problem is in the mmap emulation API that the synth-example uses:

        hwp.set_access(pcm::Access::MMapInterleaved)?;

The cpal library does NOT use this access method so I won't debug the synth-example further.

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

cpal enumerate example works and lists devices correctly:

  Devices: 
  1. "default:CARD=Codec"
    Default input stream config:
      SupportedStreamConfig { channels: 2, sample_rate: SampleRate(44100), buffer_size: Range { min: 2, max: 4294967294 }, sample_format: F32 }
...
    Default output stream config:
      SupportedStreamConfig { channels: 2, sample_rate: SampleRate(44100), buffer_size: Range { min: 2, max: 4294967294 }, sample_format: F32 }

The feedback example can't find these devices though:

/sdcard/feedback  default:CARD=Codec default:CARD=Codec
thread 'main' panicked at 'failed to find output device', examples/feedback.rs:119:6

Seems a bug in how cpal populates host.output_devices() and host.input_devices()

Interestingly:

  • boucle fails to find the input device (it calls output_devices() then input_devices())
  • feedback fails to find the output device (it calls input_devices() then output_devices())

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

I have added some tracing to alsa-rs/src/device_name.rs and cpal/src/traits.rs:

# /sdcard/feedback  default:CARD=Codec default:CARD=Codec

This is when input_devices() is called...

HintIter::new(): card None, iface: "pcm"
Hint::new: Hint { name: Some("null"), desc: Some("Discard all samples (playback) or generate zero samples (capture)"), direction: None }
Hint::new: Hint { name: Some("default:CARD=Codec"), desc: Some("On-board Codec, \nDefault Audio Device"), direction: None }
HostTrait::input_devices::supports_input(): device: "default:CARD=Codec", result: true

Iterator finishes as we match the name of the card we wanted.

This is when output_devices() is called...

HintIter::new(): card None, iface: "pcm"
Hint::new: Hint { name: Some("null"), desc: Some("Discard all samples (playback) or generate zero samples (capture)"), direction: None }
Hint::new: Hint { name: Some("default:CARD=Codec"), desc: Some("On-board Codec, \nDefault Audio Device"), direction: None }
Hint::new: Hint { name: Some("sysdefault:CARD=Codec"), desc: Some("On-board Codec, \nDefault Audio Device"), direction: None }

Interesting thing: ^^ why is direction: None here??!!

These don't get returned, so we continue iterating...

Hint::new: Hint { name: Some("default:CARD=imxspdif"), desc: Some("imx-spdif, \nDefault Audio Device"), direction: Some(Playback) }
HostTrait::output_devices::supports_output(): device: "default:CARD=imxspdif", result: true
Hint::new: Hint { name: Some("sysdefault:CARD=imxspdif"), desc: Some("imx-spdif, \nDefault Audio Device"), direction: Some(Playback) }
HostTrait::output_devices::supports_output(): device: "sysdefault:CARD=imxspdif", result: true
Hint::new: Hint { name: Some("default:CARD=imxhdmisoc"), desc: Some("imx-hdmi-soc, \nDefault Audio Device"), direction: Some(Playback) }
Hint::new: Hint { name: Some("sysdefault:CARD=imxhdmisoc"), desc: Some("imx-hdmi-soc, \nDefault Audio Device"), direction: Some(Playback) }
thread 'main' panicked at 'failed to find output device', examples/feedback.rs:119:6
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

And we don't find the match.

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

Here's a doc from alsa-lib src/control/namehint.c @ v1.0.29:

/**
 * \brief Extract a value from a hint
 * \param hint A pointer to hint
 * \param id Hint value to extract ("NAME", "DESC", or "IOID", see below)
 * \result an allocated ASCII string if success, otherwise NULL
 *
 * List of valid IDs:
 * NAME - name of device
 * DESC - description of device
 * IOID - input / output identification ("Input" or "Output"), NULL means both
 *
 * The return value should be freed when no longer needed.
 */

NULL means both. alsa::device_name::Hint::new sets direction: None in this case, which I suppose is correct. This might not be the bug after all.

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

In cpal::alsa::enumerate::Devices, the code processes the Hint by trying to open the device: DeviceHandles::open. It gets an error EBUSY when output_device() runs. So it doesn't return the default device.

Feedback.rs example hasn't opened the device, so perhaps cpal isn't closing it properly? The same issue occurs on my desktop:

cargo run --example feedback sysdefault:CARD=sofhdadsp sysdefault:CARD=sofhdadsp

@ssssam
Copy link
Owner Author

ssssam commented Nov 15, 2021

RustAudio/cpal#615

@ssssam
Copy link
Owner Author

ssssam commented Nov 16, 2021

I gave up on ALSA completely. Implementing my own pipline using alsa-rs directly is too time consuming. Working around the cpal design flaw isn't possible and fixing it is also too time consuming.

One last attempt to get this to work: use JACK.

This produces sound:

jackd -d alsa &
jack_metro --bpm 200 &
jack_connect  metro:200_bpm  system:playback_1
jack_connect  metro:200_bpm  system:playback_2

This produce an error:

# /sdcard/feedback  --jack
Using input device: "cpal_client_in"
Attempting to build both streams with f32 samples and `StreamConfig { channels: 2, sample_rate: SampleRate(48000), buffer_size: Default }`.
Error: The requested stream configuration is not supported by the device.

CPAL enumerate example suggests it should work:

   Default input stream config:
      SupportedStreamConfig { channels: 2, sample_rate: SampleRate(48000), buffer_size: Range { min: 1024, max: 1024 }, sample_format: F32 }

@ssssam
Copy link
Owner Author

ssssam commented Nov 16, 2021

Bypassing cpal and using rust-jack works: using playback_capture example:

jackd -d alsa &
jack_connect  rust_jack_simple:rust_in_l  system:capture_1
jack_connect  rust_jack_simple:rust_in_r system:capture_2
jack_connect  rust_jack_simple:rust_out_l system:playback_1
jack_connect  rust_jack_simple:rust_out_r system:playback_2
/sdcard/playback_capture

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant