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; }
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); } } }
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); } }
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)); } }
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; } }
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; } }
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; }