Unverified Commit 0c7aafc7 authored by Emily's avatar Emily Committed by GitHub
Browse files

musly: add patches for FFmpeg 7, C++17, and external deps; kissfft: build with...

musly: add patches for FFmpeg 7, C++17, and external deps; kissfft: build with CMake; libresample: 0.1.3 -> 0.1.4-unstable-2024-08-23 (#332035)
parents 1faba3c1 7245ee2b
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
From a73134e594d85abc32e27a34a78ce75c5f006f92 Mon Sep 17 00:00:00 2001
From: Emily <hello@emily.moe>
Date: Sat, 3 Aug 2024 17:49:57 +0100
Subject: [PATCH] Fix FFTW dependency check

`KISFFT_FLOAT` is not defined anywhere.
---
 test/CMakeLists.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 0a0e403..d7d8350 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -24,7 +24,7 @@ add_kissfft_test_executable(bm_kiss benchkiss.c pstats.c)
 # set_tests_properties(${NAME} PROPERTIES TIMEOUT 3600)
 
 include(FindPkgConfig)
-if(KISSFFT_FLOAT)
+if(KISSFFT_DATATYPE MATCHES "^float$")
     set(fftw3_pkg fftw3f)
 else()
     set(fftw3_pkg fftw3)
+86 −0
Original line number Diff line number Diff line
{
  lib,
  stdenv,
  fetchFromGitHub,
  cmake,
  ninja,
  pkg-config,
  fftw,
  fftwFloat,
  python3,
  datatype ? "double",
  libpng,
  enableStatic ? stdenv.hostPlatform.isStatic,
  enableOpenmp ? false,
  llvmPackages,
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "kissfft-${datatype}${lib.optionalString enableOpenmp "-openmp"}";
  version = "131.1.0";

  outputs = [
    "bin"
    "dev"
    "out"
  ];

  src = fetchFromGitHub {
    owner = "mborgerding";
    repo = "kissfft";
    rev = finalAttrs.version;
    hash = "sha256-ukikTVnmKomKXTo6zc+PhpZzEkzXN2imFwZOYlfR3Pk=";
  };

  patches = [
    # Fix FFTW dependency check
    # https://github.com/mborgerding/kissfft/pull/95
    ./fix-fftw-dependency-check.patch
  ];

  nativeBuildInputs = [
    cmake
    ninja
    pkg-config
  ];

  buildInputs =
    lib.optionals (datatype != "simd") [ libpng ]
    # TODO: This may mismatch the LLVM version in the stdenv, see #79818.
    ++ lib.optional (enableOpenmp && stdenv.cc.isClang) llvmPackages.openmp;

  nativeCheckInputs = [ (python3.withPackages (ps: [ ps.numpy ])) ];

  checkInputs = [ (if datatype == "float" then fftwFloat else fftw) ];

  cmakeFlags = [
    (lib.cmakeFeature "KISSFFT_DATATYPE" datatype)
    (lib.cmakeBool "KISSFFT_STATIC" enableStatic)
    # `test/testkiss.py` expects this…
    (lib.cmakeFeature "KISSFFT_OPENMP" (if enableOpenmp then "ON" else "OFF"))
  ];

  # Required for `test/testcpp.c`.
  env = {
    NIX_CFLAGS_COMPILE = lib.optionalString stdenv.isDarwin "-D__MATH_LONG_DOUBLE_CONSTANTS=1";
  };

  doCheck = true;

  # https://bugs.llvm.org/show_bug.cgi?id=45034
  postPatch =
    lib.optionalString
      (stdenv.hostPlatform.isLinux && stdenv.cc.isClang && lib.versionOlder stdenv.cc.version "10")
      ''
        substituteInPlace CMakeLists.txt \
          --replace "-ffast-math" ""
      '';

  meta = {
    description = "Mixed-radix Fast Fourier Transform based up on the KISS principle";
    homepage = "https://github.com/mborgerding/kissfft";
    license = lib.licenses.bsd3;
    maintainers = [ ];
    platforms = lib.platforms.all;
  };
})
+108 −0
Original line number Diff line number Diff line
From fb8e3c74d582038a358936d827f53c4c0c43d4e6 Mon Sep 17 00:00:00 2001
From: Matt Harvey <mattharvey@google.com>
Date: Mon, 27 Nov 2023 16:28:53 -0800
Subject: [PATCH] Fix testresample.c output span; add exit code

Prior to this chance, the "Resample with different factors" test only
passed for 60 of the 63 factors, with the 3 failing ones being the
largest.

1. Since only 63 distinct factors were being considered, 100 random
   samples was overkill.
2. To support noticing failure in continuous build systems, it's nice if
   the test exit()s with nonzero when there are failures.
3. The root cause was a formula error when determining which indices in
   the resampled output ought be compared. Details are explained in a
   comment.
---
 tests/testresample.c | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/tests/testresample.c b/tests/testresample.c
index aa83a46..640df5a 100644
--- a/tests/testresample.c
+++ b/tests/testresample.c
@@ -19,6 +19,8 @@
 
 #define MIN(A, B) (A) < (B)? (A) : (B)
 
+int global_error;
+
 void runtest(int srclen, double freq, double factor,
              int srcblocksize, int dstblocksize)
 {
@@ -65,10 +67,12 @@ void runtest(int srclen, double freq, double factor,
 
    if (o < 0) {
       printf("Error: resample_process returned an error: %d\n", o);
+      global_error = 1;
    }
 
    if (out <= 0) {
       printf("Error: resample_process returned %d samples\n", out);
+      global_error = 1;
       free(src);
       free(dst);
       return;
@@ -79,15 +83,16 @@ void runtest(int srclen, double freq, double factor,
       printf("   Expected ~%d, got %d samples out\n",
              expectedlen, out);
    }
-   
+
    sum = 0.0;
    sumsq = 0.0;
    errcount = 0.0;
 
-   /* Don't compute statistics on all output values; the last few
-      are guaranteed to be off because it's based on far less
-      interpolation. */
-   statlen = out - fwidth;
+   /* Don't compute statistics on all output values; the last small fraction
+      are guaranteed to be off since they are interpolated based on far fewer
+      values. When upsampling, the length of the range where this concern
+      applies is in direct proportion to the upsampling factor. */
+   statlen = out - ((int)round(fwidth * factor));
 
    for(i=0; i<statlen; i++) {
       double diff = sin((i/freq)/factor) - dst[i];
@@ -117,6 +122,7 @@ void runtest(int srclen, double freq, double factor,
       printf("   i=%d:  expected %.3f, got %.3f\n",
              i, sin((i/freq)/factor), dst[i]);
       printf("   At least %d samples had significant error.\n", errcount);
+      global_error = 1;
    }
    err = sum / statlen;
    rmserr = sqrt(sumsq / statlen);
@@ -130,6 +136,8 @@ int main(int argc, char **argv)
    int i, srclen, dstlen, ifreq;
    double factor;
 
+   global_error = 0;
+
    printf("\n*** Vary source block size*** \n\n");
    srclen = 10000;
    ifreq = 100;
@@ -172,11 +180,19 @@ int main(int argc, char **argv)
    printf("\n*** Resample with different factors ***\n\n");
    srclen = 10000;
    ifreq = 100;
-   for(i=0; i<100; i++) {
-      factor = ((rand() % 64) + 1) / 4.0;
+   for (i = 1; i < 64; i++) {
+      factor = i / 4.0;
+      dstlen = (int)(srclen * factor + 10);
+      runtest(srclen, (double)ifreq, factor, srclen, dstlen);
+   }
+
+   printf("\n*** Resample with large factors ***\n\n");
+   srclen = 200;
+   ifreq = 100;
+   for (factor = 25.0; factor < 1000.0; factor *= 1.7) {
       dstlen = (int)(srclen * factor + 10);
       runtest(srclen, (double)ifreq, factor, srclen, dstlen);
    }
 
-   return 0;
+   return global_error;
 }
+68 −0
Original line number Diff line number Diff line
{
  lib,
  stdenv,
  fetchFromGitHub,
  cmake,
  meson,
  ninja,
  pkg-config,
  libsndfile,
  libsamplerate,
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "libresample";
  version = "0.1.4-unstable-2024-08-23";

  outputs = [
    "bin"
    "dev"
    "out"
  ];

  src = fetchFromGitHub {
    owner = "minorninth";
    repo = "libresample";
    rev = "7cb7f9c3f72d4e6774d964dc324af827192df7c3";
    hash = "sha256-8gyGZVblqeHYXKFM79AcfX455+l3Tsoq3xQse5nrKAo=";
  };

  patches = [
    # Fix testresample.c output span; add exit code
    # https://github.com/minorninth/libresample/pull/7
    ./fix-test.patch
  ];

  nativeBuildInputs = [
    meson
    ninja
    pkg-config
  ];

  buildInputs =
    [
      # For `resample-sndfile`
      libsndfile
    ]
    ++ lib.optionals (!libsamplerate.meta.broken) [
      # For `compareresample`
      libsamplerate
    ];

  mesonFlags = [ (lib.mesonEnable "compareresample" (!libsamplerate.meta.broken)) ];

  doCheck = true;

  meta = {
    description = "Real-time library for sampling rate conversion library";
    homepage = "https://github.com/minorninth/libresample";
    license = lib.licenses.bsd2; # OR LGPL-2.1-or-later
    sourceProvenance = [ lib.sourceTypes.fromSource ];
    platforms = lib.platforms.all;
    maintainers = [
      lib.maintainers.sander
      lib.maintainers.emily
    ];
    mainProgram = "resample-sndfile";
  };
})
+341 −0
Original line number Diff line number Diff line
From ab430ea4460aba050d97d3e3a712a3b6dd809db9 Mon Sep 17 00:00:00 2001
From: Emily <hello@emily.moe>
Date: Mon, 15 Jul 2024 00:41:04 +0100
Subject: [PATCH 1/4] Fix build with FFmpeg 7

---
 libmusly/CMakeLists.txt     |   5 --
 libmusly/decoders/libav.cpp | 136 +++++++++++-------------------------
 2 files changed, 42 insertions(+), 99 deletions(-)

diff --git a/libmusly/CMakeLists.txt b/libmusly/CMakeLists.txt
index d6d3680..98151df 100644
--- a/libmusly/CMakeLists.txt
+++ b/libmusly/CMakeLists.txt
@@ -16,11 +16,6 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external")
         PROPERTIES COMPILE_FLAGS "-DLIBMUSLY_EXTERNAL ${LIBMUSLY_EXTERNAL_FLAGS}")
 endif()
 
-if(EXISTS "${LIBAV_INCLUDE_DIRS}/libavutil/channel_layout.h")
-    set_source_files_properties(decoders/libav.cpp
-        PROPERTIES COMPILE_FLAGS "-DHAVE_AVUTIL_CHANNEL_LAYOUT")
-endif()
-
 if(USE_OPENMP AND OPENMP_FOUND)
     # disable OpenMP for kiss FFT, it slows things down terribly
     set_source_files_properties(kissfft/kiss_fft.c
diff --git a/libmusly/decoders/libav.cpp b/libmusly/decoders/libav.cpp
index a78b904..90f93ae 100644
--- a/libmusly/decoders/libav.cpp
+++ b/libmusly/decoders/libav.cpp
@@ -20,37 +20,13 @@
 extern "C" {
     #include <libavcodec/avcodec.h>
     #include <libavformat/avformat.h>
-#ifdef HAVE_AVUTIL_CHANNEL_LAYOUT
     #include <libavutil/channel_layout.h>
-#endif
 }
 
 #include "minilog.h"
 #include "resampler.h"
 #include "libav.h"
 
-// We define some macros to be compatible to different libav versions
-// without spreading #if and #else all over the place.
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 45, 101)
-#define AV_FRAME_ALLOC avcodec_alloc_frame
-#define AV_FRAME_UNREF avcodec_get_frame_defaults
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 28, 0)
-#define AV_FRAME_FREE(X) av_free(*(X))
-#else
-#define AV_FRAME_FREE avcodec_free_frame
-#endif
-#else
-#define AV_FRAME_ALLOC av_frame_alloc
-#define AV_FRAME_UNREF av_frame_unref
-#define AV_FRAME_FREE av_frame_free
-#endif
-
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 7, 0)
-#define AV_PACKET_UNREF av_free_packet
-#else
-#define AV_PACKET_UNREF av_packet_unref
-#endif
-
 namespace musly {
 namespace decoders {
 
@@ -58,12 +34,6 @@ MUSLY_DECODER_REGIMPL(libav, 0);
 
 libav::libav()
 {
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
-    av_register_all();
-#endif
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
-    avcodec_register_all();
-#endif
 }
 
 int
@@ -177,13 +147,7 @@ libav::decodeto_22050hz_mono_float(
     AVStream *st = fmtx->streams[audio_stream_idx];
 
     // find a decoder for the stream
-#if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 14, 0)) || ((LIBAVCODEC_VERSION_MICRO >= 100) && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 33, 100)))
-    // old libav version (libavcodec < 57.14 for libav, < 57.33 for ffmpeg):
-    // stream has a codec context we can use
-    AVCodecContext *decx = st->codec;
-    #define AVCODEC_FREE_CONTEXT(x)
-#else
-    // new libav version: need to create codec context for stream
+    // need to create codec context for stream
     AVCodecParameters *decp = st->codecpar;
     AVCodecContext *decx = avcodec_alloc_context3(NULL);
     if (!decx) {
@@ -200,71 +164,63 @@ libav::decodeto_22050hz_mono_float(
         avformat_close_input(&fmtx);
         return std::vector<float>(0);
     }
-    #if LIBAVCODEC_VERSION_MICRO >= 100
-    #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58,3,102)
-    // only available in ffmpeg, deprecated after 58
-    av_codec_set_pkt_timebase(decx, st->time_base);
-    #endif
-    #endif
-    #define AVCODEC_FREE_CONTEXT(x) avcodec_free_context(x)
-#endif
-    AVCodec *dec = avcodec_find_decoder(decx->codec_id);
+    const AVCodec *dec = avcodec_find_decoder(decx->codec_id);
     if (!dec) {
         MINILOG(logERROR) << "Could not find codec.";
 
-        AVCODEC_FREE_CONTEXT(&decx);
+        avcodec_free_context(&decx);
         avformat_close_input(&fmtx);
         return std::vector<float>(0);
     }
 
     // open the decoder
     // (kindly ask for stereo downmix and floats, but not all decoders care)
-    decx->request_channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX;
     decx->request_sample_fmt = AV_SAMPLE_FMT_FLT;
 #ifdef _OPENMP
     #pragma omp critical
 #endif
     {
-    avret = avcodec_open2(decx, dec, NULL);
+    AVDictionary *options = NULL;
+    av_dict_set(&options, "downmix", "stereo", 0);
+    avret = avcodec_open2(decx, dec, &options);
     }
     if (avret < 0) {
         MINILOG(logERROR) << "Could not open codec.";
 
-        AVCODEC_FREE_CONTEXT(&decx);
+        avcodec_free_context(&decx);
         avformat_close_input(&fmtx);
         return std::vector<float>(0);
     }
 
     // Currently only mono and stereo files are supported.
-    if ((decx->channels != 1) && (decx->channels != 2)) {
+    if ((decx->ch_layout.nb_channels != 1) && (decx->ch_layout.nb_channels != 2)) {
         MINILOG(logWARNING) << "Unsupported number of channels: "
-                << decx->channels;
+                << decx->ch_layout.nb_channels;
 
-        AVCODEC_FREE_CONTEXT(&decx);
+        avcodec_free_context(&decx);
         avformat_close_input(&fmtx);
         return std::vector<float>(0);
     }
 
     // allocate a frame
-    AVFrame* frame = AV_FRAME_ALLOC();
+    AVFrame* frame = av_frame_alloc();
     if (!frame) {
         MINILOG(logWARNING) << "Could not allocate frame";
 
-        AVCODEC_FREE_CONTEXT(&decx);
+        avcodec_free_context(&decx);
         avformat_close_input(&fmtx);
         return std::vector<float>(0);
     }
 
     // allocate and initialize a packet
-    AVPacket pkt;
-    av_init_packet(&pkt);
-    pkt.data = NULL;
-    pkt.size = 0;
+    AVPacket* pkt = av_packet_alloc();
+    pkt->data = NULL;
+    pkt->size = 0;
     int got_frame = 0;
 
     // configuration
     const int input_stride = av_get_bytes_per_sample(decx->sample_fmt);
-    const int num_planes = av_sample_fmt_is_planar(decx->sample_fmt) ? decx->channels : 1;
+    const int num_planes = av_sample_fmt_is_planar(decx->sample_fmt) ? decx->ch_layout.nb_channels : 1;
     const int output_stride = sizeof(float) * num_planes;
     int decode_samples;  // how many samples to decode; zero to decode all
 
@@ -296,7 +252,7 @@ libav::decodeto_22050hz_mono_float(
         // fault when trying to access frame->data[i] for i > 0 further below)
         if ((excerpt_start > 0) and (av_seek_frame(fmtx, audio_stream_idx,
                     excerpt_start * st->time_base.den / st->time_base.num,
-                    AVSEEK_FLAG_BACKWARD || AVSEEK_FLAG_ANY) >= 0)) {
+                    AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0)) {
             // skipping went fine: decode only what's needed
             decode_samples = excerpt_length * decx->sample_rate;
             excerpt_start = 0;
@@ -333,7 +289,7 @@ libav::decodeto_22050hz_mono_float(
     // excerpt_start tells us up to how many seconds to cut from the beginning.
 
     // read packets
-    const int channels = decx->channels;
+    const int channels = decx->ch_layout.nb_channels;
     const int sample_rate = decx->sample_rate;
     float* buffer = NULL;
     int buffersize = 0;
@@ -344,35 +300,29 @@ libav::decodeto_22050hz_mono_float(
     {
         // skip all frames that are not part of the audio stream, and spurious
         // frames possibly found after seeking (wrong channels / sample_rate)
-        while (((avret = av_read_frame(fmtx, &pkt)) >= 0)
-               && ((pkt.stream_index != audio_stream_idx) ||
-                   (decx->channels != channels) ||
+        while (((avret = av_read_frame(fmtx, pkt)) >= 0)
+               && ((pkt->stream_index != audio_stream_idx) ||
+                   (decx->ch_layout.nb_channels != channels) ||
                    (decx->sample_rate != sample_rate)))
         {
-            AV_PACKET_UNREF(&pkt);
+            av_packet_unref(pkt);
             MINILOG(logTRACE) << "Skipping frame...";
         }
         if (avret < 0) {
             // stop decoding if av_read_frame() failed
-            AV_PACKET_UNREF(&pkt);
+            av_packet_unref(pkt);
             break;
         }
 
-        uint8_t* data = pkt.data;
-        int size = pkt.size;
-        while (pkt.size > 0) {
+        uint8_t* data = pkt->data;
+        int size = pkt->size;
+        while (pkt->size > 0) {
 
             // try to decode a frame
-            AV_FRAME_UNREF(frame);
+            av_frame_unref(frame);
 
             int len = 0;
             got_frame = 0;
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)
-            len = avcodec_decode_audio4(decx, frame, &got_frame, &pkt);
-            if (len < 0) {
-                avret = AVERROR(EINVAL);
-            }
-#else
             avret = avcodec_receive_frame(decx, frame);
             if (avret == 0) {
                 got_frame = 1;
@@ -381,14 +331,13 @@ libav::decodeto_22050hz_mono_float(
                 avret = 0;
             }
             if (avret == 0) {
-                avret = avcodec_send_packet(decx, &pkt);
+                avret = avcodec_send_packet(decx, pkt);
                 if (avret == 0) {
-                    len = pkt.size;
+                    len = pkt->size;
                 } else if (avret == AVERROR(EAGAIN)) {
                     avret = 0;
                 }
             }
-#endif
             if (avret < 0) {
                 MINILOG(logWARNING) << "Error decoding an audio frame";
 
@@ -400,8 +349,8 @@ libav::decodeto_22050hz_mono_float(
 
                 // if too many frames failed decoding, abort
                 MINILOG(logERROR) << "Too many errors, aborting.";
-                AV_FRAME_FREE(&frame);
-                AV_PACKET_UNREF(&pkt);
+                av_frame_free(&frame);
+                av_packet_unref(pkt);
                 avformat_close_input(&fmtx);
                 if (buffer) {
                     delete[] buffer;
@@ -414,7 +363,7 @@ libav::decodeto_22050hz_mono_float(
             // if we got a frame
             if (got_frame) {
                 // do we need to increase the buffer size?
-                int input_samples = frame->nb_samples*decx->channels;
+                int input_samples = frame->nb_samples*decx->ch_layout.nb_channels;
                 if (input_samples > buffersize) {
                     if (buffer) {
                         delete[] buffer;
@@ -434,8 +383,8 @@ libav::decodeto_22050hz_mono_float(
                             input_samples / num_planes) < 0) {
                         MINILOG(logERROR) << "Strange sample format. Abort.";
 
-                        AV_FRAME_FREE(&frame);
-                        AV_PACKET_UNREF(&pkt);
+                        av_frame_free(&frame);
+                        av_packet_unref(pkt);
                         avformat_close_input(&fmtx);
                         if (buffer) {
                             delete[] buffer;
@@ -445,7 +394,7 @@ libav::decodeto_22050hz_mono_float(
                 }
 
                 // inplace downmix to mono, if required
-                if (decx->channels == 2) {
+                if (decx->ch_layout.nb_channels == 2) {
                     for (int i = 0; i < frame->nb_samples; i++) {
                         buffer[i] = (buffer[i*2] + buffer[i*2+1]) / 2.0f;
                     }
@@ -457,13 +406,13 @@ libav::decodeto_22050hz_mono_float(
             }
 
             // consume the packet
-            pkt.data += len;
-            pkt.size -= len;
+            pkt->data += len;
+            pkt->size -= len;
         }
-        pkt.data = data;
-        pkt.size = size;
+        pkt->data = data;
+        pkt->size = size;
 
-        AV_PACKET_UNREF(&pkt);
+        av_packet_unref(pkt);
     }
     MINILOG(logTRACE) << "Decoding loop finished.";
 
@@ -514,13 +463,12 @@ libav::decodeto_22050hz_mono_float(
     if (buffer) {
         delete[] buffer;
     }
-    AV_FRAME_FREE(&frame);
+    av_frame_free(&frame);
 #ifdef _OPENMP
     #pragma omp critical
 #endif
     {
-    avcodec_close(decx);
-    AVCODEC_FREE_CONTEXT(&decx);
+    avcodec_free_context(&decx);
     avformat_close_input(&fmtx);
     }
 
-- 
2.45.2
Loading