djwong: (Default)
[personal profile] djwong
One of my basement computers has one of these Intel High-Definition Audio sound cards. There are 6 analog mini jacks on the back, 5 of which are outputs and 1 which is an input. The outputs are: stereo line out, stereo rear out, stereo side out, center, and subwoofer (LFE). Given that I have two ultra-quiet speaker zones in my house (rooms where I want to be able to listen to computer music without the sound pollution of a computer), wouldn't it be neat to route two *different* sound sources to the two stereophonic outputs that are connected to the two zones?


My living room and my master bedroom each have a set of stereo speakers. I've snaked audio cables throughout the house so that both sets can be driven by a computer that lives in the basement. The living room is connected to stereo line out, and the bedroom is connected to the stereo rear out. It is my goal for this computer to have Music Player Daemon (mpd) installed, so I can control the sound outputs remotely (yes, Android/iOS controllers exist).

Hardware Support


The first step is to figure out what hardware configurations the hardware supports. The board has a RealTek ALC889A sound card, which the Linux snd-hda-intel driver can talk to. We can use ALSA utilities for this:
# aplay -L
null
    Discard all samples (playback) or generate zero samples (capture)
default:CARD=Intel
    HDA Intel, ALC889A Analog
    Default Audio Device
front:CARD=Intel,DEV=0
    HDA Intel, ALC889A Analog
    Front speakers
surround40:CARD=Intel,DEV=0
    HDA Intel, ALC889A Analog
    4.0 Surround output to Front and Rear speakers
surround41:CARD=Intel,DEV=0
    HDA Intel, ALC889A Analog
    4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Intel,DEV=0
    HDA Intel, ALC889A Analog
    5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Intel,DEV=0
    HDA Intel, ALC889A Analog
    5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=Intel,DEV=0
    HDA Intel, ALC889A Analog
    7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
iec958:CARD=Intel,DEV=0
    HDA Intel, ALC889A Digital
    IEC958 (S/PDIF) Digital Audio Output

Okay, so this looks like a fairly boring piece of audio hardware. By default it'll drive just stereo line out, but the card can also handle various surround modes ("surround40" ... "surround71"). The "iec958" profile is for the digital audio outputs on the board, which I do not use. Unfortunately, these profiles are not quite what we want, because the software designers assumed that there is one (possibly surround) sound stream (referred to as a "sound input" from ALSA's perspective) coming in from the software mixer, and all channels of this stream should be mapped to one of the output channels. What we want to build is a system with multiple sound "inputs", and we want each "input" to be mapped to a specific output channel without intermixing. For two stereo zones, the 4.0 surround profile is useful, because it has four output channels (front left, front right, rear left, rear right). We can send one "input" (stream) to the front pair and a second "input" stream to the rear pair.

To test the 4.0 surround, plug stereo headphones (or speakers, or whatever) into the line out (green) and rear out (black) ports on the machine. Then run this:

# speaker-test -c4 -l1 -twav -D surround40

speaker-test 1.0.22

Playback device is surround40
Stream parameters are 48000Hz, S16_LE, 4 channels
WAV file(s)
Home directory /storage/home/djwong not ours.
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 32 to 262144
Period size range from 16 to 131072
Using max buffer size 262144
Periods = 4
was set period_size = 65536
was set buffer_size = 262144
0 - Front Left
1 - Front Right
3 - Rear Right
2 - Rear Left
Time per period = 9.568956

This program will play a test message through all four channels, so you can verify that this profile indeed has four separate channels.

Software Support


Okay, so it's cool that all four channels work. But, how do we convince programs to play to the "correct" set of speakers? We could configure each program to send its sound to the correct channel (front or rear)... but that's tricky. Do you want to have to communicate to all programs that they should send a stereo signal only to the front? Or only to the rear? That's annoying. Instead, let's use a software mapper (PulseAudio) to create two virtual stereo sound devices (PA sinks) which will be mapped to the corresponding audio output channels (on the -surround40 ALSA input). Then we just tell the program which PA sink it should play to.

The PulseAudio sound system can set up this virtualization. First, install PulseAudio and then ask it for the list of default sound sinks:
# pacmd list-sinks
Welcome to PulseAudio! Use "help" for usage information.
>>> 1 sink(s) available.
  * index: 0
	name: <alsa_output.pci-0000_00_1b.0.analog-stereo>
	driver: <module-alsa-card.c>
	flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
	state: RUNNING
	suspend cause: 
	priority: 9959
<snip>

Notice how the name says "-stereo", not "-surround40" or something like it? PA has picked the default ALSA profille, and as aplay -L just told us, the default ALSA profile for this card is stereo out, not 4.0 surround. Let's change that by adding stuff to /etc/pulse/daemon.conf:

default-sample-channels = 4
log-level = error

Now kill all the pulseaudio daemons and re-query the PA sinks:
>>> 1 sink(s) available.
  * index: 0
	name: <alsa_output.pci-0000_00_1b.0.analog-surround-40>
	driver: <module-alsa-card.c>
	flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
	state: RUNNING
	suspend cause: 
	priority: 9959
	volume: 0:  43% 1:  43% 2:  43% 3:  43%
	        0: -22.00 dB 1: -22.00 dB 2: -22.00 dB 3: -22.00 dB
	        balance 0.00
	base volume: 100%
	             0.00 dB
	volume steps: 65537
	muted: no

Remember that name field, we're going to need it to set up the virtual sound sinks. The sound sink configuration goes in /etc/pulse/default.pa:

load-module module-remap-sink sink_name=downstairs remix=no master=alsa_output.pci-0000_00_1b.0.analog-surround-40 channels=2 master_channel_map=front-left,front-right channel_map=front-left,front-right
load-module module-remap-sink sink_name=upstairs remix=no master=alsa_output.pci-0000_00_1b.0.analog-surround-40 channels=2 master_channel_map=rear-left,rear-right channel_map=front-left,front-right

What does this do? The first line loads a module named "module-remap-sink". This module is like device-mapper for sound devices. It creates a new sink named "downstairs". The "remix=no" clause tells PA not to remix the inputs to all output channels. The "master=" clause links the new sink to a lower-level sink, which in this case is a piece of hardware named "alsa_output.pci-0000_00_1b.0.analog-surround-40". The name should be familiar. Moving along, "channels=2" identifies the new sink as being a stereo output. "channel_map" is the list of channels that this sink accepts; the choice of "front-left,front-right" means that the sink will appear to be a basic stereo line output sink. Finally, the "master_channel_map" clause maps (for "downstairs") the sink's front left and right channels to the hardware's front and right channels. Notice that in the "upstairs" sink configuration, the sink's front left and right channels are mapped to the hardware's REAR left and right channels. Hence, stereo sounds sent to "upstairs" line out will be remapped to the rear output jacks on the computer, which is exactly what we want. You can run speaker-test -Dupstairs to verify this.

Here's what PA lists for sinks now:
# pacmd list-sinks
Welcome to PulseAudio! Use "help" for usage information.
>>> 3 sink(s) available.
  * index: 0
	name: <alsa_output.pci-0000_00_1b.0.analog-surround-40>
	driver: <module-alsa-card.c>
	flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
	state: RUNNING
	suspend cause: 
	priority: 9959
	volume: 0:  43% 1:  43% 2:  43% 3:  43%
	        0: -22.00 dB 1: -22.00 dB 2: -22.00 dB 3: -22.00 dB
	        balance 0.00
	base volume: 100%
	             0.00 dB
	volume steps: 65537
	muted: no
	current latency: 66.94 ms
	max request: 36 KiB
	max rewind: 1378 KiB
	monitor source: 0
	sample spec: s16le 4ch 44100Hz
	channel map: front-left,front-right,rear-left,rear-right
	             Surround 4.0
	used by: 1
	linked by: 2
	configured latency: 105.00 ms; range is 0.50 .. 4000.36 ms
	card: 0 <alsa_card.pci-0000_00_1b.0>
	module: 4
	properties:
		alsa.resolution_bits = "16"
		device.api = "alsa"
		device.class = "sound"
		alsa.class = "generic"
		alsa.subclass = "generic-mix"
		alsa.name = "ALC889A Analog"
		alsa.id = "ALC889A Analog"
		alsa.subdevice = "0"
		alsa.subdevice_name = "subdevice #0"
		alsa.device = "0"
		alsa.card = "0"
		alsa.card_name = "HDA Intel"
		alsa.long_card_name = "HDA Intel at 0xfc000000 irq 44"
		alsa.driver_name = "snd_hda_intel"
		device.bus_path = "pci-0000:00:1b.0"
		sysfs.path = "/devices/pci0000:00/0000:00:1b.0/sound/card0"
		device.bus = "pci"
		device.vendor.id = "8086"
		device.vendor.name = "Intel Corporation"
		device.product.id = "3a3e"
		device.product.name = "82801JI (ICH10 Family) HD Audio Controller"
		device.form_factor = "internal"
		device.string = "surround40:0"
		device.buffering.buffer_size = "1411328"
		device.buffering.fragment_size = "705664"
		device.access_mode = "mmap+timer"
		device.profile.name = "analog-surround-40"
		device.profile.description = "Analog Surround 4.0"
		device.description = "Internal Audio Analog Surround 4.0"
		alsa.mixer_name = "Realtek ALC889A"
		alsa.components = "HDA:10ec0885,1458a102,00100101"
		module-udev-detect.discovered = "1"
		device.icon_name = "audio-card-pci"
    index: 1
	name: <downstairs>
	driver: <module-remap-sink.c>
	flags: DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
	state: SUSPENDED
	suspend cause: IDLE 
	priority: 1000
	volume: 0: 100% 1: 100%
	        0: 0.00 dB 1: 0.00 dB
	        balance 0.00
	base volume: 100%
	             0.00 dB
	volume steps: 65537
	muted: no
	current latency: 0.00 ms
	max request: 18 KiB
	max rewind: 689 KiB
	monitor source: 2
	sample spec: s16le 2ch 44100Hz
	channel map: front-left,front-right
	             Stereo
	used by: 0
	linked by: 0
	configured latency: 0.00 ms; range is 0.50 .. 4000.36 ms
	module: 14
	properties:
		device.master_device = "alsa_output.pci-0000_00_1b.0.analog-surround-40"
		device.class = "filter"
		device.description = "Remapped Internal Audio Analog Surround 4.0"
		device.icon_name = "audio-card"
    index: 2
	name: <upstairs>
	driver: <module-remap-sink.c>
	flags: DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
	state: RUNNING
	suspend cause: 
	priority: 1000
	volume: 0: 100% 1: 100%
	        0: 0.00 dB 1: 0.00 dB
	        balance 0.00
	base volume: 100%
	             0.00 dB
	volume steps: 65537
	muted: no
	current latency: 66.65 ms
	max request: 18 KiB
	max rewind: 689 KiB
	monitor source: 3
	sample spec: s16le 2ch 44100Hz
	channel map: front-left,front-right
	             Stereo
	used by: 1
	linked by: 1
	configured latency: 105.00 ms; range is 0.50 .. 4000.36 ms
	module: 15
	properties:
		device.master_device = "alsa_output.pci-0000_00_1b.0.analog-surround-40"
		device.class = "filter"
		device.description = "Remapped Internal Audio Analog Surround 4.0"
		device.icon_name = "audio-card"

Looks good right now...

MPD


Put this into the MPD configuration file:
audio_output {
        type            "pulse"
        name            "pulseaudio confusion"
        sink            "upstairs"
}

This tells this MPD to send all sound to the "upstairs" PulseAudio sink that we created above. You probably want to create a second MPD config file (and run a second instance) to talk to the "downstairs" sink. Then you can use the MPD clients to control upstairs and downstairs, and to play different streams on each. Problem solved!

Profile

djwong: (Default)
Bogus J. Simpson

May 2016

S M T W T F S
1234567
891011121314
15161718192021
2223242526 2728
293031    

Style Credit

Expand Cut Tags

No cut tags
Page generated 21 May 2025 05:30
Powered by Dreamwidth Studios