aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2025-05-31 22:39:38 +0200
committerDaniil Cherednik <dan.cherednik@gmail.com>2025-05-31 22:39:38 +0200
commit1c7f2f821fb965af468cdf2a14df3ff75cc1c352 (patch)
tree1bc92237122b75c67afc326af207cf3cc9eb3d6c /src/lib
parent272af27a3d148bd13e8f15640e53ca70c64ccb9b (diff)
parent6dfc60e9d4791c3385908c61ad75c4a0093ea1eb (diff)
downloadatracdenc-1c7f2f821fb965af468cdf2a14df3ff75cc1c352.tar.gz
Merge branch 'at3plus-dev'
It looks like we are able to encode ATRAC3PLUS compatible bitstream so we can merge at3p development branch in to the main branch. Current limitation for AT3P mode: - Only 352 Kbps (proper bit allocation and some psychoacoustic must be implemented) - GHA sometime works with error (but huge bitrate hide it) - No VLC, VQ, delta encoding - No noise substitution - No gain control - No window shape switching
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/bs_encode/encode.cpp184
-rw-r--r--src/lib/bs_encode/encode.h71
-rw-r--r--src/lib/bs_encode/encode_ut.cpp178
-rw-r--r--src/lib/fft/kissfft_impl/kiss_fft.h1
-rw-r--r--src/lib/fft/kissfft_impl/tools/kiss_fftr.c153
-rw-r--r--src/lib/fft/kissfft_impl/tools/kiss_fftr.h54
m---------src/lib/libgha0
7 files changed, 640 insertions, 1 deletions
diff --git a/src/lib/bs_encode/encode.cpp b/src/lib/bs_encode/encode.cpp
new file mode 100644
index 0000000..230ae0f
--- /dev/null
+++ b/src/lib/bs_encode/encode.cpp
@@ -0,0 +1,184 @@
+/*
+ * This file is part of AtracDEnc.
+ *
+ * AtracDEnc is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * AtracDEnc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with AtracDEnc; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "encode.h"
+
+#include <iostream>
+
+namespace NAtracDEnc {
+
+using NBitStream::TBitStream;
+
+class TBitStreamEncoder::TImpl : public TBitAllocHandler {
+public:
+ explicit TImpl(std::vector<IBitStreamPartEncoder::TPtr>&& encoders);
+ void DoStart(size_t targetBits, float minLambda, float maxLambda) noexcept;
+ float DoContinue() noexcept;
+ void DoSubmit(size_t gotBits) noexcept;
+ bool DoCheck(size_t gotBits) const noexcept;
+ void DoRun(void* frameData, TBitStream& bs);
+ uint32_t DoGetCurGlobalConsumption() const noexcept;
+private:
+ std::vector<IBitStreamPartEncoder::TPtr> Encoders;
+ size_t CurEncPos;
+ size_t RepeatEncPos;
+
+ size_t TargetBits;
+ float MinLambda;
+ float MaxLambda;
+ float CurLambda;
+ float LastLambda;
+
+ bool NeedRepeat = false;
+};
+
+TBitStreamEncoder::TImpl::TImpl(std::vector<IBitStreamPartEncoder::TPtr>&& encoders)
+ : Encoders(std::move(encoders))
+ , CurEncPos(0)
+ , RepeatEncPos(0)
+{
+}
+
+void TBitStreamEncoder::TImpl::DoStart(size_t targetBits, float minLambda, float maxLambda) noexcept
+{
+ TargetBits = targetBits;
+ MinLambda = minLambda;
+ MaxLambda = maxLambda;
+}
+
+float TBitStreamEncoder::TImpl::DoContinue() noexcept
+{
+ if (MaxLambda <= MinLambda) {
+ return LastLambda;
+ }
+
+ CurLambda = (MaxLambda + MinLambda) / 2.0;
+ RepeatEncPos = CurEncPos;
+ return CurLambda;
+}
+
+void TBitStreamEncoder::TImpl::DoSubmit(size_t gotBits) noexcept
+{
+ if (MaxLambda <= MinLambda) {
+ NeedRepeat = false;
+ } else {
+ if (gotBits < TargetBits) {
+ LastLambda = CurLambda;
+ MaxLambda = CurLambda - 0.01f;
+ NeedRepeat = true;
+ } else if (gotBits > TargetBits) {
+ MinLambda = CurLambda + 0.01f;
+ NeedRepeat = true;
+ } else {
+ NeedRepeat = false;
+ }
+ }
+}
+
+bool TBitStreamEncoder::TImpl::DoCheck(size_t gotBits) const noexcept
+{
+ return gotBits < TargetBits;
+}
+
+void TBitStreamEncoder::TImpl::DoRun(void* frameData, TBitStream& bs)
+{
+ bool cont = false;
+ do {
+ for (CurEncPos = RepeatEncPos; CurEncPos < Encoders.size(); CurEncPos++) {
+ auto status = Encoders[CurEncPos]->Encode(frameData, *this);
+ if (NeedRepeat) {
+ NeedRepeat = false;
+ cont = true;
+ break;
+ } else {
+ cont = false;
+ }
+ if (status == IBitStreamPartEncoder::EStatus::Repeat) {
+ cont = true;
+ for (size_t i = 0; i < CurEncPos; i++) {
+ Encoders[i]->Reset();
+ }
+ RepeatEncPos = 0;
+ break;
+ }
+ }
+ } while (cont);
+
+ for (size_t i = 0; i < Encoders.size(); i++) {
+ Encoders[i]->Dump(bs);
+ }
+}
+
+uint32_t TBitStreamEncoder::TImpl::DoGetCurGlobalConsumption() const noexcept
+{
+ uint32_t consumption = 0;
+ for (size_t i = 0; i < CurEncPos; i++) {
+ consumption += Encoders[i]->GetConsumption();
+ }
+ return consumption;
+}
+
+/////
+
+TBitStreamEncoder::TBitStreamEncoder(std::vector<IBitStreamPartEncoder::TPtr>&& encoders)
+ : Impl(new TBitStreamEncoder::TImpl(std::move(encoders)))
+{}
+
+TBitStreamEncoder::~TBitStreamEncoder()
+{
+ delete Impl;
+}
+
+void TBitStreamEncoder::Do(void* frameData, TBitStream& bs)
+{
+ Impl->DoRun(frameData, bs);
+}
+
+/////
+
+void TBitAllocHandler::Start(size_t targetBits, float minLambda, float maxLambda) noexcept
+{
+ TBitStreamEncoder::TImpl* self = static_cast<TBitStreamEncoder::TImpl*>(this);
+ self->DoStart(targetBits, minLambda, maxLambda);
+}
+
+float TBitAllocHandler::Continue() noexcept
+{
+ TBitStreamEncoder::TImpl* self = static_cast<TBitStreamEncoder::TImpl*>(this);
+ return self->DoContinue();
+}
+
+void TBitAllocHandler::Submit(size_t gotBits) noexcept
+{
+ TBitStreamEncoder::TImpl* self = static_cast<TBitStreamEncoder::TImpl*>(this);
+ self->DoSubmit(gotBits);
+}
+
+bool TBitAllocHandler::Check(size_t gotBits) const noexcept
+{
+ const TBitStreamEncoder::TImpl* self = static_cast<const TBitStreamEncoder::TImpl*>(this);
+ return self->DoCheck(gotBits);
+}
+
+uint32_t TBitAllocHandler::GetCurGlobalConsumption() const noexcept
+{
+ const TBitStreamEncoder::TImpl* self = static_cast<const TBitStreamEncoder::TImpl*>(this);
+ return self->DoGetCurGlobalConsumption();
+}
+
+}
diff --git a/src/lib/bs_encode/encode.h b/src/lib/bs_encode/encode.h
new file mode 100644
index 0000000..3302d10
--- /dev/null
+++ b/src/lib/bs_encode/encode.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of AtracDEnc.
+ *
+ * AtracDEnc is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * AtracDEnc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with AtracDEnc; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <functional>
+
+namespace NBitStream {
+class TBitStream;
+}
+
+namespace NAtracDEnc {
+
+class TBitAllocHandler {
+public:
+ void Start(size_t targetBits, float minLambda, float maxLambda) noexcept;
+ float Continue() noexcept;
+ bool Check(size_t gitBits) const noexcept;
+ void Submit(size_t gotBits) noexcept;
+
+ // Returns consumption of all previous encoded parts (except part from this method called)
+ uint32_t GetCurGlobalConsumption() const noexcept;
+};
+
+class IBitStreamPartEncoder {
+public:
+ using TPtr = std::unique_ptr<IBitStreamPartEncoder>;
+ enum class EStatus {
+ Ok, // Ok, go to the next stage
+ Repeat, // Repeat from first stage
+ };
+
+ virtual ~IBitStreamPartEncoder() = default;
+ virtual EStatus Encode(void* frameData, TBitAllocHandler& ba) = 0;
+ virtual void Dump(NBitStream::TBitStream& bs) = 0;
+ virtual void Reset() noexcept {};
+ virtual uint32_t GetConsumption() const noexcept = 0;
+};
+
+class TBitStreamEncoder {
+public:
+ class TImpl;
+ explicit TBitStreamEncoder(std::vector<IBitStreamPartEncoder::TPtr>&& encoders);
+ ~TBitStreamEncoder();
+
+ void Do(void* frameData, NBitStream::TBitStream& bs);
+ TBitStreamEncoder(const TBitStreamEncoder&) = delete;
+ TBitStreamEncoder& operator=(const TBitStreamEncoder&) = delete;
+private:
+ std::vector<IBitStreamPartEncoder::TPtr> Encoders;
+ TImpl* Impl;
+};
+
+}
diff --git a/src/lib/bs_encode/encode_ut.cpp b/src/lib/bs_encode/encode_ut.cpp
new file mode 100644
index 0000000..39f0ff1
--- /dev/null
+++ b/src/lib/bs_encode/encode_ut.cpp
@@ -0,0 +1,178 @@
+/*
+ * This file is part of AtracDEnc.
+ *
+ * AtracDEnc is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * AtracDEnc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with AtracDEnc; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "encode.h"
+#include <gtest/gtest.h>
+#include <cmath>
+#include <memory>
+#include <bitstream/bitstream.h>
+
+using namespace NAtracDEnc;
+
+static size_t SomeBitFn1(float lambda) {
+ return sqrt(lambda * (-1.0f)) * 300;
+}
+
+static size_t SomeBitFn2(float lambda) {
+ return 1 + (SomeBitFn1(lambda) & (~(size_t)7));
+}
+
+class TPartEncoder1 : public IBitStreamPartEncoder {
+public:
+ explicit TPartEncoder1(size_t expCalls)
+ : ExpCalls(expCalls)
+ {}
+
+ EStatus Encode(void* frameData, TBitAllocHandler& ba) override {
+ EncCalls++;
+ ba.Start(1000, -15, -1);
+ return EStatus::Ok;
+ }
+
+ void Dump(NBitStream::TBitStream& bs) override {
+ EXPECT_EQ(EncCalls, ExpCalls);
+ }
+
+ uint32_t GetConsumption() const noexcept override {
+ return 0;
+ }
+private:
+ const size_t ExpCalls;
+ size_t EncCalls = 0;
+};
+
+template<size_t (*F)(float)>
+class TPartEncoder2 : public IBitStreamPartEncoder {
+public:
+ explicit TPartEncoder2(size_t expCalls)
+ : ExpCalls(expCalls)
+ {}
+
+ EStatus Encode(void* frameData, TBitAllocHandler& ba) override {
+ EncCalls++;
+ auto lambda = ba.Continue();
+ auto bits = F(lambda);
+ ba.Submit(bits);
+ Bits = bits;
+ return EStatus::Ok;
+ }
+
+ void Dump(NBitStream::TBitStream& bs) override {
+ EXPECT_EQ(EncCalls, ExpCalls);
+ for (size_t i = 0; i < Bits; i++) {
+ bs.Write(1, 1);
+ }
+ }
+
+ uint32_t GetConsumption() const noexcept override {
+ return 1 * Bits;
+ }
+private:
+ const size_t ExpCalls;
+ size_t EncCalls = 0;
+ size_t Bits = 0;
+};
+
+class TPartEncoder3 : public IBitStreamPartEncoder {
+public:
+ explicit TPartEncoder3(size_t expCalls)
+ : ExpCalls(expCalls)
+ {}
+
+ EStatus Encode(void* frameData, TBitAllocHandler& ba) override {
+ EncCalls++;
+ return EStatus::Ok;
+ }
+
+ void Dump(NBitStream::TBitStream& bs) override {
+ EXPECT_EQ(EncCalls, ExpCalls);
+ }
+
+ uint32_t GetConsumption() const noexcept override {
+ return 0;
+ }
+private:
+ const size_t ExpCalls;
+ size_t EncCalls = 0;
+};
+
+class TPartEncoder4 : public IBitStreamPartEncoder {
+public:
+ TPartEncoder4() = default;
+
+ EStatus Encode(void* frameData, TBitAllocHandler& ba) override {
+ if (EncCalls == 0) {
+ EncCalls++;
+ return EStatus::Repeat;
+ }
+
+ return EStatus::Ok;
+ }
+
+ void Dump(NBitStream::TBitStream& bs) override {
+ EXPECT_EQ(EncCalls, 1);
+ }
+
+ uint32_t GetConsumption() const noexcept override {
+ return 0;
+ }
+private:
+ size_t EncCalls = 0;
+};
+
+TEST(BsEncode, SimpleAlloc) {
+ std::vector<IBitStreamPartEncoder::TPtr> encoders;
+ encoders.emplace_back(std::make_unique<TPartEncoder1>(1));
+ encoders.emplace_back(std::make_unique<TPartEncoder2<SomeBitFn1>>(8));
+ encoders.emplace_back(std::make_unique<TPartEncoder3>(1));
+
+ NBitStream::TBitStream bs;
+ TBitStreamEncoder encoder(std::move(encoders));
+ encoder.Do(nullptr, bs);
+
+ EXPECT_EQ(bs.GetSizeInBits(), 1000);
+}
+
+TEST(BsEncode, AllocWithRepeat) {
+ std::vector<IBitStreamPartEncoder::TPtr> encoders;
+ encoders.emplace_back(std::make_unique<TPartEncoder1>(2));
+ encoders.emplace_back(std::make_unique<TPartEncoder2<SomeBitFn1>>(16));
+ encoders.emplace_back(std::make_unique<TPartEncoder3>(2));
+ encoders.emplace_back(std::make_unique<TPartEncoder4>());
+
+ NBitStream::TBitStream bs;
+ TBitStreamEncoder encoder(std::move(encoders));
+ encoder.Do(nullptr, bs);
+
+ EXPECT_EQ(bs.GetSizeInBits(), 1000);
+}
+
+TEST(BsEncode, NotExactAlloc) {
+ std::vector<IBitStreamPartEncoder::TPtr> encoders;
+ encoders.emplace_back(std::make_unique<TPartEncoder1>(1));
+ encoders.emplace_back(std::make_unique<TPartEncoder2<SomeBitFn2>>(11));
+ encoders.emplace_back(std::make_unique<TPartEncoder3>(1));
+
+ NBitStream::TBitStream bs;
+ TBitStreamEncoder encoder(std::move(encoders));
+ encoder.Do(nullptr, bs);
+
+ EXPECT_EQ(bs.GetSizeInBits(), 993);
+}
+
+
diff --git a/src/lib/fft/kissfft_impl/kiss_fft.h b/src/lib/fft/kissfft_impl/kiss_fft.h
index 7786eb6..64c50f4 100644
--- a/src/lib/fft/kissfft_impl/kiss_fft.h
+++ b/src/lib/fft/kissfft_impl/kiss_fft.h
@@ -44,7 +44,6 @@ extern "C" {
#else
# ifndef kiss_fft_scalar
/* default is float */
-# error "wrong type"
# define kiss_fft_scalar float
# endif
#endif
diff --git a/src/lib/fft/kissfft_impl/tools/kiss_fftr.c b/src/lib/fft/kissfft_impl/tools/kiss_fftr.c
new file mode 100644
index 0000000..8102132
--- /dev/null
+++ b/src/lib/fft/kissfft_impl/tools/kiss_fftr.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
+ * This file is part of KISS FFT - https://github.com/mborgerding/kissfft
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ * See COPYING file for more information.
+ */
+
+#include "kiss_fftr.h"
+#include "_kiss_fft_guts.h"
+
+struct kiss_fftr_state{
+ kiss_fft_cfg substate;
+ kiss_fft_cpx * tmpbuf;
+ kiss_fft_cpx * super_twiddles;
+#ifdef USE_SIMD
+ void * pad;
+#endif
+};
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
+{
+ int i;
+ kiss_fftr_cfg st = NULL;
+ size_t subsize = 0, memneeded;
+
+ if (nfft & 1) {
+ fprintf(stderr,"Real FFT optimization must be even.\n");
+ return NULL;
+ }
+ nfft >>= 1;
+
+ kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
+ memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
+
+ if (lenmem == NULL) {
+ st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
+ } else {
+ if (*lenmem >= memneeded)
+ st = (kiss_fftr_cfg) mem;
+ *lenmem = memneeded;
+ }
+ if (!st)
+ return NULL;
+
+ st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
+ st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
+ st->super_twiddles = st->tmpbuf + nfft;
+ kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
+
+ for (i = 0; i < nfft/2; ++i) {
+ double phase =
+ -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
+ if (inverse_fft)
+ phase *= -1;
+ kf_cexp (st->super_twiddles+i,phase);
+ }
+ return st;
+}
+
+void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
+{
+ /* input buffer timedata is stored row-wise */
+ int k,ncfft;
+ kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
+
+ if ( st->substate->inverse) {
+ fprintf(stderr,"kiss fft usage error: improper alloc\n");
+ exit(1);
+ }
+
+ ncfft = st->substate->nfft;
+
+ /*perform the parallel fft of two real signals packed in real,imag*/
+ kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
+ /* The real part of the DC element of the frequency spectrum in st->tmpbuf
+ * contains the sum of the even-numbered elements of the input time sequence
+ * The imag part is the sum of the odd-numbered elements
+ *
+ * The sum of tdc.r and tdc.i is the sum of the input time sequence.
+ * yielding DC of input time sequence
+ * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
+ * yielding Nyquist bin of input time sequence
+ */
+
+ tdc.r = st->tmpbuf[0].r;
+ tdc.i = st->tmpbuf[0].i;
+ C_FIXDIV(tdc,2);
+ CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
+ CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
+ freqdata[0].r = tdc.r + tdc.i;
+ freqdata[ncfft].r = tdc.r - tdc.i;
+#ifdef USE_SIMD
+ freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
+#else
+ freqdata[ncfft].i = freqdata[0].i = 0;
+#endif
+
+ for ( k=1;k <= ncfft/2 ; ++k ) {
+ fpk = st->tmpbuf[k];
+ fpnk.r = st->tmpbuf[ncfft-k].r;
+ fpnk.i = - st->tmpbuf[ncfft-k].i;
+ C_FIXDIV(fpk,2);
+ C_FIXDIV(fpnk,2);
+
+ C_ADD( f1k, fpk , fpnk );
+ C_SUB( f2k, fpk , fpnk );
+ C_MUL( tw , f2k , st->super_twiddles[k-1]);
+
+ freqdata[k].r = HALF_OF(f1k.r + tw.r);
+ freqdata[k].i = HALF_OF(f1k.i + tw.i);
+ freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
+ freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
+ }
+}
+
+void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
+{
+ /* input buffer timedata is stored row-wise */
+ int k, ncfft;
+
+ if (st->substate->inverse == 0) {
+ fprintf (stderr, "kiss fft usage error: improper alloc\n");
+ exit (1);
+ }
+
+ ncfft = st->substate->nfft;
+
+ st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
+ st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
+ C_FIXDIV(st->tmpbuf[0],2);
+
+ for (k = 1; k <= ncfft / 2; ++k) {
+ kiss_fft_cpx fk, fnkc, fek, fok, tmp;
+ fk = freqdata[k];
+ fnkc.r = freqdata[ncfft - k].r;
+ fnkc.i = -freqdata[ncfft - k].i;
+ C_FIXDIV( fk , 2 );
+ C_FIXDIV( fnkc , 2 );
+
+ C_ADD (fek, fk, fnkc);
+ C_SUB (tmp, fk, fnkc);
+ C_MUL (fok, tmp, st->super_twiddles[k-1]);
+ C_ADD (st->tmpbuf[k], fek, fok);
+ C_SUB (st->tmpbuf[ncfft - k], fek, fok);
+#ifdef USE_SIMD
+ st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
+#else
+ st->tmpbuf[ncfft - k].i *= -1;
+#endif
+ }
+ kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
+}
diff --git a/src/lib/fft/kissfft_impl/tools/kiss_fftr.h b/src/lib/fft/kissfft_impl/tools/kiss_fftr.h
new file mode 100644
index 0000000..588948d
--- /dev/null
+++ b/src/lib/fft/kissfft_impl/tools/kiss_fftr.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
+ * This file is part of KISS FFT - https://github.com/mborgerding/kissfft
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ * See COPYING file for more information.
+ */
+
+#ifndef KISS_FTR_H
+#define KISS_FTR_H
+
+#include "kiss_fft.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+
+ Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
+
+
+
+ */
+
+typedef struct kiss_fftr_state *kiss_fftr_cfg;
+
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
+/*
+ nfft must be even
+
+ If you don't care to allocate space, use mem = lenmem = NULL
+*/
+
+
+void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
+/*
+ input timedata has nfft scalar points
+ output freqdata has nfft/2+1 complex points
+*/
+
+void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
+/*
+ input freqdata has nfft/2+1 complex points
+ output timedata has nfft scalar points
+*/
+
+#define kiss_fftr_free KISS_FFT_FREE
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/lib/libgha b/src/lib/libgha
new file mode 160000
+Subproject 0c3e65863f31459c4be8a12051a8d6260cb1d30