/* $Id$ */ /* * Copyright (c) 2005-2006 Jean-François Wauthy (pollux@xfce.org) * Copyright (c) 2008 David Mohr (dmohr@mcbf.net) * * 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 Library 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. */ #ifdef HAVE_CONFIG_H #include #endif /* !HAVE_CONFIG_H */ #ifdef HAVE_STRING_H #include #endif #include #include #include #include #include #include #include #include #include "xfburn-global.h" #include "xfburn-error.h" #include "xfburn-transcoder-basic.h" /** Prototypes **/ /* class initialization */ static void xfburn_transcoder_basic_class_init (XfburnTranscoderBasicClass * klass); static void xfburn_transcoder_basic_init (XfburnTranscoderBasic * obj); static void xfburn_transcoder_basic_finalize (GObject * object); static void transcoder_interface_init (XfburnTranscoderInterface *iface, gpointer iface_data); /* internals */ static const gchar * get_name (XfburnTranscoder *trans); static const gchar * get_description (XfburnTranscoder *trans); static gboolean is_initialized (XfburnTranscoder *trans, GError **error); static XfburnAudioTrack * get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error); static gboolean has_audio_ext (const gchar *path); static gboolean is_valid_wav (const gchar *path); static gboolean valid_wav_headers (guchar header[44]); static struct burn_track * create_burn_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error); static gboolean needs_swap (char header[44]); #define XFBURN_TRANSCODER_BASIC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), XFBURN_TYPE_TRANSCODER_BASIC, XfburnTranscoderBasicPrivate)) enum { LAST_SIGNAL, }; typedef struct { gboolean dummy; } XfburnTranscoderBasicPrivate; /* globals */ static const gchar *errormsg_libburn_setup = "An error occurred while setting the burning backend up"; /*********************/ /* class declaration */ /*********************/ static GObject *parent_class = NULL; //static guint signals[LAST_SIGNAL]; GtkType xfburn_transcoder_basic_get_type () { static GtkType type = 0; if (type == 0) { static const GTypeInfo our_info = { sizeof (XfburnTranscoderBasicClass), NULL, NULL, (GClassInitFunc) xfburn_transcoder_basic_class_init, NULL, NULL, sizeof (XfburnTranscoderBasic), 0, (GInstanceInitFunc) xfburn_transcoder_basic_init, NULL }; static const GInterfaceInfo trans_info = { (GInterfaceInitFunc) transcoder_interface_init, NULL, NULL }; type = g_type_register_static (G_TYPE_OBJECT, "XfburnTranscoderBasic", &our_info, 0); g_type_add_interface_static (type, XFBURN_TYPE_TRANSCODER, &trans_info); } return type; } static void xfburn_transcoder_basic_class_init (XfburnTranscoderBasicClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (XfburnTranscoderBasicPrivate)); parent_class = g_type_class_peek_parent (klass); object_class->finalize = xfburn_transcoder_basic_finalize; /* signals[VOLUME_CHANGED] = g_signal_new ("volume-changed", XFBURN_TYPE_TRANSCODER_BASIC, G_SIGNAL_ACTION, G_STRUCT_OFFSET (XfburnTranscoderBasicClass, volume_changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); */ } static void xfburn_transcoder_basic_init (XfburnTranscoderBasic * obj) { //XfburnTranscoderBasicPrivate *priv = XFBURN_TRANSCODER_BASIC_GET_PRIVATE (obj); } static void xfburn_transcoder_basic_finalize (GObject * object) { //XfburnTranscoderBasicPrivate *priv = XFBURN_TRANSCODER_BASIC_GET_PRIVATE (object); G_OBJECT_CLASS (parent_class)->finalize (object); } static void transcoder_interface_init (XfburnTranscoderInterface *iface, gpointer iface_data) { iface->get_name = get_name; iface->get_description = get_description; iface->is_initialized = is_initialized; iface->get_audio_track = get_audio_track; iface->create_burn_track = create_burn_track; } /* */ /* internals */ /* */ static const gchar * get_name (XfburnTranscoder *trans) { return _("basic"); } static const gchar * get_description (XfburnTranscoder *trans) { return _("The basic transcoder is built in,\n" "and does not require any library.\n" "But it can only handle uncompressed\n" ".wav files.\n" "If you would like to create audio\n" "compositions from different types of\n" "audio files, please compile with\n" "gstreamer support."); } static gboolean is_initialized (XfburnTranscoder *trans, GError **error) { /* there is nothing to check, because there is nothing to initialize */ return TRUE; } static XfburnAudioTrack * get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error) { XfburnAudioTrack *atrack; struct stat s; if (!has_audio_ext (fn)) { g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_AUDIO_EXT, _("File %s does not have a .wav extension"), fn); return NULL; } if (!is_valid_wav (fn)) { g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_AUDIO_FORMAT, _("File %s does not contain uncompressed PCM wave audio"), fn); return NULL; } if (stat (fn, &s) != 0) { g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_STAT, _("Could not stat %s: %s"), fn, g_strerror (errno)); return NULL; } atrack = g_new0 (XfburnAudioTrack, 1); /* FIXME: when do we free inputfile?? */ atrack->inputfile = g_strdup (fn); atrack->pos = -1; atrack->length = (s.st_size - 44) / PCM_BYTES_PER_SECS; atrack->sectors = (s.st_size / AUDIO_BYTES_PER_SECTOR); if (s.st_size % AUDIO_BYTES_PER_SECTOR > 0) atrack->sectors++; return atrack; } static gboolean has_audio_ext (const gchar *path) { int len = strlen (path); const gchar *ext = path + len - 3; return (strcmp (ext, "wav") == 0); } static gboolean is_valid_wav (const gchar *path) { int fd; guchar header[44]; gboolean ret; fd = open (path, 0); if (fd == -1) { xfce_warn (_("Could not open %s."), path); return FALSE; } read (fd, header, 44); ret = valid_wav_headers (header); close (fd); return ret; } /* * Simple check of .wav headers, most info from * http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ * * I did not take particular care to make sure this check is complete. * As far as I can tell yes, but not much effort was put into verifying it. * FIXME: this works on x86, and does not consider endianness! */ static gboolean valid_wav_headers (guchar header[44]) { /* check if first 4 bytes are RIFF or RIFX */ if (header[0] == 'R' && header[1] == 'I' && header[2] == 'F') { if (!(header[3] == 'X' || header[3] == 'F')) { g_warning ("File not in riff format"); return FALSE; } } /* check if bytes 8-11 are WAVE */ if (!(header[8] == 'W' && header[9] == 'A' && header[10] == 'V' && header[11] == 'E')) { g_warning ("RIFF file not in WAVE format"); return FALSE; } /* subchunk starts with 'fmt ' */ if (!(header[12] == 'f' && header[13] == 'm' && header[14] == 't' && header[15] == ' ')) { g_warning ("Could not find format subchunk"); return FALSE; } /* check for PCM format */ if (header[16] != 16 || header[20] != 1) { g_warning ("Not in PCM format"); return FALSE; } /* check for stereo */ if (header[22] != 2) { g_warning ("Not in stereo"); return FALSE; } /* check for 44100 Hz sample rate, * being lazy here and just compare the bytes to what I know they should be */ if (!(header[24] == 0x44 && header[25] == 0xAC && header[26] == 0 && header[27] == 0)) { g_warning ("Does not have a sample rate of 44100 Hz"); return FALSE; } return TRUE; } static struct burn_track * create_burn_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error) { /* XfburnTranscoderBasic *basic = XFBURN_TRANSCODER_BASIC (trans); XfburnTranscoderBasicPrivate *priv= XFBURN_TRANSCODER_BASIC_GET_PRIVATE (basic); */ char header[44]; struct burn_track *track; atrack->fd = open (atrack->inputfile, 0); if (atrack->fd == -1) { g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_COULD_NOT_OPEN_FILE, _("Could not open %s: %s"), atrack->inputfile, g_strerror (errno)); return NULL; } /* advance the fd so that libburn skips the header, * also allows us to check for byte swapping */ read (atrack->fd, header, 44); atrack->src = burn_fd_source_new (atrack->fd, -1 , 0); if (atrack->src == NULL) { g_warning ("Could not create burn_source from %s!", atrack->inputfile); g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_BURN_SOURCE, _(errormsg_libburn_setup)); return NULL; } track = burn_track_create (); if (burn_track_set_source (track, atrack->src) != BURN_SOURCE_OK) { g_warning ("Could not add source to track %s!", atrack->inputfile); g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_BURN_SOURCE, _(errormsg_libburn_setup)); return NULL; } if (needs_swap (header)) burn_track_set_byte_swap (track, TRUE); burn_track_define_data (track, 0, 0, 1, BURN_AUDIO); return track; } static gboolean needs_swap (char header[44]) { if (header[0] == 'R' && header[1] == 'I' && header[2] == 'F' && header[3] == 'X') return TRUE; else return FALSE; } /* */ /* public */ /* */ GObject * xfburn_transcoder_basic_new () { return g_object_new (XFBURN_TYPE_TRANSCODER_BASIC, NULL); }