• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/xmms/sample.head.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 
00018 #include <glib.h>
00019 #include <math.h>
00020 #include "xmmspriv/xmms_sample.h"
00021 #include "xmms/xmms_medialib.h"
00022 #include "xmms/xmms_object.h"
00023 #include "xmms/xmms_log.h"
00024 
00025 /**
00026   * @defgroup Sample Sample Converter
00027   * @ingroup XMMSServer
00028   * @brief Convert sample formats back and forth.
00029   * @{
00030   */
00031 
00032 /**
00033  * The converter module
00034  */
00035 struct xmms_sample_converter_St {
00036     xmms_object_t obj;
00037 
00038     xmms_stream_type_t *from;
00039     xmms_stream_type_t *to;
00040 
00041     gboolean same;
00042     gboolean resample;
00043 
00044     /* buffer for result */
00045     guint bufsiz;
00046     xmms_sample_t *buf;
00047 
00048     guint interpolator_ratio;
00049     guint decimator_ratio;
00050 
00051     guint offset;
00052 
00053     xmms_sample_t *state;
00054 
00055     xmms_sample_conv_func_t func;
00056 
00057 };
00058 
00059 static void recalculate_resampler (xmms_sample_converter_t *conv, guint from, guint to);
00060 static xmms_sample_conv_func_t
00061 xmms_sample_conv_get (guint inchannels, xmms_sample_format_t intype,
00062                       guint outchannels, xmms_sample_format_t outtype,
00063                       gboolean resample);
00064 
00065 
00066 
00067 static void
00068 xmms_sample_converter_destroy (xmms_object_t *obj)
00069 {
00070     xmms_sample_converter_t *conv = (xmms_sample_converter_t *) obj;
00071 
00072     g_free (conv->buf);
00073     g_free (conv->state);
00074 }
00075 
00076 static xmms_sample_converter_t *
00077 xmms_sample_converter_init (xmms_stream_type_t *from, xmms_stream_type_t *to)
00078 {
00079     xmms_sample_converter_t *conv = xmms_object_new (xmms_sample_converter_t, xmms_sample_converter_destroy);
00080     gint fformat, fsamplerate, fchannels;
00081     gint tformat, tsamplerate, tchannels;
00082 
00083     fformat = xmms_stream_type_get_int (from, XMMS_STREAM_TYPE_FMT_FORMAT);
00084     fsamplerate = xmms_stream_type_get_int (from, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00085     fchannels = xmms_stream_type_get_int (from, XMMS_STREAM_TYPE_FMT_CHANNELS);
00086     tformat = xmms_stream_type_get_int (to, XMMS_STREAM_TYPE_FMT_FORMAT);
00087     tsamplerate = xmms_stream_type_get_int (to, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00088     tchannels = xmms_stream_type_get_int (to, XMMS_STREAM_TYPE_FMT_CHANNELS);
00089 
00090     if (tsamplerate == -1)
00091         tsamplerate = fsamplerate;
00092 
00093     conv->from = from;
00094     conv->to = to;
00095 
00096     conv->resample = fsamplerate != tsamplerate;
00097 
00098     conv->func = xmms_sample_conv_get (fchannels, fformat,
00099                                        tchannels, tformat,
00100                                        conv->resample);
00101 
00102     if (!conv->func) {
00103         xmms_object_unref (conv);
00104         xmms_log_error ("Can not convert between requested formats");
00105         return NULL;
00106     }
00107 
00108     if (conv->resample)
00109         recalculate_resampler (conv, fsamplerate, tsamplerate);
00110 
00111     return conv;
00112 }
00113 
00114 /**
00115  * Return the audio format used by the converter as source
00116  */
00117 xmms_stream_type_t *
00118 xmms_sample_converter_get_from (xmms_sample_converter_t *conv)
00119 {
00120     g_return_val_if_fail (conv, NULL);
00121 
00122     return conv->from;
00123 }
00124 
00125 /**
00126  * Return the audio format used by the converter as target
00127  */
00128 xmms_stream_type_t *
00129 xmms_sample_converter_get_to (xmms_sample_converter_t *conv)
00130 {
00131     g_return_val_if_fail (conv, NULL);
00132 
00133     return conv->to;
00134 }
00135 
00136 /**
00137  */
00138 void
00139 xmms_sample_converter_to_medialib (xmms_sample_converter_t *conv, xmms_medialib_entry_t entry)
00140 {
00141 #if 0
00142     xmms_medialib_session_t *session;
00143 
00144     session = xmms_medialib_begin_write ();
00145     xmms_medialib_entry_property_set_str (session, entry,
00146                                           XMMS_MEDIALIB_ENTRY_PROPERTY_FMT_SAMPLEFMT_IN,
00147                                           xmms_sample_name_get (conv->from->format));
00148     xmms_medialib_entry_property_set_int (session, entry,
00149                                           XMMS_MEDIALIB_ENTRY_PROPERTY_FMT_SAMPLERATE_IN,
00150                                           conv->from->samplerate);
00151     xmms_medialib_entry_property_set_int (session, entry,
00152                                           XMMS_MEDIALIB_ENTRY_PROPERTY_FMT_CHANNELS_IN,
00153                                           conv->from->channels);
00154 
00155     xmms_medialib_entry_property_set_str (session, entry,
00156                                           XMMS_MEDIALIB_ENTRY_PROPERTY_FMT_SAMPLEFMT_OUT,
00157                                           xmms_sample_name_get (conv->to->format));
00158     xmms_medialib_entry_property_set_int (session, entry,
00159                                           XMMS_MEDIALIB_ENTRY_PROPERTY_FMT_SAMPLERATE_OUT,
00160                                           conv->to->samplerate);
00161     xmms_medialib_entry_property_set_int (session, entry,
00162                                           XMMS_MEDIALIB_ENTRY_PROPERTY_FMT_CHANNELS_OUT,
00163                                           conv->to->channels);
00164 
00165     xmms_medialib_end (session);
00166 #endif
00167 }
00168 
00169 /**
00170  * Find the best pair of formats
00171  */
00172 xmms_sample_converter_t *
00173 xmms_sample_audioformats_coerce (xmms_stream_type_t *in, const GList *goal_types)
00174 {
00175     xmms_stream_type_t *best = NULL;
00176     const GList *on;
00177 /*  gint bestscore = GINT_MAX;*/
00178     gint bestscore = 100000;
00179     gint format, samplerate, channels;
00180     gint gformat, gsamplerate, gchannels;
00181 
00182     format = xmms_stream_type_get_int (in, XMMS_STREAM_TYPE_FMT_FORMAT);
00183     samplerate = xmms_stream_type_get_int (in, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00184     channels = xmms_stream_type_get_int (in, XMMS_STREAM_TYPE_FMT_CHANNELS);
00185 
00186     if (format == -1 || samplerate == -1 || channels == -1) {
00187         xmms_log_info ("In-type lacks format, samplerate or channels");
00188         return NULL;
00189     }
00190 
00191     for (on = goal_types ; on; on = g_list_next (on)) {
00192         xmms_stream_type_t *goal = on->data;
00193         const gchar *mime;
00194         gint score = 0;
00195 
00196         mime = xmms_stream_type_get_str (goal, XMMS_STREAM_TYPE_MIMETYPE);
00197         if (strcmp (mime, "audio/pcm") != 0) {
00198             continue;
00199         }
00200 
00201         gformat = xmms_stream_type_get_int (goal, XMMS_STREAM_TYPE_FMT_FORMAT);
00202         gsamplerate = xmms_stream_type_get_int (goal, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00203         gchannels = xmms_stream_type_get_int (goal, XMMS_STREAM_TYPE_FMT_CHANNELS);
00204         if (gsamplerate == -1) {
00205             gsamplerate = samplerate;
00206         }
00207         if (gformat == -1 || gchannels == -1) {
00208             continue;
00209         }
00210 
00211 
00212         if (gchannels > channels) {
00213             /* we loose no quality, just cputime */
00214             score += gchannels - channels;
00215         } else if (gchannels < gchannels) {
00216             /* quality loss! */
00217             score += 10 * (channels - gchannels);
00218         }
00219 
00220         /* the format enum should be ordered in
00221            quality order */
00222         if (gformat > format) {
00223             /* we loose no quality, just cputime */
00224             score += gformat - format;
00225         } else if (gformat < format) {
00226             /* quality loss! */
00227             score += 10 * (format - gformat);
00228         }
00229 
00230 
00231         if (gsamplerate > samplerate) {
00232             /* we loose no quality, just cputime */
00233             score += 2 * gsamplerate / samplerate;
00234         } else if (gsamplerate < samplerate) {
00235             /* quality loss! */
00236             score += 20 * samplerate / gsamplerate;
00237         }
00238 
00239         if (score < bestscore) {
00240             best = goal;
00241             bestscore = score;
00242         }
00243     }
00244 
00245     if (!best) {
00246         xmms_log_error ("Couldn't convert sample format to any of the %d goal formats", g_list_length (goal_types));
00247         return NULL;
00248     }
00249 
00250     return xmms_sample_converter_init (in, best);
00251 
00252 }
00253 
00254 /**
00255  * convert from milliseconds to samples for this format.
00256  */
00257 guint
00258 xmms_sample_ms_to_samples (const xmms_stream_type_t *st, guint milliseconds)
00259 {
00260     gint rate;
00261     rate = xmms_stream_type_get_int (st, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00262     return (guint)(((gdouble) rate) * milliseconds / 1000);
00263 }
00264 
00265 /**
00266  * Convert from samples to milliseconds for this format
00267  */
00268 guint
00269 xmms_sample_samples_to_ms (const xmms_stream_type_t *st, guint samples)
00270 {
00271     gint rate;
00272     rate = xmms_stream_type_get_int (st, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00273     return (guint) (((gdouble)samples) * 1000.0 / rate);
00274 }
00275 
00276 /**
00277  * Convert from bytes to milliseconds for this format
00278  */
00279 guint
00280 xmms_sample_bytes_to_ms (const xmms_stream_type_t *st, guint bytes)
00281 {
00282     guint samples = bytes / xmms_sample_frame_size_get (st);
00283     return xmms_sample_samples_to_ms (st, samples);
00284 }
00285 
00286 gint
00287 xmms_sample_frame_size_get (const xmms_stream_type_t *st)
00288 {
00289     gint format, channels;
00290     format = xmms_stream_type_get_int (st, XMMS_STREAM_TYPE_FMT_FORMAT);
00291     channels = xmms_stream_type_get_int (st, XMMS_STREAM_TYPE_FMT_CHANNELS);
00292     return xmms_sample_size_get (format) * channels;
00293 }
00294 
00295 static void
00296 recalculate_resampler (xmms_sample_converter_t *conv, guint from, guint to)
00297 {
00298     guint a,b;
00299 
00300     /* calculate ratio */
00301     if (from > to){
00302         a = from;
00303         b = to;
00304     } else {
00305         b = to;
00306         a = from;
00307     }
00308 
00309     while (b != 0) { /* good 'ol euclid is helpful as usual */
00310         guint t = a % b;
00311         a = b;
00312         b = t;
00313     }
00314 
00315     XMMS_DBG ("Resampling ratio: %d:%d",
00316               from / a, to / a);
00317 
00318     conv->interpolator_ratio = to/a;
00319     conv->decimator_ratio = from/a;
00320 
00321     conv->state = g_malloc0 (xmms_sample_frame_size_get (conv->from));
00322 
00323     /*
00324      * calculate filter here
00325      *
00326      * We don't use no stinkning filter. Maybe we should,
00327      * but I'm deaf anyway, I wont hear any difference.
00328      */
00329 
00330 }
00331 
00332 
00333 /**
00334  * do the actual converstion between two audio formats.
00335  */
00336 void
00337 xmms_sample_convert (xmms_sample_converter_t *conv, xmms_sample_t *in, guint len, xmms_sample_t **out, guint *outlen)
00338 {
00339     int inusiz, outusiz;
00340     int olen;
00341     guint res;
00342 
00343     inusiz = xmms_sample_frame_size_get (conv->from);
00344 
00345     g_return_if_fail (len % inusiz == 0);
00346 
00347     if (conv->same) {
00348         *outlen = len;
00349         *out = in;
00350         return;
00351     }
00352 
00353     len /= inusiz;
00354 
00355     outusiz = xmms_sample_frame_size_get (conv->to);
00356 
00357     if (conv->resample) {
00358         olen = (len * conv->interpolator_ratio / conv->decimator_ratio) * outusiz + outusiz;
00359     } else {
00360         olen = len * outusiz;
00361     }
00362     if (olen > conv->bufsiz) {
00363         void *t;
00364         t = g_realloc (conv->buf, olen);
00365         g_assert (t); /* XXX */
00366         conv->buf = t;
00367         conv->bufsiz = olen;
00368     }
00369 
00370     res = conv->func (conv, in, len, conv->buf);
00371 
00372     *outlen = res * outusiz;
00373     *out = conv->buf;
00374 
00375 }
00376 
00377 gint64
00378 xmms_sample_convert_scale (xmms_sample_converter_t *conv, gint64 samples)
00379 {
00380     /* this isn't 100% accurate, we should take care
00381        of rounding here and set conv->offset, but noone
00382        will notice, except when reading this comment :) */
00383 
00384     if (!conv->resample)
00385         return samples;
00386     return samples * conv->decimator_ratio / conv->interpolator_ratio;
00387 }
00388 
00389 gint64
00390 xmms_sample_convert_rev_scale (xmms_sample_converter_t *conv, gint64 samples)
00391 {
00392     if (!conv->resample)
00393         return samples;
00394     return samples * conv->interpolator_ratio / conv->decimator_ratio;
00395 }
00396 
00397 void
00398 xmms_sample_convert_reset (xmms_sample_converter_t *conv)
00399 {
00400     if (conv->resample) {
00401         conv->offset = 0;
00402         memset (conv->state, 0, xmms_sample_frame_size_get (conv->from));
00403     }
00404 }
00405 
00406 /**
00407  * @}
00408  */

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1