summaryrefslogtreecommitdiff
path: root/apps/codecs/libFLAC/seekable_stream_decoder.c
blob: 5af0af0d03ffb512385bf19d1605283c1c65d0d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226


CONTEXT_CUSTOM|CONTEXT_TREE = the standard list/tree defines (without directions)


*/

static const struct button_mapping button_context_standard[]  = {
    { ACTION_STD_PREV,       BUTTON_UP,                  BUTTON_NONE },
    { ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT,    BUTTON_NONE },
    { ACTION_STD_NEXT,       BUTTON_DOWN,                BUTTON_NONE },
    { ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT,  BUTTON_NONE },

    { ACTION_STD_CONTEXT,    BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_RIGHT },
    { ACTION_STD_CANCEL,     BUTTON_LEFT,                BUTTON_NONE },
    { ACTION_STD_OK,         BUTTON_RIGHT|BUTTON_REL,    BUTTON_RIGHT },
    { ACTION_STD_MENU,       BUTTON_MENU|BUTTON_REPEAT,  BUTTON_MENU },
    { ACTION_STD_CANCEL,     BUTTON_OFF,                 BUTTON_NONE },

    LAST_ITEM_IN_LIST
};

static const struct button_mapping button_context_wps[]  = {
    { ACTION_WPS_PLAY,       BUTTON_OFF|BUTTON_REL,      BUTTON_OFF },
    { ACTION_WPS_SKIPNEXT,   BUTTON_RIGHT|BUTTON_REL,    BUTTON_RIGHT },
    { ACTION_WPS_SKIPPREV,   BUTTON_LEFT|BUTTON_REL,     BUTTON_LEFT },
    { ACTION_WPS_SEEKBACK,   BUTTON_LEFT|BUTTON_REPEAT,  BUTTON_NONE },
    { ACTION_WPS_SEEKFWD,    BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
    { ACTION_WPS_STOPSEEK,   BUTTON_LEFT|BUTTON_REL,     BUTTON_LEFT|BUTTON_REPEAT },
    { ACTION_WPS_STOPSEEK,   BUTTON_RIGHT|BUTTON_REL,    BUTTON_RIGHT|BUTTON_REPEAT },
    { ACTION_WPS_STOP,       BUTTON_OFF|BUTTON_REPEAT,   BUTTON_OFF },
    { ACTION_WPS_VOLDOWN,    BUTTON_DOWN,                BUTTON_NONE },
    { ACTION_WPS_VOLDOWN,    BUTTON_DOWN|BUTTON_REPEAT,  BUTTON_NONE },
    { ACTION_WPS_VOLUP,      BUTTON_UP,                  BUTTON_NONE },
    { ACTION_WPS_VOLUP,      BUTTON_UP|BUTTON_REPEAT,    BUTTON_NONE },
    { ACTION_WPS_BROWSE,     BUTTON_MENU|BUTTON_REL,     BUTTON_MENU },
    { ACTION_WPS_CONTEXT,    BUTTON_MENU|BUTTON_REPEAT,  BUTTON_MENU },
    /* { ACTION_WPS_MENU,       BUTTON_NONE,                BUTTON_NONE }, we can't have that */
    { ACTION_STD_KEYLOCK,   BUTTON_MENU|BUTTON_DOWN,      BUTTON_NONE },
    /* { ACTION_WPS_VIEW_PLAYLIST,BUTTON_NONE,              BUTTON_NONE }, can't have this either */

    LAST_ITEM_IN_LIST
};

static const struct button_mapping button_context_settings[] = {
    { ACTION_SETTINGS_INC,          BUTTON_UP,                  BUTTON_NONE },
    { ACTION_SETTINGS_INCREPEAT,    BUTTON_UP|BUTTON_REPEAT,    BUTTON_NONE },
    { ACTION_SETTINGS_DEC,          BUTTON_DOWN,                BUTTON_NONE },
    { ACTION_SETTINGS_DECREPEAT,    BUTTON_DOWN|BUTTON_REPEAT,  BUTTON_NONE },
    { ACTION_STD_OK,                BUTTON_RIGHT,               BUTTON_NONE },
    { ACTION_STD_OK,                BUTTON_LEFT,                BUTTON_NONE },
    { ACTION_STD_CANCEL,            BUTTON_MENU,                BUTTON_NONE },

    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
};

static const struct button_mapping button_context_tree[]  = {
    { ACTION_TREE_WPS,    BUTTON_MENU|BUTTON_REL,       BUTTON_MENU },
    { ACTION_TREE_STOP,   BUTTON_OFF,                   BUTTON_NONE },
    { ACTION_STD_CANCEL,  BUTTON_LEFT|BUTTON_REPEAT,    BUTTON_NONE },
    
    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_listtree */

static const struct button_mapping button_context_tree_scroll_lr[]  = {
    { ACTION_NONE,              BUTTON_LEFT,                BUTTON_NONE },
    { ACTION_STD_CANCEL,        BUTTON_LEFT|BUTTON_REL,     BUTTON_LEFT },
    { ACTION_TREE_PGLEFT,       BUTTON_MENU|BUTTON_LEFT, BUTTON_NONE },
    { ACTION_TREE_ROOT_INIT,    BUTTON_MENU|BUTTON_LEFT|BUTTON_REPEAT,  BUTTON_MENU|BUTTON_LEFT },
    { ACTION_TREE_PGLEFT,       BUTTON_MENU|BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
    { ACTION_TREE_PGLEFT,       BUTTON_MENU|BUTTON_RIGHT|BUTTON_REL, BUTTON_MENU|BUTTON_RIGHT|BUTTON_REPEAT },
    { ACTION_NONE,              BUTTON_RIGHT,               BUTTON_NONE },
    { ACTION_STD_OK,            BUTTON_RIGHT|BUTTON_REL,    BUTTON_RIGHT },
    { ACTION_TREE_PGRIGHT,      BUTTON_MENU|BUTTON_RIGHT, BUTTON_NONE },
    { ACTION_TREE_PGRIGHT,      BUTTON_MENU|BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
    { ACTION_TREE_PGRIGHT,      BUTTON_MENU|BUTTON_RIGHT|BUTTON_REL, BUTTON_MENU|BUTTON_RIGHT|BUTTON_REPEAT },

    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM|CONTEXT_TREE),
};

static const struct button_mapping button_context_yesno[] = {
    { ACTION_YESNO_ACCEPT,      BUTTON_RIGHT,    BUTTON_NONE },
    
    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; 

static const struct button_mapping button_context_bmark[]  = {
    { ACTION_NONE,              BUTTON_LEFT,                 BUTTON_NONE },
    { ACTION_BMS_DELETE,      BUTTON_LEFT|BUTTON_REPEAT,   BUTTON_LEFT },

    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST),
}; /* button_context_settings_bmark */

static const struct button_mapping button_context_pitchscreen[]  = {
    { ACTION_PS_INC_SMALL,      BUTTON_UP,                  BUTTON_NONE },
    { ACTION_PS_INC_BIG,        BUTTON_UP|BUTTON_REPEAT,    BUTTON_NONE },
    { ACTION_PS_DEC_SMALL,      BUTTON_DOWN,                BUTTON_NONE },
    { ACTION_PS_DEC_BIG,        BUTTON_DOWN|BUTTON_REPEAT,  BUTTON_NONE },
    { ACTION_PS_NUDGE_LEFT,     BUTTON_LEFT,                BUTTON_NONE },
    { ACTION_PS_NUDGE_LEFTOFF,  BUTTON_LEFT|BUTTON_REL,     BUTTON_NONE },
    { ACTION_PS_NUDGE_RIGHT,    BUTTON_RIGHT,               BUTTON_NONE },
    { ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL,    BUTTON_NONE },
    { ACTION_PS_TOGGLE_MODE,    BUTTON_MENU|BUTTON_REPEAT,  BUTTON_MENU },
    { ACTION_PS_RESET,          BUTTON_MENU,                BUTTON_NONE },
    { ACTION_PS_EXIT,           BUTTON_OFF,                 BUTTON_NONE },
    
    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_pitchscreen */

#ifdef HAVE_RECORDING
static const struct button_mapping button_context_rectrigger[]  = {
    { ACTION_STD_OK,                BUTTON_MENU|BUTTON_REL,     BUTTON_MENU },  
    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_RECSCREEN)
};
static const struct button_mapping button_context_recscreen[]  = {
    { ACTION_REC_PAUSE,             BUTTON_MENU|BUTTON_REL,     BUTTON_MENU },  
    { ACTION_SETTINGS_INC,          BUTTON_RIGHT|BUTTON_REL,    BUTTON_RIGHT },
    { ACTION_SETTINGS_INCREPEAT,    BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
    { ACTION_SETTINGS_DEC,          BUTTON_LEFT,                BUTTON_NONE },
    { ACTION_SETTINGS_DECREPEAT,    BUTTON_LEFT|BUTTON_REPEAT,  BUTTON_NONE },
    
    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_recscreen */
#endif /* HAVE_RECORDING  */

static const struct button_mapping button_context_keyboard[]  = {
    { ACTION_KBD_LEFT,         BUTTON_LEFT,                           BUTTON_NONE },
    { ACTION_KBD_LEFT,         BUTTON_LEFT|BUTTON_REPEAT,             BUTTON_NONE },   
    { ACTION_KBD_RIGHT,        BUTTON_RIGHT,                          BUTTON_NONE },
    { ACTION_KBD_RIGHT,        BUTTON_RIGHT|BUTTON_REPEAT,            BUTTON_NONE },
    { ACTION_KBD_SELECT,       BUTTON_MENU|BUTTON_REL,                BUTTON_MENU },
    { ACTION_KBD_DONE,         BUTTON_MENU|BUTTON_REPEAT,             BUTTON_NONE },
    { ACTION_KBD_ABORT,        BUTTON_OFF,                            BUTTON_NONE },
    { ACTION_KBD_UP,           BUTTON_UP,                             BUTTON_NONE },
    { ACTION_KBD_UP,           BUTTON_UP|BUTTON_REPEAT,               BUTTON_NONE },
    { ACTION_KBD_DOWN,         BUTTON_DOWN,                           BUTTON_NONE },
    { ACTION_KBD_DOWN,         BUTTON_DOWN|BUTTON_REPEAT,             BUTTON_NONE },

    LAST_ITEM_IN_LIST
}; /* button_context_keyboard */

#if CONFIG_TUNER
static const struct button_mapping button_context_radio[]  = {
    { ACTION_FM_MENU,          BUTTON_MENU | BUTTON_REPEAT,           BUTTON_NONE },
    { ACTION_FM_RECORD_DBLPRE, BUTTON_MENU,                           BUTTON_NONE},
    { ACTION_FM_RECORD,        BUTTON_MENU | BUTTON_REL,              BUTTON_NONE },
    { ACTION_FM_STOP,          BUTTON_OFF | BUTTON_REPEAT,            BUTTON_OFF },
    { ACTION_FM_EXIT,          BUTTON_OFF | BUTTON_REL,               BUTTON_OFF },
    { ACTION_STD_PREV,         BUTTON_LEFT,                           BUTTON_NONE },
    { ACTION_STD_PREVREPEAT,   BUTTON_LEFT|BUTTON_REPEAT,             BUTTON_NONE },
    { ACTION_STD_NEXT,         BUTTON_RIGHT,                          BUTTON_NONE },
    { ACTION_STD_NEXTREPEAT,   BUTTON_RIGHT|BUTTON_REPEAT,            BUTTON_NONE },



    LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)

};
#endif

const struct button_mapping* get_context_mapping( int context )
{
    switch( context )
    {
        case CONTEXT_STD:
            return button_context_standard;
            
        case CONTEXT_WPS:
            return button_context_wps;

        case CONTEXT_SETTINGS:
            return button_context_settings;
            
        case CONTEXT_YESNOSCREEN:
            return button_context_yesno;
            
        case CONTEXT_BOOKMARKSCREEN:
            return button_context_bmark;
        case CONTEXT_PITCHSCREEN:
            return button_context_pitchscreen;
        case CONTEXT_TREE:     
        case CONTEXT_MAINMENU:
            if (global_settings.hold_lr_for_scroll_in_list)
                return button_context_tree_scroll_lr;
            /* else fall through to CUSTOM|CONTEXT_TREE */
        case CONTEXT_CUSTOM|CONTEXT_TREE:
            return button_context_tree;
#ifdef HAVE_RECORDING
        case CONTEXT_RECSCREEN:
             return button_context_recscreen;
        case CONTEXT_SETTINGS_RECTRIGGER:
            return button_context_rectrigger;
#endif
        case CONTEXT_KEYBOARD:
            return button_context_keyboard;
#if CONFIG_TUNER
        case CONTEXT_FM:
            return button_context_radio;
#endif
        case CONTEXT_LIST:
        default:
            return button_context_standard;
    }
}
='n1054' href='#n1054'>1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
/* libFLAC - Free Lossless Audio Codec library
 * Copyright (C) 2000,2001,2002,2003,2004,2005  Josh Coalson
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Xiph.org Foundation nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include "global.h" /* for calloc() */
#include <string.h> /* for memcpy()/memcmp() */
#include "FLAC/assert.h"
#include "protected/seekable_stream_decoder.h"
#include "protected/stream_decoder.h"
#include "private/float.h" /* for FLAC__double */
#include "private/md5.h"

/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */
#ifdef _MSC_VER
#define FLAC__U64L(x) x
#else
#define FLAC__U64L(x) x##LLU
#endif

/***********************************************************************
 *
 * Private class method prototypes
 *
 ***********************************************************************/

static void set_defaults_(FLAC__SeekableStreamDecoder *decoder);
static FLAC__StreamDecoderReadStatus read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data);
static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
static FLAC__bool seek_to_absolute_sample_(FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample);

/***********************************************************************
 *
 * Private class data
 *
 ***********************************************************************/

typedef struct FLAC__SeekableStreamDecoderPrivate {
	FLAC__SeekableStreamDecoderReadCallback read_callback;
	FLAC__SeekableStreamDecoderSeekCallback seek_callback;
	FLAC__SeekableStreamDecoderTellCallback tell_callback;
	FLAC__SeekableStreamDecoderLengthCallback length_callback;
	FLAC__SeekableStreamDecoderEofCallback eof_callback;
	FLAC__SeekableStreamDecoderWriteCallback write_callback;
	FLAC__SeekableStreamDecoderMetadataCallback metadata_callback;
	FLAC__SeekableStreamDecoderErrorCallback error_callback;
	void *client_data;
	FLAC__StreamDecoder *stream_decoder;
	FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek */
	struct FLAC__MD5Context md5context;
	FLAC__byte stored_md5sum[16]; /* this is what is stored in the metadata */
	FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */
	/* the rest of these are only used for seeking: */
	FLAC__StreamMetadata_StreamInfo stream_info; /* we keep this around so we can figure out how to seek quickly */
	const FLAC__StreamMetadata_SeekTable *seek_table; /* we hold a pointer to the stream decoder's seek table for the same reason */
	/* Since we always want to see the STREAMINFO and SEEK_TABLE blocks at this level, we need some extra flags to keep track of whether they should be passed on up through the metadata_callback */
	FLAC__bool ignore_stream_info_block;
	FLAC__bool ignore_seek_table_block;
	FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */
	FLAC__uint64 target_sample;
} FLAC__SeekableStreamDecoderPrivate;

/***********************************************************************
 *
 * Public static class data
 *
 ***********************************************************************/

FLAC_API const char * const FLAC__SeekableStreamDecoderStateString[] = {
	"FLAC__SEEKABLE_STREAM_DECODER_OK",
	"FLAC__SEEKABLE_STREAM_DECODER_SEEKING",
	"FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM",
	"FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR",
	"FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR",
	"FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR",
	"FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR",
	"FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED",
	"FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK",
	"FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED"
};

FLAC_API const char * const FLAC__SeekableStreamDecoderReadStatusString[] = {
	"FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK",
	"FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR"
};

FLAC_API const char * const FLAC__SeekableStreamDecoderSeekStatusString[] = {
	"FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK",
	"FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR"
};

FLAC_API const char * const FLAC__SeekableStreamDecoderTellStatusString[] = {
	"FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK",
	"FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR"
};

FLAC_API const char * const FLAC__SeekableStreamDecoderLengthStatusString[] = {
	"FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK",
	"FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR"
};


/***********************************************************************
 *
 * Class constructor/destructor
 *
 ***********************************************************************/

FLAC__SeekableStreamDecoder _ssd;
FLAC__SeekableStreamDecoderProtected _ssdprotected IDATA_ATTR;
FLAC__SeekableStreamDecoderPrivate _ssdprivate IDATA_ATTR;

FLAC_API FLAC__SeekableStreamDecoder *FLAC__seekable_stream_decoder_new(void)
{
	FLAC__SeekableStreamDecoder *decoder;

	FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */

    decoder = &_ssd;
    /*decoder = (FLAC__SeekableStreamDecoder*)calloc(1, sizeof(FLAC__SeekableStreamDecoder));
	if(decoder == 0) {
		return 0;
	}*/

	decoder->protected_ = &_ssdprotected;
    /*decoder->protected_ = (FLAC__SeekableStreamDecoderProtected*)calloc(1, sizeof(FLAC__SeekableStreamDecoderProtected));
	if(decoder->protected_ == 0) {
		free(decoder);
		return 0;
	}*/

	decoder->private_ = &_ssdprivate;
    /*decoder->private_ = (FLAC__SeekableStreamDecoderPrivate*)calloc(1, sizeof(FLAC__SeekableStreamDecoderPrivate));
	if(decoder->private_ == 0) {
		free(decoder->protected_);
		free(decoder);
		return 0;
	}*/

	decoder->private_->stream_decoder = FLAC__stream_decoder_new();
	if(0 == decoder->private_->stream_decoder) {
		/*free(decoder->private_);
		free(decoder->protected_);
		free(decoder);*/
		return 0;
	}

	set_defaults_(decoder);

	decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED;

	return decoder;
}

FLAC_API void FLAC__seekable_stream_decoder_delete(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->protected_);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->private_->stream_decoder);

	(void)FLAC__seekable_stream_decoder_finish(decoder);

	FLAC__stream_decoder_delete(decoder->private_->stream_decoder);

	/*free(decoder->private_);
	free(decoder->protected_);
	free(decoder);*/
}

/***********************************************************************
 *
 * Public class methods
 *
 ***********************************************************************/

FLAC_API FLAC__SeekableStreamDecoderState FLAC__seekable_stream_decoder_init(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);

	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED;

	if(0 == decoder->private_->read_callback || 0 == decoder->private_->seek_callback || 0 == decoder->private_->tell_callback || 0 == decoder->private_->length_callback || 0 == decoder->private_->eof_callback)
		return decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK;

	if(0 == decoder->private_->write_callback || 0 == decoder->private_->metadata_callback || 0 == decoder->private_->error_callback)
		return decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK;

	decoder->private_->seek_table = 0;

	decoder->private_->do_md5_checking = decoder->protected_->md5_checking;

	/* We initialize the FLAC__MD5Context even though we may never use it.  This
	 * is because md5 checking may be turned on to start and then turned off if
	 * a seek occurs.  So we always init the context here and finalize it in
	 * FLAC__seekable_stream_decoder_finish() to make sure things are always
	 * cleaned up properly.
	 */
	FLAC__MD5Init(&decoder->private_->md5context);

	FLAC__stream_decoder_set_read_callback(decoder->private_->stream_decoder, read_callback_);
	FLAC__stream_decoder_set_write_callback(decoder->private_->stream_decoder, write_callback_);
	FLAC__stream_decoder_set_metadata_callback(decoder->private_->stream_decoder, metadata_callback_);
	FLAC__stream_decoder_set_error_callback(decoder->private_->stream_decoder, error_callback_);
	FLAC__stream_decoder_set_client_data(decoder->private_->stream_decoder, decoder);

	/* We always want to see these blocks.  Whether or not we pass them up
	 * through the metadata callback will be determined by flags set in our
	 * implementation of ..._set_metadata_respond/ignore...()
	 */
	FLAC__stream_decoder_set_metadata_respond(decoder->private_->stream_decoder, FLAC__METADATA_TYPE_STREAMINFO);
	FLAC__stream_decoder_set_metadata_respond(decoder->private_->stream_decoder, FLAC__METADATA_TYPE_SEEKTABLE);

	if(FLAC__stream_decoder_init(decoder->private_->stream_decoder) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA)
		return decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;

	return decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_OK;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_finish(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__bool md5_failed = false;

	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);

	if(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return true;

	FLAC__ASSERT(0 != decoder->private_->stream_decoder);

	/* see the comment in FLAC__seekable_stream_decoder_init() as to why we
	 * always call FLAC__MD5Final()
	 */
	FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context);

	FLAC__stream_decoder_finish(decoder->private_->stream_decoder);

	if(decoder->private_->do_md5_checking) {
		if(memcmp(decoder->private_->stored_md5sum, decoder->private_->computed_md5sum, 16))
			md5_failed = true;
	}

	set_defaults_(decoder);

	decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED;

	return !md5_failed;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_md5_checking(FLAC__SeekableStreamDecoder *decoder, FLAC__bool value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->protected_->md5_checking = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_read_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderReadCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->read_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_seek_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderSeekCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->seek_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_tell_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderTellCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->tell_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_length_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderLengthCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->length_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_eof_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderEofCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->eof_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_write_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderWriteCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->write_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_metadata_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderMetadataCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->metadata_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_error_callback(FLAC__SeekableStreamDecoder *decoder, FLAC__SeekableStreamDecoderErrorCallback value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->error_callback = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_client_data(FLAC__SeekableStreamDecoder *decoder, void *value)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->client_data = value;
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_metadata_respond(FLAC__SeekableStreamDecoder *decoder, FLAC__MetadataType type)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	FLAC__ASSERT(0 != decoder->private_->stream_decoder);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	if(type == FLAC__METADATA_TYPE_STREAMINFO)
		decoder->private_->ignore_stream_info_block = false;
	else if(type == FLAC__METADATA_TYPE_SEEKTABLE)
		decoder->private_->ignore_seek_table_block = false;
	return FLAC__stream_decoder_set_metadata_respond(decoder->private_->stream_decoder, type);
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_metadata_respond_application(FLAC__SeekableStreamDecoder *decoder, const FLAC__byte id[4])
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	FLAC__ASSERT(0 != decoder->private_->stream_decoder);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	return FLAC__stream_decoder_set_metadata_respond_application(decoder->private_->stream_decoder, id);
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_metadata_respond_all(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	FLAC__ASSERT(0 != decoder->private_->stream_decoder);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->ignore_stream_info_block = false;
	decoder->private_->ignore_seek_table_block = false;
	return FLAC__stream_decoder_set_metadata_respond_all(decoder->private_->stream_decoder);
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_metadata_ignore(FLAC__SeekableStreamDecoder *decoder, FLAC__MetadataType type)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	FLAC__ASSERT(0 != decoder->private_->stream_decoder);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	if(type == FLAC__METADATA_TYPE_STREAMINFO) {
		decoder->private_->ignore_stream_info_block = true;
		return true;
	}
	else if(type == FLAC__METADATA_TYPE_SEEKTABLE) {
		decoder->private_->ignore_seek_table_block = true;
		return true;
	}
	else
		return FLAC__stream_decoder_set_metadata_ignore(decoder->private_->stream_decoder, type);
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_metadata_ignore_application(FLAC__SeekableStreamDecoder *decoder, const FLAC__byte id[4])
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	FLAC__ASSERT(0 != decoder->private_->stream_decoder);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	return FLAC__stream_decoder_set_metadata_ignore_application(decoder->private_->stream_decoder, id);
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_set_metadata_ignore_all(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);
	FLAC__ASSERT(0 != decoder->private_->stream_decoder);
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED)
		return false;
	decoder->private_->ignore_stream_info_block = true;
	decoder->private_->ignore_seek_table_block = true;
	return
		FLAC__stream_decoder_set_metadata_ignore_all(decoder->private_->stream_decoder) &&
		FLAC__stream_decoder_set_metadata_respond(decoder->private_->stream_decoder, FLAC__METADATA_TYPE_STREAMINFO) &&
		FLAC__stream_decoder_set_metadata_respond(decoder->private_->stream_decoder, FLAC__METADATA_TYPE_SEEKTABLE);
}

FLAC_API FLAC__SeekableStreamDecoderState FLAC__seekable_stream_decoder_get_state(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->protected_);
	return decoder->protected_->state;
}

FLAC_API FLAC__StreamDecoderState FLAC__seekable_stream_decoder_get_stream_decoder_state(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	return FLAC__stream_decoder_get_state(decoder->private_->stream_decoder);
}

FLAC_API const char *FLAC__seekable_stream_decoder_get_resolved_state_string(const FLAC__SeekableStreamDecoder *decoder)
{
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR)
		return FLAC__SeekableStreamDecoderStateString[decoder->protected_->state];
	else
		return FLAC__stream_decoder_get_resolved_state_string(decoder->private_->stream_decoder);
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_get_md5_checking(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->protected_);
	return decoder->protected_->md5_checking;
}

FLAC_API unsigned FLAC__seekable_stream_decoder_get_channels(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	return FLAC__stream_decoder_get_channels(decoder->private_->stream_decoder);
}

FLAC_API FLAC__ChannelAssignment FLAC__seekable_stream_decoder_get_channel_assignment(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	return FLAC__stream_decoder_get_channel_assignment(decoder->private_->stream_decoder);
}

FLAC_API unsigned FLAC__seekable_stream_decoder_get_bits_per_sample(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	return FLAC__stream_decoder_get_bits_per_sample(decoder->private_->stream_decoder);
}

FLAC_API unsigned FLAC__seekable_stream_decoder_get_sample_rate(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	return FLAC__stream_decoder_get_sample_rate(decoder->private_->stream_decoder);
}

FLAC_API unsigned FLAC__seekable_stream_decoder_get_blocksize(const FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	return FLAC__stream_decoder_get_blocksize(decoder->private_->stream_decoder);
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_get_decode_position(const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *position)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != position);

	if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK)
		return false;
	FLAC__ASSERT(*position >= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder->private_->stream_decoder));
	*position -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder->private_->stream_decoder);
	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_flush(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);

	decoder->private_->do_md5_checking = false;

	if(!FLAC__stream_decoder_flush(decoder->private_->stream_decoder)) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;
		return false;
	}

	decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_OK;

	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_reset(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(0 != decoder->private_);
	FLAC__ASSERT(0 != decoder->protected_);

	if(!FLAC__seekable_stream_decoder_flush(decoder)) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;
		return false;
	}

	if(!FLAC__stream_decoder_reset(decoder->private_->stream_decoder)) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;
		return false;
	}

	decoder->private_->seek_table = 0;

	decoder->private_->do_md5_checking = decoder->protected_->md5_checking;

	/* We initialize the FLAC__MD5Context even though we may never use it.  This
	 * is because md5 checking may be turned on to start and then turned off if
	 * a seek occurs.  So we always init the context here and finalize it in
	 * FLAC__seekable_stream_decoder_finish() to make sure things are always
	 * cleaned up properly.
	 */
	FLAC__MD5Init(&decoder->private_->md5context);

	decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_OK;

	return true;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_process_single(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__bool ret;
	FLAC__ASSERT(0 != decoder);

	if(decoder->private_->stream_decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM;

	if(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM)
		return true;

	FLAC__ASSERT(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_OK);

	ret = FLAC__stream_decoder_process_single(decoder->private_->stream_decoder);
	if(!ret)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;

	return ret;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_process_until_end_of_metadata(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__bool ret;
	FLAC__ASSERT(0 != decoder);

	if(decoder->private_->stream_decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM;

	if(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM)
		return true;

	FLAC__ASSERT(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_OK);

	ret = FLAC__stream_decoder_process_until_end_of_metadata(decoder->private_->stream_decoder);
	if(!ret)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;

	return ret;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_process_until_end_of_stream(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__bool ret;
	FLAC__ASSERT(0 != decoder);

	if(decoder->private_->stream_decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM;

	if(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM)
		return true;

	FLAC__ASSERT(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_OK);

	ret = FLAC__stream_decoder_process_until_end_of_stream(decoder->private_->stream_decoder);
	if(!ret)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;

	return ret;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_skip_single_frame(FLAC__SeekableStreamDecoder *decoder)
{
	FLAC__bool ret;
	FLAC__ASSERT(0 != decoder);

	if(decoder->private_->stream_decoder->protected_->state == FLAC__STREAM_DECODER_END_OF_STREAM)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM;

	if(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM)
		return true;

	FLAC__ASSERT(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_OK);

	ret = FLAC__stream_decoder_skip_single_frame(decoder->private_->stream_decoder);
	if(!ret)
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;

	return ret;
}

FLAC_API FLAC__bool FLAC__seekable_stream_decoder_seek_absolute(FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 sample)
{
	FLAC__uint64 length;

	FLAC__ASSERT(0 != decoder);
	FLAC__ASSERT(decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_OK || decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM);

	decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEKING;

	/* turn off md5 checking if a seek is attempted */
	decoder->private_->do_md5_checking = false;

	if(!FLAC__stream_decoder_reset(decoder->private_->stream_decoder)) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;
		return false;
	}
	/* get the file length */
	if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
		return false;
	}
	/* rewind */
	if(decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) != FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
		return false;
	}
	if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder->private_->stream_decoder)) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;
		return false;
	}
	if(decoder->private_->stream_info.total_samples > 0 && sample >= decoder->private_->stream_info.total_samples) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
		return false;
	}

	return seek_to_absolute_sample_(decoder, length, sample);
}

/***********************************************************************
 *
 * Private class methods
 *
 ***********************************************************************/

void set_defaults_(FLAC__SeekableStreamDecoder *decoder)
{
	decoder->private_->read_callback = 0;
	decoder->private_->seek_callback = 0;
	decoder->private_->tell_callback = 0;
	decoder->private_->length_callback = 0;
	decoder->private_->eof_callback = 0;
	decoder->private_->write_callback = 0;
	decoder->private_->metadata_callback = 0;
	decoder->private_->error_callback = 0;
	decoder->private_->client_data = 0;
	/* WATCHOUT: these should match the default behavior of FLAC__StreamDecoder */
	decoder->private_->ignore_stream_info_block = false;
	decoder->private_->ignore_seek_table_block = true;

	decoder->protected_->md5_checking = false;
}

FLAC__StreamDecoderReadStatus read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
{
	FLAC__SeekableStreamDecoder *seekable_stream_decoder = (FLAC__SeekableStreamDecoder *)client_data;
	(void)decoder;
	if(seekable_stream_decoder->private_->eof_callback(seekable_stream_decoder, seekable_stream_decoder->private_->client_data)) {
		*bytes = 0;
#if 0
		/*@@@@@@ verify that this is not needed */
		seekable_stream_decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM;
#endif
		return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
	}
	else if(*bytes > 0) {
		if(seekable_stream_decoder->private_->read_callback(seekable_stream_decoder, buffer, bytes, seekable_stream_decoder->private_->client_data) != FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK) {
			seekable_stream_decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR;
			return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
		}
		if(*bytes == 0) {
			if(seekable_stream_decoder->private_->eof_callback(seekable_stream_decoder, seekable_stream_decoder->private_->client_data)) {
#if 0
				/*@@@@@@ verify that this is not needed */
				seekable_stream_decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM;
#endif
				return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
			}
			else
				return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
		}
		else {
			return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
		}
	}
	else
		return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
}

FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
	FLAC__SeekableStreamDecoder *seekable_stream_decoder = (FLAC__SeekableStreamDecoder *)client_data;
	(void)decoder;

	if(seekable_stream_decoder->protected_->state == FLAC__SEEKABLE_STREAM_DECODER_SEEKING) {
		FLAC__uint64 this_frame_sample = frame->header.number.sample_number;
		FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize;
		FLAC__uint64 target_sample = seekable_stream_decoder->private_->target_sample;

		FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);

		seekable_stream_decoder->private_->last_frame = *frame; /* save the frame */
		if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */
			unsigned delta = (unsigned)(target_sample - this_frame_sample);
			/* kick out of seek mode */
			seekable_stream_decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_OK;
			/* shift out the samples before target_sample */
			if(delta > 0) {
				unsigned channel;
				const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS];
				for(channel = 0; channel < frame->header.channels; channel++)
					newbuffer[channel] = buffer[channel] + delta;
				seekable_stream_decoder->private_->last_frame.header.blocksize -= delta;
				seekable_stream_decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta;
				/* write the relevant samples */
				return seekable_stream_decoder->private_->write_callback(seekable_stream_decoder, &seekable_stream_decoder->private_->last_frame, newbuffer, seekable_stream_decoder->private_->client_data);
			}
			else {
				/* write the relevant samples */
				return seekable_stream_decoder->private_->write_callback(seekable_stream_decoder, frame, buffer, seekable_stream_decoder->private_->client_data);
			}
		}
		else {
			return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
		}
	}
	else {
		if(seekable_stream_decoder->private_->do_md5_checking) {
			if(!FLAC__MD5Accumulate(&seekable_stream_decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8))
				return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
		}
		return seekable_stream_decoder->private_->write_callback(seekable_stream_decoder, frame, buffer, seekable_stream_decoder->private_->client_data);
	}
}

void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
{
	FLAC__SeekableStreamDecoder *seekable_stream_decoder = (FLAC__SeekableStreamDecoder *)client_data;
	(void)decoder;

	if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
		seekable_stream_decoder->private_->stream_info = metadata->data.stream_info;
		/* save the MD5 signature for comparison later */
		memcpy(seekable_stream_decoder->private_->stored_md5sum, metadata->data.stream_info.md5sum, 16);
		if(0 == memcmp(seekable_stream_decoder->private_->stored_md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16))
			seekable_stream_decoder->private_->do_md5_checking = false;
	}
	else if(metadata->type == FLAC__METADATA_TYPE_SEEKTABLE) {
		seekable_stream_decoder->private_->seek_table = &metadata->data.seek_table;
	}

	if(seekable_stream_decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING) {
		FLAC__bool ignore_block = false;
		if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO && seekable_stream_decoder->private_->ignore_stream_info_block)
			ignore_block = true;
		else if(metadata->type == FLAC__METADATA_TYPE_SEEKTABLE && seekable_stream_decoder->private_->ignore_seek_table_block)
			ignore_block = true;
		if(!ignore_block)
			seekable_stream_decoder->private_->metadata_callback(seekable_stream_decoder, metadata, seekable_stream_decoder->private_->client_data);
	}
}

void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
	FLAC__SeekableStreamDecoder *seekable_stream_decoder = (FLAC__SeekableStreamDecoder *)client_data;
	(void)decoder;

	if(seekable_stream_decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING)
		seekable_stream_decoder->private_->error_callback(seekable_stream_decoder, status, seekable_stream_decoder->private_->client_data);
}

FLAC__bool seek_to_absolute_sample_(FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample)
{
	FLAC__uint64 first_frame_offset, lower_bound, upper_bound;
	FLAC__int64 pos = -1, last_pos = -1;
	int i, lower_seek_point = -1, upper_seek_point = -1;
	unsigned approx_bytes_per_frame;
	FLAC__uint64 last_frame_sample = FLAC__U64L(0xffffffffffffffff);
	FLAC__bool needs_seek;
	const FLAC__uint64 total_samples = decoder->private_->stream_info.total_samples;
	const unsigned min_blocksize = decoder->private_->stream_info.min_blocksize;
	const unsigned max_blocksize = decoder->private_->stream_info.max_blocksize;
	const unsigned max_framesize = decoder->private_->stream_info.max_framesize;
	const unsigned channels = FLAC__seekable_stream_decoder_get_channels(decoder);
	const unsigned bps = FLAC__seekable_stream_decoder_get_bits_per_sample(decoder);

	/* we are just guessing here, but we want to guess high, not low */
	if(max_framesize > 0) {
		approx_bytes_per_frame = max_framesize;
	}
	/*
	 * Check if it's a known fixed-blocksize stream.  Note that though
	 * the spec doesn't allow zeroes in the STREAMINFO block, we may
	 * never get a STREAMINFO block when decoding so the value of
	 * min_blocksize might be zero.
	 */
	else if(min_blocksize == max_blocksize && min_blocksize > 0) {
		/* note there are no () around 'bps/8' to keep precision up since it's an integer calulation */
		approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64;
	}
	else
		approx_bytes_per_frame = 4608 * channels * bps/8 + 64;

	/*
	 * The decode position is currently at the first frame since we
	 * rewound and processed metadata.
	 */
	if(!FLAC__seekable_stream_decoder_get_decode_position(decoder, &first_frame_offset)) {
		decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
		return false;
	}

	/*
	 * First, we set an upper and lower bound on where in the
	 * stream we will search.  For now we assume the worst case
	 * scenario, which is our best guess at the beginning of
	 * the first and last frames.
	 */
	lower_bound = first_frame_offset;

	/* calc the upper_bound, beyond which we never want to seek */
	if(max_framesize > 0)
		upper_bound = stream_length - (max_framesize + 128 + 2); /* 128 for a possible ID3V1 tag, 2 for indexing differences */
	else
		upper_bound = stream_length - ((channels * bps * FLAC__MAX_BLOCK_SIZE) / 8 + 128 + 2);

	/*
	 * Now we refine the bounds if we have a seektable with
	 * suitable points.  Note that according to the spec they
	 * must be ordered by ascending sample number.
	 */
	if(0 != decoder->private_->seek_table) {
		/* find the closest seek point <= target_sample, if it exists */
		for(i = (int)decoder->private_->seek_table->num_points - 1; i >= 0; i--) {
			if(decoder->private_->seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && decoder->private_->seek_table->points[i].sample_number <= target_sample)
				break;
		}
		if(i >= 0) { /* i.e. we found a suitable seek point... */
			lower_bound = first_frame_offset + decoder->private_->seek_table->points[i].stream_offset;
			lower_seek_point = i;
		}

		/* find the closest seek point > target_sample, if it exists */
		for(i = 0; i < (int)decoder->private_->seek_table->num_points; i++) {
			if(decoder->private_->seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && decoder->private_->seek_table->points[i].sample_number > target_sample)
				break;
		}
		if(i < (int)decoder->private_->seek_table->num_points) { /* i.e. we found a suitable seek point... */
			upper_bound = first_frame_offset + decoder->private_->seek_table->points[i].stream_offset;
			upper_seek_point = i;
		}
	}

	/*
	 * Now guess at where within those bounds our target
	 * sample will be.
	 */
	if(lower_seek_point >= 0) {
		/* first see if our sample is within a few frames of the lower seekpoint */
		if(decoder->private_->seek_table->points[lower_seek_point].sample_number <= target_sample && target_sample < decoder->private_->seek_table->points[lower_seek_point].sample_number + (decoder->private_->seek_table->points[lower_seek_point].frame_samples * 4)) {
			pos = (FLAC__int64)lower_bound;
		}
		else if(upper_seek_point >= 0) {
			const FLAC__uint64 target_offset = target_sample - decoder->private_->seek_table->points[lower_seek_point].sample_number;
			const FLAC__uint64 range_samples = decoder->private_->seek_table->points[upper_seek_point].sample_number - decoder->private_->seek_table->points[lower_seek_point].sample_number;
			const FLAC__uint64 range_bytes = (upper_bound>lower_bound? upper_bound - lower_bound - 1 : 0);
#ifndef FLAC__INTEGER_ONLY_LIBRARY
#if defined _MSC_VER || defined __MINGW32__
			/* with MSVC you have to spoon feed it the casting */
			pos = (FLAC__int64)lower_bound + (FLAC__int64)(((FLAC__double)(FLAC__int64)target_offset / (FLAC__double)(FLAC__int64)range_samples) * (FLAC__double)(FLAC__int64)(range_bytes-1)) - approx_bytes_per_frame;
#else
			pos = (FLAC__int64)lower_bound + (FLAC__int64)(((FLAC__double)target_offset / (FLAC__double)range_samples) * (FLAC__double)range_bytes) - approx_bytes_per_frame;
#endif
#else
			/* a little less accurate: */
			if (range_bytes <= 0xffffffff)
				pos = (FLAC__int64)lower_bound + (FLAC__int64)((target_offset * range_bytes) / range_samples) - approx_bytes_per_frame;
			else /* @@@ WATCHOUT, ~2TB limit */
				pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_offset>>8) * (range_bytes>>8)) / (range_samples>>16)) - approx_bytes_per_frame;
#endif
		}
	}

	/*
	 * If there's no seek table, we need to use the metadata (if we
	 * have it) and the filelength to estimate the position of the
	 * frame with the correct sample.
	 */
	if(pos < 0 && total_samples > 0) {
		/*
		 * For max accuracy we should be using
		 * (stream_length-first_frame_offset-1) in the divisor, but the
		 * difference is trivial and (stream_length-first_frame_offset)
		 * has no chance of underflow.
		 */
#ifndef FLAC__INTEGER_ONLY_LIBRARY
#if defined _MSC_VER || defined __MINGW32__
		/* with VC++ you have to spoon feed it the casting */
		pos = (FLAC__int64)first_frame_offset + (FLAC__int64)(((FLAC__double)(FLAC__int64)target_sample / (FLAC__double)(FLAC__int64)total_samples) * (FLAC__double)(FLAC__int64)(stream_length-first_frame_offset)) - approx_bytes_per_frame;
#else
		pos = (FLAC__int64)first_frame_offset + (FLAC__int64)(((FLAC__double)target_sample / (FLAC__double)total_samples) * (FLAC__double)(stream_length-first_frame_offset)) - approx_bytes_per_frame;
#endif
#else
		/* a little less accurate: */
		if (stream_length < 0xffffffff)
			pos = (FLAC__int64)first_frame_offset + (FLAC__int64)((target_sample * (stream_length-first_frame_offset)) / total_samples) - approx_bytes_per_frame;
		else /* @@@ WATCHOUT, ~2TB limit */
			pos = (FLAC__int64)first_frame_offset + (FLAC__int64)(((target_sample>>8) * ((stream_length-first_frame_offset)>>8)) / (total_samples>>16)) - approx_bytes_per_frame;
#endif
	}

	/*
	 * If there's no seek table and total_samples is unknown, we
	 * don't even bother trying to figure out a target, we just use
	 * our current position.
	 */
	if(pos < 0) {
		FLAC__uint64 upos;
		if(decoder->private_->tell_callback(decoder, &upos, decoder->private_->client_data) != FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK) {
			decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
			return false;
		}
		pos = (FLAC__int64)upos;
		needs_seek = false;
	}
	else
		needs_seek = true;

	/* clip the position to the bounds, lower bound takes precedence */
	if(pos >= (FLAC__int64)upper_bound) {
		pos = (FLAC__int64)upper_bound-1;
		needs_seek = true;
	}
	if(pos < (FLAC__int64)lower_bound) {
		pos = (FLAC__int64)lower_bound;
		needs_seek = true;
	}

	decoder->private_->target_sample = target_sample;
	while(1) {
		if(needs_seek) {
			if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK) {
				decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
				return false;
			}
			if(!FLAC__stream_decoder_flush(decoder->private_->stream_decoder)) {
				decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR;
				return false;
			}
		}
		/* Now we need to get a frame.  It is possible for our seek
		 * to land in the middle of audio data that looks exactly like
		 * a frame header from a future version of an encoder.  When
		 * that happens, FLAC__stream_decoder_process_single() will
		 * return false and the state will be
		 * FLAC__STREAM_DECODER_UNPARSEABLE_STREAM.  But there is a
		 * remote possibility that it is properly synced at such a
		 * "future-codec frame", so to make sure, we wait to see
		 * several "unparseable" errors in a row before bailing out.
		 */
		{
			unsigned unparseable_count;
			FLAC__bool got_a_frame = false;
			for (unparseable_count = 0; !got_a_frame && unparseable_count < 10; unparseable_count++) {
				if(FLAC__stream_decoder_process_single(decoder->private_->stream_decoder))
					got_a_frame = true;
				else if(decoder->private_->stream_decoder->protected_->state == FLAC__STREAM_DECODER_UNPARSEABLE_STREAM)
					/* try again.  we don't want to flush the decoder since that clears the bitbuffer */
					decoder->private_->stream_decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC;
				else /* it's a real error */
					break;
			}
			if (!got_a_frame) {
				decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
				return false;
			}
		}
		/* our write callback will change the state when it gets to the target frame */
		/* actually, we could have got_a_frame if our decoder is at FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM so we need to check for that also */
		if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) {
			break;
		}
		else { /* we need to narrow the search */
			const FLAC__uint64 this_frame_sample = decoder->private_->last_frame.header.number.sample_number;
			FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);
			if(this_frame_sample == last_frame_sample && pos < last_pos) {
				/* our last move backwards wasn't big enough, double it */
				pos -= (last_pos - pos);
				needs_seek = true;
			}
			else {
				if(target_sample < this_frame_sample) {
					last_pos = pos;
					approx_bytes_per_frame = decoder->private_->last_frame.header.blocksize * channels * bps/8 + 64;
					pos -= approx_bytes_per_frame;
					needs_seek = true;
				}
				else { /* target_sample >= this_frame_sample + this frame's blocksize */
					FLAC__uint64 upos;
					if(decoder->private_->tell_callback(decoder, &upos, decoder->private_->client_data) != FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK) {
						decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
						return false;
					}
					last_pos = pos;
					pos = (FLAC__int64)upos;
					pos -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder->private_->stream_decoder);
					needs_seek = false;
					/*
					 * if we haven't hit the target frame yet and our position hasn't changed,
					 * it means we're at the end of the stream and the seek target does not exist.
					 */
					if(last_pos == pos) {
						decoder->protected_->state = FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR;
						return false;
					}
				}
			}
			if(pos < (FLAC__int64)lower_bound)
				pos = (FLAC__int64)lower_bound;
			last_frame_sample = this_frame_sample;
		}
	}

	return true;
}