minimp3 library

git-svn-id: svn://kolibrios.org@8028 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
hidnplayr 2020-06-09 18:16:54 +00:00
parent f510a3a980
commit f4f6941e9a
7 changed files with 2382 additions and 0 deletions

View File

@ -0,0 +1,117 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

View File

@ -0,0 +1,301 @@
minimp3
==========
[![Build Status](https://travis-ci.org/lieff/minimp3.svg)](https://travis-ci.org/lieff/minimp3)
<a href="https://scan.coverity.com/projects/lieff-minimp3">
<img alt="Coverity Scan Build Status"
src="https://scan.coverity.com/projects/14844/badge.svg"/>
</a>
[![codecov](https://codecov.io/gh/lieff/minimp3/branch/master/graph/badge.svg)](https://codecov.io/gh/lieff/minimp3)
Minimalistic, single-header library for decoding MP3. minimp3 is designed to be
small, fast (with SSE and NEON support), and accurate (ISO conformant). You can
find a rough benchmark below, measured using ``perf`` on an i7-6700K, IO
included, no CPU heat to address speedstep:
| Vector | Hz | Samples| Sec | Clockticks | Clockticks per second | PSNR | Max diff |
| ----------- | ----- | ------ | ------ | --------- | ------ | ------ | - |
|compl.bit | 48000 | 248832 | 5.184 | 14306684 | 2.759M | 124.22 | 1 |
|he_32khz.bit | 32000 | 172800 | 5.4 | 8426158 | 1.560M | 139.67 | 1 |
|he_44khz.bit | 44100 | 472320 | 10.710 | 21296300 | 1.988M | 144.04 | 1 |
|he_48khz.bit | 48000 | 172800 | 3.6 | 8453846 | 2.348M | 139.67 | 1 |
|hecommon.bit | 44100 | 69120 | 1.567 | 3169715 | 2.022M | 133.93 | 1 |
|he_free.bit | 44100 | 156672 | 3.552 | 5798418 | 1.632M | 137.48 | 1 |
|he_mode.bit | 44100 | 262656 | 5.955 | 9882314 | 1.659M | 118.00 | 1 |
|si.bit | 44100 | 135936 | 3.082 | 7170520 | 2.326M | 120.30 | 1 |
|si_block.bit | 44100 | 73728 | 1.671 | 4233136 | 2.533M | 125.18 | 1 |
|si_huff.bit | 44100 | 86400 | 1.959 | 4785322 | 2.442M | 107.98 | 1 |
|sin1k0db.bit | 44100 | 725760 | 16.457 | 24842977 | 1.509M | 111.03 | 1 |
Conformance test passed on all vectors (PSNR > 96db).
## Comparison with keyj's [minimp3](http://keyj.emphy.de/minimp3/)
Comparison by features:
| Keyj minimp3 | Current |
| ------------ | ------- |
| Fixed point | Floating point |
| source: 84kb | 70kb |
| binary: 34kb (20kb compressed) | 30kb (20kb) |
| no vector opts | SSE/NEON intrinsics |
| no free format | free format support |
Below, you can find the benchmark and conformance test for keyj's minimp3:
| Vector | Hz | Samples| Sec | Clockticks | Clockticks per second | PSNR | Max diff |
| ----------- | ----- | ------ | ------ | --------- | ------ | ----- | - |
|compl.bit | 48000 | 248832 | 5.184 | 31849373 | 6.143M | 71.50 | 41 |
|he_32khz.bit | 32000 | 172800 | 5.4 | 26302319 | 4.870M | 71.63 | 24 |
|he_44khz.bit | 44100 | 472320 | 10.710 | 41628861 | 3.886M | 71.63 | 24 |
|he_48khz.bit | 48000 | 172800 | 3.6 | 25899527 | 7.194M | 71.63 | 24 |
|hecommon.bit | 44100 | 69120 | 1.567 | 20437779 | 13.039M | 71.58 | 25 |
|he_free.bit | 44100 | 0 | 0 | - | - | - | - |
|he_mode.bit | 44100 | 262656 | 5.955 | 30988984 | 5.203M | 71.78 | 27 |
|si.bit | 44100 | 135936 | 3.082 | 24096223 | 7.817M | 72.35 | 36 |
|si_block.bit | 44100 | 73728 | 1.671 | 20722017 | 12.394M | 71.84 | 26 |
|si_huff.bit | 44100 | 86400 | 1.959 | 21121376 | 10.780M | 27.80 | 65535 |
|sin1k0db.bit | 44100 | 730368 | 16.561 | 55569636 | 3.355M | 0.15 | 58814 |
Keyj minimp3 conformance test fails on all vectors (PSNR < 96db), and free
format is unsupported. This caused some problems when it was used
[here](https://github.com/lieff/lvg), and was the main motivation for this work.
## Usage
First, we need to initialize the decoder structure:
```c
//#define MINIMP3_ONLY_MP3
//#define MINIMP3_ONLY_SIMD
//#define MINIMP3_NO_SIMD
//#define MINIMP3_NONSTANDARD_BUT_LOGICAL
//#define MINIMP3_FLOAT_OUTPUT
#define MINIMP3_IMPLEMENTATION
#include "minimp3.h"
...
static mp3dec_t mp3d;
mp3dec_init(&mp3d);
```
Note that you must define ``MINIMP3_IMPLEMENTATION`` in exactly one source file.
You can ``#include`` ``minimp3.h`` in as many files as you like.
Also you can use ``MINIMP3_ONLY_MP3`` define to strip MP1/MP2 decoding code.
MINIMP3_ONLY_SIMD define controls generic (non SSE/NEON) code generation (always enabled on x64/arm64 targets).
In case you do not want any platform-specific SIMD optimizations, you can define ``MINIMP3_NO_SIMD``.
MINIMP3_NONSTANDARD_BUT_LOGICAL define saves some code bytes, and enforces non-stadnard but logical behaviour of mono-stereo transition (rare case).
MINIMP3_FLOAT_OUTPUT makes ``mp3dec_decode_frame()`` output to be float instead of short and additional function mp3dec_f32_to_s16 will be available for float->short conversion if needed.
Then. we decode the input stream frame-by-frame:
```c
/*typedef struct
{
int frame_bytes;
int channels;
int hz;
int layer;
int bitrate_kbps;
} mp3dec_frame_info_t;*/
mp3dec_frame_info_t info;
short pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
/*unsigned char *input_buf; - input byte stream*/
samples = mp3dec_decode_frame(&mp3d, input_buf, buf_size, pcm, &info);
```
The ``mp3dec_decode_frame()`` function decodes one full MP3 frame from the
input buffer, which must be large enough to hold one full frame.
The decoder will analyze the input buffer to properly sync with the MP3 stream,
and will skip ID3 data, as well as any data which is not valid. Short buffers
may cause false sync and can produce 'squealing' artefacts. The bigger the size
of the input buffer, the more reliable the sync procedure. We recommend having
as many as 10 consecutive MP3 frames (~16KB) in the input buffer at a time.
The size of the consumed MP3 data is returned in the ``mp3dec_frame_info_t``
field of the ``frame_bytes`` struct; you must remove the data corresponding to
the ``frame_bytes`` field from the input buffer before the next decoder
invocation.
The decoding function returns the number of decoded samples. The following cases
are possible:
- **0:** No MP3 data was found in the input buffer
- **384:** Layer 1
- **576:** MPEG 2 Layer 3
- **1152:** Otherwise
The following is a description of the possible combinations of the number of
samples and ``frame_bytes`` field values:
- More than 0 samples and ``frame_bytes > 0``: Succesful decode
- 0 samples and ``frame_bytes > 0``: The decoder skipped ID3 or invalid data
- 0 samples and ``frame_bytes == 0``: Insufficient data
If ``frame_bytes == 0``, the other fields may be uninitialized or unchanged; if
``frame_bytes != 0``, the other fields are available. The application may call
``mp3dec_init()`` when changing decode position, but this is not necessary.
As a special case, the decoder supports already split MP3 streams (for example,
after doing an MP4 demux). In this case, the input buffer must contain _exactly
one_ non-free-format frame.
## Seeking
You can seek to any byte in the stream and call ``mp3dec_decode_frame``; this
will work in almost all cases, but is not completely guaranteed. Probablility of
sync procedure failure lowers when MAX_FRAME_SYNC_MATCHES value grows. Default
MAX_FRAME_SYNC_MATCHES=10 and probablility of sync failure should be very low.
If granule data is accidentally detected as a valid MP3 header, short audio artefacting is
possible.
High-level mp3dec_ex_seek function supports precise seek to sample (MP3D_SEEK_TO_SAMPLE)
using index and binary search.
## Track length detect
If the file is known to be cbr, then all frames have equal size and
lack ID3 tags, which allows us to decode the first frame and calculate all frame
positions as ``frame_bytes * N``. However, because of padding, frames can differ
in size even in this case.
In general case whole stream scan is needed to calculate it's length. Scan can be
omitted if vbr tag is present (added by encoders like lame and ffmpeg), which contains
length info. High-level functions automatically use the vbr tag if present.
## High-level API
If you need only decode file/buffer or use precise seek, you can use optional high-level API.
Just ``#include`` ``minimp3_ex.h`` instead and use following additional functions:
```c
#define MP3D_SEEK_TO_BYTE 0
#define MP3D_SEEK_TO_SAMPLE 1
#define MINIMP3_PREDECODE_FRAMES 2 /* frames to pre-decode and skip after seek (to fill internal structures) */
/*#define MINIMP3_SEEK_IDX_LINEAR_SEARCH*/ /* define to use linear index search instead of binary search on seek */
#define MINIMP3_IO_SIZE (128*1024) /* io buffer size for streaming functions, must be greater than MINIMP3_BUF_SIZE */
#define MINIMP3_BUF_SIZE (16*1024) /* buffer which can hold minimum 10 consecutive mp3 frames (~16KB) worst case */
#define MINIMP3_ENABLE_RING 0 /* enable hardware magic ring buffer if available, to make less input buffer memmove(s) in callback IO mode */
#define MP3D_E_MEMORY -1
#define MP3D_E_IOERROR -2
typedef struct
{
mp3d_sample_t *buffer;
size_t samples; /* channels included, byte size = samples*sizeof(mp3d_sample_t) */
int channels, hz, layer, avg_bitrate_kbps;
} mp3dec_file_info_t;
typedef size_t (*MP3D_READ_CB)(void *buf, size_t size, void *user_data);
typedef int (*MP3D_SEEK_CB)(uint64_t position, void *user_data);
typedef struct
{
MP3D_READ_CB read;
void *read_data;
MP3D_SEEK_CB seek;
void *seek_data;
} mp3dec_io_t;
typedef struct
{
uint64_t samples;
mp3dec_frame_info_t info;
int last_error;
...
} mp3dec_ex_t;
typedef int (*MP3D_ITERATE_CB)(void *user_data, const uint8_t *frame, int frame_size, int free_format_bytes, size_t buf_size, uint64_t offset, mp3dec_frame_info_t *info);
typedef int (*MP3D_PROGRESS_CB)(void *user_data, size_t file_size, uint64_t offset, mp3dec_frame_info_t *info);
/* decode whole buffer block */
int mp3dec_load_buf(mp3dec_t *dec, const uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
int mp3dec_load_cb(mp3dec_t *dec, mp3dec_io_t *io, uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
/* iterate through frames */
int mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);
int mp3dec_iterate_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);
/* streaming decoder with seeking capability */
int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method);
int mp3dec_ex_open_cb(mp3dec_ex_t *dec, mp3dec_io_t *io, int seek_method);
void mp3dec_ex_close(mp3dec_ex_t *dec);
int mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position);
size_t mp3dec_ex_read(mp3dec_ex_t *dec, mp3d_sample_t *buf, size_t samples);
#ifndef MINIMP3_NO_STDIO
/* stdio versions of file load, iterate and stream */
int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_data);
int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int seek_method);
#ifdef _WIN32
int mp3dec_load_w(mp3dec_t *dec, const wchar_t *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
int mp3dec_iterate_w(const wchar_t *file_name, MP3D_ITERATE_CB callback, void *user_data);
int mp3dec_ex_open_w(mp3dec_ex_t *dec, const wchar_t *file_name, int seek_method);
#endif
#endif
```
Use MINIMP3_NO_STDIO define to exclude STDIO functions.
MINIMP3_ALLOW_MONO_STEREO_TRANSITION allows mixing mono and stereo in same file.
In that case ``mp3dec_frame_info_t->channels = 0`` is reported on such files and correct channels number passed to progress_cb callback for each frame in mp3dec_frame_info_t structure.
MP3D_PROGRESS_CB is optional and can be NULL, example of file decoding:
```c
mp3dec_t mp3d;
mp3dec_file_info_t info;
if (mp3dec_load(&mp3d, input_file_name, &info, NULL, NULL))
{
/* error */
}
/* mp3dec_file_info_t contains decoded samples and info,
use free(info.buffer) to deallocate samples */
```
Example of file decoding with seek capability:
```c
mp3dec_ex_t dec;
if (mp3dec_ex_open(&dec, input_file_name, MP3D_SEEK_TO_SAMPLE))
{
/* error */
}
/* dec.samples, dec.info.hz, dec.info.layer, dec.info.channels should be filled */
if (mp3dec_ex_seek(&dec, position))
{
/* error */
}
mp3d_sample_t *buffer = malloc(dec.samples*sizeof(mp3d_sample_t));
size_t readed = mp3dec_ex_read(&dec, buffer, dec.samples);
if (readed != dec.samples) /* normal eof or error condition */
{
if (dec.last_error)
{
/* error */
}
}
```
## Bindings
* https://github.com/tosone/minimp3 - go bindings
* https://github.com/notviri/rmp3 - rust `no_std` bindings which don't allocate.
* https://github.com/germangb/minimp3-rs - rust bindings
* https://github.com/johangu/node-minimp3 - NodeJS bindings
* https://github.com/pyminimp3/pyminimp3 - python bindings
* https://github.com/bashi/minimp3-wasm - wasm bindings
## Interesting links
* http://keyj.emphy.de/minimp3/
* https://github.com/technosaurus/PDMP3
* https://github.com/technosaurus/PDMP2
* https://github.com/packjpg/packMP3
* https://sites.google.com/a/kmlager.com/www/projects
* https://sourceforge.net/projects/mp3dec/
* http://blog.bjrn.se/2008/10/lets-build-mp3-decoder.html
* http://www.mp3-converter.com/mp3codec/
* http://www.multiweb.cz/twoinches/mp3inside.htm
* https://www.mp3-tech.org/
* http://id3.org/mp3Frame
* https://www.datavoyage.com/mpgscript/mpeghdr.htm

View File

@ -0,0 +1,8 @@
if tup.getconfig("NO_GCC") ~= "" then return end
if tup.getconfig("HELPERDIR") == ""
then
HELPERDIR = "../../../../programs"
end
tup.include(HELPERDIR .. "/use_gcc.lua")
CFLAGS = CFLAGS_OPTIMIZE_SPEED .. " -o minimp3.obj -nostdlib -fwhole-program"
compile_gcc{"minimp3.c"}

View File

@ -0,0 +1,3 @@
@echo off
gcc -c minimp3.c -o minimp3.obj -O2 -nostdlib
pause

View File

@ -0,0 +1,76 @@
// minimp3 for KolibriOS in native shared COFF library format.
// Some functions to allow us building without any external libs
// memset - may be optimized
typedef unsigned int size_t;
static inline void* memset(void *mem, int c, unsigned size) {
for (unsigned int i = 0; i < size; i++ )
*((char *)mem+i) = (char) c;
return mem;
}
// memcpy - may be optimized
void* memcpy(void *dest, const void *src, size_t count) {
for (unsigned int i = 0; i < count; i++)
*(char *)(dest+i) = *(char *)(src+i);
return 0;
}
// For building with mingw compiler
void __chkstk_ms(){
return;
}
// Actual minimp3 related stuff starts here
#define MINIMP3_ONLY_MP3 // No MP2
//#define MINIMP3_ONLY_SIMD // No SSE2, some platforms might not have it
#define MINIMP3_NO_SIMD
//#define MINIMP3_NONSTANDARD_BUT_LOGICAL
//#define MINIMP3_FLOAT_OUTPUT
#define MINIMP3_IMPLEMENTATION // Include the actual decoder
#include "minimp3.h"
// KolibriOS type EXPORTS header
int __stdcall start(){
return 1;
}
int __stdcall version_major(){
return 1;
}
int __stdcall version_minor(){
return 0;
}
typedef struct{
char *name;
void *f;
}export_t;
char szStart[] ="START";
char szVersion[] ="version";
char szVersionM[] ="version_min";
char szInit[] ="init";
char szDecode[] ="decode";
export_t EXPORTS[] __asm__("EXPORTS") =
{
{ szStart, start },
{ szVersion, version_major },
{ szVersionM, version_minor },
{ szInit, mp3dec_init },
{ szDecode, mp3dec_decode_frame },
{ NULL, NULL },
};
// End of file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
struc mp3dec_frame_info {
.frame_bytes dd ? ; signed int
.frame_offset dd ? ; signed int
.channels dd ? ; signed int
.hz dd ? ; signed int
.layer dd ? ; signed int
.bitrate_kbps dd ? ; signed int
}
; mp3d rb 8192
; invoke mp3dec_init, mp3d
; test eax, eax
; jz
; mp3dec_frame_info info
; pcm rw MINIMP3_MAX_SAMPLES_PER_FRAME
; invoke mp3dec_decode_frame, mp3d, input_buf, [buf_size], pcm, info
; mov [samples], eax