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

Inconsistent order of channels on C-Media 7.1 channel device #402

Open
victorallume opened this issue Jul 18, 2024 · 6 comments
Open

Inconsistent order of channels on C-Media 7.1 channel device #402

victorallume opened this issue Jul 18, 2024 · 6 comments

Comments

@victorallume
Copy link

I have a USB sound device (Startech ICUSBAUDIO7D, which comes up as ID 0d8c:0102 C-Media Electronics, Inc. CM106 Like Sound Device through lsusb) which I'm trying to program the output of for 8 channels. The python code (outputs a 50Hz sine wave) I'm using is below (each channel gets a different amplitude so I can identify them with an oscilloscope).

The sound card has 4 stereo jacks, to which I have a scope connected.

The problem is that on different runs of the program, the output mappings are not consistent - each run of the program might have the order of the channels different to the previous. So running it one time might have amplitudes (0, 0.125) on the first jack, but the next run it might have those (0.75, 0.25) on the first jack (it doesn't appear to be 'paired' per jack)

I initially discovered this when using portaudio, and decided to try using alsa-only and the issue persists.

Is this likely to be a problem with the driver, alsa-lib, or is there something else going on here?

import numpy as np
import alsaaudio

def sine_wave(amplitude=1, freq=50, sample_rate=48000):
    wave_len = sample_rate/freq
    samples = (amplitude * 32767 * np.sin(2 * np.pi * np.arange(wave_len) * freq / sample_rate)).astype(np.int16)
    return samples

def eightchan(freq=50, sample_rate=48000):
    wave_len = sample_rate//freq
    frames = np.zeros((wave_len, 8), dtype=np.int16)
    samples = sine_wave()
    for c in range(8):
        frames[:wave_len,c] = samples[:wave_len]*(c/8)
    return frames.tobytes()

if __name__ == '__main__':
    pcm = alsaaudio.PCM(device='surround71:CARD=ICUSBAUDIO7D,DEV=0', format=alsaaudio.PCM_FORMAT_S16_LE, channels=8, rate=48000, type=alsaaudio.PCM_PLAYBACK, periodsize=960, periods=1)
    frames = eightchan()
    while True:
        pcm.write(frames)

alsa-info output is attached
alsa-info.txt

@victorallume
Copy link
Author

I've been able to re-create this using aplay and an 8-channel .wav file (I made the wav file in audacity by creating 8 tracks, putting a unique 30-second tone into each channel, then exporting, ensuring each track was mapped to a unique channel). Stopping and re-starting the playback shows different channel pairs appearing on my oscilloscope

@victorallume
Copy link
Author

Further to the previous comment with the wav file - it shows the inconsistent mapping when I Ctrl-C the playback and re-start it. However, when I let the file play out, and start it again, the mapping seems to be the same every time (I've repeated this about 10 times and it's worked consistently every time). Similarly, in the original python program, if I call pcm.close() prior to terminating the program, then next time it seems to be fine (again, I repeated this about 10 times).

So I'm thinking that the driver or the device is left in an inconsistent state when the stream is interrupted. Closing the PCM resets this state, but I'm surprised this doesn't happen when the PCM is initialised (either via aplay or the python library).

@victorallume
Copy link
Author

As an attempt for a workaround, I tried the following:

  1. Open the PCM
  2. (optionally) write some frames to the PCM, (I tried 1, 2 and 4)
  3. Cleanly close the PCM object
  4. Wait some time (I tried 0, 1 and 5 seconds)
  5. (optionally) delete the PCM object
  6. Re-create the PCM object
  7. Start streaming frames to the PCM object
    (code is below)

None of this worked, so it seems there's some other initialisation or de-initialisation happening when the process is started

    pcm = alsaaudio.PCM(device='surround71:CARD=ICUSBAUDIO7D,DEV=0', format=alsaaudio.PCM_FORMAT_S16_LE, channels=8, rate=48000, type=alsaaudio.PCM_PLAYBACK, periodsize=960, periods=1)
    for i in range(4):
        pcm.write(frames)
    pcm.close()
    time.sleep(5)
    del pcm
    pcm = alsaaudio.PCM(device='surround71:CARD=ICUSBAUDIO7D,DEV=0', format=alsaaudio.PCM_FORMAT_S16_LE, channels=8, rate=48000, type=alsaaudio.PCM_PLAYBACK, periodsize=960, periods=1)

@victorallume
Copy link
Author

I've also tried alsactl init, alsa force-reload and usbreset 0d8c:0102 (and combinations of both of these) and they don't reliably get the card back from an inconsistent state. Rebooting seems to be the only thing that reliably works.

@victorallume
Copy link
Author

Actually, rebooting doesn't work reliably - the channel mapping is occassionally different after a reboot. If I close the pcm every time, it seems to reliably open with the same mapping the next run, but the initial run is not always consistent

@veldenb
Copy link

veldenb commented Oct 13, 2024

I think I experience exactly this problem when using HDMI multichannel (5.1) output using a NVidia GPU and a surround set-up.
When I run speaker-test -c 6 -D hdmi:CARD=NVidia,DEV=1 on Ubuntu 24.10, the speaker mapping changes every time I run the command. I tried to remap the channels in pipewire/wireplumber using these instructions, but it fails because the mapping is constantly changing on the ALSA (or driver) layer.

I think the mapping isn't totally random, because after a few tries (about 3 or 4) I do get the correct mapping. But, it doesn't persist. So my guess is that there are a few standard surround configurations that are rotated somehow.

edit: If I can help by providing extra info feel free to ask.

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

2 participants