]> git.sesse.net Git - vlc/blob - modules/audio_filter/converter/neon.c
7f84dce64d1bfe8610efb3ef7222af82f3d11f70
[vlc] / modules / audio_filter / converter / neon.c
1 /*****************************************************************************
2  * arm_neon.c: NEON assembly optimized audio conversions
3  *****************************************************************************
4  * Copyright (C) 2009 RĂ©mi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <vlc_common.h>
26 #include <vlc_plugin.h>
27 #include <vlc_aout.h>
28
29 static int Open (vlc_object_t *);
30
31 vlc_module_begin ()
32     set_description (N_("ARM NEON audio format conversions") )
33     set_capability ("audio filter", 20)
34     set_callbacks (Open, NULL)
35     add_requirement (NEON)
36 vlc_module_end ()
37
38 static void Do_F32_S32 (aout_instance_t *, aout_filter_t *,
39                         aout_buffer_t *, aout_buffer_t *);
40 static void Do_S32_S16 (aout_instance_t *, aout_filter_t *,
41                         aout_buffer_t *, aout_buffer_t *);
42
43 static int Open (vlc_object_t *obj)
44 {
45     aout_filter_t *filter = (aout_filter_t *)obj;
46
47     if (!AOUT_FMTS_SIMILAR (&filter->input, &filter->output))
48         return VLC_EGENERIC;
49
50     switch (filter->input.i_format)
51     {
52         case VLC_CODEC_FL32:
53             switch (filter->output.i_format)
54             {
55                 case VLC_CODEC_FI32:
56                     filter->pf_do_work = Do_F32_S32;
57                     break;
58                 default:
59                     return VLC_EGENERIC;
60             }
61             break;
62
63         case VLC_CODEC_FI32:
64             switch (filter->output.i_format)
65             {
66                 case VLC_CODEC_S16N:
67                     filter->pf_do_work = Do_S32_S16;
68                     break;
69                 default:
70                     return VLC_EGENERIC;
71             }
72             break;
73         default:
74             return VLC_EGENERIC;
75     }
76
77     filter->b_in_place = true;
78     return 0;
79 }
80
81 /**
82  * Half-precision floating point to signed fixed point conversion.
83  */
84 static void Do_F32_S32 (aout_instance_t *aout, aout_filter_t *filter,
85                         aout_buffer_t *inbuf, aout_buffer_t *outbuf)
86 {
87     unsigned nb_samples = inbuf->i_nb_samples
88                      * aout_FormatNbChannels (&filter->input);
89     const float *inp = (float *)inbuf->p_buffer;
90     const float *endp = inp + nb_samples;
91     int32_t *outp = (int32_t *)outbuf->p_buffer;
92
93     if (nb_samples & 1)
94     {
95         asm volatile (
96             "vldr.32 s0, [%[inp]]\n"
97             "vcvt.s32.f32 d0, d0, #28\n"
98             "vstr.32 s0, [%[outp]]\n"
99             :
100             : [outp] "r" (outp), [inp] "r" (inp)
101             : "d0", "memory");
102         outp++;
103         inp++;
104     }
105
106     if (nb_samples & 2)
107         asm volatile (
108             "vld1.f32 {d0}, [%[inp]]!\n"
109             "vcvt.s32.f32 d0, d0, #28\n"
110             "vst1.s32 {d0}, [%[outp]]!\n"
111             : [outp] "+r" (outp), [inp] "+r" (inp)
112             :
113             : "d0", "memory");
114
115     if (nb_samples & 4)
116         asm volatile (
117             "vld1.f32 {q0}, [%[inp]]!\n"
118             "vcvt.s32.f32 q0, q0, #28\n"
119             "vst1.s32 {q0}, [%[outp]]!\n"
120             : [outp] "+r" (outp), [inp] "+r" (inp)
121             :
122             : "q0", "memory");
123
124     while (inp != endp)
125         asm volatile (
126             "vld1.f32 {q0-q1}, [%[inp]]!\n"
127             "vcvt.s32.f32 q0, q0, #28\n"
128             "vcvt.s32.f32 q1, q1, #28\n"
129             "vst1.s32 {q0-q1}, [%[outp]]!\n"
130             : [outp] "+r" (outp), [inp] "+r" (inp)
131             :
132             : "q0", "q1", "memory");
133
134     outbuf->i_nb_samples = inbuf->i_nb_samples;
135     outbuf->i_nb_bytes = inbuf->i_nb_bytes;
136     (void) aout;
137 }
138
139 /**
140  * Signed 32-bits fixed point to signed 16-bits integer
141  */
142 static void Do_S32_S16 (aout_instance_t *aout, aout_filter_t *filter,
143                         aout_buffer_t *inbuf, aout_buffer_t *outbuf)
144 {
145     unsigned nb_samples = inbuf->i_nb_samples
146                      * aout_FormatNbChannels (&filter->input);
147     int32_t *inp = (int32_t *)inbuf->p_buffer;
148     const int32_t *endp = inp + nb_samples;
149     int16_t *outp = (int16_t *)outbuf->p_buffer;
150
151     while (nb_samples & 3)
152     {
153         const int16_t roundup = 1 << 12;
154         asm volatile (
155             "qadd r0, %[inv], %[roundup]\n"
156             "ssat %[outv], #16, r0, asr #13\n"
157             : [outv] "=r" (*outp)
158             : [inv] "r" (*inp), [roundup] "r" (roundup)
159             : "r0");
160         inp++;
161         outp++;
162         nb_samples--;
163     }
164
165     if (nb_samples & 4)
166         asm volatile (
167             "vld1.s32 {q0}, [%[inp]]!\n"
168             "vrshrn.i32 d0, q0, #13\n"
169             "vst1.s16 {d0}, [%[outp]]!\n"
170             : [outp] "+r" (outp), [inp] "+r" (inp)
171             :
172             : "q0", "memory");
173
174     if (nb_samples & 8)
175         asm volatile (
176             "vld1.s32 {q0-q1}, [%[inp]]!\n"
177             "vrshrn.i32 d0, q0, #13\n"
178             "vrshrn.i32 d1, q1, #13\n"
179             "vst1.s16 {q0}, [%[outp]]!\n"
180             : [outp] "+r" (outp), [inp] "+r" (inp)
181             :
182             : "q0", "q1", "memory");
183
184     while (inp != endp)
185         asm volatile (
186             "vld1.s32 {q0-q1}, [%[inp]]!\n"
187             "vld1.s32 {q2-q3}, [%[inp]]!\n"
188             "vrshrn.s32 d0, q0, #13\n"
189             "vrshrn.s32 d1, q1, #13\n"
190             "vrshrn.s32 d2, q2, #13\n"
191             "vrshrn.s32 d3, q3, #13\n"
192             "vst1.s16 {q0-q1}, [%[outp]]!\n"
193             : [outp] "+r" (outp), [inp] "+r" (inp)
194             :
195             : "q0", "q1", "q2", "q3", "memory");
196
197     outbuf->i_nb_samples = inbuf->i_nb_samples;
198     outbuf->i_nb_bytes = inbuf->i_nb_bytes / 2;
199     (void) aout;
200 }