MythTV master
audiooutpututil.cpp
Go to the documentation of this file.
1#include "audiooutpututil.h"
2
3#include <cstdint>
4#include <limits> // workaround QTBUG-90395
5
6#include <QtGlobal>
7#include <QtEndian>
8
10
11#include "audioconvert.h"
12#include "mythaverror.h"
13#include "mythavframe.h"
14
15extern "C" {
16#include "libavcodec/avcodec.h"
17}
18#include "pink.h"
19
20#define LOC QString("AOUtil: ")
21
22#ifdef Q_PROCESSOR_X86
23// Check cpuid for SSE2 support on x86 / x86_64
24static inline bool sse2_check()
25{
26#ifdef Q_PROCESSOR_X86_64
27 return true;
28#else
29 static int has_sse2 = -1;
30 if (has_sse2 != -1)
31 return (bool)has_sse2;
32 __asm__(
33 // -fPIC - we may not clobber ebx/rbx
34 "push %%ebx \n\t"
35 "mov $1, %%eax \n\t"
36 "cpuid \n\t"
37 "and $0x4000000, %%edx \n\t"
38 "shr $26, %%edx \n\t"
39 "pop %%ebx \n\t"
40 :"=d"(has_sse2)
41 ::"%eax","%ecx"
42 );
43 return (bool)has_sse2;
44#endif
45}
46#endif //Q_PROCESSOR_X86
47
53{
54#ifdef Q_PROCESSOR_X86
55 return sse2_check();
56#else
57 return false;
58#endif
59}
60
66int AudioOutputUtil::toFloat(AudioFormat format, void *out, const void *in,
67 int bytes)
68{
69 return AudioConvert::toFloat(format, out, in, bytes);
70}
71
77int AudioOutputUtil::fromFloat(AudioFormat format, void *out, const void *in,
78 int bytes)
79{
80 return AudioConvert::fromFloat(format, out, in, bytes);
81}
82
86void AudioOutputUtil::MonoToStereo(void *dst, const void *src, int samples)
87{
89}
90
97void AudioOutputUtil::AdjustVolume(void *buf, int len, int volume,
98 bool music, bool upmix)
99{
100 float g = volume / 100.0F;
101 auto *fptr = (float *)buf;
102 int samples = len >> 2;
103 int i = 0;
104
105 // Should be exponential - this'll do
106 g *= g;
107
108 // Try to ~ match stereo volume when upmixing
109 if (upmix)
110 g *= 1.5F;
111
112 // Music is relatively loud
113 if (music)
114 g *= 0.4F;
115
116 if (g == 1.0F)
117 return;
118
119#ifdef Q_PROCESSOR_X86
120 if (sse2_check() && samples >= 16)
121 {
122 int loops = samples >> 4;
123 i = loops << 4;
124
125 __asm__ volatile (
126 "movss %2, %%xmm0 \n\t"
127 "punpckldq %%xmm0, %%xmm0 \n\t"
128 "punpckldq %%xmm0, %%xmm0 \n\t"
129 "1: \n\t"
130 "movups (%0), %%xmm1 \n\t"
131 "movups 16(%0), %%xmm2 \n\t"
132 "mulps %%xmm0, %%xmm1 \n\t"
133 "movups 32(%0), %%xmm3 \n\t"
134 "mulps %%xmm0, %%xmm2 \n\t"
135 "movups 48(%0), %%xmm4 \n\t"
136 "mulps %%xmm0, %%xmm3 \n\t"
137 "movups %%xmm1, (%0) \n\t"
138 "mulps %%xmm0, %%xmm4 \n\t"
139 "movups %%xmm2, 16(%0) \n\t"
140 "movups %%xmm3, 32(%0) \n\t"
141 "movups %%xmm4, 48(%0) \n\t"
142 "add $64, %0 \n\t"
143 "sub $1, %%ecx \n\t"
144 "jnz 1b \n\t"
145 :"+r"(fptr)
146 :"c"(loops),"m"(g)
147 :"xmm0","xmm1","xmm2","xmm3","xmm4"
148 );
149 }
150#endif //Q_PROCESSOR_X86
151 for (; i < samples; i++)
152 *fptr++ *= g;
153}
154
155template <class AudioDataType>
156void tMuteChannel(AudioDataType *buffer, int channels, int ch, int frames)
157{
158 AudioDataType *s1 = buffer + ch;
159 AudioDataType *s2 = buffer - ch + 1;
160
161 for (int i = 0; i < frames; i++)
162 {
163 *s1 = *s2;
164 s1 += channels;
165 s2 += channels;
166 }
167}
168
175void AudioOutputUtil::MuteChannel(int obits, int channels, int ch,
176 void *buffer, int bytes)
177{
178 int frames = bytes / ((obits >> 3) * channels);
179
180 if (obits == 8)
181 tMuteChannel((uint8_t *)buffer, channels, ch, frames);
182 else if (obits == 16)
183 tMuteChannel((short *)buffer, channels, ch, frames);
184 else
185 tMuteChannel((int *)buffer, channels, ch, frames);
186}
187
188char *AudioOutputUtil::GeneratePinkFrames(char *frames, int channels,
189 int channel, int count, int bits)
190{
191 pink_noise_t pink{};
192
194
195 auto *samp16 = (int16_t*) frames;
196 auto *samp32 = (int32_t*) frames;
197
198 while (count-- > 0)
199 {
200 for(int chn = 0 ; chn < channels; chn++)
201 {
202 if (chn==channel)
203 {
204 /* Don't use MAX volume */
205 double res = generate_pink_noise_sample(&pink) *
206 static_cast<float>(0x03fffffff);
207 int32_t ires = res;
208 if (bits == 16)
209 *samp16++ = qToLittleEndian<qint16>(ires >> 16);
210 else
211 *samp32++ = qToLittleEndian<qint32>(ires);
212 }
213 else
214 {
215 if (bits == 16)
216 *samp16++ = 0;
217 else
218 *samp32++ = 0;
219 }
220 }
221 }
222 return frames;
223}
224
232int AudioOutputUtil::DecodeAudio(AVCodecContext *ctx,
233 uint8_t *buffer, int &data_size,
234 const AVPacket *pkt)
235{
236 MythAVFrame frame;
237 bool got_frame = false;
238
239 data_size = 0;
240 if (!frame)
241 {
242 return AVERROR(ENOMEM);
243 }
244
245// SUGGESTION
246// Now that avcodec_decode_audio4 is deprecated and replaced
247// by 2 calls (receive frame and send packet), this could be optimized
248// into separate routines or separate threads.
249// Also now that it always consumes a whole buffer some code
250// in the caller may be able to be optimized.
251 int ret = avcodec_receive_frame(ctx,frame);
252 if (ret == 0)
253 got_frame = true;
254 if (ret == AVERROR(EAGAIN))
255 ret = 0;
256 if (ret == 0)
257 ret = avcodec_send_packet(ctx, pkt);
258 if (ret == AVERROR(EAGAIN))
259 ret = 0;
260 else if (ret < 0)
261 {
262 std::string error;
263 LOG(VB_AUDIO, LOG_ERR, LOC +
264 QString("audio decode error: %1 (%2)")
266 .arg(got_frame));
267 return ret;
268 }
269 else
270 {
271 ret = pkt->size;
272 }
273
274 if (!got_frame)
275 {
276 LOG(VB_AUDIO, LOG_DEBUG, LOC +
277 QString("audio decode, no frame decoded (%1)").arg(ret));
278 return ret;
279 }
280
281 auto format = (AVSampleFormat)frame->format;
282
283 data_size = frame->nb_samples * frame->ch_layout.nb_channels * av_get_bytes_per_sample(format);
284
285 if (av_sample_fmt_is_planar(format))
286 {
287 InterleaveSamples(AudioOutputSettings::AVSampleFormatToFormat(format, ctx->bits_per_raw_sample),
288 frame->ch_layout.nb_channels, buffer, (const uint8_t **)frame->extended_data,
289 data_size);
290 }
291 else
292 {
293 // data is already compacted... simply copy it
294 memcpy(buffer, frame->extended_data[0], data_size);
295 }
296
297 return ret;
298}
299
305 uint8_t *output, const uint8_t *input,
306 int data_size)
307{
308 AudioConvert::DeinterleaveSamples(format, channels, output, input, data_size);
309}
310
317 uint8_t *output, const uint8_t * const *input,
318 int data_size)
319{
320 AudioConvert::InterleaveSamples(format, channels, output, input, data_size);
321}
322
328 uint8_t *output, const uint8_t *input,
329 int data_size)
330{
331 AudioConvert::InterleaveSamples(format, channels, output, input, data_size);
332}
#define LOC
void tMuteChannel(AudioDataType *buffer, int channels, int ch, int frames)
static int toFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert integer samples to floats.
void DeinterleaveSamples(int channels, uint8_t *output, const uint8_t *input, int data_size)
static void MonoToStereo(void *dst, const void *src, int samples)
Convert a mono stream to stereo by copying and interleaving samples.
static int fromFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert float samples to integers.
void InterleaveSamples(int channels, uint8_t *output, const uint8_t *const *input, int data_size)
static AudioFormat AVSampleFormatToFormat(AVSampleFormat format, int bits=0)
Return AVSampleFormat closest equivalent to AudioFormat.
static void MuteChannel(int obits, int channels, int ch, void *buffer, int bytes)
Mute individual channels through mono->stereo duplication.
static void MonoToStereo(void *dst, const void *src, int samples)
Convert a mono stream to stereo by copying and interleaving samples.
static char * GeneratePinkFrames(char *frames, int channels, int channel, int count, int bits=16)
static void DeinterleaveSamples(AudioFormat format, int channels, uint8_t *output, const uint8_t *input, int data_size)
Deinterleave input samples Deinterleave audio samples and compact them.
static void AdjustVolume(void *buffer, int len, int volume, bool music, bool upmix)
Adjust the volume of samples.
static bool has_optimized_SIMD()
Returns true if the processor supports MythTV's optimized SIMD for AudioOutputUtil/AudioConvert.
static int toFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert integer samples to floats.
static int fromFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert float samples to integers.
static void InterleaveSamples(AudioFormat format, int channels, uint8_t *output, const uint8_t *const *input, int data_size)
Interleave input samples Planar audio is contained in array of pointers Interleave audio samples (con...
static int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, const AVPacket *pkt)
DecodeAudio Decode an audio packet, and compact it if data is planar Return negative error code if an...
MythAVFrame little utility class that act as a safe way to allocate an AVFrame which can then be allo...
Definition: mythavframe.h:27
static const std::array< const uint64_t, 4 > samples
Definition: element.cpp:46
char * av_make_error_stdstring(std::string &errbuf, int errnum)
A C++ equivalent to av_make_error_string.
Definition: mythaverror.cpp:42
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def error(message)
Definition: smolt.py:409
void initialize_pink_noise(pink_noise_t *pink, int num_rows)
Definition: pink.cpp:42
float generate_pink_noise_sample(pink_noise_t *pink)
Definition: pink.cpp:56
#define output