Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decoding of the first frame #53

Closed
ghost opened this issue Dec 25, 2018 · 69 comments
Closed

Decoding of the first frame #53

ghost opened this issue Dec 25, 2018 · 69 comments
Labels
question Further information is requested

Comments

@ghost
Copy link

ghost commented Dec 25, 2018

When a frame following the first frame is decoded it uses the previous frame. How is the first frame decoded, and what is used instead of the previous frame?

@ghost
Copy link
Author

ghost commented Dec 25, 2018

Maybe I mean granule. I don't really know.

@lieff
Copy link
Owner

lieff commented Dec 25, 2018

mp3 have concept of bit-reservoir, i.e. frame can use unused tail bits (because encoder rate control fails to use all bits) for next frame. If first frame become unavailable, decoder can't decode second frame (if bit-reservoir used), but if it's still passed to decoder, bit-reservoir become full and decoder can decode 3-rd frame. The number of frames before should have 511 payload bytes in the worst case to completely fill the reservoir.

@lieff lieff added the question Further information is requested label Dec 25, 2018
@ghost
Copy link
Author

ghost commented Dec 25, 2018

That leaves more questions.
This is believed to be a frame data (I've stripped 4 bytes).
b'\x00\x00\x00\x00\x00i\x06\x00\x00\x00\x00\x00\r \xc0\x00\x00\x00\x00\x01\xa4\x1c\x00\x00\x00\x00\x004\x83\x80\x00\x00LAME3.92UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.92UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU'
Is it? Doesn't look like audio to me. Well, it may be a heavily compressed zero padded block of zeros which it probably is. Can you explain this simple example to me?
So, let me sum everything I've observed. A Layer 3 alike frame returns 1152 samples per channel when decoded. That is 1152 time samples which come from 2304 frequency samples processed by IMDCT. But let us start from the first: I have to perform a Huffman decoding. It seems that Huffman tables are not provided per frame but are part of the MP3 standard. From http://blog.bjrn.se/2008/10/lets-build-mp3-decoder.html : "The largest code table defined in the standard has samples no larger than 15. ". That's unclear. I think that means: "The MP3 standard allows a Huffman table word to be decoded into samples with a value not any larger than 15". When I've first saw this I've thought it's "no more than 15 samples". Also "Somewhat confusingly, the standard calls these possibilities “tables”. We will call them table pairs instead." I'm more confused by these "table pairs". So the Huffman decoding is as follows: get a word, decode it to 2 or 4 samples, for each of them, if the sample is 15 read linbits bits as unsigned and add'em to the sample, if the next bit is true, swap the sign. But the formats(read structs) of the big, quad and zero regions are not covered. I need to know which table is used to encode the region but I have no idea where it is stored. Same for the reordering. Heading to the requant. "The MP3 encoder uses a non-linear quantizer". When I first saw that I've thought about mulaw. No, it's more like elementwise(Hadamard, Schur) vector product. About stereo - I'm only interested in mono and stereo structs. How the stereo frame differs from the mono frame. Back to the IMDCT. So, if I'd have decompressed, amplified and mixed MDCT samples from the current frame in one buffer, and either same from the previous frame or zeros in another buffer, I'd divide both buffers to 32 parts, then create another two new buffers, then for each part I'd zero the new buffers and then write only the current part to them at the original place, for each part, do IMDCT and apply a window. "The value chunkBlockType is an integer, either 0,1,2 or 3. This decides which window is used." Where is chunkBlockType? About the overlap. So, to do IMDCT I'll take current 1152 samples and previous 1152 samples or 1152 zeros, take IMDCT then mix with IMDCT of the previous frame by the window, so I'll mix with the previous frame twice, both in frequency and time domain.
My point is I want to decode everything manually, preserving all the results of middle steps, preferably in struct array format. I want to know all properties of the frame. Not only those in the header. I've already wrote some code, I'm able to play MP3 files with Python.

@lieff
Copy link
Owner

lieff commented Dec 25, 2018

This is standard first frame lame creates to hide metadata. This frame almost empty, lame creates Info/Xing block in unused space (not used for bit reservoir too) so other applications can find it. Here where lame creates it https://github.com/gypified/libmp3lame/blob/master/libmp3lame/VbrTag.c#L900
VBRTag1/VBRTag0 - Info/Xing metadata marker.

About Huffman decode you may want to check: https://github.com/lieff/minimp3/blob/master/minimp3.h#L773
And about frame side data: https://github.com/lieff/minimp3/blob/master/minimp3.h#L470
Here where Huffman table selectors readed: https://github.com/lieff/minimp3/blob/master/minimp3.h#L576
And block type: https://github.com/lieff/minimp3/blob/master/minimp3.h#L538

PS: If you interested in python implementation take a look at https://bitbucket.org/portalfire/pymp3

@ghost
Copy link
Author

ghost commented Dec 26, 2018

I was more interested in

int tab_num = gr_info->table_select[ireg];
. than in https://github.com/lieff/minimp3/blob/master/minimp3.h#L773. What is ireg, where is its origin? Well, at L757 it's set to 0. But what does it mean - MP3 always starts from table 0? Your code seems too obfuscated for me. You name the variables inconsistently making it impossible to type a var name and find all occurencies. Also if you create a struct you might have been passed pointer to that struct but you are passing the fields what results in a complete mess.
I'm not going to re-implement all that stuff. Maybe I'll edit your code to make it more readable. Currently I have one more question. Imagine I don't have a MP3 file, but I wrote an utility that generates data that looks like a frame. Synthesizing a frame, I mean. Can I decode that frame alone, or do I need the previous frame? When I use the short mode, do I need previous granules to decode the next granule, or I can decode the granule alone?
L3_read_side_info seems useful for me. How do I call it on the frame?

@ghost
Copy link
Author

ghost commented Dec 26, 2018


typedef struct
{
    int frame_bytes, channels, hz, layer, bitrate_kbps;
} mp3dec_frame_info_t;

typedef struct
{
    float mdct_overlap[2][9*32], qmf_state[15*2*32];
    int reserv, free_format_bytes;
    unsigned char header[4], reserv_buf[511];
} mp3dec_t;
typedef struct
{
    const uint8_t *buf;
    int pos, limit;
} bs_t;

typedef struct
{
    float scf[3*64];
    uint8_t total_bands, stereo_bands, bitalloc[64], scfcod[64];
} L12_scale_info;

typedef struct
{
    uint8_t tab_offset, code_tab_width, band_count;
} L12_subband_alloc_t;

typedef struct
{
    const uint8_t *sfbtab;
    uint16_t part_23_length, big_values, scalefac_compress;
    uint8_t global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
    uint8_t table_select[3], region_count[3], subblock_gain[3];
    uint8_t preflag, scalefac_scale, count1_table, scfsi;
} L3_gr_info_t;

typedef struct
{
    bs_t bs;
    uint8_t maindata[MAX_BITRESERVOIR_BYTES + MAX_L3_FRAME_PAYLOAD_BYTES];
    L3_gr_info_t gr_info[4];
    float grbuf[2][576], scf[40], syn[18 + 15][2*32];
    uint8_t ist_pos[2][39];
} mp3dec_scratch_t;

Can you put comments?

@lieff
Copy link
Owner

lieff commented Dec 26, 2018

ireg it's region counter, there up to 3 regions can have own Huffman table, decoding starts from gr_info->table_select[0] table (it's not necessarily zero).

Even if frame (have 1 or 2 granules) do not use bit-reservoir, first frame alone will be not in good shape because of filterbank must be feeded, 2nd frame+ will be in good enough shape.

Side info encoded right after header and CRC: https://github.com/lieff/minimp3/blob/master/minimp3.h#L1699

You are right, this code is not optimized for reading but for small size (both code and binary) and performance and I'm not going to change that.

Note that canonical mpg123 have even more optimized Huffman: https://github.com/georgi/mpg123/blob/master/src/libmpg123/layer3.c#L706
For me it's even less understandable despite it have some comments.

If you are looking for most understandable and commented code, I think it's reference code "MPEG-2 Audio Simulation Software Distribution 10".

@ghost
Copy link
Author

ghost commented Dec 26, 2018

#include <iostream>

typedef unsigned char uchar;
typedef struct mp3_header{
    uchar sync1 : 8;
    uchar sync2 : 3;
    uchar mpegVer : 2;
    uchar mpegLayer : 2;
    bool notProtected : 1;
    uchar _bitrate : 4;
    uchar _samplerate : 2;
    bool padded : 1;
    bool privateBit : 1;
    uchar _stereo : 2;
    uchar _jStereo : 2;
    bool proprietary : 1;
    bool original : 1;
    uchar emphasis : 2;
    unsigned short getBitrate();
    unsigned short getSamplerate();
    bool isStereo();
};

int main() {
    std::cout << sizeof(mp3_header) << std::endl; //4
    return 0;
}

Why the code can't be like that? Just cast a pointer of a frame to (mp3_header*) and get all the fancy stuff. Also, readability can be mixed with speed. Also I don't see a point in making headers smaller.
But if you think your code is small I gonna break your illusions. Your code could be some kilobytes smaller if you use packed structs like that. Your code can be optimized even further. In fact, I'm going to fork you.

@lieff
Copy link
Owner

lieff commented Dec 26, 2018

I'm not against using bitfields, but it must be tested with at least following compilers:
gcc5-8 (arm, aarch64, x86, x64, ppc, ppc64), clang6-7 (arm, aarch64, x86, x64), armcc (arm, aarch64), msvc 20013-2017 (arm, aarch64, x86, x64).

Code size must be roughly same or less, and profiling must not show slowdown.

Usually compiler do same shift + and operations, but back some years ago I've experienced that some compilers do silly things with bitfields and do not use it since then. There also problem with big endian vs little endian on some architectures\compilers and you do not know in what order bytes are mapped to memory. In decoders it's usually handled in one place - get_bits() and then you may not think about endian till output generation.

@ghost
Copy link
Author

ghost commented Dec 26, 2018

There's no problems with endian if you use char.
Also chars can be used for endian detect.

@lieff
Copy link
Owner

lieff commented Dec 26, 2018

That's why minimp3 reads input by chars in get_bits. It's not very optimal, usually optimized decoders uses big machine word load and byte swap instruction if available for architecture.
But there problem when you map structure mp3_header sizeof(mp3_header)>sizeof(char) on input stream directly, so, you must properly handle it.

@ghost
Copy link
Author

ghost commented Dec 26, 2018

Did you say "But there's a problem when you map a structure mp3_header sizeof(mp3_header)>sizeof(char) on input stream directly, so, you must properly handle that problem."?

@lieff
Copy link
Owner

lieff commented Dec 26, 2018

#include <iostream>
#include <ios>

typedef unsigned char uchar;
typedef struct mp3_header{
    uchar sync1 : 8;
    uchar sync2 : 3;
    uchar mpegVer : 2;
    uchar mpegLayer : 2;
    bool notProtected : 1;
    uchar _bitrate : 4;
    uchar _samplerate : 2;
    bool padded : 1;
    bool privateBit : 1;
    uchar _stereo : 2;
    uchar _jStereo : 2;
    bool proprietary : 1;
    bool original : 1;
    uchar emphasis : 2;
};

int main() {
    const unsigned char stream[] = { 1, 0, 0, 0 };
    mp3_header *hdr = (mp3_header *)stream;
    std::cout << (int)hdr->sync1 << std::endl;
    return 0;
}

This construct can have problems, I've experienced:

  • Performance problem, compiler produces more instructions.
  • Endian errors.
  • Aliment issues while compiler tries to optimize loads (clang iirc and crash happen).

Modern compilers should be good, but list above should be checked.
Also clang and gcc become more and more strict about UB's and to map struct at byte alignment you must use nonstandard extensions like __attribute__ ((__aligned__ (1))) or memcpy.

@sagamusix
Copy link
Contributor

Bit fields are implementation-defined. If your compiler wants, it may choose a completely different bit packing layout than what you expect. Please don't make minimp3 rely on such implementation-defined behaviour, it's bound to fail on at least some obscure platforms where the current code works just fine.
__attribute__ ((__aligned__ (1))) is also dangerous especially when passing around pointers to data extracted from such a struct, because the extracted pointer no longer contains any alignment information, and the compiler is free to assume that the data is aligned naturally.

@lieff
Copy link
Owner

lieff commented Dec 26, 2018

Yeah, I'm completely forgot that it's also implementation defined

C99 6.7.2.1-11:An implementation may allocate any addressable storage unit large enough to hold a bit- field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.

RIP bit fields for minimp3 use-case then.

@ghost
Copy link
Author

ghost commented Dec 26, 2018

The mess in the code leads to bugs.

minimp3/minimp3.h

Line 1626 in e9df076

if (i + k + nextfb + HDR_SIZE > mp3_bytes || !hdr_compare(mp3, mp3 + k + nextfb))

should be
if (i + k + hdr_padding(mp3) + nextfb + HDR_SIZE > mp3_bytes ) break;
if(!hdr_compare(mp3, mp3 + k + nextfb)) continue;
EDIT: Oops. I meant if (i + fb + hdr_padding(mp3) + nextfb + HDR_SIZE > mp3_bytes ) break;
Which does mean
if (i + k + nextfb + HDR_SIZE > mp3_bytes ) break;
if(!hdr_compare(mp3, mp3 + k + nextfb)) continue;

@ghost
Copy link
Author

ghost commented Dec 26, 2018

RIP bit fields for minimp3 use-case then.
AFAIK, the most compilers do alignment of bitfields if there's non-bitfield members.
So, if I specify uchar:8 instead of uchar, there is one big bitfield, and no alignment should be done. Presto!
About the alignment. Alignment means the structure takes more space. It doesn't change the order, the packing does.
Of course if you want to be generic and cross-platform compatible, then RIP. But that's you, not me.

@lieff
Copy link
Owner

lieff commented Dec 26, 2018

There already more strict check i + 2*k < mp3_bytes, check inside the loop i + k + nextfb + HDR_SIZE > mp3_bytes - for corner cases and very low k numbers. So there no performance optimization in this break and it's incorrect, because next match can be valid.

100% RIP it's not because of alignment, but because of implementation defined bits location, even in uchar.

@ghost
Copy link
Author

ghost commented Dec 26, 2018

Next match? mp3_bytes seems to be filesize minus offset in bytes. So if i + k + hdr_padding(mp3) + nextfb + HDR_SIZE > mp3_bytes, it's EOF. There's nothing after EOF. No next match.

@ghost
Copy link
Author

ghost commented Dec 26, 2018

If you understand "implementation defined bits location" so well, could you please explain what deep meaning does this phrase have?

@sagamusix
Copy link
Contributor

It means that the bits can be packed in any way that the compiler sees fit. Just because you tested for example three different compilers on x86 (or whatever) that behaved the same doesn't mean that on another platform, they will behave identically. In particular this part of the quote from the standard is important:

allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined

On some non-x86 platforms it can be more optimal to pack the fields in reverse order (top to bottom rather than bottom to top). The compiler is free to do so. "I tested several compilers" is not a proof for the lack of a compiler that doesn't do something different.

@lieff
Copy link
Owner

lieff commented Dec 26, 2018

Yes, next match is possible, because at each k position there padding possibilities 0, 1 and 4, next match can have different padding.

@ghost
Copy link
Author

ghost commented Dec 26, 2018

An implementation may allocate any addressable storage unit large enough to hold a bit- field.

So any buffer that has equal or greater size than one bitfield can hold that bitfield. That doesn't mean anything because we have one great bitfield and it's first. C doesn't align first elements.

If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit.

That means adjacent bitfields are one big bitfield, and they keep the same order.

If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined.

If? Well. I said in GCC and Clang an element is only put in the next unit if it does not specify how much bits does it take, and thus, does not belong to a bitfield. But "implementation-defined". So, if a bitfield already contains 7 bits, we can't know if the next 2 bits will be placed immediately or after 1 bit. But we can! assert(sizeof(typename)==x) is our friend.

The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.

Within. RIP. But not for me, because I'm not going to be cross-platform.
Or, maybe not. A unit is byte. Inside a byte, bits can be shifted left and right. There's no reason why one could be faster then another. EDIT maybe there is, for signed numbers. Of course, the first is only shifted and the last, if it's not the edge, is only AND-ed.
At the edge, LSBs are AND-ed and shifted, and MSBs are shifted only. If to do the opposite, the number will be multiplied by the power of 2. It does not matter how the number is actually stored. EDIT the result is defined, but the direction of the shift is not. Don't know why.
So if "the order of allocation of bit-fields within a unit is implementation-defined", it's not because of speed.

@ghost
Copy link
Author

ghost commented Dec 26, 2018

How about:

struct foo {
#ifdef BIG_ENDIAN_BITFIELDS
    unsigned x:5;
    unsigned y:3;
#else
    unsigned y:3;
    unsigned x:5;
#endif
};

@ghost
Copy link
Author

ghost commented Dec 27, 2018

I was wrong with padding. But

if (i + k + nextfb + HDR_SIZE > mp3_bytes ) break;//that is EOF, the match is impossible
 if(!hdr_compare(mp3, mp3 + k + nextfb)) continue;//and there, the match is possible

That is a memory error. It could seek after the allocated memory.

@lieff
Copy link
Owner

lieff commented Dec 27, 2018

Also note that there no point to optimize this loop when iterations count is low. But imagine this loop goes 1000-2000 iterations (till MAX_FREE_FORMAT_FRAME_SIZE). This case is good optimization candidate.
Most obvious I see to do something like that:

                    if (!hdr_compare(mp3, mp3 + k + nextfb) + (i + k + nextfb + HDR_SIZE > mp3_bytes))
                        continue;

i.e. make one branch instead of two (and make something with buffer overread), which can be measurable improvement in busy loop at least on x86/x64.
But this function even not shown in complex mp3's profile. For now main problem resides in L3_imdct36/L3_dct3_9 optimization.

PS: There no overread in initial code, C standard guarantees that continue executed if i + k + nextfb + HDR_SIZE > mp3_bytes condition is true and !hdr_compare(mp3, mp3 + k + nextfb) is never executed.

@ghost
Copy link
Author

ghost commented Dec 27, 2018

So, if the loop reaches EOF it simply repeats before it reaches MAX_FREE_FORMAT_FRAME_SIZE. The part "i + 2*k < mp3_bytes - HDR_SIZE" guarantees that it stops after continue.
But that makes another problem. If there's break, the next statement is not executed too, so we have branching in both cases. || and && cause branching too, don't you know? | and & don't. Even if you have "one" condition, it generates multiple jumps. Jump if first is true, jump if second is true - that's assembly. Accumulating the result would have taken another register, and in the best case one command more, and all conditions would be evaluated, didn't you think about that? The only difference is after a break,

minimp3/minimp3.h

Line 1620 in e9df076

for (k = HDR_SIZE; !frame_bytes && k < MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - HDR_SIZE; k++)

is not executed. It skips directly to the

minimp3/minimp3.h

Line 1633 in e9df076

if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&

There's no memory error but using break could save 3 branches after the EOF. The count of the branches while the loop is the same. The only thing - fusing it would make the code shorter with (almost) no affect on performance, because my version is faster only when the loop reaches EOF otherwise it's the same speed.

@ghost
Copy link
Author

ghost commented Dec 27, 2018

The rule - if only one of many is evaluated, there's branching. Otherwise there's not.

@ghost
Copy link
Author

ghost commented Dec 27, 2018


:0
//do something
cmp a b
je 0
cmp a c
jne 1
//do something
:1

Your code.

:0
cmp a c
jne 1
//do something
cmp a b
je 0
cmp a c
jne 0
//do something
:1

@ghost
Copy link
Author

ghost commented Dec 27, 2018

Your mp3d_find_frame is 1366 bytes, my OO mp3frame_getNext is 1508.

@ghost
Copy link
Author

ghost commented Dec 27, 2018

Right. It ignores CRC, and that is obvious, but it does nothing with bs_frame. Am I correct?
I mean, if I'd did that I would have modified bs_frame.

@lieff
Copy link
Owner

lieff commented Dec 27, 2018

Nope, it updates bs_frame->pos field.

@ghost
Copy link
Author

ghost commented Dec 27, 2018

"if ((bs->pos += n) > bs->limit)"
Never did anything like that. *dp++ is my favorite but I'm always looking in the reference before doing stuff like this. What does it mean?

@lieff
Copy link
Owner

lieff commented Dec 27, 2018

This equivalent to

bs->pos += n;
if (bs->pos > bs->limit)

@ghost
Copy link
Author

ghost commented Dec 27, 2018

I do not update len alone. Pythonic way is to increment the pointer and decrement the length.

@ghost
Copy link
Author

ghost commented Dec 27, 2018

That can be useful to detect bitfield order. Credits:
https://stackoverflow.com/questions/4239993/determining-endianness-at-compile-time
#define I_AM_LITTLE (((union { uchar x : 7; uchar c : 1; }){1}).c)
EDIT: I think
#define I_AM_LITTLE (((union { uchar x; struct{ uchar y: 7; uchar c : 1; } c }){1}).c.c)
is more generic.
Of course

If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined.

So, I have to be careful that no bitfields are on the edge. 8, 3+2+2+1, 4+2+1+1,2+2+1+1+2, you got the idea.

@ghost
Copy link
Author

ghost commented Dec 27, 2018

It's polymorphism.

    
    typedef struct {
        float pcm[MAX_FREE_FORMAT_FRAME_SIZE];
        sshort len;
    } pcm;
    
    typedef struct {
        float pcm[MAX_FREE_FORMAT_FRAME_SIZE>>1][2];
        sshort len;
    } stereo;

@lieff
Copy link
Owner

lieff commented Dec 27, 2018

Yeah, I know that trick and use same idea detect float point structure: https://github.com/lieff/lvg/blob/master/swf/avm1.c#L100
But that's not so embedded project like minimp3, which target micro-controllers like ESP32.

@ghost
Copy link
Author

ghost commented Dec 27, 2018

@ghost
Copy link
Author

ghost commented Dec 29, 2018

https://pastebin.com/Dwf3c1gg
Checked by https://gist.github.com/bckpkol/c35e0a832f99f8228e5e2f2798f5bb0a
That tells L3 can be of 104, 112 or 288 bits.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

Um. Didn't know zero-length bitfiels are align commands and cannot have names.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

And even a void takes 1 byte. RIP?
No. Cause I can move extra fields to the end. But then how to make them zero?

@lieff
Copy link
Owner

lieff commented Dec 29, 2018

    uchar sync2 : 3;
    uchar sync1 : 8;

There no guarantee that this should be at end of structure for LE. Also note that sizeof(uint) can be != 4 for some architectures, especially micro-controllers + unit mentioned in standard can be anything. And also there more endian variants possible https://github.com/lieff/lvg/blob/master/swf/avm1.c#L150
Also this should be put in separate file to change L3_MPEG1/L3_STEREO macros and create several needed structure variants.
So I highly doubt it can help for single header and be roughly same code size. Get_bits approach is not very worse in terms of readability, It's also used in ffmpeg which do not have code size restrictions.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

    bool notProtected : 1;
    uchar mpegLayer : 2;
    uchar mpegVer : 2;
    uchar sync2 : 3;
    uchar sync1 : 8;

Everything's okay there. Again, I wrote a Python script that checks the alignment for all probabilities of ifdefs.
And there's I_AM_LITTLE.
And I've added some align bits.
And that's not gonna work right now.

@lieff
Copy link
Owner

lieff commented Dec 29, 2018

I think bit-fields still can be used in such low-level code, but when you create and use bit-fields objects for internal structures and do not try assume it's structure for read/write in files etc. It's just not worth it.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

My solution is std::false_type but it's only C++.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

@ghost
Copy link
Author

ghost commented Dec 29, 2018

Maybe Nim?
https://github.com/yglukhov/nimpy
But it's not finished - it cannot export types.
It seems I should hack it another way. Maybe C++.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

class bitfield {
    public:
        bitfield();
        reset(uchar* buf, uchar start, uchar bits)
        operator uint() const;
        bitfield& operator=(const uint& rhs);
        bitfield& operator+=(const uint& rhs);
        bitfield& operator-=(const uint& rhs);
        bitfield& operator*=(const uint& rhs);
        bitfield& operator/=(const uint& rhs);
        bitfield& operator%=(const uint& rhs);
        bitfield& operator&=(const uint& rhs);
        bitfield& operator|=(const uint& rhs);
        bitfield& operator^=(const uint& rhs);
        bitfield& operator<<=(const uint& rhs);
        bitfield& operator>>=(const uint& rhs);
        bool operator ==(const uint& rhs) const;
        bool operator !=(const uint& rhs) const;
        bool operator <(const uint& rhs) const;
        bool operator <=(const uint& rhs) const;
        bool operator >(const uint& rhs) const;
        bool operator >=(const uint& rhs) const;
    private:
        uchar* buf;
        uchar[2] mask;
        uchar rshift;
        bitfield& operator=(const bitfield& rhs);
        bitfield& operator+=(const bitfield& rhs);
        bitfield& operator-=(const bitfield& rhs);
        bitfield& operator*=(const bitfield& rhs);
        bitfield& operator/=(const bitfield& rhs);
        bitfield& operator%=(const bitfield& rhs);
        bitfield& operator&=(const bitfield& rhs);
        bitfield& operator|=(const bitfield& rhs);
        bitfield& operator^=(const bitfield& rhs);
        bitfield& operator<<=(const bitfield& rhs);
        bitfield& operator>>=(const bitfield& rhs);
} ;

Still not implemented but looks promising.

@lieff
Copy link
Owner

lieff commented Dec 29, 2018

I'm aware about Nim, but it's more like another scripting alternative for lieff/lvg#1
Do not see how it may help minimp3.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

@ghost
Copy link
Author

ghost commented Dec 29, 2018

https://pastebin.com/61LTNf8G
Yet not everything is implemented. I'll be mostly copy-pasting from C version.

@ghost
Copy link
Author

ghost commented Dec 29, 2018

scfsi &= 0x0F0F;

doesn't look right because of
gr->scfsi = (uint8_t)((scfsi >> 12) & 15);

Oh. Silly me. 0x0f0f is 16 bits, everything's fine.
Btw, what is contained in 4 bits that's zeroed? Is it correct to zero'em in mp3 file?
Value of region_count[2] in "tight" case is rather 0, but I'm unsure.

@lieff
Copy link
Owner

lieff commented Dec 29, 2018

This is tricky part to simplify scale factors reading. Only long types 0,1,3 uses scfsi bits and this bits must be cleared because garbage can be here which ignored by reference decoder, but simplified L3_read_scalefactors looks at them first.
There special test vectors which tests all modes, and test procedure should blow up in case of such errors.

@ghost
Copy link
Author

ghost commented Dec 30, 2018

Again - is it correct to zero'em in mp3 file?

@ghost
Copy link
Author

ghost commented Dec 30, 2018

I've optimized your parse_ext even further.
With -O3 it's the same, with -O1 mine is a little bit faster (up to 2x).
Edit: don't take me wrong, my code is equally fast with O1 and O3.
Edit: replacing unsigned long with unsigned long long makes my code faster than yours at any optimization level.
Edit. After a year. Ha! Haha. To readers. That only works on little endian, because MINIMUM finds the last dot of 8 on big endian. For BE, you shall invert the bitmask and use MAXIMUM, finally checking for == 255. For mixed endian, you are SOL. If you used that anywhere, fix. I may rewrite this in my spare time.
Edit... MINIMUM doesn't always finds the best on LE. Neither the MAXIMUM on BE. It will find a dot, but not the first dot.
Say, if it's aa.a**.* the behaviour is undefined.
If it's aa.aa.aa, the second dot is found.
I could now think about another algorithm that may always find the first dot.
For the 16 bits, two 65536 byte arrays could accelerate. One checks if there a dot, and second checks the position.
Extending to the 32 bits? Your method with two bitmasks already works well, but what if there's a better way?
32 bits means two 16 bit checks. There's no way to accelerate two. But in 64 bits, there's 4 checks.
So, unaccelerated - 1 to 4 plus 0 or 1 check.
If I halve that by ANDing lower and upper 32 bits...
1 to 2 plus 0 or 2 checks. That's lesser. It's probably impossible to shrink it further.
Just wrote a silly thing. It won't work if the dot isn't first. I mean, I removed it.
Edit. Another silly thing what may cause dot to be not findable at all. It's also probable I mistook LE for BE.
Edit - yes, for BE val<=(val|(val>>8) shall work. But holy! Look at this:
#ifdef LONGS_ARE_LITTLE
Holy. LONGS_ARE_LITTLE is always defined. It shall be:
#if LONGS_ARE_LITTLE
But it may still not work with a dumb preprocessor, which is unfortunately common.
Edit: added another algorithm. With test. Still didn't compared it. Also, the compiler complains about ISO C++.
Sorry, but I had to remove the code. Soon, I'll add new. View the history if you want fun.

Edit: nothing without a silly bug. It worked just because there was a dot. I didn't count the shift.
Edit: added extra tests. All passed.
Edit - silly. s variable is used instead of start.
Edit: with Pearson hashing, it is possible to find all dots in a char. It can be done in four ops using 16 bits. But can I determine the presence of a dot in 64 bits by less than 3 ops?
Edit: NO. The proof: binary search is the best. And 8 is 2 power 3, so no less than 3 ops.
Edit. ANDing lower and upper 32 bits won't work, because 11110000 and 00001111 equal 00000000, which is unwanted. Doing so would yield undefined behaviour. Still, I could make a check in 4 ops.
Edit, heh. Congrat me - I wrote a code that doesn't work with anything greater than -O1! But it runs fine at O1.
Edit. The code at O1 appears to be slower than brute force at O3.
Edit. When hand-opt code and brute force both compiled at O1, they are equally fast. It seems compiler does it the same way. So, here's the code (pity I can't check it at O3)
With Clang, brute force is faster at O3 and equal at O1. GCC better optimizes frute force, Clang is better with tables.
Edit. Actually tried your code. Sorry, it's slower than mine on Intel Atom, and way slower than GCC and Clang's brute force. "unlikely" doesn't help much. I would like to see what machine code GCC generated.
Edit. Forgot to say - never use non-ASCII chars, and use integers when possible.
Or maybe I accidentally used double quotes.
Edit. I take it back OOP doesn't hurt performance. It's implementation specific. When using almost exactly the code you posted, it works 2x faster than my best. Still, it isn't any faster than GCC brute force without OOP.

#include <stddef.h>
#include <stdint.h>
#define unlikely(x)     __builtin_expect(!!(x), 0)

static void parse_uri(const char *uri, char **ret, int len) {
    *ret  = NULL;
    for(int c = 0; c < len;)
    {
        uint64_t val = *(uint64_t*)(uri + c);
        if (unlikely( (((val ^ 0x003f003f003f003full) & 0x00ff00ff00ff00ffull) - 0x0001000100010001ull) & 0xff00ff00ff00ff00ull ))
        {
            if ((uri + c)[0] == '?')
                len = c; else
            if ((uri + c)[2] == '?')
                len = c + 2; else
            if ((uri + c)[4] == '?')
                len = c + 4; else
            if ((uri + c)[6] == '?')
                len = c + 6;
        }
        val >>= 8;
        if (unlikely( (((val ^ 0x003f003f003f003full) & 0x00ff00ff00ff00ffull) - 0x0001000100010001ull) & 0xff00ff00ff00ff00ull ))
        {
            int l = len;
            if ((uri + c)[1] == '?')
                l = c + 1; else
            if ((uri + c)[3] == '?')
                l = c + 3; else
            if ((uri + c)[5] == '?')
                l = c + 5; else
            if ((uri + c)[7] == '?')
                l = c + 7;
            if (l < len) 
                len = l;
        }
        c += sizeof(uint64_t);
    }
    *ret = (char *)uri + len;
}

static void parse_uri_brute(const char *uri, char **ret, int len) {
    *ret  = NULL;
    for(int c = 0; c < len; ++c)
    {
        if(uri[c]=='?') {
            *ret = (char *)uri + c;
            break;
        }
    }
}

int main() {
    char* ret=NULL;
    for(long i=0;i<1000;++i) {
        parse_uri_brute("aaaaaaaaaaaaaaaa", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbbbbbb", &ret, 16);
        parse_uri_brute("?aaaaaaaaaaaaaaa", &ret, 16);
        parse_uri_brute("?bbbbbbbbbbbbbbb", &ret, 16);
        parse_uri_brute("b?aaaaaaaaaaaaaa", &ret, 16);
        parse_uri_brute("a?bbbbbbbbbbbbbb", &ret, 16);
        parse_uri_brute("bb?aaaaaaaaaaaaa", &ret, 16);
        parse_uri_brute("aa?bbbbbbbbbbbbb", &ret, 16);
        parse_uri_brute("bbb?aaaaaaaaaaaa", &ret, 16);
        parse_uri_brute("aaa?bbbbbbbbbbbb", &ret, 16);
        parse_uri_brute("bbbb?aaaaaaaaaaa", &ret, 16);
        parse_uri_brute("aaaa?bbbbbbbbbbb", &ret, 16);
        parse_uri_brute("bbbbb?aaaaaaaaaa", &ret, 16);
        parse_uri_brute("aaaaa?bbbbbbbbbb", &ret, 16);
        parse_uri_brute("bbbbbb?aaaaaaaaa", &ret, 16);
        parse_uri_brute("aaaaaa?bbbbbbbbb", &ret, 16);
        parse_uri_brute("bbbbbbb?aaaaaaaa", &ret, 16);
        parse_uri_brute("aaaaaaa?bbbbbbbb", &ret, 16);
        parse_uri_brute("bbbbbbbb?aaaaaaa", &ret, 16);
        parse_uri_brute("aaaaaaaa?bbbbbb", &ret, 16);
        parse_uri_brute("bbbbbbbbb?aaaaaa", &ret, 16);
        parse_uri_brute("aaaaaaaaa?bbbbb", &ret, 16);
        parse_uri_brute("bbbbbbbbbb?aaaaa", &ret, 16);
        parse_uri_brute("aaaaaaaaaa?bbbb", &ret, 16);
        parse_uri_brute("bbbbbbbbbbb?aaaa", &ret, 16);
        parse_uri_brute("aaaaaaaaaaa?bbb", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbb?aaa", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaa?bbb", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbbb?aa", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaaa?bb", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbbbb?a", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaaaa?b", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbbbbb?", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaaaaa?", &ret, 16);
        parse_uri_brute("?aaaaaaa?aaaaaaa", &ret, 16);
        parse_uri_brute("?bbbbbbb?bbbbbbb", &ret, 16);
        parse_uri_brute("b?aaaaaa?aaaaaaa", &ret, 16);
        parse_uri_brute("a?bbbbbb?bbbbbbb", &ret, 16);
        parse_uri_brute("bb?aaaaa?aaaaaaa", &ret, 16);
        parse_uri_brute("aa?bbbbb?bbbbbbb", &ret, 16);
        parse_uri_brute("bbb?aaaa?aaaaaaa", &ret, 16);
        parse_uri_brute("aaa?bbbbbbbbbbbb", &ret, 16);
        parse_uri_brute("bbbb?aaa?aaaaaaa", &ret, 16);
        parse_uri_brute("aaaa?bbb?bbbbbbb", &ret, 16);
        parse_uri_brute("bbbbb?aa?aaaaaaa", &ret, 16);
        parse_uri_brute("aaaaa?bb?bbbbbbb", &ret, 16);
        parse_uri_brute("bbbbbb?a?aaaaaaa", &ret, 16);
        parse_uri_brute("aaaaaa?b?bbbbbbb", &ret, 16);
        parse_uri_brute("bbbbbbb??aaaaaaa", &ret, 16);
        parse_uri_brute("aaaaaaa??bbbbbbb", &ret, 16);
        parse_uri_brute("bbbbbbbb?aaa?aaa", &ret, 16);
        parse_uri_brute("aaaaaaaa?bbb?bbb", &ret, 16);
        parse_uri_brute("bbbbbbbbb?aa?aaa", &ret, 16);
        parse_uri_brute("aaaaaaaaa?bb?bbb", &ret, 16);
        parse_uri_brute("bbbbbbbbbb?a?aaa", &ret, 16);
        parse_uri_brute("aaaaaaaaaa?b?bbb", &ret, 16);
        parse_uri_brute("bbbbbbbbbbb??aaa", &ret, 16);
        parse_uri_brute("aaaaaaaaaaa??bbb", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbb?a?a", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaa?b?b", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbbb??a", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaaa??b", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbbbb??", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaaaa??", &ret, 16);
        parse_uri_brute("bbbbbbbbbbbbbbb?", &ret, 16);
        parse_uri_brute("aaaaaaaaaaaaaaa?", &ret, 16);
    }
    return 0;
}
#include <cstddef>
#include <cstdlib>
#include <cstdio>
#include <stdexcept>
typedef unsigned long long ull;
typedef unsigned long ui32;
typedef unsigned short ushort;
typedef unsigned char uchar;

#ifdef BCK_SILENT
#define BCK_SZ(f, a) a
#define BCK_NULL(f) ;
#else
#define BCK_SZ printf
#define BCK_NULL printf
#endif

#define LONGS_ARE_LITTLE (((union { ull x; ui32 c; }){1}).c)
#define INTS_ARE_LITTLE (((union { ui32 x; ushort c; }){1}).c)
#define SHORTS_ARE_LITTLE (((union { ushort x; uchar c; }){1}).c)
#define I_AM_LITTLE (LONGS_ARE_LITTLE&&INTS_ARE_LITTLE&&SHORTS_ARE_LITTLE)
#define I_AM_LITTLE_X (LONGS_ARE_LITTLE||INTS_ARE_LITTLE||SHORTS_ARE_LITTLE)
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

size_t find_first(char const* const s, const size_t sz, const char ch) {
    ull const* start = (ull const*)s;
#ifndef BCK_NO_OPT
    {
    STATIC_ASSERT((I_AM_LITTLE == I_AM_LITTLE_X), mixed_endian);
        static const uchar pearson_LE[] = //65536 numbers of crap;
        static const uchar pearson_BE[] = //65536 numbers of crap;
        ull const* end = start+(sz>>3);
        const ushort masks = ch|(ch<<8);
        const ui32 maski = masks|(masks<<16);
        const ull mask = maski|(((ull)maski)<<32);
        while(start!=end) {
            const ull val = (*start)^mask;
            const ushort* vals = (const ushort*)&val;
            uchar temp = 2;
            if(I_AM_LITTLE) {
                for(uchar i = 0;i != 8;i+=(temp=pearson_LE[vals[i>>1]])) if(temp!=2) return (((char const*)start)-s)+i;
            }
            else {
                for(uchar i = 0;i != 8;i+=(temp=pearson_BE[vals[i>>1]])) if(temp!=2) return (((char const*)start)-s)+i;
            }
            ++start;
        }
    }
#endif
    char const* startc = (char const*)start;
    char const* end = s+sz;
    while(startc!=end) {
        if(*startc == ch) return startc-s;
        ++startc;
    }
    throw std::out_of_range("");
}

int main() {
    for(long i = 0;i<1000;++i) {
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first(".aaaaaaaaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first(".bbbbbbbbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("b.aaaaaaaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("a.bbbbbbbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bb.aaaaaaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aa.bbbbbbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbb.aaaaaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaa.bbbbbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbb.aaaaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaa.bbbbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbb.aaaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaa.bbbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbb.aaaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaa.bbbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbb.aaaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaa.bbbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbb.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaa.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbb.aaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaa.bbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbb.aaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaa.bbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbb.aaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaa.bbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbb.aaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaa.bbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbbb.aa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaaa.bb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbbbb.a",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaaaa.b",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbbbbb.",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaaaaa.",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first(".aaaaaaa.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first(".bbbbbbb.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("b.aaaaaa.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("a.bbbbbb.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bb.aaaaa.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aa.bbbbb.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbb.aaaa.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaa.bbbb.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbb.aaa.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaa.bbb.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbb.aa.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaa.bb.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbb.a.aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaa.b.bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbb..aaaaaaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaa..bbbbbbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbb.aaa.aaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaa.bbb.bbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbb.aa.aaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaa.bb.bbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbb.a.aaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaa.b.bbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbb..aaa",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaa..bbb",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbb.a.a",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaa.b.b",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbbb..a",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaaa..b",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("bbbbbbbbbbbbbb..",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    try {
        BCK_SZ("%zd\n", find_first("aaaaaaaaaaaaaa..",
16, '.'));
    } catch(std::out_of_range&) { BCK_NULL("NULL\n"); }
    }
    return 0;
}

@ghost
Copy link
Author

ghost commented Jan 1, 2019

That loop

for (i = sci->stereo_bands; i < sci->total_bands; i++)

is redundant.
Edit: no. I said this because I added that before to make everything initialized. But, should it really be after reading scalefactors?

@lieff
Copy link
Owner

lieff commented Jan 9, 2019

Yes, scfsi bits can be zero in mp3 file.
And yes, sci->bitalloc change is necessary and used later in L12_dequantize_granule.

@lieff lieff closed this as completed Jul 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants