aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/lib/mp3/examples/standalone/main.c
blob: 91204245b4710fe99269ca43daab6242233fbe20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mp3dec.h"

#define MAX_BUFFER_LEN (MAX_NSAMP * MAX_NGRAN * MAX_NCHAN)

int16_t audiodata[MAX_BUFFER_LEN];

typedef struct {
    char *ptr, *end;
} stream;
#define READ_PTR(stream) ((void*)((stream)->ptr))
#define BYTES_LEFT(stream) ((stream)->end - (stream)->ptr)
#define CONSUME(stream, n) ((stream)->ptr += n)

void skip_id3v2(stream* self) {
    if (BYTES_LEFT(self) < 10) {
        return;
    }
    uint8_t *data = READ_PTR(self);
    if (!(
            data[0] == 'I' &&
            data[1] == 'D' &&
            data[2] == '3' &&
            data[3] != 0xff &&
            data[4] != 0xff &&
            (data[5] & 0x1f) == 0 &&
            (data[6] & 0x80) == 0 &&
            (data[7] & 0x80) == 0 &&
            (data[8] & 0x80) == 0 &&
            (data[9] & 0x80) == 0)) {
        return;
    }
    uint32_t size = (data[6] << 21) | (data[7] << 14) | (data[8] << 7) | (data[9]);
    size += 10; // size excludes the "header" (but not the "extended header")
    CONSUME(self, size + 10);
}

bool mp3file_find_sync_word(stream* self) {
    int offset = MP3FindSyncWord(READ_PTR(self), BYTES_LEFT(self));
    if (offset >= 0) {
        CONSUME(self, offset);
        return true;
    }
    return false;
}

void fatal(const char *msg) { fprintf(stderr, "%s\n", msg); exit(1); }
void perror_fatal(const char *msg) { perror(msg); exit(1); }

bool probable_overflow(int16_t a, int16_t b) {
    if(a > 32700 && b < -32700) {
        return true;
    } else if(b > 32700 && a < -32700) {
        return true;
    } else {
        return false;
    }
}

void look_for_overflow(int16_t *ptr, size_t os, int frame) {
    for(size_t i=2; i<os; i+=2) {
        int16_t l_old = ptr[i-2];
        int16_t l_new = ptr[i];
        int16_t r_old = ptr[i-1];
        int16_t r_new = ptr[i+1];

        if(probable_overflow(l_old, l_new)) {
            printf("probable overflow, left  channel,  frame %5d sample %5d, %5d\n", frame, (i/2)-1, i/2);
            printf("Consecutive sample values: %5d %5d\n", l_old, l_new);
        }
        if(probable_overflow(r_old, r_new)) {
            printf("probable overflow, right channel,  frame %5d sample %5d, %5d\n", frame, (i/2)-1, i/2);
            printf("Consecutive sample values: %5d %5d\n", r_old, r_new);
        }
    }
}

int main(int argc, char **argv) {
    if(argc != 3) {
        fprintf(stderr, "Decode MP3 into headerless LE16 stereo\n");
        fprintf(stderr, "Usage: %s input.mp3 output.bin\n", argv[0]);
        exit(99);
    }

    HMP3Decoder decoder = MP3InitDecoder();

    FILE *fi = fopen(argv[1], "rb");
    if(!fi) perror_fatal("open");

    struct stat st;
    if(fstat(fileno(fi), &st) < 0) perror_fatal("fstat");

    stream s;
    s.ptr = malloc(st.st_size);
    if(!s.ptr) perror_fatal("malloc");
    s.end = s.ptr + st.st_size;

    if(fread(s.ptr, 1, st.st_size, fi) != st.st_size) perror_fatal("fread");

    FILE *fo = fopen(argv[2], "wb");
    if(!fo) perror_fatal("open");
    
    skip_id3v2(&s);

    int frame=0;
    while(mp3file_find_sync_word(&s)) {
        MP3FrameInfo fi;
        int err = MP3GetNextFrameInfo(decoder, &fi, READ_PTR(&s));
        if(err != ERR_MP3_NONE) fatal("MP3GetNextFrameInfo");
        int bytes_left = (int)BYTES_LEFT(&s);
        uint8_t *inbuf = READ_PTR(&s);
        err = MP3Decode(decoder, &inbuf, &bytes_left,
                audiodata, 0);
        if(err != ERR_MP3_NONE) fatal("MP3Decode");
        look_for_overflow(audiodata, fi.outputSamps, ++frame);
        if(fwrite(audiodata, 1, fi.outputSamps*sizeof(int16_t), fo)
                != fi.outputSamps*sizeof(int16_t))
            perror_fatal("fwrite");
        CONSUME(&s, BYTES_LEFT(&s) - bytes_left); 
    }

    return 0;
}