Contributions/ideas Thomas Orgis era (includes backports from mhipp trunk):
+Vitaly Kirsanov <krokoziabla@gmail.com>: ports/cmake (optional CMake build)
Won-Kyu Park <wkpark@gmail.com>: patch to get rid of asm textrels (x86 PIC)
Michael Weiser <michaelweiser@users.sf.net>: update of coreaudio output to AudioComponents API
Bent Bisballe Nyeng <bbn@dynastrom.com>: patch for MPG123_NO_PEEK_END and MPG123_FORCE_SEEKABLE
(from post-0.59 changes that yet have to go into new trunk:)
Hans Schwengeler <schweng@astro.unibas.ch>: audio_dec additions
-Wojciech Barañski's Mp3Play (check the tools folder): Mp3Play frontend
+Wojciech Barañski's Mp3Play (check the tools folder): Mp3Play frontend
Daniel Koukola: audio_oss.c patch
Munechika SUMIKAWA <sumikawa@ebina.hitachi.co.jp>: IPv6
TEMNOTA <temnota@kmv.ru>: HTTP,FTP patch/playlist fix
3.2 Advanced Console Usage
You can specify the option -C to enable a terminal control interface enabling to influence playback on current title/playlist by pressing some key:
-
-= terminal control keys =-
-[s] or [ ] interrupt/restart playback (i.e. 'pause')
+[s] or [ ] interrupt/restart playback (i.e. '(un)pause')
[f] next track
[d] previous track
+[]] next directory (next track until directory part changes)
+[[] previous directory (previous track until directory part changes)
[b] back to beginning of track
-[p] pause while looping current sound chunk
+[p] loop around current position (don't combine with output buffer)
[.] forward
[,] rewind
[:] fast forward
[<] fine rewind
[+] volume up
[-] volume down
+[u] (un)mute volume
[r] RVA switch
[v] verbose switch
[l] list current playlist, indicating current track there
[t] display tag info (again)
[m] print MPEG header info (again)
+[c] or [C] pitch up (small step, big step)
+[x] or [X] pitch down (small step, big step)
+[w] reset pitch to zero
+[k] print out current position in playlist and track, for the benefit of some external tool to store bookmarks
[h] this help
[q] quit
The mpg123 code is determined to keep it's legacy. A legacy of old, old UNIX.
So anything possibly somewhat advanced should be considered to be put here, with proper #ifdef;-)
- copyright 2007-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright 2007-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis, Windows Unicode stuff by JonY.
*/
#include <shlwapi.h>
#endif
-#ifdef USE_MODULES
-# ifdef HAVE_DLFCN_H
-# include <dlfcn.h>
-# endif
-#endif
-
#include "debug.h"
#ifndef WINDOWS_UWP
return ret;
}
-#ifdef WANT_WIN32_UNICODE
-
-/* Convert unix UTF-8 (or ASCII) paths to Windows wide character paths. */
-static wchar_t* u2wpath(const char *upath)
-{
- wchar_t* wpath, *p;
- if(!upath || win32_utf8_wide(upath, &wpath, NULL) < 1)
- return NULL;
- for(p=wpath; *p; ++p)
- if(*p == L'/')
- *p = L'\\';
- return wpath;
-}
-
-/* Convert Windows wide character paths to unix UTF-8. */
-static char* w2upath(const wchar_t *wpath)
-{
- char* upath, *p;
- if(!wpath || win32_wide_utf8(wpath, &upath, NULL) < 1)
- return NULL;
- for(p=upath; *p; ++p)
- if(*p == '\\')
- *p = '/';
- return upath;
-}
-
-/* An absolute path that is too long and not already marked with
- \\?\ can be marked as a long one and still work. */
-static int wpath_need_elongation(wchar_t *wpath)
-{
- if( wpath && !PathIsRelativeW(wpath)
- && wcslen(wpath) > MAX_PATH-1
- && wcsncmp(L"\\\\?\\", wpath, 4) )
- return 1;
- else
- return 0;
-}
-
-/* Take any wide windows path and turn it into a path that is allowed
- to be longer than MAX_PATH, if it is not already. */
-static wchar_t* wlongpath(wchar_t *wpath)
-{
- size_t len, plen;
- const wchar_t *prefix = L"";
- wchar_t *wlpath = NULL;
- if(!wpath)
- return NULL;
-
- /* Absolute paths that do not start with \\?\ get that prepended
- to allow them being long. */
- if(!PathIsRelativeW(wpath) && wcsncmp(L"\\\\?\\", wpath, 4))
- {
- if(wcslen(wpath) >= 2 && PathIsUNCW(wpath))
- {
- /* \\server\path -> \\?\UNC\server\path */
- prefix = L"\\\\?\\UNC";
- ++wpath; /* Skip the first \. */
- }
- else /* c:\some/path -> \\?\c:\some\path */
- prefix = L"\\\\?\\";
- }
- plen = wcslen(prefix);
- len = plen + wcslen(wpath);
- wlpath = malloc(len+1*sizeof(wchar_t));
- if(wlpath)
- {
- /* Brute force memory copying, swprintf is too dandy. */
- memcpy(wlpath, prefix, sizeof(wchar_t)*plen);
- memcpy(wlpath+plen, wpath, sizeof(wchar_t)*(len-plen));
- wlpath[len] = 0;
- }
- return wlpath;
-}
-
-/* Convert unix path to wide windows path, optionally marking
- it as long path if necessary. */
-static wchar_t* u2wlongpath(const char *upath)
-{
- wchar_t *wpath = NULL;
- wchar_t *wlpath = NULL;
- wpath = u2wpath(upath);
- if(wpath_need_elongation(wpath))
- {
- wlpath = wlongpath(wpath);
- free(wpath);
- wpath = wlpath;
- }
- return wpath;
-}
-
#endif
-#else
-
-static wchar_t* u2wlongpath(const char *upath)
-{
- wchar_t* wpath, *p;
- if (!upath || win32_utf8_wide(upath, &wpath, NULL) < 1)
- return NULL;
- for (p = wpath; *p; ++p)
- if (*p == L'/')
- *p = L'\\';
- return wpath;
-}
-
-#endif
+#include "wpathconv.h"
/* Always add a default permission mask in case of flags|O_CREAT. */
int compat_open(const char *filename, int flags)
#endif
-#ifdef USE_MODULES
-/*
- This is what I expected the platform-specific dance for dynamic module
- support to be. Little did I know about the peculiarities of (long)
- paths and directory/file search on Windows.
-*/
-
-void *compat_dlopen(const char *path)
-{
- void *handle = NULL;
-#ifdef WANT_WIN32_UNICODE
- wchar_t *wpath;
- wpath = u2wlongpath(path);
- if(wpath)
- handle = LoadLibraryW(wpath);
- free(wpath);
-#else
- handle = dlopen(path, RTLD_NOW);
-#endif
- return handle;
-}
-
-void *compat_dlsym(void *handle, const char *name)
-{
- void *sym = NULL;
- if(!handle)
- return NULL;
-#ifdef WANT_WIN32_UNICODE
- sym = GetProcAddress(handle, name);
-#else
- sym = dlsym(handle, name);
-#endif
- return sym;
-}
+// Revisit logic of write():
+// Return -1 if interrupted before any data was written,
+// set errno to EINTR. Any other error value is serious
+// for blocking I/O, which we assume here. EAGAIN should be
+// handed through.
+// Reaction to zero-sized write attempts could also be funky, so avoid that.
+// May return short count for various reasons. I assume that
+// any serious condition will show itself as return value -1
+// eventually.
+
+// These uninterruptible write/read functions shall persist as long as
+// possible to finish the desired operation. A short byte count is short
+// because of a serious reason (maybe EOF, maybe out of disk space). You
+// can inspect errno.
-void compat_dlclose(void *handle)
-{
- if(!handle)
- return;
-#ifdef WANT_WIN32_UNICODE
- FreeLibrary(handle);
-#else
- dlclose(handle);
-#endif
-}
-
-#endif /* USE_MODULES */
-
-
-/* This shall survive signals and any return value less than given byte count
- is an error */
size_t unintr_write(int fd, void const *buffer, size_t bytes)
{
size_t written = 0;
+ errno = 0;
while(bytes)
{
+ errno = 0;
ssize_t part = write(fd, (char*)buffer+written, bytes);
- if(part < 0 && errno != EINTR)
+ // Just on short writes, we do not abort. Only when
+ // there was no successful operation (even zero write) at all.
+ // Any other error than EINTR ends things here.
+ if(part >= 0)
+ {
+ bytes -= part;
+ written += part;
+ } else if(errno != EINTR)
break;
- bytes -= part;
- written += part;
}
return written;
}
size_t unintr_read(int fd, void *buffer, size_t bytes)
{
size_t got = 0;
+ errno = 0;
while(bytes)
{
+ errno = 0;
ssize_t part = read(fd, (char*)buffer+got, bytes);
- if(part < 0 && errno != EINTR)
+ if(part >= 0)
+ {
+ bytes -= part;
+ got += part;
+ } else if(errno != EINTR)
break;
- bytes -= part;
- got += part;
}
return got;
}
+// and again for streams
+size_t unintr_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ size_t written = 0;
+ errno = 0;
+ while(size && nmemb)
+ {
+ errno = 0;
+ size_t part = fwrite((char*)ptr+written*size, size, nmemb, stream);
+ if(part > 0)
+ {
+ nmemb -= part;
+ written += part;
+ } else if(errno != EINTR)
+ break;
+ }
+ return written;
+}
+
#ifndef NO_CATCHSIGNAL
#if (!defined(WIN32) || defined (__CYGWIN__)) && defined(HAVE_SIGNAL_H)
void (*catchsignal(int signum, void(*handler)()))()
#include "config.h"
#include "intsym.h"
-/* For --nagging compilation with -std=c89, we need
- to disable the inline keyword. */
-#ifdef PLAIN_C89
+/* Disable inline for non-C99 compilers. */
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
#ifndef inline
#define inline
#endif
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
+#ifndef SSIZE_MAX
+#define SSIZE_MAX ((size_t)-1/2)
+#endif
#ifndef ULONG_MAX
#define ULONG_MAX ((unsigned long)-1)
#endif
/* If we have the size checks enabled, try to derive some sane printfs.
Simple start: Use max integer type and format if long is not big enough.
I am hesitating to use %ll without making sure that it's there... */
-#if !(defined PLAIN_C89) && (defined SIZEOF_OFF_T) && (SIZEOF_OFF_T > SIZEOF_LONG) && (defined PRIiMAX)
+#if (defined SIZEOF_OFF_T) && (SIZEOF_OFF_T > SIZEOF_LONG) && (defined PRIiMAX)
# define OFF_P PRIiMAX
typedef intmax_t off_p;
#else
typedef long off_p;
#endif
-#if !(defined PLAIN_C89) && (defined SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > SIZEOF_LONG) && (defined PRIuMAX)
+#if (defined SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > SIZEOF_LONG) && (defined PRIuMAX)
# define SIZE_P PRIuMAX
typedef uintmax_t size_p;
#else
typedef unsigned long size_p;
#endif
-#if !(defined PLAIN_C89) && (defined SIZEOF_SSIZE_T) && (SIZEOF_SSIZE_T > SIZEOF_LONG) && (defined PRIiMAX)
+#if (defined SIZEOF_SSIZE_T) && (SIZEOF_SSIZE_T > SIZEOF_LONG) && (defined PRIiMAX)
# define SSIZE_P PRIuMAX
typedef intmax_t ssize_p;
#else
#endif
/* Blocking write/read of data with signal resilience.
- Both continue after being interrupted by signals and always return the
+ They continue after being interrupted by signals and always return the
amount of processed data (shortage indicating actual problem or EOF). */
size_t unintr_write(int fd, void const *buffer, size_t bytes);
size_t unintr_read (int fd, void *buffer, size_t bytes);
+size_t unintr_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
/* That one comes from Tellie on OS/2, needed in resolver. */
#ifdef __KLIBC__
#define catchsignal INT123_catchsignal
#define safe_realloc INT123_safe_realloc
#define compat_strdup INT123_compat_strdup
+#define compat_getenv INT123_compat_getenv
#define compat_open INT123_compat_open
#define compat_fopen INT123_compat_fopen
#define compat_fdopen INT123_compat_fdopen
#define compat_fclose INT123_compat_fclose
#define win32_wide_utf8 INT123_win32_wide_utf8
#define win32_utf8_wide INT123_win32_utf8_wide
+#define compat_catpath INT123_compat_catpath
+#define compat_isdir INT123_compat_isdir
+#define compat_diropen INT123_compat_diropen
+#define compat_dirclose INT123_compat_dirclose
+#define compat_nextfile INT123_compat_nextfile
+#define compat_nextdir INT123_compat_nextdir
+#define compat_dlopen INT123_compat_dlopen
+#define compat_dlsym INT123_compat_dlsym
+#define compat_dlclose INT123_compat_dlclose
#define unintr_write INT123_unintr_write
#define unintr_read INT123_unintr_read
+#define unintr_fwrite INT123_unintr_fwrite
#define ntom_set_ntom INT123_ntom_set_ntom
#define synth_1to1 INT123_synth_1to1
#define synth_1to1_dither INT123_synth_1to1_dither
#define frame_set_seek INT123_frame_set_seek
#define frame_tell_seek INT123_frame_tell_seek
#define frame_fill_toc INT123_frame_fill_toc
-#define getbits INT123_getbits
#define getcpuflags INT123_getcpuflags
#define icy2utf8 INT123_icy2utf8
#define init_icy INT123_init_icy
#define fi_add INT123_fi_add
#define fi_set INT123_fi_set
#define fi_reset INT123_fi_reset
-#define double_to_long_rounded INT123_double_to_long_rounded
-#define scale_rounded INT123_scale_rounded
#define decode_update INT123_decode_update
#define decoder_synth_bytes INT123_decoder_synth_bytes
#define samples_to_bytes INT123_samples_to_bytes
#define bytes_to_samples INT123_bytes_to_samples
#define outblock_bytes INT123_outblock_bytes
#define postprocess_buffer INT123_postprocess_buffer
+#define open_fixed_pre INT123_open_fixed_pre
+#define open_fixed_post INT123_open_fixed_post
#define frame_cpu_opt INT123_frame_cpu_opt
#define set_synth_functions INT123_set_synth_functions
#define dectype INT123_dectype
Variadic macros are a C99 feature...
Now just predefining stuff non-variadic for up to 15 arguments.
It's cumbersome to have them all with different names, though...
+
+ Update: Also adding variadic macros now. We star to use some C99.
*/
#ifdef ME
#ifdef DEBUG
#include <stdio.h>
+
+// The future (from about 20 years ago;-):
+#define mdebug(s, ...) fprintf(stderr, DBGPRFX "[" __FILE__ ":%i] debug: " s "\n", __LINE__, __VA_ARGS__)
+
#define debug(s) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] debug: " s "\n", __LINE__)
#define debug1(s, a) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] debug: " s "\n", __LINE__, a)
#define debug2(s, a, b) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] debug: " s "\n", __LINE__, a, b)
#define debug14(s, a, b, c, d, e, f, g, h, i, j, k, l, m, n) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] debug: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n)
#define debug15(s, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] debug: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
#else
+
+#define mdebug(s, ...)
+
#define debug(s)
#define debug1(s, a)
#define debug2(s, a, b)
/* warning macros also here... */
#ifndef NO_WARNING
+
+#define mwarning(s, ...) fprintf(stderr, DBGPRFX "[" __FILE__ ":%i] warning: " s "\n", __LINE__, __VA_ARGS__)
+
#define warning(s) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] warning: " s "\n", __LINE__)
#define warning1(s, a) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] warning: " s "\n", __LINE__, a)
#define warning2(s, a, b) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] warning: " s "\n", __LINE__, a, b)
#define warning14(s, a, b, c, d, e, f, g, h, i, j, k, l, m, n) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] warning: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n)
#define warning15(s, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] warning: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
#else
+#define mwarning(s, ...)
#define warning(s)
#define warning1(s, a)
#define warning2(s, a, b)
#endif
/* error macros also here... */
-#ifndef NO_ERROR
+#ifndef NO_ERRORMSG
+
+#define merror(s, ...) fprintf(stderr, DBGPRFX "[" __FILE__ ":%i] error: " s "\n", __LINE__, __VA_ARGS__)
+
#define error(s) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] error: " s "\n", __LINE__)
#define error1(s, a) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] error: " s "\n", __LINE__, a)
#define error2(s, a, b) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] error: " s "\n", __LINE__, a, b)
#define error14(s, a, b, c, d, e, f, g, h, i, j, k, l, m, n) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] error: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n)
#define error15(s, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] error: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
#else
+#define merror(s, ...)
#define error(s)
#define error1(s, a)
#define error2(s, a, b)
/* ereturn macros also here... */
#ifndef NO_ERETURN
+
+#define mereturn(rv, s, ...) do{ fprintf(stderr, DBGPRFX "[" __FILE__ ":%i] ereturn: " s "\n", __LINE__, __VA_ARGS__); return rv; }while(0)
+
#define ereturn(rv, s) do{ fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] ereturn: " s "\n", __LINE__); return rv; }while(0)
#define ereturn1(rv, s, a) do{ fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] ereturn: " s "\n", __LINE__, a); return rv; }while(0)
#define ereturn2(rv, s, a, b) do{ fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] ereturn: " s "\n", __LINE__, a, b); return rv; }while(0)
#define ereturn14(rv, s, a, b, c, d, e, f, g, h, i, j, k, l, m, n) do{ fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] ereturn: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n); return rv; }while(0)
#define ereturn15(rv, s, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) do{ fprintf(stderr, DBGPRFX"[" __FILE__ ":%i] ereturn: " s "\n", __LINE__, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o); return rv; }while(0)
#else
+#define mereturn(rv, s, ...) return rv
#define ereturn(rv, s) return rv
#define ereturn1(rv, s, a) return rv
#define ereturn2(rv, s, a, b) return rv
#include "mpg123lib_intern.h"
-int mpg123_feature(const enum mpg123_feature_set key)
+int attribute_align_arg mpg123_feature2(int key)
+{
+ return mpg123_feature(key);
+}
+
+int attribute_align_arg mpg123_feature(const enum mpg123_feature_set key)
{
switch(key)
{
return 1;
#endif /* mpg123_output_32bit */
+ case MPG123_FEATURE_OUTPUT_FLOAT32:
+#if defined(NO_REAL) || defined(REAL_IS_DOUBLE)
+ return 0;
+#else
+ return 1;
+#endif
+
+ case MPG123_FEATURE_OUTPUT_FLOAT64:
+#if defined(NO_REAL) || !defined(REAL_IS_DOUBLE)
+ return 0;
+#else
+ return 1;
+#endif
+
case MPG123_FEATURE_PARSE_ID3V2:
#ifdef NO_ID3V2
return 0;
#else
return 0;
#endif
+ case MPG123_FEATURE_MOREINFO:
+#ifndef NO_MOREINFO
+ return 1;
+#else
+ return 0;
+#endif
default: return 0;
}
separate header just for audio format definitions not tied to
library code
- copyright 1995-2015 by the mpg123 project
+ copyright 1995-2020 by the mpg123 project
free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
*/
* \return size of one sample in bytes
*/
#define MPG123_SAMPLESIZE(enc) ( \
- (enc) & MPG123_ENC_8 \
- ? 1 \
- : ( (enc) & MPG123_ENC_16 \
- ? 2 \
- : ( (enc) & MPG123_ENC_24 \
- ? 3 \
- : ( ( (enc) & MPG123_ENC_32 \
- || (enc) == MPG123_ENC_FLOAT_32 ) \
- ? 4 \
- : ( (enc) == MPG123_ENC_FLOAT_64 \
- ? 8 \
- : 0 \
-) ) ) ) )
+ (enc) < 1 \
+ ? 0 \
+ : ( (enc) & MPG123_ENC_8 \
+ ? 1 \
+ : ( (enc) & MPG123_ENC_16 \
+ ? 2 \
+ : ( (enc) & MPG123_ENC_24 \
+ ? 3 \
+ : ( ( (enc) & MPG123_ENC_32 \
+ || (enc) == MPG123_ENC_FLOAT_32 ) \
+ ? 4 \
+ : ( (enc) == MPG123_ENC_FLOAT_64 \
+ ? 8 \
+ : 0 \
+) ) ) ) ) )
+
+/** Representation of zero in differing encodings.
+ * This exists to define proper silence in various encodings without
+ * having to link to libsyn123 to do actual conversions at runtime.
+ * You have to handle big/little endian order yourself, though.
+ * This takes the shortcut that any signed encoding has a zero with
+ * all-zero bits. Unsigned linear encodings just have the highest bit set
+ * (2^(n-1) for n bits), while the nonlinear 8-bit ones are special.
+ * \param enc the encoding (mpg123_enc_enum value)
+ * \param siz bytes per sample (return value of MPG123_SAMPLESIZE(enc))
+ * \param off byte (octet) offset counted from LSB
+ * \return unsigned byte value for the designated octet
+ */
+#define MPG123_ZEROSAMPLE(enc, siz, off) ( \
+ (enc) == MPG123_ENC_ULAW_8 \
+ ? (off == 0 ? 0xff : 0x00) \
+ : ( (enc) == MPG123_ENC_ALAW_8 \
+ ? (off == 0 ? 0xd5 : 0x00) \
+ : ( (((enc) & (MPG123_ENC_SIGNED|MPG123_ENC_FLOAT)) || (siz) != ((off)+1)) \
+ ? 0x00 \
+ : 0x80 \
+ ) ) )
/** Structure defining an audio format.
* Providing the members as individual function arguments to define a certain
/*
- format:routines to deal with audio (output) format
+ format: routines to deal with audio (output) format
- copyright 2008-14 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright 2008-20 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis, starting with parts of the old audio.c, with only faintly manage to show now
*/
#include "mpg123lib_intern.h"
+#include "sample.h"
#include "debug.h"
/* static int chans[NUM_CHANNELS] = { 1 , 2 }; */
static const int enc_float_range[2] = { 6, 8 };
/* same for 8 bit encodings */
static const int enc_8bit_range[2] = { 8, 12 };
+// for 24 bit quality (24 and 32 bit integers)
+static const int enc_24bit_range[2] = { 2, 6 };
+// for completeness, the 16 bits
+static const int enc_16bit_range[2] = { 0, 2};
/*
Only one type of float is supported.
return -1;
}
-static int cap_fit(mpg123_handle *fr, struct audioformat *nf, int f0, int f2)
+static int cap_fit(mpg123_pars *p, struct audioformat *nf, int f0, int f2)
{
int i;
int c = nf->channels-1;
- int rn = rate2num(&fr->p, nf->rate);
+ int rn = rate2num(p, nf->rate);
if(rn >= 0) for(i=f0;i<f2;i++)
{
- if(fr->p.audio_caps[c][rn][i])
+ if(p->audio_caps[c][rn][i])
{
nf->encoding = my_encodings[i];
return 1;
return 0;
}
-static int freq_fit(mpg123_handle *fr, struct audioformat *nf, int f0, int f2)
+static int imin(int a, int b)
{
- nf->rate = frame_freq(fr)>>fr->p.down_sample;
- if(cap_fit(fr,nf,f0,f2)) return 1;
- if(fr->p.flags & MPG123_AUTO_RESAMPLE)
- {
- nf->rate>>=1;
- if(cap_fit(fr,nf,f0,f2)) return 1;
- nf->rate>>=1;
- if(cap_fit(fr,nf,f0,f2)) return 1;
- }
-#ifndef NO_NTOM
- /* If nothing worked, try the other rates, only without constrains from user.
- In case you didn't guess: We enable flexible resampling if we find a working rate. */
- if( fr->p.flags & MPG123_AUTO_RESAMPLE &&
- !fr->p.force_rate && fr->p.down_sample == 0)
- {
- int i;
- int c = nf->channels-1;
- int rn = rate2num(&fr->p, frame_freq(fr));
- int rrn;
- if(rn < 0) return 0;
- /* Try higher rates first. */
- for(i=f0;i<f2;i++) for(rrn=rn+1; rrn<MPG123_RATES; ++rrn)
- if(fr->p.audio_caps[c][rrn][i])
- {
- nf->rate = my_rates[rrn];
- nf->encoding = my_encodings[i];
- return 1;
- }
- /* Then lower rates. */
- for(i=f0;i<f2;i++) for(rrn=rn-1; rrn>=0; --rrn)
- if(fr->p.audio_caps[c][rrn][i])
- {
- nf->rate = my_rates[rrn];
- nf->encoding = my_encodings[i];
- return 1;
- }
- }
-#endif
+ return a < b ? a : b;
+}
- return 0;
+static int imax(int a, int b)
+{
+ return a > b ? a : b;
+}
+
+// Find a possible encoding with given rate and channel count,
+// try differing channel count, too.
+// This updates the given format and returns TRUE if an encoding
+// was found.
+static int enc_chan_fit( mpg123_pars *p, long rate, struct audioformat *nnf
+, int f0, int f2, int try_float )
+{
+#define ENCRANGE(range) imax(f0, range[0]), imin(f2, range[1])
+ struct audioformat nf = *nnf;
+ nf.rate = rate;
+ if(cap_fit(p, &nf, ENCRANGE(enc_16bit_range)))
+ goto eend;
+ if(cap_fit(p, &nf, ENCRANGE(enc_24bit_range)))
+ goto eend;
+ if(try_float &&
+ cap_fit(p, &nf, ENCRANGE(enc_float_range)))
+ goto eend;
+ if(cap_fit(p, &nf, ENCRANGE(enc_8bit_range)))
+ goto eend;
+
+ /* try again with different stereoness */
+ if(nf.channels == 2 && !(p->flags & MPG123_FORCE_STEREO)) nf.channels = 1;
+ else if(nf.channels == 1 && !(p->flags & MPG123_FORCE_MONO)) nf.channels = 2;
+
+ if(cap_fit(p, &nf, ENCRANGE(enc_16bit_range)))
+ goto eend;
+ if(cap_fit(p, &nf, ENCRANGE(enc_24bit_range)))
+ goto eend;
+ if(try_float &&
+ cap_fit(p, &nf, ENCRANGE(enc_float_range)))
+ goto eend;
+ if(cap_fit(p, &nf, ENCRANGE(enc_8bit_range)))
+ goto eend;
+ return FALSE;
+eend:
+ *nnf = nf;
+ return TRUE;
+#undef ENCRANGE
}
/* match constraints against supported audio formats, store possible setup in frame
{
struct audioformat nf;
int f0=0;
- int f2=MPG123_ENCODINGS; /* Omit the 32bit and float encodings. */
+ int f2=MPG123_ENCODINGS+1; // Include all encodings by default.
mpg123_pars *p = &fr->p;
+ int try_float = (p->flags & MPG123_FLOAT_FALLBACK) ? 0 : 1;
/* initialize new format, encoding comes later */
nf.channels = fr->stereo;
- /* All this forcing should be removed in favour of the capabilities table... */
+ // I intended the forcing stuff to be weaved into the format support table,
+ // but this probably will never happen, as this would change library behaviour.
+ // One could introduce an additional effective format table that takes for
+ // forcings into account, but that would have to be updated on any flag
+ // change. Tedious.
if(p->flags & MPG123_FORCE_8BIT)
{
f0 = enc_8bit_range[0];
}
if(p->flags & MPG123_FORCE_FLOAT)
{
+ try_float = 1;
f0 = enc_float_range[0];
f2 = enc_float_range[1];
}
if(p->flags & MPG123_FORCE_MONO) nf.channels = 1;
if(p->flags & MPG123_FORCE_STEREO) nf.channels = 2;
+ // Strategy update: Avoid too early triggering of the NtoM decoder.
+ // Main target is the native rate, with any encoding.
+ // Then, native rate with any channel count and any encoding.
+ // Then, it's down_sample from native rate.
+ // As last resort: NtoM rate.
+ // So the priority is 1. rate 2. channels 3. encoding.
+ // As encodings go, 16 bit is tranditionally preferred as efficient choice.
+ // Next in line are wider float and integer encodings, then 8 bit as
+ // last resort.
+
#ifndef NO_NTOM
if(p->force_rate)
{
- nf.rate = p->force_rate;
- if(cap_fit(fr,&nf,f0,2)) goto end; /* 16bit encodings */
- if(cap_fit(fr,&nf,f0<=2 ? 2 : f0,f2)) goto end; /* 8bit encodings */
-
- /* try again with different stereoness */
- if(nf.channels == 2 && !(p->flags & MPG123_FORCE_STEREO)) nf.channels = 1;
- else if(nf.channels == 1 && !(p->flags & MPG123_FORCE_MONO)) nf.channels = 2;
-
- if(cap_fit(fr,&nf,f0,2)) goto end; /* 16bit encodings */
- if(cap_fit(fr,&nf,f0<=2 ? 2 : f0,f2)) goto end; /* 8bit encodings */
-
- if(NOQUIET)
- error3( "Unable to set up output format! Constraints: %s%s%liHz.",
- ( p->flags & MPG123_FORCE_STEREO ? "stereo, " :
- (p->flags & MPG123_FORCE_MONO ? "mono, " : "") ),
- (p->flags & MPG123_FORCE_8BIT ? "8bit, " : ""),
- p->force_rate );
+ if(enc_chan_fit(p, p->force_rate, &nf, f0, f2, try_float))
+ goto end;
+ // Keep the order consistent if float is considered fallback only.
+ if(!try_float &&
+ enc_chan_fit(p, p->force_rate, &nf, f0, f2, TRUE))
+ goto end;
+
+ merror( "Unable to set up output format! Constraints: %s%s%liHz."
+ , ( p->flags & MPG123_FORCE_STEREO ? "stereo, " :
+ (p->flags & MPG123_FORCE_MONO ? "mono, " : "") )
+ , ( p->flags & MPG123_FORCE_FLOAT ? "float, " :
+ (p->flags & MPG123_FORCE_8BIT ? "8bit, " : "") )
+ , p->force_rate );
/* if(NOQUIET && p->verbose <= 1) print_capabilities(fr); */
fr->err = MPG123_BAD_OUTFORMAT;
return -1;
}
#endif
-
- if(freq_fit(fr, &nf, f0, 2)) goto end; /* try rates with 16bit */
- if(freq_fit(fr, &nf, f0<=2 ? 2 : f0, f2)) goto end; /* ... 8bit */
-
- /* try again with different stereoness */
- if(nf.channels == 2 && !(p->flags & MPG123_FORCE_STEREO)) nf.channels = 1;
- else if(nf.channels == 1 && !(p->flags & MPG123_FORCE_MONO)) nf.channels = 2;
-
- if(freq_fit(fr, &nf, f0, 2)) goto end; /* try rates with 16bit */
- if(freq_fit(fr, &nf, f0<=2 ? 2 : f0, f2)) goto end; /* ... 8bit */
-
- /* Here is the _bad_ end. */
- if(NOQUIET)
+ // Native decoder rate first.
+ if(enc_chan_fit(p, frame_freq(fr)>>p->down_sample, &nf, f0, f2, try_float))
+ goto end;
+ // Then downsamplings.
+ if(p->flags & MPG123_AUTO_RESAMPLE && p->down_sample < 2)
+ {
+ if(enc_chan_fit( p, frame_freq(fr)>>(p->down_sample+1), &nf
+ , f0, f2, try_float ))
+ goto end;
+ if(p->down_sample < 1 && enc_chan_fit( p, frame_freq(fr)>>2, &nf
+ , f0, f2, try_float ))
+ goto end;
+ }
+ // And again the whole deal with float fallback.
+ if(!try_float)
+ {
+ if(enc_chan_fit(p, frame_freq(fr)>>p->down_sample, &nf, f0, f2, TRUE))
+ goto end;
+ // Then downsamplings.
+ if(p->flags & MPG123_AUTO_RESAMPLE && p->down_sample < 2)
+ {
+ if(enc_chan_fit( p, frame_freq(fr)>>(p->down_sample+1), &nf
+ , f0, f2, TRUE ))
+ goto end;
+ if(p->down_sample < 1 && enc_chan_fit( p, frame_freq(fr)>>2, &nf
+ , f0, f2, TRUE ))
+ goto end;
+ }
+ }
+#ifndef NO_NTOM
+ // Try to find any rate that works and resample using NtoM hackery.
+ if( p->flags & MPG123_AUTO_RESAMPLE && fr->p.down_sample == 0)
{
- error5( "Unable to set up output format! Constraints: %s%s%li, %li or %liHz.",
- ( p->flags & MPG123_FORCE_STEREO ? "stereo, " :
- (p->flags & MPG123_FORCE_MONO ? "mono, " : "") ),
- (p->flags & MPG123_FORCE_8BIT ? "8bit, " : ""),
- frame_freq(fr), frame_freq(fr)>>1, frame_freq(fr)>>2 );
+ int i;
+ int rn = rate2num(p, frame_freq(fr));
+ int rrn;
+ if(rn < 0) return 0;
+ /* Try higher rates first. */
+ for(rrn=rn+1; rrn<MPG123_RATES; ++rrn)
+ if(enc_chan_fit(p, my_rates[rrn], &nf, f0, f2, try_float))
+ goto end;
+ /* Then lower rates. */
+ for(i=f0;i<f2;i++) for(rrn=rn-1; rrn>=0; --rrn)
+ if(enc_chan_fit(p, my_rates[rrn], &nf, f0, f2, try_float))
+ goto end;
+ // And again for float fallback.
+ if(!try_float)
+ {
+ /* Try higher rates first. */
+ for(rrn=rn+1; rrn<MPG123_RATES; ++rrn)
+ if(enc_chan_fit(p, my_rates[rrn], &nf, f0, f2, TRUE))
+ goto end;
+ /* Then lower rates. */
+ for(i=f0;i<f2;i++) for(rrn=rn-1; rrn>=0; --rrn)
+ if(enc_chan_fit(p, my_rates[rrn], &nf, f0, f2, TRUE))
+ goto end;
+ }
}
+#endif
+
+ /* Here is the _bad_ end. */
+ merror( "Unable to set up output format! Constraints: %s%s%li, %li or %liHz."
+ , ( p->flags & MPG123_FORCE_STEREO ? "stereo, " :
+ (p->flags & MPG123_FORCE_MONO ? "mono, " : "") )
+ , ( p->flags & MPG123_FORCE_FLOAT ? "float, " :
+ (p->flags & MPG123_FORCE_8BIT ? "8bit, " : "") )
+ , frame_freq(fr), frame_freq(fr)>>1, frame_freq(fr)>>2 );
/* if(NOQUIET && p->verbose <= 1) print_capabilities(fr); */
fr->err = MPG123_BAD_OUTFORMAT;
fr->af.encsize = mpg123_encsize(fr->af.encoding);
if(fr->af.encsize < 1)
{
- if(NOQUIET) error1("Some unknown encoding??? (%i)", fr->af.encoding);
+ error1("Some unknown encoding??? (%i)", fr->af.encoding);
fr->err = MPG123_BAD_OUTFORMAT;
return -1;
return MPG123_OK;
}
+int attribute_align_arg mpg123_format2(mpg123_handle *mh, long rate, int channels, int encodings)
+{
+ int r;
+ if(mh == NULL) return MPG123_BAD_HANDLE;
+ r = mpg123_fmt2(&mh->p, rate, channels, encodings);
+ if(r != MPG123_OK){ mh->err = r; r = MPG123_ERR; }
+
+ return r;
+}
+
+// Keep old behaviour.
int attribute_align_arg mpg123_format(mpg123_handle *mh, long rate, int channels, int encodings)
{
int r;
return r;
}
-int attribute_align_arg mpg123_fmt(mpg123_pars *mp, long rate, int channels, int encodings)
+int attribute_align_arg mpg123_fmt2(mpg123_pars *mp, long rate, int channels, int encodings)
{
- int ie, ic, ratei;
+ int ie, ic, ratei, r1, r2;
int ch[2] = {0, 1};
if(mp == NULL) return MPG123_BAD_PARS;
if(!(channels & (MPG123_MONO|MPG123_STEREO))) return MPG123_BAD_CHANNEL;
if(!(channels & MPG123_STEREO)) ch[1] = 0; /* {0,0} */
else if(!(channels & MPG123_MONO)) ch[0] = 1; /* {1,1} */
- ratei = rate2num(mp, rate);
- if(ratei < 0) return MPG123_BAD_RATE;
+ if(rate)
+ {
+ r1 = rate2num(mp, rate);
+ r2 = r1+1;
+ }
+ else
+ {
+ r1 = 0;
+ r2 = MPG123_RATES+1; /* including forced rate */
+ }
+
+ if(r1 < 0) return MPG123_BAD_RATE;
/* now match the encodings */
+ for(ratei = r1; ratei < r2; ++ratei)
for(ic = 0; ic < 2; ++ic)
{
for(ie = 0; ie < MPG123_ENCODINGS; ++ie)
return MPG123_OK;
}
+// Keep old behaviour, error on rate=0.
+int attribute_align_arg mpg123_fmt(mpg123_pars *mp, long rate, int channels, int encodings)
+{
+ return (rate == 0)
+ ? MPG123_BAD_RATE
+ : mpg123_fmt2(mp, rate, channels, encodings);
+}
+
int attribute_align_arg mpg123_format_support(mpg123_handle *mh, long rate, int encoding)
{
if(mh == NULL) return 0;
}
#ifndef NO_32BIT
+
/* Remove every fourth byte, facilitating conversion from 32 bit to 24 bit integers.
This has to be aware of endianness, of course. */
static void chop_fourth_byte(struct outbuffer *buf)
{
unsigned char *wpos = buf->data;
unsigned char *rpos = buf->data;
-#ifdef WORDS_BIGENDIAN
- while((size_t) (rpos - buf->data + 4) <= buf->fill)
- {
- /* Really stupid: Copy, increment. Byte per byte. */
- *wpos = *rpos;
- wpos++; rpos++;
- *wpos = *rpos;
- wpos++; rpos++;
- *wpos = *rpos;
- wpos++; rpos++;
- rpos++; /* Skip the lowest byte (last). */
- }
-#else
- while((size_t) (rpos - buf->data + 4) <= buf->fill)
- {
- /* Really stupid: Copy, increment. Byte per byte. */
- rpos++; /* Skip the lowest byte (first). */
- *wpos = *rpos;
- wpos++; rpos++;
- *wpos = *rpos;
- wpos++; rpos++;
- *wpos = *rpos;
- wpos++; rpos++;
- }
-#endif
+ size_t blocks = buf->fill/4;
+ size_t i;
+ for(i=0; i<blocks; ++i,wpos+=3,rpos+=4)
+ DROP4BYTE(wpos, rpos)
buf->fill = wpos-buf->data;
}
size_t count = buf->fill/sizeof(int32_t);
for(i=0; i<count; ++i)
- {
- /* Different strategy since we don't have a larger type at hand.
- Also watch out for silly +-1 fun because integer constants are signed in C90! */
- if(ssamples[i] >= 0)
- usamples[i] = (uint32_t)ssamples[i] + 2147483647+1;
- /* The smallest value goes zero. */
- else if(ssamples[i] == ((int32_t)-2147483647-1))
- usamples[i] = 0;
- /* Now -value is in the positive range of signed int ... so it's a possible value at all. */
- else
- usamples[i] = (uint32_t)2147483647+1 - (uint32_t)(-ssamples[i]);
- }
+ usamples[i] = CONV_SU32(ssamples[i]);
}
#endif
size_t count = buf->fill/sizeof(int16_t);
for(i=0; i<count; ++i)
- {
- long tmp = (long)ssamples[i]+32768;
- usamples[i] = (uint16_t)tmp;
- }
+ usamples[i] = CONV_SU16(ssamples[i]);
}
#ifndef NO_REAL
#endif
#endif
+#include "swap_bytes_impl.h"
+
+void swap_endian(struct outbuffer *buf, int block)
+{
+ size_t count;
+
+ if(block >= 2)
+ {
+ count = buf->fill/(unsigned int)block;
+ swap_bytes(buf->data, (size_t)block, count);
+ }
+}
void postprocess_buffer(mpg123_handle *fr)
{
break;
#endif
}
+ if(fr->p.flags & MPG123_FORCE_ENDIAN)
+ {
+ if(
+#ifdef WORDS_BIGENDIAN
+ !(
+#endif
+ fr->p.flags & MPG123_BIG_ENDIAN
+#ifdef WORDS_BIGENDIAN
+ )
+#endif
+ )
+ swap_endian(&fr->buffer, mpg123_encsize(fr->af.encoding));
+ }
}
/*
frame: Heap of routines dealing with the core mpg123 data structure.
- copyright 2008-2014 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright 2008-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
*/
#ifdef GAPLESS
mp->flags |= MPG123_GAPLESS;
#endif
- mp->flags |= MPG123_AUTO_RESAMPLE;
+ mp->flags |= MPG123_AUTO_RESAMPLE|MPG123_FLOAT_FALLBACK;
#ifndef NO_NTOM
mp->force_rate = 0;
#endif
mp->feedpool = 5;
mp->feedbuffer = 4096;
#endif
+ mp->freeformat_framesize = -1;
}
void frame_init(mpg123_handle *fr)
#endif
fr->down_sample = 0; /* Initialize to silence harmless errors when debugging. */
+ fr->id3v2_raw = NULL;
frame_fixed_reset(fr); /* Reset only the fixed data, dynamic buffers are not there yet! */
fr->synth = NULL;
fr->synth_mono = NULL;
fi_init(&fr->index);
frame_index_setup(fr); /* Apply the size setting. */
#endif
+#ifndef NO_MOREINFO
+ fr->pinfo = NULL;
+#endif
}
#ifdef OPT_DITHER
if(fr->buffer.size < size)
{
fr->err = MPG123_BAD_BUFFER;
- if(NOQUIET) error2("have external buffer of size %"SIZE_P", need %"SIZE_P, (size_p)fr->buffer.size, (size_p)size);
-
+ if(NOQUIET)
+ merror( "have external buffer of size %"SIZE_P", need %"SIZE_P
+ , (size_p)fr->buffer.size, (size_p)size );
return MPG123_ERR;
}
}
return MPG123_OK;
}
-int attribute_align_arg mpg123_replace_buffer(mpg123_handle *mh, unsigned char *data, size_t size)
+int attribute_align_arg mpg123_replace_buffer(mpg123_handle *mh, void *data, size_t size)
{
debug2("replace buffer with %p size %"SIZE_P, data, (size_p)size);
if(mh == NULL) return MPG123_BAD_HANDLE;
if(fr->p.index_size >= 0)
{ /* Simple fixed index. */
fr->index.grow_size = 0;
- debug1("resizing index to %li", fr->p.index_size);
ret = fi_resize(&fr->index, (size_t)fr->p.index_size);
- debug2("index resized... %lu at %p", (unsigned long)fr->index.size, (void*)fr->index.data);
}
else
{ /* A growing index. We give it a start, though. */
fr->index.grow_size = (size_t)(- fr->p.index_size);
if(fr->index.size < fr->index.grow_size)
- ret = fi_resize(&fr->index, fr->index.grow_size);
+ ret = fi_resize(&fr->index, fr->index.grow_size);
else
- ret = MPG123_OK; /* We have minimal size already... and since growing is OK... */
+ ret = MPG123_OK; /* We have minimal size already... and since growing is OK... */
}
debug2("set up frame index of size %lu (ret=%i)", (unsigned long)fr->index.size, ret);
-
+ if(ret && NOQUIET)
+ error("frame index setup (initial resize) failed");
return ret;
}
#endif
#endif
fr->halfphase = 0; /* here or indeed only on first-time init? */
fr->error_protection = 0;
- fr->freeformat_framesize = -1;
+ fr->freeformat_framesize = fr->p.freeformat_framesize;
+ fr->enc_delay = -1;
+ fr->enc_padding = -1;
+ memset(fr->id3buf, 0, sizeof(fr->id3buf));
+ if(fr->id3v2_raw)
+ free(fr->id3v2_raw);
+ fr->id3v2_raw = NULL;
+ fr->id3v2_size = 0;
}
static void frame_free_buffers(mpg123_handle *fr)
return MPG123_OK;
}
+int attribute_align_arg mpg123_set_moreinfo( mpg123_handle *mh
+, struct mpg123_moreinfo *mi)
+{
+#ifndef NO_MOREINFO
+ mh->pinfo = mi;
+ return MPG123_OK;
+#else
+ mh->err = MPG123_MISSING_FEATURE;
+ return MPG123_ERR;
+#endif
+}
+
/*
Fuzzy frame offset searching (guessing).
When we don't have an accurate position, we may use an inaccurate one.
# ifndef NO_NTOM
case 3: outs = ntom_ins2outs(fr, ins); break;
# endif
- default: error1("Bad down_sample (%i) ... should not be possible!!", fr->down_sample);
+ default: if(NOQUIET)
+ merror( "Bad down_sample (%i) ... should not be possible!!"
+ , fr->down_sample );
}
return outs;
}
#ifndef NO_NTOM
case 3: outs = ntom_frmouts(fr, num); break;
#endif
- default: error1("Bad down_sample (%i) ... should not be possible!!", fr->down_sample);
+ default: if(NOQUIET)
+ merror( "Bad down_sample (%i) ... should not be possible!!"
+ , fr->down_sample );
}
return outs;
}
#ifndef NO_NTOM
case 3: outs = ntom_frame_outsamples(fr); break;
#endif
- default: error1("Bad down_sample (%i) ... should not be possible!!", fr->down_sample);
+ default: if(NOQUIET)
+ merror( "Bad down_sample (%i) ... should not be possible!!"
+ , fr->down_sample );
}
return outs;
}
#ifndef NO_NTOM
case 3: num = ntom_frameoff(fr, outs); break;
#endif
- default: error("Bad down_sample ... should not be possible!!");
+ default: if(NOQUIET)
+ error("Bad down_sample ... should not be possible!!");
}
return num;
}
if(gapless_samples > total_samples)
{
- if(NOQUIET) error2("End sample count smaller than gapless end! (%"OFF_P" < %"OFF_P"). Disabling gapless mode from now on.", (off_p)total_samples, (off_p)fr->end_s);
+ if(NOQUIET)
+ merror( "End sample count smaller than gapless end! (%"OFF_P
+ " < %"OFF_P"). Disabling gapless mode from now on."
+ , (off_p)total_samples, (off_p)fr->end_s );
/* This invalidates the current position... but what should I do? */
frame_gapless_init(fr, -1, 0, 0);
frame_gapless_realinit(fr);
void frame_skip(mpg123_handle *fr)
{
#ifndef NO_LAYER3
- if(fr->lay == 3) set_pointer(fr, 512);
+ if(fr->lay == 3) set_pointer(fr, 1, 512);
#endif
}
long feedpool;
long feedbuffer;
#endif
+ long freeformat_framesize;
};
enum frame_state_flags
/* bitstream info; bsi */
int bitindex;
+ long bits_avail;
unsigned char *wordpointer;
/* temporary storage for getbits stuff */
unsigned long ultmp;
int fsizeold;
int ssize;
unsigned int bitreservoir;
- unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */
+ unsigned char bsspace[2][MAXFRAMESIZE+512+4]; /* MAXFRAMESIZE */
unsigned char *bsbuf;
unsigned char *bsbufold;
int bsnum;
#ifndef NO_ID3V2
mpg123_id3v2 id3v2;
#endif
+ unsigned char *id3v2_raw;
+ size_t id3v2_size;
#ifndef NO_ICY
struct icy_meta icy;
#endif
void *wrapperdata;
/* A callback used to properly destruct the wrapper data. */
void (*wrapperclean)(void*);
+ int enc_delay;
+ int enc_padding;
+#ifndef NO_MOREINFO
+ struct mpg123_moreinfo *pinfo;
+#endif
};
/* generic init, does not include dynamic buffers */
576
*/
-#ifdef GAPLESS
-/* well, I take that one for granted... at least layer3 */
+// Well, I take that one for granted... at least layer3.
+// The value is needed for mpg123_getstate() in any build.
#define GAPLESS_DELAY 529
+#ifdef GAPLESS
void frame_gapless_init(mpg123_handle *fr, off_t framecount, off_t bskip, off_t eskip);
void frame_gapless_realinit(mpg123_handle *fr);
void frame_gapless_update(mpg123_handle *mh, off_t total_samples);
#define _MPG123_GETBITS_H_
#include "mpg123lib_intern.h"
+#include "debug.h"
#define backbits(fr,nob) ((void)( \
+ fr->bits_avail += nob, \
fr->bitindex -= nob, \
fr->wordpointer += (fr->bitindex>>3), \
fr->bitindex &= 0x7 ))
#define getbitoffset(fr) ((-fr->bitindex)&0x7)
-#define getbyte(fr) (*fr->wordpointer++)
-
-/* There is something wrong with that macro... the function below works also for the layer1 test case. */
-#define macro_getbits(fr, nob) ( \
- fr->ultmp = fr->wordpointer[0],\
- fr->ultmp <<= 8, \
- fr->ultmp |= fr->wordpointer[1], \
- fr->ultmp <<= 8, \
- fr->ultmp |= fr->wordpointer[2], \
- fr->ultmp <<= fr->bitindex, \
- fr->ultmp &= 0xffffff, \
- fr->bitindex += nob, \
- fr->ultmp >>= (24-nob), \
- fr->wordpointer += (fr->bitindex>>3), \
- fr->bitindex &= 7, \
- fr->ultmp)
+/* Precomputing the bytes to be read is error-prone, and some over-read
+ is even expected for Huffman. Just play safe and return zeros in case
+ of overflow. This assumes you made bitindex zero already! */
+#define getbyte(fr) ( (fr)->bits_avail-=8, (fr)->bits_avail >= 0 \
+ ? *((fr)->wordpointer++) \
+ : 0 )
static unsigned int getbits(mpg123_handle *fr, int number_of_bits)
{
#ifdef DEBUG_GETBITS
fprintf(stderr,"g%d",number_of_bits);
#endif
+ fr->bits_avail -= number_of_bits;
/* Safety catch until we got the nasty code fully figured out. */
/* No, that catch stays here, even if we think we got it figured out! */
- if( (long)(fr->wordpointer-fr->bsbuf)*8
- + fr->bitindex+number_of_bits > (long)fr->framesize*8 )
+ if(fr->bits_avail < 0)
+ {
+ if(NOQUIET)
+ error2( "Tried to read %i bits with %li available."
+ , number_of_bits, fr->bits_avail );
return 0;
+ }
/* This is actually slow: if(!number_of_bits)
return 0; */
#define skipbits(fr, nob) fr->ultmp = ( \
fr->ultmp = fr->wordpointer[0], fr->ultmp <<= 8, fr->ultmp |= fr->wordpointer[1], \
fr->ultmp <<= 8, fr->ultmp |= fr->wordpointer[2], fr->ultmp <<= fr->bitindex, \
- fr->ultmp &= 0xffffff, fr->bitindex += nob, \
+ fr->ultmp &= 0xffffff, fr->bitindex += nob, fr->bits_avail -= nob, \
fr->ultmp >>= (24-nob), fr->wordpointer += (fr->bitindex>>3), \
fr->bitindex &= 7 )
fr->ultmp = (unsigned char) (fr->wordpointer[0] << fr->bitindex), \
fr->ultmp |= ((unsigned long) fr->wordpointer[1]<<fr->bitindex)>>8, \
fr->ultmp <<= nob, fr->ultmp >>= 8, \
- fr->bitindex += nob, fr->wordpointer += (fr->bitindex>>3), \
+ fr->bitindex += nob, fr->bits_avail -= nob, \
+ fr->wordpointer += (fr->bitindex>>3), \
fr->bitindex &= 7, fr->ultmp )
#define get1bit(fr) ( \
- fr->uctmp = *fr->wordpointer << fr->bitindex, fr->bitindex++, \
+ fr->uctmp = *fr->wordpointer << fr->bitindex, \
+ ++fr->bitindex, --fr->bits_avail, \
fr->wordpointer += (fr->bitindex>>3), fr->bitindex &= 7, fr->uctmp>>7 )
/*
id3: ID3v2.3 and ID3v2.4 parsing (a relevant subset)
- copyright 2006-2013 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright 2006-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
+
+ WIP: Handling of multiple ID3 tags in a stream.
+
+ 1. With update flag: Add non-unique data, replace unique.
+ - Only one TALB, TPE1, etc.
+ - Only one TXXX with a certain description.
+ - Only one COMM with certain language and description.
+ - Only one APIC with certain type and description, generally only one
+ of type 1 and 2 each.
+ 2. Without update flag: wipe whole data and only store new stuff.
+
+ BIG BAD BUT: How to properly handle seeks in a stream that make
+ the parser encounter the same tags again in random order? Is
+ there even a correct way to handle that without storing an
+ ordered list of all tags? I could simplify the code here and just
+ communicate that a frame should be an update to previous, and
+ at which stream position the frame was encountered. But since
+ libmpg123 is driven by MPEG frames, there could be multiple
+ ID3v2 tags in direct succession treated by the parser without
+ the library user being able to interfere.
+
+ This is severely fucked. All that complexity also doesn't matter
+ in practice, as streams use ICY and individual files have just one
+ ID3v2 tag (relevant for libmpg123). It's an academic problem. But
+ for seekable files, I could implement some jumping logic to find
+ and parse all ID3v2 for once and then set a flag that only jumps
+ the frames on seeks. That covers all local disk playback. For
+ streams, seeking is no issue (seeking back, at least), so the
+ update/replace logic works.
+
+ Look at the standard:
+
+------
+5. Tag location
+
+ The default location of an ID3v2 tag is prepended to the audio so
+ that players can benefit from the information when the data is
+ streamed. It is however possible to append the tag, or make a
+ prepend/append combination. When deciding upon where an unembedded
+ tag should be located, the following order of preference SHOULD be
+ considered.
+
+ 1. Prepend the tag.
+
+ 2. Prepend a tag with all vital information and add a second tag at
+ the end of the file, before tags from other tagging systems. The
+ first tag is required to have a SEEK frame.
+
+ 3. Add a tag at the end of the file, before tags from other tagging
+ systems.
+
+ In case 2 and 3 the tag can simply be appended if no other known tags
+ are present. The suggested method to find ID3v2 tags are:
+
+ 1. Look for a prepended tag using the pattern found in section 3.1.
+
+ 2. If a SEEK frame was found, use its values to guide further
+ searching.
+
+ 3. Look for a tag footer, scanning from the back of the file.
+
+ For every new tag that is found, the old tag should be discarded
+ unless the update flag in the extended header (section 3.2) is set.
+------
+
+ For seekable streams, I simply need to implement explicit ID3v2 search along
+ that recommendation and keep the complete information. Streams that continue
+ growing during playback will not recognize added ID3v2 tags. So be it.
+ For non-seekable streams, a tag is always parsed when encountered, assuming
+ the order of update tags always matches.
+
+ First step for the 1.26 release shall be the implementaton of the update
+ logic and glossing over the theoretical problem of re-parsing update
+ frames in the wrong order by ignoring it. They are not that relevant.
+
+ TODO: Cave in and add the missing frames from the spec. Not that far to go.
+ But need another data structure to communicate those ...
*/
#include "mpg123lib_intern.h"
}
/* Add items to the list. */
-#define add_comment(mh) add_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
-#define add_text(mh) add_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
-#define add_extra(mh) add_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
-#define add_picture(mh) add_id3_picture(&((mh)->id3v2.picture), &((mh)->id3v2.pictures))
-static mpg123_text *add_id3_text(mpg123_text **list, size_t *size)
+
+#define add_comment(mh, l, d) add_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments), NULL, l, d)
+#define add_text(mh, id) add_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts), id, NULL, NULL)
+#define add_uslt(mh, l, d) add_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts), id, l, d)
+#define add_extra(mh, d) add_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras), NULL, NULL, d)
+#define add_picture(mh, t, d) add_id3_picture(&((mh)->id3v2.picture), &((mh)->id3v2.pictures), t, d)
+static mpg123_text *add_id3_text( mpg123_text **list, size_t *size
+, char id[4], char lang[3], mpg123_string *description )
{
+ mdebug( "add_id3_text id=%s lang=%s, desc=%s"
+ , id ? (char[5]) { id[0], id[1], id[2], id[3], 0 } : "(nil)"
+ , lang ? (char[4]) { lang[0], lang[1], lang[2], 0 } : "(nil)"
+ , description ? (description->fill ? description->p : "(empty)") : "(nil)" );
+ if(lang && !description)
+ return NULL; // no lone language intended
+ if(id || description)
+ {
+ // Look through list of existing texts and return an existing entry
+ // if it should be overwritten.
+ for(size_t i=0; i<*size; ++i)
+ {
+ mpg123_text *entry = *list+i;
+ if(description)
+ { // Overwrite entry with same description and same ID and language.
+ if( (!id || !memcmp(id, entry->id, 4))
+ && (!lang || !memcmp(entry->lang, lang, 3))
+ && mpg123_same_string(&(entry->description), description)
+ )
+ return entry;
+ } else if(id && !memcmp(id, entry->id, 4))
+ return entry; // Just ovewrite because of same ID.
+ mdebug("add_id3_text: entry %zu was no match", i);
+ }
+ }
+ mdebug("add_id3_text: append to list of %zu", *size);
+ // Nothing found, add new one.
mpg123_text *x = safe_realloc(*list, sizeof(mpg123_text)*(*size+1));
if(x == NULL) return NULL; /* bad */
return &((*list)[*size-1]); /* Return pointer to the added text. */
}
-static mpg123_picture *add_id3_picture(mpg123_picture **list, size_t *size)
+
+
+static mpg123_picture *add_id3_picture(mpg123_picture **list, size_t *size, char type, mpg123_string *description)
{
+ if(!description)
+ return NULL;
+
+ // Return entry to overwrite, if appropriate.
+ for(size_t i=0; i<*size; ++i)
+ {
+ mpg123_picture *entry = *list+i;
+ if( type == entry->type
+ && ( type == 1 || type == 2 ||
+ mpg123_same_string(&entry->description, description)
+ )
+ )
+ return entry;
+ }
+ // Append a new one.
mpg123_picture *x = safe_realloc(*list, sizeof(mpg123_picture)*(*size+1));
if(x == NULL) return NULL; /* bad */
return &((*list)[*size-1]); /* Return pointer to the added picture. */
}
-
-/* Remove the last item. */
-#define pop_comment(mh) pop_id3_text(&((mh)->id3v2.comment_list), &((mh)->id3v2.comments))
-#define pop_text(mh) pop_id3_text(&((mh)->id3v2.text), &((mh)->id3v2.texts))
-#define pop_extra(mh) pop_id3_text(&((mh)->id3v2.extra), &((mh)->id3v2.extras))
-#define pop_picture(mh) pop_id3_picture(&((mh)->id3v2.picture), &((mh)->id3v2.pictures))
-static void pop_id3_text(mpg123_text **list, size_t *size)
-{
- mpg123_text *x;
- if(*size < 1) return;
-
- free_mpg123_text(&((*list)[*size-1]));
- if(*size > 1)
- {
- x = safe_realloc(*list, sizeof(mpg123_text)*(*size-1));
- if(x != NULL){ *list = x; *size -= 1; }
- }
- else
- {
- free(*list);
- *list = NULL;
- *size = 0;
- }
-}
-static void pop_id3_picture(mpg123_picture **list, size_t *size)
-{
- mpg123_picture *x;
- if(*size < 1) return;
-
- free_mpg123_picture(&((*list)[*size-1]));
- if(*size > 1)
- {
- x = safe_realloc(*list, sizeof(mpg123_picture)*(*size-1));
- if(x != NULL){ *list = x; *size -= 1; }
- }
- else
- {
- free(*list);
- *list = NULL;
- *size = 0;
- }
-}
-
/* OK, back to the higher level functions. */
void exit_id3(mpg123_handle *fr)
}
/*
- Store ID3 text data in an mpg123_string; either verbatim copy or everything translated to UTF-8 encoding.
+ Store ID3 text data in an mpg123_string; either verbatim copy or
+ everything translated to UTF-8 encoding.
Preserve the zero string separator (I don't need strlen for the total size).
- ID3v2 standard says that there should be one text frame of specific type per tag, and subsequent tags overwrite old values.
- So, I always replace the text that may be stored already (perhaps with a list of zero-separated strings, though).
+ Since we can overwrite strings with ID3 update frames, don't free
+ memory, just grow strings.
*/
static void store_id3_text(mpg123_string *sb, unsigned char *source, size_t source_size, const int noquiet, const int notranslate)
{
unsigned char encoding;
+ if(sb) // Always overwrite, even with nothing.
+ sb->fill = 0;
if(!source_size)
{
debug("Empty id3 data!");
if(notranslate)
{
/* Future: Add a path for ID3 errors. */
- if(!mpg123_resize_string(sb, source_size))
+ if(!mpg123_grow_string(sb, source_size))
{
if(noquiet) error("Cannot resize target string, out of memory?");
return;
{
if(noquiet)
error1("Unknown text encoding %u, I take no chances, sorry!", encoding);
-
- mpg123_free_string(sb);
return;
}
id3_to_utf8(sb, encoding, source+1, source_size-1, noquiet);
void id3_to_utf8(mpg123_string *sb, unsigned char encoding, const unsigned char *source, size_t source_size, int noquiet)
{
unsigned int bwidth;
+ if(sb)
+ sb->fill = 0;
debug1("encoding: %u", encoding);
/* A note: ID3v2.3 uses UCS-2 non-variable 16bit encoding, v2.4 uses UTF16.
UTF-16 uses a reserved/private range in UCS-2 to add the magic, so we just always treat it as UTF. */
{
/* Text encoding $xx */
/* The text (encoded) ... */
- mpg123_text *t = add_text(fr);
+ mpg123_text *t = add_text(fr, id);
if(VERBOSE4) fprintf(stderr, "Note: Storing text from %s encoding\n", enc_name(realdata[0]));
if(t == NULL)
{
if(NOQUIET) error("Unable to attach new text!");
return;
}
+ mdebug("process_text: (over)writing entry with ID %s", t->id
+ ? (char[5]) { t->id[0], t->id[1], t->id[2], t->id[3], 0 }
+ : "(nil)" );
memcpy(t->id, id, 4);
store_id3_text(&t->text, realdata, realsize, NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT);
- if(VERBOSE4) fprintf(stderr, "Note: ID3v2 %c%c%c%c text frame: %s\n", id[0], id[1], id[2], id[3], t->text.p);
+ if(VERBOSE4) // Do not print unsanitized text to terminals!
+ fprintf(stderr, "Note: ID3v2 %c%c%c%c text frame stored\n", id[0], id[1], id[2], id[3]);
}
static void process_picture(mpg123_handle *fr, unsigned char *realdata, size_t realsize)
{
- unsigned char encoding = realdata[0];
+ unsigned char encoding;
mpg123_picture *i = NULL;
- unsigned char* workpoint;
- if(realsize == 0)
+ unsigned char* workpoint = NULL;
+ mpg123_string mime; mpg123_init_string(&mime);
+ unsigned char image_type = 0;
+ mpg123_string description; mpg123_init_string(&description);
+ unsigned char *image_data = NULL;
+ if(realsize < 1)
{
debug("Empty id3 data!");
return;
}
+ encoding = realdata[0];
+ realdata++; realsize--;
if(encoding > mpg123_id3_enc_max)
{
if(NOQUIET)
return;
}
if(VERBOSE4) fprintf(stderr, "Note: Storing picture from APIC frame.\n");
- /* decompose realdata accordingly */
- i = add_picture(fr);
- if(i == NULL)
- {
- if(NOQUIET) error("Unable to attach new picture!");
- return;
- }
- realdata++; realsize--;
+
/* get mime type (encoding is always latin-1) */
workpoint = next_text(realdata, 0, realsize);
- if (workpoint == NULL) {
- pop_picture(fr);
- if (NOQUIET) error("Unable to get mime type for picture; skipping picture.");
+ if(!workpoint)
+ {
+ if(NOQUIET)
+ error("Unable to get mime type for picture; skipping picture.");
return;
}
- id3_to_utf8(&i->mime_type, 0, realdata, workpoint - realdata, NOQUIET);
+ id3_to_utf8(&mime, 0, realdata, workpoint - realdata, NOQUIET);
realsize -= workpoint - realdata;
realdata = workpoint;
/* get picture type */
- i->type = realdata[0];
+ image_type = realdata[0];
realdata++; realsize--;
/* get description (encoding is encoding) */
workpoint = next_text(realdata, encoding, realsize);
- if (workpoint == NULL) {
- if (NOQUIET) error("Unable to get description for picture; skipping picture.");
- pop_picture(fr);
+ if(!workpoint)
+ {
+ if(NOQUIET)
+ error("Unable to get description for picture; skipping picture.");
+ mpg123_free_string(&mime);
return;
}
- id3_to_utf8(&i->description, encoding, realdata, workpoint - realdata, NOQUIET);
+ id3_to_utf8(&description, encoding, realdata, workpoint - realdata, NOQUIET);
realsize -= workpoint - realdata;
- if (realsize == 0) {
- if (NOQUIET) error("No picture data defined; skipping picture.");
- pop_picture(fr);
+ if(realsize)
+ image_data = (unsigned char*)malloc(realsize);
+ if(!realsize || !image_data) {
+ if(NOQUIET)
+ error("No picture data or malloc failure; skipping picture.");
+ mpg123_free_string(&description);
+ mpg123_free_string(&mime);
return;
}
- /* store_id3_picture(i, picture, realsize, NOQUIET)) */
- i->data = (unsigned char*)malloc(realsize);
- if (i->data == NULL) {
- if (NOQUIET) error("Unable to allocate memory for picture; skipping picture");
- pop_picture(fr);
+ memcpy(image_data, workpoint, realsize);
+
+ // All data ready now, append to/replace in list.
+ i = add_picture(fr, image_type, &description);
+ if(!i)
+ {
+ if(NOQUIET)
+ error("Unable to attach new picture!");
+ free(image_data);
+ mpg123_free_string(&description);
+ mpg123_free_string(&mime);
return;
}
- memcpy(i->data, workpoint, realsize);
+
+ // Either this is a fresh image, or one to be replaced.
+ // We hand over memory, so old storage needs to be freed.
+ free_mpg123_picture(i);
+ i->type = image_type;
i->size = realsize;
- if(VERBOSE4) fprintf(stderr, "Note: ID3v2 APIC picture frame of type: %d\n", i->type);
+ i->data = image_data;
+ mpg123_move_string(&mime, &i->mime_type);
+ mpg123_move_string(&description, &i->description);
+ if(VERBOSE4)
+ fprintf(stderr, "Note: ID3v2 APIC picture frame of type: %d\n", i->type);
}
/* Store a new comment that perhaps is a RVA / RVA_ALBUM/AUDIOPHILE / RVA_MIX/RADIO one
/* Short description (encoded!) <text> $00 (00) */
/* Then the comment text (encoded) ... */
unsigned char encoding = realdata[0];
- unsigned char *lang = realdata+1; /* I'll only use the 3 bytes! */
+ char lang[3]; // realdata + 1
unsigned char *descr = realdata+4;
unsigned char *text = NULL;
mpg123_text *xcom = NULL;
- mpg123_text localcom; /* UTF-8 variant for local processing. */
+ mpg123_text localcom; // UTF-8 variant for local processing, remember to clean up!
+ init_mpg123_text(&localcom);
if(realsize < (size_t)(descr-realdata))
{
error1("Unknown text encoding %u, I take no chances, sorry!", encoding);
return;
}
- xcom = (tt == uslt ? add_text(fr) : add_comment(fr));
- if(VERBOSE4) fprintf(stderr, "Note: Storing comment from %s encoding\n", enc_name(realdata[0]));
- if(xcom == NULL)
- {
- if(NOQUIET) error("Unable to attach new comment!");
- return;
- }
- memcpy(xcom->lang, lang, 3);
- memcpy(xcom->id, id, 4);
+ memcpy(lang, realdata+1, 3);
/* Now I can abuse a byte from lang for the encoding. */
descr[-1] = encoding;
/* Be careful with finding the end of description, I have to honor encoding here. */
text = next_text(descr, encoding, realsize-(descr-realdata));
if(text == NULL)
{
- if(NOQUIET) error("No comment text / valid description?");
- pop_comment(fr);
+ if(NOQUIET)
+ error("No comment text / valid description?");
return;
}
-
- init_mpg123_text(&localcom);
- /* Store the text, without translation to UTF-8, but for comments always a local copy in UTF-8.
- Reminder: No bailing out from here on without freeing the local comment data! */
- store_id3_text(&xcom->description, descr-1, text-descr+1, NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT);
- if(tt == comment)
- store_id3_text(&localcom.description, descr-1, text-descr+1, NOQUIET, 0);
+ { // just vor variable scope
+ mpg123_string description;
+ mpg123_init_string(&description);
+ // Store the text, with desired encoding, but for comments always a local copy in UTF-8.
+ store_id3_text( &description, descr-1, text-descr+1
+ , NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT );
+ if(tt == comment)
+ store_id3_text( &localcom.description, descr-1, text-descr+1
+ , NOQUIET, 0 );
+ if(VERBOSE4)
+ fprintf( stderr, "Note: Storing comment from %s encoding\n"
+ , enc_name(realdata[0]) );
+ xcom = tt == uslt
+ ? add_uslt(fr, lang, &description)
+ : add_comment(fr, lang, &description);
+ if(xcom == NULL)
+ {
+ if(NOQUIET)
+ error("Unable to attach new comment!");
+ mpg123_free_string(&description);
+ free_mpg123_text(&localcom);
+ return;
+ }
+ memcpy(xcom->id, id, 4);
+ memcpy(xcom->lang, lang, 3);
+ // That takes over the description allocation.
+ mpg123_move_string(&description, &xcom->description);
+ }
text[-1] = encoding; /* Byte abusal for encoding... */
store_id3_text(&xcom->text, text-1, realsize+1-(text-realdata), NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT);
if(NOQUIET) error("No extra frame text / valid description?");
return;
}
- xex = add_extra(fr);
+ { // just vor variable scope
+ mpg123_string description;
+ mpg123_init_string(&description);
+ /* The outside storage gets reencoded to UTF-8 only if not requested otherwise.
+ Remember that we really need the -1 here to hand in the encoding byte!*/
+ store_id3_text( &description, descr-1, text-descr+1
+ , NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT );
+ xex = add_extra(fr, &description);
+ if(xex)
+ mpg123_move_string(&description, &xex->description);
+ else
+ mpg123_free_string(&description);
+ }
if(xex == NULL)
{
if(NOQUIET) error("Unable to attach new extra text!");
memcpy(xex->id, id, 4);
init_mpg123_text(&localex); /* For our local copy. */
- /* The outside storage gets reencoded to UTF-8 only if not requested otherwise.
- Remember that we really need the -1 here to hand in the encoding byte!*/
- store_id3_text(&xex->description, descr-1, text-descr+1, NOQUIET, fr->p.flags & MPG123_PLAIN_ID3TEXT);
/* Our local copy is always stored in UTF-8! */
store_id3_text(&localex.description, descr-1, text-descr+1, NOQUIET, 0);
/* At first, only store the outside copy of the payload. We may not need the local copy. */
#endif /* NO_ID3V2 */
+int store_id3v2( mpg123_handle *fr
+, unsigned long first4bytes, unsigned char buf[6], unsigned long length )
+{
+ int ret = 1;
+ off_t ret2;
+ unsigned long fullen = 10+length;
+ if(fr->id3v2_raw)
+ free(fr->id3v2_raw);
+ fr->id3v2_size = 0;
+ /* Allocate one byte more for a closing zero as safety catch for strlen(). */
+ fr->id3v2_raw = malloc(fullen+1);
+ if(!fr->id3v2_raw)
+ {
+ fr->err = MPG123_OUT_OF_MEM;
+ if(NOQUIET)
+ error1("ID3v2: Arrg! Unable to allocate %lu bytes"
+ " for ID3v2 data - trying to skip instead.", length+1);
+ if((ret2=fr->rd->skip_bytes(fr,length)) < 0)
+ ret = ret2;
+ else
+ ret = 0;
+ }
+ else
+ {
+ fr->id3v2_raw[0] = (first4bytes>>24) & 0xff;
+ fr->id3v2_raw[1] = (first4bytes>>16) & 0xff;
+ fr->id3v2_raw[2] = (first4bytes>>8) & 0xff;
+ fr->id3v2_raw[3] = first4bytes & 0xff;
+ memcpy(fr->id3v2_raw+4, buf, 6);
+ if((ret2=fr->rd->read_frame_body(fr, fr->id3v2_raw+10, length)) < 0)
+ {
+ ret=ret2;
+ free(fr->id3v2_raw);
+ fr->id3v2_raw = NULL;
+ }
+ else
+ { /* Closing with a zero for paranoia. */
+ fr->id3v2_raw[fullen] = 0;
+ fr->id3v2_size = fullen;
+ }
+ }
+ return ret;
+}
+
/*
trying to parse ID3v2.3 and ID3v2.4 tags...
int parse_new_id3(mpg123_handle *fr, unsigned long first4bytes)
{
#define UNSYNC_FLAG 128
- #define EXTHEAD_FLAG 64
+ #define EXTHEAD_FLAG 64 /* ID3v2.3+ */
+ #define COMPRESS_FLAG 64 /* ID3v2.2 */
#define EXP_FLAG 32
#define FOOTER_FLAG 16
+ #define EXT_UPDATE_FLAG 64 /* ID3v2.4 only: extended header update flag */
#define UNKNOWN_FLAGS 15 /* 00001111*/
unsigned char buf[6];
unsigned long length=0;
unsigned char flags = 0;
int ret = 1;
- int ret2;
+ off_t ret2;
+ int storetag = 0;
+ unsigned int footlen = 0;
#ifndef NO_ID3V2
int skiptag = 0;
#endif
unsigned char major = first4bytes & 0xff;
debug1("ID3v2: major tag version: %i", major);
+
if(major == 0xff) return 0; /* Invalid... */
if((ret2 = fr->rd->read_frame_body(fr, buf, 6)) < 0) /* read more header information */
return ret2;
if(buf[0] == 0xff) return 0; /* Revision, will never be 0xff. */
+ if(fr->p.flags & MPG123_STORE_RAW_ID3)
+ storetag = 1;
/* second new byte are some nice flags, if these are invalid skip the whole thing */
flags = buf[1];
debug1("ID3v2: flags 0x%08x", flags);
if(NOQUIET) error4("Bad tag length (not synchsafe): 0x%02x%02x%02x%02x; You got a bad ID3 tag here.", buf[2],buf[3],buf[4],buf[5]);
return 0;
}
+ if(flags & FOOTER_FLAG)
+ footlen = 10;
debug1("ID3v2: tag data length %lu", length);
#ifndef NO_ID3V2
if(VERBOSE2) fprintf(stderr,"Note: ID3v2.%i rev %i tag of %lu bytes\n", major, buf[0], length);
, major, flags );
skiptag = 1;
}
+ // Standard says that compressed tags should be ignored as there isn't an agreed
+ // compressoion scheme.
+ if(major == 2 && flags & COMPRESS_FLAG)
+ {
+ if(NOQUIET)
+ warning("ID3v2: ignoring compressed ID3v2.2 tag");
+ skiptag = 1;
+ }
if(length < 10)
{
if(NOQUIET)
warning1("ID3v2: unrealistic small tag lengh %lu, skipping", length);
skiptag = 1;
}
+ if(!skiptag)
+ storetag = 1;
+#endif
+ if(storetag)
+ {
+ /* Stores whole tag with footer and an additonal trailing zero. */
+ if((ret2 = store_id3v2(fr, first4bytes, buf, length+footlen)) <= 0)
+ return ret2;
+ }
+#ifndef NO_ID3V2
if(skiptag)
{
+ if(VERBOSE3)
+ fprintf(stderr, "Note: skipped tag clearing possibly existing ID3v2 data");
+ reset_id3(fr); // Old data is invalid.
#endif
- if((ret2 = fr->rd->skip_bytes(fr,length)) < 0) /* will not store data in backbuff! */
- ret = ret2;
+ if(!storetag && (ret2=fr->rd->skip_bytes(fr,length+footlen))<0)
+ ret=ret2;
#ifndef NO_ID3V2
}
else
{
- unsigned char* tagdata = NULL;
- fr->id3v2.version = major;
+ unsigned char* tagdata = fr->id3v2_raw+10;
/* try to interpret that beast */
- if((tagdata = (unsigned char*) malloc(length+1)) != NULL)
+ debug("ID3v2: analysing frames...");
+ if(length > 0)
{
- debug("ID3v2: analysing frames...");
- if((ret2 = fr->rd->read_frame_body(fr,tagdata,length)) > 0)
+ unsigned char extflags = 0;
+ unsigned long tagpos = 0;
+ /* bytes of frame title and of framesize value */
+ unsigned int head_part = major > 2 ? 4 : 3;
+ unsigned int flag_part = major > 2 ? 2 : 0;
+ /* The amount of bytes that are unconditionally read for each frame: */
+ /* ID, size, flags. */
+ unsigned int framebegin = head_part+head_part+flag_part;
+ debug1("ID3v2: have read at all %lu bytes for the tag now", (unsigned long)length+6);
+ if(flags & EXTHEAD_FLAG)
{
- unsigned long tagpos = 0;
- /* bytes of frame title and of framesize value */
- unsigned int head_part = fr->id3v2.version > 2 ? 4 : 3;
- unsigned int flag_part = fr->id3v2.version > 2 ? 2 : 0;
- /* The amount of bytes that are unconditionally read for each frame: */
- /* ID, size, flags. */
- unsigned int framebegin = head_part+head_part+flag_part;
- debug1("ID3v2: have read at all %lu bytes for the tag now", (unsigned long)length+6);
- /* going to apply strlen for strings inside frames, make sure that it doesn't overflow! */
- tagdata[length] = 0;
- if(flags & EXTHEAD_FLAG)
+ debug("ID3v2: extended header");
+ if(!bytes_to_long(tagdata, tagpos) || tagpos >= length)
{
- debug("ID3v2: skipping extended header");
- if(!bytes_to_long(tagdata, tagpos) || tagpos >= length)
+ ret = 0;
+ if(NOQUIET)
+ error4( "Bad (non-synchsafe/too large) tag offset from extended header:"
+ "0x%02x%02x%02x%02x"
+ , tagdata[0], tagdata[1], tagdata[2], tagdata[3] );
+ } else if(tagpos < 6)
+ {
+ ret = 0;
+ if(NOQUIET)
+ merror("Extended header too small (%lu).", tagpos);
+ }
+ if(major == 3)
+ {
+ tagpos += 4; // The size itself is not included.
+ if(tagpos >= length)
{
ret = 0;
if(NOQUIET)
- error4( "Bad (non-synchsafe/too large) tag offset:"
- "0x%02x%02x%02x%02x"
- , tagdata[0], tagdata[1], tagdata[2], tagdata[3] );
+ error("Too much extended v2.3 header.");
+ }
+ } else if(ret) // v2.4 and at least got my 6 bytes of ext header
+ {
+ // Only v4 knows update frames, check for that.
+ // Need to step back. Header is 4 bytes length, one byte flag size,
+ // one byte flags. Flag size has to be 1!
+ if(tagdata[4] == 1 && tagdata[5] & EXT_UPDATE_FLAG)
+ {
+ if(VERBOSE3)
+ fprintf(stderr, "Note: ID3v2.4 update tag\n");
+ extflags |= EXT_UPDATE_FLAG;
}
}
- if(ret > 0)
+ }
+ if(!(extflags & EXT_UPDATE_FLAG))
+ {
+ if(VERBOSE3)
+ fprintf(stderr, "Note: non-update tag replacing existing ID3v2 data\n");
+ reset_id3(fr);
+ }
+ if(ret > 0)
+ {
+ char id[5];
+ unsigned long framesize;
+ unsigned long fflags; /* need 16 bits, actually */
+ id[4] = 0;
+ fr->id3v2.version = major;
+ /* Pos now advanced after ext head, now a frame has to follow. */
+ /* Note: tagpos <= length, which is 28 bit integer, so both */
+ /* far away from overflow for adding known small values. */
+ /* I want to read at least one full header now. */
+ while(length >= tagpos+framebegin)
{
- char id[5];
- unsigned long framesize;
- unsigned long fflags; /* need 16 bits, actually */
- id[4] = 0;
- /* Pos now advanced after ext head, now a frame has to follow. */
- /* Note: tagpos <= length, which is 28 bit integer, so both */
- /* far away from overflow for adding known small values. */
- /* I want to read at least one full header now. */
- while(length >= tagpos+framebegin)
+ int i = 0;
+ unsigned long pos = tagpos;
+ /* level 1,2,3 - 0 is info from lame/info tag! */
+ /* rva tags with ascending significance, then general frames */
+ enum frame_types tt = unknown;
+ /* we may have entered the padding zone or any other strangeness: check if we have valid frame id characters */
+ for(i=0; i< head_part; ++i)
+ if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
+ || ((tagdata[tagpos+i] > 64) && (tagdata[tagpos+i] < 91)) ) )
+ {
+ debug5("ID3v2: real tag data apparently ended after %lu bytes with 0x%02x%02x%02x%02x", tagpos, tagdata[tagpos], tagdata[tagpos+1], tagdata[tagpos+2], tagdata[tagpos+3]);
+ /* This is no hard error... let's just hope that we got something meaningful already (ret==1 in that case). */
+ goto tagparse_cleanup; /* Need to escape two loops here. */
+ }
+ if(ret > 0)
{
- int i = 0;
- unsigned long pos = tagpos;
- /* level 1,2,3 - 0 is info from lame/info tag! */
- /* rva tags with ascending significance, then general frames */
- enum frame_types tt = unknown;
- /* we may have entered the padding zone or any other strangeness: check if we have valid frame id characters */
- for(i=0; i< head_part; ++i)
- if( !( ((tagdata[tagpos+i] > 47) && (tagdata[tagpos+i] < 58))
- || ((tagdata[tagpos+i] > 64) && (tagdata[tagpos+i] < 91)) ) )
+ /* 4 or 3 bytes id */
+ strncpy(id, (char*) tagdata+pos, head_part);
+ id[head_part] = 0; /* terminate for 3 or 4 bytes */
+ pos += head_part;
+ tagpos += head_part;
+ /* size as 32 bits or 28 bits */
+ if(fr->id3v2.version == 2) threebytes_to_long(tagdata+pos, framesize);
+ else
+ if(!bytes_to_long(tagdata+pos, framesize))
{
- debug5("ID3v2: real tag data apparently ended after %lu bytes with 0x%02x%02x%02x%02x", tagpos, tagdata[tagpos], tagdata[tagpos+1], tagdata[tagpos+2], tagdata[tagpos+3]);
- /* This is no hard error... let's just hope that we got something meaningful already (ret==1 in that case). */
- goto tagparse_cleanup; /* Need to escape two loops here. */
+ /* Just assume that up to now there was some good data. */
+ if(NOQUIET) error1("ID3v2: non-syncsafe size of %s frame, skipping the remainder of tag", id);
+ break;
}
- if(ret > 0)
+ if(VERBOSE3) fprintf(stderr, "Note: ID3v2 %s frame of size %lu\n", id, framesize);
+ tagpos += head_part;
+ pos += head_part;
+ if(fr->id3v2.version > 2)
{
- /* 4 or 3 bytes id */
- strncpy(id, (char*) tagdata+pos, head_part);
- id[head_part] = 0; /* terminate for 3 or 4 bytes */
- pos += head_part;
- tagpos += head_part;
- /* size as 32 bits or 28 bits */
- if(fr->id3v2.version == 2) threebytes_to_long(tagdata+pos, framesize);
- else
- if(!bytes_to_long(tagdata+pos, framesize))
- {
- /* Just assume that up to now there was some good data. */
- if(NOQUIET) error1("ID3v2: non-syncsafe size of %s frame, skipping the remainder of tag", id);
- break;
- }
- if(VERBOSE3) fprintf(stderr, "Note: ID3v2 %s frame of size %lu\n", id, framesize);
- tagpos += head_part;
- pos += head_part;
- if(fr->id3v2.version > 2)
- {
- fflags = (((unsigned long) tagdata[pos]) << 8) | ((unsigned long) tagdata[pos+1]);
- pos += 2;
- tagpos += 2;
- }
- else fflags = 0;
+ fflags = (((unsigned long) tagdata[pos]) << 8) | ((unsigned long) tagdata[pos+1]);
+ pos += 2;
+ tagpos += 2;
+ }
+ else fflags = 0;
- if(length - tagpos < framesize)
- {
- if(NOQUIET) error("Whoa! ID3v2 frame claims to be larger than the whole rest of the tag.");
- break;
- }
- tagpos += framesize; /* the important advancement in whole tag */
- /* for sanity, after full parsing tagpos should be == pos */
- /* debug4("ID3v2: found %s frame, size %lu (as bytes: 0x%08lx), flags 0x%016lx", id, framesize, framesize, fflags); */
- /* %0abc0000 %0h00kmnp */
- #define BAD_FFLAGS (unsigned long) 36784
- #define PRES_TAG_FFLAG 16384
- #define PRES_FILE_FFLAG 8192
- #define READ_ONLY_FFLAG 4096
- #define GROUP_FFLAG 64
- #define COMPR_FFLAG 8
- #define ENCR_FFLAG 4
- #define UNSYNC_FFLAG 2
- #define DATLEN_FFLAG 1
- if(head_part < 4 && promote_framename(fr, id) != 0) continue;
-
- /* shall not or want not handle these */
- if(fflags & (BAD_FFLAGS | COMPR_FFLAG | ENCR_FFLAG))
- {
- if(NOQUIET) warning("ID3v2: skipping invalid/unsupported frame");
- continue;
- }
+ if(length - tagpos < framesize)
+ {
+ if(NOQUIET) error("Whoa! ID3v2 frame claims to be larger than the whole rest of the tag.");
+ break;
+ }
+ tagpos += framesize; /* the important advancement in whole tag */
+ /* for sanity, after full parsing tagpos should be == pos */
+ /* debug4("ID3v2: found %s frame, size %lu (as bytes: 0x%08lx), flags 0x%016lx", id, framesize, framesize, fflags); */
+ /* %0abc0000 %0h00kmnp */
+ #define BAD_FFLAGS (unsigned long) 36784
+ #define PRES_TAG_FFLAG 16384
+ #define PRES_FILE_FFLAG 8192
+ #define READ_ONLY_FFLAG 4096
+ #define GROUP_FFLAG 64
+ #define COMPR_FFLAG 8
+ #define ENCR_FFLAG 4
+ #define UNSYNC_FFLAG 2
+ #define DATLEN_FFLAG 1
+ if(head_part < 4 && promote_framename(fr, id) != 0) continue;
+
+ /* shall not or want not handle these */
+ if(fflags & (BAD_FFLAGS | COMPR_FFLAG | ENCR_FFLAG))
+ {
+ if(NOQUIET) warning("ID3v2: skipping invalid/unsupported frame");
+ continue;
+ }
- for(i = 0; i < KNOWN_FRAMES; ++i)
- if(!strncmp(frame_type[i], id, 4)){ tt = i; break; }
+ for(i = 0; i < KNOWN_FRAMES; ++i)
+ if(!strncmp(frame_type[i], id, 4)){ tt = i; break; }
- if(id[0] == 'T' && tt != extra) tt = text;
+ if(id[0] == 'T' && tt != extra) tt = text;
- if(tt != unknown)
+ if(tt != unknown)
+ {
+ int rva_mode = -1; /* mix / album */
+ unsigned long realsize = framesize;
+ unsigned char* realdata = tagdata+pos;
+ unsigned char* unsyncbuffer = NULL;
+ if(((flags & UNSYNC_FLAG) || (fflags & UNSYNC_FFLAG)) && framesize > 0)
{
- int rva_mode = -1; /* mix / album */
- unsigned long realsize = framesize;
- unsigned char* realdata = tagdata+pos;
- unsigned char* unsyncbuffer = NULL;
- if(((flags & UNSYNC_FLAG) || (fflags & UNSYNC_FFLAG)) && framesize > 0)
+ unsigned long ipos = 0;
+ unsigned long opos = 0;
+ debug("Id3v2: going to de-unsync the frame data");
+ /* de-unsync: FF00 -> FF; real FF00 is simply represented as FF0000 ... */
+ /* damn, that means I have to delete bytes from withing the data block... thus need temporal storage */
+ /* standard mandates that de-unsync should always be safe if flag is set */
+ realdata = unsyncbuffer = malloc(framesize+1); /* will need <= bytes, plus a safety zero */
+ if(realdata == NULL)
{
- unsigned long ipos = 0;
- unsigned long opos = 0;
- debug("Id3v2: going to de-unsync the frame data");
- /* de-unsync: FF00 -> FF; real FF00 is simply represented as FF0000 ... */
- /* damn, that means I have to delete bytes from withing the data block... thus need temporal storage */
- /* standard mandates that de-unsync should always be safe if flag is set */
- realdata = unsyncbuffer = malloc(framesize+1); /* will need <= bytes, plus a safety zero */
- if(realdata == NULL)
- {
- if(NOQUIET) error("ID3v2: unable to allocate working buffer for de-unsync");
- continue;
- }
- /* now going byte per byte through the data... */
- realdata[0] = tagdata[pos];
- opos = 1;
- for(ipos = pos+1; ipos < pos+framesize; ++ipos)
+ if(NOQUIET) error("ID3v2: unable to allocate working buffer for de-unsync");
+ continue;
+ }
+ /* now going byte per byte through the data... */
+ realdata[0] = tagdata[pos];
+ opos = 1;
+ for(ipos = pos+1; ipos < pos+framesize; ++ipos)
+ {
+ if(!((tagdata[ipos] == 0) && (tagdata[ipos-1] == 0xff)))
{
- if(!((tagdata[ipos] == 0) && (tagdata[ipos-1] == 0xff)))
- {
- realdata[opos++] = tagdata[ipos];
- }
+ realdata[opos++] = tagdata[ipos];
}
- realsize = opos;
- /* Append a zero to keep strlen() safe. */
- realdata[realsize] = 0;
- debug2("ID3v2: de-unsync made %lu out of %lu bytes", realsize, framesize);
}
- pos = 0; /* now at the beginning again... */
- /* Avoid reading over boundary, even if there is a */
- /* zero byte of padding for safety. */
- if(realsize) switch(tt)
+ realsize = opos;
+ /* Append a zero to keep strlen() safe. */
+ realdata[realsize] = 0;
+ debug2("ID3v2: de-unsync made %lu out of %lu bytes", realsize, framesize);
+ }
+ pos = 0; /* now at the beginning again... */
+ /* Avoid reading over boundary, even if there is a */
+ /* zero byte of padding for safety. */
+ if(realsize) switch(tt)
+ {
+ case comment:
+ case uslt:
+ process_comment(fr, tt, realdata, realsize, comment+1, id);
+ break;
+ case extra: /* perhaps foobar2000's work */
+ process_extra(fr, realdata, realsize, extra+1, id);
+ break;
+ case rva2: /* "the" RVA tag */
{
- case comment:
- case uslt:
- process_comment(fr, tt, realdata, realsize, comment+1, id);
- break;
- case extra: /* perhaps foobar2000's work */
- process_extra(fr, realdata, realsize, extra+1, id);
- break;
- case rva2: /* "the" RVA tag */
+ /* starts with null-terminated identification */
+ if(VERBOSE3) fprintf(stderr, "Note: RVA2 identification \"%s\"\n", realdata);
+ /* default: some individual value, mix mode */
+ rva_mode = 0;
+ if( !strncasecmp((char*)realdata, "album", 5)
+ || !strncasecmp((char*)realdata, "audiophile", 10)
+ || !strncasecmp((char*)realdata, "user", 4))
+ rva_mode = 1;
+ if(fr->rva.level[rva_mode] <= rva2+1)
{
- /* starts with null-terminated identification */
- if(VERBOSE3) fprintf(stderr, "Note: RVA2 identification \"%s\"\n", realdata);
- /* default: some individual value, mix mode */
- rva_mode = 0;
- if( !strncasecmp((char*)realdata, "album", 5)
- || !strncasecmp((char*)realdata, "audiophile", 10)
- || !strncasecmp((char*)realdata, "user", 4))
- rva_mode = 1;
- if(fr->rva.level[rva_mode] <= rva2+1)
+ pos += strlen((char*) realdata) + 1;
+ debug2("got my pos: %zu - %zu", realsize, pos);
+ // channel and two bytes for RVA value
+ // pos possibly just past the safety zero, so one more than realsize
+ if(pos > realsize || realsize-pos < 3)
{
- pos += strlen((char*) realdata) + 1;
- // channel and two bytes for RVA value
- // pos possibly just past the safety zero, so one more than realsize
- if(pos > realsize || realsize-pos < 3)
- {
- if(NOQUIET)
- error("bad RVA2 tag (truncated?)");
- }
- else if(realdata[pos] == 1)
- {
- ++pos;
- /* only handle master channel */
- debug("ID3v2: it is for the master channel");
- /* two bytes adjustment, one byte for bits representing peak - n bytes, eh bits, for peak */
- /* 16 bit signed integer = dB * 512. Do not shift signed integers! Multiply instead.
- Also no implementation-defined casting. Reinterpret the pointer to signed char, then do
- proper casting. */
- fr->rva.gain[rva_mode] = (float) (
- ((short)((signed char*)realdata)[pos]) * 256 + (short)realdata[pos+1] ) / 512;
- pos += 2;
- if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
- /* heh, the peak value is represented by a number of bits - but in what manner? Skipping that part */
- fr->rva.peak[rva_mode] = 0;
- fr->rva.level[rva_mode] = rva2+1;
- }
+ if(NOQUIET)
+ error("bad RVA2 tag (truncated?)");
+ }
+ else if(realdata[pos] == 1)
+ {
+ ++pos;
+ /* only handle master channel */
+ debug("ID3v2: it is for the master channel");
+ /* two bytes adjustment, one byte for bits representing peak - n bytes, eh bits, for peak */
+ /* 16 bit signed integer = dB * 512. Do not shift signed integers! Multiply instead.
+ Also no implementation-defined casting. Reinterpret the pointer to signed char, then do
+ proper casting. */
+ fr->rva.gain[rva_mode] = (float) (
+ ((short)((signed char*)realdata)[pos]) * 256 + (short)realdata[pos+1] ) / 512;
+ pos += 2;
+ if(VERBOSE3) fprintf(stderr, "Note: RVA value %fdB\n", fr->rva.gain[rva_mode]);
+ /* heh, the peak value is represented by a number of bits - but in what manner? Skipping that part */
+ fr->rva.peak[rva_mode] = 0;
+ fr->rva.level[rva_mode] = rva2+1;
}
}
- break;
- /* non-rva metainfo, simply store... */
- case text:
- process_text(fr, realdata, realsize, id);
- break;
- case picture:
- if (fr->p.flags & MPG123_PICTURE)
- process_picture(fr, realdata, realsize);
-
- break;
- default: if(NOQUIET) error1("ID3v2: unknown frame type %i", tt);
}
- if(unsyncbuffer)
- free(unsyncbuffer);
+ break;
+ /* non-rva metainfo, simply store... */
+ case text:
+ process_text(fr, realdata, realsize, id);
+ break;
+ case picture:
+ if (fr->p.flags & MPG123_PICTURE)
+ process_picture(fr, realdata, realsize);
+
+ break;
+ default: if(NOQUIET) error1("ID3v2: unknown frame type %i", tt);
}
- #undef BAD_FFLAGS
- #undef PRES_TAG_FFLAG
- #undef PRES_FILE_FFLAG
- #undef READ_ONLY_FFLAG
- #undef GROUP_FFLAG
- #undef COMPR_FFLAG
- #undef ENCR_FFLAG
- #undef UNSYNC_FFLAG
- #undef DATLEN_FFLAG
+ if(unsyncbuffer)
+ free(unsyncbuffer);
}
- else break;
- #undef KNOWN_FRAMES
+ #undef BAD_FFLAGS
+ #undef PRES_TAG_FFLAG
+ #undef PRES_FILE_FFLAG
+ #undef READ_ONLY_FFLAG
+ #undef GROUP_FFLAG
+ #undef COMPR_FFLAG
+ #undef ENCR_FFLAG
+ #undef UNSYNC_FFLAG
+ #undef DATLEN_FFLAG
}
+ else break;
+ #undef KNOWN_FRAMES
}
- }
- else
+ } else
{
- /* There are tags with zero length. Strictly not an error, then. */
- if(length > 0 && NOQUIET && ret2 != MPG123_NEED_MORE) error("ID3v2: Duh, not able to read ID3v2 tag data.");
- ret = ret2;
+ if(VERBOSE3)
+ fprintf(stderr, "Note: faulty ID3v2 tag still clearing old data\n");
+ reset_id3(fr);
}
-tagparse_cleanup:
- free(tagdata);
+ } else // No new data, but still there was a tag that invalidates old data.
+ {
+ if(VERBOSE3)
+ fprintf(stderr, "Note: empty ID3v2 clearing old data\n");
+ reset_id3(fr);
}
- else
+tagparse_cleanup:
+ /* Get rid of stored raw data that should not be kept. */
+ if(!(fr->p.flags & MPG123_STORE_RAW_ID3))
{
- if(NOQUIET) error1("ID3v2: Arrg! Unable to allocate %lu bytes for interpreting ID3v2 data - trying to skip instead.", length);
- if((ret2 = fr->rd->skip_bytes(fr,length)) < 0) ret = ret2; /* will not store data in backbuff! */
- else ret = 0;
+ free(fr->id3v2_raw);
+ fr->id3v2_raw = NULL;
+ fr->id3v2_size = 0;
}
}
#endif /* NO_ID3V2 */
- /* skip footer if present */
- if((ret > 0) && (flags & FOOTER_FLAG) && ((ret2 = fr->rd->skip_bytes(fr,length)) < 0)) ret = ret2;
-
return ret;
#undef UNSYNC_FLAG
#undef EXTHEAD_FLAG
+ #undef COMPRESS_FLAG
#undef EXP_FLAG
#undef FOOTER_FLAG
+ #undef EXT_UPDATE_FLAG
#undef UNKOWN_FLAGS
}
debug1("UTF-8 length: %lu", (unsigned long)length);
/* one extra zero byte for paranoia */
- if(!mpg123_resize_string(sb, length+1)){ mpg123_free_string(sb); return ; }
+ if(!mpg123_grow_string(sb, length+1))
+ return;
p = (unsigned char*) sb->p; /* Signedness doesn't matter but it shows I thought about the non-issue */
for(i=0; i<l; ++i)
else length += UTF8LEN(point); /* 1,2 or 3 bytes */
}
- if(!mpg123_resize_string(sb, length+1)){ mpg123_free_string(sb); return ; }
+ if(!mpg123_grow_string(sb, length+1))
+ return;
/* Now really convert, skip checks as these have been done just before. */
p = (unsigned char*) sb->p; /* Signedness doesn't matter but it shows I thought about the non-issue */
static void convert_utf8(mpg123_string *sb, const unsigned char* source, size_t len, const int noquiet)
{
- if(mpg123_resize_string(sb, len+1))
+ if(mpg123_grow_string(sb, len+1))
{
memcpy(sb->p, source, len);
sb->p[len] = 0;
sb->fill = len+1;
}
- else mpg123_free_string(sb);
}
#endif
/*
index: frame index data structure and functions
- copyright 2007-2015 by the mpg123 project
+ copyright 2007-2020 by the mpg123 project
-= free software under the terms of the LGPL 2.1 =-
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
fi->next = fi_next(fi);
debug2("new index of size %lu at %p", (unsigned long)fi->size, (void*)fi->data);
return 0;
- }
- else
- {
- error("failed to resize index!");
+ } else
return -1;
- }
}
void fi_add(struct frame_index *fi, off_t pos)
return 0;
}
+#define NEED_BITS(fr, num) \
+ if((fr)->bits_avail < num) \
+ { \
+ if(NOQUIET) \
+ error2("%u bits needed, %li available", num, (fr)->bits_avail); \
+ return -1; \
+ } \
+
static int I_step_one(unsigned int balloc[], unsigned int scale_index[2][SBLIMIT],mpg123_handle *fr)
{
unsigned int *ba=balloc;
{
int i;
int jsbound = fr->jsbound;
+ unsigned int needbits = jsbound*2*4 + (SBLIMIT-jsbound)*4;
+
+ NEED_BITS(fr, needbits);
+ needbits = 0;
for(i=0;i<jsbound;i++)
{
- *ba++ = getbits(fr, 4);
- *ba++ = getbits(fr, 4);
+ ba[0] = getbits_fast(fr, 4);
+ ba[1] = getbits_fast(fr, 4);
+ needbits += ((ba[0]?1:0)+(ba[1]?1:0))*6;
+ ba+=2;
+ }
+ for(i=jsbound;i<SBLIMIT;i++)
+ {
+ *ba = getbits_fast(fr, 4);
+ needbits += (*ba?1:0)*12;
+ ++ba;
}
- for(i=jsbound;i<SBLIMIT;i++) *ba++ = getbits(fr, 4);
if(check_balloc(fr, balloc, ba)) return -1;
ba = balloc;
-
+ NEED_BITS(fr, needbits)
for(i=0;i<jsbound;i++)
{
if ((*ba++))
- *sca++ = getbits(fr, 6);
+ *sca++ = getbits_fast(fr, 6);
if ((*ba++))
- *sca++ = getbits(fr, 6);
+ *sca++ = getbits_fast(fr, 6);
}
- for (i=jsbound;i<SBLIMIT;i++)
- if((*ba++))
+ for (i=jsbound;i<SBLIMIT;i++) if((*ba++))
{
- *sca++ = getbits(fr, 6);
- *sca++ = getbits(fr, 6);
+ *sca++ = getbits_fast(fr, 6);
+ *sca++ = getbits_fast(fr, 6);
}
}
else
{
int i;
- for(i=0;i<SBLIMIT;i++) *ba++ = getbits(fr, 4);
+ unsigned int needbits = SBLIMIT*4;
+
+ NEED_BITS(fr, needbits)
+ needbits = 0;
+ for(i=0;i<SBLIMIT;i++)
+ {
+ *ba = getbits_fast(fr, 4);
+ needbits += (*ba?1:0)*6;
+ ++ba;
+ }
if(check_balloc(fr, balloc, ba)) return -1;
ba = balloc;
+ NEED_BITS(fr, needbits)
for (i=0;i<SBLIMIT;i++)
- if ((*ba++))
- *sca++ = getbits(fr, 6);
+ if ((*ba++))
+ *sca++ = getbits_fast(fr, 6);
}
return 0;
/* Something sane in place of undefined (-1)<<n. Well, not really. */
#define MINUS_SHIFT(n) ( (int)(((unsigned int)-1)<<(n)) )
-static void I_step_two(real fraction[2][SBLIMIT],unsigned int balloc[2*SBLIMIT], unsigned int scale_index[2][SBLIMIT],mpg123_handle *fr)
+static int I_step_two(real fraction[2][SBLIMIT],unsigned int balloc[2*SBLIMIT], unsigned int scale_index[2][SBLIMIT],mpg123_handle *fr)
{
int i,n;
int smpb[2*SBLIMIT]; /* values: 0-65535 */
if(fr->stereo == 2)
{
+ unsigned int needbits = 0;
int jsbound = fr->jsbound;
register real *f0 = fraction[0];
register real *f1 = fraction[1];
+
+ ba = balloc;
+ for(sample=smpb,i=0;i<jsbound;i++)
+ {
+ if((n=*ba++))
+ needbits += n+1;
+ if((n=*ba++))
+ needbits += n+1;
+ }
+ for(i=jsbound;i<SBLIMIT;i++)
+ if((n = *ba++))
+ needbits += n+1;
+ NEED_BITS(fr, needbits)
+
ba = balloc;
for(sample=smpb,i=0;i<jsbound;i++)
{
}
else
{
+ unsigned int needbits = 0;
register real *f0 = fraction[0];
+
+ ba = balloc;
+ for(sample=smpb,i=0;i<SBLIMIT;i++)
+ if((n = *ba++))
+ needbits += n+1;
+ NEED_BITS(fr, needbits);
+
ba = balloc;
for(sample=smpb,i=0;i<SBLIMIT;i++)
if ((n = *ba++))
*sample++ = getbits(fr, n+1);
+
ba = balloc;
for(sample=smpb,i=0;i<SBLIMIT;i++)
{
for(i=fr->down_sample_sblimit;i<32;i++)
fraction[0][i] = DOUBLE_TO_REAL(0.0);
}
+ return 0;
}
int do_layer1(mpg123_handle *fr)
if(I_step_one(balloc,scale_index,fr))
{
- if(NOQUIET) error("Aborting layer I decoding after step one.\n");
+ if(NOQUIET)
+ error("Aborting layer I decoding after step one.");
return clip;
}
for(i=0;i<SCALE_BLOCK;i++)
{
- I_step_two(fraction,balloc,scale_index,fr);
+ if(I_step_two(fraction,balloc,scale_index,fr))
+ {
+ if(NOQUIET)
+ error("Aborting layer I decoding after step two.");
+ return clip;
+ }
if(single != SINGLE_STEREO)
clip += (fr->synth_mono)(fraction[single], fr);
#ifndef NO_LAYER2
-static void II_step_one(unsigned int *bit_alloc,int *scale,mpg123_handle *fr)
+static int II_step_one(unsigned int *bit_alloc,int *scale,mpg123_handle *fr)
{
int stereo = fr->stereo-1;
int sblimit = fr->II_sblimit;
unsigned int scfsi_buf[64];
unsigned int *scfsi,*bita;
int sc,step;
+ /* Count the bits needed for getbits_fast(). */
+ unsigned int needbits = 0;
+ unsigned int scale_bits[4] = { 18, 12, 6, 12 };
bita = bit_alloc;
if(stereo)
for(i=jsbound;i;i--,alloc1+=(1<<step))
{
step=alloc1->bits;
- *bita++ = (char) getbits(fr, step);
- *bita++ = (char) getbits(fr, step);
+ bita[0] = (char) getbits(fr, step);
+ bita[1] = (char) getbits(fr, step);
+ needbits += ((bita[0]?1:0)+(bita[1]?1:0))*2;
+ bita+=2;
}
for(i=sblimit-jsbound;i;i--,alloc1+=(1<<step))
{
step=alloc1->bits;
bita[0] = (char) getbits(fr, step);
bita[1] = bita[0];
+ needbits += (bita[0]?1:0)*2*2;
bita+=2;
}
bita = bit_alloc;
scfsi=scfsi_buf;
+ if(fr->bits_avail < needbits)
+ {
+ if(NOQUIET)
+ error2("need %u bits, have %li", needbits, fr->bits_avail);
+ return -1;
+ }
for(i=sblimit2;i;i--)
if(*bita++) *scfsi++ = (char) getbits_fast(fr, 2);
}
for(i=sblimit;i;i--,alloc1+=(1<<step))
{
step=alloc1->bits;
- *bita++ = (char) getbits(fr, step);
+ *bita = (char) getbits(fr, step);
+ if(*bita)
+ needbits += 2;
+ ++bita;
}
bita = bit_alloc;
scfsi=scfsi_buf;
+ if(fr->bits_avail < needbits)
+ {
+ if(NOQUIET)
+ error2("need %u bits, have %li", needbits, fr->bits_avail);
+ return -1;
+ }
for(i=sblimit;i;i--)
if(*bita++) *scfsi++ = (char) getbits_fast(fr, 2);
}
+ needbits = 0;
+ bita = bit_alloc;
+ scfsi=scfsi_buf;
+ for(i=sblimit2;i;--i)
+ if(*bita++)
+ needbits += scale_bits[*scfsi++];
+ if(fr->bits_avail < needbits)
+ {
+ if(NOQUIET)
+ error2("need %u bits, have %li", needbits, fr->bits_avail);
+ return -1;
+ }
+
bita = bit_alloc;
scfsi=scfsi_buf;
- for(i=sblimit2;i;i--)
+ for(i=sblimit2;i;--i)
if(*bita++)
switch(*scfsi++)
{
*scale++ = sc;
break;
}
+
+ return 0;
}
}
else
fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = DOUBLE_TO_REAL(0.0);
+ if(fr->bits_avail < 0)
+ return; /* Caller checks that again. */
}
}
fraction[0][2][i] = REAL_SCALE_LAYER12(fr->muls[*tab][m1]); fraction[1][2][i] = REAL_SCALE_LAYER12(fr->muls[*tab][m2]);
}
scale+=6;
+ if(fr->bits_avail < 0)
+ return; /* Caller checks that again. */
}
else
{
if(stereo == 1 || single == SINGLE_MIX) /* also, mix not really handled */
single = SINGLE_LEFT;
- II_step_one(bit_alloc, scale, fr);
+ if(II_step_one(bit_alloc, scale, fr))
+ {
+ if(NOQUIET)
+ error("first step of layer I decoding failed");
+ return clip;
+ }
for(i=0;i<SCALE_BLOCK;i++)
{
II_step_two(bit_alloc,fraction,scale,fr,i>>2);
+ if(fr->bits_avail < 0)
+ {
+ if(NOQUIET)
+ error("missing bits in layer II step two");
+ return clip;
+ }
for(j=0;j<3;j++)
{
if(single != SINGLE_STEREO)
const int tabs[2][5] = { { 2,9,5,3,4 } , { 1,8,1,2,9 } };
const int *tab = tabs[fr->lsf];
+ { /* First ensure we got enough bits available. */
+ unsigned int needbits = 0;
+ needbits += tab[1]; /* main_data_begin */
+ needbits += stereo == 1 ? tab[2] : tab[3]; /* private */
+ if(!fr->lsf)
+ needbits += stereo*4; /* scfsi */
+ /* For each granule for each channel ... */
+ needbits += tab[0]*stereo*(29+tab[4]+1+22+(!fr->lsf?1:0)+2);
+ if(fr->bits_avail < needbits) \
+ {
+ if(NOQUIET)
+ error2( "%u bits for side info needed, only %li available"
+ , needbits, fr->bits_avail );
+ return 1;
+ }
+ }
+
si->main_data_begin = getbits(fr, tab[1]);
if(si->main_data_begin > fr->bitreservoir)
}
/* Keep track of the available data bytes for the bit reservoir.
- Think: Substract the 2 crc bytes in parser already? */
- fr->bitreservoir = fr->bitreservoir + fr->framesize - fr->ssize - (fr->error_protection ? 2 : 0);
+ CRC is included in ssize already. */
+ fr->bitreservoir = fr->bitreservoir + fr->framesize - fr->ssize;
+
/* Limit the reservoir to the max for MPEG 1.0 or 2.x . */
if(fr->bitreservoir > (unsigned int) (fr->lsf == 0 ? 511 : 255))
fr->bitreservoir = (fr->lsf == 0 ? 511 : 255);
/* Now back into less commented territory. It's code. It works. */
if (stereo == 1)
- si->private_bits = getbits_fast(fr, tab[2]);
+ si->private_bits = getbits(fr, tab[2]);
else
- si->private_bits = getbits_fast(fr, tab[3]);
+ si->private_bits = getbits(fr, tab[3]);
if(!fr->lsf) for(ch=0; ch<stereo; ch++)
{
si->ch[ch].gr[0].scfsi = -1;
- si->ch[ch].gr[1].scfsi = getbits_fast(fr, 4);
+ si->ch[ch].gr[1].scfsi = getbits(fr, 4);
}
for (gr=0; gr<tab[0]; gr++)
for (ch=0; ch<stereo; ch++)
{
register struct gr_info_s *gr_info = &(si->ch[ch].gr[gr]);
-
+ unsigned int qss;
gr_info->part2_3_length = getbits(fr, 12);
gr_info->big_values = getbits(fr, 9);
if(gr_info->big_values > 288)
if(NOQUIET) error("big_values too large!");
gr_info->big_values = 288;
}
- gr_info->pow2gain = fr->gainpow2+256 - getbits_fast(fr, 8) + powdiff;
- if(ms_stereo) gr_info->pow2gain += 2;
+ qss = getbits_fast(fr, 8);
+ gr_info->pow2gain = fr->gainpow2+256 - qss + powdiff;
+ if(ms_stereo)
+ gr_info->pow2gain += 2;
+#ifndef NO_MOREINFO
+ if(fr->pinfo)
+ fr->pinfo->qss[gr][ch] = qss;
+#endif
gr_info->scalefac_compress = getbits(fr, tab[4]);
if(gr_info->part2_3_length == 0)
{
- if(gr_info->scalefac_compress > 0)
- debug1( "scalefac_compress _should_ be zero instead of %i"
+ if(gr_info->scalefac_compress > 0 && VERBOSE2)
+ error1( "scalefac_compress should be zero instead of %i"
, gr_info->scalefac_compress );
gr_info->scalefac_compress = 0;
}
- if(get1bit(fr))
+ /* 22 bits for if/else block */
+ if(getbits(fr,1))
{ /* window switch flag */
int i;
gr_info->block_type = getbits_fast(fr, 2);
*/
gr_info->table_select[2] = 0;
for(i=0;i<3;i++)
- gr_info->full_gain[i] = gr_info->pow2gain + (getbits_fast(fr, 3)<<3);
+ {
+ unsigned int sbg = (getbits_fast(fr, 3)<<3);
+ gr_info->full_gain[i] = gr_info->pow2gain + sbg;
+#ifndef NO_MOREINFO
+ if(fr->pinfo)
+ fr->pinfo->sub_gain[gr][ch][i] = sbg / 8;
+#endif
+ }
if(gr_info->block_type == 0)
{
if(gr_info->block_type == 2)
{
int i=18;
- numbits = (num0 + num1) * 18;
+ numbits = (num0 + num1) * 18 /* num0 * (17+1?) + num1 * 18 */
+ - (gr_info->mixed_block_flag ? num0 : 0);
+ if(numbits > gr_info->part2_3_length)
+ return -1;
if(gr_info->mixed_block_flag)
{
*scf++ = getbits_fast(fr, num0);
i = 9;
- numbits -= num0; /* num0 * 17 + num1 * 18 */
}
for(;i;i--) *scf++ = getbits_fast(fr, num0);
if(scfsi < 0)
{ /* scfsi < 0 => granule == 0 */
+ numbits = (num0 + num1) * 10 + num0;
+ if(numbits > gr_info->part2_3_length)
+ return -1;
+
for(i=11;i;i--) *scf++ = getbits_fast(fr, num0);
for(i=10;i;i--) *scf++ = getbits_fast(fr, num1);
- numbits = (num0 + num1) * 10 + num0;
*scf++ = 0;
}
else
{
- numbits = 0;
+ numbits = !(scfsi & 0x8) * num0 * 6
+ + !(scfsi & 0x4) * num0 * 5
+ + !(scfsi & 0x2) * num1 * 5
+ + !(scfsi & 0x1) * num1 * 5;
+ if(numbits > gr_info->part2_3_length)
+ return -1;
+
if(!(scfsi & 0x8))
{
for (i=0;i<6;i++) *scf++ = getbits_fast(fr, num0);
-
- numbits += num0 * 6;
}
else scf += 6;
if(!(scfsi & 0x4))
{
for (i=0;i<5;i++) *scf++ = getbits_fast(fr, num0);
-
- numbits += num0 * 5;
}
else scf += 5;
if(!(scfsi & 0x2))
{
for(i=0;i<5;i++) *scf++ = getbits_fast(fr, num1);
-
- numbits += num1 * 5;
}
else scf += 5;
if(!(scfsi & 0x1))
{
for (i=0;i<5;i++) *scf++ = getbits_fast(fr, num1);
-
- numbits += num1 * 5;
}
else scf += 5;
*scf++ = 0; /* no l[21] in original sources */
}
}
+
return numbits;
}
{
const unsigned char *pnt;
int i,j,n=0,numbits=0;
- unsigned int slen;
+ unsigned int slen, slen2;
const unsigned char stab[3][6][4] =
{
if(gr_info->part2_3_length == 0)
{
- int i;
for(i=0;i<39;i++)
*scf++ = 0;
return 0;
}
+ slen2 = slen;
+ for(i=0;i<4;i++)
+ {
+ int num = slen2 & 0x7;
+ slen2 >>= 3;
+ if(num)
+ numbits += pnt[i] * num;
+ }
+ if(numbits > gr_info->part2_3_length)
+ return -1;
+
for(i=0;i<4;i++)
{
int num = slen & 0x7;
if(num)
{
for(j=0;j<(int)(pnt[i]);j++) *scf++ = getbits_fast(fr, num);
-
- numbits += pnt[i] * num;
}
else
for(j=0;j<(int)(pnt[i]);j++) *scf++ = 0;
mask <<= 8-num;
part2remain -= num;
+ /* Bitindex is zero now, we are allowed to use getbyte(). */
+
{
int bv = gr_info->big_values;
int region1 = gr_info->region1start;
if( (!mc) )
{
mc = *m++;
+//fprintf(stderr, "%i setting xrpnt = xr + %i (%ld)\n", __LINE__, *m, xrpnt-(real*)xr);
xrpnt = ((real *) xr) + (*m++);
lwin = *m++;
cb = *m++;
if(!mc)
{
mc = *m++;
+//fprintf(stderr, "%i setting xrpnt = xr + %i (%ld)\n", __LINE__, *m, xrpnt-(real*)xr);
xrpnt = ((real *) xr) + (*m++);
lwin = *m++;
cb = *m++;
part2remain += num;
backbits(fr, num);
num = 0;
+
}
else
{
if(part2remain > 0) skipbits(fr, part2remain);
else if(part2remain < 0)
{
- debug1("Can't rewind stream by %d bits!",-part2remain);
+ if(VERBOSE2)
+ error1("Can't rewind stream by %d bits!",-part2remain);
return 1; /* -> error */
}
return 0;
}
}
+#ifndef NO_MOREINFO
+static void fill_pinfo_side(mpg123_handle *fr, struct III_sideinfo *si, int gr, int stereo1)
+{
+ int i, sb;
+ float ifqstep; /* Why not double? */
+ int ch, ss;;
+
+ for(ch = 0; ch < stereo1; ++ch)
+ {
+ struct gr_info_s *gr_infos = &(si->ch[ch].gr[gr]);
+ fr->pinfo->big_values[gr][ch] = gr_infos->big_values;
+ fr->pinfo->scalefac_scale[gr][ch] = gr_infos->scalefac_scale;
+ fr->pinfo->mixed[gr][ch] = gr_infos->mixed_block_flag;
+ fr->pinfo->blocktype[gr][ch] = gr_infos->block_type;
+ fr->pinfo->mainbits[gr][ch] = gr_infos->part2_3_length;
+ fr->pinfo->preflag[gr][ch] = gr_infos->preflag;
+ if(gr == 1)
+ fr->pinfo->scfsi[ch] = gr_infos->scfsi;
+ }
+
+ for(ch = 0; ch < stereo1; ++ch)
+ {
+ struct gr_info_s *gr_infos = &(si->ch[ch].gr[gr]);
+ ifqstep = (fr->pinfo->scalefac_scale[gr][ch] == 0) ? .5 : 1.0;
+ if(2 == gr_infos->block_type)
+ {
+ for(i = 0; i < 3; ++i)
+ {
+ for(sb = 0; sb < 12; ++sb)
+ {
+ int j = 3 * sb + i;
+ /*
+ is_p = scalefac[sfb*3+lwin-gr_infos->mixed_block_flag];
+ */
+ /* scalefac was copied into pinfo->sfb_s[] before */
+ fr->pinfo->sfb_s[gr][ch][j] = -ifqstep *
+ fr->pinfo->sfb_s[gr][ch][j - gr_infos->mixed_block_flag];
+ fr->pinfo->sfb_s[gr][ch][j] -= 2 *
+ (fr->pinfo->sub_gain[gr][ch][i]);
+ }
+ fr->pinfo->sfb_s[gr][ch][3 * sb + i] =
+ -2 * (fr->pinfo->sub_gain[gr][ch][i]);
+ }
+ } else
+ {
+ for(sb = 0; sb < 21; ++sb)
+ {
+ /* scalefac was copied into pinfo->sfb[] before */
+ fr->pinfo->sfb[gr][ch][sb] = fr->pinfo->sfb_s[gr][ch][sb];
+ if (gr_infos->preflag)
+ fr->pinfo->sfb[gr][ch][sb] += pretab_choice[1][sb];
+ fr->pinfo->sfb[gr][ch][sb] *= -ifqstep;
+ }
+ fr->pinfo->sfb[gr][ch][21] = 0;
+ }
+ }
+
+
+ for(ch = 0; ch < stereo1; ++ch)
+ {
+ int j = 0;
+ for(sb = 0; sb < SBLIMIT; ++sb)
+ for (ss = 0; ss < SSLIMIT; ++ss, ++j)
+ fr->pinfo->xr[gr][ch][j] = fr->layer3.hybrid_in[ch][sb][ss];
+ }
+}
+#endif
/* And at the end... the main layer3 handler */
int do_layer3(mpg123_handle *fr)
return clip;
}
- set_pointer(fr,sideinfo.main_data_begin);
-
+ set_pointer(fr, 1, sideinfo.main_data_begin);
+#ifndef NO_MOREINFO
+ if(fr->pinfo)
+ {
+ fr->pinfo->maindata = sideinfo.main_data_begin;
+ fr->pinfo->padding = fr->padding;
+ }
+#endif
for(gr=0;gr<granules;gr++)
{
/* hybridIn[2][SBLIMIT][SSLIMIT] */
{
struct gr_info_s *gr_info = &(sideinfo.ch[0].gr[gr]);
long part2bits;
+ if(gr_info->part2_3_length > fr->bits_avail)
+ {
+ if(NOQUIET)
+ error2(
+ "part2_3_length (%u) too large for available bit count (%li)"
+ , gr_info->part2_3_length, fr->bits_avail );
+ return clip;
+ }
if(fr->lsf)
part2bits = III_get_scale_factors_2(fr, scalefacs[0],gr_info,0);
else
part2bits = III_get_scale_factors_1(fr, scalefacs[0],gr_info,0,gr);
+ if(part2bits < 0)
+ {
+ if(VERBOSE2)
+ error("not enough bits for scale factors");
+ return clip;
+ }
+
+#ifndef NO_MOREINFO
+ if(fr->pinfo)
+ {
+ int i;
+ fr->pinfo->sfbits[gr][0] = part2bits;
+ for(i=0; i<39; ++i)
+ fr->pinfo->sfb_s[gr][0][i] = scalefacs[0][i];
+ }
+#endif
+
if(III_dequantize_sample(fr, hybridIn[0], scalefacs[0],gr_info,sfreq,part2bits))
{
- if(VERBOSE2) error("dequantization failed!");
+ if(NOQUIET)
+ error("dequantization failed!");
+ return clip;
+ }
+ if(fr->bits_avail < 0)
+ {
+ if(NOQUIET)
+ error("bit deficit after dequant");
return clip;
}
}
else
part2bits = III_get_scale_factors_1(fr, scalefacs[1],gr_info,1,gr);
+ if(part2bits < 0)
+ {
+ if(VERBOSE2)
+ error("not enough bits for scale factors");
+ return clip;
+ }
+
+#ifndef NO_MOREINFO
+ if(fr->pinfo)
+ {
+ int i;
+ fr->pinfo->sfbits[gr][1] = part2bits;
+ for(i=0; i<39; ++i)
+ fr->pinfo->sfb_s[gr][1][i] = scalefacs[1][i];
+ }
+#endif
+
if(III_dequantize_sample(fr, hybridIn[1],scalefacs[1],gr_info,sfreq,part2bits))
{
- if(VERBOSE2) error("dequantization failed!");
+ if(NOQUIET)
+ error("dequantization failed!");
+ return clip;
+ }
+ if(fr->bits_avail < 0)
+ {
+ if(NOQUIET)
+ error("bit deficit after dequant");
return clip;
}
}
}
+#ifndef NO_MOREINFO
+ if(fr->pinfo)
+ fill_pinfo_side(fr, &sideinfo, gr, stereo1);
+#endif
+
for(ch=0;ch<stereo1;ch++)
{
struct gr_info_s *gr_info = &(sideinfo.ch[ch].gr[gr]);
/*
lfs_alias: Aliases to the small/native API functions with the size of long int as suffix.
- copyright 2010-2013 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright 2010-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
}' < mpg123.h.in
*/
+int NATIVE_NAME(mpg123_open_fixed)( mpg123_handle *mh, const char *path
+, int channels, int encoding );
+int attribute_align_arg ALIAS_NAME(mpg123_open_fixed)( mpg123_handle *mh, const char *path
+, int channels, int encoding )
+{
+ return NATIVE_NAME(mpg123_open_fixed)(mh, path, channels, encoding);
+}
+
int NATIVE_NAME(mpg123_open)(mpg123_handle *mh, const char *path);
int attribute_align_arg ALIAS_NAME(mpg123_open)(mpg123_handle *mh, const char *path)
{
/*
lfs_wrap: Crappy wrapper code for supporting crappy ambiguous large file support.
- copyright 2010 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright 2010-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis, thanks to Guido Draheim for consulting
case IO_FD: return ioh->r_lseek(ioh->fd, smalloff, whence);
case IO_HANDLE: return ioh->r_h_lseek(ioh->handle, smalloff, whence);
}
- error("Serious breakage - bad IO type in LFS wrapper!");
return -1;
}
else
#undef mpg123_replace_reader
#undef mpg123_replace_reader_handle
#undef mpg123_open
+#undef mpg123_open_fixed
#undef mpg123_open_fd
#undef mpg123_open_handle
else return MPG123_LARGENAME(mpg123_open)(mh, path);
}
+// This one needs to follow the logic of the original, and wrap the actual
+// mpg123_open() here.
+int attribute_align_arg mpg123_open_fixed( mpg123_handle *mh, const char *path
+, int channels, int encoding )
+{
+ int err = open_fixed_pre(mh, channels, encoding);
+ if(err == MPG123_OK)
+ err = mpg123_open(mh, path);
+ if(err == MPG123_OK)
+ err = open_fixed_post(mh, channels, encoding);
+ return err;
+}
+
/*
This is in fact very similar to the above:
The open routines always need to watch out for a prepared wrapper handle to use replaced normal I/O.
/*
libmpg123: MPEG Audio Decoder library
- copyright 1995-2014 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright 1995-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
*/
/* Want accurate rounding function regardless of decoder setup. */
#define FORCE_ACCURATE
#include "sample.h"
+#include "parse.h"
#define SEEKFRAME(mh) ((mh)->ignoreframe < 0 ? 0 : (mh)->ignoreframe)
void attribute_align_arg mpg123_exit(void)
{
/* nothing yet, but something later perhaps */
+ /* Nope. This is dead space. */
}
/* create a new handle with specified decoder, decoder can be "", "auto" or NULL for auto-detection */
#ifdef FRAME_INDEX
mp->index_size = val;
#else
- ret = MPG123_NO_INDEX;
+ if(val) // It is only an eror if you want to enable the index.
+ ret = MPG123_NO_INDEX;
#endif
break;
case MPG123_PREFRAMES:
ret = MPG123_MISSING_FEATURE;
#endif
break;
+ case MPG123_FREEFORMAT_SIZE:
+ mp->freeformat_framesize = val;
+ break;
default:
ret = MPG123_BAD_PARAM;
}
ret = MPG123_MISSING_FEATURE;
#endif
break;
+ case MPG123_FREEFORMAT_SIZE:
+ *val = mp->freeformat_framesize;
+ break;
default:
ret = MPG123_BAD_PARAM;
}
theval = mh->state_flags & FRAME_FRESH_DECODER;
mh->state_flags &= ~FRAME_FRESH_DECODER;
break;
+ case MPG123_ENC_DELAY:
+ theval = mh->enc_delay;
+ break;
+ case MPG123_ENC_PADDING:
+ theval = mh->enc_padding;
+ break;
+ case MPG123_DEC_DELAY:
+ theval = mh->lay == 3 ? GAPLESS_DELAY : -1;
+ break;
default:
mh->err = MPG123_BAD_KEY;
ret = MPG123_ERR;
return ret;
}
+
int attribute_align_arg mpg123_eq(mpg123_handle *mh, enum mpg123_channels channel, int band, double val)
{
#ifndef NO_EQUALIZER
return open_stream(mh, path, -1);
}
+// The convenience function mpg123_open_fixed() wraps over acual mpg123_open
+// and hence needs to have the exact same code in lfs_wrap.c. The flesh is
+// in open_fixed_pre() and open_fixed_post(), wich are only defined here.
+int open_fixed_pre(mpg123_handle *mh, int channels, int encoding)
+{
+ if(!mh)
+ return MPG123_BAD_HANDLE;
+ mh->p.flags |= MPG123_NO_FRANKENSTEIN;
+ int err = mpg123_format_none(mh);
+ if(err == MPG123_OK)
+ err = mpg123_format2(mh, 0, channels, encoding);
+ return err;
+}
+
+int open_fixed_post(mpg123_handle *mh, int channels, int encoding)
+{
+ if(!mh)
+ return MPG123_BAD_HANDLE;
+ long rate;
+ int err = mpg123_getformat(mh, &rate, &channels, &encoding);
+ if(err == MPG123_OK)
+ err = mpg123_format_none(mh);
+ if(err == MPG123_OK)
+ err = mpg123_format(mh, rate, channels, encoding);
+ if(err == MPG123_OK)
+ {
+ if(mh->track_frames < 1 && (mh->rdat.flags & READER_SEEKABLE))
+ {
+ debug("open_fixed_post: scan because we can seek and do not know track_frames");
+ err = mpg123_scan(mh);
+ }
+ }
+ if(err != MPG123_OK)
+ mpg123_close(mh);
+ return err;
+}
+
+int attribute_align_arg mpg123_open_fixed( mpg123_handle *mh, const char *path
+, int channels, int encoding )
+{
+ int err = open_fixed_pre(mh, channels, encoding);
+ if(err == MPG123_OK)
+ err = mpg123_open(mh, path);
+ if(err == MPG123_OK)
+ err = open_fixed_post(mh, channels, encoding);
+ return err;
+}
+
int attribute_align_arg mpg123_open_fd(mpg123_handle *mh, int fd)
{
if(mh == NULL) return MPG123_BAD_HANDLE;
{
mh->down_sample_sblimit = SBLIMIT * mh->af.rate;
mh->down_sample_sblimit /= frame_freq(mh);
+ if(mh->down_sample_sblimit < 1)
+ mh->down_sample_sblimit = 1;
}
else mh->down_sample_sblimit = SBLIMIT;
mh->outblock = outblock_bytes(mh,
do_rva(mh);
debug3("done updating decoder structure with native rate %li and af.rate %li and down_sample %i", frame_freq(mh), mh->af.rate, mh->down_sample);
+ mh->decoder_change = 0;
return 0;
}
else return MPG123_ERR; /* Some real error. */
}
/* Now, there should be new data to decode ... and also possibly new stream properties */
- if(mh->header_change > 1)
+ if(mh->header_change > 1 || mh->decoder_change)
{
- debug("big header change");
+ debug("big header or decoder change");
change = 1;
mh->header_change = 0;
/* Need to update decoder structure right away since frame might need to
All other situations resulted in returns from the loop. */
if(change)
{
- mh->decoder_change = 0;
if(mh->fresh)
{
#ifdef GAPLESS
if(mh == NULL) return MPG123_BAD_HANDLE;
if(mh->buffer.size < mh->outblock) return MPG123_NO_SPACE;
mh->buffer.fill = 0; /* always start fresh */
+ /* Be nice: Set these also for sensible values in case of error. */
+ if(audio) *audio = NULL;
+ if(bytes) *bytes = 0;
while(TRUE)
{
/* decode if possible */
if(mh->to_decode)
{
+ if(num != NULL) *num = mh->num;
if(mh->new_format)
{
debug("notifiying new format");
mh->new_format = 0;
return MPG123_NEW_FORMAT;
}
- if(num != NULL) *num = mh->num;
debug("decoding");
+ if(mh->decoder_change && decode_update(mh) < 0)
+ return MPG123_ERR;
decode_the_frame(mh);
mh->to_decode = mh->to_ignore = FALSE;
}
}
-int attribute_align_arg mpg123_read(mpg123_handle *mh, unsigned char *out, size_t size, size_t *done)
+int attribute_align_arg mpg123_read(mpg123_handle *mh, void *out, size_t size, size_t *done)
{
return mpg123_decode(mh, NULL, 0, out, size, done);
}
}
*/
-int attribute_align_arg mpg123_decode(mpg123_handle *mh, const unsigned char *inmemory, size_t inmemsize, unsigned char *outmemory, size_t outmemsize, size_t *done)
+int attribute_align_arg mpg123_decode(mpg123_handle *mh, const unsigned char *inmemory, size_t inmemsize, void *outmem, size_t outmemsize, size_t *done)
{
int ret = MPG123_OK;
size_t mdone = 0;
+ unsigned char *outmemory = outmem;
if(done != NULL) *done = 0;
if(mh == NULL) return MPG123_BAD_HANDLE;
-#ifndef NO_FEEDER
if(inmemsize > 0 && mpg123_feed(mh, inmemory, inmemsize) != MPG123_OK)
{
ret = MPG123_ERR;
ret = MPG123_NO_SPACE;
goto decodeend;
}
+ if(mh->decoder_change && decode_update(mh) < 0)
+ {
+ ret = MPG123_ERR;
+ goto decodeend;
+ }
decode_the_frame(mh);
mh->to_decode = mh->to_ignore = FALSE;
mh->buffer.p = mh->buffer.data;
decodeend:
if(done != NULL) *done = mdone;
return ret;
-#else
- mh->err = MPG123_MISSING_FEATURE;
- return MPG123_ERR;
-#endif
}
long attribute_align_arg mpg123_clip(mpg123_handle *mh)
case 1: mi->mode = MPG123_M_JOINT; break;
case 2: mi->mode = MPG123_M_DUAL; break;
case 3: mi->mode = MPG123_M_MONO; break;
- default: error("That mode cannot be!");
+ default: mi->mode = 0; // Nothing good to do here.
}
mi->mode_ext = mh->mode_ext;
mi->framesize = mh->framesize+4; /* Include header. */
return MPG123_OK;
}
+int attribute_align_arg mpg123_id3_raw( mpg123_handle *mh
+, unsigned char **v1, size_t *v1_size
+, unsigned char **v2, size_t *v2_size )
+{
+ if(!mh)
+ return MPG123_ERR;
+ if(v1 != NULL)
+ *v1 = mh->id3buf[0] ? mh->id3buf : NULL;
+ if(v1_size != NULL)
+ *v1_size = mh->id3buf[0] ? 128 : 0;
+ if(v2 != NULL)
+ *v2 = mh->id3v2_raw;
+ if(v2_size != NULL)
+ *v2_size = mh->id3v2_size;
+ return MPG123_OK;
+}
+
int attribute_align_arg mpg123_icy(mpg123_handle *mh, char **icy_meta)
{
if(mh == NULL) return MPG123_BAD_HANDLE;
}
}
+void attribute_align_arg mpg123_free(void *ptr)
+{
+ free(ptr);
+}
+
static const char *mpg123_error[] =
{
"No error... (code 0)",
#ifndef MPG123_LIB_H
#define MPG123_LIB_H
-#include "../src/libmpg123/fmt123.h"
+#include <fmt123.h>
/** \file mpg123.h The header file for the libmpg123 MPEG Audio decoder */
#endif
#define MPG123_LARGENAME(func) MPG123_MACROCAT(func, MPG123_LARGESUFFIX)
+#define mpg123_open_fixed MPG123_LARGENAME(mpg123_open_fixed)
#define mpg123_open MPG123_LARGENAME(mpg123_open)
#define mpg123_open_fd MPG123_LARGENAME(mpg123_open_fd)
#define mpg123_open_handle MPG123_LARGENAME(mpg123_open_handle)
typedef struct mpg123_handle_struct mpg123_handle;
/** Function to initialise the mpg123 library.
- * This function is not thread-safe. Call it exactly once per process, before any other (possibly threaded) work with the library.
+ * This should be called once in a non-parallel context. It is not explicitly
+ * thread-safe, but repeated/concurrent calls still _should_ be safe as static
+ * tables are filled with the same values anyway.
*
* \return MPG123_OK if successful, otherwise an error number.
*/
MPG123_EXPORT int mpg123_init(void);
-/** Function to close down the mpg123 library.
- * This function is not thread-safe. Call it exactly once per process, before any other (possibly threaded) work with the library. */
+/** Superfluous Function to close down the mpg123 library.
+ * This was created with the thought that there sometime will be cleanup code
+ * to be run after library use. This never materialized. You can forget about
+ * this function and it is only here for old programs that do call it.
+ */
MPG123_EXPORT void mpg123_exit(void);
/** Create a handle with optional choice of decoder (named by a string, see mpg123_decoders() or mpg123_supported_decoders()).
*/
MPG123_EXPORT void mpg123_delete(mpg123_handle *mh);
+/** Free plain memory allocated within libmpg123.
+ * This is for library users that are not sure to use the same underlying
+ * memory allocator as libmpg123. It is just a wrapper over free() in
+ * the underlying C library.
+ */
+MPG123_EXPORT void mpg123_free(void *ptr);
+
/** Enumeration of the parameters types that it is possible to set/get. */
enum mpg123_parms
{
MPG123_UPSPEED, /**< play every Nth frame (integer) */
MPG123_START_FRAME, /**< start with this frame (skip frames before that, integer) */
MPG123_DECODE_FRAMES, /**< decode only this number of frames (integer) */
- MPG123_ICY_INTERVAL, /**< stream contains ICY metadata with this interval (integer) */
+ MPG123_ICY_INTERVAL, /**< Stream contains ICY metadata with this interval (integer).
+ Make sure to set this _before_ opening a stream.*/
MPG123_OUTSCALE, /**< the scale for output samples (amplitude - integer or float according to mpg123 output format, normally integer) */
MPG123_TIMEOUT, /**< timeout for reading from a stream (not supported on win32, integer) */
MPG123_REMOVE_FLAGS, /**< remove some flags (inverse of MPG123_ADD_FLAGS, integer) */
,MPG123_PREFRAMES /**< Decode/ignore that many frames in advance for layer 3. This is needed to fill bit reservoir after seeking, for example (but also at least one frame in advance is needed to have all "normal" data for layer 3). Give a positive integer value, please.*/
,MPG123_FEEDPOOL /**< For feeder mode, keep that many buffers in a pool to avoid frequent malloc/free. The pool is allocated on mpg123_open_feed(). If you change this parameter afterwards, you can trigger growth and shrinkage during decoding. The default value could change any time. If you care about this, then set it. (integer) */
,MPG123_FEEDBUFFER /**< Minimal size of one internal feeder buffer, again, the default value is subject to change. (integer) */
+ ,MPG123_FREEFORMAT_SIZE /**< Tell the parser a free-format frame size to
+ * avoid read-ahead to get it. A value of -1 (default) means that the parser
+ * will determine it. The parameter value is applied during decoder setup
+ * for a freshly opened stream only.
+ */
};
/** Flag bits for MPG123_FLAGS, use the usual binary or to combine. */
* the stream is assumed as non-seekable unless overridden.
*/
,MPG123_FORCE_SEEKABLE = 0x40000 /**< 19th bit: Force the stream to be seekable. */
+ ,MPG123_STORE_RAW_ID3 = 0x80000 /**< store raw ID3 data (even if skipping) */
+ ,MPG123_FORCE_ENDIAN = 0x100000 /**< Enforce endianess of output samples.
+ * This is not reflected in the format codes. If this flag is set along with
+ * MPG123_BIG_ENDIAN, MPG123_ENC_SIGNED16 means s16be, without
+ * MPG123_BIG_ENDIAN, it means s16le. Normal operation without
+ * MPG123_FORCE_ENDIAN produces output in native byte order.
+ */
+ ,MPG123_BIG_ENDIAN = 0x200000 /**< Choose big endian instead of little. */
+ ,MPG123_NO_READAHEAD = 0x400000 /**< Disable read-ahead in parser. If
+ * you know you provide full frames to the feeder API, this enables
+ * decoder output from the first one on, instead of having to wait for
+ * the next frame to confirm that the stream is healthy. It also disables
+ * free format support unless you provide a frame size using
+ * MPG123_FREEFORMAT_SIZE.
+ */
+ ,MPG123_FLOAT_FALLBACK = 0x800000 /**< Consider floating point output encoding only after
+ * trying other (possibly downsampled) rates and encodings first. This is to
+ * support efficient playback where floating point output is only configured for
+ * an external resampler, bypassing that resampler when the desired rate can
+ * be produced directly. This is enabled by default to be closer to older versions
+ * of libmpg123 which did not enable float automatically at all. If disabled,
+ * float is considered after the 16 bit default and higher-bit integer encodings
+ * for any rate. */
+ ,MPG123_NO_FRANKENSTEIN = 0x1000000 /**< Disable support for Frankenstein streams
+ * (different MPEG streams stiched together). Do not accept serious change of MPEG
+ * header inside a single stream. With this flag, the audio output format cannot
+ * change during decoding unless you open a new stream. This also stops decoding
+ * after an announced end of stream (Info header contained a number of frames
+ * and this number has been reached). This makes your MP3 files behave more like
+ * ordinary media files with defined structure, rather than stream dumps with
+ * some sugar. */
};
/** choices for MPG123_RVA */
,MPG123_FEATURE_PARSE_ICY /**< ICY support */
,MPG123_FEATURE_TIMEOUT_READ /**< Reader with timeout (network). */
,MPG123_FEATURE_EQUALIZER /**< tunable equalizer */
+ ,MPG123_FEATURE_MOREINFO /**< more info extraction (for frame analyzer) */
+ ,MPG123_FEATURE_OUTPUT_FLOAT32 /**< 32 bit float output */
+ ,MPG123_FEATURE_OUTPUT_FLOAT64 /**< 64 bit float output (usually never) */
};
/** Query libmpg123 features.
*/
MPG123_EXPORT int mpg123_feature(const enum mpg123_feature_set key);
+/** Query libmpg123 features with better ABI compatibility
+ *
+ * This is the same as mpg123_feature(), but this time not using
+ * the enum as argument. Compilers don't have to agree on the size of
+ * enums and hence they are not safe in public API.
+ *
+ * \param key feature selection
+ * \return 1 for success, 0 for unimplemented functions
+ */
+MPG123_EXPORT int mpg123_feature2(int key);
+
/* @} */
*
* Functions to get and select the format of the decoded audio.
*
- * Before you dive in, please be warned that you might get confused by this. This seems to happen a lot, therefore I am trying to explain in advance.
+ * Before you dive in, please be warned that you might get confused by this.
+ * This seems to happen a lot, therefore I am trying to explain in advance.
+ * If you do feel confused and just want to decode your normal MPEG audio files that
+ * don't alter properties in the middle, just use mpg123_open_fixed() with a fixed encoding
+ * and channel count and forget about a matrix of audio formats. If you want to get funky,
+ * read ahead ...
*
* The mpg123 library decides what output format to use when encountering the first frame in a stream, or actually any frame that is still valid but differs from the frames before in the prompted output format. At such a deciding point, an internal table of allowed encodings, sampling rates and channel setups is consulted. According to this table, an output format is chosen and the decoding engine set up accordingly (including optimized routines for different output formats). This might seem unusual but it just follows from the non-existence of "MPEG audio files" with defined overall properties. There are streams, streams are concatenations of (semi) independent frames. We store streams on disk and call them "MPEG audio files", but that does not change their nature as the decoder is concerned (the LAME/Xing header for gapless decoding makes things interesting again).
*
/** An array of supported standard sample rates
* These are possible native sample rates of MPEG audio files.
- * You can still force mpg123 to resample to a different one, but by default you will only get audio in one of these samplings.
+ * You can still force mpg123 to resample to a different one, but by
+ * default you will only get audio in one of these samplings.
+ * This list is in ascending order.
* \param list Store a pointer to the sample rates array there.
* \param number Store the number of sample rates there. */
MPG123_EXPORT void mpg123_rates(const long **list, size_t *number);
MPG123_EXPORT int mpg123_format( mpg123_handle *mh
, long rate, int channels, int encodings );
+/** Set the audio format support of a mpg123_handle in detail:
+ * \param mh handle
+ * \param rate The sample rate value (in Hertz). Special value 0 means
+ * all rates (the reason for this variant of mpg123_format()).
+ * \param channels A combination of MPG123_STEREO and MPG123_MONO.
+ * \param encodings A combination of accepted encodings for rate and channels,
+ * p.ex MPG123_ENC_SIGNED16 | MPG123_ENC_ULAW_8 (or 0 for no support).
+ * Please note that some encodings may not be supported in the library build
+ * and thus will be ignored here.
+ * \return MPG123_OK on success, MPG123_ERR if there was an error. */
+MPG123_EXPORT int mpg123_format2( mpg123_handle *mh
+, long rate, int channels, int encodings );
+
/** Check to see if a specific format at a specific rate is supported
* by mpg123_handle.
* \param mh handle
* @{
*/
-/* reading samples / triggering decoding, possible return values: */
-/** Enumeration of the error codes returned by libmpg123 functions. */
+/** Open a simple MPEG file with fixed properties.
+ *
+ * This function shall simplify the common use case of a plain MPEG
+ * file on disk that you want to decode, with one fixed sample
+ * rate and channel count, and usually a length defined by a Lame/Info/Xing
+ * tag. It will:
+ *
+ * - set the MPG123_NO_FRANKENSTEIN flag
+ * - set up format support according to given parameters,
+ * - open the file,
+ * - query audio format,
+ * - fix the audio format support table to ensure the format stays the same,
+ * - call mpg123_scan() if there is no header frame to tell the track length.
+ *
+ * From that on, you can call mpg123_getformat() for querying the sample
+ * rate (and channel count in case you allowed both) and mpg123_length()
+ * to get a pretty safe number for the duration.
+ * Only the sample rate is left open as that indeed is a fixed property of
+ * MPEG files. You could set MPG123_FORCE_RATE beforehand, but that may trigger
+ * low-quality resampling in the decoder, only do so if in dire need.
+ * The library will convert mono files to stereo for you, and vice versa.
+ * If any constraint cannot be satisified (most likely because of a non-default
+ * build of libmpg123), you get MPG123_ERR returned and can query the detailed
+ * cause from the handle. Only on MPG123_OK there will an open file that you
+ * then close using mpg123_close(), or implicitly on mpg123_delete() or the next
+ * call to open another file.
+ *
+ * So, for your usual CD rip collection, you could use
+ *
+ * mpg123_open_fixed(mh, path, MPG123_STEREO, MPG123_ENC_SIGNED_16)
+ *
+ * and be happy calling mpg123_getformat() to verify 44100 Hz rate, then just
+ * playing away with mpg123_read(). The occasional mono file, or MP2 file,
+ * will also be decoded without you really noticing. Just the speed could be
+ * wrong if you do not care about sample rate at all.
+ * \param mh handle
+ * \param path filesystem path
+ * \param channels allowed channel count, either 1 (MPG123_MONO) or
+ * 2 (MPG123_STEREO), or bitwise or of them, but then you're halfway back to
+ * calling mpg123_format() again;-)
+ * \param encoding a definite encoding from enum mpg123_enc_enum
+ * or a bitmask like for mpg123_format(), defeating the purpose somewhat
+ */
+MPG123_EXPORT int mpg123_open_fixed(mpg123_handle *mh, const char *path
+, int channels, int encoding);
/** Open and prepare to decode the specified file by filesystem path.
* This does not open HTTP urls; libmpg123 contains no networking code.
/** Open a new bitstream and prepare for direct feeding
* This works together with mpg123_decode(); you are responsible for reading and feeding the input bitstream.
+ * Also, you are expected to handle ICY metadata extraction yourself. This
+ * input method does not handle MPG123_ICY_INTERVAL. It does parse ID3 frames, though.
* \param mh handle
* \return MPG123_OK on success
*/
MPG123_EXPORT int mpg123_close(mpg123_handle *mh);
/** Read from stream and decode up to outmemsize bytes.
+ *
+ * Note: The type of outmemory changed to a void pointer in mpg123 1.26.0
+ * (API version 45).
+ *
* \param mh handle
* \param outmemory address of output buffer to write to
* \param outmemsize maximum number of bytes to write
* \return MPG123_OK or error/message code
*/
MPG123_EXPORT int mpg123_read(mpg123_handle *mh
-, unsigned char *outmemory, size_t outmemsize, size_t *done );
+, void *outmemory, size_t outmemsize, size_t *done );
/** Feed data for a stream that has been opened with mpg123_open_feed().
* It's give and take: You provide the bytestream, mpg123 gives you the decoded samples.
* without taking decoded data.
* Think of this function being the union of mpg123_read() and mpg123_feed() (which it actually is, sort of;-).
* You can actually always decide if you want those specialized functions in separate steps or one call this one here.
+ *
+ * Note: The type of outmemory changed to a void pointer in mpg123 1.26.0
+ * (API version 45).
+ *
* \param mh handle
* \param inmemory input buffer
* \param inmemsize number of input bytes
*/
MPG123_EXPORT int mpg123_decode( mpg123_handle *mh
, const unsigned char *inmemory, size_t inmemsize
-, unsigned char *outmemory, size_t outmemsize, size_t *done );
+, void *outmemory, size_t outmemsize, size_t *done );
/** Decode next MPEG frame to internal buffer
* or read a frame and return after setting a new format.
enum mpg123_vbr vbr; /**< The VBR mode. */
};
+/** Data structure for even more detailed information out of the decoder,
+ * for MPEG layer III only.
+ * This was added to support the frame analyzer by the Lame project and
+ * just follows what was used there before. You know what the fields mean
+ * if you want use this structure. */
+struct mpg123_moreinfo
+{
+ double xr[2][2][576];
+ double sfb[2][2][22]; /* [2][2][SBMAX_l] */
+ double sfb_s[2][2][3*13]; /* [2][2][3*SBMAX_s] */
+ int qss[2][2];
+ int big_values[2][2];
+ int sub_gain[2][2][3];
+ int scalefac_scale[2][2];
+ int preflag[2][2];
+ int blocktype[2][2];
+ int mixed[2][2];
+ int mainbits[2][2];
+ int sfbits[2][2];
+ int scfsi[2];
+ int maindata;
+ int padding;
+};
+
/** Get frame information about the MPEG audio bitstream and store it in a mpg123_frameinfo structure.
* \param mh handle
* \param mi address of existing frameinfo structure to write to
*/
MPG123_EXPORT int mpg123_info(mpg123_handle *mh, struct mpg123_frameinfo *mi);
+/** Trigger collection of additional decoder information while decoding.
+ * \param mh handle
+ * \param mi pointer to data storage (NULL to disable collection)
+ * \return MPG123_OK if the collection was enabled/disabled as desired, MPG123_ERR
+ * otherwise (e.g. if the feature is disabled)
+ */
+MPG123_EXPORT int mpg123_set_moreinfo( mpg123_handle *mh
+, struct mpg123_moreinfo *mi );
+
/** Get the safe output buffer size for all cases
* (when you want to replace the internal buffer)
* \return safe buffer size
*/
MPG123_EXPORT int mpg123_scan(mpg123_handle *mh);
-/** Return, if possible, the full (expected) length of current track in frames.
+/** Return, if possible, the full (expected) length of current track in
+ * MPEG frames.
* \param mh handle
* \return length >= 0 or MPG123_ERR if there is no length guess possible.
*/
MPG123_EXPORT off_t mpg123_framelength(mpg123_handle *mh);
-/** Return, if possible, the full (expected) length of current track in samples.
+/** Return, if possible, the full (expected) length of current
+ * track in samples (PCM frames).
+ *
+ * This relies either on an Info frame at the beginning or a previous
+ * call to mpg123_scan() to get the real number of MPEG frames in a
+ * file. It will guess based on file size if neither Info frame nor
+ * scan data are present. In any case, there is no guarantee that the
+ * decoder will not give you more data, for example in case the open
+ * file gets appended to during decoding.
* \param mh handle
* \return length >= 0 or MPG123_ERR if there is no length guess possible.
*/
,MPG123_BUFFERFILL /**< Get fill of internal (feed) input buffer as integer byte count returned as long and as double. An error is returned on integer overflow while converting to (signed) long, but the returned floating point value shold still be fine. */
,MPG123_FRANKENSTEIN /**< Stream consists of carelessly stitched together files. Seeking may yield unexpected results (also with MPG123_ACCURATE, it may be confused). */
,MPG123_FRESH_DECODER /**< Decoder structure has been updated, possibly indicating changed stream (integer value, 0 if false, 1 if true). Flag is cleared after retrieval. */
+ ,MPG123_ENC_DELAY /** Encoder delay read from Info tag (layer III, -1 if unknown). */
+ ,MPG123_ENC_PADDING /** Encoder padding read from Info tag (layer III, -1 if unknown). */
+ ,MPG123_DEC_DELAY /** Decoder delay (for layer III only, -1 otherwise). */
};
/** Get various current decoder/stream state information.
size_t fill; /**< number of used bytes (including closing zero byte) */
} mpg123_string;
-/** Create and allocate memory for a new mpg123_string
+/** Allocate and intialize a new string.
+ * \param val optional initial string value (can be NULL)
+ */
+MPG123_EXPORT mpg123_string* mpg123_new_string(const char* val);
+
+/** Free memory of contents and the string structure itself.
+ * \param sb string handle
+ */
+MPG123_EXPORT void mpg123_delete_string(mpg123_string* sb);
+
+/** Initialize an existing mpg123_string structure to {NULL, 0, 0}.
+ * If you hand in a NULL pointer here, your program should crash. The other
+ * string functions are more forgiving, but this one here is too basic.
* \param sb string handle (address of existing structure on your side)
*/
MPG123_EXPORT void mpg123_init_string(mpg123_string* sb);
-/** Free-up mempory for an existing mpg123_string
+/** Free-up memory of the contents of an mpg123_string (not the struct itself).
+ * This also calls mpg123_init_string() and hence is safe to be called
+ * repeatedly.
* \param sb string handle
*/
MPG123_EXPORT void mpg123_free_string(mpg123_string* sb);
*/
MPG123_EXPORT int mpg123_copy_string(mpg123_string* from, mpg123_string* to);
+/** Move the contents of one mpg123_string string to another.
+ * This frees any memory associated with the target and moves over the
+ * pointers from the source, leaving the source without content after
+ * that. The only possible error is that you hand in NULL pointers.
+ * If you handed in a valid source, its contents will be gone, even if
+ * there was no target to move to. If you hand in a valid target, its
+ * original contents will also always be gone, to be replaced with the
+ * source's contents if there was some.
+ * \param from source string handle
+ * \param to target string handle
+ * \return 0 on error, 1 on success
+ */
+MPG123_EXPORT int mpg123_move_string(mpg123_string* from, mpg123_string* to);
+
/** Append a C-String to an mpg123_string
* \param sb string handle
* \param stuff to append
MPG123_EXPORT int mpg123_set_substring( mpg123_string *sb
, const char *stuff, size_t from, size_t count );
-/** Count characters in a mpg123 string (non-null bytes or UTF-8 characters).
- * Even with the fill property, the character count is not obvious as there could be multiple trailing null bytes.
+/** Count characters in a mpg123 string (non-null bytes or Unicode points).
+ * This function is of limited use, as it does just count code points
+ * encoded in an UTF-8 string, only loosely related to the count of visible
+ * characters. Get your full Unicode handling support elsewhere.
* \param sb string handle
* \param utf8 a flag to tell if the string is in utf8 encoding
* \return character count
*/
MPG123_EXPORT int mpg123_chomp_string(mpg123_string *sb);
+/** Determine if two strings contain the same data.
+ * This only returns 1 if both given handles are non-NULL and
+ * if they are filled with the same bytes.
+ * \param a first string handle
+ * \param b second string handle
+ * \return 0 for different strings, 1 for identical
+ */
+MPG123_EXPORT int mpg123_same_string(mpg123_string *a, mpg123_string *b);
+
/** The mpg123 text encodings. This contains encodings we encounter in ID3 tags or ICY meta info. */
enum mpg123_text_encoding
{
/** Sub data structure for ID3v2, for storing various text fields (including comments).
* This is for ID3v2 COMM, TXXX and all the other text fields.
- * Only COMM and TXXX have a description, only COMM and USLT have a language.
- * You should consult the ID3v2 specification for the use of the various text fields ("frames" in ID3v2 documentation, I use "fields" here to separate from MPEG frames). */
+ * Only COMM, TXXX and USLT may have a description, only COMM and USLT
+ * have a language.
+ * You should consult the ID3v2 specification for the use of the various text fields
+ * ("frames" in ID3v2 documentation, I use "fields" here to separate from MPEG frames). */
typedef struct
{
char lang[3]; /**< Three-letter language code (not terminated). */
size_t texts; /**< Numer of text fields. */
mpg123_text *extra; /**< The array of extra (TXXX) fields. */
size_t extras; /**< Number of extra text (TXXX) fields. */
- mpg123_picture *picture; /**< Array of ID3v2 pictures fields (APIC). */
+ mpg123_picture *picture; /**< Array of ID3v2 pictures fields (APIC).
+ Only populated if MPG123_PICTURE flag is set! */
size_t pictures; /**< Number of picture (APIC) fields. */
} mpg123_id3v2;
MPG123_EXPORT int mpg123_id3( mpg123_handle *mh
, mpg123_id3v1 **v1, mpg123_id3v2 **v2 );
+/** Return pointers to and size of stored raw ID3 data if storage has
+ * been configured with MPG123_RAW_ID3 and stream parsing passed the
+ * metadata already. Null value with zero size is a possibility!
+ * The storage can change at any next API call.
+ * \param v1 address to store pointer to v1 tag
+ * \param v1_size size of v1 data in bytes
+ * \param v2 address to store pointer to v2 tag
+ * \param v2_size size of v2 data in bytes
+ * \return MPG123_OK or MPG123_ERR. Only on MPG123_OK the output
+ * values are set.
+ */
+MPG123_EXPORT int mpg123_id3_raw( mpg123_handle *mh
+, unsigned char **v1, size_t *v1_size
+, unsigned char **v2, size_t *v2_size );
+
/** Point icy_meta to existing data structure wich may change on any next read/decode function call.
* \param mh handle
* \param icy_meta return address for ICY meta string (set to NULL if nothing there)
MPG123_EXPORT int mpg123_fmt(mpg123_pars *mp
, long rate, int channels, int encodings);
+/** Set the audio format support of a mpg123_pars in detail:
+ * \param mp parameter handle
+ * \param rate The sample rate value (in Hertz). Special value 0 means
+ * all rates (reason for this variant of mpg123_fmt).
+ * \param channels A combination of MPG123_STEREO and MPG123_MONO.
+ * \param encodings A combination of accepted encodings for rate and channels,
+ * p.ex MPG123_ENC_SIGNED16|MPG123_ENC_ULAW_8 (or 0 for no
+ * support).
+ * \return MPG123_OK on success
+*/
+MPG123_EXPORT int mpg123_fmt2(mpg123_pars *mp
+, long rate, int channels, int encodings);
+
/** Check to see if a specific format at a specific rate is supported
* by mpg123_pars.
* \param mp parameter handle
* Note that the required buffer size could be bigger than expected from output
* encoding if libmpg123 has to convert from primary decoder output (p.ex. 32 bit
* storage for 24 bit output).
+ *
+ * Note: The type of data changed to a void pointer in mpg123 1.26.0
+ * (API version 45).
+ *
* \param mh handle
* \param data pointer to user buffer
* \param size of buffer in bytes
* \return MPG123_OK on success
*/
MPG123_EXPORT int mpg123_replace_buffer(mpg123_handle *mh
-, unsigned char *data, size_t size);
+, void *data, size_t size);
/** The max size of one frame's decoded output with current settings.
* Use that to determine an appropriate minimum buffer size for decoding one frame.
/* Postprocessing format conversion of freshly decoded buffer. */
void postprocess_buffer(mpg123_handle *fr);
+int open_fixed_pre(mpg123_handle *mh, int channels, int encoding);
+int open_fixed_post(mpg123_handle *mh, int channels, int encoding);
+
/* If networking is enabled and we really mean internal networking, the timeout_read function is available. */
#if defined (NETWORK) && !defined (WANT_WIN32_SOCKETS)
/* Does not work with win32 */
/*
parse: spawned from common; clustering around stream/frame parsing
- copyright ?-2014 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright ?-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp & Thomas Orgis
*/
lame_offset += 3; /* 24 in */
if(VERBOSE3) fprintf(stderr, "Note: Encoder delay = %i; padding = %i\n"
, (int)pad_in, (int)pad_out);
+ /* Store even if libmpg123 does not do gapless decoding itself. */
+ fr->enc_delay = (int)pad_in;
+ fr->enc_padding = (int)pad_out;
#ifdef GAPLESS
if(fr->p.flags & MPG123_GAPLESS)
frame_gapless_init(fr, fr->track_frames, pad_in, pad_out);
debug("repeat!");
fr->to_decode = fr->to_ignore = TRUE;
--fr->halfphase;
- fr->bitindex = 0;
- fr->wordpointer = (unsigned char *) fr->bsbuf;
+ set_pointer(fr, 0, 0);
if(fr->lay == 3) memcpy (fr->bsbuf, fr->ssave, fr->ssize);
if(fr->error_protection) fr->crc = getbits(fr, 16); /* skip crc */
return 1;
/* From now on, old frame data is tainted by parsing attempts. */
fr->to_decode = fr->to_ignore = FALSE;
+
+ if( fr->p.flags & MPG123_NO_FRANKENSTEIN &&
+ ( (fr->track_frames > 0 && fr->num >= fr->track_frames-1)
+#ifdef GAPLESS
+ || (fr->gapless_frames > 0 && fr->num >= fr->gapless_frames-1)
+#endif
+ ) )
+ {
+ mdebug( "stopping parsing at %"OFF_P
+ " frames as indicated fixed track length"
+ , (off_p)fr->num+1 );
+ return 0;
+ }
+
read_again:
/* In case we are looping to find a valid frame, discard any buffered data before the current position.
This is essential to prevent endless looping, always going back to the beginning when feeder buffer is exhausted. */
if(!fr->firsthead)
{
- ret = do_readahead(fr, newhead);
+ ret = fr->p.flags & MPG123_NO_READAHEAD
+ ? PARSE_GOOD
+ : do_readahead(fr, newhead);
/* readahead can fail mit NEED_MORE, in which case we must also make the just read header available again for next go */
if(ret < 0) fr->rd->back_bytes(fr, 4);
JUMP_CONCLUSION(ret);
/* Now we should have our valid header and proceed to reading the frame. */
+ if(fr->p.flags & MPG123_NO_FRANKENSTEIN)
+ {
+ if(fr->firsthead && !head_compatible(fr->firsthead, newhead))
+ {
+ mdebug( "stopping before reading frame %"OFF_P
+ " as its header indicates Frankenstein coming for you", (off_p)fr->num );
+ return 0;
+ }
+ }
+
/* if filepos is invalid, so is framepos */
framepos = fr->rd->tell(fr) - 4;
/* flip/init buffer for Layer 3 */
{
unsigned char *newbuf = fr->bsspace[fr->bsnum]+512;
/* read main data into memory */
+ debug2("read frame body of %i at %"OFF_P, fr->framesize, framepos+4);
if((ret=fr->rd->read_frame_body(fr,newbuf,fr->framesize))<0)
{
/* if failed: flip back */
- debug("need more?");
+ debug1("%s", ret == MPG123_NEED_MORE ? "need more" : "read error");
goto read_frame_bad;
}
fr->bsbufold = fr->bsbuf;
debug2("fr->firsthead: %08lx, audio_start: %li", fr->firsthead, (long int)fr->audio_start);
}
- fr->bitindex = 0;
- fr->wordpointer = (unsigned char *) fr->bsbuf;
+ set_pointer(fr, 0, 0);
+
/* Question: How bad does the floating point value get with repeated recomputation?
Also, considering that we can play the file or parts of many times. */
if(++fr->mean_frames != 0)
if(fr->freeformat_framesize < 0)
{
int ret;
+ if(fr->p.flags & MPG123_NO_READAHEAD)
+ {
+ if(VERBOSE3)
+ error("Got no free-format frame size and am not allowed to read ahead.");
+ return PARSE_BAD;
+ }
*freeformat_count += 1;
if(*freeformat_count > 5)
{
fr->do_layer = do_layer1;
if(!fr->freeformat)
{
- fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
- fr->framesize /= freqs[fr->sampling_frequency];
- fr->framesize = ((fr->framesize+fr->padding)<<2)-4;
+ long fs = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
+ fs /= freqs[fr->sampling_frequency];
+ fs = ((fs+fr->padding)<<2)-4;
+ fr->framesize = (int)fs;
}
break;
#endif
if(!fr->freeformat)
{
debug2("bitrate index: %i (%i)", fr->bitrate_index, tabsel_123[fr->lsf][1][fr->bitrate_index] );
- fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
- fr->framesize /= freqs[fr->sampling_frequency];
- fr->framesize += fr->padding - 4;
+ long fs = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
+ fs /= freqs[fr->sampling_frequency];
+ fs += fr->padding - 4;
+ fr->framesize = (int)fs;
}
break;
#endif
if(!fr->freeformat)
{
- fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
- fr->framesize /= freqs[fr->sampling_frequency]<<(fr->lsf);
- fr->framesize = fr->framesize + fr->padding - 4;
+ long fs = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
+ fs /= freqs[fr->sampling_frequency]<<(fr->lsf);
+ fs += fr->padding - 4;
+ fr->framesize = fs;
+ }
+ if(fr->framesize < fr->ssize)
+ {
+ if(NOQUIET)
+ error2( "Frame smaller than mandatory side info (%i < %i)!"
+ , fr->framesize, fr->ssize );
+ return PARSE_BAD;
}
break;
#endif
return PARSE_GOOD;
}
-void set_pointer(mpg123_handle *fr, long backstep)
-{
- fr->wordpointer = fr->bsbuf + fr->ssize - backstep;
- if (backstep)
- memcpy(fr->wordpointer,fr->bsbufold+fr->fsizeold-backstep,backstep);
+/* Prepare for bit reading. Two stages:
+ 0. Layers 1 and 2, side info for layer 3
+ 1. Second call for possible bit reservoir for layer 3 part 2,3.
+ This overwrites side info needed for stage 0.
- fr->bitindex = 0;
+ Continuing to read bits after layer 3 side info shall fail unless
+ set_pointer() is called to refresh things.
+*/
+void set_pointer(mpg123_handle *fr, int part2, long backstep)
+{
+ fr->bitindex = 0;
+ if(fr->lay == 3)
+ {
+ if(part2)
+ {
+ fr->wordpointer = fr->bsbuf + fr->ssize - backstep;
+ if(backstep)
+ memcpy( fr->wordpointer, fr->bsbufold+fr->fsizeold-backstep
+ , backstep );
+ fr->bits_avail = (long)(fr->framesize - fr->ssize + backstep)*8;
+ }
+ else
+ {
+ fr->wordpointer = fr->bsbuf;
+ fr->bits_avail = fr->ssize*8;
+ }
+ }
+ else
+ {
+ fr->wordpointer = fr->bsbuf;
+ fr->bits_avail = fr->framesize*8;
+ }
}
/********************************/
if(limit >= 0 && *headcount >= limit)
{
if(NOQUIET) error1("Giving up searching valid MPEG header after %li bytes of junk.", *headcount);
- return PARSE_END;
+ fr->err = MPG123_RESYNC_FAIL;
+ return PARSE_ERR;
}
else debug1("hopefully found one at %"OFF_P, (off_p)fr->rd->tell(fr));
long frame_freq(mpg123_handle *fr);
int read_frame_recover(mpg123_handle* fr); /* dead? */
int read_frame(mpg123_handle *fr);
-void set_pointer(mpg123_handle *fr, long backstep);
+void set_pointer(mpg123_handle *fr, int part2, long backstep);
int position_info(mpg123_handle* fr, unsigned long no, long buffsize, unsigned long* frames_left, double* current_seconds, double* seconds_left);
double compute_bpf(mpg123_handle *fr);
long time_to_frame(mpg123_handle *fr, double seconds);
/* TODO: Check all read calls (in loops, especially!) for return value 0 (EOF)! */
-
+/* Check if get_fileinfo should read ID3 info or not, seems a bit out of place here. */
+/* #define EXTRA_DEBUG */
/*
readers.c: reading input data
- copyright ?-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
+ copyright ?-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp
*/
}
-/* returns size on success... */
+/* returns size on success... otherwise an error code < 0 */
static int generic_read_frame_body(mpg123_handle *fr,unsigned char *buf, int size)
{
long l;
-
- if((l=fr->rd->fullread(fr,buf,size)) != size)
- {
- long ll = l;
- if(ll <= 0) ll = 0;
- return READER_MORE;
- }
- return l;
+ l=fr->rd->fullread(fr,buf,size);
+ return (l >= 0 && l<size) ? READER_ERROR : l;
}
static off_t generic_tell(mpg123_handle *fr)
{
off_t len;
- if((len=io_seek(&fr->rdat,0,SEEK_END)) < 0) return -1;
-
- if(io_seek(&fr->rdat,-128,SEEK_END) < 0) return -1;
-
- if(fr->rd->fullread(fr,(unsigned char *)fr->id3buf,128) != 128) return -1;
-
- if(!strncmp((char*)fr->id3buf,"TAG",3)) len -= 128;
-
- if(io_seek(&fr->rdat,0,SEEK_SET) < 0) return -1;
+ if((len=io_seek(&fr->rdat,0,SEEK_END)) < 0)
+ {
+ debug("cannot seek to end");
+ return -1;
+ } else if(len >= 128)
+ {
+ if(io_seek(&fr->rdat,-128,SEEK_END) < 0)
+ {
+ debug("cannot seek to END-128");
+ return -1;
+ }
+ if(fr->rd->fullread(fr,(unsigned char *)fr->id3buf,128) != 128)
+ {
+ debug("cannot read ID3v1?!");
+ return -1;
+ }
+ if(!strncmp((char*)fr->id3buf,"TAG",3)) len -= 128;
+ } else
+ {
+ debug("stream too short for ID3");
+ }
- if(len <= 0) return -1;
+ if(io_seek(&fr->rdat,0,SEEK_SET) < 0)
+ {
+ debug("cannot seek back");
+ return -1;
+ }
+ debug1("returning length: %"OFF_P, (off_p)len);
return len;
}
{
struct bufferchain *bc = &fr->rdat.buffer;
ssize_t gotcount;
+ if(VERBOSE3)
+ mdebug("buffered_fullread: want %zd", count);
if(bc->size - bc->pos < count)
{ /* Add more stuff to buffer. If hitting end of file, adjust count. */
unsigned char readbuf[4096];
count = bc->size - bc->pos; /* We want only what we got. */
}
gotcount = bc_give(bc, out, count);
-
- if(VERBOSE3) debug2("wanted %li, got %li", (long)count, (long)gotcount);
-
+ if(VERBOSE3)
+ mdebug("buffered_fullread: got %zd", gotcount);
if(gotcount != count){ if(NOQUIET) error("gotcount != count"); return READER_ERROR; }
else return gotcount;
}
int flags;
if(fr->rdat.r_read != NULL)
{
- error("Timeout reading does not work with user-provided read function. Implement it yourself!");
+ if(NOQUIET)
+ error( "Timeout reading does not work with user-provided"
+ " read function. Implement it yourself!" );
return -1;
}
flags = fcntl(fr->rdat.filept, F_GETFL);
*/
if(fr->rdat.filelen >= 0)
{
+ debug("seekable stream");
fr->rdat.flags |= READER_SEEKABLE;
if(!strncmp((char*)fr->id3buf,"TAG",3))
{
else if(fr->p.flags & MPG123_SEEKBUFFER)
{
#ifdef NO_FEEDER
- error("Buffered readers not supported in this build.");
+ if(NOQUIET)
+ error("Buffered readers not supported in this build.");
fr->err = MPG123_MISSING_FEATURE;
return -1;
#else
if (fr->rd == &readers[READER_STREAM])
{
+ debug("switching to buffered stream reader");
fr->rd = &readers[READER_BUF_STREAM];
fr->rdat.fullread = plain_fullread;
}
#ifndef NO_ICY
else if(fr->rd == &readers[READER_ICY_STREAM])
{
+ debug("switching to buffered ICY stream reader");
fr->rd = &readers[READER_BUF_ICY_STREAM];
fr->rdat.fullread = icy_fullread;
}
bc_init(&fr->rdat.buffer);
fr->rdat.filelen = 0; /* We carry the offset, but never know how big the stream is. */
fr->rdat.flags |= READER_BUFFERED;
-#endif /* NO_FEEDER */
+#endif /* NO_ICY */
}
return 0;
}
{
debug("feed reader");
#ifdef NO_FEEDER
- error("Buffered readers not supported in this build.");
+ if(NOQUIET)
+ error("Buffered readers not supported in this build.");
fr->err = MPG123_MISSING_FEATURE;
return -1;
#else
#define WRITE_REAL_SAMPLE(samples,sum,clip) *(samples) = ((real)1./SHORT_SCALE)*(sum)
#endif
+/* Finished 32 bit sample to unsigned 32 bit sample. */
+#define CONV_SU32(s) \
+( (s >= 0) \
+ ? ((uint32_t)s + (uint32_t)2147483648UL) \
+ : (s == -2147483647-1 /* Work around to prevent a non-conformant MSVC warning/error */ \
+ ? 0 /* Separate because negation would overflow. */ \
+ : (uint32_t)2147483648UL - (uint32_t)(-s) ) \
+)
+
+/* Finished 16 bit sample to unsigned 16 bit sample. */
+#define CONV_SU16(s) (uint16_t)((int32_t)(s)+32768)
+
+/* Same style for syn123 generic conversion. */
+#define CONV_SU8(s) (uint8_t)((int16_t)s+128)
+
+/* Unsigned 32 bit sample to signed 32 bit sample. */
+#define CONV_US32(u) \
+( (u >= 2147483648UL) \
+ ? (int32_t)((uint32_t)u - (uint32_t)2147483648UL) \
+ : ((u == 0) \
+ ? (int32_t)-2147483648UL \
+ : -(int32_t)((uint32_t)2147483648UL - u) ) \
+)
+
+/* Unsigned 16 bit sample to signed 16 bit sample. */
+#define CONV_US16(s) (int16_t)((int32_t)s-32768)
+
+/* Same style for syn123 generic conversion. */
+#define CONV_US8(s) (int8_t)((int16_t)s-128)
+
+/* 24 bit conversion: drop or add a least significant byte. */
+#ifdef WORDS_BIGENDIAN
+/* Highest byte first. Drop last. */
+#define DROP4BYTE(w,r) {(w)[0]=(r)[0]; (w)[1]=(r)[1]; (w)[2]=(r)[2];}
+#define ADD4BYTE(w,r) {(w)[0]=(r)[0]; (w)[1]=(r)[1]; (w)[2]=(r)[2]; (w)[3]=0}
+#else
+/* Lowest byte first, drop that. */
+#define DROP4BYTE(w,r) {(w)[0]=(r)[1]; (w)[1]=(r)[2]; (w)[2]=(r)[3];}
+#define ADD4BYTE(w,r) {(w)[0]=0; (w)[1]=(r)[0]; (w)[2]=(r)[1]; (w)[3]=(r)[2];}
+#endif
+
#endif
/*
stringbuf: mimicking a bit of C++ to more safely handle strings
- copyright 2006-17 by the mpg123 project
+ copyright 2006-20 by the mpg123 project
- free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Thomas Orgis
#include <string.h>
#include "debug.h"
+mpg123_string* attribute_align_arg mpg123_new_string(const char *val)
+{
+ mpg123_string *sb = malloc(sizeof(mpg123_string));
+ if(!sb)
+ return NULL;
+ mpg123_init_string(sb);
+ mpg123_set_string(sb, val);
+ return sb;
+}
+
+void attribute_align_arg mpg123_delete_string(mpg123_string* sb)
+{
+ if(!sb)
+ return;
+ mpg123_free_string(sb);
+ free(sb);
+}
+
void attribute_align_arg mpg123_init_string(mpg123_string* sb)
{
/* Handing in NULL here is a fatal mistake and rightfully so. */
{
sb->p = t;
sb->size = new;
+ if(sb->size < sb->fill)
+ {
+ // Cut short the existing data, properly.
+ sb->fill = sb->size;
+ sb->p[sb->fill-1] = 0;
+ }
return 1;
}
else return 0;
else return 0;
}
+int attribute_align_arg mpg123_move_string(mpg123_string *from, mpg123_string *to)
+{
+ if(to)
+ mpg123_free_string(to);
+ else
+ mpg123_free_string(from);
+ if(from && to)
+ *to = *from;
+ if(from)
+ mpg123_init_string(from);
+ return (from && to) ? 1 : 0;
+}
+
int attribute_align_arg mpg123_add_string(mpg123_string* sb, const char* stuff)
{
debug1("adding %s", stuff);
return 1;
}
+
+int attribute_align_arg mpg123_same_string(mpg123_string *a, mpg123_string *b)
+{
+ if(!a || !b)
+ return 0;
+ if(a->fill != b->fill)
+ return 0;
+ if(memcmp(a->p, b->p, a->fill))
+ return 0;
+ return 1;
+}
--- /dev/null
+/*
+ swap_bytes: Swap byte order of samples in a buffer.
+
+ copyright 2018 by the mpg123 project
+ licensed under the terms of the LGPL 2.1
+ see COPYING and AUTHORS files in distribution or http://mpg123.org
+
+ initially written by Thomas Orgis
+
+ This is C source to include in your code to get the function named
+ swap_bytes. It is serves intentional duplication in libmpg123 and
+ libsyn123 to avoid introducing a dependency between them. This function
+ is too small for that.
+*/
+
+/* Other headers are already included! */
+
+/* Optionally use platform-specific byteswap macros. */
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+
+/* Plain stupid swapping of elements in a byte array. */
+/* This is the fallback when there is no native bswap macro. */
+#define SWAP(a,b) tmp = p[a]; p[a] = p[b]; p[b] = tmp;
+
+/* Convert samplecount elements of samplesize bytes each in buffer buf. */
+static void swap_bytes(void *buf, size_t samplesize, size_t samplecount)
+{
+ unsigned char *p = buf;
+ unsigned char *pend = (unsigned char*)buf+samplesize*samplecount;
+ unsigned char tmp;
+
+ if(samplesize < 2)
+ return;
+ switch(samplesize)
+ {
+ case 2: /* AB -> BA */
+#ifdef HAVE_BYTESWAP_H
+ {
+ uint16_t* pp = (uint16_t*)p;
+ for(; pp<(uint16_t*)pend; ++pp)
+ *pp = bswap_16(*pp);
+ }
+#else
+ for(; p<pend; p+=2)
+ {
+ SWAP(0,1)
+ }
+#endif
+ break;
+ case 3: /* ABC -> CBA */
+ for(; p<pend; p+=3)
+ {
+ SWAP(0,2)
+ }
+ break;
+ case 4: /* ABCD -> DCBA */
+#ifdef HAVE_BYTESWAP_H
+ {
+ uint32_t* pp = (uint32_t*)p;
+ for(; pp<(uint32_t*)pend; ++pp)
+ *pp = bswap_32(*pp);
+ }
+#else
+ for(; p<pend; p+=4)
+ {
+ SWAP(0,3)
+ SWAP(1,2)
+ }
+#endif
+ break;
+ case 8: /* ABCDEFGH -> HGFEDCBA */
+#ifdef HAVE_BYTESWAP_H
+ {
+ uint64_t* pp = (uint64_t*)p;
+ for(; pp<(uint64_t*)pend; ++pp)
+ *pp = bswap_64(*pp);
+ }
+#else
+ for(; p<pend; p+=8)
+ {
+ SWAP(0,7)
+ SWAP(1,6)
+ SWAP(2,5)
+ SWAP(3,4)
+ }
+#endif
+ break;
+ /* All the weird choices with the full nested loop. */
+ default:
+ for(; p<pend; p+=samplesize)
+ {
+ size_t j;
+ for(j=0; j<samplesize/2; ++j)
+ {
+ SWAP(j, samplesize-j-1)
+ }
+ }
+ }
+}
+
+#undef SWAP