xref: /btstack/test/sbc/sbc_encoder.py (revision ef8a7a12f41cfc681f8f5d2d0890863f62c52a4e)
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)
9c21a9c2fSMilanka Ringwald
10ad470863SMilanka Ringwalddef fetch_samples_for_next_sbc_frame(fin, frame):
11*ef8a7a12SMilanka Ringwald    raw_data = fin.readframes(frame.nr_blocks * frame.nr_subbands)
125c9bef5bSMilanka Ringwald    fmt = "%ih" % (len(raw_data) / 2)
131522543dSMilanka Ringwald    data = struct.unpack(fmt, raw_data)
141522543dSMilanka Ringwald
155c9bef5bSMilanka Ringwald    if frame.nr_channels == 2:
169462b3daSMilanka Ringwald        for i in range(len(data)/2):
175c9bef5bSMilanka Ringwald            frame.pcm[0][i] = data[2*i]
185c9bef5bSMilanka Ringwald            frame.pcm[1][i] = data[2*i+1]
191522543dSMilanka Ringwald    else:
205c9bef5bSMilanka Ringwald        for i in range(len(data)):
215c9bef5bSMilanka Ringwald            frame.pcm[0][i] = data[i]
22c21a9c2fSMilanka Ringwald
23c21a9c2fSMilanka Ringwald
24ad470863SMilanka Ringwalddef sbc_frame_analysis(frame, ch, blk, C):
25c21a9c2fSMilanka Ringwald    global X
26ad470863SMilanka Ringwald
27c21a9c2fSMilanka Ringwald    M = frame.nr_subbands
28c21a9c2fSMilanka Ringwald    L = 10 * M
29c21a9c2fSMilanka Ringwald    M2 = 2*M
30c21a9c2fSMilanka Ringwald    L2 = 2*L
31c21a9c2fSMilanka Ringwald
32c21a9c2fSMilanka Ringwald    Z = np.zeros(L)
33c21a9c2fSMilanka Ringwald    Y = np.zeros(M2)
34c21a9c2fSMilanka Ringwald    W = np.zeros(shape=(M, M2))
35c21a9c2fSMilanka Ringwald    S = np.zeros(M)
36c21a9c2fSMilanka Ringwald
37c21a9c2fSMilanka Ringwald    for i in range(L-1, M-1, -1):
385c9bef5bSMilanka Ringwald        X[ch][i] = X[ch][i-M]
39c21a9c2fSMilanka Ringwald    for i in range(M-1, -1, -1):
405c9bef5bSMilanka Ringwald        X[ch][i] = frame.EX[M-1-i]
41c21a9c2fSMilanka Ringwald    for i in range(L):
425c9bef5bSMilanka Ringwald        Z[i] = X[ch][i] * C[i]
43c21a9c2fSMilanka Ringwald
44c21a9c2fSMilanka Ringwald    for i in range(M2):
45c21a9c2fSMilanka Ringwald        for k in range(5):
46af086c98SMilanka Ringwald            Y[i] += Z[i+k*M2]
47c21a9c2fSMilanka Ringwald
48c21a9c2fSMilanka Ringwald    for i in range(M):
49c21a9c2fSMilanka Ringwald        for k in range(M2):
50af086c98SMilanka Ringwald            W[i][k] = np.cos((i+0.5)*(k-M/2)*np.pi/M)
51c21a9c2fSMilanka Ringwald            S[i] += W[i][k] * Y[k]
52c21a9c2fSMilanka Ringwald
53c21a9c2fSMilanka Ringwald    for sb in range(M):
54c21a9c2fSMilanka Ringwald        frame.sb_sample[blk][ch][sb] = S[sb]
55c21a9c2fSMilanka Ringwald
56ad470863SMilanka Ringwalddef sbc_analysis(frame):
57c21a9c2fSMilanka Ringwald    if frame.nr_subbands == 4:
58ad470863SMilanka Ringwald        C = Proto_4_40
59c21a9c2fSMilanka Ringwald    elif frame.nr_subbands == 8:
60ad470863SMilanka Ringwald        C = Proto_8_80
61c21a9c2fSMilanka Ringwald    else:
62c21a9c2fSMilanka Ringwald        return -1
63c21a9c2fSMilanka Ringwald
64c21a9c2fSMilanka Ringwald    frame.sb_sample = np.ndarray(shape=(frame.nr_blocks, frame.nr_channels, frame.nr_subbands))
65c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
665c9bef5bSMilanka Ringwald        index = 0
67c21a9c2fSMilanka Ringwald        for blk in range(frame.nr_blocks):
68c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
69c9498b12SMilanka Ringwald                frame.EX[sb] = frame.pcm[ch][index]
70c21a9c2fSMilanka Ringwald                index+=1
71ad470863SMilanka Ringwald            sbc_frame_analysis(frame, ch, blk, C)
72ad470863SMilanka Ringwald    return 0
73c21a9c2fSMilanka Ringwald
74*ef8a7a12SMilanka Ringwalddef sbc_encode(frame, force_channel_mode):
75ad470863SMilanka Ringwald    err = sbc_analysis(frame)
76ad470863SMilanka Ringwald    if err >= 0:
77*ef8a7a12SMilanka Ringwald        err = sbc_quantization(frame, force_channel_mode)
78ad470863SMilanka Ringwald    return err
79c21a9c2fSMilanka Ringwald
80*ef8a7a12SMilanka Ringwalddef sbc_quantization(frame, force_channel_mode):
81*ef8a7a12SMilanka Ringwald    calculate_channel_mode_and_scale_factors(frame, force_channel_mode)
82c21a9c2fSMilanka Ringwald    frame.bits = sbc_bit_allocation(frame)
83c21a9c2fSMilanka Ringwald
84c21a9c2fSMilanka Ringwald    # Reconstruct the Audio Samples
85c21a9c2fSMilanka Ringwald    frame.levels = np.zeros(shape=(frame.nr_channels, frame.nr_subbands), dtype = np.int32)
86c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
87c21a9c2fSMilanka Ringwald        for sb in range(frame.nr_subbands):
88ad470863SMilanka Ringwald            frame.levels[ch][sb] = (1 << frame.bits[ch][sb]) - 1 #pow(2.0, frame.bits[ch][sb]) - 1
89c21a9c2fSMilanka Ringwald
9057f2bc22SMilanka Ringwald    frame.syncword = 0x9c
91c21a9c2fSMilanka Ringwald    frame.crc_check = calculate_crc(frame)
92c21a9c2fSMilanka Ringwald
9357f2bc22SMilanka Ringwald
94c21a9c2fSMilanka Ringwald    for blk in range(frame.nr_blocks):
95c21a9c2fSMilanka Ringwald        for ch in range(frame.nr_channels):
96c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
97c21a9c2fSMilanka Ringwald                if frame.levels[ch][sb] > 0:
98c21a9c2fSMilanka Ringwald                    SB = frame.sb_sample[blk][ch][sb]
99c21a9c2fSMilanka Ringwald                    L  = frame.levels[ch][sb]
100ad470863SMilanka Ringwald                    SF = frame.scalefactor[ch][sb]
10141a4a18dSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = np.uint16(((SB * L / SF + L) - 1.0)/2.0)
102c21a9c2fSMilanka Ringwald                else:
103c21a9c2fSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = 0
104c21a9c2fSMilanka Ringwald
105c21a9c2fSMilanka Ringwald    return 0
106c21a9c2fSMilanka Ringwald
107ad470863SMilanka Ringwalddef sbc_write_frame(fout, sbc_encoder_frame):
108ad470863SMilanka Ringwald    stream = frame_to_bitstream(sbc_encoder_frame)
109ad470863SMilanka Ringwald    barray = bytearray(stream)
110ad470863SMilanka Ringwald    fout.write(barray)
111ad470863SMilanka Ringwald
112ba114a98SMatthias Ringwaldif __name__ == "__main__":
113ba114a98SMatthias Ringwald    usage = '''
114*ef8a7a12SMilanka Ringwald    Usage:      ./sbc_encoder.py input.wav blocks subbands bitpool allocation_method[0-LOUDNESS,1-SNR] force_channel_mode[2-STEREO,3-JOINT_STEREO]
1155665ea35SMilanka Ringwald    Example:    ./sbc_encoder.py fanfare.wav 16 4 31 0
116ba114a98SMatthias Ringwald    '''
117ba114a98SMatthias Ringwald    nr_blocks = 0
118ba114a98SMatthias Ringwald    nr_subbands = 0
119c21a9c2fSMilanka Ringwald
120ad470863SMilanka Ringwald
1215665ea35SMilanka Ringwald    if (len(sys.argv) < 6):
122ba114a98SMatthias Ringwald        print(usage)
123ba114a98SMatthias Ringwald        sys.exit(1)
124ba114a98SMatthias Ringwald    try:
125ba114a98SMatthias Ringwald        infile = sys.argv[1]
126ba114a98SMatthias Ringwald        if not infile.endswith('.wav'):
127ba114a98SMatthias Ringwald            print(usage)
128ba114a98SMatthias Ringwald            sys.exit(1)
129ad470863SMilanka Ringwald        sbcfile = infile.replace('.wav', '-encoded.sbc')
130ad470863SMilanka Ringwald
131ba114a98SMatthias Ringwald        nr_blocks = int(sys.argv[2])
132ba114a98SMatthias Ringwald        nr_subbands = int(sys.argv[3])
133ba114a98SMatthias Ringwald        bitpool = int(sys.argv[4])
1345665ea35SMilanka Ringwald        allocation_method = int(sys.argv[5])
135*ef8a7a12SMilanka Ringwald        force_channel_mode = 0
136*ef8a7a12SMilanka Ringwald        if len(sys.argv) == 6:
137*ef8a7a12SMilanka Ringwald            force_channel_mode = int(sys.argv[6])
138*ef8a7a12SMilanka Ringwald            if force_channel_mode != 2 or force_channel_mode != 3:
139*ef8a7a12SMilanka Ringwald                print(usage)
140*ef8a7a12SMilanka Ringwald                sys.exit(1)
141c21a9c2fSMilanka Ringwald
142ba114a98SMatthias Ringwald        fin = wave.open(infile, 'rb')
143ad470863SMilanka Ringwald        nr_channels = fin.getnchannels()
144ad470863SMilanka Ringwald        sampling_frequency = fin.getframerate()
145ad470863SMilanka Ringwald        nr_audio_frames = fin.getnframes()
146c21a9c2fSMilanka Ringwald
147ad470863SMilanka Ringwald        subband_frame_count = 0
148ba114a98SMatthias Ringwald        audio_frame_count = 0
149ad470863SMilanka Ringwald        nr_samples = nr_blocks * nr_subbands
150ad470863SMilanka Ringwald        fout = open(sbcfile, 'wb')
151ad470863SMilanka Ringwald        while audio_frame_count < nr_audio_frames:
152ad470863SMilanka Ringwald            if subband_frame_count % 200 == 0:
153ad470863SMilanka Ringwald                print("== Frame %d ==" % (subband_frame_count))
154c21a9c2fSMilanka Ringwald
1555665ea35SMilanka Ringwald            sbc_encoder_frame = SBCFrame(nr_blocks, nr_subbands, nr_channels, bitpool, sampling_frequency, allocation_method)
156ad470863SMilanka Ringwald            fetch_samples_for_next_sbc_frame(fin, sbc_encoder_frame)
157c21a9c2fSMilanka Ringwald
158*ef8a7a12SMilanka Ringwald            sbc_encode(sbc_encoder_frame, force_channel_mode)
159ad470863SMilanka Ringwald            sbc_write_frame(fout, sbc_encoder_frame)
160c21a9c2fSMilanka Ringwald
161ad470863SMilanka Ringwald            audio_frame_count += nr_samples
162ad470863SMilanka Ringwald            subband_frame_count += 1
163c21a9c2fSMilanka Ringwald
164ad470863SMilanka Ringwald        fin.close()
165ad470863SMilanka Ringwald        fout.close()
166ad470863SMilanka Ringwald        print("DONE, WAV file %s encoded into SBC file %s " % (infile, sbcfile))
167c21a9c2fSMilanka Ringwald
168c21a9c2fSMilanka Ringwald
169ba114a98SMatthias Ringwald    except IOError as e:
170ba114a98SMatthias Ringwald        print(usage)
171ba114a98SMatthias Ringwald        sys.exit(1)
172c21a9c2fSMilanka Ringwald
173c21a9c2fSMilanka Ringwald
174c21a9c2fSMilanka Ringwald
175c21a9c2fSMilanka Ringwald
176c21a9c2fSMilanka Ringwald
177