xref: /btstack/test/sbc/sbc_encoder.py (revision 6ccd8248590f666db07dd7add13fecb4f5664fb5)
1*6ccd8248SMilanka Ringwald#!/usr/bin/env python3
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)
91bd8157eSMilanka Ringwaldimplementation = "SIG"
10a7e6f4ddSMilanka Ringwaldmsbc_enabled = 0
11a7e6f4ddSMilanka Ringwaldtotal_time_ms = 0
12c21a9c2fSMilanka Ringwald
13ad470863SMilanka Ringwalddef fetch_samples_for_next_sbc_frame(fin, frame):
14ef8a7a12SMilanka Ringwald    raw_data = fin.readframes(frame.nr_blocks * frame.nr_subbands)
155c9bef5bSMilanka Ringwald    fmt = "%ih" % (len(raw_data) / 2)
161522543dSMilanka Ringwald    data = struct.unpack(fmt, raw_data)
171522543dSMilanka Ringwald
185c9bef5bSMilanka Ringwald    if frame.nr_channels == 2:
199462b3daSMilanka Ringwald        for i in range(len(data)/2):
205c9bef5bSMilanka Ringwald            frame.pcm[0][i] = data[2*i]
215c9bef5bSMilanka Ringwald            frame.pcm[1][i] = data[2*i+1]
221522543dSMilanka Ringwald    else:
235c9bef5bSMilanka Ringwald        for i in range(len(data)):
245c9bef5bSMilanka Ringwald            frame.pcm[0][i] = data[i]
25c21a9c2fSMilanka Ringwald
26c21a9c2fSMilanka Ringwald
27a7e6f4ddSMilanka Ringwalddef sbc_frame_analysis_sig(frame, ch, blk, C):
28c21a9c2fSMilanka Ringwald    global X
29ad470863SMilanka Ringwald
30c21a9c2fSMilanka Ringwald    M = frame.nr_subbands
31c21a9c2fSMilanka Ringwald    L = 10 * M
32c21a9c2fSMilanka Ringwald    M2 = 2*M
33c21a9c2fSMilanka Ringwald    L2 = 2*L
34c21a9c2fSMilanka Ringwald
35c21a9c2fSMilanka Ringwald    Z = np.zeros(L)
36c21a9c2fSMilanka Ringwald    Y = np.zeros(M2)
37c21a9c2fSMilanka Ringwald    W = np.zeros(shape=(M, M2))
38c21a9c2fSMilanka Ringwald    S = np.zeros(M)
39c21a9c2fSMilanka Ringwald
40c21a9c2fSMilanka Ringwald    for i in range(L-1, M-1, -1):
415c9bef5bSMilanka Ringwald        X[ch][i] = X[ch][i-M]
42c21a9c2fSMilanka Ringwald    for i in range(M-1, -1, -1):
435c9bef5bSMilanka Ringwald        X[ch][i] = frame.EX[M-1-i]
44c21a9c2fSMilanka Ringwald    for i in range(L):
455c9bef5bSMilanka Ringwald        Z[i] = X[ch][i] * C[i]
46c21a9c2fSMilanka Ringwald
47c21a9c2fSMilanka Ringwald    for i in range(M2):
48c21a9c2fSMilanka Ringwald        for k in range(5):
49af086c98SMilanka Ringwald            Y[i] += Z[i+k*M2]
50c21a9c2fSMilanka Ringwald
51c21a9c2fSMilanka Ringwald    for i in range(M):
52c21a9c2fSMilanka Ringwald        for k in range(M2):
53af086c98SMilanka Ringwald            W[i][k] = np.cos((i+0.5)*(k-M/2)*np.pi/M)
54c21a9c2fSMilanka Ringwald            S[i] += W[i][k] * Y[k]
55c21a9c2fSMilanka Ringwald
56c21a9c2fSMilanka Ringwald    for sb in range(M):
57c21a9c2fSMilanka Ringwald        frame.sb_sample[blk][ch][sb] = S[sb]
58c21a9c2fSMilanka Ringwald
591bd8157eSMilanka Ringwald
601bd8157eSMilanka Ringwalddef sbc_frame_analysis(frame, ch, blk, proto_table):
611bd8157eSMilanka Ringwald    global total_time_ms, implementation
621bd8157eSMilanka Ringwald
631bd8157eSMilanka Ringwald    t1 = time_ms()
641bd8157eSMilanka Ringwald    if implementation == "SIG":
651bd8157eSMilanka Ringwald         sbc_frame_analysis_sig(frame, ch, blk, proto_table)
661bd8157eSMilanka Ringwald    else:
671bd8157eSMilanka Ringwald        print ("Analysis %s not implemented" % implementation)
681bd8157eSMilanka Ringwald        exit(1)
691bd8157eSMilanka Ringwald
701bd8157eSMilanka Ringwald    t2 = time_ms()
711bd8157eSMilanka Ringwald    total_time_ms += t2-t1
721bd8157eSMilanka Ringwald
731bd8157eSMilanka Ringwald
74ad470863SMilanka Ringwalddef sbc_analysis(frame):
75c21a9c2fSMilanka Ringwald    if frame.nr_subbands == 4:
76ad470863SMilanka Ringwald        C = Proto_4_40
77c21a9c2fSMilanka Ringwald    elif frame.nr_subbands == 8:
78ad470863SMilanka Ringwald        C = Proto_8_80
79c21a9c2fSMilanka Ringwald    else:
80c21a9c2fSMilanka Ringwald        return -1
81c21a9c2fSMilanka Ringwald
82c21a9c2fSMilanka Ringwald    frame.sb_sample = np.ndarray(shape=(frame.nr_blocks, frame.nr_channels, frame.nr_subbands))
83c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
845c9bef5bSMilanka Ringwald        index = 0
85c21a9c2fSMilanka Ringwald        for blk in range(frame.nr_blocks):
86c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
87c9498b12SMilanka Ringwald                frame.EX[sb] = frame.pcm[ch][index]
88c21a9c2fSMilanka Ringwald                index+=1
89ad470863SMilanka Ringwald            sbc_frame_analysis(frame, ch, blk, C)
90ad470863SMilanka Ringwald    return 0
91c21a9c2fSMilanka Ringwald
92ef8a7a12SMilanka Ringwalddef sbc_encode(frame, force_channel_mode):
93ad470863SMilanka Ringwald    err = sbc_analysis(frame)
94ad470863SMilanka Ringwald    if err >= 0:
95ef8a7a12SMilanka Ringwald        err = sbc_quantization(frame, force_channel_mode)
96ad470863SMilanka Ringwald    return err
97c21a9c2fSMilanka Ringwald
98ef8a7a12SMilanka Ringwalddef sbc_quantization(frame, force_channel_mode):
99a7e6f4ddSMilanka Ringwald    global msbc_enabled
100ef8a7a12SMilanka Ringwald    calculate_channel_mode_and_scale_factors(frame, force_channel_mode)
101c21a9c2fSMilanka Ringwald    frame.bits = sbc_bit_allocation(frame)
102c21a9c2fSMilanka Ringwald
103c21a9c2fSMilanka Ringwald    # Reconstruct the Audio Samples
104c21a9c2fSMilanka Ringwald    frame.levels = np.zeros(shape=(frame.nr_channels, frame.nr_subbands), dtype = np.int32)
105c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
106c21a9c2fSMilanka Ringwald        for sb in range(frame.nr_subbands):
107ad470863SMilanka Ringwald            frame.levels[ch][sb] = (1 << frame.bits[ch][sb]) - 1 #pow(2.0, frame.bits[ch][sb]) - 1
108c21a9c2fSMilanka Ringwald
109a7e6f4ddSMilanka Ringwald    if msbc_enabled:
110a7e6f4ddSMilanka Ringwald        frame.syncword = 0xad
111a7e6f4ddSMilanka Ringwald    else:
11257f2bc22SMilanka Ringwald        frame.syncword = 0x9c
113a7e6f4ddSMilanka Ringwald
114c21a9c2fSMilanka Ringwald    frame.crc_check = calculate_crc(frame)
115c21a9c2fSMilanka Ringwald
11657f2bc22SMilanka Ringwald
117c21a9c2fSMilanka Ringwald    for blk in range(frame.nr_blocks):
118c21a9c2fSMilanka Ringwald        for ch in range(frame.nr_channels):
119c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
120c21a9c2fSMilanka Ringwald                if frame.levels[ch][sb] > 0:
121c21a9c2fSMilanka Ringwald                    SB = frame.sb_sample[blk][ch][sb]
122c21a9c2fSMilanka Ringwald                    L  = frame.levels[ch][sb]
123ad470863SMilanka Ringwald                    SF = frame.scalefactor[ch][sb]
12441a4a18dSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = np.uint16(((SB * L / SF + L) - 1.0)/2.0)
125c21a9c2fSMilanka Ringwald                else:
126c21a9c2fSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = 0
127c21a9c2fSMilanka Ringwald
128c21a9c2fSMilanka Ringwald    return 0
129c21a9c2fSMilanka Ringwald
130ad470863SMilanka Ringwalddef sbc_write_frame(fout, sbc_encoder_frame):
131ad470863SMilanka Ringwald    stream = frame_to_bitstream(sbc_encoder_frame)
132ad470863SMilanka Ringwald    barray = bytearray(stream)
133ad470863SMilanka Ringwald    fout.write(barray)
134ad470863SMilanka Ringwald
135ba114a98SMatthias Ringwaldif __name__ == "__main__":
136ba114a98SMatthias Ringwald    usage = '''
137a7e6f4ddSMilanka Ringwald    Usage:      ./sbc_encoder.py input.wav blocks subbands bitpool allocation_method[0-LOUDNESS,1-SNR] force_channel_mode[2-STEREO,3-JOINT_STEREO] [0-sbc|1-msbc]
138a7e6f4ddSMilanka Ringwald    Example:    ./sbc_encoder.py fanfare.wav 16 4 31 0 0
139ba114a98SMatthias Ringwald    '''
140ba114a98SMatthias Ringwald    nr_blocks = 0
141ba114a98SMatthias Ringwald    nr_subbands = 0
142c21a9c2fSMilanka Ringwald
143a7e6f4ddSMilanka Ringwald    if (len(sys.argv) < 7):
144ba114a98SMatthias Ringwald        print(usage)
145ba114a98SMatthias Ringwald        sys.exit(1)
146ba114a98SMatthias Ringwald    try:
147ba114a98SMatthias Ringwald        infile = sys.argv[1]
148ba114a98SMatthias Ringwald        if not infile.endswith('.wav'):
149ba114a98SMatthias Ringwald            print(usage)
150ba114a98SMatthias Ringwald            sys.exit(1)
151a7e6f4ddSMilanka Ringwald
152a7e6f4ddSMilanka Ringwald        msbc_enabled = int(sys.argv[7])
153164f5121SMilanka Ringwald        print("msbc_enabled %d"%msbc_enabled)
154a7e6f4ddSMilanka Ringwald        if msbc_enabled:
155a7e6f4ddSMilanka Ringwald            sbcfile = infile.replace('.wav', '-encoded.msbc')
156a7e6f4ddSMilanka Ringwald        else:
157ad470863SMilanka Ringwald            sbcfile = infile.replace('.wav', '-encoded.sbc')
158ad470863SMilanka Ringwald
159ba114a98SMatthias Ringwald        nr_blocks = int(sys.argv[2])
160ba114a98SMatthias Ringwald        nr_subbands = int(sys.argv[3])
161ba114a98SMatthias Ringwald        bitpool = int(sys.argv[4])
1625665ea35SMilanka Ringwald        allocation_method = int(sys.argv[5])
163a7e6f4ddSMilanka Ringwald
164ef8a7a12SMilanka Ringwald        force_channel_mode = int(sys.argv[6])
165164f5121SMilanka Ringwald        print("force_channel_mode %d"%force_channel_mode)
166164f5121SMilanka Ringwald
167c21a9c2fSMilanka Ringwald
168ba114a98SMatthias Ringwald        fin = wave.open(infile, 'rb')
169ad470863SMilanka Ringwald        nr_channels = fin.getnchannels()
170a7e6f4ddSMilanka Ringwald        if msbc_enabled:
171a7e6f4ddSMilanka Ringwald            sampling_frequency = 16000
172a7e6f4ddSMilanka Ringwald            nr_channels = 1
173a7e6f4ddSMilanka Ringwald            bitpool = 26
174a7e6f4ddSMilanka Ringwald            nr_subbands = 8
175a7e6f4ddSMilanka Ringwald            allocation_method = 0
176a7e6f4ddSMilanka Ringwald            force_channel_mode = 0
177a7e6f4ddSMilanka Ringwald        else:
178ad470863SMilanka Ringwald            sampling_frequency = fin.getframerate()
179a7e6f4ddSMilanka Ringwald            nr_channels = fin.getnchannels()
180ad470863SMilanka Ringwald        nr_audio_frames = fin.getnframes()
181c21a9c2fSMilanka Ringwald
182ad470863SMilanka Ringwald        subband_frame_count = 0
183ba114a98SMatthias Ringwald        audio_frame_count = 0
184ad470863SMilanka Ringwald        nr_samples = nr_blocks * nr_subbands
185ad470863SMilanka Ringwald        fout = open(sbcfile, 'wb')
186ad470863SMilanka Ringwald        while audio_frame_count < nr_audio_frames:
187ad470863SMilanka Ringwald            if subband_frame_count % 200 == 0:
188ad470863SMilanka Ringwald                print("== Frame %d == "  % (subband_frame_count))
189c21a9c2fSMilanka Ringwald
190164f5121SMilanka Ringwald            sbc_encoder_frame = SBCFrame(nr_blocks, nr_subbands, nr_channels, bitpool, sampling_frequency, allocation_method, force_channel_mode)
191164f5121SMilanka Ringwald
192a7e6f4ddSMilanka Ringwald            if subband_frame_count == 0:
193*6ccd8248SMilanka Ringwald                print (sbc_encoder_frame)
194ad470863SMilanka Ringwald            fetch_samples_for_next_sbc_frame(fin, sbc_encoder_frame)
195c21a9c2fSMilanka Ringwald
196ef8a7a12SMilanka Ringwald            sbc_encode(sbc_encoder_frame, force_channel_mode)
197ad470863SMilanka Ringwald            sbc_write_frame(fout, sbc_encoder_frame)
198c21a9c2fSMilanka Ringwald
199ad470863SMilanka Ringwald            audio_frame_count += nr_samples
200ad470863SMilanka Ringwald            subband_frame_count += 1
201c21a9c2fSMilanka Ringwald
202ad470863SMilanka Ringwald        fin.close()
203ad470863SMilanka Ringwald        fout.close()
204ad470863SMilanka Ringwald        print("DONE, WAV file %s encoded into SBC file %s " % (infile, sbcfile))
205a7e6f4ddSMilanka Ringwald        if subband_frame_count > 0:
206a7e6f4ddSMilanka Ringwald            print ("Average analysis time per frame: %d ms/frame" % (total_time_ms/subband_frame_count))
2071bd8157eSMilanka Ringwald        else:
2081bd8157eSMilanka Ringwald            print ("No frame found")
209c21a9c2fSMilanka Ringwald
210c21a9c2fSMilanka Ringwald
211ba114a98SMatthias Ringwald    except IOError as e:
212ba114a98SMatthias Ringwald        print(usage)
213ba114a98SMatthias Ringwald        sys.exit(1)
214c21a9c2fSMilanka Ringwald
215c21a9c2fSMilanka Ringwald
216c21a9c2fSMilanka Ringwald
217c21a9c2fSMilanka Ringwald
218c21a9c2fSMilanka Ringwald
219