audiopanorama: use orc to speedup processing

Use special variants for the case when we don't change the panorama (pan=0.0).
Simplify the processing functions by passing the panorama value directy instead
of the instance. Use orc for clearing buffers too.
This commit is contained in:
Stefan Sauer 2013-06-09 20:35:18 +02:00
parent 6e23f1fec4
commit 1dc06932a2
4 changed files with 382 additions and 233 deletions

View file

@ -5,6 +5,9 @@ plugin_LTLIBRARIES = libgstaudiofx.la
# FIXME 0.11: ignore GValueArray warnings for now until this is sorted
ERROR_CFLAGS=
ORC_SOURCE=audiopanoramaorc
include $(top_srcdir)/common/orc.mak
# sources used to compile this plug-in
libgstaudiofx_la_SOURCES = audiofx.c\
audiopanorama.c \
@ -22,16 +25,19 @@ libgstaudiofx_la_SOURCES = audiofx.c\
audiofirfilter.c \
audioecho.c \
gstscaletempo.c
nodist_libgstaudiofx_la_SOURCES = $(ORC_NODIST_SOURCES)
# flags used to compile this plugin
libgstaudiofx_la_CFLAGS = $(GST_CFLAGS) \
$(GST_BASE_CFLAGS) \
$(GST_PLUGINS_BASE_CFLAGS)
$(GST_PLUGINS_BASE_CFLAGS) \
$(ORC_CFLAGS)
libgstaudiofx_la_LIBADD = $(GST_LIBS) \
$(GST_BASE_LIBS) \
$(GST_PLUGINS_BASE_LIBS) \
-lgstaudio-$(GST_API_VERSION) \
-lgstfft-$(GST_API_VERSION) \
$(ORC_LIBS) \
$(LIBM)
libgstaudiofx_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstaudiofx_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)

View file

@ -45,7 +45,14 @@
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#ifdef HAVE_ORC
#include <orc/orcfunctions.h>
#else
#define orc_memset memset
#endif
#include "audiopanorama.h"
#include "audiopanoramaorc.h"
#define GST_CAT_DEFAULT gst_audio_panorama_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@ -109,23 +116,23 @@ static GstCaps *gst_audio_panorama_transform_caps (GstBaseTransform * base,
static gboolean gst_audio_panorama_set_caps (GstBaseTransform * base,
GstCaps * incaps, GstCaps * outcaps);
static void gst_audio_panorama_transform_m2s_int (GstAudioPanorama * filter,
static void gst_audio_panorama_m2s_int (gfloat pan,
gint16 * idata, gint16 * odata, guint num_samples);
static void gst_audio_panorama_transform_s2s_int (GstAudioPanorama * filter,
static void gst_audio_panorama_s2s_int (gfloat pan,
gint16 * idata, gint16 * odata, guint num_samples);
static void gst_audio_panorama_transform_m2s_float (GstAudioPanorama * filter,
static void gst_audio_panorama_m2s_float (gfloat pan,
gfloat * idata, gfloat * odata, guint num_samples);
static void gst_audio_panorama_transform_s2s_float (GstAudioPanorama * filter,
static void gst_audio_panorama_s2s_float (gfloat pan,
gfloat * idata, gfloat * odata, guint num_samples);
static void gst_audio_panorama_transform_m2s_int_simple (GstAudioPanorama *
filter, gint16 * idata, gint16 * odata, guint num_samples);
static void gst_audio_panorama_transform_s2s_int_simple (GstAudioPanorama *
filter, gint16 * idata, gint16 * odata, guint num_samples);
static void gst_audio_panorama_transform_m2s_float_simple (GstAudioPanorama *
filter, gfloat * idata, gfloat * odata, guint num_samples);
static void gst_audio_panorama_transform_s2s_float_simple (GstAudioPanorama *
filter, gfloat * idata, gfloat * odata, guint num_samples);
static void gst_audio_panorama_m2s_int_simple (gfloat pan,
gint16 * idata, gint16 * odata, guint num_samples);
static void gst_audio_panorama_s2s_int_simple (gfloat pan,
gint16 * idata, gint16 * odata, guint num_samples);
static void gst_audio_panorama_m2s_float_simple (gfloat pan,
gfloat * idata, gfloat * odata, guint num_samples);
static void gst_audio_panorama_s2s_float_simple (gfloat pan,
gfloat * idata, gfloat * odata, guint num_samples);
static GstFlowReturn gst_audio_panorama_transform (GstBaseTransform * base,
GstBuffer * inbuf, GstBuffer * outbuf);
@ -134,20 +141,20 @@ static GstFlowReturn gst_audio_panorama_transform (GstBaseTransform * base,
/* Table with processing functions: [channels][format][method] */
static GstAudioPanoramaProcessFunc panorama_process_functions[2][2][2] = {
{
{(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_m2s_int,
(GstAudioPanoramaProcessFunc)
gst_audio_panorama_transform_m2s_int_simple},
{(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_m2s_float,
(GstAudioPanoramaProcessFunc)
gst_audio_panorama_transform_m2s_float_simple}
{
(GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_int,
(GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_int_simple},
{
(GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_float,
(GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_float_simple}
},
{
{(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_s2s_int,
(GstAudioPanoramaProcessFunc)
gst_audio_panorama_transform_s2s_int_simple},
{(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_s2s_float,
(GstAudioPanoramaProcessFunc)
gst_audio_panorama_transform_s2s_float_simple}
{
(GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_int,
(GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_int_simple},
{
(GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_float,
(GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_float_simple}
}
};
@ -366,250 +373,128 @@ no_format:
}
/* psychoacoustic processing functions */
/* mono to stereo panning
* pan: -1.0 0.0 1.0
* l: 1.0 0.5 0.0
* r: 0.0 0.5 1.0
*
* FIXME: we should use -3db (1/sqtr(2)) for 50:50
*/
static void
gst_audio_panorama_transform_m2s_int (GstAudioPanorama * filter, gint16 * idata,
gint16 * odata, guint num_samples)
gst_audio_panorama_m2s_int (gfloat pan, gint16 * idata, gint16 * odata, guint n)
{
guint i;
gdouble val;
glong lval, rval;
gdouble rpan, lpan;
/* pan: -1.0 0.0 1.0
* lpan: 1.0 0.5 0.0
* rpan: 0.0 0.5 1.0
*
* FIXME: we should use -3db (1/sqtr(2)) for 50:50
*/
rpan = (gdouble) (filter->panorama + 1.0) / 2.0;
lpan = 1.0 - rpan;
for (i = 0; i < num_samples; i++) {
val = (gdouble) * idata++;
lval = (glong) (val * lpan);
rval = (glong) (val * rpan);
*odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
*odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
}
gfloat r = (pan + 1.0) / 2.0;
audiopanoramam_orc_process_s16_ch1_psy (odata, idata, 1.0 - r, r, n);
}
static void
gst_audio_panorama_transform_s2s_int (GstAudioPanorama * filter, gint16 * idata,
gint16 * odata, guint num_samples)
gst_audio_panorama_m2s_float (gfloat pan, gfloat * idata,
gfloat * odata, guint n)
{
guint i;
glong lval, rval;
gdouble lival, rival;
gdouble lrpan, llpan, rrpan, rlpan;
gfloat r = (pan + 1.0) / 2.0;
audiopanoramam_orc_process_f32_ch1_psy (odata, idata, 1.0 - r, r, n);
}
/* pan: -1.0 0.0 1.0
* llpan: 1.0 1.0 0.0
* lrpan: 1.0 0.0 0.0
* rrpan: 0.0 1.0 1.0
* rlpan: 0.0 0.0 1.0
*/
if (filter->panorama > 0) {
rlpan = (gdouble) filter->panorama;
llpan = 1.0 - rlpan;
lrpan = 0.0;
rrpan = 1.0;
/* stereo balance
* pan: -1.0 0.0 1.0
* ll: 1.0 1.0 0.0
* lr: 1.0 0.0 0.0
* rr: 0.0 1.0 1.0
* rl: 0.0 0.0 1.0
*/
static void
gst_audio_panorama_s2s_int (gfloat pan, gint16 * idata, gint16 * odata, guint n)
{
if (pan == 0.0) {
audiopanoramam_orc_process_s16_ch2_none (odata, idata, n);
} else if (pan > 0.0) {
gfloat rl = pan;
gfloat ll = 1.0 - rl;
audiopanoramam_orc_process_s16_ch2_psy_right (odata, idata, ll, rl, n);
} else {
rrpan = (gdouble) (1.0 + filter->panorama);
lrpan = 1.0 - rrpan;
rlpan = 0.0;
llpan = 1.0;
}
for (i = 0; i < num_samples; i++) {
lival = (gdouble) * idata++;
rival = (gdouble) * idata++;
lval = lival * llpan + rival * lrpan;
rval = lival * rlpan + rival * rrpan;
*odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
*odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
gfloat rr = 1.0 + pan;
gfloat lr = 1.0 - rr;
audiopanoramam_orc_process_s16_ch2_psy_left (odata, idata, lr, rr, n);
}
}
static void
gst_audio_panorama_transform_m2s_float (GstAudioPanorama * filter,
gfloat * idata, gfloat * odata, guint num_samples)
gst_audio_panorama_s2s_float (gfloat pan, gfloat * idata,
gfloat * odata, guint n)
{
guint i;
gfloat val;
gdouble rpan, lpan;
/* pan: -1.0 0.0 1.0
* lpan: 1.0 0.5 0.0
* rpan: 0.0 0.5 1.0
*
* FIXME: we should use -3db (1/sqtr(2)) for 50:50
*/
rpan = (gdouble) (filter->panorama + 1.0) / 2.0;
lpan = 1.0 - rpan;
for (i = 0; i < num_samples; i++) {
val = *idata++;
*odata++ = val * lpan;
*odata++ = val * rpan;
}
}
static void
gst_audio_panorama_transform_s2s_float (GstAudioPanorama * filter,
gfloat * idata, gfloat * odata, guint num_samples)
{
guint i;
gfloat lival, rival;
gdouble lrpan, llpan, rrpan, rlpan;
/* pan: -1.0 0.0 1.0
* llpan: 1.0 1.0 0.0
* lrpan: 1.0 0.0 0.0
* rrpan: 0.0 1.0 1.0
* rlpan: 0.0 0.0 1.0
*/
if (filter->panorama > 0) {
rlpan = (gdouble) filter->panorama;
llpan = 1.0 - rlpan;
lrpan = 0.0;
rrpan = 1.0;
if (pan == 0.0) {
audiopanoramam_orc_process_f32_ch2_none (odata, idata, n);
} else if (pan > 0.0) {
gfloat rl = pan;
gfloat ll = 1.0 - rl;
audiopanoramam_orc_process_f32_ch2_psy_right (odata, idata, ll, rl, n);
} else {
rrpan = (gdouble) (1.0 + filter->panorama);
lrpan = 1.0 - rrpan;
rlpan = 0.0;
llpan = 1.0;
}
for (i = 0; i < num_samples; i++) {
lival = *idata++;
rival = *idata++;
*odata++ = lival * llpan + rival * lrpan;
*odata++ = lival * rlpan + rival * rrpan;
gfloat rr = 1.0 + pan;
gfloat lr = 1.0 - rr;
audiopanoramam_orc_process_f32_ch2_psy_left (odata, idata, lr, rr, n);
}
}
/* simple processing functions */
static void
gst_audio_panorama_transform_m2s_int_simple (GstAudioPanorama * filter,
gint16 * idata, gint16 * odata, guint num_samples)
gst_audio_panorama_m2s_int_simple (gfloat pan, gint16 * idata,
gint16 * odata, guint n)
{
guint i;
gdouble pan;
glong lval, rval;
if (filter->panorama > 0.0) {
pan = 1.0 - filter->panorama;
for (i = 0; i < num_samples; i++) {
rval = *idata++;
lval = (glong) ((gdouble) rval * pan);
*odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
*odata++ = (gint16) rval;
}
if (pan == 0.0) {
audiopanoramam_orc_process_s16_ch1_none (odata, idata, n);
} else if (pan > 0.0) {
gfloat lpan = 1.0 - pan;
audiopanoramam_orc_process_s16_ch1_sim_left (odata, idata, lpan, n);
} else {
pan = 1.0 + filter->panorama;
for (i = 0; i < num_samples; i++) {
lval = *idata++;
rval = (glong) ((gdouble) lval * pan);
*odata++ = (gint16) lval;
*odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
}
gfloat rpan = 1.0 + pan;
audiopanoramam_orc_process_s16_ch1_sim_right (odata, idata, rpan, n);
}
}
static void
gst_audio_panorama_transform_s2s_int_simple (GstAudioPanorama * filter,
gint16 * idata, gint16 * odata, guint num_samples)
gst_audio_panorama_s2s_int_simple (gfloat pan, gint16 * idata,
gint16 * odata, guint n)
{
guint i;
glong lval, rval;
gdouble lival, rival, pan;
if (filter->panorama > 0.0) {
pan = 1.0 - filter->panorama;
for (i = 0; i < num_samples; i++) {
lival = (gdouble) * idata++;
rival = (gdouble) * idata++;
lval = (glong) (lival * pan);
rval = (glong) rival;
*odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
*odata++ = (gint16) rval;
}
if (pan == 0.0) {
audiopanoramam_orc_process_s16_ch2_none (odata, idata, n);
} else if (pan > 0.0) {
gfloat lpan = 1.0 - pan;
audiopanoramam_orc_process_s16_ch2_sim_left (odata, idata, lpan, n);
} else {
pan = 1.0 + filter->panorama;
for (i = 0; i < num_samples; i++) {
lival = (gdouble) * idata++;
rival = (gdouble) * idata++;
lval = (glong) lival;
rval = (glong) (rival * pan);
*odata++ = (gint16) lval;
*odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
}
gfloat rpan = 1.0 + pan;
audiopanoramam_orc_process_s16_ch2_sim_right (odata, idata, rpan, n);
}
}
static void
gst_audio_panorama_transform_m2s_float_simple (GstAudioPanorama * filter,
gfloat * idata, gfloat * odata, guint num_samples)
gst_audio_panorama_m2s_float_simple (gfloat pan, gfloat * idata,
gfloat * odata, guint n)
{
guint i;
gfloat val, pan;
if (filter->panorama > 0.0) {
pan = 1.0 - filter->panorama;
for (i = 0; i < num_samples; i++) {
val = *idata++;
*odata++ = val * pan;
*odata++ = val;
}
if (pan == 0.0) {
audiopanoramam_orc_process_f32_ch1_none (odata, idata, n);
} else if (pan > 0.0) {
gfloat lpan = 1.0 - pan;
audiopanoramam_orc_process_f32_ch1_sim_left (odata, idata, lpan, n);
} else {
pan = 1.0 + filter->panorama;
for (i = 0; i < num_samples; i++) {
val = *idata++;
*odata++ = val;
*odata++ = val * pan;
}
gfloat rpan = 1.0 + pan;
audiopanoramam_orc_process_f32_ch1_sim_right (odata, idata, rpan, n);
}
}
static void
gst_audio_panorama_transform_s2s_float_simple (GstAudioPanorama * filter,
gfloat * idata, gfloat * odata, guint num_samples)
gst_audio_panorama_s2s_float_simple (gfloat pan, gfloat * idata,
gfloat * odata, guint n)
{
guint i;
gfloat lival, rival, pan;
if (filter->panorama > 0.0) {
pan = 1.0 - filter->panorama;
for (i = 0; i < num_samples; i++) {
lival = *idata++;
rival = *idata++;
*odata++ = lival * pan;
*odata++ = rival;
}
if (pan == 0.0) {
audiopanoramam_orc_process_f32_ch2_none (odata, idata, n);
} else if (pan > 0.0) {
gfloat lpan = 1.0 - pan;
audiopanoramam_orc_process_f32_ch2_sim_left (odata, idata, lpan, n);
} else {
pan = 1.0 + filter->panorama;
for (i = 0; i < num_samples; i++) {
lival = *idata++;
rival = *idata++;
*odata++ = lival;
*odata++ = rival * pan;
}
gfloat rpan = 1.0 + pan;
audiopanoramam_orc_process_f32_ch2_sim_right (odata, idata, rpan, n);
}
}
@ -638,13 +523,13 @@ gst_audio_panorama_transform (GstBaseTransform * base, GstBuffer * inbuf,
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP))) {
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
memset (outmap.data, 0, outmap.size);
orc_memset (outmap.data, 0, outmap.size);
} else {
/* output always stereo, input mono or stereo,
* and info describes input format */
guint num_samples = outmap.size / (2 * GST_AUDIO_INFO_BPS (&filter->info));
filter->process (filter, inmap.data, outmap.data, num_samples);
filter->process (filter->panorama, inmap.data, outmap.data, num_samples);
}
gst_buffer_unmap (inbuf, &inmap);

View file

@ -37,7 +37,7 @@ G_BEGIN_DECLS
typedef struct _GstAudioPanorama GstAudioPanorama;
typedef struct _GstAudioPanoramaClass GstAudioPanoramaClass;
typedef void (*GstAudioPanoramaProcessFunc)(GstAudioPanorama*, guint8*, guint8*, guint);
typedef void (*GstAudioPanoramaProcessFunc)(gfloat, guint8*, guint8*, guint);
typedef enum
{

View file

@ -0,0 +1,258 @@
# pass through functions
.function audiopanoramam_orc_process_s16_ch1_none
.source 2 s1 gint16
.dest 4 d1 gint16
mergewl d1 s1 s1
.function audiopanoramam_orc_process_f32_ch1_none
.source 4 s1 gfloat
.dest 8 d1 gfloat
mergelq d1 s1 s1
.function audiopanoramam_orc_process_s16_ch2_none
.source 4 s1 gint16
.dest 4 d1 gint16
x2 copyw d1 s1
.function audiopanoramam_orc_process_f32_ch2_none
.source 8 s1 gfloat
.dest 8 d1 gfloat
x2 copyl d1 s1
# psychoacoustic processing function
.function audiopanoramam_orc_process_s16_ch1_psy
.source 2 s1 gint16
.dest 4 d1 gint16
.floatparam 4 lpan
.floatparam 4 rpan
.temp 8 t1
.temp 4 left
.temp 4 right
convswl left s1
convlf left left
mulf right left rpan
mulf left left lpan
mergelq t1 left right
x2 convfl t1 t1
x2 convssslw d1 t1
.function audiopanoramam_orc_process_f32_ch1_psy
.source 4 s1 gfloat
.dest 8 d1 gfloat
.floatparam 4 lpan
.floatparam 4 rpan
.temp 4 left
.temp 4 right
mulf right s1 rpan
mulf left s1 lpan
mergelq d1 left right
.function audiopanoramam_orc_process_s16_ch2_psy_right
.source 4 s1 gint16
.dest 4 d1 gint16
.floatparam 4 llpan
.floatparam 4 rlpan
.temp 8 t1
.temp 4 left
.temp 4 right
.temp 4 right1
x2 convswl t1 s1
x2 convlf t1 t1
select0ql left t1
select1ql right t1
mulf right1 left rlpan
mulf left left llpan
addf right right1 right
mergelq t1 left right
x2 convfl t1 t1
x2 convssslw d1 t1
.function audiopanoramam_orc_process_s16_ch2_psy_left
.source 4 s1 gint16
.dest 4 d1 gint16
.floatparam 4 lrpan
.floatparam 4 rrpan
.temp 8 t1
.temp 4 left
.temp 4 left1
.temp 4 right
x2 convswl t1 s1
x2 convlf t1 t1
select0ql left t1
select1ql right t1
mulf left1 right lrpan
mulf right right rrpan
addf left left1 left
mergelq t1 left right
x2 convfl t1 t1
x2 convssslw d1 t1
.function audiopanoramam_orc_process_f32_ch2_psy_right
.source 8 s1 gfloat
.dest 8 d1 gfloat
.floatparam 4 llpan
.floatparam 4 rlpan
.temp 4 left
.temp 4 right
.temp 4 right1
select0ql left s1
select1ql right s1
mulf right1 left rlpan
mulf left left llpan
addf right right1 right
mergelq d1 left right
.function audiopanoramam_orc_process_f32_ch2_psy_left
.source 8 s1 gfloat
.dest 8 d1 gfloat
.floatparam 4 lrpan
.floatparam 4 rrpan
.temp 4 left
.temp 4 left1
.temp 4 right
select0ql left s1
select1ql right s1
mulf left1 right lrpan
mulf right right rrpan
addf left left1 left
mergelq d1 left right
# simple processing functions
.function audiopanoramam_orc_process_s16_ch1_sim_right
.source 2 s1 gint16
.dest 4 d1 gint16
.floatparam 4 rpan
.temp 8 t1
.temp 4 left
.temp 4 right
convswl left s1
convlf left left
mulf right left rpan
mergelq t1 left right
x2 convfl t1 t1
x2 convssslw d1 t1
.function audiopanoramam_orc_process_s16_ch1_sim_left
.source 2 s1 gint16
.dest 4 d1 gint16
.floatparam 4 lpan
.temp 8 t1
.temp 4 left
.temp 4 right
convswl right s1
convlf right right
mulf left right lpan
mergelq t1 left right
x2 convfl t1 t1
x2 convssslw d1 t1
.function audiopanoramam_orc_process_s16_ch2_sim_right
.source 4 s1 gint16
.dest 4 d1 gint16
.floatparam 4 rpan
.temp 8 t1
.temp 4 left
.temp 4 right
x2 convswl t1 s1
x2 convlf t1 t1
select0ql left t1
select1ql right t1
mulf right right rpan
mergelq t1 left right
x2 convfl t1 t1
x2 convssslw d1 t1
.function audiopanoramam_orc_process_s16_ch2_sim_left
.source 4 s1 gint16
.dest 4 d1 gint16
.floatparam 4 lpan
.temp 8 t1
.temp 4 left
.temp 4 right
x2 convswl t1 s1
x2 convlf t1 t1
select0ql left t1
select1ql right t1
mulf left left lpan
mergelq t1 left right
x2 convfl t1 t1
x2 convssslw d1 t1
.function audiopanoramam_orc_process_f32_ch1_sim_right
.source 4 s1 gfloat
.dest 8 d1 gfloat
.floatparam 4 rpan
.temp 4 left
.temp 4 right
copyl left s1
mulf right s1 rpan
mergelq d1 left right
.function audiopanoramam_orc_process_f32_ch1_sim_left
.source 4 s1 gfloat
.dest 8 d1 gfloat
.floatparam 4 lpan
.temp 4 left
.temp 4 right
mulf left s1 lpan
copyl right s1
mergelq d1 left right
.function audiopanoramam_orc_process_f32_ch2_sim_right
.source 8 s1 gfloat
.dest 8 d1 gfloat
.floatparam 4 rpan
.temp 4 left
.temp 4 right
select0ql left s1
select1ql right s1
mulf right right rpan
mergelq d1 left right
.function audiopanoramam_orc_process_f32_ch2_sim_left
.source 8 s1 gfloat
.dest 8 d1 gfloat
.floatparam 4 lpan
.temp 4 left
.temp 4 right
select0ql left s1
select1ql right s1
mulf left left lpan
mergelq d1 left right