Example #1
0
soundfile_t *
soundfile_open_write(const char *path, int n_channels, double sampling_rate) {
	dp(30, "path=%s n_channels=%d sampling_rate=%g\n", path, n_channels, sampling_rate);
	soundfile_t *s = salloc(sizeof *s);
	s->m = sft_write;
	s->channels = n_channels;
	s->samplerate = sampling_rate;
	if 	(g_regex_match_simple ("\\.wv$", path, 0, 0)) {
		s->t = sft_wavpack;
		s->file = fopen(path, "w+b");
		if (!s->file)
			die("can not open output file '%s'", path);
		WavpackContext *wpc = s->p = WavpackOpenFileOutput(write_block, s, NULL);
		if (!s->p)
			die("can not open input file '%s'", path);
  		WavpackConfig config = {0};
		config.bytes_per_sample = 2;
		s->bits_per_sample = config.bits_per_sample = 16;
		config.channel_mask = n_channels == 1 ? 4 : 3; // Microsoft standard: 4 == mono, 3 == stereo
		config.num_channels = n_channels;
		config.sample_rate = sampling_rate;
		if (!WavpackSetConfiguration(wpc, &config, -1))
        	die("WavpackSetConfiguration failed: %s\n", WavpackGetErrorMessage(wpc));
		if (!WavpackPackInit(wpc))
        	die("WavpackPackInit failed: %s\n", WavpackGetErrorMessage(wpc));
			
	} else {
		SF_INFO	 	outfile_info = {0};
		outfile_info.samplerate = sampling_rate;
		outfile_info.channels = n_channels;
		outfile_info.format = SF_FORMAT_PCM_16;
		
		if (g_regex_match_simple ("\\.flac$", path, 0, 0))
			outfile_info.format |= SF_FORMAT_FLAC;
		else
			outfile_info.format |= SF_FORMAT_WAV;
		if (strcmp(path, "-"))
			s->p = sf_open(path, SFM_WRITE, &outfile_info);
		else
			s->p = sf_open_fd(1, SFM_WRITE, &outfile_info, 0);  // write to stdout if name is "-"
		if (!s->p)
			die("can not open output file '%s'", path);
		s->t = sft_libsndfile;
	}
	return s;
}
Example #2
0
void
soundfile_close(soundfile_t *sf) {
	dp(30, "sf=%p \n", sf);
	if (sf->t == sft_libsndfile) {
		sf_close(sf->p);
	} else {
		if (sf->m == sft_write) {
		    if (!WavpackFlushSamples(sf->p))
           		die("WavpackFlushSamples failed: %s\n", WavpackGetErrorMessage(sf->p));
			WavpackCloseFile(sf->p);
			fclose(sf->file);
		}
	}
}
Example #3
0
void
soundfile_write_header(soundfile_t *sf, void *header, int h_size) {
	dp(30, "sf=%p header=%p h_size=%d\n", sf, header, h_size);
	if (sf->t == sft_wavpack) {
		WavpackContext *wpc = sf->p;
		if (!WavpackAddWrapper(wpc, header, h_size)) {
			sdie(sf->p, "error adding header to wavpack: %s\n", WavpackGetErrorMessage(wpc));
		}
	} else if (sf->t == sft_libsndfile) {
		sdie(sf->p, "can't add header to sndfile with this function");
	} else {
		sdie(sf->p, "can't yet add header to %d type files", sf->t);
	}
}
Example #4
0
void
soundfile_write_double(soundfile_t *sf, double *buffer, index_t n_frames) {
	if (sf->t == sft_libsndfile) {
		sf_count_t count; 
		if ((count = sf_writef_double(sf->p, buffer, n_frames)) != n_frames)
			sdie(sf->p, "sf_writef_double returned %d (expected %d): ", count, n_frames);
	} else {
		WavpackContext *wpc = sf->p;
		int32_t sample_buffer[n_frames*sf->channels];
		for (int i = 0; i < 10; i++)
			dp(30, "buffer[%d]=%g\n", i, (double)buffer[i]);
		
		double multiplier = 1L << (sf->bits_per_sample - 1);
		for (int i = 0; i < n_frames*sf->channels; i++)
  	  		sample_buffer[i] = multiplier*buffer[i]; // FIXME may overflow buffer[1] == 1
		if (!WavpackPackSamples(sf->p, sample_buffer, n_frames))
            die("WavpackPackSamples failed: %s\n", WavpackGetErrorMessage(wpc));
	}
}
Example #5
0
static GstFlowReturn
gst_wavpack_dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf)
{
  GstWavpackDec *dec;
  GstBuffer *outbuf = NULL;
  GstFlowReturn ret = GST_FLOW_OK;
  WavpackHeader wph;
  int32_t decoded, unpacked_size;
  gboolean format_changed;
  gint width, depth, i, j, max;
  gint32 *dec_data = NULL;
  guint8 *out_data;
  GstMapInfo map, omap;

  dec = GST_WAVPACK_DEC (bdec);

  g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);

  gst_buffer_map (buf, &map, GST_MAP_READ);

  /* check input, we only accept framed input with complete chunks */
  if (map.size < sizeof (WavpackHeader))
    goto input_not_framed;

  if (!gst_wavpack_read_header (&wph, map.data))
    goto invalid_header;

  if (map.size < wph.ckSize + 4 * 1 + 4)
    goto input_not_framed;

  if (!(wph.flags & INITIAL_BLOCK))
    goto input_not_framed;

  dec->wv_id.buffer = map.data;
  dec->wv_id.length = map.size;
  dec->wv_id.position = 0;

  /* create a new wavpack context if there is none yet but if there
   * was already one (i.e. caps were set on the srcpad) check whether
   * the new one has the same caps */
  if (!dec->context) {
    gchar error_msg[80];

    dec->context = WavpackOpenFileInputEx (dec->stream_reader,
        &dec->wv_id, NULL, error_msg, OPEN_STREAMING, 0);

    /* expect this to work */
    if (!dec->context) {
      GST_WARNING_OBJECT (dec, "Couldn't decode buffer: %s", error_msg);
      goto context_failed;
    }
  }

  g_assert (dec->context != NULL);

  format_changed =
      (dec->sample_rate != WavpackGetSampleRate (dec->context)) ||
      (dec->channels != WavpackGetNumChannels (dec->context)) ||
      (dec->depth != WavpackGetBytesPerSample (dec->context) * 8) ||
      (dec->channel_mask != WavpackGetChannelMask (dec->context));

  if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (dec)) ||
      format_changed) {
    gint channel_mask;

    dec->sample_rate = WavpackGetSampleRate (dec->context);
    dec->channels = WavpackGetNumChannels (dec->context);
    dec->depth = WavpackGetBytesPerSample (dec->context) * 8;

    channel_mask = WavpackGetChannelMask (dec->context);
    if (channel_mask == 0)
      channel_mask = gst_wavpack_get_default_channel_mask (dec->channels);

    dec->channel_mask = channel_mask;

    gst_wavpack_dec_negotiate (dec);

    /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something
     * is decoded or after the format has changed */
    gst_wavpack_dec_post_tags (dec);
  }

  /* alloc output buffer */
  dec_data = g_malloc (4 * wph.block_samples * dec->channels);

  /* decode */
  decoded = WavpackUnpackSamples (dec->context, dec_data, wph.block_samples);
  if (decoded != wph.block_samples)
    goto decode_error;

  unpacked_size = (dec->width / 8) * wph.block_samples * dec->channels;
  outbuf = gst_buffer_new_and_alloc (unpacked_size);

  /* legacy; pass along offset, whatever that might entail */
  GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buf);

  gst_buffer_map (outbuf, &omap, GST_MAP_WRITE);
  out_data = omap.data;

  width = dec->width;
  depth = dec->depth;
  max = dec->channels * wph.block_samples;
  if (width == 8) {
    gint8 *outbuffer = (gint8 *) out_data;
    gint *reorder_map = dec->channel_reorder_map;

    for (i = 0; i < max; i += dec->channels) {
      for (j = 0; j < dec->channels; j++)
        *outbuffer++ = (gint8) (dec_data[i + reorder_map[j]]);
    }
  } else if (width == 16) {
    gint16 *outbuffer = (gint16 *) out_data;
    gint *reorder_map = dec->channel_reorder_map;

    for (i = 0; i < max; i += dec->channels) {
      for (j = 0; j < dec->channels; j++)
        *outbuffer++ = (gint16) (dec_data[i + reorder_map[j]]);
    }
  } else if (dec->width == 32) {
    gint32 *outbuffer = (gint32 *) out_data;
    gint *reorder_map = dec->channel_reorder_map;

    if (width != depth) {
      for (i = 0; i < max; i += dec->channels) {
        for (j = 0; j < dec->channels; j++)
          *outbuffer++ =
              (gint32) (dec_data[i + reorder_map[j]] << (width - depth));
      }
    } else {
      for (i = 0; i < max; i += dec->channels) {
        for (j = 0; j < dec->channels; j++)
          *outbuffer++ = (gint32) (dec_data[i + reorder_map[j]]);
      }
    }
  } else {
    g_assert_not_reached ();
  }

  gst_buffer_unmap (outbuf, &omap);
  gst_buffer_unmap (buf, &map);
  buf = NULL;

  g_free (dec_data);

  ret = gst_audio_decoder_finish_frame (bdec, outbuf, 1);

out:
  if (buf)
    gst_buffer_unmap (buf, &map);

  if (G_UNLIKELY (ret != GST_FLOW_OK)) {
    GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (ret));
  }

  return ret;

/* ERRORS */
input_not_framed:
  {
    GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Expected framed input"));
    ret = GST_FLOW_ERROR;
    goto out;
  }
invalid_header:
  {
    GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Invalid wavpack header"));
    ret = GST_FLOW_ERROR;
    goto out;
  }
context_failed:
  {
    GST_AUDIO_DECODER_ERROR (bdec, 1, LIBRARY, INIT, (NULL),
        ("error creating Wavpack context"), ret);
    goto out;
  }
decode_error:
  {
    const gchar *reason = "unknown";

    if (dec->context) {
      reason = WavpackGetErrorMessage (dec->context);
    } else {
      reason = "couldn't create decoder context";
    }
    GST_AUDIO_DECODER_ERROR (bdec, 1, STREAM, DECODE, (NULL),
        ("decoding error: %s", reason), ret);
    g_free (dec_data);
    if (ret == GST_FLOW_OK)
      gst_audio_decoder_finish_frame (bdec, NULL, 1);
    goto out;
  }
}
Example #6
0
static GstFlowReturn
gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf)
{
    GstWavpackDec *dec;
    GstBuffer *outbuf;
    GstFlowReturn ret = GST_FLOW_OK;
    WavpackHeader wph;
    int32_t decoded, unpacked_size;
    gboolean format_changed;

    dec = GST_WAVPACK_DEC (GST_PAD_PARENT (pad));

    /* check input, we only accept framed input with complete chunks */
    if (GST_BUFFER_SIZE (buf) < sizeof (WavpackHeader))
        goto input_not_framed;

    if (!gst_wavpack_read_header (&wph, GST_BUFFER_DATA (buf)))
        goto invalid_header;

    if (GST_BUFFER_SIZE (buf) < wph.ckSize + 4 * 1 + 4)
        goto input_not_framed;

    if (!(wph.flags & INITIAL_BLOCK))
        goto input_not_framed;

    dec->wv_id.buffer = GST_BUFFER_DATA (buf);
    dec->wv_id.length = GST_BUFFER_SIZE (buf);
    dec->wv_id.position = 0;

    /* create a new wavpack context if there is none yet but if there
     * was already one (i.e. caps were set on the srcpad) check whether
     * the new one has the same caps */
    if (!dec->context) {
        gchar error_msg[80];

        dec->context = WavpackOpenFileInputEx (dec->stream_reader,
                                               &dec->wv_id, NULL, error_msg, OPEN_STREAMING, 0);

        if (!dec->context) {
            GST_WARNING ("Couldn't decode buffer: %s", error_msg);
            dec->error_count++;
            if (dec->error_count <= WAVPACK_DEC_MAX_ERRORS) {
                goto out;               /* just return OK for now */
            } else {
                goto decode_error;
            }
        }
    }

    g_assert (dec->context != NULL);

    dec->error_count = 0;

    format_changed =
        (dec->sample_rate != WavpackGetSampleRate (dec->context)) ||
        (dec->channels != WavpackGetNumChannels (dec->context)) ||
        (dec->depth != WavpackGetBitsPerSample (dec->context)) ||
#ifdef WAVPACK_OLD_API
        (dec->channel_mask != dec->context->config.channel_mask);
#else
        (dec->channel_mask != WavpackGetChannelMask (dec->context));
#endif

    if (!GST_PAD_CAPS (dec->srcpad) || format_changed) {
        GstCaps *caps;
        gint channel_mask;

        dec->sample_rate = WavpackGetSampleRate (dec->context);
        dec->channels = WavpackGetNumChannels (dec->context);
        dec->depth = WavpackGetBitsPerSample (dec->context);

        caps = gst_caps_new_simple ("audio/x-raw-int",
                                    "rate", G_TYPE_INT, dec->sample_rate,
                                    "channels", G_TYPE_INT, dec->channels,
                                    "depth", G_TYPE_INT, dec->depth,
                                    "width", G_TYPE_INT, 32,
                                    "endianness", G_TYPE_INT, G_BYTE_ORDER,
                                    "signed", G_TYPE_BOOLEAN, TRUE, NULL);

#ifdef WAVPACK_OLD_API
        channel_mask = dec->context->config.channel_mask;
#else
        channel_mask = WavpackGetChannelMask (dec->context);
#endif
        if (channel_mask == 0)
            channel_mask = gst_wavpack_get_default_channel_mask (dec->channels);

        dec->channel_mask = channel_mask;

        /* Only set the channel layout for more than two channels
         * otherwise things break unfortunately */
        if (channel_mask != 0 && dec->channels > 2)
            if (!gst_wavpack_set_channel_layout (caps, channel_mask))
                GST_WARNING_OBJECT (dec, "Failed to set channel layout");

        GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps);

        /* should always succeed */
        gst_pad_set_caps (dec->srcpad, caps);
        gst_caps_unref (caps);

        /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something
         * is decoded or after the format has changed */
        gst_wavpack_dec_post_tags (dec);
    }

    /* alloc output buffer */
    unpacked_size = 4 * wph.block_samples * dec->channels;
    ret = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET (buf),
                                unpacked_size, GST_PAD_CAPS (dec->srcpad), &outbuf);

    if (ret != GST_FLOW_OK)
        goto out;

    gst_buffer_copy_metadata (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS);

    /* If we got a DISCONT buffer forward the flag. Nothing else
     * has to be done as libwavpack doesn't store state between
     * Wavpack blocks */
    if (GST_BUFFER_IS_DISCONT (buf) || dec->next_block_index != wph.block_index)
        GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);

    dec->next_block_index = wph.block_index + wph.block_samples;

    /* decode */
    decoded = WavpackUnpackSamples (dec->context,
                                    (int32_t *) GST_BUFFER_DATA (outbuf), wph.block_samples);
    if (decoded != wph.block_samples)
        goto decode_error;

    if ((outbuf = gst_audio_buffer_clip (outbuf, &dec->segment,
                                         dec->sample_rate, 4 * dec->channels))) {
        GST_LOG_OBJECT (dec, "pushing buffer with time %" GST_TIME_FORMAT,
                        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
        ret = gst_pad_push (dec->srcpad, outbuf);
    }

out:

    if (G_UNLIKELY (ret != GST_FLOW_OK)) {
        GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (ret));
    }

    gst_buffer_unref (buf);

    return ret;

    /* ERRORS */
input_not_framed:
    {
        GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Expected framed input"));
        gst_buffer_unref (buf);
        return GST_FLOW_ERROR;
    }
invalid_header:
    {
        GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Invalid wavpack header"));
        gst_buffer_unref (buf);
        return GST_FLOW_ERROR;
    }
decode_error:
    {
        const gchar *reason = "unknown";

        if (dec->context) {
#ifdef WAVPACK_OLD_API
            reason = dec->context->error_message;
#else
            reason = WavpackGetErrorMessage (dec->context);
#endif
        } else {
            reason = "couldn't create decoder context";
        }
        GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
                           ("Failed to decode wavpack stream: %s", reason));
        gst_buffer_unref (outbuf);
        gst_buffer_unref (buf);
        return GST_FLOW_ERROR;
    }
}
Example #7
0
gboolean
wavpack_tag_write_file_tag (const ET_File *ETFile,
                            GError **error)
{
    WavpackStreamReader writer = { wavpack_read_bytes, wavpack_get_pos,
                                   wavpack_set_pos_abs, wavpack_set_pos_rel,
                                   wavpack_push_back_byte, wavpack_get_length,
                                   wavpack_can_seek, wavpack_write_bytes };
    GFile *file;
    EtWavpackWriteState state;
    const gchar *filename;
    const File_Tag *FileTag;
    WavpackContext *wpc;
    gchar message[80];
    gchar *buffer;

    g_return_val_if_fail (ETFile != NULL && ETFile->FileTag != NULL, FALSE);
    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

    filename = ((File_Name *)((GList *)ETFile->FileNameCur)->data)->value;
    FileTag = (File_Tag *)ETFile->FileTag->data;

    file = g_file_new_for_path (filename);
    state.error = NULL;
    state.iostream = g_file_open_readwrite (file, NULL, &state.error);
    g_object_unref (file);

    if (!state.iostream)
    {
        g_propagate_error (error, state.error);
        return FALSE;
    }

    state.istream = G_FILE_INPUT_STREAM (g_io_stream_get_input_stream (G_IO_STREAM (state.iostream)));
    state.ostream = G_FILE_OUTPUT_STREAM (g_io_stream_get_output_stream (G_IO_STREAM (state.iostream)));
    state.seekable = G_SEEKABLE (state.iostream);

    /* NULL for the WavPack correction file. */
    wpc = WavpackOpenFileInputEx (&writer, &state, NULL, message,
                                  OPEN_EDIT_TAGS, 0);

    if (wpc == NULL)
    {
        if (state.error)
        {
            g_propagate_error (error, state.error);
        }
        else
        {
            g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s",
                         message);
        }

        g_object_unref (state.iostream);
        return FALSE;
    }

    /* Title. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "title", FileTag->title))
    {
        goto err;
    }

    /* Artist. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "artist", FileTag->artist))
    {
        goto err;
    }

    /* Album artist. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "album artist",
                                               FileTag->album_artist))
    {
        goto err;
    }

    /* Album. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "album", FileTag->album))
    {
        goto err;
    }

    /* Discnumber. */
    if (FileTag->disc_number && FileTag->disc_total)
    {
        buffer = g_strdup_printf ("%s/%s", FileTag->disc_number,
                                  FileTag->disc_total);

        if (!et_wavpack_append_or_delete_tag_item (wpc, "part", buffer))
        {
            g_free (buffer);
            goto err;
        }
        else
        {
            g_free (buffer);
        }
    }
    else
    {
        if (!et_wavpack_append_or_delete_tag_item (wpc, "part",
                                                   FileTag->disc_number))
        {
            goto err;
        }
    }

    /* Year. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "year", FileTag->year))
    {
        goto err;
    }

    /* Tracknumber + tracktotal. */
    if (FileTag->track_total)
    {
        buffer = g_strdup_printf ("%s/%s", FileTag->track,
                                  FileTag->track_total);

        if (!et_wavpack_append_or_delete_tag_item (wpc, "track", buffer))
        {
            g_free (buffer);
            goto err;
        }
        else
        {
            g_free (buffer);
        }
    }
    else
    {
        if (!et_wavpack_append_or_delete_tag_item (wpc, "track",
                                                   FileTag->track))
        {
            goto err;
        }
    }

    /* Genre. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "genre", FileTag->genre))
    {
        goto err;
    }

    /* Comment. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "comment", FileTag->comment))
    {
        goto err;
    }

    /* Composer. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "composer",
                                               FileTag->composer))
    {
        goto err;
    }

    /* Original artist. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "original artist",
                                               FileTag->orig_artist))
    {
        goto err;
    }

    /* Copyright. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "copyright",
                                               FileTag->copyright))
    {
        goto err;
    }

    /* URL. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "copyright url",
                                               FileTag->url))
    {
        goto err;
    }

    /* Encoded by. */
    if (!et_wavpack_append_or_delete_tag_item (wpc, "encoded by",
                                               FileTag->encoded_by))
    {
        goto err;
    }

    if (WavpackWriteTag (wpc) == 0)
    {
        goto err;
    }

    WavpackCloseFile (wpc);

    g_object_unref (state.iostream);

    return TRUE;

err:
    g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s",
                 WavpackGetErrorMessage (wpc));
    WavpackCloseFile (wpc);
    return FALSE;
}