3b41e2c87ba127c8b5ced6420fa41564447ddf3e
[vlc.git] / 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     add_submodule ()
34         set_capability ("audio filter", 20)
35         set_callbacks (Open, NULL)
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 (!(vlc_CPU () & CPU_CAPABILITY_NEON))
48         return VLC_EGENERIC;
49     if (!AOUT_FMTS_SIMILAR (&filter->input, &filter->output))
50         return VLC_EGENERIC;
51
52     switch (filter->input.i_format)
53     {
54         case VLC_CODEC_FL32:
55             switch (filter->output.i_format)
56             {
57                 case VLC_CODEC_FI32:
58                     filter->pf_do_work = Do_F32_S32;
59                     break;
60                 default:
61                     return VLC_EGENERIC;
62             }
63             break;
64
65         case VLC_CODEC_FI32:
66             switch (filter->output.i_format)
67             {
68                 case VLC_CODEC_S16N:
69                     filter->pf_do_work = Do_S32_S16;
70                     break;
71                 default:
72                     return VLC_EGENERIC;
73             }
74             break;
75         default:
76             return VLC_EGENERIC;
77     }
78
79     filter->b_in_place = true;
80     return 0;
81 }
82
83 /**
84  * Half-precision floating point to signed fixed point conversion.
85  */
86 static void Do_F32_S32 (aout_instance_t *aout, aout_filter_t *filter,
87                         aout_buffer_t *inbuf, aout_buffer_t *outbuf)
88 {
89     unsigned nb_samples = inbuf->i_nb_samples
90                      * aout_FormatNbChannels (&filter->input);
91     const float *inp = (float *)inbuf->p_buffer;
92     const float *endp = inp + nb_samples;
93     int32_t *outp = (int32_t *)outbuf->p_buffer;
94
95     if (nb_samples & 1)
96     {
97         asm volatile (
98             "vldr.32 s0, [%[inp]]\n"
99             "vcvt.s32.f32 d0, d0, #28\n"
100             "vstr.32 s0, [%[outp]]\n"
101             :
102             : [outp] "r" (outp), [inp] "r" (inp)
103             : "d0", "memory");
104         outp++;
105         inp++;
106     }
107
108     if (nb_samples & 2)
109         asm volatile (
110             "vld1.f32 {d0}, [%[inp]]!\n"
111             "vcvt.s32.f32 d0, d0, #28\n"
112             "vst1.s32 {d0}, [%[outp]]!\n"
113             : [outp] "+r" (outp), [inp] "+r" (inp)
114             :
115             : "d0", "memory");
116
117     if (nb_samples & 4)
118         asm volatile (
119             "vld1.f32 {q0}, [%[inp]]!\n"
120             "vcvt.s32.f32 q0, q0, #28\n"
121             "vst1.s32 {q0}, [%[outp]]!\n"
122             : [outp] "+r" (outp), [inp] "+r" (inp)
123             :
124             : "q0", "memory");
125
126     while (inp != endp)
127         asm volatile (
128             "vld1.f32 {q0-q1}, [%[inp]]!\n"
129             "vcvt.s32.f32 q0, q0, #28\n"
130             "vcvt.s32.f32 q1, q1, #28\n"
131             "vst1.s32 {q0-q1}, [%[outp]]!\n"
132             : [outp] "+r" (outp), [inp] "+r" (inp)
133             :
134             : "q0", "q1", "memory");
135
136     outbuf->i_nb_samples = inbuf->i_nb_samples;
137     outbuf->i_nb_bytes = inbuf->i_nb_bytes;
138     (void) aout;
139 }
140
141 /**
142  * Signed 32-bits fixed point to signed 16-bits integer
143  */
144 static void Do_S32_S16 (aout_instance_t *aout, aout_filter_t *filter,
145                         aout_buffer_t *inbuf, aout_buffer_t *outbuf)
146 {
147     unsigned nb_samples = inbuf->i_nb_samples
148                      * aout_FormatNbChannels (&filter->input);
149     int32_t *inp = (int32_t *)inbuf->p_buffer;
150     const int32_t *endp = inp + nb_samples;
151     int16_t *outp = (int16_t *)outbuf->p_buffer;
152
153     while (nb_samples & 3)
154     {
155         const int16_t roundup = 1 << 12;
156         asm volatile (
157             "qadd r0, %[inv], %[roundup]\n"
158             "ssat %[outv], #16, r0, asr #13\n"
159             : [outv] "=r" (*outp)
160             : [inv] "r" (*inp), [roundup] "r" (roundup)
161             : "r0");
162         inp++;
163         outp++;
164         nb_samples--;
165     }
166
167     if (nb_samples & 4)
168         asm volatile (
169             "vld1.s32 {q0}, [%[inp]]!\n"
170             "vrshrn.i32 d0, q0, #13\n"
171             "vst1.s16 {d0}, [%[outp]]!\n"
172             : [outp] "+r" (outp), [inp] "+r" (inp)
173             :
174             : "q0", "memory");
175
176     while (inp != endp)
177         asm volatile (
178             "vld1.s32 {q0-q1}, [%[inp]]!\n"
179             "vrshrn.s32 d0, q0, #13\n"
180             "vrshrn.s32 d1, q1, #13\n"
181             "vst1.s16 {q0}, [%[outp]]!\n"
182             : [outp] "+r" (outp), [inp] "+r" (inp)
183             :
184             : "q0", "q1", "memory");
185
186     outbuf->i_nb_samples = inbuf->i_nb_samples;
187     outbuf->i_nb_bytes = inbuf->i_nb_bytes / 2;
188     (void) aout;
189 }