xref: /btstack/test/sbc/sbc_encoder.py (revision 1bd8157e8c1d5bbfe83038c2ea9e6b99d35fcb36)
1c21a9c2fSMilanka Ringwald#!/usr/bin/env python
2c21a9c2fSMilanka Ringwaldimport numpy as np
3c21a9c2fSMilanka Ringwaldimport wave
4c21a9c2fSMilanka Ringwaldimport struct
5c21a9c2fSMilanka Ringwaldimport sys
6c21a9c2fSMilanka Ringwaldfrom sbc import *
7c21a9c2fSMilanka Ringwald
85c9bef5bSMilanka RingwaldX = np.zeros(shape=(2,80), dtype = np.int16)
9*1bd8157eSMilanka Ringwaldimplementation = "SIG"
10c21a9c2fSMilanka Ringwald
11ad470863SMilanka Ringwalddef fetch_samples_for_next_sbc_frame(fin, frame):
12ef8a7a12SMilanka Ringwald    raw_data = fin.readframes(frame.nr_blocks * frame.nr_subbands)
135c9bef5bSMilanka Ringwald    fmt = "%ih" % (len(raw_data) / 2)
141522543dSMilanka Ringwald    data = struct.unpack(fmt, raw_data)
151522543dSMilanka Ringwald
165c9bef5bSMilanka Ringwald    if frame.nr_channels == 2:
179462b3daSMilanka Ringwald        for i in range(len(data)/2):
185c9bef5bSMilanka Ringwald            frame.pcm[0][i] = data[2*i]
195c9bef5bSMilanka Ringwald            frame.pcm[1][i] = data[2*i+1]
201522543dSMilanka Ringwald    else:
215c9bef5bSMilanka Ringwald        for i in range(len(data)):
225c9bef5bSMilanka Ringwald            frame.pcm[0][i] = data[i]
23c21a9c2fSMilanka Ringwald
24c21a9c2fSMilanka Ringwald
25ad470863SMilanka Ringwalddef sbc_frame_analysis(frame, ch, blk, C):
26c21a9c2fSMilanka Ringwald    global X
27ad470863SMilanka Ringwald
28c21a9c2fSMilanka Ringwald    M = frame.nr_subbands
29c21a9c2fSMilanka Ringwald    L = 10 * M
30c21a9c2fSMilanka Ringwald    M2 = 2*M
31c21a9c2fSMilanka Ringwald    L2 = 2*L
32c21a9c2fSMilanka Ringwald
33c21a9c2fSMilanka Ringwald    Z = np.zeros(L)
34c21a9c2fSMilanka Ringwald    Y = np.zeros(M2)
35c21a9c2fSMilanka Ringwald    W = np.zeros(shape=(M, M2))
36c21a9c2fSMilanka Ringwald    S = np.zeros(M)
37c21a9c2fSMilanka Ringwald
38c21a9c2fSMilanka Ringwald    for i in range(L-1, M-1, -1):
395c9bef5bSMilanka Ringwald        X[ch][i] = X[ch][i-M]
40c21a9c2fSMilanka Ringwald    for i in range(M-1, -1, -1):
415c9bef5bSMilanka Ringwald        X[ch][i] = frame.EX[M-1-i]
42c21a9c2fSMilanka Ringwald    for i in range(L):
435c9bef5bSMilanka Ringwald        Z[i] = X[ch][i] * C[i]
44c21a9c2fSMilanka Ringwald
45c21a9c2fSMilanka Ringwald    for i in range(M2):
46c21a9c2fSMilanka Ringwald        for k in range(5):
47af086c98SMilanka Ringwald            Y[i] += Z[i+k*M2]
48c21a9c2fSMilanka Ringwald
49c21a9c2fSMilanka Ringwald    for i in range(M):
50c21a9c2fSMilanka Ringwald        for k in range(M2):
51af086c98SMilanka Ringwald            W[i][k] = np.cos((i+0.5)*(k-M/2)*np.pi/M)
52c21a9c2fSMilanka Ringwald            S[i] += W[i][k] * Y[k]
53c21a9c2fSMilanka Ringwald
54c21a9c2fSMilanka Ringwald    for sb in range(M):
55c21a9c2fSMilanka Ringwald        frame.sb_sample[blk][ch][sb] = S[sb]
56c21a9c2fSMilanka Ringwald
57*1bd8157eSMilanka Ringwald
58*1bd8157eSMilanka Ringwalddef sbc_frame_analysis(frame, ch, blk, proto_table):
59*1bd8157eSMilanka Ringwald    global total_time_ms, implementation
60*1bd8157eSMilanka Ringwald
61*1bd8157eSMilanka Ringwald    t1 = time_ms()
62*1bd8157eSMilanka Ringwald    if implementation == "SIG":
63*1bd8157eSMilanka Ringwald         sbc_frame_analysis_sig(frame, ch, blk, proto_table)
64*1bd8157eSMilanka Ringwald    else:
65*1bd8157eSMilanka Ringwald        print ("Analysis %s not implemented" % implementation)
66*1bd8157eSMilanka Ringwald        exit(1)
67*1bd8157eSMilanka Ringwald
68*1bd8157eSMilanka Ringwald    t2 = time_ms()
69*1bd8157eSMilanka Ringwald    total_time_ms += t2-t1
70*1bd8157eSMilanka Ringwald
71*1bd8157eSMilanka Ringwald
72ad470863SMilanka Ringwalddef sbc_analysis(frame):
73c21a9c2fSMilanka Ringwald    if frame.nr_subbands == 4:
74ad470863SMilanka Ringwald        C = Proto_4_40
75c21a9c2fSMilanka Ringwald    elif frame.nr_subbands == 8:
76ad470863SMilanka Ringwald        C = Proto_8_80
77c21a9c2fSMilanka Ringwald    else:
78c21a9c2fSMilanka Ringwald        return -1
79c21a9c2fSMilanka Ringwald
80c21a9c2fSMilanka Ringwald    frame.sb_sample = np.ndarray(shape=(frame.nr_blocks, frame.nr_channels, frame.nr_subbands))
81c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
825c9bef5bSMilanka Ringwald        index = 0
83c21a9c2fSMilanka Ringwald        for blk in range(frame.nr_blocks):
84c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
85c9498b12SMilanka Ringwald                frame.EX[sb] = frame.pcm[ch][index]
86c21a9c2fSMilanka Ringwald                index+=1
87ad470863SMilanka Ringwald            sbc_frame_analysis(frame, ch, blk, C)
88ad470863SMilanka Ringwald    return 0
89c21a9c2fSMilanka Ringwald
90ef8a7a12SMilanka Ringwalddef sbc_encode(frame, force_channel_mode):
91ad470863SMilanka Ringwald    err = sbc_analysis(frame)
92ad470863SMilanka Ringwald    if err >= 0:
93ef8a7a12SMilanka Ringwald        err = sbc_quantization(frame, force_channel_mode)
94ad470863SMilanka Ringwald    return err
95c21a9c2fSMilanka Ringwald
96ef8a7a12SMilanka Ringwalddef sbc_quantization(frame, force_channel_mode):
97ef8a7a12SMilanka Ringwald    calculate_channel_mode_and_scale_factors(frame, force_channel_mode)
98c21a9c2fSMilanka Ringwald    frame.bits = sbc_bit_allocation(frame)
99c21a9c2fSMilanka Ringwald
100c21a9c2fSMilanka Ringwald    # Reconstruct the Audio Samples
101c21a9c2fSMilanka Ringwald    frame.levels = np.zeros(shape=(frame.nr_channels, frame.nr_subbands), dtype = np.int32)
102c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
103c21a9c2fSMilanka Ringwald        for sb in range(frame.nr_subbands):
104ad470863SMilanka Ringwald            frame.levels[ch][sb] = (1 << frame.bits[ch][sb]) - 1 #pow(2.0, frame.bits[ch][sb]) - 1
105c21a9c2fSMilanka Ringwald
10657f2bc22SMilanka Ringwald    frame.syncword = 0x9c
107c21a9c2fSMilanka Ringwald    frame.crc_check = calculate_crc(frame)
108c21a9c2fSMilanka Ringwald
10957f2bc22SMilanka Ringwald
110c21a9c2fSMilanka Ringwald    for blk in range(frame.nr_blocks):
111c21a9c2fSMilanka Ringwald        for ch in range(frame.nr_channels):
112c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
113c21a9c2fSMilanka Ringwald                if frame.levels[ch][sb] > 0:
114c21a9c2fSMilanka Ringwald                    SB = frame.sb_sample[blk][ch][sb]
115c21a9c2fSMilanka Ringwald                    L  = frame.levels[ch][sb]
116ad470863SMilanka Ringwald                    SF = frame.scalefactor[ch][sb]
11741a4a18dSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = np.uint16(((SB * L / SF + L) - 1.0)/2.0)
118c21a9c2fSMilanka Ringwald                else:
119c21a9c2fSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = 0
120c21a9c2fSMilanka Ringwald
121c21a9c2fSMilanka Ringwald    return 0
122c21a9c2fSMilanka Ringwald
123ad470863SMilanka Ringwalddef sbc_write_frame(fout, sbc_encoder_frame):
124ad470863SMilanka Ringwald    stream = frame_to_bitstream(sbc_encoder_frame)
125ad470863SMilanka Ringwald    barray = bytearray(stream)
126ad470863SMilanka Ringwald    fout.write(barray)
127ad470863SMilanka Ringwald
128ba114a98SMatthias Ringwaldif __name__ == "__main__":
129ba114a98SMatthias Ringwald    usage = '''
130ef8a7a12SMilanka Ringwald    Usage:      ./sbc_encoder.py input.wav blocks subbands bitpool allocation_method[0-LOUDNESS,1-SNR] force_channel_mode[2-STEREO,3-JOINT_STEREO]
1315665ea35SMilanka Ringwald    Example:    ./sbc_encoder.py fanfare.wav 16 4 31 0
132ba114a98SMatthias Ringwald    '''
133ba114a98SMatthias Ringwald    nr_blocks = 0
134ba114a98SMatthias Ringwald    nr_subbands = 0
135c21a9c2fSMilanka Ringwald
136ad470863SMilanka Ringwald
1375665ea35SMilanka Ringwald    if (len(sys.argv) < 6):
138ba114a98SMatthias Ringwald        print(usage)
139ba114a98SMatthias Ringwald        sys.exit(1)
140ba114a98SMatthias Ringwald    try:
141ba114a98SMatthias Ringwald        infile = sys.argv[1]
142ba114a98SMatthias Ringwald        if not infile.endswith('.wav'):
143ba114a98SMatthias Ringwald            print(usage)
144ba114a98SMatthias Ringwald            sys.exit(1)
145ad470863SMilanka Ringwald        sbcfile = infile.replace('.wav', '-encoded.sbc')
146ad470863SMilanka Ringwald
147ba114a98SMatthias Ringwald        nr_blocks = int(sys.argv[2])
148ba114a98SMatthias Ringwald        nr_subbands = int(sys.argv[3])
149ba114a98SMatthias Ringwald        bitpool = int(sys.argv[4])
1505665ea35SMilanka Ringwald        allocation_method = int(sys.argv[5])
151ef8a7a12SMilanka Ringwald        force_channel_mode = 0
152ef8a7a12SMilanka Ringwald        if len(sys.argv) == 6:
153ef8a7a12SMilanka Ringwald            force_channel_mode = int(sys.argv[6])
154ef8a7a12SMilanka Ringwald            if force_channel_mode != 2 or force_channel_mode != 3:
155ef8a7a12SMilanka Ringwald                print(usage)
156ef8a7a12SMilanka Ringwald                sys.exit(1)
157c21a9c2fSMilanka Ringwald
158ba114a98SMatthias Ringwald        fin = wave.open(infile, 'rb')
159ad470863SMilanka Ringwald        nr_channels = fin.getnchannels()
160ad470863SMilanka Ringwald        sampling_frequency = fin.getframerate()
161ad470863SMilanka Ringwald        nr_audio_frames = fin.getnframes()
162c21a9c2fSMilanka Ringwald
163ad470863SMilanka Ringwald        subband_frame_count = 0
164ba114a98SMatthias Ringwald        audio_frame_count = 0
165ad470863SMilanka Ringwald        nr_samples = nr_blocks * nr_subbands
166ad470863SMilanka Ringwald        fout = open(sbcfile, 'wb')
167ad470863SMilanka Ringwald        while audio_frame_count < nr_audio_frames:
168ad470863SMilanka Ringwald            if subband_frame_count % 200 == 0:
169ad470863SMilanka Ringwald                print("== Frame %d ==" % (subband_frame_count))
170c21a9c2fSMilanka Ringwald
1715665ea35SMilanka Ringwald            sbc_encoder_frame = SBCFrame(nr_blocks, nr_subbands, nr_channels, bitpool, sampling_frequency, allocation_method)
172ad470863SMilanka Ringwald            fetch_samples_for_next_sbc_frame(fin, sbc_encoder_frame)
173c21a9c2fSMilanka Ringwald
174ef8a7a12SMilanka Ringwald            sbc_encode(sbc_encoder_frame, force_channel_mode)
175ad470863SMilanka Ringwald            sbc_write_frame(fout, sbc_encoder_frame)
176c21a9c2fSMilanka Ringwald
177ad470863SMilanka Ringwald            audio_frame_count += nr_samples
178ad470863SMilanka Ringwald            subband_frame_count += 1
179c21a9c2fSMilanka Ringwald
180ad470863SMilanka Ringwald        fin.close()
181ad470863SMilanka Ringwald        fout.close()
182ad470863SMilanka Ringwald        print("DONE, WAV file %s encoded into SBC file %s " % (infile, sbcfile))
183*1bd8157eSMilanka Ringwald        if frame_count > 0:
184*1bd8157eSMilanka Ringwald            print ("Average analysis time per frame: %d ms/frame" % (total_time_ms/frame_count))
185*1bd8157eSMilanka Ringwald        else:
186*1bd8157eSMilanka Ringwald            print ("No frame found")
187c21a9c2fSMilanka Ringwald
188c21a9c2fSMilanka Ringwald
189ba114a98SMatthias Ringwald    except IOError as e:
190ba114a98SMatthias Ringwald        print(usage)
191ba114a98SMatthias Ringwald        sys.exit(1)
192c21a9c2fSMilanka Ringwald
193c21a9c2fSMilanka Ringwald
194c21a9c2fSMilanka Ringwald
195c21a9c2fSMilanka Ringwald
196c21a9c2fSMilanka Ringwald
197