callback_info *init_callback_info(void) { callback_info *info; if ((info = malloc (sizeof (callback_info))) == NULL) { FLACNG_ERROR("Could not allocate memory for callback structure!"); return NULL; } memset (info, 0, sizeof (callback_info)); if ((info->output_buffer = malloc (BUFFER_SIZE_BYTE)) == NULL) { FLACNG_ERROR("Could not allocate memory for output buffer!"); free (info); return NULL; } reset_info(info); AUDDBG("Playback buffer allocated for %d samples, %d bytes\n", BUFFER_SIZE_SAMP, BUFFER_SIZE_BYTE); return info; }
static size_t read_cb(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle) { size_t read; if (handle == NULL) { FLACNG_ERROR("Trying to read data from an uninitialized file!\n"); return -1; } read = vfs_fread(ptr, size, nmemb, handle); switch (read) { case -1: FLACNG_ERROR("Error while reading from stream!\n"); return -1; case 0: AUDDBG("Stream reached EOF\n"); return 0; default: return read; } }
FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { callback_info* info = (callback_info*) client_data; size_t read; if (info->fd == NULL) { FLACNG_ERROR("Trying to read data from an uninitialized file!\n"); return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } if (*bytes == 0) return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; read = vfs_fread(buffer, 1, *bytes, info->fd); *bytes = read; switch (read) { case -1: FLACNG_ERROR("Error while reading from stream!\n"); return FLAC__STREAM_DECODER_READ_STATUS_ABORT; case 0: AUDDBG("Stream reached EOF\n"); return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; default: return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } }
bool_t read_metadata(FLAC__StreamDecoder *decoder, callback_info *info) { FLAC__StreamDecoderState ret; reset_info(info); /* Reset the decoder */ if (FLAC__stream_decoder_reset(decoder) == false) { FLACNG_ERROR("Could not reset the decoder!\n"); return FALSE; } /* Try to decode the metadata */ if (FLAC__stream_decoder_process_until_end_of_metadata(decoder) == false) { ret = FLAC__stream_decoder_get_state(decoder); AUDDBG("Could not read the metadata: %s(%d)!\n", FLAC__StreamDecoderStateString[ret], ret); reset_info(info); return FALSE; } return TRUE; }
static void squeeze_audio(int32_t* src, void* dst, unsigned count, unsigned res) { int i; int32_t* rp = src; int8_t* wp = dst; int16_t* wp2 = dst; int32_t* wp4 = dst; switch (res) { case 8: for (i = 0; i < count; i++, wp++, rp++) *wp = *rp & 0xff; break; case 16: for (i = 0; i < count; i++, wp2++, rp++) *wp2 = *rp & 0xffff; break; case 24: case 32: for (i = 0; i < count; i++, wp4++, rp++) *wp4 = *rp; break; default: FLACNG_ERROR("Can not convert to %u bps\n", res); } }
FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) { glong sample; gshort channel; callback_info *info = (callback_info*) client_data; /* * Check if there is more data decoded than we have space * for. This _should_ not happen given how our buffer is sized, * but you never know. */ if (info->buffer_free < (frame->header.blocksize * frame->header.channels)) { FLACNG_ERROR("BUG! Too much data decoded from stream!\n"); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } if (frame->header.bits_per_sample != 8 && frame->header.bits_per_sample != 16 && frame->header.bits_per_sample != 24 && frame->header.bits_per_sample != 32) { FLACNG_ERROR("Unsupported bitrate found in stream: %d!\n", frame->header.bits_per_sample); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } /* * Copy the frame metadata, will be compared to stream * metadata later * This also describes the format of the current buffer content. */ info->frame.channels = frame->header.channels; info->frame.samplerate = frame->header.sample_rate; info->frame.bits_per_sample = frame->header.bits_per_sample; for (sample = 0; sample < frame->header.blocksize; sample++) { for (channel = 0; channel < frame->header.channels; channel++) { *(info->write_pointer++) = buffer[channel][sample]; info->buffer_free -= 1; info->buffer_used += 1; } } return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; }
static int seek_cb(FLAC__IOHandle handle, FLAC__int64 offset, int whence) { if (vfs_fseek(handle, offset, whence) != 0) { FLACNG_ERROR("Could not seek to %ld!\n", (long)offset); return -1; } return 0; }
bool_t flac_update_song_tuple(const char *filename, VFSFile *fd, const Tuple *tuple) { AUDDBG("Update song tuple.\n"); FLAC__Metadata_Iterator *iter; FLAC__Metadata_Chain *chain; FLAC__StreamMetadata *vc_block; FLAC__Metadata_ChainStatus status; chain = FLAC__metadata_chain_new(); if (!FLAC__metadata_chain_read_with_callbacks(chain, fd, io_callbacks)) goto ERR; iter = FLAC__metadata_iterator_new(); FLAC__metadata_iterator_init(iter, chain); while (FLAC__metadata_iterator_next(iter)) if (FLAC__metadata_iterator_get_block_type(iter) == FLAC__METADATA_TYPE_VORBIS_COMMENT) { FLAC__metadata_iterator_delete_block(iter, true); break; } vc_block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); insert_str_tuple_to_vc(vc_block, tuple, FIELD_TITLE, "TITLE"); insert_str_tuple_to_vc(vc_block, tuple, FIELD_ARTIST, "ARTIST"); insert_str_tuple_to_vc(vc_block, tuple, FIELD_ALBUM, "ALBUM"); insert_str_tuple_to_vc(vc_block, tuple, FIELD_GENRE, "GENRE"); insert_str_tuple_to_vc(vc_block, tuple, FIELD_COMMENT, "COMMENT"); insert_str_tuple_to_vc(vc_block, tuple, FIELD_MBID, "musicbrainz_trackid"); insert_int_tuple_to_vc(vc_block, tuple, FIELD_YEAR, "DATE"); insert_int_tuple_to_vc(vc_block, tuple, FIELD_TRACK_NUMBER, "TRACKNUMBER"); FLAC__metadata_iterator_insert_block_after(iter, vc_block); FLAC__metadata_iterator_delete(iter); FLAC__metadata_chain_sort_padding(chain); if (!FLAC__metadata_chain_write_with_callbacks(chain, TRUE, fd, io_callbacks)) goto ERR; FLAC__metadata_chain_delete(chain); return TRUE; ERR: status = FLAC__metadata_chain_status(chain); FLAC__metadata_chain_delete(chain); FLACNG_ERROR("An error occured: %s\n", FLAC__Metadata_ChainStatusString[status]); return FALSE; }
FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 offset, void *client_data) { callback_info *info = (callback_info*) client_data; if (vfs_fseek(info->fd, offset, SEEK_SET) != 0) { FLACNG_ERROR("Could not seek to %lld!\n", (long long)offset); return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; } return FLAC__STREAM_DECODER_SEEK_STATUS_OK; }
static FLAC__int64 tell_cb(FLAC__IOHandle handle) { uint64_t offset; if ((offset = vfs_ftell(handle)) == -1) { FLACNG_ERROR("Could not tell current position!\n"); return -1; } AUDDBG ("Current position: %d\n", (int) offset); return offset; }
static bool_t flac_init (void) { FLAC__StreamDecoderInitStatus ret; /* Callback structure and decoder for main decoding loop */ if ((info = init_callback_info()) == NULL) { FLACNG_ERROR("Could not initialize the main callback structure!\n"); return FALSE; } if ((decoder = FLAC__stream_decoder_new()) == NULL) { FLACNG_ERROR("Could not create the main FLAC decoder instance!\n"); return FALSE; } if (FLAC__STREAM_DECODER_INIT_STATUS_OK != (ret = FLAC__stream_decoder_init_stream( decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, info))) { FLACNG_ERROR("Could not initialize the main FLAC decoder: %s(%d)\n", FLAC__StreamDecoderInitStatusString[ret], ret); return FALSE; } AUDDBG("Plugin initialized.\n"); return TRUE; }
bool_t flac_get_image(const char *filename, VFSFile *fd, void **data, int64_t *length) { AUDDBG("Probe for song image.\n"); FLAC__Metadata_Iterator *iter; FLAC__Metadata_Chain *chain; FLAC__StreamMetadata *metadata = NULL; FLAC__Metadata_ChainStatus status; bool_t has_image = FALSE; chain = FLAC__metadata_chain_new(); if (!FLAC__metadata_chain_read_with_callbacks(chain, fd, io_callbacks)) goto ERR; iter = FLAC__metadata_iterator_new(); FLAC__metadata_iterator_init(iter, chain); while (FLAC__metadata_iterator_next(iter)) if (FLAC__metadata_iterator_get_block_type(iter) == FLAC__METADATA_TYPE_PICTURE) break; if (FLAC__metadata_iterator_get_block_type(iter) == FLAC__METADATA_TYPE_PICTURE) { metadata = FLAC__metadata_iterator_get_block(iter); if (metadata->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER) { AUDDBG("FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER found."); * data = malloc (metadata->data.picture.data_length); * length = metadata->data.picture.data_length; memcpy (* data, metadata->data.picture.data, * length); has_image = TRUE; } } FLAC__metadata_iterator_delete(iter); FLAC__metadata_chain_delete(chain); return has_image; ERR: status = FLAC__metadata_chain_status(chain); FLAC__metadata_chain_delete(chain); FLACNG_ERROR("An error occured: %s\n", FLAC__Metadata_ChainStatusString[status]); return FALSE; }
FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *offset, void *client_data) { callback_info *info = (callback_info*) client_data; if ((*offset = vfs_ftell(info->fd)) == -1) { FLACNG_ERROR("Could not tell current position!\n"); return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; } AUDDBG ("Current position: %d\n", (gint) * offset); return FLAC__STREAM_DECODER_TELL_STATUS_OK; }
Tuple *flac_probe_for_tuple(const char *filename, VFSFile *fd) { AUDDBG("Probe for tuple.\n"); Tuple *tuple = NULL; FLAC__Metadata_Iterator *iter; FLAC__Metadata_Chain *chain; FLAC__StreamMetadata *metadata = NULL; FLAC__Metadata_ChainStatus status; FLAC__StreamMetadata_VorbisComment_Entry *entry; char *key; char *value; tuple = tuple_new_from_filename(filename); tuple_set_str(tuple, FIELD_CODEC, NULL, "Free Lossless Audio Codec (FLAC)"); tuple_set_str(tuple, FIELD_QUALITY, NULL, _("lossless")); chain = FLAC__metadata_chain_new(); if (!FLAC__metadata_chain_read_with_callbacks(chain, fd, io_callbacks)) goto ERR; iter = FLAC__metadata_iterator_new(); FLAC__metadata_iterator_init(iter, chain); do { switch (FLAC__metadata_iterator_get_block_type(iter)) { case FLAC__METADATA_TYPE_VORBIS_COMMENT: if (FLAC__metadata_iterator_get_block_type(iter) == FLAC__METADATA_TYPE_VORBIS_COMMENT) { metadata = FLAC__metadata_iterator_get_block(iter); AUDDBG("Vorbis comment contains %d fields\n", metadata->data.vorbis_comment.num_comments); AUDDBG("Vendor string: %s\n", metadata->data.vorbis_comment.vendor_string.entry); entry = metadata->data.vorbis_comment.comments; for (int i = 0; i < metadata->data.vorbis_comment.num_comments; i++, entry++) { if (FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(*entry, &key, &value) == false) AUDDBG("Could not parse comment\n"); else { parse_comment(tuple, key, value); free(key); free(value); } } } break; case FLAC__METADATA_TYPE_STREAMINFO: metadata = FLAC__metadata_iterator_get_block(iter); /* Calculate the stream length (milliseconds) */ if (metadata->data.stream_info.sample_rate == 0) { FLACNG_ERROR("Invalid sample rate for stream!\n"); tuple_set_int(tuple, FIELD_LENGTH, NULL, -1); } else { tuple_set_int(tuple, FIELD_LENGTH, NULL, (metadata->data.stream_info.total_samples / metadata->data.stream_info.sample_rate) * 1000); AUDDBG("Stream length: %d seconds\n", tuple_get_int(tuple, FIELD_LENGTH, NULL)); } int64_t size = vfs_fsize(fd); if (size == -1 || metadata->data.stream_info.total_samples == 0) tuple_set_int(tuple, FIELD_BITRATE, NULL, 0); else { int bitrate = 8 * size * (int64_t) metadata->data.stream_info.sample_rate / metadata->data.stream_info.total_samples; tuple_set_int(tuple, FIELD_BITRATE, NULL, (bitrate + 500) / 1000); } break; default: ; } } while (FLAC__metadata_iterator_next(iter)); FLAC__metadata_iterator_delete(iter); FLAC__metadata_chain_delete(chain); return tuple; ERR: status = FLAC__metadata_chain_status(chain); FLAC__metadata_chain_delete(chain); FLACNG_ERROR("An error occured: %s\n", FLAC__Metadata_ChainStatusString[status]); return tuple; }
static bool_t flac_play (const char * filename, VFSFile * file) { if (!file) return FALSE; void * play_buffer = NULL; bool_t error = FALSE; info->fd = file; if (read_metadata(decoder, info) == FALSE) { FLACNG_ERROR("Could not prepare file for playing!\n"); error = TRUE; goto ERR_NO_CLOSE; } play_buffer = g_malloc (BUFFER_SIZE_BYTE); if (! aud_input_open_audio (SAMPLE_FMT (info->bits_per_sample), info->sample_rate, info->channels)) { error = TRUE; goto ERR_NO_CLOSE; } aud_input_set_bitrate(info->bitrate); while (FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_END_OF_STREAM) { if (aud_input_check_stop ()) break; int seek_value = aud_input_check_seek (); if (seek_value >= 0) FLAC__stream_decoder_seek_absolute (decoder, (int64_t) seek_value * info->sample_rate / 1000); /* Try to decode a single frame of audio */ if (FLAC__stream_decoder_process_single(decoder) == FALSE) { FLACNG_ERROR("Error while decoding!\n"); error = TRUE; break; } squeeze_audio(info->output_buffer, play_buffer, info->buffer_used, info->bits_per_sample); aud_input_write_audio(play_buffer, info->buffer_used * SAMPLE_SIZE(info->bits_per_sample)); reset_info(info); } ERR_NO_CLOSE: g_free (play_buffer); reset_info(info); if (FLAC__stream_decoder_flush(decoder) == FALSE) FLACNG_ERROR("Could not flush decoder state!\n"); return ! error; }
void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { FLACNG_ERROR("FLAC decoder error callback was called: %d\n", status); }