@@ -60,34 +60,37 @@ fn update_pcm(
60
60
match s. direction {
61
61
d if d == VIRTIO_SND_D_OUTPUT => Direction :: Playback ,
62
62
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 ! ( ) ,
64
66
} ,
65
67
false ,
66
68
) ?;
67
69
68
70
{
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
+ } ;
69
90
let hwp = HwParams :: any ( & pcm) ?;
70
91
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 ) ?;
91
94
hwp. set_format ( match s. params . format {
92
95
virtio_sound:: VIRTIO_SND_PCM_FMT_IMA_ADPCM => Format :: ImaAdPCM ,
93
96
virtio_sound:: VIRTIO_SND_PCM_FMT_MU_LAW => Format :: MuLaw ,
@@ -114,17 +117,76 @@ fn update_pcm(
114
117
virtio_sound:: VIRTIO_SND_PCM_FMT_DSD_U16 => Format :: DSDU16LE ,
115
118
virtio_sound:: VIRTIO_SND_PCM_FMT_DSD_U32 => Format :: DSDU32LE ,
116
119
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 ! ( ) ,
118
123
} ) ?;
119
124
120
125
hwp. set_access ( Access :: RWInterleaved ) ?;
121
126
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:
124
178
//
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.
128
190
129
191
pcm. hw_params ( & hwp) ?;
130
192
}
0 commit comments