From 85e621dec6a56f4801add8a45b6e77ff67a92b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Wed, 4 Oct 2017 00:36:26 +0200 Subject: [PATCH] qtractor: add some more ARM NEON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Müller --- ...h => 0004-Add-ARM-NEON-acceleration.patch} | 173 ++++++++++++++++-- ...eleration-for-time-stretch-not-yet-t.patch | 95 ++++++++++ .../qtractor/qtractor_0.8.4.bb | 3 +- 3 files changed, 258 insertions(+), 13 deletions(-) rename recipes-misc/recipes-multimedia/qtractor/files/{0004-Add-ARM-NEON-intrinsics.patch => 0004-Add-ARM-NEON-acceleration.patch} (52%) create mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0005-Add-ARM-NEON-acceleration-for-time-stretch-not-yet-t.patch diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0004-Add-ARM-NEON-intrinsics.patch b/recipes-misc/recipes-multimedia/qtractor/files/0004-Add-ARM-NEON-acceleration.patch similarity index 52% rename from recipes-misc/recipes-multimedia/qtractor/files/0004-Add-ARM-NEON-intrinsics.patch rename to recipes-misc/recipes-multimedia/qtractor/files/0004-Add-ARM-NEON-acceleration.patch index 2f4a92a4..1fc5284f 100644 --- a/recipes-misc/recipes-multimedia/qtractor/files/0004-Add-ARM-NEON-intrinsics.patch +++ b/recipes-misc/recipes-multimedia/qtractor/files/0004-Add-ARM-NEON-acceleration.patch @@ -1,25 +1,33 @@ -From 1af04054ebaac13e11559948f212080ad19ba903 Mon Sep 17 00:00:00 2001 +From 2dfcb12439328353cac5846e81872b8484c2403e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= -Date: Wed, 15 Feb 2017 22:10:28 +0100 -Subject: [PATCH] Add ARM NEON intrinsics +Date: Tue, 3 Oct 2017 21:39:19 +0200 +Subject: [PATCH 1/2] Add ARM NEON acceleration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -* More or less a 1/1 copy of SSE implementation +* More or less a 1/1 copy of SSE implementation. Where possible make use of + multiply/accumulate (vmla) * Runtime NEON detection is not easy to implement. The only implementations I - found did not work properly [1]. So let's trust the compiler. + found did not work properly e.g [1]. So let's trust the compiler. +* On RaspberryPi3 test arrangement with 8 Midi-Tracks / 4 LV2-Plugins the idle + load is reduced from 30% -> 29%. [1] http://git.openembedded.org/meta-openembedded/commit/meta-oe?id=cae9cafb9c536fd5ea40d1457c0ee1fcd6a6aa43 +Upstream-Status: Submitted [2] + +[2] https://github.com/rncbc/qtractor/pull/81 + Signed-off-by: Andreas Müller --- - src/qtractorAudioEngine.cpp | 40 +++++++++++++++++++ - src/qtractorAudioMonitor.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++-- - 2 files changed, 131 insertions(+), 4 deletions(-) + src/qtractorAudioEngine.cpp | 40 ++++++++++++++++ + src/qtractorAudioMonitor.cpp | 95 ++++++++++++++++++++++++++++++++++-- + src/qtractorInsertPlugin.cpp | 111 +++++++++++++++++++++++++++++++++++++++---- + 3 files changed, 234 insertions(+), 12 deletions(-) diff --git a/src/qtractorAudioEngine.cpp b/src/qtractorAudioEngine.cpp -index e34da4b..6bbd65e 100644 +index 55dd2c7..e79e272 100644 --- a/src/qtractorAudioEngine.cpp +++ b/src/qtractorAudioEngine.cpp @@ -118,6 +118,38 @@ static inline void sse_buffer_add ( @@ -72,7 +80,7 @@ index e34da4b..6bbd65e 100644 m_pfnBufferAdd = std_buffer_add; } -@@ -2135,6 +2171,10 @@ qtractorAudioBus::qtractorAudioBus ( +@@ -2133,6 +2169,10 @@ qtractorAudioBus::qtractorAudioBus ( m_pfnBufferAdd = sse_buffer_add; else #endif @@ -84,7 +92,7 @@ index e34da4b..6bbd65e 100644 } diff --git a/src/qtractorAudioMonitor.cpp b/src/qtractorAudioMonitor.cpp -index ec791f4..82cd5ef 100644 +index e9a0fd1..19f212b 100644 --- a/src/qtractorAudioMonitor.cpp +++ b/src/qtractorAudioMonitor.cpp @@ -53,7 +53,6 @@ static inline bool sse_enabled (void) @@ -208,6 +216,147 @@ index ec791f4..82cd5ef 100644 setChannels(iChannels); } +diff --git a/src/qtractorInsertPlugin.cpp b/src/qtractorInsertPlugin.cpp +index 40e21e3..a5f909c 100644 +--- a/src/qtractorInsertPlugin.cpp ++++ b/src/qtractorInsertPlugin.cpp +@@ -155,6 +155,94 @@ static inline void sse_process_add ( + + #endif + ++#if defined(__ARM_NEON__) ++#include "arm_neon.h" ++ ++// NEON enabled processor versions. ++static inline void neon_process_gain ( ++ float **ppFrames, unsigned int iFrames, ++ unsigned short iChannels, float fGain ) ++{ ++ float32x4_t vGain = vdupq_n_f32(fGain); ++ ++ for (unsigned short i = 0; i < iChannels; ++i) { ++ float *pFrames = ppFrames[i]; ++ unsigned int nframes = iFrames; ++ for (; (long(pFrames) & 15) && (nframes > 0); --nframes) ++ *pFrames++ *= fGain; ++ for (; nframes >= 4; nframes -= 4) { ++ vst1q_f32(pFrames, ++ vmulq_f32( ++ vld1q_f32(pFrames), vGain ++ ) ++ ); ++ pFrames += 4; ++ } ++ for (; nframes > 0; --nframes) ++ *pFrames++ *= fGain; ++ } ++} ++ ++static inline void neon_process_dry_wet ( ++ float **ppBuffer, float **ppFrames, unsigned int iFrames, ++ unsigned short iChannels, float fDry, float fWet ) ++{ ++ float32x4_t vDry = vdupq_n_f32(fDry); ++ float32x4_t vWet = vdupq_n_f32(fWet); ++ ++ for (unsigned short i = 0; i < iChannels; ++i) { ++ float *pBuffer = ppBuffer[i]; ++ float *pFrames = ppFrames[i]; ++ unsigned int nframes = iFrames; ++ for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) { ++ *pBuffer *= fWet; ++ *pBuffer++ += fDry * *pFrames++; ++ } ++ for (; nframes >= 4; nframes -= 4) { ++ float32x4_t vBuffer = vld1q_f32(pBuffer); ++ vBuffer = vmulq_f32(vBuffer, vWet); ++ float32x4_t vFrames = vld1q_f32(pFrames); ++ // Vr[i] := Va[i] + Vb[i] * Vc[i] ++ vBuffer = vmlaq_f32(vBuffer, vDry, vFrames); ++ vst1q_f32(pBuffer, vBuffer); ++ pFrames += 4; ++ pBuffer += 4; ++ } ++ for (; nframes > 0; --nframes) { ++ *pBuffer *= fWet; ++ *pBuffer++ += fDry * *pFrames++; ++ } ++ } ++} ++ ++static inline void neon_process_add ( ++ float **ppBuffer, float **ppFrames, unsigned int iFrames, ++ unsigned short iChannels, float fGain ) ++{ ++ float32x4_t vGain = vdupq_n_f32(fGain); ++ ++ for (unsigned short i = 0; i < iChannels; ++i) { ++ float *pBuffer = ppBuffer[i]; ++ float *pFrames = ppFrames[i]; ++ unsigned int nframes = iFrames; ++ for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) ++ *pBuffer++ += fGain * *pFrames++; ++ for (; nframes >= 4; nframes -= 4) { ++ float32x4_t vBuffer = vld1q_f32(pBuffer); ++ float32x4_t vFrames = vld1q_f32(pFrames); ++ //Vr[i] := Va[i] + Vb[i] * Vc[i] ++ vBuffer = vmlaq_f32(vBuffer, vGain, vFrames); ++ vst1q_f32(pBuffer, vBuffer); ++ pFrames += 4; ++ pBuffer += 4; ++ } ++ for (; nframes > 0; --nframes) ++ *pBuffer++ += fGain * *pFrames++; ++ } ++} ++ ++#endif ++ + + // Standard processor versions. + static inline void std_process_gain ( +@@ -367,13 +455,17 @@ qtractorAudioInsertPlugin::qtractorAudioInsertPlugin ( + if (sse_enabled()) { + m_pfnProcessGain = sse_process_gain; + m_pfnProcessDryWet = sse_process_dry_wet; +- } else { ++ } else + #endif +- m_pfnProcessGain = std_process_gain; +- m_pfnProcessDryWet = std_process_dry_wet; +-#if defined(__SSE__) +- } ++#if defined(__ARM_NEON__) ++ m_pfnProcessGain = neon_process_gain; ++ m_pfnProcessDryWet = neon_process_dry_wet; ++ if(false) + #endif ++ { ++ m_pfnProcessGain = std_process_gain; ++ m_pfnProcessDryWet = std_process_dry_wet; ++ } + + // Create and attach the custom parameters... + m_pSendGainParam = new qtractorInsertPluginParam(this, 0); +@@ -1120,12 +1212,15 @@ qtractorAudioAuxSendPlugin::qtractorAudioAuxSendPlugin ( + #if defined(__SSE__) + if (sse_enabled()) { + m_pfnProcessAdd = sse_process_add; +- } else { ++ } else ++#endif ++#if defined(__ARM_NEON__) ++ m_pfnProcessAdd = neon_process_add; ++ if(false) + #endif ++ { + m_pfnProcessAdd = std_process_add; +-#if defined(__SSE__) + } +-#endif + + // Create and attach the custom parameters... + m_pSendGainParam = new qtractorInsertPluginParam(this, 0); -- -2.9.3 +2.9.5 diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0005-Add-ARM-NEON-acceleration-for-time-stretch-not-yet-t.patch b/recipes-misc/recipes-multimedia/qtractor/files/0005-Add-ARM-NEON-acceleration-for-time-stretch-not-yet-t.patch new file mode 100644 index 00000000..4fa62582 --- /dev/null +++ b/recipes-misc/recipes-multimedia/qtractor/files/0005-Add-ARM-NEON-acceleration-for-time-stretch-not-yet-t.patch @@ -0,0 +1,95 @@ +From e96d295e0d6b36b9b722ad3d4c0b2013e569e9d5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andreas=20M=C3=BCller?= +Date: Tue, 3 Oct 2017 21:45:43 +0200 +Subject: [PATCH 2/2] Add ARM NEON acceleration for time stretch - not yet + tested +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Upstream-Status: Pending + +Signed-off-by: Andreas Müller +--- + src/qtractorTimeStretch.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + +diff --git a/src/qtractorTimeStretch.cpp b/src/qtractorTimeStretch.cpp +index 751b9bc..6461b8b 100644 +--- a/src/qtractorTimeStretch.cpp ++++ b/src/qtractorTimeStretch.cpp +@@ -121,6 +121,60 @@ static inline float sse_cross_corr ( + + #endif + ++#if defined(__ARM_NEON__) ++#include "arm_neon.h" ++ ++// NEON enabled version. ++static inline float neon_cross_corr ( ++ const float *pV1, const float *pV2, unsigned int iOverlapLength ) ++{ ++ float32x4_t vCorr, vNorm, vTemp; ++ ++ // See notes in sse_cross_corr ++ ++ // Ensure overlapLength is divisible by 8 ++ // assert((m_iOverlapLength % 8) == 0); ++ iOverlapLength >>= 4; ++ ++ // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors ++ vCorr = vdupq_n_f32(0.0); ++ vNorm = vdupq_n_f32(0.0); ++ ++ // Unroll the loop by factor of 4 * 4 operations ++ for (unsigned int i = 0; i < iOverlapLength; ++i) { ++ // vCorr += pV1[0..3] * pV2[0..3] ++ vTemp = vld1q_f32(pV1); ++ vCorr = vmlaq_f32(vCorr, vTemp, vld1q_f32(pV2)); ++ vNorm = vmlaq_f32(vNorm, vTemp, vTemp); ++ // vCorr += pV1[4..7] * pV2[4..7] ++ vTemp = vld1q_f32(pV1 + 4); ++ vCorr = vmlaq_f32(vCorr, vTemp, vld1q_f32(pV2 + 4)); ++ vNorm = vmlaq_f32(vNorm, vTemp, vTemp); ++ // vCorr += pV1[8..11] * pV2[8..11] ++ vTemp = vld1q_f32(pV1 + 8); ++ vCorr = vmlaq_f32(vCorr, vTemp, vld1q_f32(pV2 + 8)); ++ vNorm = vmlaq_f32(vNorm, vTemp, vTemp); ++ // vCorr += pV1[12..15] * pV2[12..15] ++ vTemp = vld1q_f32(pV1 + 12); ++ vCorr = vmlaq_f32(vCorr, vTemp, vld1q_f32(pV2 + 12)); ++ vNorm = vmlaq_f32(vNorm, vTemp, vTemp); ++ pV1 += 16; ++ pV2 += 16; ++ } ++ ++ float pvNorm[4]; ++ vst1q_f32(pvNorm, vNorm); ++ float fNorm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]); ++ ++ if (fNorm < 1e-9f) fNorm = 1.0f; // avoid div by zero ++ ++ float pvCorr[4]; ++ vst1q_f32(pvCorr, vCorr); ++ return (pvCorr[0] + pvCorr[1] + pvCorr[2] + pvCorr[3]) / ::sqrtf(fNorm); ++} ++ ++#endif ++ + + // Standard (slow) version. + static inline float std_cross_corr ( +@@ -167,6 +221,10 @@ qtractorTimeStretch::qtractorTimeStretch ( + m_pfnCrossCorr = sse_cross_corr; + else + #endif ++#if defined(__ARM_NEON__) ++ m_pfnCrossCorr = neon_cross_corr; ++ if(false) ++#endif + m_pfnCrossCorr = std_cross_corr; + + setParameters(iSampleRate); +-- +2.9.5 + diff --git a/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb b/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb index f31d4378..2ead594d 100644 --- a/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb +++ b/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb @@ -22,7 +22,8 @@ SRC_URI = " \ file://0001-find-native-qt-build-tools-by-configure-options-auto.patch \ file://0002-do-nor-try-run-for-float-sse-detection.patch \ file://0003-do-nor-try-run-for-suil-libs-detection.patch \ - file://0004-Add-ARM-NEON-intrinsics.patch \ + file://0004-Add-ARM-NEON-acceleration.patch \ + file://0005-Add-ARM-NEON-acceleration-for-time-stretch-not-yet-t.patch \ " SRC_URI[md5sum] = "7a2ea586eed8b8d8c7ebfdc876b33841" SRC_URI[sha256sum] = "3ace8b4fdf623e5e6ccecd7047de73f72bcfdd10b01f187079832092ac936b9d"