From 6213a31f7b2700e5f60a1d887927e4518b24b2ad Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Thu, 1 Mar 2001 21:47:12 +0000 Subject: [PATCH] rough implementation (haven't even compile it yet) --- src/plugin_winamp3/in_flac.cpp | 325 ++++++++++++++++++++++++++++++++++------- 1 file changed, 276 insertions(+), 49 deletions(-) diff --git a/src/plugin_winamp3/in_flac.cpp b/src/plugin_winamp3/in_flac.cpp index 4048bd1..061cf0b 100644 --- a/src/plugin_winamp3/in_flac.cpp +++ b/src/plugin_winamp3/in_flac.cpp @@ -1,4 +1,3 @@ -/* in_flac - Winamp3 FLAC input plugin * Copyright (C) 2001 Josh Coalson * * This program is free software; you can redistribute it and/or @@ -19,6 +18,36 @@ #include #include #include "core_api.h" +#include "FLAC/all.h" + +typedef struct { + byte raw[128]; + char title[31]; + char artist[31]; + char album[31]; + char comment[31]; + unsigned year; + unsigned track; /* may be 0 if v1 (not v1.1) tag */ + unsigned genre; + char description[1024]; /* the formatted description passed to xmms */ +} id3v1_struct; + +typedef struct { + bool abort_flag; + bool is_playing; + bool eof; + unsigned total_samples; + unsigned bits_per_sample; + unsigned channels; + unsigned sample_rate; + unsigned length_in_msec; +} file_info_struct; + +static bool get_id3v1_tag_(const char *filename, id3v1_struct *tag); +static bool decoder_init_(const char *filename); +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data); +static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data); +static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); static char plugin_description_[] = "Reference FLAC Player v" FLAC__VERSION_STRING; static HINSTANCE g_hDllInstance_; @@ -26,21 +55,20 @@ static HINSTANCE g_hDllInstance_; class FLAC_Info : public WInputInfo { private: - char title[MAX_PATH], infostr[MAX_PATH]; - int length_in_ms; + id3v1_struct tag_; + int length_in_msec_; public: FLAC_Info(); ~FLAC_Info(); int Open(char *url); void GetTitle(char *buf, int maxlen); void GetInfoString(char *buf, int maxlen); - inline int GetLength(void) { return length_in_ms; } + inline int GetLength(void) { return length_in_msec_; } }; FLAC_Info::FLAC_Info() : WInputInfo() { - infostr[0] = title[0] = 0; - length_in_ms = -1; + length_in_msec_ = -1; } FLAC_Info::~FLAC_Info() @@ -49,39 +77,58 @@ FLAC_Info::~FLAC_Info() int FLAC_Info::Open(char *url) { - char *p=url+strlen(url); - while (*p != '\\' && p >= url) p--; - strcpy(title, ++p); + const char *filename = url; /* @@@ right now we only handle files */ - HANDLE hFile; - length_in_ms = -1; - hFile = CreateFile(url, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile != INVALID_HANDLE_VALUE) { - length_in_ms = (GetFileSize(hFile, NULL)*10)/(SAMPLERATE/100*NCH*(BPS/8)); + (void)get_id3v1_tag_(filename, &tag_); + + file_info_struct tmp_file_info; + FLAC__FileDecoder *tmp_decoder = FLAC__file_decoder_get_new_instance(); + if(0 == tmp_decoder) { + length_in_msec_ = -1; + return 1; } - CloseHandle(hFile); + tmp_file_info.abort_flag = false; + tmp_decoder->check_md5 = false; /* turn off MD5 checking in the decoder */ + if(FLAC__file_decoder_init(tmp_decoder, filename, write_callback_, metadata_callback_, error_callback_, &tmp_file_info) != FLAC__FILE_DECODER_OK) { + length_in_msec_ = -1; + return 1; + } + if(!FLAC__file_decoder_process_metadata(tmp_decoder)) { + length_in_msec_ = -1; + return 1; + } + + length_in_msec_ = (int)tmp_file_info.length_in_msec; + + if(tmp_decoder->state != FLAC__FILE_DECODER_UNINITIALIZED) + FLAC__file_decoder_finish(tmp_decoder); + FLAC__file_decoder_free_instance(tmp_decoder); + return 0; } void FLAC_Info::GetTitle(char *buf, int maxlen) { strncpy(buf, title, maxlen-1); - buf[maxlen-1]=0; + buf[maxlen-1] = 0; } void FLAC_Info::GetInfoString(char *buf, int maxlen) { - strncpy(buf, infostr, maxlen-1); - buf[maxlen-1]=0; + strncpy(buf, description, maxlen-1); + buf[maxlen-1] = 0; } class FLAC_Source : public WInputSource { private: - char title[MAX_PATH], infostr[MAX_PATH]; - int length_in_ms; - HANDLE input_file; + id3v1_struct tag_; + FLAC__FileDecoder *decoder_; + int16 reservoir_[FLAC__MAX_BLOCK_SIZE * 2 * 2]; /* *2 for max channels, another *2 for overflow */ + unsigned reservoir_samples_; + file_info_struct file_info_; + bool audio_error_; public: FLAC_Source(); ~FLAC_Source(); @@ -92,63 +139,131 @@ class FLAC_Source : public WInputSource int SetPosition(int); // sets position in ms void GetTitle(char *buf, int maxlen); void GetInfoString(char *buf, int maxlen); - inline int GetLength(void) { return -1; } + inline int GetLength(void) { return (int)file_info_.length_in_msec; } + void cleanup(); }; -void FLAC_Source::GetTitle(char *buf, int maxlen) +FLAC_Source::FLAC_Source() : WInputSource() { - strncpy(buf, title, maxlen-1); - buf[maxlen-1] = 0; + decoder_ = FLAC__file_decoder_get_new_instance(); + file_info_.length_in_msec = -1; + audio_error_ = false; } -void FLAC_Source::GetInfoString(char *buf, int maxlen) +FLAC_Source::~FLAC_Source() { - strncpy(buf, infostr, maxlen-1); - buf[maxlen-1] = 0; + if(decoder_) + FLAC__file_decoder_free_instance(decoder_); } -FLAC_Source::FLAC_Source() : WInputSource() +void FLAC_Source::GetTitle(char *buf, int maxlen) { - infostr[0] = title[0] = 0; - length_in_ms = -1; - input_file = NULL; + strncpy(buf, title, maxlen-1); + buf[maxlen-1] = 0; } -FLAC_Source::~FLAC_Source() +void FLAC_Source::GetInfoString(char *buf, int maxlen) { - if(input_file) - CloseHandle(input_file); + strncpy(buf, description, maxlen-1); + buf[maxlen-1] = 0; } int FLAC_Source::Open(char *url, bool *killswitch) { - char *p = url+strlen(url); - while (*p != '\\' && p >= url) p--; - strcpy(title, ++p); + const char *filename = url; /* @@@ right now we only handle files */ + + { + FILE *f = fopen(filename, "r"); + if(0 == f) + return 1; + fclose(f); + } + + (void)get_id3v1_tag_(filename, &tag); - length_in_ms = -1; - input_file = CreateFile(url, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (input_file == INVALID_HANDLE_VALUE) return 1; + file_info_.length_in_msec = -1; + + if(!decoder_init_(filename)) + return 1; + + if(file_info_.abort_flag) { + cleanup(); + return 1; + } + + reservoir_samples_ = 0; + audio_error_ = false; + file_info_.is_playing = true; + file_info_.eof = false; - length_in_ms = (GetFileSize(input_file, NULL)*10)/(SAMPLERATE/100*NCH*(BPS/8)); return 0; } int FLAC_Source::GetSamples(char *sample_buffer, int bytes, int *bps, int *nch, int *srate, bool *killswitch) { - *srate = SAMPLERATE; - *bps = BPS; - *nch = NCH; + int return_bytes = 0; + + *bps = (int)file_info_.bits_per_sample; + *nch = (int)file_info_.channels; + *srate = (int)file_info_.sample_rate; + + if(!file_info_.eof) { + const unsigned channels = file_info_.channels; + const unsigned bytes_per_sample = (file_info_.bits_per_sample+7) / 8; + const unsigned wide_samples = bytes / channels / bytes_per_sample; +if(bytes&0x3)fprintf(stderr,"@@@ Got odd buffer size request\n"); + while(reservoir_samples_ < wide_samples) { + if(decoder->state == FLAC__FILE_DECODER_END_OF_FILE) { + file_info_.eof = true; + break; + } + else if(!FLAC__file_decoder_process_one_frame(decoder_)) + break; + } + if(reservoir_samples_ > 0) { + unsigned i, n = min(reservoir_samples_, wide_samples), delta; + int16 output = (int16*)sample_buffer; - unsigned long l; - ReadFile(input_file, sample_buffer, bytes, &l, NULL); + for(i = 0; i < n*channels; i++) + output[i] = reservoir_[i]; + delta = i; + for( ; i < reservoir_samples_*channels; i++) + reservoir[i-delta] = reservoir[i]; + reservoir_samples_ -= n; - return l; + return_bytes = (int)(n * bytes_per_sample * channels); + } + else { + file_info_.eof = true; + } + } + return return_bytes; } int FLAC_Source::SetPosition(int position) { - return 1; // seeking not possible + const double distance = (double)position * 1000.0 / (double)file_info_.length_in_msec; + unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples); + if(FLAC__file_decoder_seek_absolute(decoder_, (uint64)target_sample)) { + file_info_.eof = false; + reservoir_samples_ = 0; + return 0; + } + else { + return 1; + } +} + +void FLAC_Source::cleanup() +{ + if(decoder_ && decoder_->state != FLAC__FILE_DECODER_UNINITIALIZED) + FLAC__file_decoder_finish(decoder_); + + reservoir_samples_ = 0; + audio_error_ = false; + file_info_.is_playing = false; + file_info_.eof = false; + file_info_.length_in_msec = -1; } @@ -156,6 +271,118 @@ int FLAC_Source::SetPosition(int position) * local routines **********************************************************************/ +bool get_id3v1_tag_(const char *filename, id3v1_struct *tag) +{ + const char *temp; + FILE *f = fopen(filename, "rb"); + memset(tag, 0, sizeof(id3v1_struct)); + + /* set the title and description to the filename by default */ + temp = strrchr(filename, '/'); + if(!temp) + temp = filename; + else + temp++; + strcpy(tag->description, temp); + *strrchr(tag->description, '.') = '\0'; + strncpy(tag->title, tag->description, 30); tag->title[30] = '\0'; + + if(0 == f) + return false; + if(-1 == fseek(f, -128, SEEK_END)) { + fclose(f); + return false; + } + if(fread(tag->raw, 1, 128, f) < 128) { + fclose(f); + return false; + } + fclose(f); + if(strncmp(tag->raw, "TAG", 3)) + return false; + else { + char year_str[5]; + + memcpy(tag->title, tag->raw+3, 30); + memcpy(tag->artist, tag->raw+33, 30); + memcpy(tag->album, tag->raw+63, 30); + memcpy(year_str, tag->raw+93, 4); year_str[4] = '\0'; tag->year = atoi(year_str); + memcpy(tag->comment, tag->raw+97, 30); + tag->genre = (unsigned)((byte)tag->raw[127]); + tag->track = (unsigned)((byte)tag->raw[126]); + + sprintf(tag->description, "%s - %s", tag->artist, tag->title); + + return true; + } +} + +bool decoder_init_(const char *filename) +{ + if(decoder_ == 0) + return false; + + decoder_->check_md5 = false; /* turn off MD5 checking in the decoder */ + + if(FLAC__file_decoder_init(decoder_, filename, write_callback_, metadata_callback_, error_callback_, &file_info_) != FLAC__FILE_DECODER_OK) + return false; + + file_info_.abort_flag = false; + + if(!FLAC__file_decoder_process_metadata(decoder_)) + return false; + + return true; +} + +FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + const unsigned bps = file_info->bits_per_sample, channels = file_info->channels, wide_samples = frame->header.blocksize; + unsigned wide_sample, sample, channel; + + (void)decoder; + + if(file_info->abort_flag) + return FLAC__STREAM_DECODER_WRITE_ABORT; + + assert(bps == 16); + + for(sample = reservoir_samples_*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++, sample++) + reservoir_[sample] = (int16)buffer[channel][wide_sample]; + + reservoir_samples_ += wide_samples; + + return FLAC__STREAM_DECODER_WRITE_CONTINUE; +} + +void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + (void)decoder; + if(metadata->type == FLAC__METADATA_TYPE_ENCODING) { + assert(metadata->data.encoding.total_samples < 0x100000000); /* this plugin can only handle < 4 gigasamples */ + file_info->total_samples = (unsigned)(metadata->data.encoding.total_samples&0xffffffff); + file_info->bits_per_sample = metadata->data.encoding.bits_per_sample; + file_info->channels = metadata->data.encoding.channels; + file_info->sample_rate = metadata->data.encoding.sample_rate; + + if(file_info->bits_per_sample != 16) { + file_info->abort_flag = true; + return; + } + file_info->length_in_msec = file_info->total_samples * 10 / (file_info->sample_rate / 100); + } +} + +void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + (void)decoder; + if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC) + file_info->abort_flag = true; +} /*********************************************************************** -- 2.7.4