Skip to content

Commit 2ba56be

Browse files
epilysstsquad
authored andcommitted
sound/alsa: fix host hwparams calculation
The host's period and buffer sizes were not being calculated correctly. This resulted in timing mismatches. This commit calculates the correct sizes by taking the guest's parameters into account. Impossible cases when matching with stream parameters will lead to unreachable!(). Signed-off-by: Manos Pitsidianakis <[email protected]>
1 parent 84c5840 commit 2ba56be

File tree

1 file changed

+89
-27
lines changed
  • staging/vhost-device-sound/src/audio_backends

1 file changed

+89
-27
lines changed

staging/vhost-device-sound/src/audio_backends/alsa.rs

Lines changed: 89 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -60,34 +60,37 @@ fn update_pcm(
6060
match s.direction {
6161
d if d == VIRTIO_SND_D_OUTPUT => Direction::Playback,
6262
d if d == VIRTIO_SND_D_INPUT => Direction::Capture,
63-
other => panic!("Invalid virtio-sound stream: {}", other),
63+
// We initialize the stream direction ourselves in device.rs, so it should never
64+
// have another value.
65+
_ => unreachable!(),
6466
},
6567
false,
6668
)?;
6769

6870
{
71+
let rate = match s.params.rate {
72+
virtio_sound::VIRTIO_SND_PCM_RATE_5512 => 5512,
73+
virtio_sound::VIRTIO_SND_PCM_RATE_8000 => 8000,
74+
virtio_sound::VIRTIO_SND_PCM_RATE_11025 => 11025,
75+
virtio_sound::VIRTIO_SND_PCM_RATE_16000 => 16000,
76+
virtio_sound::VIRTIO_SND_PCM_RATE_22050 => 22050,
77+
virtio_sound::VIRTIO_SND_PCM_RATE_32000 => 32000,
78+
virtio_sound::VIRTIO_SND_PCM_RATE_44100 => 44100,
79+
virtio_sound::VIRTIO_SND_PCM_RATE_48000 => 48000,
80+
virtio_sound::VIRTIO_SND_PCM_RATE_64000 => 64000,
81+
virtio_sound::VIRTIO_SND_PCM_RATE_88200 => 88200,
82+
virtio_sound::VIRTIO_SND_PCM_RATE_96000 => 96000,
83+
virtio_sound::VIRTIO_SND_PCM_RATE_176400 => 176400,
84+
virtio_sound::VIRTIO_SND_PCM_RATE_192000 => 192000,
85+
virtio_sound::VIRTIO_SND_PCM_RATE_384000 => 384000,
86+
// We check if a rate value is supported in PCM_SET_PARAMS so it should never have
87+
// an unknown value.
88+
_ => unreachable!(),
89+
};
6990
let hwp = HwParams::any(&pcm)?;
7091
hwp.set_channels(s.params.channels.into())?;
71-
hwp.set_rate(
72-
match s.params.rate {
73-
virtio_sound::VIRTIO_SND_PCM_RATE_5512 => 5512,
74-
virtio_sound::VIRTIO_SND_PCM_RATE_8000 => 8000,
75-
virtio_sound::VIRTIO_SND_PCM_RATE_11025 => 11025,
76-
virtio_sound::VIRTIO_SND_PCM_RATE_16000 => 16000,
77-
virtio_sound::VIRTIO_SND_PCM_RATE_22050 => 22050,
78-
virtio_sound::VIRTIO_SND_PCM_RATE_32000 => 32000,
79-
virtio_sound::VIRTIO_SND_PCM_RATE_44100 => 44100,
80-
virtio_sound::VIRTIO_SND_PCM_RATE_48000 => 48000,
81-
virtio_sound::VIRTIO_SND_PCM_RATE_64000 => 64000,
82-
virtio_sound::VIRTIO_SND_PCM_RATE_88200 => 88200,
83-
virtio_sound::VIRTIO_SND_PCM_RATE_96000 => 96000,
84-
virtio_sound::VIRTIO_SND_PCM_RATE_176400 => 176400,
85-
virtio_sound::VIRTIO_SND_PCM_RATE_192000 => 192000,
86-
virtio_sound::VIRTIO_SND_PCM_RATE_384000 => 384000,
87-
_ => 44100,
88-
},
89-
ValueOr::Nearest,
90-
)?;
92+
hwp.set_rate(rate, ValueOr::Nearest)?;
93+
hwp.set_rate_resample(false)?;
9194
hwp.set_format(match s.params.format {
9295
virtio_sound::VIRTIO_SND_PCM_FMT_IMA_ADPCM => Format::ImaAdPCM,
9396
virtio_sound::VIRTIO_SND_PCM_FMT_MU_LAW => Format::MuLaw,
@@ -114,17 +117,76 @@ fn update_pcm(
114117
virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U16 => Format::DSDU16LE,
115118
virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U32 => Format::DSDU32LE,
116119
virtio_sound::VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME => Format::iec958_subframe(),
117-
_ => Format::Unknown,
120+
// We check if a format value is supported in PCM_SET_PARAMS so it should never have
121+
// an unknown value.
122+
_ => unreachable!(),
118123
})?;
119124

120125
hwp.set_access(Access::RWInterleaved)?;
121126

122-
// > A period is the number of frames in between each hardware interrupt.
123-
// - https://www.alsa-project.org/wiki/FramesPeriods
127+
let frame_size = u32::from(s.params.channels)
128+
* match s.params.format {
129+
virtio_sound::VIRTIO_SND_PCM_FMT_A_LAW
130+
| virtio_sound::VIRTIO_SND_PCM_FMT_S8
131+
| virtio_sound::VIRTIO_SND_PCM_FMT_U8
132+
| virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U8
133+
| virtio_sound::VIRTIO_SND_PCM_FMT_MU_LAW => 1,
134+
virtio_sound::VIRTIO_SND_PCM_FMT_S16
135+
| virtio_sound::VIRTIO_SND_PCM_FMT_U16
136+
| virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U16
137+
| virtio_sound::VIRTIO_SND_PCM_FMT_IMA_ADPCM => 2,
138+
virtio_sound::VIRTIO_SND_PCM_FMT_S18_3
139+
| virtio_sound::VIRTIO_SND_PCM_FMT_U18_3
140+
| virtio_sound::VIRTIO_SND_PCM_FMT_S20_3
141+
| virtio_sound::VIRTIO_SND_PCM_FMT_U20_3
142+
| virtio_sound::VIRTIO_SND_PCM_FMT_S24_3
143+
| virtio_sound::VIRTIO_SND_PCM_FMT_U24_3
144+
| virtio_sound::VIRTIO_SND_PCM_FMT_S24
145+
| virtio_sound::VIRTIO_SND_PCM_FMT_U24 => 3,
146+
virtio_sound::VIRTIO_SND_PCM_FMT_S20
147+
| virtio_sound::VIRTIO_SND_PCM_FMT_U20
148+
| virtio_sound::VIRTIO_SND_PCM_FMT_S32
149+
| virtio_sound::VIRTIO_SND_PCM_FMT_U32
150+
| virtio_sound::VIRTIO_SND_PCM_FMT_FLOAT
151+
| virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U32
152+
| virtio_sound::VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME => 4,
153+
virtio_sound::VIRTIO_SND_PCM_FMT_FLOAT64 => 8,
154+
// We check if a format value is supported in PCM_SET_PARAMS so it should never
155+
// have an unknown value.
156+
_ => unreachable!(),
157+
};
158+
159+
// Calculate desirable bytes/sec rate to achieve the stream's desired
160+
// parameters:
161+
162+
let bps_rate = frame_size * rate;
163+
164+
// Calculate period size for ~100ms (arbitrary) interrupt period:
165+
166+
let period_bytes = bps_rate / 10;
167+
168+
// Finally, calculate the size of a period (in frames):
169+
170+
let period_frames = period_bytes / frame_size;
171+
172+
hwp.set_period_size(period_frames as i64, alsa::ValueOr::Less)?;
173+
174+
// Online ALSA driver recommendations seem to be that the buffer should be at
175+
// least 2 * period_size.
176+
//
177+
// https://www.alsa-project.org/wiki/FramesPeriods says:
124178
//
125-
// FIXME: What values should we set for buffer size and period size? (Should we
126-
// set them at all?) virtio-sound spec deals in bytes but ALSA deals
127-
// in frames. The alsa bindings sometimes use frames and sometimes bytes.
179+
// > It seems (writing-an-alsa-driver.pdf), however, that it is the ALSA runtime that
180+
// > decides on the actual buffer_size and period_size, depending on: the requested
181+
// > number of channels, and their respective properties (rate and sampling resolution) -
182+
// > as well as the parameters set in the snd_pcm_hardware structure (in the driver).
183+
//
184+
// So, if the operation fails let's assume the ALSA runtime has set a better value.
185+
if let Err(err) = hwp.set_buffer_size_near(2 * period_frames as i64) {
186+
log::error!("could not set buffer size {}: {}", 2 * period_frames, err);
187+
}
188+
189+
// Read more at https://www.alsa-project.org/wiki/FramesPeriods.
128190

129191
pcm.hw_params(&hwp)?;
130192
}

0 commit comments

Comments
 (0)