2 * PCM Plug-In shared (kernel/library) code
3 * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
4 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sound/driver.h>
29 #ifdef CONFIG_SND_PCM_OSS_PLUGINS
31 #include <linux/slab.h>
32 #include <linux/time.h>
33 #include <linux/vmalloc.h>
34 #include <sound/core.h>
35 #include <sound/pcm.h>
36 #include <sound/pcm_params.h>
37 #include "pcm_plugin.h"
39 #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
40 #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
43 * because some cards might have rates "very close", we ignore
44 * all "resampling" requests within +-5%
46 static int rate_match(unsigned int src_rate
, unsigned int dst_rate
)
48 unsigned int low
= (src_rate
* 95) / 100;
49 unsigned int high
= (src_rate
* 105) / 100;
50 return dst_rate
>= low
&& dst_rate
<= high
;
53 static int snd_pcm_plugin_alloc(struct snd_pcm_plugin
*plugin
, snd_pcm_uframes_t frames
)
55 struct snd_pcm_plugin_format
*format
;
59 struct snd_pcm_plugin_channel
*c
;
61 if (plugin
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
62 format
= &plugin
->src_format
;
64 format
= &plugin
->dst_format
;
66 if ((width
= snd_pcm_format_physical_width(format
->format
)) < 0)
68 size
= frames
* format
->channels
* width
;
69 snd_assert((size
% 8) == 0, return -ENXIO
);
71 if (plugin
->buf_frames
< frames
) {
73 plugin
->buf
= vmalloc(size
);
74 plugin
->buf_frames
= frames
;
77 plugin
->buf_frames
= 0;
80 c
= plugin
->buf_channels
;
81 if (plugin
->access
== SNDRV_PCM_ACCESS_RW_INTERLEAVED
) {
82 for (channel
= 0; channel
< format
->channels
; channel
++, c
++) {
86 c
->area
.addr
= plugin
->buf
;
87 c
->area
.first
= channel
* width
;
88 c
->area
.step
= format
->channels
* width
;
90 } else if (plugin
->access
== SNDRV_PCM_ACCESS_RW_NONINTERLEAVED
) {
91 snd_assert((size
% format
->channels
) == 0,);
92 size
/= format
->channels
;
93 for (channel
= 0; channel
< format
->channels
; channel
++, c
++) {
97 c
->area
.addr
= plugin
->buf
+ (channel
* size
);
106 int snd_pcm_plug_alloc(struct snd_pcm_substream
*plug
, snd_pcm_uframes_t frames
)
109 snd_assert(snd_pcm_plug_first(plug
) != NULL
, return -ENXIO
);
110 if (snd_pcm_plug_stream(plug
) == SNDRV_PCM_STREAM_PLAYBACK
) {
111 struct snd_pcm_plugin
*plugin
= snd_pcm_plug_first(plug
);
112 while (plugin
->next
) {
113 if (plugin
->dst_frames
)
114 frames
= plugin
->dst_frames(plugin
, frames
);
115 snd_assert(frames
> 0, return -ENXIO
);
116 plugin
= plugin
->next
;
117 err
= snd_pcm_plugin_alloc(plugin
, frames
);
122 struct snd_pcm_plugin
*plugin
= snd_pcm_plug_last(plug
);
123 while (plugin
->prev
) {
124 if (plugin
->src_frames
)
125 frames
= plugin
->src_frames(plugin
, frames
);
126 snd_assert(frames
> 0, return -ENXIO
);
127 plugin
= plugin
->prev
;
128 err
= snd_pcm_plugin_alloc(plugin
, frames
);
137 snd_pcm_sframes_t
snd_pcm_plugin_client_channels(struct snd_pcm_plugin
*plugin
,
138 snd_pcm_uframes_t frames
,
139 struct snd_pcm_plugin_channel
**channels
)
141 *channels
= plugin
->buf_channels
;
145 int snd_pcm_plugin_build(struct snd_pcm_substream
*plug
,
147 struct snd_pcm_plugin_format
*src_format
,
148 struct snd_pcm_plugin_format
*dst_format
,
150 struct snd_pcm_plugin
**ret
)
152 struct snd_pcm_plugin
*plugin
;
153 unsigned int channels
;
155 snd_assert(plug
!= NULL
, return -ENXIO
);
156 snd_assert(src_format
!= NULL
&& dst_format
!= NULL
, return -ENXIO
);
157 plugin
= kzalloc(sizeof(*plugin
) + extra
, GFP_KERNEL
);
162 plugin
->stream
= snd_pcm_plug_stream(plug
);
163 plugin
->access
= SNDRV_PCM_ACCESS_RW_INTERLEAVED
;
164 plugin
->src_format
= *src_format
;
165 plugin
->src_width
= snd_pcm_format_physical_width(src_format
->format
);
166 snd_assert(plugin
->src_width
> 0, );
167 plugin
->dst_format
= *dst_format
;
168 plugin
->dst_width
= snd_pcm_format_physical_width(dst_format
->format
);
169 snd_assert(plugin
->dst_width
> 0, );
170 if (plugin
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
171 channels
= src_format
->channels
;
173 channels
= dst_format
->channels
;
174 plugin
->buf_channels
= kcalloc(channels
, sizeof(*plugin
->buf_channels
), GFP_KERNEL
);
175 if (plugin
->buf_channels
== NULL
) {
176 snd_pcm_plugin_free(plugin
);
179 plugin
->client_channels
= snd_pcm_plugin_client_channels
;
184 int snd_pcm_plugin_free(struct snd_pcm_plugin
*plugin
)
188 if (plugin
->private_free
)
189 plugin
->private_free(plugin
);
190 kfree(plugin
->buf_channels
);
196 snd_pcm_sframes_t
snd_pcm_plug_client_size(struct snd_pcm_substream
*plug
, snd_pcm_uframes_t drv_frames
)
198 struct snd_pcm_plugin
*plugin
, *plugin_prev
, *plugin_next
;
199 int stream
= snd_pcm_plug_stream(plug
);
201 snd_assert(plug
!= NULL
, return -ENXIO
);
204 if (stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
205 plugin
= snd_pcm_plug_last(plug
);
206 while (plugin
&& drv_frames
> 0) {
207 plugin_prev
= plugin
->prev
;
208 if (plugin
->src_frames
)
209 drv_frames
= plugin
->src_frames(plugin
, drv_frames
);
210 plugin
= plugin_prev
;
212 } else if (stream
== SNDRV_PCM_STREAM_CAPTURE
) {
213 plugin
= snd_pcm_plug_first(plug
);
214 while (plugin
&& drv_frames
> 0) {
215 plugin_next
= plugin
->next
;
216 if (plugin
->dst_frames
)
217 drv_frames
= plugin
->dst_frames(plugin
, drv_frames
);
218 plugin
= plugin_next
;
225 snd_pcm_sframes_t
snd_pcm_plug_slave_size(struct snd_pcm_substream
*plug
, snd_pcm_uframes_t clt_frames
)
227 struct snd_pcm_plugin
*plugin
, *plugin_prev
, *plugin_next
;
228 snd_pcm_sframes_t frames
;
229 int stream
= snd_pcm_plug_stream(plug
);
231 snd_assert(plug
!= NULL
, return -ENXIO
);
235 if (stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
236 plugin
= snd_pcm_plug_first(plug
);
237 while (plugin
&& frames
> 0) {
238 plugin_next
= plugin
->next
;
239 if (plugin
->dst_frames
) {
240 frames
= plugin
->dst_frames(plugin
, frames
);
244 plugin
= plugin_next
;
246 } else if (stream
== SNDRV_PCM_STREAM_CAPTURE
) {
247 plugin
= snd_pcm_plug_last(plug
);
249 plugin_prev
= plugin
->prev
;
250 if (plugin
->src_frames
) {
251 frames
= plugin
->src_frames(plugin
, frames
);
255 plugin
= plugin_prev
;
262 static int snd_pcm_plug_formats(struct snd_mask
*mask
, int format
)
264 struct snd_mask formats
= *mask
;
265 u64 linfmts
= (SNDRV_PCM_FMTBIT_U8
| SNDRV_PCM_FMTBIT_S8
|
266 SNDRV_PCM_FMTBIT_U16_LE
| SNDRV_PCM_FMTBIT_S16_LE
|
267 SNDRV_PCM_FMTBIT_U16_BE
| SNDRV_PCM_FMTBIT_S16_BE
|
268 SNDRV_PCM_FMTBIT_U24_LE
| SNDRV_PCM_FMTBIT_S24_LE
|
269 SNDRV_PCM_FMTBIT_U24_BE
| SNDRV_PCM_FMTBIT_S24_BE
|
270 SNDRV_PCM_FMTBIT_U32_LE
| SNDRV_PCM_FMTBIT_S32_LE
|
271 SNDRV_PCM_FMTBIT_U32_BE
| SNDRV_PCM_FMTBIT_S32_BE
);
272 snd_mask_set(&formats
, SNDRV_PCM_FORMAT_MU_LAW
);
274 if (formats
.bits
[0] & (u32
)linfmts
)
275 formats
.bits
[0] |= (u32
)linfmts
;
276 if (formats
.bits
[1] & (u32
)(linfmts
>> 32))
277 formats
.bits
[1] |= (u32
)(linfmts
>> 32);
278 return snd_mask_test(&formats
, format
);
281 static int preferred_formats
[] = {
282 SNDRV_PCM_FORMAT_S16_LE
,
283 SNDRV_PCM_FORMAT_S16_BE
,
284 SNDRV_PCM_FORMAT_U16_LE
,
285 SNDRV_PCM_FORMAT_U16_BE
,
286 SNDRV_PCM_FORMAT_S24_LE
,
287 SNDRV_PCM_FORMAT_S24_BE
,
288 SNDRV_PCM_FORMAT_U24_LE
,
289 SNDRV_PCM_FORMAT_U24_BE
,
290 SNDRV_PCM_FORMAT_S32_LE
,
291 SNDRV_PCM_FORMAT_S32_BE
,
292 SNDRV_PCM_FORMAT_U32_LE
,
293 SNDRV_PCM_FORMAT_U32_BE
,
298 int snd_pcm_plug_slave_format(int format
, struct snd_mask
*format_mask
)
300 if (snd_mask_test(format_mask
, format
))
302 if (! snd_pcm_plug_formats(format_mask
, format
))
304 if (snd_pcm_format_linear(format
)) {
305 int width
= snd_pcm_format_width(format
);
306 int unsignd
= snd_pcm_format_unsigned(format
);
307 int big
= snd_pcm_format_big_endian(format
);
309 int wid
, width1
=width
;
311 for (wid
= 0; wid
< 4; ++wid
) {
313 for (end
= 0; end
< 2; ++end
) {
314 int sgn
, unsignd1
= unsignd
;
315 for (sgn
= 0; sgn
< 2; ++sgn
) {
316 format1
= snd_pcm_build_linear_format(width1
, unsignd1
, big1
);
318 snd_mask_test(format_mask
, format1
))
320 unsignd1
= !unsignd1
;
336 case SNDRV_PCM_FORMAT_MU_LAW
:
337 for (i
= 0; i
< ARRAY_SIZE(preferred_formats
); ++i
) {
338 int format1
= preferred_formats
[i
];
339 if (snd_mask_test(format_mask
, format1
))
348 int snd_pcm_plug_format_plugins(struct snd_pcm_substream
*plug
,
349 struct snd_pcm_hw_params
*params
,
350 struct snd_pcm_hw_params
*slave_params
)
352 struct snd_pcm_plugin_format tmpformat
;
353 struct snd_pcm_plugin_format dstformat
;
354 struct snd_pcm_plugin_format srcformat
;
355 int src_access
, dst_access
;
356 struct snd_pcm_plugin
*plugin
= NULL
;
358 int stream
= snd_pcm_plug_stream(plug
);
359 int slave_interleaved
= (params_channels(slave_params
) == 1 ||
360 params_access(slave_params
) == SNDRV_PCM_ACCESS_RW_INTERLEAVED
);
363 case SNDRV_PCM_STREAM_PLAYBACK
:
364 dstformat
.format
= params_format(slave_params
);
365 dstformat
.rate
= params_rate(slave_params
);
366 dstformat
.channels
= params_channels(slave_params
);
367 srcformat
.format
= params_format(params
);
368 srcformat
.rate
= params_rate(params
);
369 srcformat
.channels
= params_channels(params
);
370 src_access
= SNDRV_PCM_ACCESS_RW_INTERLEAVED
;
371 dst_access
= (slave_interleaved
? SNDRV_PCM_ACCESS_RW_INTERLEAVED
:
372 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED
);
374 case SNDRV_PCM_STREAM_CAPTURE
:
375 dstformat
.format
= params_format(params
);
376 dstformat
.rate
= params_rate(params
);
377 dstformat
.channels
= params_channels(params
);
378 srcformat
.format
= params_format(slave_params
);
379 srcformat
.rate
= params_rate(slave_params
);
380 srcformat
.channels
= params_channels(slave_params
);
381 src_access
= (slave_interleaved
? SNDRV_PCM_ACCESS_RW_INTERLEAVED
:
382 SNDRV_PCM_ACCESS_RW_NONINTERLEAVED
);
383 dst_access
= SNDRV_PCM_ACCESS_RW_INTERLEAVED
;
389 tmpformat
= srcformat
;
391 pdprintf("srcformat: format=%i, rate=%i, channels=%i\n",
395 pdprintf("dstformat: format=%i, rate=%i, channels=%i\n",
400 /* Format change (linearization) */
401 if (! rate_match(srcformat
.rate
, dstformat
.rate
) &&
402 ! snd_pcm_format_linear(srcformat
.format
)) {
403 if (srcformat
.format
!= SNDRV_PCM_FORMAT_MU_LAW
)
405 tmpformat
.format
= SNDRV_PCM_FORMAT_S16
;
406 err
= snd_pcm_plugin_build_mulaw(plug
,
407 &srcformat
, &tmpformat
,
411 err
= snd_pcm_plugin_append(plugin
);
413 snd_pcm_plugin_free(plugin
);
416 srcformat
= tmpformat
;
417 src_access
= dst_access
;
420 /* channels reduction */
421 if (srcformat
.channels
> dstformat
.channels
) {
422 tmpformat
.channels
= dstformat
.channels
;
423 err
= snd_pcm_plugin_build_route(plug
, &srcformat
, &tmpformat
, &plugin
);
424 pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat
.channels
, tmpformat
.channels
, err
);
427 err
= snd_pcm_plugin_append(plugin
);
429 snd_pcm_plugin_free(plugin
);
432 srcformat
= tmpformat
;
433 src_access
= dst_access
;
436 /* rate resampling */
437 if (!rate_match(srcformat
.rate
, dstformat
.rate
)) {
438 if (srcformat
.format
!= SNDRV_PCM_FORMAT_S16
) {
439 /* convert to S16 for resampling */
440 tmpformat
.format
= SNDRV_PCM_FORMAT_S16
;
441 err
= snd_pcm_plugin_build_linear(plug
,
442 &srcformat
, &tmpformat
,
446 err
= snd_pcm_plugin_append(plugin
);
448 snd_pcm_plugin_free(plugin
);
451 srcformat
= tmpformat
;
452 src_access
= dst_access
;
454 tmpformat
.rate
= dstformat
.rate
;
455 err
= snd_pcm_plugin_build_rate(plug
,
456 &srcformat
, &tmpformat
,
458 pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat
.rate
, tmpformat
.rate
, err
);
461 err
= snd_pcm_plugin_append(plugin
);
463 snd_pcm_plugin_free(plugin
);
466 srcformat
= tmpformat
;
467 src_access
= dst_access
;
471 if (srcformat
.format
!= dstformat
.format
) {
472 tmpformat
.format
= dstformat
.format
;
473 if (srcformat
.format
== SNDRV_PCM_FORMAT_MU_LAW
||
474 tmpformat
.format
== SNDRV_PCM_FORMAT_MU_LAW
) {
475 err
= snd_pcm_plugin_build_mulaw(plug
,
476 &srcformat
, &tmpformat
,
479 else if (snd_pcm_format_linear(srcformat
.format
) &&
480 snd_pcm_format_linear(tmpformat
.format
)) {
481 err
= snd_pcm_plugin_build_linear(plug
,
482 &srcformat
, &tmpformat
,
487 pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat
.format
, tmpformat
.format
, err
);
490 err
= snd_pcm_plugin_append(plugin
);
492 snd_pcm_plugin_free(plugin
);
495 srcformat
= tmpformat
;
496 src_access
= dst_access
;
499 /* channels extension */
500 if (srcformat
.channels
< dstformat
.channels
) {
501 tmpformat
.channels
= dstformat
.channels
;
502 err
= snd_pcm_plugin_build_route(plug
, &srcformat
, &tmpformat
, &plugin
);
503 pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat
.channels
, tmpformat
.channels
, err
);
506 err
= snd_pcm_plugin_append(plugin
);
508 snd_pcm_plugin_free(plugin
);
511 srcformat
= tmpformat
;
512 src_access
= dst_access
;
516 if (src_access
!= dst_access
) {
517 err
= snd_pcm_plugin_build_copy(plug
,
521 pdprintf("interleave change (copy: returns %i)\n", err
);
524 err
= snd_pcm_plugin_append(plugin
);
526 snd_pcm_plugin_free(plugin
);
534 snd_pcm_sframes_t
snd_pcm_plug_client_channels_buf(struct snd_pcm_substream
*plug
,
536 snd_pcm_uframes_t count
,
537 struct snd_pcm_plugin_channel
**channels
)
539 struct snd_pcm_plugin
*plugin
;
540 struct snd_pcm_plugin_channel
*v
;
541 struct snd_pcm_plugin_format
*format
;
542 int width
, nchannels
, channel
;
543 int stream
= snd_pcm_plug_stream(plug
);
545 snd_assert(buf
!= NULL
, return -ENXIO
);
546 if (stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
547 plugin
= snd_pcm_plug_first(plug
);
548 format
= &plugin
->src_format
;
550 plugin
= snd_pcm_plug_last(plug
);
551 format
= &plugin
->dst_format
;
553 v
= plugin
->buf_channels
;
555 if ((width
= snd_pcm_format_physical_width(format
->format
)) < 0)
557 nchannels
= format
->channels
;
558 snd_assert(plugin
->access
== SNDRV_PCM_ACCESS_RW_INTERLEAVED
|| format
->channels
<= 1, return -ENXIO
);
559 for (channel
= 0; channel
< nchannels
; channel
++, v
++) {
562 v
->wanted
= (stream
== SNDRV_PCM_STREAM_CAPTURE
);
564 v
->area
.first
= channel
* width
;
565 v
->area
.step
= nchannels
* width
;
570 snd_pcm_sframes_t
snd_pcm_plug_write_transfer(struct snd_pcm_substream
*plug
, struct snd_pcm_plugin_channel
*src_channels
, snd_pcm_uframes_t size
)
572 struct snd_pcm_plugin
*plugin
, *next
;
573 struct snd_pcm_plugin_channel
*dst_channels
;
575 snd_pcm_sframes_t frames
= size
;
577 plugin
= snd_pcm_plug_first(plug
);
578 while (plugin
&& frames
> 0) {
579 if ((next
= plugin
->next
) != NULL
) {
580 snd_pcm_sframes_t frames1
= frames
;
581 if (plugin
->dst_frames
)
582 frames1
= plugin
->dst_frames(plugin
, frames
);
583 if ((err
= next
->client_channels(next
, frames1
, &dst_channels
)) < 0) {
586 if (err
!= frames1
) {
588 if (plugin
->src_frames
)
589 frames
= plugin
->src_frames(plugin
, frames1
);
593 pdprintf("write plugin: %s, %li\n", plugin
->name
, frames
);
594 if ((frames
= plugin
->transfer(plugin
, src_channels
, dst_channels
, frames
)) < 0)
596 src_channels
= dst_channels
;
599 return snd_pcm_plug_client_size(plug
, frames
);
602 snd_pcm_sframes_t
snd_pcm_plug_read_transfer(struct snd_pcm_substream
*plug
, struct snd_pcm_plugin_channel
*dst_channels_final
, snd_pcm_uframes_t size
)
604 struct snd_pcm_plugin
*plugin
, *next
;
605 struct snd_pcm_plugin_channel
*src_channels
, *dst_channels
;
606 snd_pcm_sframes_t frames
= size
;
609 frames
= snd_pcm_plug_slave_size(plug
, frames
);
614 plugin
= snd_pcm_plug_first(plug
);
615 while (plugin
&& frames
> 0) {
616 if ((next
= plugin
->next
) != NULL
) {
617 if ((err
= plugin
->client_channels(plugin
, frames
, &dst_channels
)) < 0) {
622 dst_channels
= dst_channels_final
;
624 pdprintf("read plugin: %s, %li\n", plugin
->name
, frames
);
625 if ((frames
= plugin
->transfer(plugin
, src_channels
, dst_channels
, frames
)) < 0)
628 src_channels
= dst_channels
;
633 int snd_pcm_area_silence(const struct snd_pcm_channel_area
*dst_area
, size_t dst_offset
,
634 size_t samples
, int format
)
636 /* FIXME: sub byte resolution and odd dst_offset */
638 unsigned int dst_step
;
640 const unsigned char *silence
;
643 dst
= dst_area
->addr
+ (dst_area
->first
+ dst_area
->step
* dst_offset
) / 8;
644 width
= snd_pcm_format_physical_width(format
);
647 if (dst_area
->step
== (unsigned int) width
&& width
>= 8)
648 return snd_pcm_format_set_silence(format
, dst
, samples
);
649 silence
= snd_pcm_format_silence_64(format
);
652 dst_step
= dst_area
->step
/ 8;
655 int dstbit
= dst_area
->first
% 8;
656 int dstbit_step
= dst_area
->step
% 8;
657 while (samples
-- > 0) {
663 dstbit
+= dstbit_step
;
671 while (samples
-- > 0) {
672 memcpy(dst
, silence
, width
);
679 int snd_pcm_area_copy(const struct snd_pcm_channel_area
*src_area
, size_t src_offset
,
680 const struct snd_pcm_channel_area
*dst_area
, size_t dst_offset
,
681 size_t samples
, int format
)
683 /* FIXME: sub byte resolution and odd dst_offset */
686 int src_step
, dst_step
;
687 src
= src_area
->addr
+ (src_area
->first
+ src_area
->step
* src_offset
) / 8;
689 return snd_pcm_area_silence(dst_area
, dst_offset
, samples
, format
);
690 dst
= dst_area
->addr
+ (dst_area
->first
+ dst_area
->step
* dst_offset
) / 8;
693 width
= snd_pcm_format_physical_width(format
);
696 if (src_area
->step
== (unsigned int) width
&&
697 dst_area
->step
== (unsigned int) width
&& width
>= 8) {
698 size_t bytes
= samples
* width
/ 8;
699 memcpy(dst
, src
, bytes
);
702 src_step
= src_area
->step
/ 8;
703 dst_step
= dst_area
->step
/ 8;
706 int srcbit
= src_area
->first
% 8;
707 int srcbit_step
= src_area
->step
% 8;
708 int dstbit
= dst_area
->first
% 8;
709 int dstbit_step
= dst_area
->step
% 8;
710 while (samples
-- > 0) {
711 unsigned char srcval
;
713 srcval
= *src
& 0x0f;
715 srcval
= (*src
& 0xf0) >> 4;
717 *dst
= (*dst
& 0xf0) | srcval
;
719 *dst
= (*dst
& 0x0f) | (srcval
<< 4);
721 srcbit
+= srcbit_step
;
727 dstbit
+= dstbit_step
;
735 while (samples
-- > 0) {
736 memcpy(dst
, src
, width
);