[beast/devel: 2/28] BSE: added FLAC support.



commit ee8c80df85046d0d2936781542cf7d82f47b67e0
Author: Stefan Westerfeld <stefan space twc de>
Date:   Sun Jun 27 21:52:07 2010 +0200

    BSE: added FLAC support.

 bse/Makefile.am           |    2 +-
 bse/bsedatahandle-flac.cc |  227 +++++++++++++++++++++++++++++++++++++++++++++
 bse/bseloader-flac.cc     |  181 ++++++++++++++++++++++++++++++++++++
 bse/gslcommon.cc          |    1 +
 bse/gslcommon.hh          |    1 +
 bse/gsldatahandle.hh      |    4 +
 6 files changed, 415 insertions(+), 1 deletions(-)
---
diff --git a/bse/Makefile.am b/bse/Makefile.am
index 9b3c2c6..b05260c 100644
--- a/bse/Makefile.am
+++ b/bse/Makefile.am
@@ -91,7 +91,7 @@ bse_sources = $(strip \
        bsecxxarg.cc            bsecxxmodule.cc         bsecxxplugin.cc                 bseloader.cc \
        bseresampler.cc         bsedatahandle-resample.cc                               bsedatahandle-fir.cc \
        bseloader-aiff.cc       bseloader-guspatch.cc   bseloader-oggvorbis.cc          bseloader-bsewave.cc \
-       bseloader-mad.cc        bseloader-wav.cc        \
+       bseloader-mad.cc        bseloader-wav.cc        bseloader-flac.cc               bsedatahandle-flac.cc 
\
        bsebusmodule.cc         \
        bsebasics.cc            \
        bseprobe.cc             \
diff --git a/bse/bsedatahandle-flac.cc b/bse/bsedatahandle-flac.cc
new file mode 100644
index 0000000..43f2c01
--- /dev/null
+++ b/bse/bsedatahandle-flac.cc
@@ -0,0 +1,227 @@
+/* BSE - Bedevilled Sound Engine
+ * Copyright (C) 2001, 2003 Tim Janik and Stefan Westerfeld
+ *
+ * This library 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.
+ *
+ * This library 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.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "gsldatahandle.h"
+#include "gsldatautils.h"
+#include "gslfilter.h"
+#include "bseblockutils.hh"
+#include <complex>
+#include <vector>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "stream_decoder.h"
+
+namespace Bse {
+
+using std::vector;
+using std::string;
+using std::min;
+
+class DataHandleFlac;
+
+struct CDataHandleFlac : public GslDataHandle
+{
+  // back pointer to get casting right, even in presence of C++ vtable:
+  DataHandleFlac* cxx_dh;
+};
+
+class DataHandleFlac {
+private:
+  static FLAC__StreamDecoderWriteStatus
+  flac_write_callback (const FLAC__StreamDecoder  *decoder,
+                       const FLAC__Frame          *frame,
+                       const FLAC__int32          *const buffer[],
+                       void                       *client_data)
+  {
+    DataHandleFlac *dh = static_cast<DataHandleFlac *> (client_data);
+    dh->m_buffer_start = frame->header.number.sample_number;
+    dh->m_buffer.clear();
+    for (int i = 0; i < frame->header.blocksize; i++)
+      dh->m_buffer.push_back (buffer[0][i] * (1 / 32768.0));
+    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+  }
+  static void
+  flac_error_callback (const FLAC__StreamDecoder     *decoder,
+                       FLAC__StreamDecoderErrorStatus status,
+                       void                          *client_data)
+  {
+  }
+
+protected:
+  CDataHandleFlac      m_dhandle;
+  bool                 m_init_ok;
+  string                m_file_name;
+  FLAC__StreamDecoder  *m_decoder;
+  int64                 m_buffer_start;
+  vector<float>         m_buffer;
+  float                 m_osc_freq;
+
+public:
+  DataHandleFlac (const string& file_name,
+                 float         osc_freq) :
+    m_init_ok (false),
+    m_decoder (NULL)
+  {
+    memset (&m_dhandle, 0, sizeof (m_dhandle));
+    m_init_ok = gsl_data_handle_common_init (&m_dhandle, NULL);
+    m_file_name = file_name;
+    m_dhandle.name = g_strdup (m_file_name.c_str());
+    m_osc_freq = osc_freq;
+  }
+
+  /* protected destructor: (use reference counting instead) */
+  virtual
+  ~DataHandleFlac()
+  {
+    if (m_init_ok)
+      gsl_data_handle_common_free (&m_dhandle);
+  }
+
+  BseErrorType
+  open (GslDataHandleSetup *setup)
+  {
+    m_decoder = FLAC__stream_decoder_new();
+    if (!m_decoder)
+      return BSE_ERROR_IO;
+
+    int err = FLAC__stream_decoder_init_file (m_decoder, m_file_name.c_str(),
+                                              flac_write_callback, NULL, flac_error_callback, this);
+    if (err != 0)
+      return BSE_ERROR_IO;
+
+    /* decode enough to figure out number of channels */
+    FLAC__bool mdok;
+    do {
+      mdok = FLAC__stream_decoder_process_single (m_decoder);
+    } while (FLAC__stream_decoder_get_channels (m_decoder) == 0 && mdok);
+
+    if (FLAC__stream_decoder_get_channels (m_decoder) == 0)
+      return BSE_ERROR_IO;
+
+    setup->n_channels = FLAC__stream_decoder_get_channels (m_decoder);
+    setup->n_values = FLAC__stream_decoder_get_total_samples (m_decoder);
+    setup->bit_depth = FLAC__stream_decoder_get_bits_per_sample (m_decoder);
+    setup->mix_freq = FLAC__stream_decoder_get_sample_rate (m_decoder);
+    setup->xinfos = bse_xinfos_add_float (setup->xinfos, "osc-freq", m_osc_freq);
+
+    return BSE_ERROR_NONE;
+  }
+
+  void
+  close()
+  {
+    m_dhandle.setup.xinfos = NULL;     /* cleanup pointer reference */
+  }
+
+  int64
+  read (int64  voffset,
+       int64  n_values,
+       float *values)
+  {
+    if (voffset >= m_buffer_start + m_buffer.size())
+      {
+        // try to read on, probably we'll have just the samples we need, then
+        FLAC__bool mdok = FLAC__stream_decoder_process_single (m_decoder);
+      }
+
+    if (voffset >= m_buffer_start && voffset < m_buffer_start + m_buffer.size())
+      {
+        int64 buffer_offset = voffset - m_buffer_start;
+        n_values = MIN (n_values, m_buffer.size() - buffer_offset);
+        std::copy (m_buffer.begin() + buffer_offset, m_buffer.begin() + buffer_offset + n_values, values);
+        return n_values;
+      }
+
+    // need to seek to get to the right location
+    FLAC__bool seek_ok = FLAC__stream_decoder_seek_absolute (m_decoder, voffset);
+    if (!seek_ok)
+      return -1;
+
+    if (voffset == m_buffer_start)
+      return read (voffset, n_values, values);    // will work this time, since we have the right samples now
+
+    return 0;
+  }
+
+  static GslDataHandle*
+  dh_create (DataHandleFlac *cxx_dh)
+  {
+    static GslDataHandleFuncs dh_vtable =
+    {
+      dh_open,
+      dh_read,
+      dh_close,
+      NULL,
+      NULL,
+      dh_destroy,
+    };
+
+    if (cxx_dh->m_init_ok)
+      {
+       cxx_dh->m_dhandle.vtable = &dh_vtable;
+       cxx_dh->m_dhandle.cxx_dh = cxx_dh;      /* make casts work, later on */
+       return &cxx_dh->m_dhandle;
+      }
+    else
+      {
+       delete cxx_dh;
+       return NULL;
+      }
+  }
+  static DataHandleFlac*
+  dh_cast (GslDataHandle *dhandle)
+  {
+    return static_cast<CDataHandleFlac *> (dhandle)->cxx_dh;
+  }
+private:
+/* for the "C" API (vtable) */
+  static BseErrorType
+  dh_open (GslDataHandle *dhandle, GslDataHandleSetup *setup)
+  {
+    return dh_cast (dhandle)->open (setup);
+  }
+  static void
+  dh_close (GslDataHandle *dhandle)
+  {
+    dh_cast (dhandle)->close();
+  }
+  static void
+  dh_destroy (GslDataHandle *dhandle)
+  {
+    delete dh_cast (dhandle);
+  }
+  static int64
+  dh_read (GslDataHandle *dhandle,
+          int64          voffset,
+          int64          n_values,
+          gfloat        *values)
+  {
+    return dh_cast (dhandle)->read (voffset, n_values, values);
+  }
+};
+
+}
+
+using namespace Bse;
+
+extern "C" GslDataHandle*
+bse_data_handle_new_flac (const char    *file_name,
+                          gfloat         osc_freq)
+{
+  DataHandleFlac *cxx_dh = new DataHandleFlac (file_name, osc_freq);
+  return DataHandleFlac::dh_create (cxx_dh);
+}
diff --git a/bse/bseloader-flac.cc b/bse/bseloader-flac.cc
new file mode 100644
index 0000000..3a58df5
--- /dev/null
+++ b/bse/bseloader-flac.cc
@@ -0,0 +1,181 @@
+  #include "bseloader.h"
+  #include <stdio.h>
+  #include <errno.h>
+  #include <string.h>
+  #include <vector>
+  #include <string>
+  #include <FLAC/stream_decoder.h>
+
+namespace {
+
+static void
+flac_error_callback (const FLAC__StreamDecoder     *decoder,
+                     FLAC__StreamDecoderErrorStatus status,
+                     void                          *client_data)
+{
+}
+
+static FLAC__StreamDecoderWriteStatus
+flac_write_callback (const FLAC__StreamDecoder  *decoder,
+                     const FLAC__Frame          *frame,
+                     const FLAC__int32          *const buffer[],
+                     void                       *client_data)
+{
+  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+struct FileInfo
+{
+  BseWaveFileInfo wfi;
+  BseWaveDsc      wdsc;
+
+  FileInfo (const gchar  *file_name,
+            BseErrorType *error_p)
+  {
+    /* initialize C structures with zeros */
+    memset (&wfi, 0, sizeof (wfi));
+    memset (&wdsc, 0, sizeof (wdsc));
+
+
+    /* setup decoder, decoding from file */
+    FLAC__StreamDecoder* decoder = FLAC__stream_decoder_new();
+    if (!decoder)
+      {
+        *error_p = BSE_ERROR_INTERNAL;  // should not happen
+        return;
+      }
+    int r = FLAC__stream_decoder_init_file (decoder, file_name, flac_write_callback, NULL, 
flac_error_callback, NULL);
+    if (r != 0)
+      {
+        *error_p = gsl_error_from_errno (errno, BSE_ERROR_FILE_OPEN_FAILED);
+        return;
+      }
+
+    /* decode enough to figure out channel count */
+    do {
+      FLAC__bool mdok = FLAC__stream_decoder_process_single (decoder);
+    } while (FLAC__stream_decoder_get_channels (decoder) == 0);
+
+    /* allocate and fill BseWaveFileInfo */
+    wfi.n_waves = 1;
+    wfi.waves = (typeof (wfi.waves)) g_malloc0 (sizeof (wfi.waves[0]) * wfi.n_waves);
+    wfi.waves[0].name = g_strdup (file_name);
+
+    /* allocate and fill BseWaveDsc */
+    wdsc.n_chunks = 1;
+    wdsc.chunks = (typeof (wdsc.chunks)) g_malloc0 (sizeof (wdsc.chunks[0]) * wdsc.n_chunks);
+    wdsc.name = g_strdup (file_name);
+    wdsc.n_channels = FLAC__stream_decoder_get_channels (decoder);
+
+    /* fill GslWaveChunk */
+    wdsc.chunks[0].mix_freq = FLAC__stream_decoder_get_sample_rate (decoder);
+    wdsc.chunks[0].osc_freq = 440.0;
+  }
+
+  ~FileInfo()
+  {
+    /* free BseWaveDsc */
+    for (guint i = 0; i < wdsc.n_chunks; i++)
+      g_strfreev (wdsc.chunks[i].xinfos);
+    
+    g_strfreev (wdsc.xinfos);
+    g_free (wdsc.name);
+    g_free (wdsc.chunks);
+    
+    /* free BseWaveFileInfo */
+    if (wfi.waves)
+      {
+       g_free (wfi.waves[0].name);
+       g_free (wfi.waves);
+      }
+  }
+};
+
+}
+
+static BseWaveFileInfo*
+flac_load_file_info (gpointer      data,
+                    const gchar  *file_name,
+                    BseErrorType *error_p)
+{
+  FileInfo *file_info = new FileInfo (file_name, error_p);
+  if (*error_p)
+    {
+      delete file_info;
+      return NULL;
+    }
+  
+  return &file_info->wfi;
+}
+
+static void
+flac_free_file_info (gpointer         data,
+                   BseWaveFileInfo *wave_file_info)
+{
+  FileInfo *file_info = reinterpret_cast<FileInfo*> (wave_file_info);
+  delete file_info;
+}
+
+static BseWaveDsc*
+flac_load_wave_dsc (gpointer         data,
+                   BseWaveFileInfo *wave_file_info,
+                   guint            nth_wave,
+                   BseErrorType    *error_p)
+{
+  FileInfo *file_info = reinterpret_cast<FileInfo*> (wave_file_info);
+  return &file_info->wdsc;
+}
+
+static void
+flac_free_wave_dsc (gpointer    data,
+                   BseWaveDsc *wave_dsc)
+{
+}
+
+static GslDataHandle*
+flac_create_chunk_handle (gpointer      data,
+                         BseWaveDsc   *wave_dsc,
+                         guint         nth_chunk,
+                         BseErrorType *error_p)
+{
+  g_return_val_if_fail (nth_chunk == 0, NULL);
+  
+  FileInfo *file_info = reinterpret_cast<FileInfo*> (wave_dsc->file_info);
+  const BseWaveChunkDsc *chunk = &wave_dsc->chunks[nth_chunk];
+  
+  GslDataHandle *dhandle;
+  dhandle = bse_data_handle_new_flac (file_info->wfi.file_name,
+                                     chunk->osc_freq);
+  return dhandle;
+}
+
+extern "C" void
+bse_init_loader_flac (void)
+{
+  static const gchar *file_exts[] = { "flac", NULL, };
+  static const gchar *mime_types[] = { "audio/x-flac", NULL, };
+  static const gchar *magics[] = {
+    "0  string  fLaC",              // free lossless audio codec magic
+    NULL,
+  };
+  static BseLoader loader = {
+    "FLAC",
+    file_exts,
+    mime_types,
+    BSE_LOADER_NO_FLAGS,
+    magics,
+    0,  /* priority */
+    NULL,
+    flac_load_file_info,
+    flac_free_file_info,
+    flac_load_wave_dsc,
+    flac_free_wave_dsc,
+    flac_create_chunk_handle,
+  };
+  static gboolean initialized = FALSE;
+  
+  g_assert (initialized == FALSE);
+  initialized = TRUE;
+  
+  bse_loader_register (&loader);
+}
diff --git a/bse/gslcommon.cc b/bse/gslcommon.cc
index bc5f4ff..cab6e7e 100644
--- a/bse/gslcommon.cc
+++ b/bse/gslcommon.cc
@@ -408,4 +408,5 @@ gsl_init (void)
   _gsl_init_loader_oggvorbis ();
   _gsl_init_loader_mad ();
   bse_init_loader_gus_patch ();
+  bse_init_loader_flac ();
 }
diff --git a/bse/gslcommon.hh b/bse/gslcommon.hh
index 8a34ed4..8f21abb 100644
--- a/bse/gslcommon.hh
+++ b/bse/gslcommon.hh
@@ -56,6 +56,7 @@ void  _gsl_init_loader_wav            (void);
 void   _gsl_init_loader_oggvorbis      (void);
 void   _gsl_init_loader_mad            (void);
 void   bse_init_loader_gus_patch       (void);
+void   bse_init_loader_flac            (void);
 #define                GSL_N_IO_RETRIES        (5)
 G_END_DECLS
 
diff --git a/bse/gsldatahandle.hh b/bse/gsldatahandle.hh
index a99e625..6d064a2 100644
--- a/bse/gsldatahandle.hh
+++ b/bse/gsldatahandle.hh
@@ -115,6 +115,10 @@ GslDataHandle*       bse_data_handle_new_fir_lowpass   (GslDataHandle *src_handle,
 gdouble           bse_data_handle_fir_response_db   (GslDataHandle *fir_handle,         // implemented in 
bsedatahandle-fir.cc
                                                      gdouble        freq);
 
+/* --- flac datahandle --- */
+GslDataHandle*    bse_data_handle_new_flac          (const gchar*   file_name,
+                                                     gfloat         osc_freq);
+
 
 
 /* --- xinfo handling --- */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]