[iortcw] 29/497: All: Update opusfile to 0.5

Simon McVittie smcv at debian.org
Fri Sep 8 10:36:08 UTC 2017


This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to annotated tag 1.42d
in repository iortcw.

commit 7101f8079995854fbc13cab3c07392a1a528ff98
Author: M4N4T4RMS at gmail.com <M4N4T4RMS at gmail.com@e65d2741-a53d-b2dc-ae96-bb75fa5e4c4a>
Date:   Mon Jan 27 11:14:45 2014 +0000

    All: Update opusfile to 0.5
---
 MP/Makefile                                        |   2 +-
 .../code/opusfile-0.5}/include/opusfile.h          | 299 ++++++++++--
 .../code/opusfile-0.5}/src/http.c                  | 330 +++++++++----
 .../code/opusfile-0.5}/src/info.c                  | 186 ++++---
 .../{opusfile-0.4 => opusfile-0.5}/src/internal.c  |   0
 .../{opusfile-0.4 => opusfile-0.5}/src/internal.h  |   7 +
 .../code/opusfile-0.5}/src/opusfile.c              | 537 ++++++++++++---------
 .../code/opusfile-0.5}/src/stream.c                |  14 +-
 .../{opusfile-0.4 => opusfile-0.5}/src/wincerts.c  |   0
 MP/code/opusfile-0.5/src/winerrno.h                |  90 ++++
 SP/Makefile                                        |   2 +-
 .../code/opusfile-0.5}/include/opusfile.h          | 299 ++++++++++--
 .../code/opusfile-0.5}/src/http.c                  | 330 +++++++++----
 .../code/opusfile-0.5}/src/info.c                  | 186 ++++---
 .../{opusfile-0.4 => opusfile-0.5}/src/internal.c  |   0
 .../{opusfile-0.4 => opusfile-0.5}/src/internal.h  |   7 +
 .../code/opusfile-0.5}/src/opusfile.c              | 537 ++++++++++++---------
 .../code/opusfile-0.5}/src/stream.c                |  14 +-
 .../{opusfile-0.4 => opusfile-0.5}/src/wincerts.c  |   0
 SP/code/opusfile-0.5/src/winerrno.h                |  90 ++++
 20 files changed, 2000 insertions(+), 930 deletions(-)

diff --git a/MP/Makefile b/MP/Makefile
index 55ac0a5..52850e6 100644
--- a/MP/Makefile
+++ b/MP/Makefile
@@ -270,7 +270,7 @@ UIDIR=$(MOUNT_DIR)/ui
 JPDIR=$(MOUNT_DIR)/jpeg-8c
 OGGDIR=$(MOUNT_DIR)/libogg-1.3.1
 OPUSDIR=$(MOUNT_DIR)/opus-1.1
-OPUSFILEDIR=$(MOUNT_DIR)/opusfile-0.4
+OPUSFILEDIR=$(MOUNT_DIR)/opusfile-0.5
 ZDIR=$(MOUNT_DIR)/zlib
 Q3ASMDIR=$(MOUNT_DIR)/tools/asm
 LBURGDIR=$(MOUNT_DIR)/tools/lcc/lburg
diff --git a/SP/code/opusfile-0.4/include/opusfile.h b/MP/code/opusfile-0.5/include/opusfile.h
similarity index 87%
rename from SP/code/opusfile-0.4/include/opusfile.h
rename to MP/code/opusfile-0.5/include/opusfile.h
index 31eeaed..850cd6b 100644
--- a/SP/code/opusfile-0.4/include/opusfile.h
+++ b/MP/code/opusfile-0.5/include/opusfile.h
@@ -71,7 +71,7 @@
     <a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
     mappings</a>.
    A special stereo API can convert everything to 2 channels, making it simple
-    to support multichannel files in a application which only has stereo
+    to support multichannel files in an application which only has stereo
     output.
    Although the <tt>libopusfile</tt> ABI provides support for the theoretical
     maximum number of channels, the current implementation does not support
@@ -90,9 +90,10 @@
     (ignoring any other streams that might be concurrently multiplexed with it,
     such as a video stream).
 
-   The channel count can also change between links, but if your application is
-    not prepared to deal with this, it can use the stereo API to ensure the
-    audio from all links will always get decoded into a common format.
+   The channel count can also change between links.
+   If your application is not prepared to deal with this, it can use the stereo
+    API to ensure the audio from all links will always get decoded into a
+    common format.
    Since <tt>libopusfile</tt> always decodes to 48 kHz, you do not have to
     worry about the sample rate changing between links (as was possible with
     Vorbis).
@@ -108,6 +109,8 @@ extern "C" {
 # include <ogg/ogg.h>
 # include <opus_multistream.h>
 
+/**@cond PRIVATE*/
+
 /*Enable special features for gcc and gcc-compatible compilers.*/
 # if !defined(OP_GNUC_PREREQ)
 #  if defined(__GNUC__)&&defined(__GNUC_MINOR__)
@@ -122,10 +125,12 @@ extern "C" {
 #  pragma GCC visibility push(default)
 # endif
 
-typedef struct OpusHead       OpusHead;
-typedef struct OpusTags       OpusTags;
-typedef struct OpusPictureTag OpusPictureTag;
-typedef struct OggOpusFile    OggOpusFile;
+typedef struct OpusHead          OpusHead;
+typedef struct OpusTags          OpusTags;
+typedef struct OpusPictureTag    OpusPictureTag;
+typedef struct OpusServerInfo    OpusServerInfo;
+typedef struct OpusFileCallbacks OpusFileCallbacks;
+typedef struct OggOpusFile       OggOpusFile;
 
 /*Warning attributes for libopusfile functions.*/
 # if OP_GNUC_PREREQ(3,4)
@@ -139,6 +144,8 @@ typedef struct OggOpusFile    OggOpusFile;
 #  define OP_ARG_NONNULL(_x)
 # endif
 
+/**@endcond*/
+
 /**\defgroup error_codes Error Codes*/
 /*@{*/
 /**\name List of possible error codes
@@ -377,9 +384,9 @@ struct OpusPictureTag{
      <li>#OP_PIC_FORMAT_UNKNOWN,</li>
      <li>#OP_PIC_FORMAT_URL,</li>
      <li>#OP_PIC_FORMAT_JPEG,</li>
-     <li>#OP_PIC_FORMAT_PNG,</li>
-     <li>#OP_PIC_FORMAT_GIF, or</li>
-     </ul>.*/
+     <li>#OP_PIC_FORMAT_PNG, or</li>
+     <li>#OP_PIC_FORMAT_GIF.</li>
+     </ul>*/
   int            format;
 };
 
@@ -445,7 +452,7 @@ ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp)
                       for validity.
    \param[in]  _data The contents of the 'comment' header packet.
    \param      _len  The number of bytes of data in the 'info' header packet.
-   \retval 0             Success.
+   \retval 0              Success.
    \retval #OP_ENOTFORMAT If the data does not start with the "OpusTags"
                            string.
    \retval #OP_EBADHEADER If the contents of the packet otherwise violate the
@@ -454,6 +461,15 @@ ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp)
 OP_WARN_UNUSED_RESULT int opus_tags_parse(OpusTags *_tags,
  const unsigned char *_data,size_t _len) OP_ARG_NONNULL(2);
 
+/**Performs a deep copy of an #OpusTags structure.
+   \param _dst The #OpusTags structure to copy into.
+               If this function fails, the contents of this structure remain
+                untouched.
+   \param _src The #OpusTags structure to copy from.
+   \retval 0          Success.
+   \retval #OP_EFAULT If there wasn't enough memory to copy the tags.*/
+int opus_tags_copy(OpusTags *_dst,const OpusTags *_src) OP_ARG_NONNULL(1);
+
 /**Initializes an #OpusTags structure.
    This should be called on a freshly allocated #OpusTags structure before
     attempting to use it.
@@ -529,7 +545,7 @@ int opus_tags_query_count(const OpusTags *_tags,const char *_tag)
                         On error, no value is returned, and the previous
                          contents remain unchanged.
    \return 0 on success, or a negative value on error.
-   \retval OP_EFALSE There was no track gain available in the given tags.*/
+   \retval #OP_FALSE There was no track gain available in the given tags.*/
 int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8)
  OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
 
@@ -540,6 +556,32 @@ int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8)
    \param _tags The #OpusTags structure to clear.*/
 void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
 
+/**Check if \a _comment is an instance of a \a _tag_name tag.
+   \see opus_tagncompare
+   \param _tag_name A NUL-terminated, case-insensitive, ASCII string containing
+                     the name of the tag to check for (without the terminating
+                     '=' character).
+   \param _comment  The comment string to check.
+   \return An integer less than, equal to, or greater than zero if \a _comment
+            is found respectively, to be less than, to match, or be greater
+            than a "tag=value" string whose tag matches \a _tag_name.*/
+int opus_tagcompare(const char *_tag_name,const char *_comment);
+
+/**Check if \a _comment is an instance of a \a _tag_name tag.
+   This version is slightly more efficient than opus_tagcompare() if the length
+    of the tag name is already known (e.g., because it is a constant).
+   \see opus_tagcompare
+   \param _tag_name A case-insensitive ASCII string containing the name of the
+                     tag to check for (without the terminating '=' character).
+   \param _tag_len  The number of characters in the tag name.
+                    This must be non-negative.
+   \param _comment  The comment string to check.
+   \return An integer less than, equal to, or greater than zero if \a _comment
+            is found respectively, to be less than, to match, or be greater
+            than a "tag=value" string whose tag matches the first \a _tag_len
+            characters of \a _tag_name.*/
+int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment);
+
 /**Parse a single METADATA_BLOCK_PICTURE tag.
    This decodes the BASE64-encoded content of the tag and returns a structure
     with the MIME type, description, image parameters (if known), and the
@@ -568,9 +610,10 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
                      of opus_tags_query().
    \return 0 on success or a negative value on error.
    \retval #OP_ENOTFORMAT The METADATA_BLOCK_PICTURE contents were not valid.
-   \retval #OP_EFAULT     A memory allocation failed.*/
-int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag)
- OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
+   \retval #OP_EFAULT     There was not enough memory to store the picture tag
+                           contents.*/
+OP_WARN_UNUSED_RESULT int opus_picture_tag_parse(OpusPictureTag *_pic,
+ const char *_tag) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
 
 /**Initializes an #OpusPictureTag structure.
    This should be called on a freshly allocated #OpusPictureTag structure
@@ -601,6 +644,8 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
    They may be expanded in the future.*/
 /*@{*/
 
+/**@cond PRIVATE*/
+
 /*These are the raw numbers used to define the request codes.
   They should not be used directly.*/
 #define OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST (6464)
@@ -608,6 +653,7 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
 #define OP_HTTP_PROXY_PORT_REQUEST            (6592)
 #define OP_HTTP_PROXY_USER_REQUEST            (6656)
 #define OP_HTTP_PROXY_PASS_REQUEST            (6720)
+#define OP_GET_SERVER_INFO_REQUEST            (6784)
 
 #define OP_URL_OPT(_request) ((_request)+(char *)0)
 
@@ -615,6 +661,64 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
    provided to one of the URL options.*/
 #define OP_CHECK_INT(_x) ((void)((_x)==(opus_int32)0),(opus_int32)(_x))
 #define OP_CHECK_CONST_CHAR_PTR(_x) ((_x)+((_x)-(const char *)(_x)))
+#define OP_CHECK_SERVER_INFO_PTR(_x) ((_x)+((_x)-(OpusServerInfo *)(_x)))
+
+/**@endcond*/
+
+/**HTTP/Shoutcast/Icecast server information associated with a URL.*/
+struct OpusServerInfo{
+  /**The name of the server (icy-name/ice-name).
+     This is <code>NULL</code> if there was no <code>icy-name</code> or
+      <code>ice-name</code> header.*/
+  char        *name;
+  /**A short description of the server (icy-description/ice-description).
+     This is <code>NULL</code> if there was no <code>icy-description</code> or
+      <code>ice-description</code> header.*/
+  char        *description;
+  /**The genre the server falls under (icy-genre/ice-genre).
+     This is <code>NULL</code> if there was no <code>icy-genre</code> or
+      <code>ice-genre</code> header.*/
+  char        *genre;
+  /**The homepage for the server (icy-url/ice-url).
+     This is <code>NULL</code> if there was no <code>icy-url</code> or
+      <code>ice-url</code> header.*/
+  char        *url;
+  /**The software used by the origin server (Server).
+     This is <code>NULL</code> if there was no <code>Server</code> header.*/
+  char        *server;
+  /**The media type of the entity sent to the recepient (Content-Type).
+     This is <code>NULL</code> if there was no <code>Content-Type</code>
+      header.*/
+  char        *content_type;
+  /**The nominal stream bitrate in kbps (icy-br/ice-bitrate).
+     This is <code>-1</code> if there was no <code>icy-br</code> or
+      <code>ice-bitrate</code> header.*/
+  opus_int32   bitrate_kbps;
+  /**Flag indicating whether the server is public (<code>1</code>) or not
+      (<code>0</code>) (icy-pub/ice-public).
+     This is <code>-1</code> if there was no <code>icy-pub</code> or
+      <code>ice-public</code> header.*/
+  int          is_public;
+  /**Flag indicating whether the server is using HTTPS instead of HTTP.
+     This is <code>0</code> unless HTTPS is being used.
+     This may not match the protocol used in the original URL if there were
+      redirections.*/
+  int          is_ssl;
+};
+
+/**Initializes an #OpusServerInfo structure.
+   All fields are set as if the corresponding header was not available.
+   \param _info The #OpusServerInfo structure to initialize.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.*/
+void opus_server_info_init(OpusServerInfo *_info) OP_ARG_NONNULL(1);
+
+/**Clears the #OpusServerInfo structure.
+   This should be called on an #OpusServerInfo structure after it is no longer
+    needed.
+   It will free all memory used by the structure members.
+   \param _info The #OpusServerInfo structure to clear.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.*/
+void opus_server_info_clear(OpusServerInfo *_info) OP_ARG_NONNULL(1);
 
 /**Skip the certificate check when connecting via TLS/SSL (https).
    \param _b <code>opus_int32</code>: Whether or not to skip the certificate
@@ -660,7 +764,7 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
                                arguments.
    \hideinitializer*/
 #define OP_HTTP_PROXY_USER(_user) \
- OP_URL_OPT(OP_HTTP_PROXY_USER_REQUEST),OP_CHECK_CONST_CHAR_PTR(_host)
+ OP_URL_OPT(OP_HTTP_PROXY_USER_REQUEST),OP_CHECK_CONST_CHAR_PTR(_user)
 
 /**Use the given password for authentication when proxying connections.
    All proxy parameters are ignored for non-http and non-https URLs.
@@ -673,7 +777,28 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
                                arguments.
    \hideinitializer*/
 #define OP_HTTP_PROXY_PASS(_pass) \
- OP_URL_OPT(OP_HTTP_PROXY_PASS_REQUEST),OP_CHECK_CONST_CHAR_PTR(_host)
+ OP_URL_OPT(OP_HTTP_PROXY_PASS_REQUEST),OP_CHECK_CONST_CHAR_PTR(_pass)
+
+/**Parse information about the streaming server (if any) and return it.
+   Very little validation is done.
+   In particular, OpusServerInfo::url may not be a valid URL,
+    OpusServerInfo::bitrate_kbps may not really be in kbps, and
+    OpusServerInfo::content_type may not be a valid MIME type.
+   The character set of the string fields is not specified anywhere, and should
+    not be assumed to be valid UTF-8.
+   \param _info OpusServerInfo *: Returns information about the server.
+                                  If there is any error opening the stream, the
+                                   contents of this structure remain
+                                   unmodified.
+                                  On success, fills in the structure with the
+                                   server information that was available, if
+                                   any.
+                                  After a successful return, the contents of
+                                   this structure should be freed by calling
+                                   opus_server_info_clear().
+   \hideinitializer*/
+#define OP_GET_SERVER_INFO(_info) \
+ OP_URL_OPT(OP_GET_SERVER_INFO_REQUEST),OP_CHECK_SERVER_INFO_PTR(_info)
 
 /*@}*/
 /*@}*/
@@ -690,8 +815,6 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
     block of memory, or URLs.*/
 /*@{*/
 
-typedef struct OpusFileCallbacks OpusFileCallbacks;
-
 /**Reads up to \a _nbytes bytes of data from \a _stream.
    \param      _stream The stream to read from.
    \param[out] _ptr    The buffer to store the data in.
@@ -825,6 +948,7 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
     takes a va_list instead of a variable number of arguments.
    It does not call the <code>va_end</code> macro, and because it invokes the
     <code>va_arg</code> macro, the value of \a _ap is undefined after the call.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param[out]    _cb  The callbacks to use for this stream.
                        If there is an error creating the stream, nothing will
                         be filled in here.
@@ -833,6 +957,10 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
                         schemes are supported.
                        Both <http:> and <https:> may be disabled at compile
                         time, in which case opening such URLs will always fail.
+                       Currently this only supports URIs.
+                       IRIs should be converted to UTF-8 and URL-escaped, with
+                        internationalized domain names encoded in punycode,
+                        before passing them to this function.
    \param[in,out] _ap  A list of the \ref url_options "optional flags" to use.
                        This is a variable-length list of options terminated
                         with <code>NULL</code>.
@@ -841,7 +969,8 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
 OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
  const char *_url,va_list _ap) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
 
-/**Creates a stream that reads from the given URL using the specified proxy.
+/**Creates a stream that reads from the given URL.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param[out] _cb  The callbacks to use for this stream.
                     If there is an error creating the stream, nothing will be
                      filled in here.
@@ -850,6 +979,10 @@ OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
                      are supported.
                     Both <http:> and <https:> may be disabled at compile time,
                      in which case opening such URLs will always fail.
+                    Currently this only supports URIs.
+                    IRIs should be converted to UTF-8 and URL-escaped, with
+                     internationalized domain names encoded in punycode, before
+                     passing them to this function.
    \param      ...  The \ref url_options "optional flags" to use.
                     This is a variable-length list of options terminated with
                      <code>NULL</code>.
@@ -932,12 +1065,17 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_memory(const unsigned char *_data,
     takes a va_list instead of a variable number of arguments.
    It does not call the <code>va_end</code> macro, and because it invokes the
     <code>va_arg</code> macro, the value of \a _ap is undefined after the call.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param         _url   The URL to open.
                          Currently only the <file:>, <http:>, and <https:>
                           schemes are supported.
                          Both <http:> and <https:> may be disabled at compile
                           time, in which case opening such URLs will always
                           fail.
+                         Currently this only supports URIs.
+                         IRIs should be converted to UTF-8 and URL-escaped,
+                          with internationalized domain names encoded in
+                          punycode, before passing them to this function.
    \param[out]    _error Returns 0 on success, or a failure code on error.
                          You may pass in <code>NULL</code> if you don't want
                           the failure code.
@@ -952,13 +1090,16 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_vopen_url(const char *_url,
  int *_error,va_list _ap) OP_ARG_NONNULL(1);
 
 /**Open a stream from a URL.
-   However, this approach will not work for live streams or HTTP/1.0 servers
-    (which do not support Range requets).
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param      _url   The URL to open.
                       Currently only the <file:>, <http:>, and <https:> schemes
                        are supported.
                       Both <http:> and <https:> may be disabled at compile
                        time, in which case opening such URLs will always fail.
+                      Currently this only supports URIs.
+                      IRIs should be converted to UTF-8 and URL-escaped, with
+                       internationalized domain names encoded in punycode,
+                       before passing them to this function.
    \param[out] _error Returns 0 on success, or a failure code on error.
                       You may pass in <code>NULL</code> if you don't want the
                        failure code.
@@ -1081,6 +1222,7 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_memory(const unsigned char *_data,
     takes a va_list instead of a variable number of arguments.
    It does not call the <code>va_end</code> macro, and because it invokes the
     <code>va_arg</code> macro, the value of \a _ap is undefined after the call.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \see op_test_url
    \see op_test_callbacks
    \param         _url    The URL to open.
@@ -1089,6 +1231,10 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_memory(const unsigned char *_data,
                           Both <http:> and <https:> may be disabled at compile
                            time, in which case opening such URLs will always
                            fail.
+                          Currently this only supports URIs.
+                          IRIs should be converted to UTF-8 and URL-escaped,
+                           with internationalized domain names encoded in
+                           punycode, before passing them to this function.
    \param[out]    _error  Returns 0 on success, or a failure code on error.
                           You may pass in <code>NULL</code> if you don't want
                            the failure code.
@@ -1103,12 +1249,17 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_vtest_url(const char *_url,
  int *_error,va_list _ap) OP_ARG_NONNULL(1);
 
 /**Partially open a stream from a URL.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \see op_test_callbacks
    \param      _url    The URL to open.
                        Currently only the <file:>, <http:>, and <https:>
                         schemes are supported.
                        Both <http:> and <https:> may be disabled at compile
                         time, in which case opening such URLs will always fail.
+                       Currently this only supports URIs.
+                       IRIs should be converted to UTF-8 and URL-escaped, with
+                        internationalized domain names encoded in punycode,
+                        before passing them to this function.
    \param[out] _error  Returns 0 on success, or a failure code on error.
                        You may pass in <code>NULL</code> if you don't want the
                         failure code.
@@ -1255,7 +1406,7 @@ void op_free(OggOpusFile *_of);
    This function may be called on partially-opened streams.
    \param _of The \c OggOpusFile whose seekable status is to be returned.
    \return A non-zero value if seekable, and 0 if unseekable.*/
-int op_seekable(OggOpusFile *_of) OP_ARG_NONNULL(1);
+int op_seekable(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Returns the number of links in this chained stream.
    This function may be called on partially-opened streams, but it will always
@@ -1263,9 +1414,9 @@ int op_seekable(OggOpusFile *_of) OP_ARG_NONNULL(1);
    The actual number of links is not known until the stream is fully opened.
    \param _of The \c OggOpusFile from which to retrieve the link count.
    \return For fully-open seekable sources, this returns the total number of
-            links in the whole stream.
+            links in the whole stream, which will be at least 1.
            For partially-open or unseekable sources, this always returns 1.*/
-int op_link_count(OggOpusFile *_of) OP_ARG_NONNULL(1);
+int op_link_count(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Get the serial number of the given link in a (possibly-chained) Ogg Opus
     stream.
@@ -1280,7 +1431,7 @@ int op_link_count(OggOpusFile *_of) OP_ARG_NONNULL(1);
             the serial number of the last link.
            If the source is not seekable, this always returns the serial number
             of the current link.*/
-opus_uint32 op_serialno(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+opus_uint32 op_serialno(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the channel count of the given link in a (possibly-chained) Ogg Opus
     stream.
@@ -1297,7 +1448,7 @@ opus_uint32 op_serialno(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
             the channel count of the last link.
            If the source is not seekable, this always returns the channel count
             of the current link.*/
-int op_channel_count(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+int op_channel_count(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the total (compressed) size of the stream, or of an individual link in
     a (possibly-chained) Ogg Opus stream, including all headers and Ogg muxing
@@ -1315,7 +1466,7 @@ int op_channel_count(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
    \retval #OP_EINVAL The source is not seekable (so we can't know the length),
                        \a _li wasn't less than the total number of links in
                        the stream, or the stream was only partially open.*/
-opus_int64 op_raw_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+opus_int64 op_raw_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the total PCM length (number of samples at 48 kHz) of the stream, or of
     an individual link in a (possibly-chained) Ogg Opus stream.
@@ -1333,7 +1484,7 @@ opus_int64 op_raw_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
    \retval #OP_EINVAL The source is not seekable (so we can't know the length),
                        \a _li wasn't less than the total number of links in
                        the stream, or the stream was only partially open.*/
-ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the ID header information for the given link in a (possibly chained) Ogg
     Opus stream.
@@ -1349,7 +1500,7 @@ ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
                information for the current link is always returned, if
                available.
    \return The contents of the ID header for the given link.*/
-const OpusHead *op_head(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+const OpusHead *op_head(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the comment header information for the given link in a (possibly
     chained) Ogg Opus stream.
@@ -1367,7 +1518,7 @@ const OpusHead *op_head(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
    \return The contents of the comment header for the given link, or
             <code>NULL</code> if this is an unseekable stream that encountered
             an invalid link.*/
-const OpusTags *op_tags(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+const OpusTags *op_tags(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Retrieve the index of the current link.
    This is the link that produced the data most recently read by
@@ -1384,7 +1535,7 @@ const OpusTags *op_tags(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
             each time a new link is encountered (even though op_link_count()
             always returns 1).
    \retval #OP_EINVAL The stream was only partially open.*/
-int op_current_link(OggOpusFile *_of) OP_ARG_NONNULL(1);
+int op_current_link(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Computes the bitrate for a given link in a (possibly chained) Ogg Opus
     stream.
@@ -1397,7 +1548,7 @@ int op_current_link(OggOpusFile *_of) OP_ARG_NONNULL(1);
    \retval #OP_EINVAL The stream was only partially open, the stream was not
                        seekable, or \a _li was larger than the number of
                        links.*/
-opus_int32 op_bitrate(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+opus_int32 op_bitrate(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Compute the instantaneous bitrate, measured as the ratio of bits to playable
     samples decoded since a) the last call to op_bitrate_instant(), b) the last
@@ -1416,7 +1567,7 @@ opus_int32 op_bitrate_instant(OggOpusFile *_of) OP_ARG_NONNULL(1);
    \param _of The \c OggOpusFile from which to retrieve the position indicator.
    \return The byte position that is currently being read from.
    \retval #OP_EINVAL The stream was only partially open.*/
-opus_int64 op_raw_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
+opus_int64 op_raw_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Obtain the PCM offset of the next sample to be read.
    If the stream is not properly timestamped, this might not increment by the
@@ -1425,7 +1576,7 @@ opus_int64 op_raw_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
    \param _of The \c OggOpusFile from which to retrieve the PCM offset.
    \return The PCM offset of the next sample to be read.
    \retval #OP_EINVAL The stream was only partially open.*/
-ogg_int64_t op_pcm_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
+ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /*@}*/
 /*@}*/
@@ -1526,13 +1677,73 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
     not check the error return code from op_read_float() or its associated
     functions.
    If the remote peer does not close the connection gracefully (with a TLS
-    "close notify" message), these functions will return OP_EREAD instead of 0
+    "close notify" message), these functions will return #OP_EREAD instead of 0
     when they reach the end of the file.
    If you are reading from an <https:> URL (particularly if seeking is not
     supported), you should make sure to check for this error and warn the user
     appropriately.*/
 /*@{*/
 
+/**Indicates that the decoding callback should produce signed 16-bit
+    native-endian output samples.*/
+#define OP_DEC_FORMAT_SHORT (7008)
+/**Indicates that the decoding callback should produce 32-bit native-endian
+    float samples.*/
+#define OP_DEC_FORMAT_FLOAT (7040)
+
+/**Indicates that the decoding callback did not decode anything, and that
+    <tt>libopusfile</tt> should decode normally instead.*/
+#define OP_DEC_USE_DEFAULT  (6720)
+
+/**Called to decode an Opus packet.
+   This should invoke the functional equivalent of opus_multistream_decode() or
+    opus_multistream_decode_float(), except that it returns 0 on success
+    instead of the number of decoded samples (which is known a priori).
+   \param _ctx       The application-provided callback context.
+   \param _decoder   The decoder to use to decode the packet.
+   \param[out] _pcm  The buffer to decode into.
+                     This will always have enough room for \a _nchannels of
+                      \a _nsamples samples, which should be placed into this
+                      buffer interleaved.
+   \param _op        The packet to decode.
+                     This will always have its granule position set to a valid
+                      value.
+   \param _nsamples  The number of samples expected from the packet.
+   \param _nchannels The number of channels expected from the packet.
+   \param _format    The desired sample output format.
+                     This is either #OP_DEC_FORMAT_SHORT or
+                      #OP_DEC_FORMAT_FLOAT.
+   \param _li        The index of the link from which this packet was decoded.
+   \return A non-negative value on success, or a negative value on error.
+           The error codes should be the same as those returned by
+            opus_multistream_decode() or opus_multistream_decode_float().
+   \retval 0                   Decoding was successful.
+                               The application has filled the buffer with
+                                exactly <code>\a _nsamples*\a
+                                _nchannels</code> samples in the requested
+                                format.
+   \retval #OP_DEC_USE_DEFAULT No decoding was done.
+                               <tt>libopusfile</tt> should decode normally
+                                instead.*/
+typedef int (*op_decode_cb_func)(void *_ctx,OpusMSDecoder *_decoder,void *_pcm,
+ const ogg_packet *_op,int _nsamples,int _nchannels,int _format,int _li);
+
+/**Sets the packet decode callback function.
+   This is called once for each packet that needs to be decoded.
+   A call to this function is no guarantee that the audio will eventually be
+    delivered to the application.
+   Some or all of the data from the packet may be discarded (i.e., at the
+    beginning or end of a link, or after a seek), however the callback is
+    required to provide all of it.
+   \param _of        The \c OggOpusFile on which to set the decode callback.
+   \param _decode_cb The callback function to call.
+                     This may be <code>NULL</code> to disable calling the
+                      callback.
+   \param _ctx       The application-provided context pointer to pass to the
+                      callback on each call.*/
+void op_set_decode_callback(OggOpusFile *_of,
+ op_decode_cb_func _decode_cb,void *_ctx) OP_ARG_NONNULL(1);
+
 /**Gain offset type that indicates that the provided offset is relative to the
     header gain.
    This is the default.*/
@@ -1564,7 +1775,19 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
    \return 0 on success or a negative value on error.
    \retval #OP_EINVAL The \a _gain_type was unrecognized.*/
 int op_set_gain_offset(OggOpusFile *_of,
- int _gain_type,opus_int32 _gain_offset_q8);
+ int _gain_type,opus_int32 _gain_offset_q8) OP_ARG_NONNULL(1);
+
+/**Sets whether or not dithering is enabled for 16-bit decoding.
+   By default, when <tt>libopusfile</tt> is compiled to use floating-point
+    internally, calling op_read() or op_read_stereo() will first decode to
+    float, and then convert to fixed-point using noise-shaping dithering.
+   This flag can be used to disable that dithering.
+   When the application uses op_read_float() or op_read_float_stereo(), or when
+    the library has been compiled to decode directly to fixed point, this flag
+    has no effect.
+   \param _of      The \c OggOpusFile on which to enable or disable dithering.
+   \param _enabled A non-zero value to enable dithering, or 0 to disable it.*/
+void op_set_dither_enabled(OggOpusFile *_of,int _enabled) OP_ARG_NONNULL(1);
 
 /**Reads more samples from the stream.
    \note Although \a _buf_size must indicate the total number of values that
diff --git a/SP/code/opusfile-0.4/src/http.c b/MP/code/opusfile-0.5/src/http.c
similarity index 92%
rename from SP/code/opusfile-0.4/src/http.c
rename to MP/code/opusfile-0.5/src/http.c
index d172578..4a9eaf5 100644
--- a/SP/code/opusfile-0.4/src/http.c
+++ b/MP/code/opusfile-0.5/src/http.c
@@ -44,7 +44,8 @@
   RFC 6066: Transport Layer Security (TLS) Extensions: Extension Definitions
   RFC 6125: Representation and Verification of Domain-Based Application Service
    Identity within Internet Public Key Infrastructure Using X.509 (PKIX)
-   Certificates in the Context of Transport Layer Security (TLS)*/
+   Certificates in the Context of Transport Layer Security (TLS)
+  RFC 6555: Happy Eyeballs: Success with Dual-Stack Hosts*/
 
 typedef struct OpusParsedURL   OpusParsedURL;
 typedef struct OpusStringBuf   OpusStringBuf;
@@ -60,7 +61,7 @@ static char *op_string_range_dup(const char *_start,const char *_end){
   if(OP_UNLIKELY(len>=INT_MAX))return NULL;
   ret=(char *)_ogg_malloc(sizeof(*ret)*(len+1));
   if(OP_LIKELY(ret!=NULL)){
-    memcpy(ret,_start,sizeof(*ret)*(len));
+    ret=(char *)memcpy(ret,_start,sizeof(*ret)*(len));
     ret[len]='\0';
   }
   return ret;
@@ -358,6 +359,11 @@ typedef int op_sock;
    when seeking, and time out rapidly.*/
 # define OP_NCONNS_MAX (4)
 
+/*The amount of time before we attempt to re-resolve the host.
+  This is 10 minutes, as recommended in RFC 6555 for expiring cached connection
+   results for dual-stack hosts.*/
+# define OP_RESOLVE_CACHE_TIMEOUT_MS (10*60*(opus_int32)1000)
+
 /*The number of redirections at which we give up.
   The value here is the current default in Firefox.
   RFC 2068 mandated a maximum of 5, but RFC 2616 relaxed that to "a client
@@ -616,6 +622,8 @@ static void op_sb_clear(OpusStringBuf *_sb){
   _ogg_free(_sb->buf);
 }
 
+/*Make sure we have room for at least _capacity characters (plus 1 more for the
+   terminating NUL).*/
 static int op_sb_ensure_capacity(OpusStringBuf *_sb,int _capacity){
   char *buf;
   int   cbuf;
@@ -633,6 +641,8 @@ static int op_sb_ensure_capacity(OpusStringBuf *_sb,int _capacity){
   return 0;
 }
 
+/*Increase the capacity of the buffer, but not to more than _max_size
+   characters (plus 1 more for the terminating NUL).*/
 static int op_sb_grow(OpusStringBuf *_sb,int _max_size){
   char *buf;
   int   cbuf;
@@ -827,6 +837,8 @@ struct OpusHTTPStream{
     struct sockaddr_in  v4;
     struct sockaddr_in6 v6;
   }                addr;
+  /*The last time we re-resolved the host.*/
+  struct timeb     resolve_time;
   /*A buffer used to build HTTP requests.*/
   OpusStringBuf    request;
   /*A buffer used to build proxy CONNECT requests.*/
@@ -838,6 +850,10 @@ struct OpusHTTPStream{
   opus_int64       content_length;
   /*The position indicator used when no connection is active.*/
   opus_int64       pos;
+  /*The host we actually connected to.*/
+  char            *connect_host;
+  /*The port we actually connected to.*/
+  unsigned         connect_port;
   /*The connection we're currently reading from.
     This can be -1 if no connection is active.*/
   int              cur_conni;
@@ -871,6 +887,7 @@ static void op_http_stream_init(OpusHTTPStream *_stream){
   op_sb_init(&_stream->request);
   op_sb_init(&_stream->proxy_connect);
   op_sb_init(&_stream->response);
+  _stream->connect_host=NULL;
   _stream->seekable=0;
 }
 
@@ -910,6 +927,7 @@ static void op_http_stream_clear(OpusHTTPStream *_stream){
   op_sb_clear(&_stream->response);
   op_sb_clear(&_stream->proxy_connect);
   op_sb_clear(&_stream->request);
+  if(_stream->connect_host!=_stream->url.host)_ogg_free(_stream->connect_host);
   op_parsed_url_clear(&_stream->url);
 }
 
@@ -977,11 +995,11 @@ static int op_http_conn_estimate_available(OpusHTTPConn *_conn){
 static opus_int32 op_time_diff_ms(const struct timeb *_end,
  const struct timeb *_start){
   opus_int64 dtime;
-  dtime=_end->time-_start->time;
+  dtime=_end->time-(opus_int64)_start->time;
   OP_ASSERT(_end->millitm<1000);
   OP_ASSERT(_start->millitm<1000);
-  if(OP_UNLIKELY(dtime>(0x7FFFFFFF-1000)/1000))return 0x7FFFFFFF;
-  if(OP_UNLIKELY(dtime<(-0x7FFFFFFF+999)/1000))return -0x7FFFFFFF-1;
+  if(OP_UNLIKELY(dtime>(OP_INT32_MAX-1000)/1000))return OP_INT32_MAX;
+  if(OP_UNLIKELY(dtime<(OP_INT32_MIN+1000)/1000))return OP_INT32_MIN;
   return (opus_int32)dtime*1000+_end->millitm-_start->millitm;
 }
 
@@ -1527,7 +1545,7 @@ static BIO_METHOD op_bio_retry_method={
 
 /*Establish a CONNECT tunnel and pipeline the start of the TLS handshake for
    proxying https URL requests.*/
-int op_http_conn_establish_tunnel(OpusHTTPStream *_stream,
+static int op_http_conn_establish_tunnel(OpusHTTPStream *_stream,
  OpusHTTPConn *_conn,op_sock _fd,SSL *_ssl_conn,BIO *_ssl_bio){
   BIO  *retry_bio;
   char *status_code;
@@ -1796,7 +1814,7 @@ static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){
 }
 
 /*Perform the TLS handshake on a new connection.*/
-int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
+static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
  op_sock _fd,SSL *_ssl_conn){
   SSL_SESSION *ssl_session;
   BIO         *ssl_bio;
@@ -1862,9 +1880,9 @@ int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
                     left to try.
                     *_addr will be set to NULL in this case.*/
 static int op_sock_connect_next(op_sock _fd,
- struct addrinfo **_addr,int _ai_family){
-  struct addrinfo *addr;
-  int err;
+ const struct addrinfo **_addr,int _ai_family){
+  const struct addrinfo *addr;
+  int                    err;
   addr=*_addr;
   for(;;){
     /*Move to the next address of the requested type.*/
@@ -1883,36 +1901,30 @@ static int op_sock_connect_next(op_sock _fd,
 /*The number of address families to try connecting to simultaneously.*/
 # define OP_NPROTOS (2)
 
-static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
- struct addrinfo *_addrs,struct timeb *_start_time){
-  struct addrinfo *addr;
-  struct addrinfo *addrs[OP_NPROTOS];
-  struct pollfd    fds[OP_NPROTOS];
-  int              ai_family;
-  int              nprotos;
-  int              ret;
-  int              pi;
-  int              pj;
+static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
+ const struct addrinfo *_addrs,struct timeb *_start_time){
+  const struct addrinfo *addr;
+  const struct addrinfo *addrs[OP_NPROTOS];
+  struct pollfd          fds[OP_NPROTOS];
+  int                    ai_family;
+  int                    nprotos;
+  int                    ret;
+  int                    pi;
+  int                    pj;
   for(pi=0;pi<OP_NPROTOS;pi++)addrs[pi]=NULL;
-  addr=_addrs;
   /*Try connecting via both IPv4 and IPv6 simultaneously, and keep the first
-     one that succeeds.*/
-  for(;addr!=NULL;addr=addr->ai_next){
-    /*Give IPv6 a slight edge by putting it first in the list.*/
-    if(addr->ai_family==AF_INET6){
+     one that succeeds.
+    Start by finding the first address from each family.
+    We order the first connection attempts in the same order the address
+     families were returned in the DNS records in accordance with RFC 6555.*/
+  for(addr=_addrs,nprotos=0;addr!=NULL&&nprotos<OP_NPROTOS;addr=addr->ai_next){
+    if(addr->ai_family==AF_INET6||addr->ai_family==AF_INET){
       OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in6));
-      if(addrs[0]==NULL)addrs[0]=addr;
-    }
-    else if(addr->ai_family==AF_INET){
       OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in));
-      if(addrs[1]==NULL)addrs[1]=addr;
-    }
-  }
-  /*Consolidate the list of addresses.*/
-  for(pi=nprotos=0;pi<OP_NPROTOS;pi++){
-    if(addrs[pi]!=NULL){
-      addrs[nprotos]=addrs[pi];
-      nprotos++;
+      /*If we've seen this address family before, skip this address for now.*/
+      for(pi=0;pi<nprotos;pi++)if(addrs[pi]->ai_family==addr->ai_family)break;
+      if(pi<nprotos)continue;
+      addrs[nprotos++]=addr;
     }
   }
   /*Pop the connection off the free list and put it on the LRU list.*/
@@ -1924,7 +1936,12 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
   *&_conn->read_time=*_start_time;
   _conn->read_bytes=0;
   _conn->read_rate=0;
-  /*Try to start a connection to each protocol.*/
+  /*Try to start a connection to each protocol.
+    RFC 6555 says it is RECOMMENDED that connection attempts be paced
+     150...250 ms apart "to balance human factors against network load", but
+     that "stateful algorithms" (that's us) "are expected to be more
+     aggressive".
+    We are definitely more aggressive: we don't pace at all.*/
   for(pi=0;pi<nprotos;pi++){
     ai_family=addrs[pi]->ai_family;
     fds[pi].fd=socket(ai_family,SOCK_STREAM,addrs[pi]->ai_protocol);
@@ -2016,6 +2033,29 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
   return 0;
 }
 
+static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
+ const struct addrinfo *_addrs,struct timeb *_start_time){
+  struct timeb     resolve_time;
+  struct addrinfo *new_addrs;
+  int              ret;
+  /*Re-resolve the host if we need to (RFC 6555 says we MUST do so
+     occasionally).*/
+  new_addrs=NULL;
+  ftime(&resolve_time);
+  if(_addrs!=&_stream->addr_info||op_time_diff_ms(&resolve_time,
+   &_stream->resolve_time)>=OP_RESOLVE_CACHE_TIMEOUT_MS){
+    new_addrs=op_resolve(_stream->connect_host,_stream->connect_port);
+    if(OP_LIKELY(new_addrs!=NULL)){
+      _addrs=new_addrs;
+      *&_stream->resolve_time=*&resolve_time;
+    }
+    else if(OP_LIKELY(_addrs==NULL))return OP_FALSE;
+  }
+  ret=op_http_connect_impl(_stream,_conn,_addrs,_start_time);
+  if(new_addrs!=NULL)freeaddrinfo(new_addrs);
+  return ret;
+}
+
 # define OP_BASE64_LENGTH(_len) (((_len)+2)/3*4)
 
 static const char BASE64_TABLE[64]={
@@ -2130,53 +2170,33 @@ static int op_http_allow_pipelining(const char *_server){
 
 static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
  int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port,
- const char *_proxy_user,const char *_proxy_pass){
+ const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){
   struct addrinfo *addrs;
-  const char      *last_host;
-  unsigned         last_port;
   int              nredirs;
   int              ret;
-  if(_proxy_host!=NULL&&OP_UNLIKELY(_proxy_port>65535U))return OP_EINVAL;
-  last_host=NULL;
-  /*We shouldn't have to initialize last_port, but gcc is too dumb to figure
-     out that last_host!=NULL implies we've already taken one trip through the
-     loop.*/
-  last_port=0;
 #if defined(_WIN32)
   op_init_winsock();
 #endif
   ret=op_parse_url(&_stream->url,_url);
   if(OP_UNLIKELY(ret<0))return ret;
+  if(_proxy_host!=NULL){
+    if(OP_UNLIKELY(_proxy_port>65535U))return OP_EINVAL;
+    _stream->connect_host=op_string_dup(_proxy_host);
+    _stream->connect_port=_proxy_port;
+  }
+  else{
+    _stream->connect_host=_stream->url.host;
+    _stream->connect_port=_stream->url.port;
+  }
+  addrs=NULL;
   for(nredirs=0;nredirs<OP_REDIRECT_LIMIT;nredirs++){
-    struct timeb  start_time;
-    struct timeb  end_time;
-    char         *next;
-    char         *status_code;
-    const char   *host;
-    unsigned      port;
-    int           minor_version_pos;
-    int           v1_1_compat;
-    if(_proxy_host==NULL){
-      host=_stream->url.host;
-      port=_stream->url.port;
-    }
-    else{
-      host=_proxy_host;
-      port=_proxy_port;
-    }
-    /*If connecting to the same place as last time, don't re-resolve it.*/
-    addrs=NULL;
-    if(last_host!=NULL){
-      if(strcmp(last_host,host)==0&&last_port==port)addrs=&_stream->addr_info;
-      else if(_stream->ssl_session!=NULL){
-        /*Forget any cached SSL session from the last host.*/
-        SSL_SESSION_free(_stream->ssl_session);
-        _stream->ssl_session=NULL;
-      }
-      if(last_host!=_proxy_host)_ogg_free((void *)last_host);
-    }
-    last_host=host;
-    last_port=port;
+    OpusParsedURL  next_url;
+    struct timeb   start_time;
+    struct timeb   end_time;
+    char          *next;
+    char          *status_code;
+    int            minor_version_pos;
+    int            v1_1_compat;
     /*Initialize the SSL library if necessary.*/
     if(OP_URL_IS_SSL(&_stream->url)&&_stream->ssl_ctx==NULL){
       SSL_CTX *ssl_ctx;
@@ -2212,6 +2232,7 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
       if(_proxy_host!=NULL){
         /*We need to establish a CONNECT tunnel to handle https proxying.
           Build the request we'll send to do so.*/
+        _stream->proxy_connect.nbuf=0;
         ret=op_sb_append(&_stream->proxy_connect,"CONNECT ",8);
         ret|=op_sb_append_string(&_stream->proxy_connect,_stream->url.host);
         ret|=op_sb_append_port(&_stream->proxy_connect,_stream->url.port);
@@ -2239,12 +2260,7 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
       }
     }
     /*Actually make the connection.*/
-    if(addrs!=&_stream->addr_info){
-      addrs=op_resolve(host,port);
-      if(OP_UNLIKELY(addrs==NULL))return OP_FALSE;
-    }
     ret=op_http_connect(_stream,_stream->conns+0,addrs,&start_time);
-    if(addrs!=&_stream->addr_info)freeaddrinfo(addrs);
     if(OP_UNLIKELY(ret<0))return ret;
     /*Build the request to send.*/
     _stream->request.nbuf=0;
@@ -2312,13 +2328,15 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
     if(status_code[0]=='2'){
       opus_int64 content_length;
       opus_int64 range_length;
-      int        pipeline;
+      int        pipeline_supported;
+      int        pipeline_disabled;
       /*We only understand 20x codes.*/
       if(status_code[1]!='0')return OP_FALSE;
       content_length=-1;
       range_length=-1;
-      /*Pipelining is disabled by default.*/
-      pipeline=0;
+      /*Pipelining must be explicitly enabled.*/
+      pipeline_supported=0;
+      pipeline_disabled=0;
       for(;;){
         char *header;
         char *cdr;
@@ -2376,9 +2394,9 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
              error out and reconnect, adding lots of latency.*/
           ret=op_http_parse_connection(cdr);
           if(OP_UNLIKELY(ret<0))return ret;
-          pipeline-=ret;
+          pipeline_disabled|=ret;
         }
-        else if(strcmp(header,"server")){
+        else if(strcmp(header,"server")==0){
           /*If we got a Server response header, and it wasn't from a known-bad
              server, enable pipelining, as long as it's at least HTTP/1.1.
             According to RFC 2145, the server is supposed to respond with the
@@ -2386,7 +2404,51 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
              suspected that we incorrectly implement the HTTP specification.
             So it should send back at least HTTP/1.1, despite our HTTP/1.0
              request.*/
-          pipeline+=v1_1_compat&&op_http_allow_pipelining(cdr);
+          pipeline_supported=v1_1_compat;
+          if(v1_1_compat)pipeline_disabled|=!op_http_allow_pipelining(cdr);
+          if(_info!=NULL&&_info->server==NULL)_info->server=op_string_dup(cdr);
+        }
+        /*Collect station information headers if the caller requested it.
+          If there's more than one copy of a header, the first one wins.*/
+        else if(_info!=NULL){
+          if(strcmp(header,"content-type")==0){
+            if(_info->content_type==NULL){
+              _info->content_type=op_string_dup(cdr);
+            }
+          }
+          else if(header[0]=='i'&&header[1]=='c'
+           &&(header[2]=='e'||header[2]=='y')&&header[3]=='-'){
+            if(strcmp(header+4,"name")==0){
+              if(_info->name==NULL)_info->name=op_string_dup(cdr);
+            }
+            else if(strcmp(header+4,"description")==0){
+              if(_info->description==NULL)_info->description=op_string_dup(cdr);
+            }
+            else if(strcmp(header+4,"genre")==0){
+              if(_info->genre==NULL)_info->genre=op_string_dup(cdr);
+            }
+            else if(strcmp(header+4,"url")==0){
+              if(_info->url==NULL)_info->url=op_string_dup(cdr);
+            }
+            else if(strcmp(header,"icy-br")==0
+             ||strcmp(header,"ice-bitrate")==0){
+              if(_info->bitrate_kbps<0){
+                opus_int64 bitrate_kbps;
+                /*Just re-using this function to parse a random unsigned
+                   integer field.*/
+                bitrate_kbps=op_http_parse_content_length(cdr);
+                if(bitrate_kbps>=0&&bitrate_kbps<=OP_INT32_MAX){
+                  _info->bitrate_kbps=(opus_int32)bitrate_kbps;
+                }
+              }
+            }
+            else if(strcmp(header,"icy-pub")==0
+             ||strcmp(header,"ice-public")==0){
+              if(_info->is_public<0&&(cdr[0]=='0'||cdr[0]=='1')&&cdr[1]=='\0'){
+                _info->is_public=cdr[0]-'0';
+              }
+            }
+          }
         }
       }
       switch(status_code[2]){
@@ -2421,15 +2483,16 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
         default:return OP_FALSE;
       }
       _stream->content_length=content_length;
-      _stream->pipeline=pipeline>0;
+      _stream->pipeline=pipeline_supported&&!pipeline_disabled;
       /*Pipelining requires HTTP/1.1 persistent connections.*/
-      if(pipeline)_stream->request.buf[minor_version_pos]='1';
+      if(_stream->pipeline)_stream->request.buf[minor_version_pos]='1';
       _stream->conns[0].pos=0;
       _stream->conns[0].end_pos=_stream->seekable?content_length:-1;
       _stream->conns[0].chunk_size=-1;
       _stream->cur_conni=0;
       _stream->connect_rate=op_time_diff_ms(&end_time,&start_time);
       _stream->connect_rate=OP_MAX(_stream->connect_rate,1);
+      if(_info!=NULL)_info->is_ssl=OP_URL_IS_SSL(&_stream->url);
       /*The URL has been successfully opened.*/
       return 0;
     }
@@ -2472,15 +2535,33 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
       if(strcmp(header,"location")==0&&OP_LIKELY(_url==NULL))_url=cdr;
     }
     if(OP_UNLIKELY(_url==NULL))return OP_FALSE;
-    /*Don't free last_host if it came from the last URL.*/
-    if(last_host!=_proxy_host)_stream->url.host=NULL;
-    op_parsed_url_clear(&_stream->url);
-    ret=op_parse_url(&_stream->url,_url);
-    if(OP_UNLIKELY(ret<0)){
-      if(ret==OP_EINVAL)ret=OP_FALSE;
-      if(last_host!=_proxy_host)_ogg_free((void *)last_host);
-      return ret;
+    ret=op_parse_url(&next_url,_url);
+    if(OP_UNLIKELY(ret<0))return ret;
+    if(_proxy_host==NULL||_stream->ssl_session!=NULL){
+      if(strcmp(_stream->url.host,next_url.host)==0
+       &&_stream->url.port==next_url.port){
+        /*Try to skip re-resolve when connecting to the same host.*/
+        addrs=&_stream->addr_info;
+      }
+      else{
+        if(_stream->ssl_session!=NULL){
+          /*Forget any cached SSL session from the last host.*/
+          SSL_SESSION_free(_stream->ssl_session);
+          _stream->ssl_session=NULL;
+        }
+      }
+    }
+    if(_proxy_host==NULL){
+      OP_ASSERT(_stream->connect_host==_stream->url.host);
+      _stream->connect_host=next_url.host;
+      _stream->connect_port=next_url.port;
     }
+    /*Always try to skip re-resolve for proxy connections.*/
+    else addrs=&_stream->addr_info;
+    op_parsed_url_clear(&_stream->url);
+    *&_stream->url=*&next_url;
+    /*TODO: On servers/proxies that support pipelining, we might be able to
+       re-use this connection.*/
     op_http_conn_close(_stream,_stream->conns+0,&_stream->lru_head,1);
   }
   /*Redirection limit reached.*/
@@ -2756,7 +2837,7 @@ static int op_http_conn_read_body(OpusHTTPStream *_stream,
           /*Unless there's a bug, we should be able to convert
              (next_pos,next_end) into valid (_pos,_chunk_size) parameters.*/
           OP_ASSERT(next_end<0
-           ||next_end-next_pos>=0&&next_end-next_pos<=0x7FFFFFFF);
+           ||next_end-next_pos>=0&&next_end-next_pos<=OP_INT32_MAX);
           ret=op_http_conn_open_pos(_stream,_conn,next_pos,
            next_end<0?-1:(opus_int32)(next_end-next_pos));
           if(OP_UNLIKELY(ret<0))return OP_EREAD;
@@ -3124,12 +3205,33 @@ static const OpusFileCallbacks OP_HTTP_CALLBACKS={
 };
 #endif
 
+void opus_server_info_init(OpusServerInfo *_info){
+  _info->name=NULL;
+  _info->description=NULL;
+  _info->genre=NULL;
+  _info->url=NULL;
+  _info->server=NULL;
+  _info->content_type=NULL;
+  _info->bitrate_kbps=-1;
+  _info->is_public=-1;
+  _info->is_ssl=0;
+}
+
+void opus_server_info_clear(OpusServerInfo *_info){
+  _ogg_free(_info->content_type);
+  _ogg_free(_info->server);
+  _ogg_free(_info->url);
+  _ogg_free(_info->genre);
+  _ogg_free(_info->description);
+  _ogg_free(_info->name);
+}
+
 /*The actual URL stream creation function.
   This one isn't extensible like the application-level interface, but because
    it isn't public, we're free to change it in the future.*/
 static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
  int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port,
- const char *_proxy_user,const char *_proxy_pass){
+ const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){
   const char *path;
   /*Check to see if this is a valid file: URL.*/
   path=op_parse_file_url(_url);
@@ -3151,7 +3253,7 @@ static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
     if(OP_UNLIKELY(stream==NULL))return NULL;
     op_http_stream_init(stream);
     ret=op_http_stream_open(stream,_url,_skip_certificate_check,
-     _proxy_host,_proxy_port,_proxy_user,_proxy_pass);
+     _proxy_host,_proxy_port,_proxy_user,_proxy_pass,_info);
     if(OP_UNLIKELY(ret<0)){
       op_http_stream_clear(stream);
       _ogg_free(stream);
@@ -3166,22 +3268,25 @@ static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
   (void)_proxy_port;
   (void)_proxy_user;
   (void)_proxy_pass;
+  (void)_info;
   return NULL;
 #endif
 }
 
 void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
  const char *_url,va_list _ap){
-  int         skip_certificate_check;
-  const char *proxy_host;
-  opus_int32  proxy_port;
-  const char *proxy_user;
-  const char *proxy_pass;
+  int             skip_certificate_check;
+  const char     *proxy_host;
+  opus_int32      proxy_port;
+  const char     *proxy_user;
+  const char     *proxy_pass;
+  OpusServerInfo *pinfo;
   skip_certificate_check=0;
   proxy_host=NULL;
   proxy_port=8080;
   proxy_user=NULL;
   proxy_pass=NULL;
+  pinfo=NULL;
   for(;;){
     ptrdiff_t request;
     request=va_arg(_ap,char *)-(char *)NULL;
@@ -3204,12 +3309,27 @@ void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
       case OP_HTTP_PROXY_PASS_REQUEST:{
         proxy_pass=va_arg(_ap,const char *);
       }break;
+      case OP_GET_SERVER_INFO_REQUEST:{
+        pinfo=va_arg(_ap,OpusServerInfo *);
+      }break;
       /*Some unknown option.*/
       default:return NULL;
     }
   }
+  /*If the caller has requested server information, proxy it to a local copy to
+     simplify error handling.*/
+  if(pinfo!=NULL){
+    OpusServerInfo  info;
+    void           *ret;
+    opus_server_info_init(&info);
+    ret=op_url_stream_create_impl(_cb,_url,skip_certificate_check,
+     proxy_host,proxy_port,proxy_user,proxy_pass,&info);
+    if(ret!=NULL)*pinfo=*&info;
+    else opus_server_info_clear(&info);
+    return ret;
+  }
   return op_url_stream_create_impl(_cb,_url,skip_certificate_check,
-   proxy_host,proxy_port,proxy_user,proxy_pass);
+   proxy_host,proxy_port,proxy_user,proxy_pass,NULL);
 }
 
 void *op_url_stream_create(OpusFileCallbacks *_cb,
diff --git a/SP/code/opusfile-0.4/src/info.c b/MP/code/opusfile-0.5/src/info.c
similarity index 82%
rename from SP/code/opusfile-0.4/src/info.c
rename to MP/code/opusfile-0.5/src/info.c
index 6159467..6cf9851 100644
--- a/SP/code/opusfile-0.4/src/info.c
+++ b/MP/code/opusfile-0.5/src/info.c
@@ -90,24 +90,58 @@ void opus_tags_init(OpusTags *_tags){
 }
 
 void opus_tags_clear(OpusTags *_tags){
-  int i;
-  for(i=_tags->comments;i-->0;)_ogg_free(_tags->user_comments[i]);
+  int ci;
+  for(ci=_tags->comments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
   _ogg_free(_tags->user_comments);
   _ogg_free(_tags->comment_lengths);
   _ogg_free(_tags->vendor);
 }
 
+/*Ensure there's room for up to _ncomments comments.*/
+static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
+  char   **user_comments;
+  int     *comment_lengths;
+  size_t   size;
+  if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
+  size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
+  if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
+  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
+  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
+  comment_lengths[_ncomments]=0;
+  _tags->comment_lengths=comment_lengths;
+  size=sizeof(*_tags->user_comments)*(_ncomments+1);
+  if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
+  user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
+  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
+  user_comments[_ncomments]=NULL;
+  _tags->user_comments=user_comments;
+  return 0;
+}
+
+/*Duplicate a (possibly non-NUL terminated) string with a known length.*/
+static char *op_strdup_with_len(const char *_s,size_t _len){
+  size_t  size;
+  char   *ret;
+  size=sizeof(*ret)*(_len+1);
+  if(OP_UNLIKELY(size<_len))return NULL;
+  ret=(char *)_ogg_malloc(size);
+  if(OP_LIKELY(ret!=NULL)){
+    ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len);
+    ret[_len]='\0';
+  }
+  return ret;
+}
+
 /*The actual implementation of opus_tags_parse().
   Unlike the public API, this function requires _tags to already be
    initialized, modifies its contents before success is guaranteed, and assumes
    the caller will clear it on error.*/
-int opus_tags_parse_impl(OpusTags *_tags,
+static int opus_tags_parse_impl(OpusTags *_tags,
  const unsigned char *_data,size_t _len){
   opus_uint32 count;
-  size_t      size;
   size_t      len;
   int         ncomments;
-  int         i;
+  int         ci;
   len=_len;
   if(len<8)return OP_ENOTFORMAT;
   if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT;
@@ -119,14 +153,8 @@ int opus_tags_parse_impl(OpusTags *_tags,
   len-=4;
   if(count>len)return OP_EBADHEADER;
   if(_tags!=NULL){
-    char *vendor;
-    size=count+1;
-    if(size<count)return OP_EFAULT;
-    vendor=(char *)_ogg_malloc(size);
-    if(vendor==NULL)return OP_EFAULT;
-    memcpy(vendor,_data,count);
-    vendor[count]='\0';
-    _tags->vendor=vendor;
+    _tags->vendor=op_strdup_with_len((char *)_data,count);
+    if(_tags->vendor==NULL)return OP_EFAULT;
   }
   _data+=count;
   len-=count;
@@ -139,20 +167,14 @@ int opus_tags_parse_impl(OpusTags *_tags,
   /*Check for overflow (the API limits this to an int).*/
   if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
   if(_tags!=NULL){
-    size=sizeof(*_tags->comment_lengths)*(count+1);
-    if(size/sizeof(*_tags->comment_lengths)!=count+1)return OP_EFAULT;
-    _tags->comment_lengths=(int *)_ogg_malloc(size);
-    size=sizeof(*_tags->user_comments)*(count+1);
-    if(size/sizeof(*_tags->user_comments)!=count+1)return OP_EFAULT;
-    _tags->user_comments=(char **)_ogg_malloc(size);
-    if(_tags->comment_lengths==NULL||_tags->user_comments==NULL){
-      return OP_EFAULT;
-    }
+    int ret;
+    ret=op_tags_ensure_capacity(_tags,count);
+    if(ret<0)return ret;
   }
   ncomments=(int)count;
-  for(i=0;i<ncomments;i++){
+  for(ci=0;ci<ncomments;ci++){
     /*Check to make sure there's minimally sufficient data left in the packet.*/
-    if((size_t)(ncomments-i)>len>>2)return OP_EBADHEADER;
+    if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER;
     count=op_parse_uint32le(_data);
     _data+=4;
     len-=4;
@@ -160,22 +182,14 @@ int opus_tags_parse_impl(OpusTags *_tags,
     /*Check for overflow (the API limits this to an int).*/
     if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
     if(_tags!=NULL){
-      _tags->comment_lengths[i]=(int)count;
-      size=count+1;
-      if(size<count)return OP_EFAULT;
-      _tags->user_comments[i]=(char *)_ogg_malloc(size);
-      if(_tags->user_comments[i]==NULL)return OP_EFAULT;
-      _tags->comments=i+1;
-      memcpy(_tags->user_comments[i],_data,count);
-      _tags->user_comments[i][count]='\0';
+      _tags->user_comments[ci]=op_strdup_with_len((char *)_data,count);
+      if(_tags->user_comments[ci]==NULL)return OP_EFAULT;
+      _tags->comment_lengths[ci]=(int)count;
+      _tags->comments=ci+1;
     }
     _data+=count;
     len-=count;
   }
-  if(_tags!=NULL){
-    _tags->user_comments[ncomments]=NULL;
-    _tags->comment_lengths[ncomments]=0;
-  }
   return 0;
 }
 
@@ -192,23 +206,40 @@ int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){
   else return opus_tags_parse_impl(NULL,_data,_len);
 }
 
-/*Add room for a new comment.*/
-static int op_tags_add_prepare(OpusTags *_tags){
-  char **user_comments;
-  int   *comment_lengths;
-  int    ncomments;
-  ncomments=_tags->comments;
-  user_comments=(char **)_ogg_realloc(_tags->user_comments,
-   sizeof(*_tags->user_comments)*(ncomments+2));
-  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
-  _tags->user_comments=user_comments;
-  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,
-   sizeof(*_tags->comment_lengths)*(ncomments+2));
-  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
-  _tags->comment_lengths=comment_lengths;
-  comment_lengths[ncomments]=comment_lengths[ncomments+1]=0;
-  /*Our caller will always set user_comments[ncomments].*/
-  user_comments[ncomments+1]=NULL;
+/*The actual implementation of opus_tags_copy().
+  Unlike the public API, this function requires _dst to already be
+   initialized, modifies its contents before success is guaranteed, and assumes
+   the caller will clear it on error.*/
+static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){
+  char *vendor;
+  int   ncomments;
+  int   ret;
+  int   ci;
+  vendor=_src->vendor;
+  _dst->vendor=op_strdup_with_len(vendor,strlen(vendor));
+  if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT;
+  ncomments=_src->comments;
+  ret=op_tags_ensure_capacity(_dst,ncomments);
+  if(OP_UNLIKELY(ret<0))return ret;
+  for(ci=0;ci<ncomments;ci++){
+    int len;
+    len=_src->comment_lengths[ci];
+    OP_ASSERT(len>=0);
+    _dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len);
+    if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT;
+    _dst->comment_lengths[ci]=len;
+    _dst->comments=ci+1;
+  }
+  return 0;
+}
+
+int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){
+  OpusTags dst;
+  int      ret;
+  opus_tags_init(&dst);
+  ret=opus_tags_copy_impl(&dst,_src);
+  if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst);
+  else *_dst=*&dst;
   return 0;
 }
 
@@ -218,12 +249,13 @@ int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
   int   value_len;
   int   ncomments;
   int   ret;
-  ret=op_tags_add_prepare(_tags);
+  ncomments=_tags->comments;
+  ret=op_tags_ensure_capacity(_tags,ncomments+1);
   if(OP_UNLIKELY(ret<0))return ret;
   tag_len=strlen(_tag);
   value_len=strlen(_value);
-  ncomments=_tags->comments;
   /*+2 for '=' and '\0'.*/
+  _tags->comment_lengths[ncomments]=0;
   _tags->user_comments[ncomments]=comment=
    (char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
   if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
@@ -235,26 +267,29 @@ int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
 }
 
 int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
-  char *comment;
-  int   ncomments;
-  int   comment_len;
-  int   ret;
-  ret=op_tags_add_prepare(_tags);
-  if(OP_UNLIKELY(ret<0))return ret;
-  comment_len=strlen(_comment);
+  int comment_len;
+  int ncomments;
+  int ret;
   ncomments=_tags->comments;
-  _tags->user_comments[ncomments]=comment=(char *)
-   _ogg_malloc(sizeof(*_tags->user_comments[ncomments])*(comment_len+1));
-  if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
+  ret=op_tags_ensure_capacity(_tags,ncomments+1);
+  if(OP_UNLIKELY(ret<0))return ret;
+  comment_len=(int)strlen(_comment);
+  _tags->comment_lengths[ncomments]=0;
+  _tags->user_comments[ncomments]=op_strdup_with_len(_comment,comment_len);
+  if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
   _tags->comment_lengths[ncomments]=comment_len;
-  memcpy(comment,_comment,sizeof(*comment)*(comment_len+1));
   return 0;
 }
 
-/*Is _a a "tag=value" comment whose tag matches _b?
-  0 if it is, a non-zero value otherwise.*/
-static int op_tagcompare(const char *_a,const char *_b,int _n){
-  return op_strncasecmp(_a,_b,_n)||_a[_n]!='=';
+int opus_tagcompare(const char *_tag_name,const char *_comment){
+  return opus_tagncompare(_tag_name,strlen(_tag_name),_comment);
+}
+
+int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){
+  int ret;
+  OP_ASSERT(_tag_len>=0);
+  ret=op_strncasecmp(_tag_name,_comment,_tag_len);
+  return ret?ret:'='-_comment[_tag_len];
 }
 
 const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
@@ -268,7 +303,7 @@ const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
   user_comments=_tags->user_comments;
   found=0;
   for(ci=0;ci<ncomments;ci++){
-    if(!op_tagcompare(user_comments[ci],_tag,tag_len)){
+    if(!opus_tagncompare(_tag,tag_len,user_comments[ci])){
       /*We return a pointer to the data, not a copy.*/
       if(_count==found++)return user_comments[ci]+tag_len+1;
     }
@@ -288,23 +323,20 @@ int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
   user_comments=_tags->user_comments;
   found=0;
   for(ci=0;ci<ncomments;ci++){
-    if(!op_tagcompare(user_comments[ci],_tag,tag_len))found++;
+    if(!opus_tagncompare(_tag,tag_len,user_comments[ci]))found++;
   }
   return found;
 }
 
 int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){
   char **comments;
-  int   *comment_lengths;
   int    ncomments;
   int    ci;
   comments=_tags->user_comments;
-  comment_lengths=_tags->comment_lengths;
   ncomments=_tags->comments;
   /*Look for the first valid R128_TRACK_GAIN tag and use that.*/
   for(ci=0;ci<ncomments;ci++){
-    if(comment_lengths[ci]>16
-     &&op_strncasecmp(comments[ci],"R128_TRACK_GAIN=",16)==0){
+    if(opus_tagncompare("R128_TRACK_GAIN",15,comments[ci])==0){
       char       *p;
       opus_int32  gain_q8;
       int         negative;
@@ -617,7 +649,7 @@ int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){
   size_t          buf_sz;
   size_t          tag_length;
   int             ret;
-  if(op_strncasecmp(_tag,"METADATA_BLOCK_PICTURE=",23)==0)_tag+=23;
+  if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23;
   /*Figure out how much BASE64-encoded data we have.*/
   tag_length=strlen(_tag);
   if(tag_length&3)return OP_ENOTFORMAT;
diff --git a/MP/code/opusfile-0.4/src/internal.c b/MP/code/opusfile-0.5/src/internal.c
similarity index 100%
rename from MP/code/opusfile-0.4/src/internal.c
rename to MP/code/opusfile-0.5/src/internal.c
diff --git a/MP/code/opusfile-0.4/src/internal.h b/MP/code/opusfile-0.5/src/internal.h
similarity index 96%
rename from MP/code/opusfile-0.4/src/internal.h
rename to MP/code/opusfile-0.5/src/internal.h
index d35d531..0811491 100644
--- a/MP/code/opusfile-0.4/src/internal.h
+++ b/MP/code/opusfile-0.5/src/internal.h
@@ -94,6 +94,8 @@ void op_fatal_impl(const char *_str,const char *_file,int _line);
 
 # define OP_INT64_MAX (2*(((ogg_int64_t)1<<62)-1)|1)
 # define OP_INT64_MIN (-OP_INT64_MAX-1)
+# define OP_INT32_MAX (2*(((ogg_int32_t)1<<30)-1)|1)
+# define OP_INT32_MIN (-OP_INT32_MAX-1)
 
 # define OP_MIN(_a,_b)        ((_a)<(_b)?(_a):(_b))
 # define OP_MAX(_a,_b)        ((_a)>(_b)?(_a):(_b))
@@ -201,6 +203,10 @@ struct OggOpusFile{
   int                op_count;
   /*Central working state for the packet-to-PCM decoder.*/
   OpusMSDecoder     *od;
+  /*The application-provided packet decode callback.*/
+  op_decode_cb_func  decode_cb;
+  /*The application-provided packet decode callback context.*/
+  void              *decode_cb_ctx;
   /*The stream count used to initialize the decoder.*/
   int                od_stream_count;
   /*The coupled stream count used to initialize the decoder.*/
@@ -229,6 +235,7 @@ struct OggOpusFile{
   float              dither_b[OP_NCHANNELS_MAX*4];
   opus_uint32        dither_seed;
   int                dither_mute;
+  int                dither_disabled;
   /*The number of channels represented by the internal state.
     This gets set to 0 whenever anything that would prevent state propagation
      occurs (switching between the float/short APIs, or between the
diff --git a/SP/code/opusfile-0.4/src/opusfile.c b/MP/code/opusfile-0.5/src/opusfile.c
similarity index 92%
rename from SP/code/opusfile-0.4/src/opusfile.c
rename to MP/code/opusfile-0.5/src/opusfile.c
index c02e5a2..392ddb2 100644
--- a/SP/code/opusfile-0.4/src/opusfile.c
+++ b/MP/code/opusfile-0.5/src/opusfile.c
@@ -167,7 +167,7 @@ static int op_seek_helper(OggOpusFile *_of,opus_int64 _offset){
 
 /*Get the current position indicator of the underlying source.
   This should be the same as the value reported by tell().*/
-static opus_int64 op_position(OggOpusFile *_of){
+static opus_int64 op_position(const OggOpusFile *_of){
   /*The current position indicator is _not_ simply offset.
     We may also have unprocessed, buffered data in the sync state.*/
   return _of->offset+_of->oy.fill-_of->oy.returned;
@@ -226,7 +226,7 @@ static opus_int64 op_get_next_page(OggOpusFile *_of,ogg_page *_og,
   return OP_FALSE;
 }
 
-static int op_add_serialno(ogg_page *_og,
+static int op_add_serialno(const ogg_page *_og,
  ogg_uint32_t **_serialnos,int *_nserialnos,int *_cserialnos){
   ogg_uint32_t *serialnos;
   int           nserialnos;
@@ -259,7 +259,7 @@ static int op_lookup_serialno(ogg_uint32_t _s,
   return i<_nserialnos;
 }
 
-static int op_lookup_page_serialno(ogg_page *_og,
+static int op_lookup_page_serialno(const ogg_page *_og,
  const ogg_uint32_t *_serialnos,int _nserialnos){
   return op_lookup_serialno(ogg_page_serialno(_og),_serialnos,_nserialnos);
 }
@@ -1003,7 +1003,7 @@ static int op_find_final_pcm_offset(OggOpusFile *_of,
 
 /*Rescale the number _x from the range [0,_from] to [0,_to].
   _from and _to must be positive.*/
-opus_int64 op_rescale64(opus_int64 _x,opus_int64 _from,opus_int64 _to){
+static opus_int64 op_rescale64(opus_int64 _x,opus_int64 _from,opus_int64 _to){
   opus_int64 frac;
   opus_int64 ret;
   int        i;
@@ -1147,7 +1147,7 @@ static int op_bisect_forward_serialno(OggOpusFile *_of,
        (potentially not a page we care about).*/
     /*Scan the seek records we already have to save us some bisection.*/
     for(sri=0;sri<nsr;sri++){
-      if(op_lookup_serialno(_sr[sri].serialno,*_serialnos,*_nserialnos))break;
+      if(op_lookup_serialno(_sr[sri].serialno,serialnos,nserialnos))break;
     }
     /*Is the last page in our current list of serial numbers?*/
     if(sri<=0)break;
@@ -1333,11 +1333,11 @@ static void op_update_gain(OggOpusFile *_of){
 }
 
 static int op_make_decode_ready(OggOpusFile *_of){
-  OpusHead *head;
-  int       li;
-  int       stream_count;
-  int       coupled_count;
-  int       channel_count;
+  const OpusHead *head;
+  int             li;
+  int             stream_count;
+  int             coupled_count;
+  int             channel_count;
   if(_of->ready_state>OP_STREAMSET)return 0;
   if(OP_UNLIKELY(_of->ready_state<OP_STREAMSET))return OP_EFAULT;
   li=_of->seekable?_of->cur_link:0;
@@ -1519,14 +1519,12 @@ static int op_open1(OggOpusFile *_of,
   seekable=_cb->seek!=NULL&&(*_cb->seek)(_source,0,SEEK_CUR)!=-1;
   /*If seek is implemented, tell must also be implemented.*/
   if(seekable){
+    opus_int64 pos;
     if(OP_UNLIKELY(_of->callbacks.tell==NULL))return OP_EINVAL;
-    else{
-      opus_int64 pos;
-      pos=(*_of->callbacks.tell)(_of->source);
-      /*If the current position is not equal to the initial bytes consumed,
-         absolute seeking will not work.*/
-      if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL;
-    }
+    pos=(*_of->callbacks.tell)(_of->source);
+    /*If the current position is not equal to the initial bytes consumed,
+       absolute seeking will not work.*/
+    if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL;
   }
   _of->seekable=seekable;
   /*Don't seek yet.
@@ -1686,25 +1684,25 @@ void op_free(OggOpusFile *_of){
   }
 }
 
-int op_seekable(OggOpusFile *_of){
+int op_seekable(const OggOpusFile *_of){
   return _of->seekable;
 }
 
-int op_link_count(OggOpusFile *_of){
+int op_link_count(const OggOpusFile *_of){
   return _of->nlinks;
 }
 
-ogg_uint32_t op_serialno(OggOpusFile *_of,int _li){
+ogg_uint32_t op_serialno(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1;
   if(!_of->seekable)_li=0;
   return _of->links[_li<0?_of->cur_link:_li].serialno;
 }
 
-int op_channel_count(OggOpusFile *_of,int _li){
+int op_channel_count(const OggOpusFile *_of,int _li){
   return op_head(_of,_li)->channel_count;
 }
 
-opus_int64 op_raw_total(OggOpusFile *_of,int _li){
+opus_int64 op_raw_total(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED)
    ||OP_UNLIKELY(!_of->seekable)
    ||OP_UNLIKELY(_li>=_of->nlinks)){
@@ -1715,7 +1713,7 @@ opus_int64 op_raw_total(OggOpusFile *_of,int _li){
    -_of->links[_li].offset;
 }
 
-ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li){
+ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li){
   OggOpusLink *links;
   ogg_int64_t  diff;
   int          nlinks;
@@ -1745,13 +1743,13 @@ ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li){
   return diff-links[_li].head.pre_skip;
 }
 
-const OpusHead *op_head(OggOpusFile *_of,int _li){
+const OpusHead *op_head(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1;
   if(!_of->seekable)_li=0;
   return &_of->links[_li<0?_of->cur_link:_li].head;
 }
 
-const OpusTags *op_tags(OggOpusFile *_of,int _li){
+const OpusTags *op_tags(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1;
   if(!_of->seekable){
     if(_of->ready_state<OP_STREAMSET&&_of->ready_state!=OP_PARTOPEN){
@@ -1763,7 +1761,7 @@ const OpusTags *op_tags(OggOpusFile *_of,int _li){
   return &_of->links[_li].tags;
 }
 
-int op_current_link(OggOpusFile *_of){
+int op_current_link(const OggOpusFile *_of){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   return _of->cur_link;
 }
@@ -1774,11 +1772,13 @@ static opus_int32 op_calc_bitrate(opus_int64 _bytes,ogg_int64_t _samples){
   /*These rates are absurd, but let's handle them anyway.*/
   if(OP_UNLIKELY(_bytes>(OP_INT64_MAX-(_samples>>1))/(48000*8))){
     ogg_int64_t den;
-    if(OP_UNLIKELY(_bytes/(0x7FFFFFFFF/(48000*8))>=_samples))return 0x7FFFFFFF;
+    if(OP_UNLIKELY(_bytes/(OP_INT32_MAX/(48000*8))>=_samples)){
+      return OP_INT32_MAX;
+    }
     den=_samples/(48000*8);
     return (opus_int32)((_bytes+(den>>1))/den);
   }
-  if(OP_UNLIKELY(_samples<=0))return 0x7FFFFFFF;
+  if(OP_UNLIKELY(_samples<=0))return OP_INT32_MAX;
   /*This can't actually overflow in normal operation: even with a pre-skip of
      545 2.5 ms frames with 8 streams running at 1282*8+1 bytes per packet
      (1275 byte frames + Opus framing overhead + Ogg lacing values), that all
@@ -1786,10 +1786,11 @@ static opus_int32 op_calc_bitrate(opus_int64 _bytes,ogg_int64_t _samples){
     The only way to get bitrates larger than that is with excessive Opus
      padding, more encoded streams than output channels, or lots and lots of
      Ogg pages with no packets on them.*/
-  return (opus_int32)OP_MIN((_bytes*48000*8+(_samples>>1))/_samples,0x7FFFFFFF);
+  return (opus_int32)OP_MIN((_bytes*48000*8+(_samples>>1))/_samples,
+   OP_INT32_MAX);
 }
 
-opus_int32 op_bitrate(OggOpusFile *_of,int _li){
+opus_int32 op_bitrate(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED)||OP_UNLIKELY(!_of->seekable)
    ||OP_UNLIKELY(_li>=_of->nlinks)){
     return OP_EINVAL;
@@ -1825,11 +1826,8 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
   int           seekable;
   int           cur_link;
   int           ret;
-  if(OP_LIKELY(_of->ready_state>=OP_INITSET)
-   &&OP_LIKELY(_of->op_pos<_of->op_count)){
-    /*We're ready to decode and have at least one packet available already.*/
-    return 1;
-  }
+  /*We shouldn't get here if we have unprocessed packets.*/
+  OP_ASSERT(_of->ready_state<OP_INITSET||_of->op_pos>=_of->op_count);
   if(!_readp)return 0;
   seekable=_of->seekable;
   links=_of->links;
@@ -2084,7 +2082,7 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
 }
 
 int op_raw_seek(OggOpusFile *_of,opus_int64 _pos){
-  int          ret;
+  int ret;
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   /*Don't dump the decoder state if we can't seek.*/
   if(OP_UNLIKELY(!_of->seekable))return OP_ENOSEEK;
@@ -2115,10 +2113,10 @@ int op_raw_seek(OggOpusFile *_of,opus_int64 _pos){
    position in an individual link.*/
 static ogg_int64_t op_get_granulepos(const OggOpusFile *_of,
  ogg_int64_t _pcm_offset,int *_li){
-  OggOpusLink *links;
-  ogg_int64_t  duration;
-  int          nlinks;
-  int          li;
+  const OggOpusLink *links;
+  ogg_int64_t        duration;
+  int                nlinks;
+  int                li;
   OP_ASSERT(_pcm_offset>=0);
   nlinks=_of->nlinks;
   links=_of->links;
@@ -2166,25 +2164,25 @@ static ogg_int64_t op_get_granulepos(const OggOpusFile *_of,
   Account for that (and report it as an error condition).*/
 static int op_pcm_seek_page(OggOpusFile *_of,
  ogg_int64_t _target_gp,int _li){
-  OggOpusLink  *link;
-  ogg_page      og;
-  ogg_int64_t   pcm_pre_skip;
-  ogg_int64_t   pcm_start;
-  ogg_int64_t   pcm_end;
-  ogg_int64_t   best_gp;
-  ogg_int64_t   diff;
-  ogg_uint32_t  serialno;
-  opus_int32    pre_skip;
-  opus_int64    begin;
-  opus_int64    end;
-  opus_int64    boundary;
-  opus_int64    best;
-  opus_int64    page_offset;
-  opus_int64    d0;
-  opus_int64    d1;
-  opus_int64    d2;
-  int           force_bisect;
-  int           ret;
+  const OggOpusLink *link;
+  ogg_page           og;
+  ogg_int64_t        pcm_pre_skip;
+  ogg_int64_t        pcm_start;
+  ogg_int64_t        pcm_end;
+  ogg_int64_t        best_gp;
+  ogg_int64_t        diff;
+  ogg_uint32_t       serialno;
+  opus_int32         pre_skip;
+  opus_int64         begin;
+  opus_int64         end;
+  opus_int64         boundary;
+  opus_int64         best;
+  opus_int64         page_offset;
+  opus_int64         d0;
+  opus_int64         d1;
+  opus_int64         d2;
+  int                force_bisect;
+  int                ret;
   _of->bytes_tracked=0;
   _of->samples_tracked=0;
   link=_of->links+_li;
@@ -2405,16 +2403,16 @@ static int op_pcm_seek_page(OggOpusFile *_of,
 }
 
 int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
-  OggOpusLink *link;
-  ogg_int64_t  pcm_start;
-  ogg_int64_t  target_gp;
-  ogg_int64_t  prev_packet_gp;
-  ogg_int64_t  skip;
-  ogg_int64_t  diff;
-  int          op_count;
-  int          op_pos;
-  int          ret;
-  int          li;
+  const OggOpusLink *link;
+  ogg_int64_t        pcm_start;
+  ogg_int64_t        target_gp;
+  ogg_int64_t        prev_packet_gp;
+  ogg_int64_t        skip;
+  ogg_int64_t        diff;
+  int                op_count;
+  int                op_pos;
+  int                ret;
+  int                li;
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   if(OP_UNLIKELY(!_of->seekable))return OP_ENOSEEK;
   if(OP_UNLIKELY(_pcm_offset<0))return OP_EINVAL;
@@ -2458,7 +2456,7 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
   if(_pcm_offset<=link->head.pre_skip)skip=0;
   else skip=OP_MAX(_pcm_offset-80*48,0);
   OP_ASSERT(_pcm_offset-skip>=0);
-  OP_ASSERT(_pcm_offset-skip<0x7FFFFFFF-120*48);
+  OP_ASSERT(_pcm_offset-skip<OP_INT32_MAX-120*48);
   /*Skip packets until we find one with samples past our skip target.*/
   for(;;){
     op_count=_of->op_count;
@@ -2484,7 +2482,7 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
   /*We skipped too far.
     Either the timestamps were illegal or there was a hole in the data.*/
   if(diff>skip)return OP_EBADLINK;
-  OP_ASSERT(_pcm_offset-diff<0x7FFFFFFF);
+  OP_ASSERT(_pcm_offset-diff<OP_INT32_MAX);
   /*TODO: If there are further holes/illegal timestamps, we still won't decode
      to the correct sample.
     However, at least op_pcm_tell() will report the correct value immediately
@@ -2493,7 +2491,7 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
   return 0;
 }
 
-opus_int64 op_raw_tell(OggOpusFile *_of){
+opus_int64 op_raw_tell(const OggOpusFile *_of){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   return _of->offset;
 }
@@ -2503,10 +2501,10 @@ opus_int64 op_raw_tell(OggOpusFile *_of){
   For unseekable sources, this gets reset to 0 at the beginning of each link.*/
 static ogg_int64_t op_get_pcm_offset(const OggOpusFile *_of,
  ogg_int64_t _gp,int _li){
-  OggOpusLink *links;
-  ogg_int64_t  pcm_offset;
-  ogg_int64_t  delta;
-  int          li;
+  const OggOpusLink *links;
+  ogg_int64_t        pcm_offset;
+  ogg_int64_t        delta;
+  int                li;
   links=_of->links;
   pcm_offset=0;
   OP_ASSERT(_li<_of->nlinks);
@@ -2521,15 +2519,23 @@ static ogg_int64_t op_get_pcm_offset(const OggOpusFile *_of,
     _gp=links[_li].pcm_end;
   }
   if(OP_LIKELY(op_granpos_cmp(_gp,links[_li].pcm_start)>0)){
-    OP_ALWAYS_TRUE(!op_granpos_diff(&delta,_gp,links[_li].pcm_start));
+    if(OP_UNLIKELY(op_granpos_diff(&delta,_gp,links[_li].pcm_start)<0)){
+      /*This means an unseekable stream claimed to have a page from more than
+         2 billion days after we joined.*/
+      OP_ASSERT(!_of->seekable);
+      return OP_INT64_MAX;
+    }
     if(delta<links[_li].head.pre_skip)delta=0;
     else delta-=links[_li].head.pre_skip;
+    /*In the seekable case, _gp was limited by pcm_end.
+      In the unseekable case, pcm_offset should be 0.*/
+    OP_ASSERT(pcm_offset<=OP_INT64_MAX-delta);
     pcm_offset+=delta;
   }
   return pcm_offset;
 }
 
-ogg_int64_t op_pcm_tell(OggOpusFile *_of){
+ogg_int64_t op_pcm_tell(const OggOpusFile *_of){
   ogg_int64_t gp;
   int         nbuffered;
   int         li;
@@ -2545,6 +2551,12 @@ ogg_int64_t op_pcm_tell(OggOpusFile *_of){
   return op_get_pcm_offset(_of,gp,li);
 }
 
+void op_set_decode_callback(OggOpusFile *_of,
+ op_decode_cb_func _decode_cb,void *_ctx){
+  _of->decode_cb=_decode_cb;
+  _of->decode_cb_ctx=_ctx;
+}
+
 int op_set_gain_offset(OggOpusFile *_of,
  int _gain_type,opus_int32 _gain_offset_q8){
   if(_gain_type!=OP_HEADER_GAIN&&_gain_type!=OP_TRACK_GAIN
@@ -2560,15 +2572,22 @@ int op_set_gain_offset(OggOpusFile *_of,
   return 0;
 }
 
+void op_set_dither_enabled(OggOpusFile *_of,int _enabled){
+#if !defined(OP_FIXED_POINT)
+  _of->dither_disabled=!_enabled;
+  if(!_enabled)_of->dither_mute=65;
+#endif
+}
+
 /*Allocate the decoder scratch buffer.
   This is done lazily, since if the user provides large enough buffers, we'll
    never need it.*/
 static int op_init_buffer(OggOpusFile *_of){
   int nchannels_max;
   if(_of->seekable){
-    OggOpusLink *links;
-    int          nlinks;
-    int          li;
+    const OggOpusLink *links;
+    int                nlinks;
+    int                li;
     links=_of->links;
     nlinks=_of->nlinks;
     nchannels_max=1;
@@ -2583,6 +2602,39 @@ static int op_init_buffer(OggOpusFile *_of){
   return 0;
 }
 
+/*Decode a single packet into the target buffer.*/
+static int op_decode(OggOpusFile *_of,op_sample *_pcm,
+ const ogg_packet *_op,int _nsamples,int _nchannels){
+  int ret;
+  /*First we try using the application-provided decode callback.*/
+  if(_of->decode_cb!=NULL){
+#if defined(OP_FIXED_POINT)
+    ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op,
+     _nsamples,_nchannels,OP_DEC_FORMAT_SHORT,_of->cur_link);
+#else
+    ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op,
+     _nsamples,_nchannels,OP_DEC_FORMAT_FLOAT,_of->cur_link);
+#endif
+  }
+  else ret=OP_DEC_USE_DEFAULT;
+  /*If the application didn't want to handle decoding, do it ourselves.*/
+  if(ret==OP_DEC_USE_DEFAULT){
+#if defined(OP_FIXED_POINT)
+    ret=opus_multistream_decode(_of->od,
+     _op->packet,_op->bytes,_pcm,_nsamples,0);
+#else
+    ret=opus_multistream_decode_float(_of->od,
+     _op->packet,_op->bytes,_pcm,_nsamples,0);
+#endif
+    OP_ASSERT(ret<0||ret==_nsamples);
+  }
+  /*If the application returned a positive value other than 0 or
+     OP_DEC_USE_DEFAULT, fail.*/
+  else if(OP_UNLIKELY(ret>0))return OP_EBADPACKET;
+  if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
+  return ret;
+}
+
 /*Read more samples from the stream, using the same API as op_read() or
    op_read_float().*/
 static int op_read_native(OggOpusFile *_of,
@@ -2599,10 +2651,8 @@ static int op_read_native(OggOpusFile *_of,
       od_buffer_pos=_of->od_buffer_pos;
       nsamples=_of->od_buffer_size-od_buffer_pos;
       /*If we have buffered samples, return them.*/
-      if(OP_UNLIKELY(nsamples>0)){
-        if(OP_UNLIKELY(nsamples*nchannels>_buf_size)){
-          nsamples=_buf_size/nchannels;
-        }
+      if(nsamples>0){
+        if(nsamples*nchannels>_buf_size)nsamples=_buf_size/nchannels;
         memcpy(_pcm,_of->od_buffer+nchannels*od_buffer_pos,
          sizeof(*_pcm)*nchannels*nsamples);
         od_buffer_pos+=nsamples;
@@ -2613,11 +2663,11 @@ static int op_read_native(OggOpusFile *_of,
       /*If we have buffered packets, decode one.*/
       op_pos=_of->op_pos;
       if(OP_LIKELY(op_pos<_of->op_count)){
-        ogg_packet  *pop;
-        ogg_int64_t  diff;
-        opus_int32   cur_discard_count;
-        int          duration;
-        int          trimmed_duration;
+        const ogg_packet *pop;
+        ogg_int64_t       diff;
+        opus_int32        cur_discard_count;
+        int               duration;
+        int               trimmed_duration;
         pop=_of->op+op_pos++;
         _of->op_pos=op_pos;
         cur_discard_count=_of->cur_discard_count;
@@ -2646,15 +2696,8 @@ static int op_read_native(OggOpusFile *_of,
             if(OP_UNLIKELY(ret<0))return ret;
             buf=_of->od_buffer;
           }
-#if defined(OP_FIXED_POINT)
-          ret=opus_multistream_decode(_of->od,
-           pop->packet,pop->bytes,buf,120*48,0);
-#else
-          ret=opus_multistream_decode_float(_of->od,
-           pop->packet,pop->bytes,buf,120*48,0);
-#endif
-          if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
-          OP_ASSERT(ret==duration);
+          ret=op_decode(_of,buf,pop,duration,nchannels);
+          if(OP_UNLIKELY(ret<0))return ret;
           /*Perform pre-skip/pre-roll.*/
           od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count);
           cur_discard_count-=od_buffer_pos;
@@ -2665,31 +2708,22 @@ static int op_read_native(OggOpusFile *_of,
              what was decoded.*/
           _of->bytes_tracked+=pop->bytes;
           _of->samples_tracked+=trimmed_duration-od_buffer_pos;
-          /*Don't grab another page yet.*/
-          if(OP_LIKELY(od_buffer_pos<trimmed_duration))continue;
         }
         else{
           /*Otherwise decode directly into the user's buffer.*/
-#if defined(OP_FIXED_POINT)
-          ret=opus_multistream_decode(_of->od,pop->packet,pop->bytes,
-           _pcm,_buf_size/nchannels,0);
-#else
-          ret=opus_multistream_decode_float(_of->od,pop->packet,pop->bytes,
-           _pcm,_buf_size/nchannels,0);
-#endif
-          if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
-          OP_ASSERT(ret==duration);
+          ret=op_decode(_of,_pcm,pop,duration,nchannels);
+          if(OP_UNLIKELY(ret<0))return ret;
           if(OP_LIKELY(trimmed_duration>0)){
             /*Perform pre-skip/pre-roll.*/
             od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count);
             cur_discard_count-=od_buffer_pos;
             _of->cur_discard_count=cur_discard_count;
-            if(OP_UNLIKELY(od_buffer_pos>0)
-             &&OP_LIKELY(od_buffer_pos<trimmed_duration)){
+            trimmed_duration-=od_buffer_pos;
+            if(OP_LIKELY(trimmed_duration>0)
+             &&OP_UNLIKELY(od_buffer_pos>0)){
               memmove(_pcm,_pcm+od_buffer_pos*nchannels,
-               sizeof(*_pcm)*(trimmed_duration-od_buffer_pos)*nchannels);
+               sizeof(*_pcm)*trimmed_duration*nchannels);
             }
-            trimmed_duration-=od_buffer_pos;
             /*Update bitrate tracking based on the actual samples we used from
                what was decoded.*/
             _of->bytes_tracked+=pop->bytes;
@@ -2700,6 +2734,9 @@ static int op_read_native(OggOpusFile *_of,
             }
           }
         }
+        /*Don't grab another page yet.
+          This one might have more packets, or might have buffered data now.*/
+        continue;
       }
     }
     /*Suck in another page.*/
@@ -2712,12 +2749,15 @@ static int op_read_native(OggOpusFile *_of,
   }
 }
 
+/*A generic filter to apply to the decoded audio data.
+  _src is non-const because we will destructively modify the contents of the
+   source buffer that we consume in some cases.*/
 typedef int (*op_read_filter_func)(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_sample *_src,int _nsamples,int _nchannels);
 
 /*Decode some samples and then apply a custom filter to them.
   This is used to convert to different output formats.*/
-static int op_read_native_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
+static int op_filter_read_native(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_read_filter_func _filter,int *_li){
   int ret;
   /*Ensure we have some decoded samples in our buffer.*/
@@ -2741,11 +2781,46 @@ static int op_read_native_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
   return ret;
 }
 
-#if defined(OP_FIXED_POINT)
+#if !defined(OP_FIXED_POINT)||!defined(OP_DISABLE_FLOAT_API)
 
-int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){
-  return op_read_native(_of,_pcm,_buf_size,_li);
-}
+/*Matrices for downmixing from the supported channel counts to stereo.
+  The matrices with 5 or more channels are normalized to a total volume of 2.0,
+   since most mixes sound too quiet if normalized to 1.0 (as there is generally
+   little volume in the side/rear channels).*/
+static const float OP_STEREO_DOWNMIX[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={
+  /*3.0*/
+  {
+    {0.5858F,0.0F},{0.4142F,0.4142F},{0.0F,0.5858F}
+  },
+  /*quadrophonic*/
+  {
+    {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
+  },
+  /*5.0*/
+  {
+    {0.651F,0.0F},{0.46F,0.46F},{0.0F,0.651F},{0.5636F,0.3254F},
+    {0.3254F,0.5636F}
+  },
+  /*5.1*/
+  {
+    {0.529F,0.0F},{0.3741F,0.3741F},{0.0F,0.529F},{0.4582F,0.2645F},
+    {0.2645F,0.4582F},{0.3741F,0.3741F}
+  },
+  /*6.1*/
+  {
+    {0.4553F,0.0F},{0.322F,0.322F},{0.0F,0.4553F},{0.3943F,0.2277F},
+    {0.2277F,0.3943F},{0.2788F,0.2788F},{0.322F,0.322F}
+  },
+  /*7.1*/
+  {
+    {0.3886F,0.0F},{0.2748F,0.2748F},{0.0F,0.3886F},{0.3366F,0.1943F},
+    {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F},{0.2748F,0.2748F}
+  }
+};
+
+#endif
+
+#if defined(OP_FIXED_POINT)
 
 /*Matrices for downmixing from the supported channel counts to stereo.
   The matrices with 5 or more channels are normalized to a total volume of 2.0,
@@ -2783,6 +2858,10 @@ static const opus_int16 OP_STEREO_DOWNMIX_Q14
   }
 };
 
+int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){
+  return op_read_native(_of,_pcm,_buf_size,_li);
+}
+
 static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_sample *_src,int _nsamples,int _nchannels){
   (void)_of;
@@ -2807,6 +2886,7 @@ static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
           l+=OP_STEREO_DOWNMIX_Q14[_nchannels-3][ci][0]*s;
           r+=OP_STEREO_DOWNMIX_Q14[_nchannels-3][ci][1]*s;
         }
+        /*TODO: For 5 or more channels, we should do soft clipping here.*/
         dst[2*i+0]=(opus_int16)OP_CLAMP(-32768,l+8192>>14,32767);
         dst[2*i+1]=(opus_int16)OP_CLAMP(-32768,r+8192>>14,32767);
       }
@@ -2816,7 +2896,7 @@ static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
 }
 
 int op_read_stereo(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size){
-  return op_read_native_filter(_of,_pcm,_buf_size,op_stereo_filter,NULL);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_stereo_filter,NULL);
 }
 
 # if !defined(OP_DISABLE_FLOAT_API)
@@ -2834,33 +2914,51 @@ static int op_short2float_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
 }
 
 int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){
-  return op_read_native_filter(_of,_pcm,_buf_size,op_short2float_filter,_li);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_short2float_filter,_li);
 }
 
 static int op_short2float_stereo_filter(OggOpusFile *_of,
  void *_dst,int _dst_sz,op_sample *_src,int _nsamples,int _nchannels){
   float *dst;
+  int    i;
   dst=(float *)_dst;
   _nsamples=OP_MIN(_nsamples,_dst_sz>>1);
   if(_nchannels==1){
-    int i;
     _nsamples=op_short2float_filter(_of,dst,_nsamples,_src,_nsamples,1);
     for(i=_nsamples;i-->0;)dst[2*i+0]=dst[2*i+1]=dst[i];
-    return _nsamples;
   }
-  /*It would be better to convert to floats and then downmix (so that we don't
-     risk clipping with more than 5 channels), but that would require a large
-     stack buffer, which is probably not a good idea if you're using the
-     fixed-point build.*/
-  if(_nchannels>2){
-    _nsamples=op_stereo_filter(_of,_src,_nsamples*2,
-     _src,_nsamples,_nchannels);
+  else if(_nchannels<5){
+    /*For 3 or 4 channels, we can downmix in fixed point without risk of
+       clipping.*/
+    if(_nchannels>2){
+      _nsamples=op_stereo_filter(_of,_src,_nsamples*2,
+       _src,_nsamples,_nchannels);
+    }
+    return op_short2float_filter(_of,dst,_dst_sz,_src,_nsamples,2);
+  }
+  else{
+    /*For 5 or more channels, we convert to floats and then downmix (so that we
+       don't risk clipping).*/
+    for(i=0;i<_nsamples;i++){
+      float l;
+      float r;
+      int   ci;
+      l=r=0;
+      for(ci=0;ci<_nchannels;ci++){
+        float s;
+        s=(1.0F/32768)*_src[_nchannels*i+ci];
+        l+=OP_STEREO_DOWNMIX[_nchannels-3][ci][0]*s;
+        r+=OP_STEREO_DOWNMIX[_nchannels-3][ci][1]*s;
+      }
+      dst[2*i+0]=l;
+      dst[2*i+1]=r;
+    }
   }
-  return op_short2float_filter(_of,dst,_dst_sz,_src,_nsamples,2);
+  return _nsamples;
 }
 
 int op_read_float_stereo(OggOpusFile *_of,float *_pcm,int _buf_size){
-  return op_read_native_filter(_of,_pcm,_buf_size,
+  return op_filter_read_native(_of,_pcm,_buf_size,
    op_short2float_stereo_filter,NULL);
 }
 
@@ -2916,85 +3014,85 @@ static const float OP_FCOEF_A[4]={
   0.9030F,0.0116F,-0.5853F,-0.2571F
 };
 
-static void op_shaped_dither16(OggOpusFile *_of,opus_int16 *_dst,
+static int op_float2short_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
  float *_src,int _nsamples,int _nchannels){
-  opus_uint32 seed;
-  int         mute;
+  opus_int16 *dst;
   int         ci;
   int         i;
-  mute=_of->dither_mute;
-  seed=_of->dither_seed;
-  if(_of->state_channel_count!=_nchannels){
-    mute=65;
+  dst=(opus_int16 *)_dst;
+  if(OP_UNLIKELY(_nsamples*_nchannels>_dst_sz))_nsamples=_dst_sz/_nchannels;
 # if defined(OP_SOFT_CLIP)
+  if(_of->state_channel_count!=_nchannels){
     for(ci=0;ci<_nchannels;ci++)_of->clip_state[ci]=0;
-# endif
   }
-# if defined(OP_SOFT_CLIP)
   opus_pcm_soft_clip(_src,_nsamples,_nchannels,_of->clip_state);
 # endif
-  /*In order to avoid replacing digital silence with quiet dither noise, we
-     mute if the output has been silent for a while.*/
-  if(mute>64)memset(_of->dither_a,0,sizeof(*_of->dither_a)*4*_nchannels);
-  for(i=0;i<_nsamples;i++){
-    int silent;
-    silent=1;
-    for(ci=0;ci<_nchannels;ci++){
-      float r;
-      float s;
-      float err;
-      int   si;
-      int   j;
-      s=_src[_nchannels*i+ci];
-      silent&=s==0;
-      s*=OP_GAIN;
-      err=0;
-      for(j=0;j<4;j++){
-        err+=OP_FCOEF_B[j]*_of->dither_b[ci*4+j]
-         -OP_FCOEF_A[j]*_of->dither_a[ci*4+j];
-      }
-      for(j=3;j-->0;)_of->dither_a[ci*4+j+1]=_of->dither_a[ci*4+j];
-      for(j=3;j-->0;)_of->dither_b[ci*4+j+1]=_of->dither_b[ci*4+j];
-      _of->dither_a[ci*4]=err;
-      s-=err;
-      if(mute>16)r=0;
-      else{
-        seed=op_rand(seed);
-        r=seed*OP_PRNG_GAIN;
-        seed=op_rand(seed);
-        r-=seed*OP_PRNG_GAIN;
+  if(_of->dither_disabled){
+    for(i=0;i<_nchannels*_nsamples;i++){
+      dst[i]=op_float2int(OP_CLAMP(-32768,32768.0F*_src[i],32767));
+    }
+  }
+  else{
+    opus_uint32 seed;
+    int         mute;
+    seed=_of->dither_seed;
+    mute=_of->dither_mute;
+    if(_of->state_channel_count!=_nchannels)mute=65;
+    /*In order to avoid replacing digital silence with quiet dither noise, we
+       mute if the output has been silent for a while.*/
+    if(mute>64)memset(_of->dither_a,0,sizeof(*_of->dither_a)*4*_nchannels);
+    for(i=0;i<_nsamples;i++){
+      int silent;
+      silent=1;
+      for(ci=0;ci<_nchannels;ci++){
+        float r;
+        float s;
+        float err;
+        int   si;
+        int   j;
+        s=_src[_nchannels*i+ci];
+        silent&=s==0;
+        s*=OP_GAIN;
+        err=0;
+        for(j=0;j<4;j++){
+          err+=OP_FCOEF_B[j]*_of->dither_b[ci*4+j]
+           -OP_FCOEF_A[j]*_of->dither_a[ci*4+j];
+        }
+        for(j=3;j-->0;)_of->dither_a[ci*4+j+1]=_of->dither_a[ci*4+j];
+        for(j=3;j-->0;)_of->dither_b[ci*4+j+1]=_of->dither_b[ci*4+j];
+        _of->dither_a[ci*4]=err;
+        s-=err;
+        if(mute>16)r=0;
+        else{
+          seed=op_rand(seed);
+          r=seed*OP_PRNG_GAIN;
+          seed=op_rand(seed);
+          r-=seed*OP_PRNG_GAIN;
+        }
+        /*Clamp in float out of paranoia that the input will be > 96 dBFS and
+           wrap if the integer is clamped.*/
+        si=op_float2int(OP_CLAMP(-32768,s+r,32767));
+        dst[_nchannels*i+ci]=(opus_int16)si;
+        /*Including clipping in the noise shaping is generally disastrous: the
+           futile effort to restore the clipped energy results in more clipping.
+          However, small amounts---at the level which could normally be created
+           by dither and rounding---are harmless and can even reduce clipping
+           somewhat due to the clipping sometimes reducing the dither + rounding
+           error.*/
+        _of->dither_b[ci*4]=mute>16?0:OP_CLAMP(-1.5F,si-s,1.5F);
       }
-      /*Clamp in float out of paranoia that the input will be > 96 dBFS and
-         wrap if the integer is clamped.*/
-      si=op_float2int(OP_CLAMP(-32768,s+r,32767));
-      _dst[_nchannels*i+ci]=(opus_int16)si;
-      /*Including clipping in the noise shaping is generally disastrous: the
-         futile effort to restore the clipped energy results in more clipping.
-        However, small amounts---at the level which could normally be created
-         by dither and rounding---are harmless and can even reduce clipping
-         somewhat due to the clipping sometimes reducing the dither + rounding
-         error.*/
-      _of->dither_b[ci*4]=mute>16?0:OP_CLAMP(-1.5F,si-s,1.5F);
-    }
-    mute++;
-    if(!silent)mute=0;
-  }
-  _of->dither_mute=OP_MIN(mute,65);
-  _of->dither_seed=seed;
+      mute++;
+      if(!silent)mute=0;
+    }
+    _of->dither_mute=OP_MIN(mute,65);
+    _of->dither_seed=seed;
+  }
   _of->state_channel_count=_nchannels;
-}
-
-static int op_float2short_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
- op_sample *_src,int _nsamples,int _nchannels){
-  opus_int16 *dst;
-  dst=(opus_int16 *)_dst;
-  if(OP_UNLIKELY(_nsamples*_nchannels>_dst_sz))_nsamples=_dst_sz/_nchannels;
-  op_shaped_dither16(_of,dst,_src,_nsamples,_nchannels);
   return _nsamples;
 }
 
 int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){
-  return op_read_native_filter(_of,_pcm,_buf_size,op_float2short_filter,_li);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_float2short_filter,_li);
 }
 
 int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){
@@ -3002,41 +3100,6 @@ int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){
   return op_read_native(_of,_pcm,_buf_size,_li);
 }
 
-/*Matrices for downmixing from the supported channel counts to stereo.
-  The matrices with 5 or more channels are normalized to a total volume of 2.0,
-   since most mixes sound too quiet if normalized to 1.0 (as there is generally
-   little volume in the side/rear channels).*/
-static const float OP_STEREO_DOWNMIX[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={
-  /*3.0*/
-  {
-    {0.5858F,0.0F},{0.4142F,0.4142F},{0.0F,0.5858F}
-  },
-  /*quadrophonic*/
-  {
-    {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
-  },
-  /*5.0*/
-  {
-    {0.651F,0.0F},{0.46F,0.46F},{0.0F,0.651F},{0.5636F,0.3254F},
-    {0.3254F,0.5636F}
-  },
-  /*5.1*/
-  {
-    {0.529F,0.0F},{0.3741F,0.3741F},{0.0F,0.529F},{0.4582F,0.2645F},
-    {0.2645F,0.4582F},{0.3741F,0.3741F}
-  },
-  /*6.1*/
-  {
-    {0.4553F,0.0F},{0.322F,0.322F},{0.0F,0.4553F},{0.3943F,0.2277F},
-    {0.2277F,0.3943F},{0.2788F,0.2788F},{0.322F,0.322F}
-  },
-  /*7.1*/
-  {
-    {0.3886F,0.0F},{0.2748F,0.2748F},{0.0F,0.3886F},{0.3366F,0.1943F},
-    {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F},{0.2748F,0.2748F}
-  }
-};
-
 static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_sample *_src,int _nsamples,int _nchannels){
   (void)_of;
@@ -3071,30 +3134,30 @@ static int op_float2short_stereo_filter(OggOpusFile *_of,
  void *_dst,int _dst_sz,op_sample *_src,int _nsamples,int _nchannels){
   opus_int16 *dst;
   dst=(opus_int16 *)_dst;
-  _nsamples=OP_MIN(_nsamples,_dst_sz>>1);
   if(_nchannels==1){
     int i;
-    op_shaped_dither16(_of,dst,_src,_nsamples,1);
+    _nsamples=op_float2short_filter(_of,dst,_dst_sz>>1,_src,_nsamples,1);
     for(i=_nsamples;i-->0;)dst[2*i+0]=dst[2*i+1]=dst[i];
   }
   else{
     if(_nchannels>2){
+      _nsamples=OP_MIN(_nsamples,_dst_sz>>1);
       _nsamples=op_stereo_filter(_of,_src,_nsamples*2,
        _src,_nsamples,_nchannels);
     }
-    op_shaped_dither16(_of,dst,_src,_nsamples,2);
+    _nsamples=op_float2short_filter(_of,dst,_dst_sz,_src,_nsamples,2);
   }
   return _nsamples;
 }
 
 int op_read_stereo(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size){
-  return op_read_native_filter(_of,_pcm,_buf_size,
+  return op_filter_read_native(_of,_pcm,_buf_size,
    op_float2short_stereo_filter,NULL);
 }
 
 int op_read_float_stereo(OggOpusFile *_of,float *_pcm,int _buf_size){
   _of->state_channel_count=0;
-  return op_read_native_filter(_of,_pcm,_buf_size,op_stereo_filter,NULL);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_stereo_filter,NULL);
 }
 
 #endif
diff --git a/SP/code/opusfile-0.4/src/stream.c b/MP/code/opusfile-0.5/src/stream.c
similarity index 98%
rename from SP/code/opusfile-0.4/src/stream.c
rename to MP/code/opusfile-0.5/src/stream.c
index 8bcdf00..0238a6b 100644
--- a/SP/code/opusfile-0.4/src/stream.c
+++ b/MP/code/opusfile-0.5/src/stream.c
@@ -141,7 +141,7 @@ static wchar_t *op_utf8_to_utf16(const char *_src){
   size_t   len;
   len=strlen(_src);
   /*Worst-case output is 1 wide character per 1 input character.*/
-  dst=(wchar_t *)malloc(sizeof(*dst)*(len+1));
+  dst=(wchar_t *)_ogg_malloc(sizeof(*dst)*(len+1));
   if(dst!=NULL){
     size_t si;
     size_t di;
@@ -162,7 +162,7 @@ static wchar_t *op_utf8_to_utf16(const char *_src){
           if((c0&0xE0)==0xC0){
             wchar_t w;
             /*Start byte says this is a 2-byte sequence.*/
-            w=c0&0x1F<<6|c1&0x3F;
+            w=(c0&0x1F)<<6|c1&0x3F;
             if(w>=0x80U){
               /*This is a 2-byte sequence that is not overlong.*/
               dst[di++]=w;
@@ -218,7 +218,7 @@ static wchar_t *op_utf8_to_utf16(const char *_src){
         }
       }
       /*If we got here, we encountered an illegal UTF-8 sequence.*/
-      free(dst);
+      _ogg_free(dst);
       return NULL;
     }
     OP_ASSERT(di<=len);
@@ -244,8 +244,8 @@ void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
     if(wmode==NULL)errno=EINVAL;
     else if(wpath==NULL)errno=ENOENT;
     else fp=_wfopen(wpath,wmode);
-    free(wmode);
-    free(wpath);
+    _ogg_free(wmode);
+    _ogg_free(wpath);
   }
 #endif
   if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
@@ -275,8 +275,8 @@ void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode,
     if(wmode==NULL)errno=EINVAL;
     else if(wpath==NULL)errno=ENOENT;
     else fp=_wfreopen(wpath,wmode,(FILE *)_stream);
-    free(wmode);
-    free(wpath);
+    _ogg_free(wmode);
+    _ogg_free(wpath);
   }
 #endif
   if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
diff --git a/MP/code/opusfile-0.4/src/wincerts.c b/MP/code/opusfile-0.5/src/wincerts.c
similarity index 100%
rename from MP/code/opusfile-0.4/src/wincerts.c
rename to MP/code/opusfile-0.5/src/wincerts.c
diff --git a/MP/code/opusfile-0.5/src/winerrno.h b/MP/code/opusfile-0.5/src/winerrno.h
new file mode 100644
index 0000000..32a90b4
--- /dev/null
+++ b/MP/code/opusfile-0.5/src/winerrno.h
@@ -0,0 +1,90 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                *
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
+ *                                                                  *
+ ********************************************************************/
+#if !defined(_opusfile_winerrno_h)
+# define _opusfile_winerrno_h (1)
+
+# include <errno.h>
+# include <winerror.h>
+
+/*These conflict with the MSVC errno.h definitions, but we don't need to use
+   the original ones in any file that deals with sockets.
+  We could map the WSA errors to the errno.h ones (most of which are only
+   available on sufficiently new versions of MSVC), but they aren't ordered the
+   same, and given how rarely we actually look at the values, I don't think
+   it's worth a lookup table.*/
+# undef EWOULDBLOCK
+# undef EINPROGRESS
+# undef EALREADY
+# undef ENOTSOCK
+# undef EDESTADDRREQ
+# undef EMSGSIZE
+# undef EPROTOTYPE
+# undef ENOPROTOOPT
+# undef EPROTONOSUPPORT
+# undef EOPNOTSUPP
+# undef EAFNOSUPPORT
+# undef EADDRINUSE
+# undef EADDRNOTAVAIL
+# undef ENETDOWN
+# undef ENETUNREACH
+# undef ENETRESET
+# undef ECONNABORTED
+# undef ECONNRESET
+# undef ENOBUFS
+# undef EISCONN
+# undef ENOTCONN
+# undef ETIMEDOUT
+# undef ECONNREFUSED
+# undef ELOOP
+# undef ENAMETOOLONG
+# undef EHOSTUNREACH
+# undef ENOTEMPTY
+
+# define EWOULDBLOCK     (WSAEWOULDBLOCK-WSABASEERR)
+# define EINPROGRESS     (WSAEINPROGRESS-WSABASEERR)
+# define EALREADY        (WSAEALREADY-WSABASEERR)
+# define ENOTSOCK        (WSAENOTSOCK-WSABASEERR)
+# define EDESTADDRREQ    (WSAEDESTADDRREQ-WSABASEERR)
+# define EMSGSIZE        (WSAEMSGSIZE-WSABASEERR)
+# define EPROTOTYPE      (WSAEPROTOTYPE-WSABASEERR)
+# define ENOPROTOOPT     (WSAENOPROTOOPT-WSABASEERR)
+# define EPROTONOSUPPORT (WSAEPROTONOSUPPORT-WSABASEERR)
+# define ESOCKTNOSUPPORT (WSAESOCKTNOSUPPORT-WSABASEERR)
+# define EOPNOTSUPP      (WSAEOPNOTSUPP-WSABASEERR)
+# define EPFNOSUPPORT    (WSAEPFNOSUPPORT-WSABASEERR)
+# define EAFNOSUPPORT    (WSAEAFNOSUPPORT-WSABASEERR)
+# define EADDRINUSE      (WSAEADDRINUSE-WSABASEERR)
+# define EADDRNOTAVAIL   (WSAEADDRNOTAVAIL-WSABASEERR)
+# define ENETDOWN        (WSAENETDOWN-WSABASEERR)
+# define ENETUNREACH     (WSAENETUNREACH-WSABASEERR)
+# define ENETRESET       (WSAENETRESET-WSABASEERR)
+# define ECONNABORTED    (WSAECONNABORTED-WSABASEERR)
+# define ECONNRESET      (WSAECONNRESET-WSABASEERR)
+# define ENOBUFS         (WSAENOBUFS-WSABASEERR)
+# define EISCONN         (WSAEISCONN-WSABASEERR)
+# define ENOTCONN        (WSAENOTCONN-WSABASEERR)
+# define ESHUTDOWN       (WSAESHUTDOWN-WSABASEERR)
+# define ETOOMANYREFS    (WSAETOOMANYREFS-WSABASEERR)
+# define ETIMEDOUT       (WSAETIMEDOUT-WSABASEERR)
+# define ECONNREFUSED    (WSAECONNREFUSED-WSABASEERR)
+# define ELOOP           (WSAELOOP-WSABASEERR)
+# define ENAMETOOLONG    (WSAENAMETOOLONG-WSABASEERR)
+# define EHOSTDOWN       (WSAEHOSTDOWN-WSABASEERR)
+# define EHOSTUNREACH    (WSAEHOSTUNREACH-WSABASEERR)
+# define ENOTEMPTY       (WSAENOTEMPTY-WSABASEERR)
+# define EPROCLIM        (WSAEPROCLIM-WSABASEERR)
+# define EUSERS          (WSAEUSERS-WSABASEERR)
+# define EDQUOT          (WSAEDQUOT-WSABASEERR)
+# define ESTALE          (WSAESTALE-WSABASEERR)
+# define EREMOTE         (WSAEREMOTE-WSABASEERR)
+
+#endif
diff --git a/SP/Makefile b/SP/Makefile
index 07e38c9..7601be7 100644
--- a/SP/Makefile
+++ b/SP/Makefile
@@ -266,7 +266,7 @@ UIDIR=$(MOUNT_DIR)/ui
 JPDIR=$(MOUNT_DIR)/jpeg-8c
 OGGDIR=$(MOUNT_DIR)/libogg-1.3.1
 OPUSDIR=$(MOUNT_DIR)/opus-1.1
-OPUSFILEDIR=$(MOUNT_DIR)/opusfile-0.4
+OPUSFILEDIR=$(MOUNT_DIR)/opusfile-0.5
 ZDIR=$(MOUNT_DIR)/zlib
 SPLDIR=$(MOUNT_DIR)/splines
 Q3ASMDIR=$(MOUNT_DIR)/tools/asm
diff --git a/MP/code/opusfile-0.4/include/opusfile.h b/SP/code/opusfile-0.5/include/opusfile.h
similarity index 87%
rename from MP/code/opusfile-0.4/include/opusfile.h
rename to SP/code/opusfile-0.5/include/opusfile.h
index 31eeaed..850cd6b 100644
--- a/MP/code/opusfile-0.4/include/opusfile.h
+++ b/SP/code/opusfile-0.5/include/opusfile.h
@@ -71,7 +71,7 @@
     <a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
     mappings</a>.
    A special stereo API can convert everything to 2 channels, making it simple
-    to support multichannel files in a application which only has stereo
+    to support multichannel files in an application which only has stereo
     output.
    Although the <tt>libopusfile</tt> ABI provides support for the theoretical
     maximum number of channels, the current implementation does not support
@@ -90,9 +90,10 @@
     (ignoring any other streams that might be concurrently multiplexed with it,
     such as a video stream).
 
-   The channel count can also change between links, but if your application is
-    not prepared to deal with this, it can use the stereo API to ensure the
-    audio from all links will always get decoded into a common format.
+   The channel count can also change between links.
+   If your application is not prepared to deal with this, it can use the stereo
+    API to ensure the audio from all links will always get decoded into a
+    common format.
    Since <tt>libopusfile</tt> always decodes to 48 kHz, you do not have to
     worry about the sample rate changing between links (as was possible with
     Vorbis).
@@ -108,6 +109,8 @@ extern "C" {
 # include <ogg/ogg.h>
 # include <opus_multistream.h>
 
+/**@cond PRIVATE*/
+
 /*Enable special features for gcc and gcc-compatible compilers.*/
 # if !defined(OP_GNUC_PREREQ)
 #  if defined(__GNUC__)&&defined(__GNUC_MINOR__)
@@ -122,10 +125,12 @@ extern "C" {
 #  pragma GCC visibility push(default)
 # endif
 
-typedef struct OpusHead       OpusHead;
-typedef struct OpusTags       OpusTags;
-typedef struct OpusPictureTag OpusPictureTag;
-typedef struct OggOpusFile    OggOpusFile;
+typedef struct OpusHead          OpusHead;
+typedef struct OpusTags          OpusTags;
+typedef struct OpusPictureTag    OpusPictureTag;
+typedef struct OpusServerInfo    OpusServerInfo;
+typedef struct OpusFileCallbacks OpusFileCallbacks;
+typedef struct OggOpusFile       OggOpusFile;
 
 /*Warning attributes for libopusfile functions.*/
 # if OP_GNUC_PREREQ(3,4)
@@ -139,6 +144,8 @@ typedef struct OggOpusFile    OggOpusFile;
 #  define OP_ARG_NONNULL(_x)
 # endif
 
+/**@endcond*/
+
 /**\defgroup error_codes Error Codes*/
 /*@{*/
 /**\name List of possible error codes
@@ -377,9 +384,9 @@ struct OpusPictureTag{
      <li>#OP_PIC_FORMAT_UNKNOWN,</li>
      <li>#OP_PIC_FORMAT_URL,</li>
      <li>#OP_PIC_FORMAT_JPEG,</li>
-     <li>#OP_PIC_FORMAT_PNG,</li>
-     <li>#OP_PIC_FORMAT_GIF, or</li>
-     </ul>.*/
+     <li>#OP_PIC_FORMAT_PNG, or</li>
+     <li>#OP_PIC_FORMAT_GIF.</li>
+     </ul>*/
   int            format;
 };
 
@@ -445,7 +452,7 @@ ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp)
                       for validity.
    \param[in]  _data The contents of the 'comment' header packet.
    \param      _len  The number of bytes of data in the 'info' header packet.
-   \retval 0             Success.
+   \retval 0              Success.
    \retval #OP_ENOTFORMAT If the data does not start with the "OpusTags"
                            string.
    \retval #OP_EBADHEADER If the contents of the packet otherwise violate the
@@ -454,6 +461,15 @@ ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp)
 OP_WARN_UNUSED_RESULT int opus_tags_parse(OpusTags *_tags,
  const unsigned char *_data,size_t _len) OP_ARG_NONNULL(2);
 
+/**Performs a deep copy of an #OpusTags structure.
+   \param _dst The #OpusTags structure to copy into.
+               If this function fails, the contents of this structure remain
+                untouched.
+   \param _src The #OpusTags structure to copy from.
+   \retval 0          Success.
+   \retval #OP_EFAULT If there wasn't enough memory to copy the tags.*/
+int opus_tags_copy(OpusTags *_dst,const OpusTags *_src) OP_ARG_NONNULL(1);
+
 /**Initializes an #OpusTags structure.
    This should be called on a freshly allocated #OpusTags structure before
     attempting to use it.
@@ -529,7 +545,7 @@ int opus_tags_query_count(const OpusTags *_tags,const char *_tag)
                         On error, no value is returned, and the previous
                          contents remain unchanged.
    \return 0 on success, or a negative value on error.
-   \retval OP_EFALSE There was no track gain available in the given tags.*/
+   \retval #OP_FALSE There was no track gain available in the given tags.*/
 int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8)
  OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
 
@@ -540,6 +556,32 @@ int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8)
    \param _tags The #OpusTags structure to clear.*/
 void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
 
+/**Check if \a _comment is an instance of a \a _tag_name tag.
+   \see opus_tagncompare
+   \param _tag_name A NUL-terminated, case-insensitive, ASCII string containing
+                     the name of the tag to check for (without the terminating
+                     '=' character).
+   \param _comment  The comment string to check.
+   \return An integer less than, equal to, or greater than zero if \a _comment
+            is found respectively, to be less than, to match, or be greater
+            than a "tag=value" string whose tag matches \a _tag_name.*/
+int opus_tagcompare(const char *_tag_name,const char *_comment);
+
+/**Check if \a _comment is an instance of a \a _tag_name tag.
+   This version is slightly more efficient than opus_tagcompare() if the length
+    of the tag name is already known (e.g., because it is a constant).
+   \see opus_tagcompare
+   \param _tag_name A case-insensitive ASCII string containing the name of the
+                     tag to check for (without the terminating '=' character).
+   \param _tag_len  The number of characters in the tag name.
+                    This must be non-negative.
+   \param _comment  The comment string to check.
+   \return An integer less than, equal to, or greater than zero if \a _comment
+            is found respectively, to be less than, to match, or be greater
+            than a "tag=value" string whose tag matches the first \a _tag_len
+            characters of \a _tag_name.*/
+int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment);
+
 /**Parse a single METADATA_BLOCK_PICTURE tag.
    This decodes the BASE64-encoded content of the tag and returns a structure
     with the MIME type, description, image parameters (if known), and the
@@ -568,9 +610,10 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
                      of opus_tags_query().
    \return 0 on success or a negative value on error.
    \retval #OP_ENOTFORMAT The METADATA_BLOCK_PICTURE contents were not valid.
-   \retval #OP_EFAULT     A memory allocation failed.*/
-int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag)
- OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
+   \retval #OP_EFAULT     There was not enough memory to store the picture tag
+                           contents.*/
+OP_WARN_UNUSED_RESULT int opus_picture_tag_parse(OpusPictureTag *_pic,
+ const char *_tag) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
 
 /**Initializes an #OpusPictureTag structure.
    This should be called on a freshly allocated #OpusPictureTag structure
@@ -601,6 +644,8 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
    They may be expanded in the future.*/
 /*@{*/
 
+/**@cond PRIVATE*/
+
 /*These are the raw numbers used to define the request codes.
   They should not be used directly.*/
 #define OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST (6464)
@@ -608,6 +653,7 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
 #define OP_HTTP_PROXY_PORT_REQUEST            (6592)
 #define OP_HTTP_PROXY_USER_REQUEST            (6656)
 #define OP_HTTP_PROXY_PASS_REQUEST            (6720)
+#define OP_GET_SERVER_INFO_REQUEST            (6784)
 
 #define OP_URL_OPT(_request) ((_request)+(char *)0)
 
@@ -615,6 +661,64 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
    provided to one of the URL options.*/
 #define OP_CHECK_INT(_x) ((void)((_x)==(opus_int32)0),(opus_int32)(_x))
 #define OP_CHECK_CONST_CHAR_PTR(_x) ((_x)+((_x)-(const char *)(_x)))
+#define OP_CHECK_SERVER_INFO_PTR(_x) ((_x)+((_x)-(OpusServerInfo *)(_x)))
+
+/**@endcond*/
+
+/**HTTP/Shoutcast/Icecast server information associated with a URL.*/
+struct OpusServerInfo{
+  /**The name of the server (icy-name/ice-name).
+     This is <code>NULL</code> if there was no <code>icy-name</code> or
+      <code>ice-name</code> header.*/
+  char        *name;
+  /**A short description of the server (icy-description/ice-description).
+     This is <code>NULL</code> if there was no <code>icy-description</code> or
+      <code>ice-description</code> header.*/
+  char        *description;
+  /**The genre the server falls under (icy-genre/ice-genre).
+     This is <code>NULL</code> if there was no <code>icy-genre</code> or
+      <code>ice-genre</code> header.*/
+  char        *genre;
+  /**The homepage for the server (icy-url/ice-url).
+     This is <code>NULL</code> if there was no <code>icy-url</code> or
+      <code>ice-url</code> header.*/
+  char        *url;
+  /**The software used by the origin server (Server).
+     This is <code>NULL</code> if there was no <code>Server</code> header.*/
+  char        *server;
+  /**The media type of the entity sent to the recepient (Content-Type).
+     This is <code>NULL</code> if there was no <code>Content-Type</code>
+      header.*/
+  char        *content_type;
+  /**The nominal stream bitrate in kbps (icy-br/ice-bitrate).
+     This is <code>-1</code> if there was no <code>icy-br</code> or
+      <code>ice-bitrate</code> header.*/
+  opus_int32   bitrate_kbps;
+  /**Flag indicating whether the server is public (<code>1</code>) or not
+      (<code>0</code>) (icy-pub/ice-public).
+     This is <code>-1</code> if there was no <code>icy-pub</code> or
+      <code>ice-public</code> header.*/
+  int          is_public;
+  /**Flag indicating whether the server is using HTTPS instead of HTTP.
+     This is <code>0</code> unless HTTPS is being used.
+     This may not match the protocol used in the original URL if there were
+      redirections.*/
+  int          is_ssl;
+};
+
+/**Initializes an #OpusServerInfo structure.
+   All fields are set as if the corresponding header was not available.
+   \param _info The #OpusServerInfo structure to initialize.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.*/
+void opus_server_info_init(OpusServerInfo *_info) OP_ARG_NONNULL(1);
+
+/**Clears the #OpusServerInfo structure.
+   This should be called on an #OpusServerInfo structure after it is no longer
+    needed.
+   It will free all memory used by the structure members.
+   \param _info The #OpusServerInfo structure to clear.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.*/
+void opus_server_info_clear(OpusServerInfo *_info) OP_ARG_NONNULL(1);
 
 /**Skip the certificate check when connecting via TLS/SSL (https).
    \param _b <code>opus_int32</code>: Whether or not to skip the certificate
@@ -660,7 +764,7 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
                                arguments.
    \hideinitializer*/
 #define OP_HTTP_PROXY_USER(_user) \
- OP_URL_OPT(OP_HTTP_PROXY_USER_REQUEST),OP_CHECK_CONST_CHAR_PTR(_host)
+ OP_URL_OPT(OP_HTTP_PROXY_USER_REQUEST),OP_CHECK_CONST_CHAR_PTR(_user)
 
 /**Use the given password for authentication when proxying connections.
    All proxy parameters are ignored for non-http and non-https URLs.
@@ -673,7 +777,28 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
                                arguments.
    \hideinitializer*/
 #define OP_HTTP_PROXY_PASS(_pass) \
- OP_URL_OPT(OP_HTTP_PROXY_PASS_REQUEST),OP_CHECK_CONST_CHAR_PTR(_host)
+ OP_URL_OPT(OP_HTTP_PROXY_PASS_REQUEST),OP_CHECK_CONST_CHAR_PTR(_pass)
+
+/**Parse information about the streaming server (if any) and return it.
+   Very little validation is done.
+   In particular, OpusServerInfo::url may not be a valid URL,
+    OpusServerInfo::bitrate_kbps may not really be in kbps, and
+    OpusServerInfo::content_type may not be a valid MIME type.
+   The character set of the string fields is not specified anywhere, and should
+    not be assumed to be valid UTF-8.
+   \param _info OpusServerInfo *: Returns information about the server.
+                                  If there is any error opening the stream, the
+                                   contents of this structure remain
+                                   unmodified.
+                                  On success, fills in the structure with the
+                                   server information that was available, if
+                                   any.
+                                  After a successful return, the contents of
+                                   this structure should be freed by calling
+                                   opus_server_info_clear().
+   \hideinitializer*/
+#define OP_GET_SERVER_INFO(_info) \
+ OP_URL_OPT(OP_GET_SERVER_INFO_REQUEST),OP_CHECK_SERVER_INFO_PTR(_info)
 
 /*@}*/
 /*@}*/
@@ -690,8 +815,6 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
     block of memory, or URLs.*/
 /*@{*/
 
-typedef struct OpusFileCallbacks OpusFileCallbacks;
-
 /**Reads up to \a _nbytes bytes of data from \a _stream.
    \param      _stream The stream to read from.
    \param[out] _ptr    The buffer to store the data in.
@@ -825,6 +948,7 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
     takes a va_list instead of a variable number of arguments.
    It does not call the <code>va_end</code> macro, and because it invokes the
     <code>va_arg</code> macro, the value of \a _ap is undefined after the call.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param[out]    _cb  The callbacks to use for this stream.
                        If there is an error creating the stream, nothing will
                         be filled in here.
@@ -833,6 +957,10 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
                         schemes are supported.
                        Both <http:> and <https:> may be disabled at compile
                         time, in which case opening such URLs will always fail.
+                       Currently this only supports URIs.
+                       IRIs should be converted to UTF-8 and URL-escaped, with
+                        internationalized domain names encoded in punycode,
+                        before passing them to this function.
    \param[in,out] _ap  A list of the \ref url_options "optional flags" to use.
                        This is a variable-length list of options terminated
                         with <code>NULL</code>.
@@ -841,7 +969,8 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
 OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
  const char *_url,va_list _ap) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
 
-/**Creates a stream that reads from the given URL using the specified proxy.
+/**Creates a stream that reads from the given URL.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param[out] _cb  The callbacks to use for this stream.
                     If there is an error creating the stream, nothing will be
                      filled in here.
@@ -850,6 +979,10 @@ OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
                      are supported.
                     Both <http:> and <https:> may be disabled at compile time,
                      in which case opening such URLs will always fail.
+                    Currently this only supports URIs.
+                    IRIs should be converted to UTF-8 and URL-escaped, with
+                     internationalized domain names encoded in punycode, before
+                     passing them to this function.
    \param      ...  The \ref url_options "optional flags" to use.
                     This is a variable-length list of options terminated with
                      <code>NULL</code>.
@@ -932,12 +1065,17 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_memory(const unsigned char *_data,
     takes a va_list instead of a variable number of arguments.
    It does not call the <code>va_end</code> macro, and because it invokes the
     <code>va_arg</code> macro, the value of \a _ap is undefined after the call.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param         _url   The URL to open.
                          Currently only the <file:>, <http:>, and <https:>
                           schemes are supported.
                          Both <http:> and <https:> may be disabled at compile
                           time, in which case opening such URLs will always
                           fail.
+                         Currently this only supports URIs.
+                         IRIs should be converted to UTF-8 and URL-escaped,
+                          with internationalized domain names encoded in
+                          punycode, before passing them to this function.
    \param[out]    _error Returns 0 on success, or a failure code on error.
                          You may pass in <code>NULL</code> if you don't want
                           the failure code.
@@ -952,13 +1090,16 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_vopen_url(const char *_url,
  int *_error,va_list _ap) OP_ARG_NONNULL(1);
 
 /**Open a stream from a URL.
-   However, this approach will not work for live streams or HTTP/1.0 servers
-    (which do not support Range requets).
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \param      _url   The URL to open.
                       Currently only the <file:>, <http:>, and <https:> schemes
                        are supported.
                       Both <http:> and <https:> may be disabled at compile
                        time, in which case opening such URLs will always fail.
+                      Currently this only supports URIs.
+                      IRIs should be converted to UTF-8 and URL-escaped, with
+                       internationalized domain names encoded in punycode,
+                       before passing them to this function.
    \param[out] _error Returns 0 on success, or a failure code on error.
                       You may pass in <code>NULL</code> if you don't want the
                        failure code.
@@ -1081,6 +1222,7 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_memory(const unsigned char *_data,
     takes a va_list instead of a variable number of arguments.
    It does not call the <code>va_end</code> macro, and because it invokes the
     <code>va_arg</code> macro, the value of \a _ap is undefined after the call.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \see op_test_url
    \see op_test_callbacks
    \param         _url    The URL to open.
@@ -1089,6 +1231,10 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_memory(const unsigned char *_data,
                           Both <http:> and <https:> may be disabled at compile
                            time, in which case opening such URLs will always
                            fail.
+                          Currently this only supports URIs.
+                          IRIs should be converted to UTF-8 and URL-escaped,
+                           with internationalized domain names encoded in
+                           punycode, before passing them to this function.
    \param[out]    _error  Returns 0 on success, or a failure code on error.
                           You may pass in <code>NULL</code> if you don't want
                            the failure code.
@@ -1103,12 +1249,17 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_vtest_url(const char *_url,
  int *_error,va_list _ap) OP_ARG_NONNULL(1);
 
 /**Partially open a stream from a URL.
+   \note If you use this function, you must link against <tt>libopusurl</tt>.
    \see op_test_callbacks
    \param      _url    The URL to open.
                        Currently only the <file:>, <http:>, and <https:>
                         schemes are supported.
                        Both <http:> and <https:> may be disabled at compile
                         time, in which case opening such URLs will always fail.
+                       Currently this only supports URIs.
+                       IRIs should be converted to UTF-8 and URL-escaped, with
+                        internationalized domain names encoded in punycode,
+                        before passing them to this function.
    \param[out] _error  Returns 0 on success, or a failure code on error.
                        You may pass in <code>NULL</code> if you don't want the
                         failure code.
@@ -1255,7 +1406,7 @@ void op_free(OggOpusFile *_of);
    This function may be called on partially-opened streams.
    \param _of The \c OggOpusFile whose seekable status is to be returned.
    \return A non-zero value if seekable, and 0 if unseekable.*/
-int op_seekable(OggOpusFile *_of) OP_ARG_NONNULL(1);
+int op_seekable(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Returns the number of links in this chained stream.
    This function may be called on partially-opened streams, but it will always
@@ -1263,9 +1414,9 @@ int op_seekable(OggOpusFile *_of) OP_ARG_NONNULL(1);
    The actual number of links is not known until the stream is fully opened.
    \param _of The \c OggOpusFile from which to retrieve the link count.
    \return For fully-open seekable sources, this returns the total number of
-            links in the whole stream.
+            links in the whole stream, which will be at least 1.
            For partially-open or unseekable sources, this always returns 1.*/
-int op_link_count(OggOpusFile *_of) OP_ARG_NONNULL(1);
+int op_link_count(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Get the serial number of the given link in a (possibly-chained) Ogg Opus
     stream.
@@ -1280,7 +1431,7 @@ int op_link_count(OggOpusFile *_of) OP_ARG_NONNULL(1);
             the serial number of the last link.
            If the source is not seekable, this always returns the serial number
             of the current link.*/
-opus_uint32 op_serialno(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+opus_uint32 op_serialno(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the channel count of the given link in a (possibly-chained) Ogg Opus
     stream.
@@ -1297,7 +1448,7 @@ opus_uint32 op_serialno(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
             the channel count of the last link.
            If the source is not seekable, this always returns the channel count
             of the current link.*/
-int op_channel_count(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+int op_channel_count(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the total (compressed) size of the stream, or of an individual link in
     a (possibly-chained) Ogg Opus stream, including all headers and Ogg muxing
@@ -1315,7 +1466,7 @@ int op_channel_count(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
    \retval #OP_EINVAL The source is not seekable (so we can't know the length),
                        \a _li wasn't less than the total number of links in
                        the stream, or the stream was only partially open.*/
-opus_int64 op_raw_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+opus_int64 op_raw_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the total PCM length (number of samples at 48 kHz) of the stream, or of
     an individual link in a (possibly-chained) Ogg Opus stream.
@@ -1333,7 +1484,7 @@ opus_int64 op_raw_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
    \retval #OP_EINVAL The source is not seekable (so we can't know the length),
                        \a _li wasn't less than the total number of links in
                        the stream, or the stream was only partially open.*/
-ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the ID header information for the given link in a (possibly chained) Ogg
     Opus stream.
@@ -1349,7 +1500,7 @@ ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
                information for the current link is always returned, if
                available.
    \return The contents of the ID header for the given link.*/
-const OpusHead *op_head(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+const OpusHead *op_head(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Get the comment header information for the given link in a (possibly
     chained) Ogg Opus stream.
@@ -1367,7 +1518,7 @@ const OpusHead *op_head(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
    \return The contents of the comment header for the given link, or
             <code>NULL</code> if this is an unseekable stream that encountered
             an invalid link.*/
-const OpusTags *op_tags(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+const OpusTags *op_tags(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Retrieve the index of the current link.
    This is the link that produced the data most recently read by
@@ -1384,7 +1535,7 @@ const OpusTags *op_tags(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
             each time a new link is encountered (even though op_link_count()
             always returns 1).
    \retval #OP_EINVAL The stream was only partially open.*/
-int op_current_link(OggOpusFile *_of) OP_ARG_NONNULL(1);
+int op_current_link(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Computes the bitrate for a given link in a (possibly chained) Ogg Opus
     stream.
@@ -1397,7 +1548,7 @@ int op_current_link(OggOpusFile *_of) OP_ARG_NONNULL(1);
    \retval #OP_EINVAL The stream was only partially open, the stream was not
                        seekable, or \a _li was larger than the number of
                        links.*/
-opus_int32 op_bitrate(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
+opus_int32 op_bitrate(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
 
 /**Compute the instantaneous bitrate, measured as the ratio of bits to playable
     samples decoded since a) the last call to op_bitrate_instant(), b) the last
@@ -1416,7 +1567,7 @@ opus_int32 op_bitrate_instant(OggOpusFile *_of) OP_ARG_NONNULL(1);
    \param _of The \c OggOpusFile from which to retrieve the position indicator.
    \return The byte position that is currently being read from.
    \retval #OP_EINVAL The stream was only partially open.*/
-opus_int64 op_raw_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
+opus_int64 op_raw_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /**Obtain the PCM offset of the next sample to be read.
    If the stream is not properly timestamped, this might not increment by the
@@ -1425,7 +1576,7 @@ opus_int64 op_raw_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
    \param _of The \c OggOpusFile from which to retrieve the PCM offset.
    \return The PCM offset of the next sample to be read.
    \retval #OP_EINVAL The stream was only partially open.*/
-ogg_int64_t op_pcm_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
+ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
 
 /*@}*/
 /*@}*/
@@ -1526,13 +1677,73 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
     not check the error return code from op_read_float() or its associated
     functions.
    If the remote peer does not close the connection gracefully (with a TLS
-    "close notify" message), these functions will return OP_EREAD instead of 0
+    "close notify" message), these functions will return #OP_EREAD instead of 0
     when they reach the end of the file.
    If you are reading from an <https:> URL (particularly if seeking is not
     supported), you should make sure to check for this error and warn the user
     appropriately.*/
 /*@{*/
 
+/**Indicates that the decoding callback should produce signed 16-bit
+    native-endian output samples.*/
+#define OP_DEC_FORMAT_SHORT (7008)
+/**Indicates that the decoding callback should produce 32-bit native-endian
+    float samples.*/
+#define OP_DEC_FORMAT_FLOAT (7040)
+
+/**Indicates that the decoding callback did not decode anything, and that
+    <tt>libopusfile</tt> should decode normally instead.*/
+#define OP_DEC_USE_DEFAULT  (6720)
+
+/**Called to decode an Opus packet.
+   This should invoke the functional equivalent of opus_multistream_decode() or
+    opus_multistream_decode_float(), except that it returns 0 on success
+    instead of the number of decoded samples (which is known a priori).
+   \param _ctx       The application-provided callback context.
+   \param _decoder   The decoder to use to decode the packet.
+   \param[out] _pcm  The buffer to decode into.
+                     This will always have enough room for \a _nchannels of
+                      \a _nsamples samples, which should be placed into this
+                      buffer interleaved.
+   \param _op        The packet to decode.
+                     This will always have its granule position set to a valid
+                      value.
+   \param _nsamples  The number of samples expected from the packet.
+   \param _nchannels The number of channels expected from the packet.
+   \param _format    The desired sample output format.
+                     This is either #OP_DEC_FORMAT_SHORT or
+                      #OP_DEC_FORMAT_FLOAT.
+   \param _li        The index of the link from which this packet was decoded.
+   \return A non-negative value on success, or a negative value on error.
+           The error codes should be the same as those returned by
+            opus_multistream_decode() or opus_multistream_decode_float().
+   \retval 0                   Decoding was successful.
+                               The application has filled the buffer with
+                                exactly <code>\a _nsamples*\a
+                                _nchannels</code> samples in the requested
+                                format.
+   \retval #OP_DEC_USE_DEFAULT No decoding was done.
+                               <tt>libopusfile</tt> should decode normally
+                                instead.*/
+typedef int (*op_decode_cb_func)(void *_ctx,OpusMSDecoder *_decoder,void *_pcm,
+ const ogg_packet *_op,int _nsamples,int _nchannels,int _format,int _li);
+
+/**Sets the packet decode callback function.
+   This is called once for each packet that needs to be decoded.
+   A call to this function is no guarantee that the audio will eventually be
+    delivered to the application.
+   Some or all of the data from the packet may be discarded (i.e., at the
+    beginning or end of a link, or after a seek), however the callback is
+    required to provide all of it.
+   \param _of        The \c OggOpusFile on which to set the decode callback.
+   \param _decode_cb The callback function to call.
+                     This may be <code>NULL</code> to disable calling the
+                      callback.
+   \param _ctx       The application-provided context pointer to pass to the
+                      callback on each call.*/
+void op_set_decode_callback(OggOpusFile *_of,
+ op_decode_cb_func _decode_cb,void *_ctx) OP_ARG_NONNULL(1);
+
 /**Gain offset type that indicates that the provided offset is relative to the
     header gain.
    This is the default.*/
@@ -1564,7 +1775,19 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
    \return 0 on success or a negative value on error.
    \retval #OP_EINVAL The \a _gain_type was unrecognized.*/
 int op_set_gain_offset(OggOpusFile *_of,
- int _gain_type,opus_int32 _gain_offset_q8);
+ int _gain_type,opus_int32 _gain_offset_q8) OP_ARG_NONNULL(1);
+
+/**Sets whether or not dithering is enabled for 16-bit decoding.
+   By default, when <tt>libopusfile</tt> is compiled to use floating-point
+    internally, calling op_read() or op_read_stereo() will first decode to
+    float, and then convert to fixed-point using noise-shaping dithering.
+   This flag can be used to disable that dithering.
+   When the application uses op_read_float() or op_read_float_stereo(), or when
+    the library has been compiled to decode directly to fixed point, this flag
+    has no effect.
+   \param _of      The \c OggOpusFile on which to enable or disable dithering.
+   \param _enabled A non-zero value to enable dithering, or 0 to disable it.*/
+void op_set_dither_enabled(OggOpusFile *_of,int _enabled) OP_ARG_NONNULL(1);
 
 /**Reads more samples from the stream.
    \note Although \a _buf_size must indicate the total number of values that
diff --git a/MP/code/opusfile-0.4/src/http.c b/SP/code/opusfile-0.5/src/http.c
similarity index 92%
rename from MP/code/opusfile-0.4/src/http.c
rename to SP/code/opusfile-0.5/src/http.c
index d172578..4a9eaf5 100644
--- a/MP/code/opusfile-0.4/src/http.c
+++ b/SP/code/opusfile-0.5/src/http.c
@@ -44,7 +44,8 @@
   RFC 6066: Transport Layer Security (TLS) Extensions: Extension Definitions
   RFC 6125: Representation and Verification of Domain-Based Application Service
    Identity within Internet Public Key Infrastructure Using X.509 (PKIX)
-   Certificates in the Context of Transport Layer Security (TLS)*/
+   Certificates in the Context of Transport Layer Security (TLS)
+  RFC 6555: Happy Eyeballs: Success with Dual-Stack Hosts*/
 
 typedef struct OpusParsedURL   OpusParsedURL;
 typedef struct OpusStringBuf   OpusStringBuf;
@@ -60,7 +61,7 @@ static char *op_string_range_dup(const char *_start,const char *_end){
   if(OP_UNLIKELY(len>=INT_MAX))return NULL;
   ret=(char *)_ogg_malloc(sizeof(*ret)*(len+1));
   if(OP_LIKELY(ret!=NULL)){
-    memcpy(ret,_start,sizeof(*ret)*(len));
+    ret=(char *)memcpy(ret,_start,sizeof(*ret)*(len));
     ret[len]='\0';
   }
   return ret;
@@ -358,6 +359,11 @@ typedef int op_sock;
    when seeking, and time out rapidly.*/
 # define OP_NCONNS_MAX (4)
 
+/*The amount of time before we attempt to re-resolve the host.
+  This is 10 minutes, as recommended in RFC 6555 for expiring cached connection
+   results for dual-stack hosts.*/
+# define OP_RESOLVE_CACHE_TIMEOUT_MS (10*60*(opus_int32)1000)
+
 /*The number of redirections at which we give up.
   The value here is the current default in Firefox.
   RFC 2068 mandated a maximum of 5, but RFC 2616 relaxed that to "a client
@@ -616,6 +622,8 @@ static void op_sb_clear(OpusStringBuf *_sb){
   _ogg_free(_sb->buf);
 }
 
+/*Make sure we have room for at least _capacity characters (plus 1 more for the
+   terminating NUL).*/
 static int op_sb_ensure_capacity(OpusStringBuf *_sb,int _capacity){
   char *buf;
   int   cbuf;
@@ -633,6 +641,8 @@ static int op_sb_ensure_capacity(OpusStringBuf *_sb,int _capacity){
   return 0;
 }
 
+/*Increase the capacity of the buffer, but not to more than _max_size
+   characters (plus 1 more for the terminating NUL).*/
 static int op_sb_grow(OpusStringBuf *_sb,int _max_size){
   char *buf;
   int   cbuf;
@@ -827,6 +837,8 @@ struct OpusHTTPStream{
     struct sockaddr_in  v4;
     struct sockaddr_in6 v6;
   }                addr;
+  /*The last time we re-resolved the host.*/
+  struct timeb     resolve_time;
   /*A buffer used to build HTTP requests.*/
   OpusStringBuf    request;
   /*A buffer used to build proxy CONNECT requests.*/
@@ -838,6 +850,10 @@ struct OpusHTTPStream{
   opus_int64       content_length;
   /*The position indicator used when no connection is active.*/
   opus_int64       pos;
+  /*The host we actually connected to.*/
+  char            *connect_host;
+  /*The port we actually connected to.*/
+  unsigned         connect_port;
   /*The connection we're currently reading from.
     This can be -1 if no connection is active.*/
   int              cur_conni;
@@ -871,6 +887,7 @@ static void op_http_stream_init(OpusHTTPStream *_stream){
   op_sb_init(&_stream->request);
   op_sb_init(&_stream->proxy_connect);
   op_sb_init(&_stream->response);
+  _stream->connect_host=NULL;
   _stream->seekable=0;
 }
 
@@ -910,6 +927,7 @@ static void op_http_stream_clear(OpusHTTPStream *_stream){
   op_sb_clear(&_stream->response);
   op_sb_clear(&_stream->proxy_connect);
   op_sb_clear(&_stream->request);
+  if(_stream->connect_host!=_stream->url.host)_ogg_free(_stream->connect_host);
   op_parsed_url_clear(&_stream->url);
 }
 
@@ -977,11 +995,11 @@ static int op_http_conn_estimate_available(OpusHTTPConn *_conn){
 static opus_int32 op_time_diff_ms(const struct timeb *_end,
  const struct timeb *_start){
   opus_int64 dtime;
-  dtime=_end->time-_start->time;
+  dtime=_end->time-(opus_int64)_start->time;
   OP_ASSERT(_end->millitm<1000);
   OP_ASSERT(_start->millitm<1000);
-  if(OP_UNLIKELY(dtime>(0x7FFFFFFF-1000)/1000))return 0x7FFFFFFF;
-  if(OP_UNLIKELY(dtime<(-0x7FFFFFFF+999)/1000))return -0x7FFFFFFF-1;
+  if(OP_UNLIKELY(dtime>(OP_INT32_MAX-1000)/1000))return OP_INT32_MAX;
+  if(OP_UNLIKELY(dtime<(OP_INT32_MIN+1000)/1000))return OP_INT32_MIN;
   return (opus_int32)dtime*1000+_end->millitm-_start->millitm;
 }
 
@@ -1527,7 +1545,7 @@ static BIO_METHOD op_bio_retry_method={
 
 /*Establish a CONNECT tunnel and pipeline the start of the TLS handshake for
    proxying https URL requests.*/
-int op_http_conn_establish_tunnel(OpusHTTPStream *_stream,
+static int op_http_conn_establish_tunnel(OpusHTTPStream *_stream,
  OpusHTTPConn *_conn,op_sock _fd,SSL *_ssl_conn,BIO *_ssl_bio){
   BIO  *retry_bio;
   char *status_code;
@@ -1796,7 +1814,7 @@ static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){
 }
 
 /*Perform the TLS handshake on a new connection.*/
-int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
+static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
  op_sock _fd,SSL *_ssl_conn){
   SSL_SESSION *ssl_session;
   BIO         *ssl_bio;
@@ -1862,9 +1880,9 @@ int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
                     left to try.
                     *_addr will be set to NULL in this case.*/
 static int op_sock_connect_next(op_sock _fd,
- struct addrinfo **_addr,int _ai_family){
-  struct addrinfo *addr;
-  int err;
+ const struct addrinfo **_addr,int _ai_family){
+  const struct addrinfo *addr;
+  int                    err;
   addr=*_addr;
   for(;;){
     /*Move to the next address of the requested type.*/
@@ -1883,36 +1901,30 @@ static int op_sock_connect_next(op_sock _fd,
 /*The number of address families to try connecting to simultaneously.*/
 # define OP_NPROTOS (2)
 
-static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
- struct addrinfo *_addrs,struct timeb *_start_time){
-  struct addrinfo *addr;
-  struct addrinfo *addrs[OP_NPROTOS];
-  struct pollfd    fds[OP_NPROTOS];
-  int              ai_family;
-  int              nprotos;
-  int              ret;
-  int              pi;
-  int              pj;
+static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
+ const struct addrinfo *_addrs,struct timeb *_start_time){
+  const struct addrinfo *addr;
+  const struct addrinfo *addrs[OP_NPROTOS];
+  struct pollfd          fds[OP_NPROTOS];
+  int                    ai_family;
+  int                    nprotos;
+  int                    ret;
+  int                    pi;
+  int                    pj;
   for(pi=0;pi<OP_NPROTOS;pi++)addrs[pi]=NULL;
-  addr=_addrs;
   /*Try connecting via both IPv4 and IPv6 simultaneously, and keep the first
-     one that succeeds.*/
-  for(;addr!=NULL;addr=addr->ai_next){
-    /*Give IPv6 a slight edge by putting it first in the list.*/
-    if(addr->ai_family==AF_INET6){
+     one that succeeds.
+    Start by finding the first address from each family.
+    We order the first connection attempts in the same order the address
+     families were returned in the DNS records in accordance with RFC 6555.*/
+  for(addr=_addrs,nprotos=0;addr!=NULL&&nprotos<OP_NPROTOS;addr=addr->ai_next){
+    if(addr->ai_family==AF_INET6||addr->ai_family==AF_INET){
       OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in6));
-      if(addrs[0]==NULL)addrs[0]=addr;
-    }
-    else if(addr->ai_family==AF_INET){
       OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in));
-      if(addrs[1]==NULL)addrs[1]=addr;
-    }
-  }
-  /*Consolidate the list of addresses.*/
-  for(pi=nprotos=0;pi<OP_NPROTOS;pi++){
-    if(addrs[pi]!=NULL){
-      addrs[nprotos]=addrs[pi];
-      nprotos++;
+      /*If we've seen this address family before, skip this address for now.*/
+      for(pi=0;pi<nprotos;pi++)if(addrs[pi]->ai_family==addr->ai_family)break;
+      if(pi<nprotos)continue;
+      addrs[nprotos++]=addr;
     }
   }
   /*Pop the connection off the free list and put it on the LRU list.*/
@@ -1924,7 +1936,12 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
   *&_conn->read_time=*_start_time;
   _conn->read_bytes=0;
   _conn->read_rate=0;
-  /*Try to start a connection to each protocol.*/
+  /*Try to start a connection to each protocol.
+    RFC 6555 says it is RECOMMENDED that connection attempts be paced
+     150...250 ms apart "to balance human factors against network load", but
+     that "stateful algorithms" (that's us) "are expected to be more
+     aggressive".
+    We are definitely more aggressive: we don't pace at all.*/
   for(pi=0;pi<nprotos;pi++){
     ai_family=addrs[pi]->ai_family;
     fds[pi].fd=socket(ai_family,SOCK_STREAM,addrs[pi]->ai_protocol);
@@ -2016,6 +2033,29 @@ static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
   return 0;
 }
 
+static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
+ const struct addrinfo *_addrs,struct timeb *_start_time){
+  struct timeb     resolve_time;
+  struct addrinfo *new_addrs;
+  int              ret;
+  /*Re-resolve the host if we need to (RFC 6555 says we MUST do so
+     occasionally).*/
+  new_addrs=NULL;
+  ftime(&resolve_time);
+  if(_addrs!=&_stream->addr_info||op_time_diff_ms(&resolve_time,
+   &_stream->resolve_time)>=OP_RESOLVE_CACHE_TIMEOUT_MS){
+    new_addrs=op_resolve(_stream->connect_host,_stream->connect_port);
+    if(OP_LIKELY(new_addrs!=NULL)){
+      _addrs=new_addrs;
+      *&_stream->resolve_time=*&resolve_time;
+    }
+    else if(OP_LIKELY(_addrs==NULL))return OP_FALSE;
+  }
+  ret=op_http_connect_impl(_stream,_conn,_addrs,_start_time);
+  if(new_addrs!=NULL)freeaddrinfo(new_addrs);
+  return ret;
+}
+
 # define OP_BASE64_LENGTH(_len) (((_len)+2)/3*4)
 
 static const char BASE64_TABLE[64]={
@@ -2130,53 +2170,33 @@ static int op_http_allow_pipelining(const char *_server){
 
 static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
  int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port,
- const char *_proxy_user,const char *_proxy_pass){
+ const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){
   struct addrinfo *addrs;
-  const char      *last_host;
-  unsigned         last_port;
   int              nredirs;
   int              ret;
-  if(_proxy_host!=NULL&&OP_UNLIKELY(_proxy_port>65535U))return OP_EINVAL;
-  last_host=NULL;
-  /*We shouldn't have to initialize last_port, but gcc is too dumb to figure
-     out that last_host!=NULL implies we've already taken one trip through the
-     loop.*/
-  last_port=0;
 #if defined(_WIN32)
   op_init_winsock();
 #endif
   ret=op_parse_url(&_stream->url,_url);
   if(OP_UNLIKELY(ret<0))return ret;
+  if(_proxy_host!=NULL){
+    if(OP_UNLIKELY(_proxy_port>65535U))return OP_EINVAL;
+    _stream->connect_host=op_string_dup(_proxy_host);
+    _stream->connect_port=_proxy_port;
+  }
+  else{
+    _stream->connect_host=_stream->url.host;
+    _stream->connect_port=_stream->url.port;
+  }
+  addrs=NULL;
   for(nredirs=0;nredirs<OP_REDIRECT_LIMIT;nredirs++){
-    struct timeb  start_time;
-    struct timeb  end_time;
-    char         *next;
-    char         *status_code;
-    const char   *host;
-    unsigned      port;
-    int           minor_version_pos;
-    int           v1_1_compat;
-    if(_proxy_host==NULL){
-      host=_stream->url.host;
-      port=_stream->url.port;
-    }
-    else{
-      host=_proxy_host;
-      port=_proxy_port;
-    }
-    /*If connecting to the same place as last time, don't re-resolve it.*/
-    addrs=NULL;
-    if(last_host!=NULL){
-      if(strcmp(last_host,host)==0&&last_port==port)addrs=&_stream->addr_info;
-      else if(_stream->ssl_session!=NULL){
-        /*Forget any cached SSL session from the last host.*/
-        SSL_SESSION_free(_stream->ssl_session);
-        _stream->ssl_session=NULL;
-      }
-      if(last_host!=_proxy_host)_ogg_free((void *)last_host);
-    }
-    last_host=host;
-    last_port=port;
+    OpusParsedURL  next_url;
+    struct timeb   start_time;
+    struct timeb   end_time;
+    char          *next;
+    char          *status_code;
+    int            minor_version_pos;
+    int            v1_1_compat;
     /*Initialize the SSL library if necessary.*/
     if(OP_URL_IS_SSL(&_stream->url)&&_stream->ssl_ctx==NULL){
       SSL_CTX *ssl_ctx;
@@ -2212,6 +2232,7 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
       if(_proxy_host!=NULL){
         /*We need to establish a CONNECT tunnel to handle https proxying.
           Build the request we'll send to do so.*/
+        _stream->proxy_connect.nbuf=0;
         ret=op_sb_append(&_stream->proxy_connect,"CONNECT ",8);
         ret|=op_sb_append_string(&_stream->proxy_connect,_stream->url.host);
         ret|=op_sb_append_port(&_stream->proxy_connect,_stream->url.port);
@@ -2239,12 +2260,7 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
       }
     }
     /*Actually make the connection.*/
-    if(addrs!=&_stream->addr_info){
-      addrs=op_resolve(host,port);
-      if(OP_UNLIKELY(addrs==NULL))return OP_FALSE;
-    }
     ret=op_http_connect(_stream,_stream->conns+0,addrs,&start_time);
-    if(addrs!=&_stream->addr_info)freeaddrinfo(addrs);
     if(OP_UNLIKELY(ret<0))return ret;
     /*Build the request to send.*/
     _stream->request.nbuf=0;
@@ -2312,13 +2328,15 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
     if(status_code[0]=='2'){
       opus_int64 content_length;
       opus_int64 range_length;
-      int        pipeline;
+      int        pipeline_supported;
+      int        pipeline_disabled;
       /*We only understand 20x codes.*/
       if(status_code[1]!='0')return OP_FALSE;
       content_length=-1;
       range_length=-1;
-      /*Pipelining is disabled by default.*/
-      pipeline=0;
+      /*Pipelining must be explicitly enabled.*/
+      pipeline_supported=0;
+      pipeline_disabled=0;
       for(;;){
         char *header;
         char *cdr;
@@ -2376,9 +2394,9 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
              error out and reconnect, adding lots of latency.*/
           ret=op_http_parse_connection(cdr);
           if(OP_UNLIKELY(ret<0))return ret;
-          pipeline-=ret;
+          pipeline_disabled|=ret;
         }
-        else if(strcmp(header,"server")){
+        else if(strcmp(header,"server")==0){
           /*If we got a Server response header, and it wasn't from a known-bad
              server, enable pipelining, as long as it's at least HTTP/1.1.
             According to RFC 2145, the server is supposed to respond with the
@@ -2386,7 +2404,51 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
              suspected that we incorrectly implement the HTTP specification.
             So it should send back at least HTTP/1.1, despite our HTTP/1.0
              request.*/
-          pipeline+=v1_1_compat&&op_http_allow_pipelining(cdr);
+          pipeline_supported=v1_1_compat;
+          if(v1_1_compat)pipeline_disabled|=!op_http_allow_pipelining(cdr);
+          if(_info!=NULL&&_info->server==NULL)_info->server=op_string_dup(cdr);
+        }
+        /*Collect station information headers if the caller requested it.
+          If there's more than one copy of a header, the first one wins.*/
+        else if(_info!=NULL){
+          if(strcmp(header,"content-type")==0){
+            if(_info->content_type==NULL){
+              _info->content_type=op_string_dup(cdr);
+            }
+          }
+          else if(header[0]=='i'&&header[1]=='c'
+           &&(header[2]=='e'||header[2]=='y')&&header[3]=='-'){
+            if(strcmp(header+4,"name")==0){
+              if(_info->name==NULL)_info->name=op_string_dup(cdr);
+            }
+            else if(strcmp(header+4,"description")==0){
+              if(_info->description==NULL)_info->description=op_string_dup(cdr);
+            }
+            else if(strcmp(header+4,"genre")==0){
+              if(_info->genre==NULL)_info->genre=op_string_dup(cdr);
+            }
+            else if(strcmp(header+4,"url")==0){
+              if(_info->url==NULL)_info->url=op_string_dup(cdr);
+            }
+            else if(strcmp(header,"icy-br")==0
+             ||strcmp(header,"ice-bitrate")==0){
+              if(_info->bitrate_kbps<0){
+                opus_int64 bitrate_kbps;
+                /*Just re-using this function to parse a random unsigned
+                   integer field.*/
+                bitrate_kbps=op_http_parse_content_length(cdr);
+                if(bitrate_kbps>=0&&bitrate_kbps<=OP_INT32_MAX){
+                  _info->bitrate_kbps=(opus_int32)bitrate_kbps;
+                }
+              }
+            }
+            else if(strcmp(header,"icy-pub")==0
+             ||strcmp(header,"ice-public")==0){
+              if(_info->is_public<0&&(cdr[0]=='0'||cdr[0]=='1')&&cdr[1]=='\0'){
+                _info->is_public=cdr[0]-'0';
+              }
+            }
+          }
         }
       }
       switch(status_code[2]){
@@ -2421,15 +2483,16 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
         default:return OP_FALSE;
       }
       _stream->content_length=content_length;
-      _stream->pipeline=pipeline>0;
+      _stream->pipeline=pipeline_supported&&!pipeline_disabled;
       /*Pipelining requires HTTP/1.1 persistent connections.*/
-      if(pipeline)_stream->request.buf[minor_version_pos]='1';
+      if(_stream->pipeline)_stream->request.buf[minor_version_pos]='1';
       _stream->conns[0].pos=0;
       _stream->conns[0].end_pos=_stream->seekable?content_length:-1;
       _stream->conns[0].chunk_size=-1;
       _stream->cur_conni=0;
       _stream->connect_rate=op_time_diff_ms(&end_time,&start_time);
       _stream->connect_rate=OP_MAX(_stream->connect_rate,1);
+      if(_info!=NULL)_info->is_ssl=OP_URL_IS_SSL(&_stream->url);
       /*The URL has been successfully opened.*/
       return 0;
     }
@@ -2472,15 +2535,33 @@ static int op_http_stream_open(OpusHTTPStream *_stream,const char *_url,
       if(strcmp(header,"location")==0&&OP_LIKELY(_url==NULL))_url=cdr;
     }
     if(OP_UNLIKELY(_url==NULL))return OP_FALSE;
-    /*Don't free last_host if it came from the last URL.*/
-    if(last_host!=_proxy_host)_stream->url.host=NULL;
-    op_parsed_url_clear(&_stream->url);
-    ret=op_parse_url(&_stream->url,_url);
-    if(OP_UNLIKELY(ret<0)){
-      if(ret==OP_EINVAL)ret=OP_FALSE;
-      if(last_host!=_proxy_host)_ogg_free((void *)last_host);
-      return ret;
+    ret=op_parse_url(&next_url,_url);
+    if(OP_UNLIKELY(ret<0))return ret;
+    if(_proxy_host==NULL||_stream->ssl_session!=NULL){
+      if(strcmp(_stream->url.host,next_url.host)==0
+       &&_stream->url.port==next_url.port){
+        /*Try to skip re-resolve when connecting to the same host.*/
+        addrs=&_stream->addr_info;
+      }
+      else{
+        if(_stream->ssl_session!=NULL){
+          /*Forget any cached SSL session from the last host.*/
+          SSL_SESSION_free(_stream->ssl_session);
+          _stream->ssl_session=NULL;
+        }
+      }
+    }
+    if(_proxy_host==NULL){
+      OP_ASSERT(_stream->connect_host==_stream->url.host);
+      _stream->connect_host=next_url.host;
+      _stream->connect_port=next_url.port;
     }
+    /*Always try to skip re-resolve for proxy connections.*/
+    else addrs=&_stream->addr_info;
+    op_parsed_url_clear(&_stream->url);
+    *&_stream->url=*&next_url;
+    /*TODO: On servers/proxies that support pipelining, we might be able to
+       re-use this connection.*/
     op_http_conn_close(_stream,_stream->conns+0,&_stream->lru_head,1);
   }
   /*Redirection limit reached.*/
@@ -2756,7 +2837,7 @@ static int op_http_conn_read_body(OpusHTTPStream *_stream,
           /*Unless there's a bug, we should be able to convert
              (next_pos,next_end) into valid (_pos,_chunk_size) parameters.*/
           OP_ASSERT(next_end<0
-           ||next_end-next_pos>=0&&next_end-next_pos<=0x7FFFFFFF);
+           ||next_end-next_pos>=0&&next_end-next_pos<=OP_INT32_MAX);
           ret=op_http_conn_open_pos(_stream,_conn,next_pos,
            next_end<0?-1:(opus_int32)(next_end-next_pos));
           if(OP_UNLIKELY(ret<0))return OP_EREAD;
@@ -3124,12 +3205,33 @@ static const OpusFileCallbacks OP_HTTP_CALLBACKS={
 };
 #endif
 
+void opus_server_info_init(OpusServerInfo *_info){
+  _info->name=NULL;
+  _info->description=NULL;
+  _info->genre=NULL;
+  _info->url=NULL;
+  _info->server=NULL;
+  _info->content_type=NULL;
+  _info->bitrate_kbps=-1;
+  _info->is_public=-1;
+  _info->is_ssl=0;
+}
+
+void opus_server_info_clear(OpusServerInfo *_info){
+  _ogg_free(_info->content_type);
+  _ogg_free(_info->server);
+  _ogg_free(_info->url);
+  _ogg_free(_info->genre);
+  _ogg_free(_info->description);
+  _ogg_free(_info->name);
+}
+
 /*The actual URL stream creation function.
   This one isn't extensible like the application-level interface, but because
    it isn't public, we're free to change it in the future.*/
 static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
  int _skip_certificate_check,const char *_proxy_host,unsigned _proxy_port,
- const char *_proxy_user,const char *_proxy_pass){
+ const char *_proxy_user,const char *_proxy_pass,OpusServerInfo *_info){
   const char *path;
   /*Check to see if this is a valid file: URL.*/
   path=op_parse_file_url(_url);
@@ -3151,7 +3253,7 @@ static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
     if(OP_UNLIKELY(stream==NULL))return NULL;
     op_http_stream_init(stream);
     ret=op_http_stream_open(stream,_url,_skip_certificate_check,
-     _proxy_host,_proxy_port,_proxy_user,_proxy_pass);
+     _proxy_host,_proxy_port,_proxy_user,_proxy_pass,_info);
     if(OP_UNLIKELY(ret<0)){
       op_http_stream_clear(stream);
       _ogg_free(stream);
@@ -3166,22 +3268,25 @@ static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
   (void)_proxy_port;
   (void)_proxy_user;
   (void)_proxy_pass;
+  (void)_info;
   return NULL;
 #endif
 }
 
 void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
  const char *_url,va_list _ap){
-  int         skip_certificate_check;
-  const char *proxy_host;
-  opus_int32  proxy_port;
-  const char *proxy_user;
-  const char *proxy_pass;
+  int             skip_certificate_check;
+  const char     *proxy_host;
+  opus_int32      proxy_port;
+  const char     *proxy_user;
+  const char     *proxy_pass;
+  OpusServerInfo *pinfo;
   skip_certificate_check=0;
   proxy_host=NULL;
   proxy_port=8080;
   proxy_user=NULL;
   proxy_pass=NULL;
+  pinfo=NULL;
   for(;;){
     ptrdiff_t request;
     request=va_arg(_ap,char *)-(char *)NULL;
@@ -3204,12 +3309,27 @@ void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
       case OP_HTTP_PROXY_PASS_REQUEST:{
         proxy_pass=va_arg(_ap,const char *);
       }break;
+      case OP_GET_SERVER_INFO_REQUEST:{
+        pinfo=va_arg(_ap,OpusServerInfo *);
+      }break;
       /*Some unknown option.*/
       default:return NULL;
     }
   }
+  /*If the caller has requested server information, proxy it to a local copy to
+     simplify error handling.*/
+  if(pinfo!=NULL){
+    OpusServerInfo  info;
+    void           *ret;
+    opus_server_info_init(&info);
+    ret=op_url_stream_create_impl(_cb,_url,skip_certificate_check,
+     proxy_host,proxy_port,proxy_user,proxy_pass,&info);
+    if(ret!=NULL)*pinfo=*&info;
+    else opus_server_info_clear(&info);
+    return ret;
+  }
   return op_url_stream_create_impl(_cb,_url,skip_certificate_check,
-   proxy_host,proxy_port,proxy_user,proxy_pass);
+   proxy_host,proxy_port,proxy_user,proxy_pass,NULL);
 }
 
 void *op_url_stream_create(OpusFileCallbacks *_cb,
diff --git a/MP/code/opusfile-0.4/src/info.c b/SP/code/opusfile-0.5/src/info.c
similarity index 82%
rename from MP/code/opusfile-0.4/src/info.c
rename to SP/code/opusfile-0.5/src/info.c
index 6159467..6cf9851 100644
--- a/MP/code/opusfile-0.4/src/info.c
+++ b/SP/code/opusfile-0.5/src/info.c
@@ -90,24 +90,58 @@ void opus_tags_init(OpusTags *_tags){
 }
 
 void opus_tags_clear(OpusTags *_tags){
-  int i;
-  for(i=_tags->comments;i-->0;)_ogg_free(_tags->user_comments[i]);
+  int ci;
+  for(ci=_tags->comments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
   _ogg_free(_tags->user_comments);
   _ogg_free(_tags->comment_lengths);
   _ogg_free(_tags->vendor);
 }
 
+/*Ensure there's room for up to _ncomments comments.*/
+static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
+  char   **user_comments;
+  int     *comment_lengths;
+  size_t   size;
+  if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
+  size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
+  if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
+  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
+  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
+  comment_lengths[_ncomments]=0;
+  _tags->comment_lengths=comment_lengths;
+  size=sizeof(*_tags->user_comments)*(_ncomments+1);
+  if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
+  user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
+  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
+  user_comments[_ncomments]=NULL;
+  _tags->user_comments=user_comments;
+  return 0;
+}
+
+/*Duplicate a (possibly non-NUL terminated) string with a known length.*/
+static char *op_strdup_with_len(const char *_s,size_t _len){
+  size_t  size;
+  char   *ret;
+  size=sizeof(*ret)*(_len+1);
+  if(OP_UNLIKELY(size<_len))return NULL;
+  ret=(char *)_ogg_malloc(size);
+  if(OP_LIKELY(ret!=NULL)){
+    ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len);
+    ret[_len]='\0';
+  }
+  return ret;
+}
+
 /*The actual implementation of opus_tags_parse().
   Unlike the public API, this function requires _tags to already be
    initialized, modifies its contents before success is guaranteed, and assumes
    the caller will clear it on error.*/
-int opus_tags_parse_impl(OpusTags *_tags,
+static int opus_tags_parse_impl(OpusTags *_tags,
  const unsigned char *_data,size_t _len){
   opus_uint32 count;
-  size_t      size;
   size_t      len;
   int         ncomments;
-  int         i;
+  int         ci;
   len=_len;
   if(len<8)return OP_ENOTFORMAT;
   if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT;
@@ -119,14 +153,8 @@ int opus_tags_parse_impl(OpusTags *_tags,
   len-=4;
   if(count>len)return OP_EBADHEADER;
   if(_tags!=NULL){
-    char *vendor;
-    size=count+1;
-    if(size<count)return OP_EFAULT;
-    vendor=(char *)_ogg_malloc(size);
-    if(vendor==NULL)return OP_EFAULT;
-    memcpy(vendor,_data,count);
-    vendor[count]='\0';
-    _tags->vendor=vendor;
+    _tags->vendor=op_strdup_with_len((char *)_data,count);
+    if(_tags->vendor==NULL)return OP_EFAULT;
   }
   _data+=count;
   len-=count;
@@ -139,20 +167,14 @@ int opus_tags_parse_impl(OpusTags *_tags,
   /*Check for overflow (the API limits this to an int).*/
   if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
   if(_tags!=NULL){
-    size=sizeof(*_tags->comment_lengths)*(count+1);
-    if(size/sizeof(*_tags->comment_lengths)!=count+1)return OP_EFAULT;
-    _tags->comment_lengths=(int *)_ogg_malloc(size);
-    size=sizeof(*_tags->user_comments)*(count+1);
-    if(size/sizeof(*_tags->user_comments)!=count+1)return OP_EFAULT;
-    _tags->user_comments=(char **)_ogg_malloc(size);
-    if(_tags->comment_lengths==NULL||_tags->user_comments==NULL){
-      return OP_EFAULT;
-    }
+    int ret;
+    ret=op_tags_ensure_capacity(_tags,count);
+    if(ret<0)return ret;
   }
   ncomments=(int)count;
-  for(i=0;i<ncomments;i++){
+  for(ci=0;ci<ncomments;ci++){
     /*Check to make sure there's minimally sufficient data left in the packet.*/
-    if((size_t)(ncomments-i)>len>>2)return OP_EBADHEADER;
+    if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER;
     count=op_parse_uint32le(_data);
     _data+=4;
     len-=4;
@@ -160,22 +182,14 @@ int opus_tags_parse_impl(OpusTags *_tags,
     /*Check for overflow (the API limits this to an int).*/
     if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
     if(_tags!=NULL){
-      _tags->comment_lengths[i]=(int)count;
-      size=count+1;
-      if(size<count)return OP_EFAULT;
-      _tags->user_comments[i]=(char *)_ogg_malloc(size);
-      if(_tags->user_comments[i]==NULL)return OP_EFAULT;
-      _tags->comments=i+1;
-      memcpy(_tags->user_comments[i],_data,count);
-      _tags->user_comments[i][count]='\0';
+      _tags->user_comments[ci]=op_strdup_with_len((char *)_data,count);
+      if(_tags->user_comments[ci]==NULL)return OP_EFAULT;
+      _tags->comment_lengths[ci]=(int)count;
+      _tags->comments=ci+1;
     }
     _data+=count;
     len-=count;
   }
-  if(_tags!=NULL){
-    _tags->user_comments[ncomments]=NULL;
-    _tags->comment_lengths[ncomments]=0;
-  }
   return 0;
 }
 
@@ -192,23 +206,40 @@ int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){
   else return opus_tags_parse_impl(NULL,_data,_len);
 }
 
-/*Add room for a new comment.*/
-static int op_tags_add_prepare(OpusTags *_tags){
-  char **user_comments;
-  int   *comment_lengths;
-  int    ncomments;
-  ncomments=_tags->comments;
-  user_comments=(char **)_ogg_realloc(_tags->user_comments,
-   sizeof(*_tags->user_comments)*(ncomments+2));
-  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
-  _tags->user_comments=user_comments;
-  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,
-   sizeof(*_tags->comment_lengths)*(ncomments+2));
-  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
-  _tags->comment_lengths=comment_lengths;
-  comment_lengths[ncomments]=comment_lengths[ncomments+1]=0;
-  /*Our caller will always set user_comments[ncomments].*/
-  user_comments[ncomments+1]=NULL;
+/*The actual implementation of opus_tags_copy().
+  Unlike the public API, this function requires _dst to already be
+   initialized, modifies its contents before success is guaranteed, and assumes
+   the caller will clear it on error.*/
+static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){
+  char *vendor;
+  int   ncomments;
+  int   ret;
+  int   ci;
+  vendor=_src->vendor;
+  _dst->vendor=op_strdup_with_len(vendor,strlen(vendor));
+  if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT;
+  ncomments=_src->comments;
+  ret=op_tags_ensure_capacity(_dst,ncomments);
+  if(OP_UNLIKELY(ret<0))return ret;
+  for(ci=0;ci<ncomments;ci++){
+    int len;
+    len=_src->comment_lengths[ci];
+    OP_ASSERT(len>=0);
+    _dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len);
+    if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT;
+    _dst->comment_lengths[ci]=len;
+    _dst->comments=ci+1;
+  }
+  return 0;
+}
+
+int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){
+  OpusTags dst;
+  int      ret;
+  opus_tags_init(&dst);
+  ret=opus_tags_copy_impl(&dst,_src);
+  if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst);
+  else *_dst=*&dst;
   return 0;
 }
 
@@ -218,12 +249,13 @@ int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
   int   value_len;
   int   ncomments;
   int   ret;
-  ret=op_tags_add_prepare(_tags);
+  ncomments=_tags->comments;
+  ret=op_tags_ensure_capacity(_tags,ncomments+1);
   if(OP_UNLIKELY(ret<0))return ret;
   tag_len=strlen(_tag);
   value_len=strlen(_value);
-  ncomments=_tags->comments;
   /*+2 for '=' and '\0'.*/
+  _tags->comment_lengths[ncomments]=0;
   _tags->user_comments[ncomments]=comment=
    (char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
   if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
@@ -235,26 +267,29 @@ int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
 }
 
 int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
-  char *comment;
-  int   ncomments;
-  int   comment_len;
-  int   ret;
-  ret=op_tags_add_prepare(_tags);
-  if(OP_UNLIKELY(ret<0))return ret;
-  comment_len=strlen(_comment);
+  int comment_len;
+  int ncomments;
+  int ret;
   ncomments=_tags->comments;
-  _tags->user_comments[ncomments]=comment=(char *)
-   _ogg_malloc(sizeof(*_tags->user_comments[ncomments])*(comment_len+1));
-  if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
+  ret=op_tags_ensure_capacity(_tags,ncomments+1);
+  if(OP_UNLIKELY(ret<0))return ret;
+  comment_len=(int)strlen(_comment);
+  _tags->comment_lengths[ncomments]=0;
+  _tags->user_comments[ncomments]=op_strdup_with_len(_comment,comment_len);
+  if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
   _tags->comment_lengths[ncomments]=comment_len;
-  memcpy(comment,_comment,sizeof(*comment)*(comment_len+1));
   return 0;
 }
 
-/*Is _a a "tag=value" comment whose tag matches _b?
-  0 if it is, a non-zero value otherwise.*/
-static int op_tagcompare(const char *_a,const char *_b,int _n){
-  return op_strncasecmp(_a,_b,_n)||_a[_n]!='=';
+int opus_tagcompare(const char *_tag_name,const char *_comment){
+  return opus_tagncompare(_tag_name,strlen(_tag_name),_comment);
+}
+
+int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){
+  int ret;
+  OP_ASSERT(_tag_len>=0);
+  ret=op_strncasecmp(_tag_name,_comment,_tag_len);
+  return ret?ret:'='-_comment[_tag_len];
 }
 
 const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
@@ -268,7 +303,7 @@ const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
   user_comments=_tags->user_comments;
   found=0;
   for(ci=0;ci<ncomments;ci++){
-    if(!op_tagcompare(user_comments[ci],_tag,tag_len)){
+    if(!opus_tagncompare(_tag,tag_len,user_comments[ci])){
       /*We return a pointer to the data, not a copy.*/
       if(_count==found++)return user_comments[ci]+tag_len+1;
     }
@@ -288,23 +323,20 @@ int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
   user_comments=_tags->user_comments;
   found=0;
   for(ci=0;ci<ncomments;ci++){
-    if(!op_tagcompare(user_comments[ci],_tag,tag_len))found++;
+    if(!opus_tagncompare(_tag,tag_len,user_comments[ci]))found++;
   }
   return found;
 }
 
 int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){
   char **comments;
-  int   *comment_lengths;
   int    ncomments;
   int    ci;
   comments=_tags->user_comments;
-  comment_lengths=_tags->comment_lengths;
   ncomments=_tags->comments;
   /*Look for the first valid R128_TRACK_GAIN tag and use that.*/
   for(ci=0;ci<ncomments;ci++){
-    if(comment_lengths[ci]>16
-     &&op_strncasecmp(comments[ci],"R128_TRACK_GAIN=",16)==0){
+    if(opus_tagncompare("R128_TRACK_GAIN",15,comments[ci])==0){
       char       *p;
       opus_int32  gain_q8;
       int         negative;
@@ -617,7 +649,7 @@ int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){
   size_t          buf_sz;
   size_t          tag_length;
   int             ret;
-  if(op_strncasecmp(_tag,"METADATA_BLOCK_PICTURE=",23)==0)_tag+=23;
+  if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23;
   /*Figure out how much BASE64-encoded data we have.*/
   tag_length=strlen(_tag);
   if(tag_length&3)return OP_ENOTFORMAT;
diff --git a/SP/code/opusfile-0.4/src/internal.c b/SP/code/opusfile-0.5/src/internal.c
similarity index 100%
rename from SP/code/opusfile-0.4/src/internal.c
rename to SP/code/opusfile-0.5/src/internal.c
diff --git a/SP/code/opusfile-0.4/src/internal.h b/SP/code/opusfile-0.5/src/internal.h
similarity index 96%
rename from SP/code/opusfile-0.4/src/internal.h
rename to SP/code/opusfile-0.5/src/internal.h
index d35d531..0811491 100644
--- a/SP/code/opusfile-0.4/src/internal.h
+++ b/SP/code/opusfile-0.5/src/internal.h
@@ -94,6 +94,8 @@ void op_fatal_impl(const char *_str,const char *_file,int _line);
 
 # define OP_INT64_MAX (2*(((ogg_int64_t)1<<62)-1)|1)
 # define OP_INT64_MIN (-OP_INT64_MAX-1)
+# define OP_INT32_MAX (2*(((ogg_int32_t)1<<30)-1)|1)
+# define OP_INT32_MIN (-OP_INT32_MAX-1)
 
 # define OP_MIN(_a,_b)        ((_a)<(_b)?(_a):(_b))
 # define OP_MAX(_a,_b)        ((_a)>(_b)?(_a):(_b))
@@ -201,6 +203,10 @@ struct OggOpusFile{
   int                op_count;
   /*Central working state for the packet-to-PCM decoder.*/
   OpusMSDecoder     *od;
+  /*The application-provided packet decode callback.*/
+  op_decode_cb_func  decode_cb;
+  /*The application-provided packet decode callback context.*/
+  void              *decode_cb_ctx;
   /*The stream count used to initialize the decoder.*/
   int                od_stream_count;
   /*The coupled stream count used to initialize the decoder.*/
@@ -229,6 +235,7 @@ struct OggOpusFile{
   float              dither_b[OP_NCHANNELS_MAX*4];
   opus_uint32        dither_seed;
   int                dither_mute;
+  int                dither_disabled;
   /*The number of channels represented by the internal state.
     This gets set to 0 whenever anything that would prevent state propagation
      occurs (switching between the float/short APIs, or between the
diff --git a/MP/code/opusfile-0.4/src/opusfile.c b/SP/code/opusfile-0.5/src/opusfile.c
similarity index 92%
rename from MP/code/opusfile-0.4/src/opusfile.c
rename to SP/code/opusfile-0.5/src/opusfile.c
index c02e5a2..392ddb2 100644
--- a/MP/code/opusfile-0.4/src/opusfile.c
+++ b/SP/code/opusfile-0.5/src/opusfile.c
@@ -167,7 +167,7 @@ static int op_seek_helper(OggOpusFile *_of,opus_int64 _offset){
 
 /*Get the current position indicator of the underlying source.
   This should be the same as the value reported by tell().*/
-static opus_int64 op_position(OggOpusFile *_of){
+static opus_int64 op_position(const OggOpusFile *_of){
   /*The current position indicator is _not_ simply offset.
     We may also have unprocessed, buffered data in the sync state.*/
   return _of->offset+_of->oy.fill-_of->oy.returned;
@@ -226,7 +226,7 @@ static opus_int64 op_get_next_page(OggOpusFile *_of,ogg_page *_og,
   return OP_FALSE;
 }
 
-static int op_add_serialno(ogg_page *_og,
+static int op_add_serialno(const ogg_page *_og,
  ogg_uint32_t **_serialnos,int *_nserialnos,int *_cserialnos){
   ogg_uint32_t *serialnos;
   int           nserialnos;
@@ -259,7 +259,7 @@ static int op_lookup_serialno(ogg_uint32_t _s,
   return i<_nserialnos;
 }
 
-static int op_lookup_page_serialno(ogg_page *_og,
+static int op_lookup_page_serialno(const ogg_page *_og,
  const ogg_uint32_t *_serialnos,int _nserialnos){
   return op_lookup_serialno(ogg_page_serialno(_og),_serialnos,_nserialnos);
 }
@@ -1003,7 +1003,7 @@ static int op_find_final_pcm_offset(OggOpusFile *_of,
 
 /*Rescale the number _x from the range [0,_from] to [0,_to].
   _from and _to must be positive.*/
-opus_int64 op_rescale64(opus_int64 _x,opus_int64 _from,opus_int64 _to){
+static opus_int64 op_rescale64(opus_int64 _x,opus_int64 _from,opus_int64 _to){
   opus_int64 frac;
   opus_int64 ret;
   int        i;
@@ -1147,7 +1147,7 @@ static int op_bisect_forward_serialno(OggOpusFile *_of,
        (potentially not a page we care about).*/
     /*Scan the seek records we already have to save us some bisection.*/
     for(sri=0;sri<nsr;sri++){
-      if(op_lookup_serialno(_sr[sri].serialno,*_serialnos,*_nserialnos))break;
+      if(op_lookup_serialno(_sr[sri].serialno,serialnos,nserialnos))break;
     }
     /*Is the last page in our current list of serial numbers?*/
     if(sri<=0)break;
@@ -1333,11 +1333,11 @@ static void op_update_gain(OggOpusFile *_of){
 }
 
 static int op_make_decode_ready(OggOpusFile *_of){
-  OpusHead *head;
-  int       li;
-  int       stream_count;
-  int       coupled_count;
-  int       channel_count;
+  const OpusHead *head;
+  int             li;
+  int             stream_count;
+  int             coupled_count;
+  int             channel_count;
   if(_of->ready_state>OP_STREAMSET)return 0;
   if(OP_UNLIKELY(_of->ready_state<OP_STREAMSET))return OP_EFAULT;
   li=_of->seekable?_of->cur_link:0;
@@ -1519,14 +1519,12 @@ static int op_open1(OggOpusFile *_of,
   seekable=_cb->seek!=NULL&&(*_cb->seek)(_source,0,SEEK_CUR)!=-1;
   /*If seek is implemented, tell must also be implemented.*/
   if(seekable){
+    opus_int64 pos;
     if(OP_UNLIKELY(_of->callbacks.tell==NULL))return OP_EINVAL;
-    else{
-      opus_int64 pos;
-      pos=(*_of->callbacks.tell)(_of->source);
-      /*If the current position is not equal to the initial bytes consumed,
-         absolute seeking will not work.*/
-      if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL;
-    }
+    pos=(*_of->callbacks.tell)(_of->source);
+    /*If the current position is not equal to the initial bytes consumed,
+       absolute seeking will not work.*/
+    if(OP_UNLIKELY(pos!=(opus_int64)_initial_bytes))return OP_EINVAL;
   }
   _of->seekable=seekable;
   /*Don't seek yet.
@@ -1686,25 +1684,25 @@ void op_free(OggOpusFile *_of){
   }
 }
 
-int op_seekable(OggOpusFile *_of){
+int op_seekable(const OggOpusFile *_of){
   return _of->seekable;
 }
 
-int op_link_count(OggOpusFile *_of){
+int op_link_count(const OggOpusFile *_of){
   return _of->nlinks;
 }
 
-ogg_uint32_t op_serialno(OggOpusFile *_of,int _li){
+ogg_uint32_t op_serialno(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1;
   if(!_of->seekable)_li=0;
   return _of->links[_li<0?_of->cur_link:_li].serialno;
 }
 
-int op_channel_count(OggOpusFile *_of,int _li){
+int op_channel_count(const OggOpusFile *_of,int _li){
   return op_head(_of,_li)->channel_count;
 }
 
-opus_int64 op_raw_total(OggOpusFile *_of,int _li){
+opus_int64 op_raw_total(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED)
    ||OP_UNLIKELY(!_of->seekable)
    ||OP_UNLIKELY(_li>=_of->nlinks)){
@@ -1715,7 +1713,7 @@ opus_int64 op_raw_total(OggOpusFile *_of,int _li){
    -_of->links[_li].offset;
 }
 
-ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li){
+ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li){
   OggOpusLink *links;
   ogg_int64_t  diff;
   int          nlinks;
@@ -1745,13 +1743,13 @@ ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li){
   return diff-links[_li].head.pre_skip;
 }
 
-const OpusHead *op_head(OggOpusFile *_of,int _li){
+const OpusHead *op_head(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1;
   if(!_of->seekable)_li=0;
   return &_of->links[_li<0?_of->cur_link:_li].head;
 }
 
-const OpusTags *op_tags(OggOpusFile *_of,int _li){
+const OpusTags *op_tags(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_li>=_of->nlinks))_li=_of->nlinks-1;
   if(!_of->seekable){
     if(_of->ready_state<OP_STREAMSET&&_of->ready_state!=OP_PARTOPEN){
@@ -1763,7 +1761,7 @@ const OpusTags *op_tags(OggOpusFile *_of,int _li){
   return &_of->links[_li].tags;
 }
 
-int op_current_link(OggOpusFile *_of){
+int op_current_link(const OggOpusFile *_of){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   return _of->cur_link;
 }
@@ -1774,11 +1772,13 @@ static opus_int32 op_calc_bitrate(opus_int64 _bytes,ogg_int64_t _samples){
   /*These rates are absurd, but let's handle them anyway.*/
   if(OP_UNLIKELY(_bytes>(OP_INT64_MAX-(_samples>>1))/(48000*8))){
     ogg_int64_t den;
-    if(OP_UNLIKELY(_bytes/(0x7FFFFFFFF/(48000*8))>=_samples))return 0x7FFFFFFF;
+    if(OP_UNLIKELY(_bytes/(OP_INT32_MAX/(48000*8))>=_samples)){
+      return OP_INT32_MAX;
+    }
     den=_samples/(48000*8);
     return (opus_int32)((_bytes+(den>>1))/den);
   }
-  if(OP_UNLIKELY(_samples<=0))return 0x7FFFFFFF;
+  if(OP_UNLIKELY(_samples<=0))return OP_INT32_MAX;
   /*This can't actually overflow in normal operation: even with a pre-skip of
      545 2.5 ms frames with 8 streams running at 1282*8+1 bytes per packet
      (1275 byte frames + Opus framing overhead + Ogg lacing values), that all
@@ -1786,10 +1786,11 @@ static opus_int32 op_calc_bitrate(opus_int64 _bytes,ogg_int64_t _samples){
     The only way to get bitrates larger than that is with excessive Opus
      padding, more encoded streams than output channels, or lots and lots of
      Ogg pages with no packets on them.*/
-  return (opus_int32)OP_MIN((_bytes*48000*8+(_samples>>1))/_samples,0x7FFFFFFF);
+  return (opus_int32)OP_MIN((_bytes*48000*8+(_samples>>1))/_samples,
+   OP_INT32_MAX);
 }
 
-opus_int32 op_bitrate(OggOpusFile *_of,int _li){
+opus_int32 op_bitrate(const OggOpusFile *_of,int _li){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED)||OP_UNLIKELY(!_of->seekable)
    ||OP_UNLIKELY(_li>=_of->nlinks)){
     return OP_EINVAL;
@@ -1825,11 +1826,8 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
   int           seekable;
   int           cur_link;
   int           ret;
-  if(OP_LIKELY(_of->ready_state>=OP_INITSET)
-   &&OP_LIKELY(_of->op_pos<_of->op_count)){
-    /*We're ready to decode and have at least one packet available already.*/
-    return 1;
-  }
+  /*We shouldn't get here if we have unprocessed packets.*/
+  OP_ASSERT(_of->ready_state<OP_INITSET||_of->op_pos>=_of->op_count);
   if(!_readp)return 0;
   seekable=_of->seekable;
   links=_of->links;
@@ -2084,7 +2082,7 @@ static int op_fetch_and_process_page(OggOpusFile *_of,
 }
 
 int op_raw_seek(OggOpusFile *_of,opus_int64 _pos){
-  int          ret;
+  int ret;
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   /*Don't dump the decoder state if we can't seek.*/
   if(OP_UNLIKELY(!_of->seekable))return OP_ENOSEEK;
@@ -2115,10 +2113,10 @@ int op_raw_seek(OggOpusFile *_of,opus_int64 _pos){
    position in an individual link.*/
 static ogg_int64_t op_get_granulepos(const OggOpusFile *_of,
  ogg_int64_t _pcm_offset,int *_li){
-  OggOpusLink *links;
-  ogg_int64_t  duration;
-  int          nlinks;
-  int          li;
+  const OggOpusLink *links;
+  ogg_int64_t        duration;
+  int                nlinks;
+  int                li;
   OP_ASSERT(_pcm_offset>=0);
   nlinks=_of->nlinks;
   links=_of->links;
@@ -2166,25 +2164,25 @@ static ogg_int64_t op_get_granulepos(const OggOpusFile *_of,
   Account for that (and report it as an error condition).*/
 static int op_pcm_seek_page(OggOpusFile *_of,
  ogg_int64_t _target_gp,int _li){
-  OggOpusLink  *link;
-  ogg_page      og;
-  ogg_int64_t   pcm_pre_skip;
-  ogg_int64_t   pcm_start;
-  ogg_int64_t   pcm_end;
-  ogg_int64_t   best_gp;
-  ogg_int64_t   diff;
-  ogg_uint32_t  serialno;
-  opus_int32    pre_skip;
-  opus_int64    begin;
-  opus_int64    end;
-  opus_int64    boundary;
-  opus_int64    best;
-  opus_int64    page_offset;
-  opus_int64    d0;
-  opus_int64    d1;
-  opus_int64    d2;
-  int           force_bisect;
-  int           ret;
+  const OggOpusLink *link;
+  ogg_page           og;
+  ogg_int64_t        pcm_pre_skip;
+  ogg_int64_t        pcm_start;
+  ogg_int64_t        pcm_end;
+  ogg_int64_t        best_gp;
+  ogg_int64_t        diff;
+  ogg_uint32_t       serialno;
+  opus_int32         pre_skip;
+  opus_int64         begin;
+  opus_int64         end;
+  opus_int64         boundary;
+  opus_int64         best;
+  opus_int64         page_offset;
+  opus_int64         d0;
+  opus_int64         d1;
+  opus_int64         d2;
+  int                force_bisect;
+  int                ret;
   _of->bytes_tracked=0;
   _of->samples_tracked=0;
   link=_of->links+_li;
@@ -2405,16 +2403,16 @@ static int op_pcm_seek_page(OggOpusFile *_of,
 }
 
 int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
-  OggOpusLink *link;
-  ogg_int64_t  pcm_start;
-  ogg_int64_t  target_gp;
-  ogg_int64_t  prev_packet_gp;
-  ogg_int64_t  skip;
-  ogg_int64_t  diff;
-  int          op_count;
-  int          op_pos;
-  int          ret;
-  int          li;
+  const OggOpusLink *link;
+  ogg_int64_t        pcm_start;
+  ogg_int64_t        target_gp;
+  ogg_int64_t        prev_packet_gp;
+  ogg_int64_t        skip;
+  ogg_int64_t        diff;
+  int                op_count;
+  int                op_pos;
+  int                ret;
+  int                li;
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   if(OP_UNLIKELY(!_of->seekable))return OP_ENOSEEK;
   if(OP_UNLIKELY(_pcm_offset<0))return OP_EINVAL;
@@ -2458,7 +2456,7 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
   if(_pcm_offset<=link->head.pre_skip)skip=0;
   else skip=OP_MAX(_pcm_offset-80*48,0);
   OP_ASSERT(_pcm_offset-skip>=0);
-  OP_ASSERT(_pcm_offset-skip<0x7FFFFFFF-120*48);
+  OP_ASSERT(_pcm_offset-skip<OP_INT32_MAX-120*48);
   /*Skip packets until we find one with samples past our skip target.*/
   for(;;){
     op_count=_of->op_count;
@@ -2484,7 +2482,7 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
   /*We skipped too far.
     Either the timestamps were illegal or there was a hole in the data.*/
   if(diff>skip)return OP_EBADLINK;
-  OP_ASSERT(_pcm_offset-diff<0x7FFFFFFF);
+  OP_ASSERT(_pcm_offset-diff<OP_INT32_MAX);
   /*TODO: If there are further holes/illegal timestamps, we still won't decode
      to the correct sample.
     However, at least op_pcm_tell() will report the correct value immediately
@@ -2493,7 +2491,7 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset){
   return 0;
 }
 
-opus_int64 op_raw_tell(OggOpusFile *_of){
+opus_int64 op_raw_tell(const OggOpusFile *_of){
   if(OP_UNLIKELY(_of->ready_state<OP_OPENED))return OP_EINVAL;
   return _of->offset;
 }
@@ -2503,10 +2501,10 @@ opus_int64 op_raw_tell(OggOpusFile *_of){
   For unseekable sources, this gets reset to 0 at the beginning of each link.*/
 static ogg_int64_t op_get_pcm_offset(const OggOpusFile *_of,
  ogg_int64_t _gp,int _li){
-  OggOpusLink *links;
-  ogg_int64_t  pcm_offset;
-  ogg_int64_t  delta;
-  int          li;
+  const OggOpusLink *links;
+  ogg_int64_t        pcm_offset;
+  ogg_int64_t        delta;
+  int                li;
   links=_of->links;
   pcm_offset=0;
   OP_ASSERT(_li<_of->nlinks);
@@ -2521,15 +2519,23 @@ static ogg_int64_t op_get_pcm_offset(const OggOpusFile *_of,
     _gp=links[_li].pcm_end;
   }
   if(OP_LIKELY(op_granpos_cmp(_gp,links[_li].pcm_start)>0)){
-    OP_ALWAYS_TRUE(!op_granpos_diff(&delta,_gp,links[_li].pcm_start));
+    if(OP_UNLIKELY(op_granpos_diff(&delta,_gp,links[_li].pcm_start)<0)){
+      /*This means an unseekable stream claimed to have a page from more than
+         2 billion days after we joined.*/
+      OP_ASSERT(!_of->seekable);
+      return OP_INT64_MAX;
+    }
     if(delta<links[_li].head.pre_skip)delta=0;
     else delta-=links[_li].head.pre_skip;
+    /*In the seekable case, _gp was limited by pcm_end.
+      In the unseekable case, pcm_offset should be 0.*/
+    OP_ASSERT(pcm_offset<=OP_INT64_MAX-delta);
     pcm_offset+=delta;
   }
   return pcm_offset;
 }
 
-ogg_int64_t op_pcm_tell(OggOpusFile *_of){
+ogg_int64_t op_pcm_tell(const OggOpusFile *_of){
   ogg_int64_t gp;
   int         nbuffered;
   int         li;
@@ -2545,6 +2551,12 @@ ogg_int64_t op_pcm_tell(OggOpusFile *_of){
   return op_get_pcm_offset(_of,gp,li);
 }
 
+void op_set_decode_callback(OggOpusFile *_of,
+ op_decode_cb_func _decode_cb,void *_ctx){
+  _of->decode_cb=_decode_cb;
+  _of->decode_cb_ctx=_ctx;
+}
+
 int op_set_gain_offset(OggOpusFile *_of,
  int _gain_type,opus_int32 _gain_offset_q8){
   if(_gain_type!=OP_HEADER_GAIN&&_gain_type!=OP_TRACK_GAIN
@@ -2560,15 +2572,22 @@ int op_set_gain_offset(OggOpusFile *_of,
   return 0;
 }
 
+void op_set_dither_enabled(OggOpusFile *_of,int _enabled){
+#if !defined(OP_FIXED_POINT)
+  _of->dither_disabled=!_enabled;
+  if(!_enabled)_of->dither_mute=65;
+#endif
+}
+
 /*Allocate the decoder scratch buffer.
   This is done lazily, since if the user provides large enough buffers, we'll
    never need it.*/
 static int op_init_buffer(OggOpusFile *_of){
   int nchannels_max;
   if(_of->seekable){
-    OggOpusLink *links;
-    int          nlinks;
-    int          li;
+    const OggOpusLink *links;
+    int                nlinks;
+    int                li;
     links=_of->links;
     nlinks=_of->nlinks;
     nchannels_max=1;
@@ -2583,6 +2602,39 @@ static int op_init_buffer(OggOpusFile *_of){
   return 0;
 }
 
+/*Decode a single packet into the target buffer.*/
+static int op_decode(OggOpusFile *_of,op_sample *_pcm,
+ const ogg_packet *_op,int _nsamples,int _nchannels){
+  int ret;
+  /*First we try using the application-provided decode callback.*/
+  if(_of->decode_cb!=NULL){
+#if defined(OP_FIXED_POINT)
+    ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op,
+     _nsamples,_nchannels,OP_DEC_FORMAT_SHORT,_of->cur_link);
+#else
+    ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op,
+     _nsamples,_nchannels,OP_DEC_FORMAT_FLOAT,_of->cur_link);
+#endif
+  }
+  else ret=OP_DEC_USE_DEFAULT;
+  /*If the application didn't want to handle decoding, do it ourselves.*/
+  if(ret==OP_DEC_USE_DEFAULT){
+#if defined(OP_FIXED_POINT)
+    ret=opus_multistream_decode(_of->od,
+     _op->packet,_op->bytes,_pcm,_nsamples,0);
+#else
+    ret=opus_multistream_decode_float(_of->od,
+     _op->packet,_op->bytes,_pcm,_nsamples,0);
+#endif
+    OP_ASSERT(ret<0||ret==_nsamples);
+  }
+  /*If the application returned a positive value other than 0 or
+     OP_DEC_USE_DEFAULT, fail.*/
+  else if(OP_UNLIKELY(ret>0))return OP_EBADPACKET;
+  if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
+  return ret;
+}
+
 /*Read more samples from the stream, using the same API as op_read() or
    op_read_float().*/
 static int op_read_native(OggOpusFile *_of,
@@ -2599,10 +2651,8 @@ static int op_read_native(OggOpusFile *_of,
       od_buffer_pos=_of->od_buffer_pos;
       nsamples=_of->od_buffer_size-od_buffer_pos;
       /*If we have buffered samples, return them.*/
-      if(OP_UNLIKELY(nsamples>0)){
-        if(OP_UNLIKELY(nsamples*nchannels>_buf_size)){
-          nsamples=_buf_size/nchannels;
-        }
+      if(nsamples>0){
+        if(nsamples*nchannels>_buf_size)nsamples=_buf_size/nchannels;
         memcpy(_pcm,_of->od_buffer+nchannels*od_buffer_pos,
          sizeof(*_pcm)*nchannels*nsamples);
         od_buffer_pos+=nsamples;
@@ -2613,11 +2663,11 @@ static int op_read_native(OggOpusFile *_of,
       /*If we have buffered packets, decode one.*/
       op_pos=_of->op_pos;
       if(OP_LIKELY(op_pos<_of->op_count)){
-        ogg_packet  *pop;
-        ogg_int64_t  diff;
-        opus_int32   cur_discard_count;
-        int          duration;
-        int          trimmed_duration;
+        const ogg_packet *pop;
+        ogg_int64_t       diff;
+        opus_int32        cur_discard_count;
+        int               duration;
+        int               trimmed_duration;
         pop=_of->op+op_pos++;
         _of->op_pos=op_pos;
         cur_discard_count=_of->cur_discard_count;
@@ -2646,15 +2696,8 @@ static int op_read_native(OggOpusFile *_of,
             if(OP_UNLIKELY(ret<0))return ret;
             buf=_of->od_buffer;
           }
-#if defined(OP_FIXED_POINT)
-          ret=opus_multistream_decode(_of->od,
-           pop->packet,pop->bytes,buf,120*48,0);
-#else
-          ret=opus_multistream_decode_float(_of->od,
-           pop->packet,pop->bytes,buf,120*48,0);
-#endif
-          if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
-          OP_ASSERT(ret==duration);
+          ret=op_decode(_of,buf,pop,duration,nchannels);
+          if(OP_UNLIKELY(ret<0))return ret;
           /*Perform pre-skip/pre-roll.*/
           od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count);
           cur_discard_count-=od_buffer_pos;
@@ -2665,31 +2708,22 @@ static int op_read_native(OggOpusFile *_of,
              what was decoded.*/
           _of->bytes_tracked+=pop->bytes;
           _of->samples_tracked+=trimmed_duration-od_buffer_pos;
-          /*Don't grab another page yet.*/
-          if(OP_LIKELY(od_buffer_pos<trimmed_duration))continue;
         }
         else{
           /*Otherwise decode directly into the user's buffer.*/
-#if defined(OP_FIXED_POINT)
-          ret=opus_multistream_decode(_of->od,pop->packet,pop->bytes,
-           _pcm,_buf_size/nchannels,0);
-#else
-          ret=opus_multistream_decode_float(_of->od,pop->packet,pop->bytes,
-           _pcm,_buf_size/nchannels,0);
-#endif
-          if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
-          OP_ASSERT(ret==duration);
+          ret=op_decode(_of,_pcm,pop,duration,nchannels);
+          if(OP_UNLIKELY(ret<0))return ret;
           if(OP_LIKELY(trimmed_duration>0)){
             /*Perform pre-skip/pre-roll.*/
             od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count);
             cur_discard_count-=od_buffer_pos;
             _of->cur_discard_count=cur_discard_count;
-            if(OP_UNLIKELY(od_buffer_pos>0)
-             &&OP_LIKELY(od_buffer_pos<trimmed_duration)){
+            trimmed_duration-=od_buffer_pos;
+            if(OP_LIKELY(trimmed_duration>0)
+             &&OP_UNLIKELY(od_buffer_pos>0)){
               memmove(_pcm,_pcm+od_buffer_pos*nchannels,
-               sizeof(*_pcm)*(trimmed_duration-od_buffer_pos)*nchannels);
+               sizeof(*_pcm)*trimmed_duration*nchannels);
             }
-            trimmed_duration-=od_buffer_pos;
             /*Update bitrate tracking based on the actual samples we used from
                what was decoded.*/
             _of->bytes_tracked+=pop->bytes;
@@ -2700,6 +2734,9 @@ static int op_read_native(OggOpusFile *_of,
             }
           }
         }
+        /*Don't grab another page yet.
+          This one might have more packets, or might have buffered data now.*/
+        continue;
       }
     }
     /*Suck in another page.*/
@@ -2712,12 +2749,15 @@ static int op_read_native(OggOpusFile *_of,
   }
 }
 
+/*A generic filter to apply to the decoded audio data.
+  _src is non-const because we will destructively modify the contents of the
+   source buffer that we consume in some cases.*/
 typedef int (*op_read_filter_func)(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_sample *_src,int _nsamples,int _nchannels);
 
 /*Decode some samples and then apply a custom filter to them.
   This is used to convert to different output formats.*/
-static int op_read_native_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
+static int op_filter_read_native(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_read_filter_func _filter,int *_li){
   int ret;
   /*Ensure we have some decoded samples in our buffer.*/
@@ -2741,11 +2781,46 @@ static int op_read_native_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
   return ret;
 }
 
-#if defined(OP_FIXED_POINT)
+#if !defined(OP_FIXED_POINT)||!defined(OP_DISABLE_FLOAT_API)
 
-int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){
-  return op_read_native(_of,_pcm,_buf_size,_li);
-}
+/*Matrices for downmixing from the supported channel counts to stereo.
+  The matrices with 5 or more channels are normalized to a total volume of 2.0,
+   since most mixes sound too quiet if normalized to 1.0 (as there is generally
+   little volume in the side/rear channels).*/
+static const float OP_STEREO_DOWNMIX[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={
+  /*3.0*/
+  {
+    {0.5858F,0.0F},{0.4142F,0.4142F},{0.0F,0.5858F}
+  },
+  /*quadrophonic*/
+  {
+    {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
+  },
+  /*5.0*/
+  {
+    {0.651F,0.0F},{0.46F,0.46F},{0.0F,0.651F},{0.5636F,0.3254F},
+    {0.3254F,0.5636F}
+  },
+  /*5.1*/
+  {
+    {0.529F,0.0F},{0.3741F,0.3741F},{0.0F,0.529F},{0.4582F,0.2645F},
+    {0.2645F,0.4582F},{0.3741F,0.3741F}
+  },
+  /*6.1*/
+  {
+    {0.4553F,0.0F},{0.322F,0.322F},{0.0F,0.4553F},{0.3943F,0.2277F},
+    {0.2277F,0.3943F},{0.2788F,0.2788F},{0.322F,0.322F}
+  },
+  /*7.1*/
+  {
+    {0.3886F,0.0F},{0.2748F,0.2748F},{0.0F,0.3886F},{0.3366F,0.1943F},
+    {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F},{0.2748F,0.2748F}
+  }
+};
+
+#endif
+
+#if defined(OP_FIXED_POINT)
 
 /*Matrices for downmixing from the supported channel counts to stereo.
   The matrices with 5 or more channels are normalized to a total volume of 2.0,
@@ -2783,6 +2858,10 @@ static const opus_int16 OP_STEREO_DOWNMIX_Q14
   }
 };
 
+int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){
+  return op_read_native(_of,_pcm,_buf_size,_li);
+}
+
 static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_sample *_src,int _nsamples,int _nchannels){
   (void)_of;
@@ -2807,6 +2886,7 @@ static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
           l+=OP_STEREO_DOWNMIX_Q14[_nchannels-3][ci][0]*s;
           r+=OP_STEREO_DOWNMIX_Q14[_nchannels-3][ci][1]*s;
         }
+        /*TODO: For 5 or more channels, we should do soft clipping here.*/
         dst[2*i+0]=(opus_int16)OP_CLAMP(-32768,l+8192>>14,32767);
         dst[2*i+1]=(opus_int16)OP_CLAMP(-32768,r+8192>>14,32767);
       }
@@ -2816,7 +2896,7 @@ static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
 }
 
 int op_read_stereo(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size){
-  return op_read_native_filter(_of,_pcm,_buf_size,op_stereo_filter,NULL);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_stereo_filter,NULL);
 }
 
 # if !defined(OP_DISABLE_FLOAT_API)
@@ -2834,33 +2914,51 @@ static int op_short2float_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
 }
 
 int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){
-  return op_read_native_filter(_of,_pcm,_buf_size,op_short2float_filter,_li);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_short2float_filter,_li);
 }
 
 static int op_short2float_stereo_filter(OggOpusFile *_of,
  void *_dst,int _dst_sz,op_sample *_src,int _nsamples,int _nchannels){
   float *dst;
+  int    i;
   dst=(float *)_dst;
   _nsamples=OP_MIN(_nsamples,_dst_sz>>1);
   if(_nchannels==1){
-    int i;
     _nsamples=op_short2float_filter(_of,dst,_nsamples,_src,_nsamples,1);
     for(i=_nsamples;i-->0;)dst[2*i+0]=dst[2*i+1]=dst[i];
-    return _nsamples;
   }
-  /*It would be better to convert to floats and then downmix (so that we don't
-     risk clipping with more than 5 channels), but that would require a large
-     stack buffer, which is probably not a good idea if you're using the
-     fixed-point build.*/
-  if(_nchannels>2){
-    _nsamples=op_stereo_filter(_of,_src,_nsamples*2,
-     _src,_nsamples,_nchannels);
+  else if(_nchannels<5){
+    /*For 3 or 4 channels, we can downmix in fixed point without risk of
+       clipping.*/
+    if(_nchannels>2){
+      _nsamples=op_stereo_filter(_of,_src,_nsamples*2,
+       _src,_nsamples,_nchannels);
+    }
+    return op_short2float_filter(_of,dst,_dst_sz,_src,_nsamples,2);
+  }
+  else{
+    /*For 5 or more channels, we convert to floats and then downmix (so that we
+       don't risk clipping).*/
+    for(i=0;i<_nsamples;i++){
+      float l;
+      float r;
+      int   ci;
+      l=r=0;
+      for(ci=0;ci<_nchannels;ci++){
+        float s;
+        s=(1.0F/32768)*_src[_nchannels*i+ci];
+        l+=OP_STEREO_DOWNMIX[_nchannels-3][ci][0]*s;
+        r+=OP_STEREO_DOWNMIX[_nchannels-3][ci][1]*s;
+      }
+      dst[2*i+0]=l;
+      dst[2*i+1]=r;
+    }
   }
-  return op_short2float_filter(_of,dst,_dst_sz,_src,_nsamples,2);
+  return _nsamples;
 }
 
 int op_read_float_stereo(OggOpusFile *_of,float *_pcm,int _buf_size){
-  return op_read_native_filter(_of,_pcm,_buf_size,
+  return op_filter_read_native(_of,_pcm,_buf_size,
    op_short2float_stereo_filter,NULL);
 }
 
@@ -2916,85 +3014,85 @@ static const float OP_FCOEF_A[4]={
   0.9030F,0.0116F,-0.5853F,-0.2571F
 };
 
-static void op_shaped_dither16(OggOpusFile *_of,opus_int16 *_dst,
+static int op_float2short_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
  float *_src,int _nsamples,int _nchannels){
-  opus_uint32 seed;
-  int         mute;
+  opus_int16 *dst;
   int         ci;
   int         i;
-  mute=_of->dither_mute;
-  seed=_of->dither_seed;
-  if(_of->state_channel_count!=_nchannels){
-    mute=65;
+  dst=(opus_int16 *)_dst;
+  if(OP_UNLIKELY(_nsamples*_nchannels>_dst_sz))_nsamples=_dst_sz/_nchannels;
 # if defined(OP_SOFT_CLIP)
+  if(_of->state_channel_count!=_nchannels){
     for(ci=0;ci<_nchannels;ci++)_of->clip_state[ci]=0;
-# endif
   }
-# if defined(OP_SOFT_CLIP)
   opus_pcm_soft_clip(_src,_nsamples,_nchannels,_of->clip_state);
 # endif
-  /*In order to avoid replacing digital silence with quiet dither noise, we
-     mute if the output has been silent for a while.*/
-  if(mute>64)memset(_of->dither_a,0,sizeof(*_of->dither_a)*4*_nchannels);
-  for(i=0;i<_nsamples;i++){
-    int silent;
-    silent=1;
-    for(ci=0;ci<_nchannels;ci++){
-      float r;
-      float s;
-      float err;
-      int   si;
-      int   j;
-      s=_src[_nchannels*i+ci];
-      silent&=s==0;
-      s*=OP_GAIN;
-      err=0;
-      for(j=0;j<4;j++){
-        err+=OP_FCOEF_B[j]*_of->dither_b[ci*4+j]
-         -OP_FCOEF_A[j]*_of->dither_a[ci*4+j];
-      }
-      for(j=3;j-->0;)_of->dither_a[ci*4+j+1]=_of->dither_a[ci*4+j];
-      for(j=3;j-->0;)_of->dither_b[ci*4+j+1]=_of->dither_b[ci*4+j];
-      _of->dither_a[ci*4]=err;
-      s-=err;
-      if(mute>16)r=0;
-      else{
-        seed=op_rand(seed);
-        r=seed*OP_PRNG_GAIN;
-        seed=op_rand(seed);
-        r-=seed*OP_PRNG_GAIN;
+  if(_of->dither_disabled){
+    for(i=0;i<_nchannels*_nsamples;i++){
+      dst[i]=op_float2int(OP_CLAMP(-32768,32768.0F*_src[i],32767));
+    }
+  }
+  else{
+    opus_uint32 seed;
+    int         mute;
+    seed=_of->dither_seed;
+    mute=_of->dither_mute;
+    if(_of->state_channel_count!=_nchannels)mute=65;
+    /*In order to avoid replacing digital silence with quiet dither noise, we
+       mute if the output has been silent for a while.*/
+    if(mute>64)memset(_of->dither_a,0,sizeof(*_of->dither_a)*4*_nchannels);
+    for(i=0;i<_nsamples;i++){
+      int silent;
+      silent=1;
+      for(ci=0;ci<_nchannels;ci++){
+        float r;
+        float s;
+        float err;
+        int   si;
+        int   j;
+        s=_src[_nchannels*i+ci];
+        silent&=s==0;
+        s*=OP_GAIN;
+        err=0;
+        for(j=0;j<4;j++){
+          err+=OP_FCOEF_B[j]*_of->dither_b[ci*4+j]
+           -OP_FCOEF_A[j]*_of->dither_a[ci*4+j];
+        }
+        for(j=3;j-->0;)_of->dither_a[ci*4+j+1]=_of->dither_a[ci*4+j];
+        for(j=3;j-->0;)_of->dither_b[ci*4+j+1]=_of->dither_b[ci*4+j];
+        _of->dither_a[ci*4]=err;
+        s-=err;
+        if(mute>16)r=0;
+        else{
+          seed=op_rand(seed);
+          r=seed*OP_PRNG_GAIN;
+          seed=op_rand(seed);
+          r-=seed*OP_PRNG_GAIN;
+        }
+        /*Clamp in float out of paranoia that the input will be > 96 dBFS and
+           wrap if the integer is clamped.*/
+        si=op_float2int(OP_CLAMP(-32768,s+r,32767));
+        dst[_nchannels*i+ci]=(opus_int16)si;
+        /*Including clipping in the noise shaping is generally disastrous: the
+           futile effort to restore the clipped energy results in more clipping.
+          However, small amounts---at the level which could normally be created
+           by dither and rounding---are harmless and can even reduce clipping
+           somewhat due to the clipping sometimes reducing the dither + rounding
+           error.*/
+        _of->dither_b[ci*4]=mute>16?0:OP_CLAMP(-1.5F,si-s,1.5F);
       }
-      /*Clamp in float out of paranoia that the input will be > 96 dBFS and
-         wrap if the integer is clamped.*/
-      si=op_float2int(OP_CLAMP(-32768,s+r,32767));
-      _dst[_nchannels*i+ci]=(opus_int16)si;
-      /*Including clipping in the noise shaping is generally disastrous: the
-         futile effort to restore the clipped energy results in more clipping.
-        However, small amounts---at the level which could normally be created
-         by dither and rounding---are harmless and can even reduce clipping
-         somewhat due to the clipping sometimes reducing the dither + rounding
-         error.*/
-      _of->dither_b[ci*4]=mute>16?0:OP_CLAMP(-1.5F,si-s,1.5F);
-    }
-    mute++;
-    if(!silent)mute=0;
-  }
-  _of->dither_mute=OP_MIN(mute,65);
-  _of->dither_seed=seed;
+      mute++;
+      if(!silent)mute=0;
+    }
+    _of->dither_mute=OP_MIN(mute,65);
+    _of->dither_seed=seed;
+  }
   _of->state_channel_count=_nchannels;
-}
-
-static int op_float2short_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
- op_sample *_src,int _nsamples,int _nchannels){
-  opus_int16 *dst;
-  dst=(opus_int16 *)_dst;
-  if(OP_UNLIKELY(_nsamples*_nchannels>_dst_sz))_nsamples=_dst_sz/_nchannels;
-  op_shaped_dither16(_of,dst,_src,_nsamples,_nchannels);
   return _nsamples;
 }
 
 int op_read(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size,int *_li){
-  return op_read_native_filter(_of,_pcm,_buf_size,op_float2short_filter,_li);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_float2short_filter,_li);
 }
 
 int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){
@@ -3002,41 +3100,6 @@ int op_read_float(OggOpusFile *_of,float *_pcm,int _buf_size,int *_li){
   return op_read_native(_of,_pcm,_buf_size,_li);
 }
 
-/*Matrices for downmixing from the supported channel counts to stereo.
-  The matrices with 5 or more channels are normalized to a total volume of 2.0,
-   since most mixes sound too quiet if normalized to 1.0 (as there is generally
-   little volume in the side/rear channels).*/
-static const float OP_STEREO_DOWNMIX[OP_NCHANNELS_MAX-2][OP_NCHANNELS_MAX][2]={
-  /*3.0*/
-  {
-    {0.5858F,0.0F},{0.4142F,0.4142F},{0.0F,0.5858F}
-  },
-  /*quadrophonic*/
-  {
-    {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
-  },
-  /*5.0*/
-  {
-    {0.651F,0.0F},{0.46F,0.46F},{0.0F,0.651F},{0.5636F,0.3254F},
-    {0.3254F,0.5636F}
-  },
-  /*5.1*/
-  {
-    {0.529F,0.0F},{0.3741F,0.3741F},{0.0F,0.529F},{0.4582F,0.2645F},
-    {0.2645F,0.4582F},{0.3741F,0.3741F}
-  },
-  /*6.1*/
-  {
-    {0.4553F,0.0F},{0.322F,0.322F},{0.0F,0.4553F},{0.3943F,0.2277F},
-    {0.2277F,0.3943F},{0.2788F,0.2788F},{0.322F,0.322F}
-  },
-  /*7.1*/
-  {
-    {0.3886F,0.0F},{0.2748F,0.2748F},{0.0F,0.3886F},{0.3366F,0.1943F},
-    {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F},{0.2748F,0.2748F}
-  }
-};
-
 static int op_stereo_filter(OggOpusFile *_of,void *_dst,int _dst_sz,
  op_sample *_src,int _nsamples,int _nchannels){
   (void)_of;
@@ -3071,30 +3134,30 @@ static int op_float2short_stereo_filter(OggOpusFile *_of,
  void *_dst,int _dst_sz,op_sample *_src,int _nsamples,int _nchannels){
   opus_int16 *dst;
   dst=(opus_int16 *)_dst;
-  _nsamples=OP_MIN(_nsamples,_dst_sz>>1);
   if(_nchannels==1){
     int i;
-    op_shaped_dither16(_of,dst,_src,_nsamples,1);
+    _nsamples=op_float2short_filter(_of,dst,_dst_sz>>1,_src,_nsamples,1);
     for(i=_nsamples;i-->0;)dst[2*i+0]=dst[2*i+1]=dst[i];
   }
   else{
     if(_nchannels>2){
+      _nsamples=OP_MIN(_nsamples,_dst_sz>>1);
       _nsamples=op_stereo_filter(_of,_src,_nsamples*2,
        _src,_nsamples,_nchannels);
     }
-    op_shaped_dither16(_of,dst,_src,_nsamples,2);
+    _nsamples=op_float2short_filter(_of,dst,_dst_sz,_src,_nsamples,2);
   }
   return _nsamples;
 }
 
 int op_read_stereo(OggOpusFile *_of,opus_int16 *_pcm,int _buf_size){
-  return op_read_native_filter(_of,_pcm,_buf_size,
+  return op_filter_read_native(_of,_pcm,_buf_size,
    op_float2short_stereo_filter,NULL);
 }
 
 int op_read_float_stereo(OggOpusFile *_of,float *_pcm,int _buf_size){
   _of->state_channel_count=0;
-  return op_read_native_filter(_of,_pcm,_buf_size,op_stereo_filter,NULL);
+  return op_filter_read_native(_of,_pcm,_buf_size,op_stereo_filter,NULL);
 }
 
 #endif
diff --git a/MP/code/opusfile-0.4/src/stream.c b/SP/code/opusfile-0.5/src/stream.c
similarity index 98%
rename from MP/code/opusfile-0.4/src/stream.c
rename to SP/code/opusfile-0.5/src/stream.c
index 8bcdf00..0238a6b 100644
--- a/MP/code/opusfile-0.4/src/stream.c
+++ b/SP/code/opusfile-0.5/src/stream.c
@@ -141,7 +141,7 @@ static wchar_t *op_utf8_to_utf16(const char *_src){
   size_t   len;
   len=strlen(_src);
   /*Worst-case output is 1 wide character per 1 input character.*/
-  dst=(wchar_t *)malloc(sizeof(*dst)*(len+1));
+  dst=(wchar_t *)_ogg_malloc(sizeof(*dst)*(len+1));
   if(dst!=NULL){
     size_t si;
     size_t di;
@@ -162,7 +162,7 @@ static wchar_t *op_utf8_to_utf16(const char *_src){
           if((c0&0xE0)==0xC0){
             wchar_t w;
             /*Start byte says this is a 2-byte sequence.*/
-            w=c0&0x1F<<6|c1&0x3F;
+            w=(c0&0x1F)<<6|c1&0x3F;
             if(w>=0x80U){
               /*This is a 2-byte sequence that is not overlong.*/
               dst[di++]=w;
@@ -218,7 +218,7 @@ static wchar_t *op_utf8_to_utf16(const char *_src){
         }
       }
       /*If we got here, we encountered an illegal UTF-8 sequence.*/
-      free(dst);
+      _ogg_free(dst);
       return NULL;
     }
     OP_ASSERT(di<=len);
@@ -244,8 +244,8 @@ void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
     if(wmode==NULL)errno=EINVAL;
     else if(wpath==NULL)errno=ENOENT;
     else fp=_wfopen(wpath,wmode);
-    free(wmode);
-    free(wpath);
+    _ogg_free(wmode);
+    _ogg_free(wpath);
   }
 #endif
   if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
@@ -275,8 +275,8 @@ void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode,
     if(wmode==NULL)errno=EINVAL;
     else if(wpath==NULL)errno=ENOENT;
     else fp=_wfreopen(wpath,wmode,(FILE *)_stream);
-    free(wmode);
-    free(wpath);
+    _ogg_free(wmode);
+    _ogg_free(wpath);
   }
 #endif
   if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
diff --git a/SP/code/opusfile-0.4/src/wincerts.c b/SP/code/opusfile-0.5/src/wincerts.c
similarity index 100%
rename from SP/code/opusfile-0.4/src/wincerts.c
rename to SP/code/opusfile-0.5/src/wincerts.c
diff --git a/SP/code/opusfile-0.5/src/winerrno.h b/SP/code/opusfile-0.5/src/winerrno.h
new file mode 100644
index 0000000..32a90b4
--- /dev/null
+++ b/SP/code/opusfile-0.5/src/winerrno.h
@@ -0,0 +1,90 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
+ *                                                                  *
+ * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012                *
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
+ *                                                                  *
+ ********************************************************************/
+#if !defined(_opusfile_winerrno_h)
+# define _opusfile_winerrno_h (1)
+
+# include <errno.h>
+# include <winerror.h>
+
+/*These conflict with the MSVC errno.h definitions, but we don't need to use
+   the original ones in any file that deals with sockets.
+  We could map the WSA errors to the errno.h ones (most of which are only
+   available on sufficiently new versions of MSVC), but they aren't ordered the
+   same, and given how rarely we actually look at the values, I don't think
+   it's worth a lookup table.*/
+# undef EWOULDBLOCK
+# undef EINPROGRESS
+# undef EALREADY
+# undef ENOTSOCK
+# undef EDESTADDRREQ
+# undef EMSGSIZE
+# undef EPROTOTYPE
+# undef ENOPROTOOPT
+# undef EPROTONOSUPPORT
+# undef EOPNOTSUPP
+# undef EAFNOSUPPORT
+# undef EADDRINUSE
+# undef EADDRNOTAVAIL
+# undef ENETDOWN
+# undef ENETUNREACH
+# undef ENETRESET
+# undef ECONNABORTED
+# undef ECONNRESET
+# undef ENOBUFS
+# undef EISCONN
+# undef ENOTCONN
+# undef ETIMEDOUT
+# undef ECONNREFUSED
+# undef ELOOP
+# undef ENAMETOOLONG
+# undef EHOSTUNREACH
+# undef ENOTEMPTY
+
+# define EWOULDBLOCK     (WSAEWOULDBLOCK-WSABASEERR)
+# define EINPROGRESS     (WSAEINPROGRESS-WSABASEERR)
+# define EALREADY        (WSAEALREADY-WSABASEERR)
+# define ENOTSOCK        (WSAENOTSOCK-WSABASEERR)
+# define EDESTADDRREQ    (WSAEDESTADDRREQ-WSABASEERR)
+# define EMSGSIZE        (WSAEMSGSIZE-WSABASEERR)
+# define EPROTOTYPE      (WSAEPROTOTYPE-WSABASEERR)
+# define ENOPROTOOPT     (WSAENOPROTOOPT-WSABASEERR)
+# define EPROTONOSUPPORT (WSAEPROTONOSUPPORT-WSABASEERR)
+# define ESOCKTNOSUPPORT (WSAESOCKTNOSUPPORT-WSABASEERR)
+# define EOPNOTSUPP      (WSAEOPNOTSUPP-WSABASEERR)
+# define EPFNOSUPPORT    (WSAEPFNOSUPPORT-WSABASEERR)
+# define EAFNOSUPPORT    (WSAEAFNOSUPPORT-WSABASEERR)
+# define EADDRINUSE      (WSAEADDRINUSE-WSABASEERR)
+# define EADDRNOTAVAIL   (WSAEADDRNOTAVAIL-WSABASEERR)
+# define ENETDOWN        (WSAENETDOWN-WSABASEERR)
+# define ENETUNREACH     (WSAENETUNREACH-WSABASEERR)
+# define ENETRESET       (WSAENETRESET-WSABASEERR)
+# define ECONNABORTED    (WSAECONNABORTED-WSABASEERR)
+# define ECONNRESET      (WSAECONNRESET-WSABASEERR)
+# define ENOBUFS         (WSAENOBUFS-WSABASEERR)
+# define EISCONN         (WSAEISCONN-WSABASEERR)
+# define ENOTCONN        (WSAENOTCONN-WSABASEERR)
+# define ESHUTDOWN       (WSAESHUTDOWN-WSABASEERR)
+# define ETOOMANYREFS    (WSAETOOMANYREFS-WSABASEERR)
+# define ETIMEDOUT       (WSAETIMEDOUT-WSABASEERR)
+# define ECONNREFUSED    (WSAECONNREFUSED-WSABASEERR)
+# define ELOOP           (WSAELOOP-WSABASEERR)
+# define ENAMETOOLONG    (WSAENAMETOOLONG-WSABASEERR)
+# define EHOSTDOWN       (WSAEHOSTDOWN-WSABASEERR)
+# define EHOSTUNREACH    (WSAEHOSTUNREACH-WSABASEERR)
+# define ENOTEMPTY       (WSAENOTEMPTY-WSABASEERR)
+# define EPROCLIM        (WSAEPROCLIM-WSABASEERR)
+# define EUSERS          (WSAEUSERS-WSABASEERR)
+# define EDQUOT          (WSAEDQUOT-WSABASEERR)
+# define ESTALE          (WSAESTALE-WSABASEERR)
+# define EREMOTE         (WSAEREMOTE-WSABASEERR)
+
+#endif

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/iortcw.git



More information about the Pkg-games-commits mailing list