OSDN Git Service

add support for libFLAC
authorShohei Urabe <root@mput.dip.jp>
Sat, 17 Apr 2004 15:36:48 +0000 (15:36 +0000)
committerShohei Urabe <root@mput.dip.jp>
Sat, 17 Apr 2004 15:36:48 +0000 (15:36 +0000)
ChangeLog
configure
configure.in
interface/Makefile.in
timidity/Makefile.am
timidity/Makefile.in
timidity/flac_a.c [new file with mode: 0644]
timidity/output.c
timidity/timidity.c

index b99b7ba..0fe60f0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2004-04-18  Iwata <b6330015@ipc.kit.jp>
+
+       * timidity/flac_a.c : Add
+       * configure.in, timidity/output.c, timidity/timidity.c,
+         timidity/Makefile.am, : Add libFLAC supert
+
 2004-04-14  Kentaro Sato <kentaro@ranvis.com>
 
        * libarc/arc.c, timidity/timidity.c:
index 702439b..34181a6 100755 (executable)
--- a/configure
+++ b/configure
@@ -882,6 +882,7 @@ Optional Features:
                               jack:      JACK
                               ao:        Libao
                               vorbis:    Ogg Vorbis
+                              flac:      FLAC / OggFLAC
                               gogo:      MP3 GOGO (Only Windows is supported)
   --disable-alsatest      Do not try to compile and run a test Alsa program
   --disable-artstest       Do not try to compile and run a test ARTS program
@@ -943,7 +944,7 @@ Optional Packages:
   --with-nas-includes=DIR NAS include files are in dir
   --with-default-output=<mode>  Specify default output mode (optional):
                                 (default|alsa|alib|arts|nas|
-                                esd|wav|au|aiff|list|vorbis|
+                                esd|wav|au|aiff|list|vorbis|flac|
                                 gogo|portaudio|jack|ao)
   --with-alsa-prefix=PFX  Prefix where Alsa library is installed(optional)
   --with-alsa-inc-prefix=PFX  Prefix where include libraries are (optional)
@@ -11603,7 +11604,7 @@ fi;
 # audio section
 #
 
-audio_targets='default oss alsa sun hpux irix mme sb_dsp w32 alib nas arts esd vorbis gogo portaudio jack ao'
+audio_targets='default oss alsa sun hpux irix mme sb_dsp w32 alib nas arts esd vorbis flac gogo portaudio jack ao'
 
 
 # Check whether --with-nas-library or --without-nas-library was given.
@@ -14175,6 +14176,19 @@ else
 echo "${ECHO_T}no" >&6
 fi
 
+echo "$as_me:$LINENO: checking enable_audio=flac" >&5
+echo $ECHO_N "checking enable_audio=flac... $ECHO_C" >&6
+if test "x$au_enable_flac" = xyes; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SYSEXTRAS="$SYSEXTRAS flac_a.c"
+  EXTRALIBS="$EXTRALIBS -lOggFLAC -lFLAC -logg"
+  EXTRADEFS="$EXTRADEFS -DAU_FLAC -DAU_OGGFLAC"
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
 echo "$as_me:$LINENO: checking enable_audio=gogo" >&5
 echo $ECHO_N "checking enable_audio=gogo... $ECHO_C" >&6
 if test "x$au_enable_gogo" = xyes; then
@@ -14229,6 +14243,7 @@ case ".$DEFAULT_PLAYMODE" in
   .aiff)     TIMIDITY_OUTPUT_ID=a ;;
   .list)     TIMIDITY_OUTPUT_ID=l ;;
   .vorbis)   TIMIDITY_OUTPUT_ID=v ;;
+  .flac)     TIMIDITY_OUTPUT_ID=F ;;
   .gogo)     TIMIDITY_OUTPUT_ID=g ;;
   .jack)     TIMIDITY_OUTPUT_ID=j ;;
   *)         TIMIDITY_OUTPUT_ID= ;;
index 84f755f..b85d128 100644 (file)
@@ -608,11 +608,12 @@ dnl arts(R):      aRts
 dnl esd(e):     EsounD
 dnl portaudio(p) PortAudio
 dnl vorbis(v):  Ogg Vorbis
+dnl flac(F):    FLAC / OggFLAC
 dnl gogo(g):    MP3 GOGO
 dnl jack(j):    JACK
 dnl ao(O):      Libao
 
-audio_targets='default oss alsa sun hpux irix mme sb_dsp w32 alib nas arts esd vorbis gogo portaudio jack ao'
+audio_targets='default oss alsa sun hpux irix mme sb_dsp w32 alib nas arts esd vorbis flac gogo portaudio jack ao'
 
 AC_ARG_WITH(nas-library,
   [  --with-nas-library=library NAS absolute library path(Don't use -laudio)])
@@ -640,6 +641,7 @@ AC_ARG_ENABLE(audio,
                               jack:      JACK
                               ao:        Libao
                               vorbis:    Ogg Vorbis
+                              flac:      FLAC / OggFLAC
                               gogo:      MP3 GOGO (Only Windows is supported)],
   [ enable_audio=$enableval
     have_audio_opt=yes ],
@@ -660,7 +662,7 @@ DEFAULT_PLAYMODE=
 AC_ARG_WITH(default-output,
   [  --with-default-output=<mode>  Specify default output mode (optional):
                                 (default|alsa|alib|arts|nas|
-                                esd|wav|au|aiff|list|vorbis|
+                                esd|wav|au|aiff|list|vorbis|flac|
                                 gogo|portaudio|jack|ao)],
   [ if test "$enable_audio" != no; then
     DEFAULT_PLAYMODE=$withval
@@ -1099,6 +1101,16 @@ else
   AC_MSG_RESULT(no)
 fi
 
+AC_MSG_CHECKING(enable_audio=flac)
+if test "x$au_enable_flac" = xyes; then
+  AC_MSG_RESULT(yes)
+  SYSEXTRAS="$SYSEXTRAS flac_a.c"
+  EXTRALIBS="$EXTRALIBS -lOggFLAC -lFLAC -logg"
+  EXTRADEFS="$EXTRADEFS -DAU_FLAC -DAU_OGGFLAC"
+else
+  AC_MSG_RESULT(no)
+fi
+
 dnl Win32 Gogo-no-coder
 AC_MSG_CHECKING(enable_audio=gogo)
 if test "x$au_enable_gogo" = xyes; then
@@ -1152,6 +1164,7 @@ case ".$DEFAULT_PLAYMODE" in
   .aiff)     TIMIDITY_OUTPUT_ID=a ;;
   .list)     TIMIDITY_OUTPUT_ID=l ;;
   .vorbis)   TIMIDITY_OUTPUT_ID=v ;;
+  .flac)     TIMIDITY_OUTPUT_ID=F ;;
   .gogo)     TIMIDITY_OUTPUT_ID=g ;;
   .jack)     TIMIDITY_OUTPUT_ID=j ;;
   *)         TIMIDITY_OUTPUT_ID= ;;
index f94a480..ee0aa09 100644 (file)
@@ -472,9 +472,9 @@ EXTRA_DIST = \
        interface_n.txt \
        interface_s.txt
 
-@ENABLE_DYNAMIC_TCLTK_TRUE@install_tk = install.tk
 
 @ENABLE_TCLTK_TRUE@install_tk = install.tk
+@ENABLE_DYNAMIC_TCLTK_TRUE@install_tk = install.tk
 @ENABLE_DYNAMIC_XAW_TRUE@install_xaw = install.xaw
 
 @ENABLE_XAW_TRUE@install_xaw = install.xaw
index 849af7e..44c81d2 100644 (file)
@@ -140,6 +140,7 @@ EXTRA_timidity_SOURCES = \
        portaudio_a.c \
        sun_a.c \
        vorbis_a.c \
+       flac_a.c \
        w32_a.c \
        w32_gogo.c \
        w32_gogo.h \
index 98d4489..c4a2e5d 100644 (file)
@@ -422,6 +422,7 @@ EXTRA_timidity_SOURCES = \
        portaudio_a.c \
        sun_a.c \
        vorbis_a.c \
+       flac_a.c \
        w32_a.c \
        w32_gogo.c \
        w32_gogo.h \
@@ -436,10 +437,10 @@ calcnewt_SOURCES = calcnewt.c
 
 CLEANFILES = newton_table.c
 
-@ENABLE_W32GUI_TRUE@W32GUI_RES = $(top_builddir)/interface/w32g_res.res
-
 @ENABLE_W32GUI_FALSE@@ENABLE_W32G_SYN_TRUE@W32GUI_RES = $(top_builddir)/interface/w32g_res.res
 
+@ENABLE_W32GUI_TRUE@W32GUI_RES = $(top_builddir)/interface/w32g_res.res
+
 timidity_LDADD = \
        $(SYSEXTRAS:.c=.o) \
        $(top_builddir)/interface/libinterface.a \
@@ -524,13 +525,13 @@ am__depfiles_maybe = depfiles
 @AMDEP_TRUE@   ./$(DEPDIR)/dl_dyld.Po ./$(DEPDIR)/dl_hpux.Po \
 @AMDEP_TRUE@   ./$(DEPDIR)/dl_w32.Po ./$(DEPDIR)/effect.Po \
 @AMDEP_TRUE@   ./$(DEPDIR)/esd_a.Po ./$(DEPDIR)/filter.Po \
-@AMDEP_TRUE@   ./$(DEPDIR)/freq.Po ./$(DEPDIR)/gogo_a.Po \
-@AMDEP_TRUE@   ./$(DEPDIR)/hpux_a.Po ./$(DEPDIR)/hpux_d_a.Po \
-@AMDEP_TRUE@   ./$(DEPDIR)/instrum.Po ./$(DEPDIR)/jack_a.Po \
-@AMDEP_TRUE@   ./$(DEPDIR)/list_a.Po ./$(DEPDIR)/loadtab.Po \
-@AMDEP_TRUE@   ./$(DEPDIR)/m2m.Po ./$(DEPDIR)/mac_a.Po \
-@AMDEP_TRUE@   ./$(DEPDIR)/mac_dlog.Po ./$(DEPDIR)/mac_main.Po \
-@AMDEP_TRUE@   ./$(DEPDIR)/mac_qt_a.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/flac_a.Po ./$(DEPDIR)/freq.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/gogo_a.Po ./$(DEPDIR)/hpux_a.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/hpux_d_a.Po ./$(DEPDIR)/instrum.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/jack_a.Po ./$(DEPDIR)/list_a.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/loadtab.Po ./$(DEPDIR)/m2m.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/mac_a.Po ./$(DEPDIR)/mac_dlog.Po \
+@AMDEP_TRUE@   ./$(DEPDIR)/mac_main.Po ./$(DEPDIR)/mac_qt_a.Po \
 @AMDEP_TRUE@   ./$(DEPDIR)/mac_soundspec.Po ./$(DEPDIR)/mfi.Po \
 @AMDEP_TRUE@   ./$(DEPDIR)/mfnode.Po ./$(DEPDIR)/miditrace.Po \
 @AMDEP_TRUE@   ./$(DEPDIR)/mix.Po ./$(DEPDIR)/mod.Po \
@@ -632,6 +633,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/effect.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/esd_a.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flac_a.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/freq.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gogo_a.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hpux_a.Po@am__quote@
diff --git a/timidity/flac_a.c b/timidity/flac_a.c
new file mode 100644 (file)
index 0000000..4f72e02
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+    TiMidity++ -- MIDI to WAVE converter and player
+    Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
+    Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    flac_a.c
+       Written by Iwata <b6330015@kit.jp>
+    Functions to output FLAC / OggFLAC  (*.flac, *.ogg).
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <FLAC/all.h>
+#ifdef AU_OGGFLAC
+#include <OggFLAC/stream_encoder.h>
+#endif
+
+#include "timidity.h"
+#include "output.h"
+#include "controls.h"
+#include "timer.h"
+#include "instrum.h"
+#include "playmidi.h"
+#include "readmidi.h"
+#include "miditrace.h"
+
+static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
+static void close_output(void);
+static int output_data(char *buf, int32 nbytes);
+static int acntl(int request, void *arg);
+
+/* export the playback mode */
+
+#define dpm flac_play_mode
+
+PlayMode dpm = {
+  DEFAULT_RATE, PE_SIGNED|PE_16BIT, PF_PCM_STREAM,
+  -1,
+  {0}, /* default: get all the buffer fragments you can */
+#ifndef AU_OGGFLAC
+  "FLAC", 'F',
+#else
+  "FLAC / OggFLAC", 'F',
+#endif /* AU_OGGFLAC */
+  NULL,
+  open_output,
+  close_output,
+  output_data,
+  acntl
+};
+
+typedef struct {
+  unsigned long in_bytes;
+  unsigned long out_bytes;
+  union {
+    FLAC__StreamEncoderState flac;
+#ifdef AU_OGGFLAC
+    OggFLAC__StreamEncoderState ogg;
+#endif
+  } state;
+  union {
+    union {
+      FLAC__StreamEncoder *stream;
+    } flac;
+#ifdef AU_OGGFLAC
+    union {
+      OggFLAC__StreamEncoder *stream;
+    } ogg;
+#endif
+  } encoder;
+} FLAC_ctx;
+
+typedef struct {
+#ifdef AU_OGGFLAC
+  int isogg;
+#endif
+  int verify;
+  int padding;
+  int blocksize;
+  int mid_side;
+  int adaptive_mid_side;
+  int exhaustive_model_search;
+  int max_lpc_order;
+  int qlp_coeff_precision_search;
+  int qlp_coeff_precision;
+  int min_residual_partition_order;
+  int max_residual_partition_order;
+} FLAC_options;
+
+/* default compress level is 5 */
+FLAC_options flac_options = {
+#ifdef AU_OGGFLAC
+  0,    /* isogg */
+#endif
+  0,    /* verify */
+  4096, /* padding */
+  4608, /* blocksize */
+  1,    /* mid_side */
+  0,    /* adaptive_mid_side */
+  0,    /* exhaustive-model-search */
+  8,    /* max_lpc_order */
+  0,    /* qlp_coeff_precision_search */
+  0,    /* qlp_coeff_precision */
+  3,    /* min_residual_partition_order */
+  3     /* max_residual_partition_order */
+};
+
+static long serial_number = 0;
+FLAC_ctx *flac_ctx = NULL;
+
+#ifdef AU_OGGFLAC
+static FLAC__StreamEncoderWriteStatus
+ogg_stream_encoder_write_callback(const OggFLAC__StreamEncoder *encoder,
+                                 const FLAC__byte buffer[],
+                                 unsigned bytes, unsigned samples,
+                                 unsigned current_frame, void *client_data);
+#endif
+static FLAC__StreamEncoderWriteStatus
+flac_stream_encoder_write_callback(const FLAC__StreamEncoder *encoder,
+                                  const FLAC__byte buffer[],
+                                  unsigned bytes, unsigned samples,
+                                  unsigned current_frame, void *client_data);
+static void flac_stream_encoder_metadata_callback(const FLAC__StreamEncoder *encoder,
+                                                 const FLAC__StreamMetadata *metadata,
+                                                 void *client_data);
+
+/* preset */
+void flac_set_compression_level(int compression_level)
+{
+  switch (compression_level) {
+  case 0:
+    flac_options.max_lpc_order = 0;
+    flac_options.blocksize = 1152;
+    flac_options.mid_side = 0;
+    flac_options.adaptive_mid_side = 0;
+    flac_options.min_residual_partition_order = 2;
+    flac_options.max_residual_partition_order = 2;
+    flac_options.exhaustive_model_search = 0;
+    break;
+  case 1:
+    flac_options.max_lpc_order = 0;
+    flac_options.blocksize = 1152;
+    flac_options.mid_side = 0;
+    flac_options.adaptive_mid_side = 1;
+    flac_options.min_residual_partition_order = 2;
+    flac_options.max_residual_partition_order = 2;
+    flac_options.exhaustive_model_search = 0;
+    break;
+  case 2:
+    flac_options.max_lpc_order = 0;
+    flac_options.blocksize = 1152;
+    flac_options.mid_side = 1;
+    flac_options.adaptive_mid_side = 0;
+    flac_options.min_residual_partition_order = 0;
+    flac_options.max_residual_partition_order = 3;
+    flac_options.exhaustive_model_search = 0;
+    break;
+  case 3:
+    flac_options.max_lpc_order = 6;
+    flac_options.blocksize = 4608;
+    flac_options.mid_side = 0;
+    flac_options.adaptive_mid_side = 0;
+    flac_options.min_residual_partition_order = 3;
+    flac_options.max_residual_partition_order = 3;
+    flac_options.exhaustive_model_search = 0;
+    break;
+  case 4:
+    flac_options.max_lpc_order = 8;
+    flac_options.blocksize = 4608;
+    flac_options.mid_side = 0;
+    flac_options.adaptive_mid_side = 1;
+    flac_options.min_residual_partition_order = 3;
+    flac_options.max_residual_partition_order = 3;
+    flac_options.exhaustive_model_search = 0;
+    break;
+  case 6:
+    flac_options.max_lpc_order = 8;
+    flac_options.blocksize = 4608;
+    flac_options.mid_side = 1;
+    flac_options.adaptive_mid_side = 0;
+    flac_options.min_residual_partition_order = 0;
+    flac_options.max_residual_partition_order = 4;
+    flac_options.exhaustive_model_search = 0;
+    break;
+  case 7:
+    flac_options.max_lpc_order = 8;
+    flac_options.blocksize = 4608;
+    flac_options.mid_side = 1;
+    flac_options.adaptive_mid_side = 0;
+    flac_options.min_residual_partition_order = 0;
+    flac_options.max_residual_partition_order = 6;
+    flac_options.exhaustive_model_search = 1;
+    break;
+  case 8:
+    flac_options.max_lpc_order = 12;
+    flac_options.blocksize = 4608;
+    flac_options.mid_side = 1;
+    flac_options.adaptive_mid_side = 0;
+    flac_options.min_residual_partition_order = 0;
+    flac_options.max_residual_partition_order = 6;
+    flac_options.exhaustive_model_search = 1;
+    break;
+  case 5:
+  default:
+    flac_options.max_lpc_order = 8;
+    flac_options.blocksize = 4608;
+    flac_options.mid_side = 1;
+    flac_options.adaptive_mid_side = 0;
+    flac_options.min_residual_partition_order = 3;
+    flac_options.max_residual_partition_order = 3;
+    flac_options.exhaustive_model_search = 0;
+  }
+}
+
+void flac_set_option_padding(int padding)
+{
+  flac_options.padding = padding;
+}
+void flac_set_option_verify(int verify)
+{
+  flac_options.verify = verify;
+}
+#ifdef AU_OGGFLAC
+void flac_set_option_oggflac(int isogg)
+{
+  flac_options.isogg = isogg;
+}
+#endif
+
+static int flac_session_close()
+{
+  FLAC_ctx *ctx = flac_ctx;
+
+  if (dpm.fd > 0) {
+    close(dpm.fd);
+  }
+  dpm.fd = -1;
+
+  if (ctx != NULL) {
+#ifdef AU_OGGFLAC
+    if (flac_options.isogg) {
+      if (ctx->encoder.ogg.stream) {
+       OggFLAC__stream_encoder_finish(ctx->encoder.ogg.stream);
+       OggFLAC__stream_encoder_delete(ctx->encoder.ogg.stream);
+      }
+    }
+    else
+#endif /* AU_OGGFLAC */
+    {
+      if (ctx->encoder.flac.stream) {
+       FLAC__stream_encoder_finish(ctx->encoder.flac.stream);
+       FLAC__stream_encoder_delete(ctx->encoder.flac.stream);
+      }
+    }
+    free(ctx);
+    flac_ctx = NULL;
+  }
+}
+
+static int flac_output_open(const char *fname, const char *comment)
+{
+  int fd;
+  int nch;
+  FLAC__StreamMetadata padding;
+  FLAC__StreamMetadata *metadata[4];
+  int num_metadata = 0;
+
+  FLAC_ctx *ctx;
+
+  if (flac_ctx == NULL)
+    flac_session_close();
+
+  if (!(flac_ctx = (FLAC_ctx *)calloc(sizeof(FLAC_ctx), 1))) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s", strerror(errno));
+    return -1;
+  }
+
+  ctx = flac_ctx;
+
+  ctx->in_bytes = ctx->out_bytes = 0;
+
+  if(strcmp(fname, "-") == 0) {
+    fd = 1; /* data to stdout */
+    if (comment == NULL)
+      comment = "(stdout)";
+  }
+  else {
+    /* Open the audio file */
+    fd = open(fname, FILE_OUTPUT_MODE);
+    if(fd < 0) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+               fname, strerror(errno));
+      return -1;
+    }
+    if(comment == NULL)
+      comment = fname;
+  }
+
+  dpm.fd = fd;
+  nch = (dpm.encoding & PE_MONO) ? 1 : 2;
+
+  if (0 < flac_options.padding) {
+    padding.is_last = 0;
+    padding.type = FLAC__METADATA_TYPE_PADDING;
+    padding.length = flac_options.padding;
+    metadata[num_metadata++] = &padding;
+  }
+
+#ifdef AU_OGGFLAC
+  if (flac_options.isogg) {
+    if ((ctx->encoder.ogg.stream = OggFLAC__stream_encoder_new()) == NULL) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot create OggFLAC stream");
+      flac_session_close();
+      return -1;
+    }
+
+    OggFLAC__stream_encoder_set_channels(ctx->encoder.ogg.stream, nch);
+    /* 16bps only */
+    OggFLAC__stream_encoder_set_bits_per_sample(ctx->encoder.ogg.stream, 16);
+
+    /* set sequential number for serial */
+    serial_number++;
+    if (serial_number == 1) {
+      srand(time(NULL));
+      serial_number = rand();
+    }
+    OggFLAC__stream_encoder_set_serial_number(ctx->encoder.ogg.stream, serial_number);
+
+    OggFLAC__stream_encoder_set_verify(ctx->encoder.ogg.stream, flac_options.verify);
+
+    if (!FLAC__format_sample_rate_is_valid(dpm.rate)) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "invalid sampling rate %d",
+               dpm.rate);
+      flac_session_close();
+      return -1;
+    }
+    OggFLAC__stream_encoder_set_sample_rate(ctx->encoder.ogg.stream, dpm.rate);
+
+    OggFLAC__stream_encoder_set_qlp_coeff_precision(ctx->encoder.ogg.stream, flac_options.qlp_coeff_precision);
+    /* expensive! */
+    OggFLAC__stream_encoder_set_do_qlp_coeff_prec_search(ctx->encoder.ogg.stream, flac_options.qlp_coeff_precision_search);
+
+    if (nch == 2) {
+      OggFLAC__stream_encoder_set_do_mid_side_stereo(ctx->encoder.ogg.stream, flac_options.mid_side);
+      OggFLAC__stream_encoder_set_loose_mid_side_stereo(ctx->encoder.ogg.stream, flac_options.adaptive_mid_side);
+    }
+
+    OggFLAC__stream_encoder_set_max_lpc_order(ctx->encoder.ogg.stream, flac_options.max_lpc_order);
+    OggFLAC__stream_encoder_set_min_residual_partition_order(ctx->encoder.ogg.stream, flac_options.min_residual_partition_order);
+    OggFLAC__stream_encoder_set_max_residual_partition_order(ctx->encoder.ogg.stream, flac_options.max_residual_partition_order);
+
+    OggFLAC__stream_encoder_set_blocksize(ctx->encoder.ogg.stream, flac_options.blocksize);
+
+    OggFLAC__stream_encoder_set_client_data(ctx->encoder.ogg.stream, ctx);
+
+    if (0 < num_metadata)
+      OggFLAC__stream_encoder_set_metadata(ctx->encoder.ogg.stream, metadata, num_metadata);
+
+    /* set callback */
+    OggFLAC__stream_encoder_set_write_callback(ctx->encoder.ogg.stream, ogg_stream_encoder_write_callback);
+
+    ctx->state.ogg = OggFLAC__stream_encoder_init(ctx->encoder.ogg.stream);
+    if (ctx->state.ogg != OggFLAC__STREAM_ENCODER_OK) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot create OggFLAC state (%s)",
+               OggFLAC__StreamEncoderStateString[ctx->state.ogg]);
+      flac_session_close();
+      return -1;
+    }
+  }
+  else
+#endif /* AU_OGGFLAC */
+  {
+    if ((ctx->encoder.flac.stream = FLAC__stream_encoder_new()) == NULL) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot create FLAC stream");
+      flac_session_close();
+      return -1;
+    }
+
+    FLAC__stream_encoder_set_channels(ctx->encoder.flac.stream, nch);
+    /* 16bps only */
+    FLAC__stream_encoder_set_bits_per_sample(ctx->encoder.flac.stream, 16);
+
+    FLAC__stream_encoder_set_verify(ctx->encoder.flac.stream, flac_options.verify);
+
+    if (!FLAC__format_sample_rate_is_valid(dpm.rate)) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "invalid sampling rate %d",
+               dpm.rate);
+      flac_session_close();
+      return -1;
+    }
+    FLAC__stream_encoder_set_sample_rate(ctx->encoder.flac.stream, dpm.rate);
+
+    FLAC__stream_encoder_set_qlp_coeff_precision(ctx->encoder.flac.stream, flac_options.qlp_coeff_precision);
+    /* expensive! */
+    FLAC__stream_encoder_set_do_qlp_coeff_prec_search(ctx->encoder.flac.stream, flac_options.qlp_coeff_precision_search);
+
+    if (nch == 2) {
+      FLAC__stream_encoder_set_do_mid_side_stereo(ctx->encoder.flac.stream, flac_options.mid_side);
+      FLAC__stream_encoder_set_loose_mid_side_stereo(ctx->encoder.flac.stream, flac_options.adaptive_mid_side);
+    }
+
+    FLAC__stream_encoder_set_max_lpc_order(ctx->encoder.flac.stream, flac_options.max_lpc_order);
+    FLAC__stream_encoder_set_min_residual_partition_order(ctx->encoder.flac.stream, flac_options.min_residual_partition_order);
+    FLAC__stream_encoder_set_max_residual_partition_order(ctx->encoder.flac.stream, flac_options.max_residual_partition_order);
+
+    FLAC__stream_encoder_set_blocksize(ctx->encoder.flac.stream, flac_options.blocksize);
+    FLAC__stream_encoder_set_client_data(ctx->encoder.flac.stream, ctx);
+
+    if (0 < num_metadata)
+      FLAC__stream_encoder_set_metadata(ctx->encoder.flac.stream, metadata, num_metadata);
+
+    /* set callback */
+    FLAC__stream_encoder_set_metadata_callback(ctx->encoder.flac.stream, flac_stream_encoder_metadata_callback);
+    FLAC__stream_encoder_set_write_callback(ctx->encoder.flac.stream, flac_stream_encoder_write_callback);
+
+    ctx->state.flac = FLAC__stream_encoder_init(ctx->encoder.flac.stream);
+    if (ctx->state.flac != FLAC__STREAM_ENCODER_OK) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot create FLAC state (%s)",
+               FLAC__StreamEncoderStateString[ctx->state.flac]);
+      flac_session_close();
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+static int auto_flac_output_open(const char *input_filename, const char *title)
+{
+  char *output_filename;
+
+#ifdef AU_OGGFLAC
+  if (flac_options.isogg) {
+#if !defined ( IA_W32GUI ) && !defined ( IA_W32G_SYN )
+  output_filename = create_auto_output_name(input_filename, "ogg", NULL, 0);
+#else
+  output_filename = create_auto_output_name(input_filename, "ogg", w32g_output_dir, w32g_auto_output_mode);
+#endif
+  }
+  else
+#endif /* AU_OGGFLAC */
+  {
+#if !defined ( IA_W32GUI ) && !defined ( IA_W32G_SYN )
+    output_filename = create_auto_output_name(input_filename, "flac", NULL, 0);
+#else
+    output_filename = create_auto_output_name(input_filename, "flac", w32g_output_dir, w32g_auto_output_mode);
+#endif
+  }
+  if (output_filename == NULL) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "unknown output file name");
+         return -1;
+  }
+  if ((flac_output_open(output_filename, input_filename)) == -1) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "files open failed %s->%s", output_filename, input_filename);
+    free(output_filename);
+    return -1;
+  }
+  if (dpm.name != NULL)
+    free(dpm.name);
+  dpm.name = output_filename;
+  ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Output %s", dpm.name);
+  return 0;
+}
+
+static int open_output(void)
+{
+  int include_enc, exclude_enc;  
+
+  include_enc = exclude_enc = 0;
+
+  /* only 16 bit is supported */
+  include_enc |= PE_16BIT | PE_SIGNED;
+  exclude_enc |= PE_BYTESWAP | PE_24BIT;
+  dpm.encoding = validate_encoding(dpm.encoding, include_enc, exclude_enc);
+
+#ifdef AU_OGGFLAC
+  if (flac_options.isogg) {
+    ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "*** cannot write back seekpoints when encoding to Ogg yet ***");
+    ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "*** and stream end will not be written.                   ***");
+  }
+#endif
+
+#if !defined ( IA_W32GUI ) && !defined ( IA_W32G_SYN )
+  if(dpm.name == NULL) {
+    dpm.flag |= PF_AUTO_SPLIT_FILE;
+    dpm.name = NULL;
+  } else {
+    dpm.flag &= ~PF_AUTO_SPLIT_FILE;
+    if(flac_output_open(dpm.name, NULL) == -1)
+      return -1;
+  }
+#else
+  if(w32g_auto_output_mode > 0) {
+    dpm.flag |= PF_AUTO_SPLIT_FILE;
+    dpm.name = NULL;
+    } else {
+    dpm.flag &= ~PF_AUTO_SPLIT_FILE;
+    if (flac_output_open(dpm.name, NULL) == -1)
+      return -1;
+  }
+#endif
+
+  return 0;
+}
+
+#ifdef AU_OGGFLAC
+static FLAC__StreamEncoderWriteStatus
+ogg_stream_encoder_write_callback(const OggFLAC__StreamEncoder *encoder,
+                                 const FLAC__byte buffer[],
+                                 unsigned bytes, unsigned samples,
+                                 unsigned current_frame, void *client_data)
+{
+  FLAC_ctx *ctx = (FLAC_ctx *)client_data;
+
+  ctx->out_bytes += bytes;
+
+  if (write(dpm.fd, buffer, bytes) != -1)
+    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+  else
+    return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
+}
+#endif
+static FLAC__StreamEncoderWriteStatus
+flac_stream_encoder_write_callback(const FLAC__StreamEncoder *encoder,
+                                  const FLAC__byte buffer[],
+                                  unsigned bytes, unsigned samples,
+                                  unsigned current_frame, void *client_data)
+{
+  FLAC_ctx *ctx = (FLAC_ctx *)client_data;
+
+  ctx->out_bytes += bytes;
+
+  if (write(dpm.fd, buffer, bytes) == bytes)
+    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+  else
+    return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
+}
+static void flac_stream_encoder_metadata_callback(const FLAC__StreamEncoder *encoder,
+                                                 const FLAC__StreamMetadata *metadata,
+                                                 void *client_data)
+{
+}
+
+static int output_data(char *buf, int32 nbytes)
+{
+  FLAC__int32 *oggbuf;
+  FLAC__int16 *s;
+  int i;
+  int nch = (dpm.encoding & PE_MONO) ? 1 : 2;
+
+  FLAC_ctx *ctx = flac_ctx;
+
+  if (dpm.fd < 0)
+    return 0;
+
+  if (ctx == NULL) {
+    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "FLAC stream is not initialized");
+    return -1;
+  }
+
+  oggbuf = (FLAC__int32 *)safe_malloc(nbytes * sizeof(FLAC__int32) / nch);
+
+  /*
+    packing 16 -> 32 bit sample
+  */
+  s = (FLAC__int16 *)buf;
+  for (i = 0; i < nbytes / nch; i++) {
+    oggbuf[i] = *s++;
+  }
+
+#ifdef AU_OGGFLAC
+  if (flac_options.isogg) {
+    ctx->state.ogg = OggFLAC__stream_encoder_get_state(ctx->encoder.ogg.stream);
+    if (ctx->state.ogg != OggFLAC__STREAM_ENCODER_OK) {
+      if (ctx->state.ogg == OggFLAC__STREAM_ENCODER_FLAC_STREAM_ENCODER_ERROR) {
+       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "FLAC stream verify error (%s)",
+                 FLAC__StreamDecoderStateString[OggFLAC__stream_encoder_get_verify_decoder_state(ctx->encoder.ogg.stream)]);
+      }
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot encode OggFLAC stream (%s)",
+               OggFLAC__StreamEncoderStateString[ctx->state.ogg]);
+      flac_session_close();
+      return -1;
+    }
+
+    if (!OggFLAC__stream_encoder_process_interleaved(ctx->encoder.ogg.stream, oggbuf,
+                                                    nbytes / nch / 2)) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot encode OggFLAC stream");
+      flac_session_close();
+      return -1;
+    }
+  }
+  else
+#endif /* AU_OGGFLAC */
+  {
+    ctx->state.flac = FLAC__stream_encoder_get_state(ctx->encoder.flac.stream);
+    if (ctx->state.flac != FLAC__STREAM_ENCODER_OK) {
+      if (ctx->state.flac == FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR |
+         FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA) {
+       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "FLAC stream verify error (%s)",
+                 FLAC__StreamDecoderStateString[FLAC__stream_encoder_get_verify_decoder_state(ctx->encoder.flac.stream)]);
+      }
+      else {
+       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot encode FLAC stream (%s)",
+                 FLAC__StreamEncoderStateString[ctx->state.flac]);
+      }
+      flac_session_close();
+      return -1;
+    }
+
+    if (!FLAC__stream_encoder_process_interleaved(ctx->encoder.flac.stream, oggbuf,
+                                                 nbytes / nch / 2 )) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "cannot encode FLAC stream");
+      flac_session_close();
+      return -1;
+    }
+  }
+  ctx->in_bytes += nbytes;
+
+  free(oggbuf);
+  return 0;
+}
+
+static void close_output(void)
+{
+  FLAC_ctx *ctx;
+
+  ctx = flac_ctx;
+
+  if (ctx == NULL)
+    return;
+
+  if (dpm.fd < 0) {
+    flac_session_close();
+    return;
+  }
+
+  if (flac_options.isogg) {
+#ifdef AU_OGGFLAC
+    if ((ctx->state.ogg = OggFLAC__stream_encoder_get_state(ctx->encoder.ogg.stream)) != OggFLAC__STREAM_ENCODER_OK) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "OggFLAC stream encoder is invalid (%s)",
+               OggFLAC__StreamEncoderStateString[ctx->state.ogg]);
+      /* fall through */
+    }
+  }
+  else
+#endif /* AU_OGGFLAC */
+  {
+    if ((ctx->state.flac = FLAC__stream_encoder_get_state(ctx->encoder.flac.stream)) != FLAC__STREAM_ENCODER_OK) {
+      ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "FLAC stream encoder is invalid (%s)",
+               OggFLAC__StreamEncoderStateString[ctx->state.flac]);
+      /* fall through */
+    }
+  }
+
+  ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Wrote %lu/%lu bytes(%lu%% compressed)",
+           ctx->out_bytes, ctx->in_bytes, ctx->out_bytes * 100 / ctx->in_bytes);
+
+  flac_session_close();
+}
+
+static int acntl(int request, void *arg)
+{
+  switch(request) {
+  case PM_REQ_PLAY_START:
+    if(dpm.flag & PF_AUTO_SPLIT_FILE)
+      return auto_flac_output_open(current_file_info->filename, current_file_info->seq_name);
+    break;
+  case PM_REQ_PLAY_END:
+    if(dpm.flag & PF_AUTO_SPLIT_FILE) {
+      close_output();
+      return 0;
+    }
+    break;
+  case PM_REQ_DISCARD:
+    return 0;
+  }
+  return -1;
+}
index 78cf22e..1653e30 100644 (file)
@@ -113,6 +113,9 @@ extern PlayMode list_play_mode;
 #ifdef AU_VORBIS
 extern PlayMode vorbis_play_mode;
 #endif /* AU_VORBIS */
+#ifdef AU_FLAC
+extern PlayMode flac_play_mode;
+#endif /* AU_FLAC */
 #ifdef AU_GOGO
 extern PlayMode gogo_play_mode;
 #endif /* AU_GOGO */
@@ -165,6 +168,9 @@ PlayMode *play_mode_list[] = {
 #ifdef AU_VORBIS
   &vorbis_play_mode,
 #endif /* AU_VORBIS */
+#ifdef AU_FLAC
+  &flac_play_mode,
+#endif /* AU_FLAC */
 #ifdef AU_GOGO
   &gogo_play_mode,
 #endif /* AU_GOGO */
index 0fc3eaa..533c6b1 100644 (file)
@@ -193,6 +193,14 @@ enum {
        TIM_OPT_FREQ_TABLE,
        TIM_OPT_PURE_INT,
        TIM_OPT_MODULE,
+#ifdef AU_FLAC
+       TIM_OPT_FLAC_VERIFY,
+       TIM_OPT_FLAC_PADDING,
+       TIM_OPT_FLAC_COMPRESSION_LEVEL,
+#ifdef AU_OGGFLAC
+       TIM_OPT_FLAC_OGGFLAC,
+#endif /* AU_OGGFLAC */
+#endif /* AU_FLAC */
        /* last entry */
        TIM_OPT_LAST = TIM_OPT_PURE_INT
 };
@@ -317,6 +325,14 @@ static const struct option longopts[] = {
        { "freq-table",             required_argument, NULL, TIM_OPT_FREQ_TABLE },
        { "pure-intonation",        optional_argument, NULL, TIM_OPT_PURE_INT },
        { "module",                 required_argument, NULL, TIM_OPT_MODULE },
+#ifdef AU_FLAC
+       { "flac-verify",            no_argument,       NULL, TIM_OPT_FLAC_VERIFY },
+       { "flac-padding",           required_argument, NULL, TIM_OPT_FLAC_PADDING },
+       { "flac-compression-level", required_argument, NULL, TIM_OPT_FLAC_COMPRESSION_LEVEL },
+#ifdef AU_OGGFLAC
+       { "oggflac",                no_argument,       NULL, TIM_OPT_FLAC_OGGFLAC },
+#endif /* AU_OGGFLAC */
+#endif /* AU_FLAC */
        { NULL,                     no_argument,       NULL, '\0'     }
 };
 #define INTERACTIVE_INTERFACE_IDS "kmqagrwAWP"
@@ -439,6 +455,14 @@ static inline void expand_escape_string(char *);
 static inline int parse_opt_Z(char *);
 static inline int parse_opt_Z1(const char *);
 static inline int parse_opt_default_module(const char *);
+#ifdef AU_FLAC
+static inline int parse_opt_flac_verify(const char *);
+static inline int parse_opt_flac_padding(const char *);
+static inline int parse_opt_flac_compression_level(const char *);
+#ifdef AU_OGGFLAC
+static inline int parse_opt_flac_oggflac(const char *);
+#endif /* AU_OGGFLAC */
+#endif /* AU_FLAC */
 __attribute__((noreturn))
 static inline int parse_opt_fail(const char *);
 static inline int set_value(int *, int, int, int, char *);
@@ -2776,6 +2800,18 @@ MAIN_INTERFACE int set_tim_opt_long(int c, char *optarg, int index)
                return parse_opt_Z1(arg);
        case TIM_OPT_MODULE:
                return parse_opt_default_module(arg);
+#ifdef AU_FLAC
+       case TIM_OPT_FLAC_VERIFY:
+               return parse_opt_flac_verify(arg);
+       case TIM_OPT_FLAC_PADDING:
+               return parse_opt_flac_padding(arg);
+       case TIM_OPT_FLAC_COMPRESSION_LEVEL:
+               return parse_opt_flac_compression_level(arg);
+#ifdef AU_OGGFLAC
+       case TIM_OPT_FLAC_OGGFLAC:
+               return parse_opt_flac_oggflac(arg);
+#endif /* AU_OGGFLAC */
+#endif /* AU_FLAC */
        default:
                ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
                                "[BUG] Inconceivable case branch %d", c);
@@ -3795,7 +3831,19 @@ static inline int parse_opt_h(const char *arg)
        fputs(NLS, fp);
 #ifdef AU_AO
        show_ao_device_info(fp);
+       fputs(NLS, fp);
 #endif /* AU_AO */
+       fputs(NLS, fp);
+#ifdef AU_FLAC
+       fputs("FLAC output format long options:" NLS
+"  --flac-verify                 Verify a correct encoding" NLS
+"  --flac-padding=n              Write a PADDING block of length n" NLS
+"  --flac-compression-level=n    Set compression level n:[0..8]" NLS
+#ifdef AU_OGGFLAC
+"  --oggflac                     Output OggFLAC stream(experimental)" NLS
+#endif /* AU_OGGFLAC */
+, fp);
+#endif /* AU_FLAC */
        close_pager(fp);
        exit(EXIT_SUCCESS);
 }
@@ -4558,6 +4606,37 @@ static inline int parse_opt_default_module(const char *arg)
        return 0;
 }
 
+#ifdef AU_FLAC
+extern void flac_set_option_verify(int verify);
+extern void flac_set_option_padding(int padding);
+extern void flac_compression_level_preset(int compression_level);
+
+static inline int parse_opt_flac_verify(const char *arg)
+{
+       flac_set_option_verify(1);
+       return 0;
+}
+static inline int parse_opt_flac_padding(const char *arg)
+{
+       flac_set_option_padding(atoi(arg));
+       return 0;
+}
+static inline int parse_opt_flac_compression_level(const char *arg)
+{
+       flac_set_compression_level(atoi(arg));
+       return 0;
+}
+#ifdef AU_OGGFLAC
+extern void flac_set_option_oggflac();
+
+static inline int parse_opt_flac_oggflac(const char *arg)
+{
+       flac_set_option_oggflac(1);
+       return 0;
+}
+#endif /* AU_OGGFLAC */
+#endif /* AU_FLAC */
+
 __attribute__((noreturn))
 static inline int parse_opt_fail(const char *arg)
 {
@@ -4923,7 +5002,6 @@ MAIN_INTERFACE int timidity_pre_load_configuration(void)
     /* UNIX */
     if(!read_config_file(CONFIG_FILE, 0)) {
                got_a_configuration = 1;
-               return 0;
        }
 #endif
 
@@ -4936,6 +5014,8 @@ MAIN_INTERFACE int timidity_pre_load_configuration(void)
     if(read_user_config_file())
        ctl->cmsg(CMSG_INFO, VERB_NOISY,
                  "Warning: Can't read ~/.timidity.cfg correctly");
+    else
+       got_a_configuration = 1;
     return 0;
 }