2005-04-16 15:20:36 -07:00
/*
* Linear conversion Plug - In
2007-10-15 09:50:19 +02:00
* Copyright ( c ) 1999 by Jaroslav Kysela < perex @ perex . cz > ,
2005-04-16 15:20:36 -07:00
* Abramo Bagnara < abramo @ alsa - project . org >
*
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/time.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include "pcm_plugin.h"
/*
* Basic linear conversion plugin
*/
2005-11-17 14:01:49 +01:00
struct linear_priv {
2007-08-08 15:50:58 +02:00
int cvt_endian ; /* need endian conversion? */
unsigned int src_ofs ; /* byte offset in source format */
unsigned int dst_ofs ; /* byte soffset in destination format */
unsigned int copy_ofs ; /* byte offset in temporary u32 data */
unsigned int dst_bytes ; /* byte size of destination format */
unsigned int copy_bytes ; /* bytes to copy per conversion */
unsigned int flip ; /* MSB flip for signeness, done after endian conv */
2005-11-17 14:01:49 +01:00
} ;
2005-04-16 15:20:36 -07:00
2007-08-08 15:50:58 +02:00
static inline void do_convert ( struct linear_priv * data ,
unsigned char * dst , unsigned char * src )
{
unsigned int tmp = 0 ;
unsigned char * p = ( unsigned char * ) & tmp ;
memcpy ( p + data - > copy_ofs , src + data - > src_ofs , data - > copy_bytes ) ;
if ( data - > cvt_endian )
tmp = swab32 ( tmp ) ;
tmp ^ = data - > flip ;
memcpy ( dst , p + data - > dst_ofs , data - > dst_bytes ) ;
}
2005-11-17 14:01:49 +01:00
static void convert ( struct snd_pcm_plugin * plugin ,
const struct snd_pcm_plugin_channel * src_channels ,
struct snd_pcm_plugin_channel * dst_channels ,
2005-04-16 15:20:36 -07:00
snd_pcm_uframes_t frames )
{
2005-11-17 14:01:49 +01:00
struct linear_priv * data = ( struct linear_priv * ) plugin - > extra_data ;
2005-04-16 15:20:36 -07:00
int channel ;
int nchannels = plugin - > src_format . channels ;
for ( channel = 0 ; channel < nchannels ; + + channel ) {
char * src ;
char * dst ;
int src_step , dst_step ;
snd_pcm_uframes_t frames1 ;
if ( ! src_channels [ channel ] . enabled ) {
if ( dst_channels [ channel ] . wanted )
snd_pcm_area_silence ( & dst_channels [ channel ] . area , 0 , frames , plugin - > dst_format . format ) ;
dst_channels [ channel ] . enabled = 0 ;
continue ;
}
dst_channels [ channel ] . enabled = 1 ;
src = src_channels [ channel ] . area . addr + src_channels [ channel ] . area . first / 8 ;
dst = dst_channels [ channel ] . area . addr + dst_channels [ channel ] . area . first / 8 ;
src_step = src_channels [ channel ] . area . step / 8 ;
dst_step = dst_channels [ channel ] . area . step / 8 ;
frames1 = frames ;
while ( frames1 - - > 0 ) {
2007-08-08 15:50:58 +02:00
do_convert ( data , dst , src ) ;
2005-04-16 15:20:36 -07:00
src + = src_step ;
dst + = dst_step ;
}
}
}
2005-11-17 14:01:49 +01:00
static snd_pcm_sframes_t linear_transfer ( struct snd_pcm_plugin * plugin ,
const struct snd_pcm_plugin_channel * src_channels ,
struct snd_pcm_plugin_channel * dst_channels ,
2005-04-16 15:20:36 -07:00
snd_pcm_uframes_t frames )
{
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! plugin | | ! src_channels | | ! dst_channels ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
if ( frames = = 0 )
return 0 ;
# ifdef CONFIG_SND_DEBUG
{
unsigned int channel ;
for ( channel = 0 ; channel < plugin - > src_format . channels ; channel + + ) {
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( src_channels [ channel ] . area . first % 8 | |
src_channels [ channel ] . area . step % 8 ) )
return - ENXIO ;
if ( snd_BUG_ON ( dst_channels [ channel ] . area . first % 8 | |
dst_channels [ channel ] . area . step % 8 ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
}
}
# endif
2019-12-04 15:48:24 +01:00
if ( frames > dst_channels [ 0 ] . frames )
frames = dst_channels [ 0 ] . frames ;
2005-04-16 15:20:36 -07:00
convert ( plugin , src_channels , dst_channels , frames ) ;
return frames ;
}
2011-02-14 11:00:47 +01:00
static void init_data ( struct linear_priv * data ,
snd_pcm_format_t src_format , snd_pcm_format_t dst_format )
2005-04-16 15:20:36 -07:00
{
2007-08-08 15:50:58 +02:00
int src_le , dst_le , src_bytes , dst_bytes ;
2005-04-16 15:20:36 -07:00
2007-08-08 15:50:58 +02:00
src_bytes = snd_pcm_format_width ( src_format ) / 8 ;
dst_bytes = snd_pcm_format_width ( dst_format ) / 8 ;
src_le = snd_pcm_format_little_endian ( src_format ) > 0 ;
dst_le = snd_pcm_format_little_endian ( dst_format ) > 0 ;
2005-04-16 15:20:36 -07:00
2007-08-08 15:50:58 +02:00
data - > dst_bytes = dst_bytes ;
data - > cvt_endian = src_le ! = dst_le ;
data - > copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes ;
if ( src_le ) {
data - > copy_ofs = 4 - data - > copy_bytes ;
data - > src_ofs = src_bytes - data - > copy_bytes ;
} else
data - > src_ofs = snd_pcm_format_physical_width ( src_format ) / 8 -
src_bytes ;
if ( dst_le )
data - > dst_ofs = 4 - data - > dst_bytes ;
else
data - > dst_ofs = snd_pcm_format_physical_width ( dst_format ) / 8 -
dst_bytes ;
if ( snd_pcm_format_signed ( src_format ) ! =
snd_pcm_format_signed ( dst_format ) ) {
if ( dst_le )
2011-02-14 11:00:47 +01:00
data - > flip = ( __force u32 ) cpu_to_le32 ( 0x80000000 ) ;
2007-08-08 15:50:58 +02:00
else
2011-02-14 11:00:47 +01:00
data - > flip = ( __force u32 ) cpu_to_be32 ( 0x80000000 ) ;
2007-08-08 15:50:58 +02:00
}
2005-04-16 15:20:36 -07:00
}
2005-11-17 14:01:49 +01:00
int snd_pcm_plugin_build_linear ( struct snd_pcm_substream * plug ,
struct snd_pcm_plugin_format * src_format ,
struct snd_pcm_plugin_format * dst_format ,
struct snd_pcm_plugin * * r_plugin )
2005-04-16 15:20:36 -07:00
{
int err ;
2005-11-17 14:01:49 +01:00
struct linear_priv * data ;
struct snd_pcm_plugin * plugin ;
2005-04-16 15:20:36 -07:00
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! r_plugin ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
* r_plugin = NULL ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( src_format - > rate ! = dst_format - > rate ) )
return - ENXIO ;
if ( snd_BUG_ON ( src_format - > channels ! = dst_format - > channels ) )
return - ENXIO ;
if ( snd_BUG_ON ( ! snd_pcm_format_linear ( src_format - > format ) | |
! snd_pcm_format_linear ( dst_format - > format ) ) )
return - ENXIO ;
2005-04-16 15:20:36 -07:00
err = snd_pcm_plugin_build ( plug , " linear format conversion " ,
src_format , dst_format ,
2005-11-17 14:01:49 +01:00
sizeof ( struct linear_priv ) , & plugin ) ;
2005-04-16 15:20:36 -07:00
if ( err < 0 )
return err ;
2005-11-17 14:01:49 +01:00
data = ( struct linear_priv * ) plugin - > extra_data ;
2007-08-08 15:50:58 +02:00
init_data ( data , src_format - > format , dst_format - > format ) ;
2005-04-16 15:20:36 -07:00
plugin - > transfer = linear_transfer ;
* r_plugin = plugin ;
return 0 ;
}