/* GSL - Generic Sound Layer
 * Copyright (C) 2002 Tim Janik
 *
 * 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 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.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include "gsl/gslloader.h"

#include <gsl/gsldatahandle.h>
#include "gsldatahandle-mad.h"

#include <unistd.h>


/* --- structures --- */
typedef struct
{
  GslWaveFileInfo wfi;
  guint           n_channels;
  gfloat	  mix_freq;
  gfloat	  osc_freq;
} FileInfo;


/* --- functions --- */
static GslWaveFileInfo*
mad_load_file_info (gpointer      data,
		    const gchar  *file_name,
		    GslErrorType *error_p)
{
  FileInfo *fi;
  guint n_channels;
  gfloat mix_freq;
  GslErrorType error;

  error = gsl_data_handle_mad_testopen (file_name, &n_channels, &mix_freq);
  if (error)
    {
      *error_p = error;
      return NULL;
    }

  fi = gsl_new_struct0 (FileInfo, 1);
  fi->wfi.n_waves = 1;	/* we support only a single MPEG stream */
  fi->wfi.waves = g_malloc0 (sizeof (fi->wfi.waves[0]) * fi->wfi.n_waves);
  fi->wfi.waves[0].name = g_strdup (file_name);
  fi->n_channels = n_channels;
  fi->mix_freq = mix_freq;
  fi->osc_freq = 440.0;	/* FIXME */

  return &fi->wfi;
}

static void
mad_free_file_info (gpointer         data,
		    GslWaveFileInfo *file_info)
{
  FileInfo *fi = (FileInfo*) file_info;
  guint i;

  for (i = 0; i < fi->wfi.n_waves; i++)
    g_free (fi->wfi.waves[i].name);
  g_free (fi->wfi.waves);
  gsl_delete_struct (FileInfo, fi);
}

static GslWaveDsc*
mad_load_wave_dsc (gpointer         data,
		   GslWaveFileInfo *file_info,
		   guint            nth_wave,
		   GslErrorType    *error_p)
{
  FileInfo *fi = (FileInfo*) file_info;
  GslWaveDsc *wdsc = gsl_new_struct0 (GslWaveDsc, 1);

  wdsc->name = g_strdup (fi->wfi.waves[0].name);
  wdsc->n_channels = fi->n_channels;
  wdsc->n_chunks = 1;
  wdsc->chunks = g_new0 (GslWaveChunkDsc, 1);
  wdsc->chunks[0].osc_freq = fi->osc_freq;
  wdsc->chunks[0].mix_freq = fi->mix_freq;

  return wdsc;
}

static void
mad_free_wave_dsc (gpointer    data,
		   GslWaveDsc *wdsc)
{
  g_free (wdsc->name);
  g_free (wdsc->chunks);
  gsl_delete_struct (GslWaveDsc, wdsc);
}

static GslDataHandle*
mad_create_chunk_handle (gpointer      data,
			 GslWaveDsc   *wdsc,
			 guint         nth_chunk,
			 GslErrorType *error_p)
{
  FileInfo *fi = (FileInfo*) wdsc->file_info;
  GslDataHandle *dhandle;

  g_return_val_if_fail (nth_chunk == 0, NULL);

  dhandle = gsl_data_handle_new_mad (fi->wfi.file_name);

  if (!dhandle)
    *error_p = GSL_ERROR_OPEN_FAILED;
  return dhandle;
}


#define	MAGIC_MPEG_HEADER	 "0 beshort   &0xffe0\n" /* MPEG */		\
                                 "2 ubyte&0x0c <0x0c\n"	 /* valid samplefreq */	\
                                 "2 ubyte&0xf0 <0xf0\n"	 /* valid bitrate */
#define	MAGIC_MPEG10_I		(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x18\n"   /* 1.0 */		\
                                 "1 byte&0x06 =0x06\n"   /* I */)
#define	MAGIC_MPEG10_II		(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x18\n"   /* 1.0 */		\
                                 "1 byte&0x06 =0x04\n"   /* II */)
#define	MAGIC_MPEG10_III	(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x18\n"   /* 1.0 */		\
                                 "1 byte&0x06 =0x02\n"   /* III */)
#define	MAGIC_MPEG20_I		(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x10\n"   /* 2.0 */		\
                                 "1 byte&0x06 =0x06\n"   /* I */)
#define	MAGIC_MPEG20_II		(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x10\n"   /* 2.0 */		\
                                 "1 byte&0x06 =0x04\n"   /* II */)
#define	MAGIC_MPEG20_III	(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x10\n"   /* 2.0 */		\
                                 "1 byte&0x06 =0x02\n"   /* III */)
#define	MAGIC_MPEG25_I		(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x00\n"   /* 2.5 */		\
                                 "1 byte&0x06 =0x06\n"   /* I */)
#define	MAGIC_MPEG25_II		(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x00\n"   /* 2.5 */		\
                                 "1 byte&0x06 =0x04\n"   /* II */)
#define	MAGIC_MPEG25_III	(MAGIC_MPEG_HEADER				\
                                 "1 byte&0x18 =0x00\n"   /* 2.5 */		\
                                 "1 byte&0x06 =0x02\n"   /* III */)
#define	MAGIC_RIFF_MPEG		("0  string  RIFF\n"				\
                                 "8  string  WAVE\n"				\
                                 "12 string  fmt\\s\n"	/* "fmt " */		\
                                 "20 leshort 80\n"	/* format: MPEG */)
#define	MAGIC_RIFF_MPEG_III	("0  string  RIFF\n"				\
                                 "8  string  WAVE\n"				\
                                 "12 string  fmt\\s\n"	/* "fmt " */		\
                                 "20 leshort 85\n"	/* format: MPEG III */)
#define	MAGIC_MPEG_ID3		("0  string  ID3\n"	/* ID3v2 tag for mp3 */	\
                                 "3  ubyte   <0xff\n"	/* major version */	\
                                 "4  ubyte   <0xff\n"	/* revision */)

void
_gsl_init_loader_mad (void)
{
  static const gchar *file_exts[] = {
    "mp1", "mp2", "mp3",
    NULL,
  };
  static const gchar *mime_types[] = {
    "audio/mp3", "audio/x-mp3", "audio/mpg3", "audio/x-mpg3", "audio/mpeg3", "audio/x-mpeg3",
    "audio/mp2", "audio/x-mp2", "audio/mpg2", "audio/x-mpg2", "audio/mpeg2", "audio/x-mpeg2",
    "audio/mp1", "audio/x-mp1", "audio/mpg1", "audio/x-mpg1", "audio/mpeg1", "audio/x-mpeg1",
    "audio/mpeg", "audio/x-mpeg",
    NULL,
  };
  static const gchar *magics[] = {
    MAGIC_MPEG10_I, MAGIC_MPEG10_II, MAGIC_MPEG10_III,
    MAGIC_MPEG20_I, MAGIC_MPEG20_II, MAGIC_MPEG20_III,
    MAGIC_MPEG25_I, MAGIC_MPEG25_II, MAGIC_MPEG25_III,
    MAGIC_RIFF_MPEG, MAGIC_RIFF_MPEG_III,
    MAGIC_MPEG_ID3,
    NULL,
  };
  static GslLoader loader = {
    "MPEG Audio (MAD: MPEG 1.0/2.0/2.5 Layer III/II/I Decoder)",
    file_exts,
    mime_types,
    magics,
    0,	/* priority */
    NULL,
    mad_load_file_info,
    mad_free_file_info,
    mad_load_wave_dsc,
    mad_free_wave_dsc,
    mad_create_chunk_handle,
  };
  static gboolean initialized = FALSE;

  g_assert (initialized == FALSE);
  initialized = TRUE;

  if (GSL_HAVE_LIBMAD)
    gsl_loader_register (&loader);
}
