xref: /btstack/test/sbc/sbc_encoder.py (revision 1522543de84c04e141f181431af0e80f00b6b718)
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
8ad470863SMilanka RingwaldX = np.zeros(80, dtype = np.int16)
9c21a9c2fSMilanka Ringwald
10ad470863SMilanka Ringwald
11ad470863SMilanka Ringwalddef fetch_samples_for_next_sbc_frame(fin, frame):
12*1522543dSMilanka Ringwald    nr_samples = frame.nr_blocks * frame.nr_subbands * frame.nr_channels
13*1522543dSMilanka Ringwald    raw_data = fin.readframes(nr_samples) # Returns byte data
14ad470863SMilanka Ringwald    len_raw_data =  len(raw_data) / 2
15c21a9c2fSMilanka Ringwald
16ad470863SMilanka Ringwald    fmt = "%ih" % len_raw_data # read signed 2 byte shorts
17ad470863SMilanka Ringwald
18*1522543dSMilanka Ringwald    data = struct.unpack(fmt, raw_data)
19*1522543dSMilanka Ringwald    len_data = len(data)
20*1522543dSMilanka Ringwald
21*1522543dSMilanka Ringwald    for i in range(frame.nr_blocks * frame.nr_subbands):
22*1522543dSMilanka Ringwald        for ch in range(frame.nr_channels):
23*1522543dSMilanka Ringwald            index = i*2 + ch
24*1522543dSMilanka Ringwald            if index < len_data:
25*1522543dSMilanka Ringwald                frame.pcm[ch][i] = data[i*2 + ch]
26*1522543dSMilanka Ringwald            else:
27*1522543dSMilanka Ringwald                frame.pcm[ch][i] = 0
28c21a9c2fSMilanka Ringwald
29c21a9c2fSMilanka Ringwald
30ad470863SMilanka Ringwalddef sbc_frame_analysis(frame, ch, blk, C):
31c21a9c2fSMilanka Ringwald    global X
32ad470863SMilanka Ringwald
33c21a9c2fSMilanka Ringwald    M = frame.nr_subbands
34c21a9c2fSMilanka Ringwald    L = 10 * M
35c21a9c2fSMilanka Ringwald    M2 = 2*M
36c21a9c2fSMilanka Ringwald    L2 = 2*L
37c21a9c2fSMilanka Ringwald
38c21a9c2fSMilanka Ringwald    Z = np.zeros(L)
39c21a9c2fSMilanka Ringwald    Y = np.zeros(M2)
40c21a9c2fSMilanka Ringwald    W = np.zeros(shape=(M, M2))
41c21a9c2fSMilanka Ringwald    S = np.zeros(M)
42c21a9c2fSMilanka Ringwald
43c21a9c2fSMilanka Ringwald    for i in range(L-1, M-1, -1):
44c21a9c2fSMilanka Ringwald        X[i] = X[i-M]
45c21a9c2fSMilanka Ringwald    for i in range(M-1, -1, -1):
46c21a9c2fSMilanka Ringwald        X[i] = frame.EX[M-1-i]
47c21a9c2fSMilanka Ringwald
48c21a9c2fSMilanka Ringwald    for i in range(L):
49c21a9c2fSMilanka Ringwald        Z[i] = X[i] * C[i]
50c21a9c2fSMilanka Ringwald
51c21a9c2fSMilanka Ringwald    for i in range(M2):
52c21a9c2fSMilanka Ringwald        for k in range(5):
53af086c98SMilanka Ringwald            Y[i] += Z[i+k*M2]
54c21a9c2fSMilanka Ringwald
55c21a9c2fSMilanka Ringwald    for i in range(M):
56c21a9c2fSMilanka Ringwald        for k in range(M2):
57af086c98SMilanka Ringwald            W[i][k] = np.cos((i+0.5)*(k-M/2)*np.pi/M)
58c21a9c2fSMilanka Ringwald            S[i] += W[i][k] * Y[k]
59c21a9c2fSMilanka Ringwald
60c21a9c2fSMilanka Ringwald    for sb in range(M):
61c21a9c2fSMilanka Ringwald        frame.sb_sample[blk][ch][sb] = S[sb]
62c21a9c2fSMilanka Ringwald
63ad470863SMilanka Ringwalddef sbc_analysis(frame):
64c21a9c2fSMilanka Ringwald    if frame.nr_subbands == 4:
65ad470863SMilanka Ringwald        C = Proto_4_40
66c21a9c2fSMilanka Ringwald    elif frame.nr_subbands == 8:
67ad470863SMilanka Ringwald        C = Proto_8_80
68c21a9c2fSMilanka Ringwald    else:
69c21a9c2fSMilanka Ringwald        return -1
70c21a9c2fSMilanka Ringwald
71c21a9c2fSMilanka Ringwald    frame.sb_sample = np.ndarray(shape=(frame.nr_blocks, frame.nr_channels, frame.nr_subbands))
72c21a9c2fSMilanka Ringwald    index = 0
73c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
74c21a9c2fSMilanka Ringwald        for blk in range(frame.nr_blocks):
75c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
76*1522543dSMilanka Ringwald                frame.EX[sb] = np.int16(frame.pcm[ch][index])
77c21a9c2fSMilanka Ringwald                index+=1
78ad470863SMilanka Ringwald            sbc_frame_analysis(frame, ch, blk, C)
79ad470863SMilanka Ringwald    return 0
80c21a9c2fSMilanka Ringwald
81ad470863SMilanka Ringwalddef sbc_encode(frame):
82ad470863SMilanka Ringwald    err = sbc_analysis(frame)
83ad470863SMilanka Ringwald    if err >= 0:
84ad470863SMilanka Ringwald        err = sbc_quantization(frame)
85ad470863SMilanka Ringwald    return err
86c21a9c2fSMilanka Ringwald
87ad470863SMilanka Ringwalddef calculate_joint_stereo_signal(frame):
88ad470863SMilanka Ringwald    sb_sample = np.zeros(shape = (frame.nr_blocks,frame.nr_channels,frame.nr_subbands), dtype = np.uint32)
89ad470863SMilanka Ringwald    scale_factor = np.zeros(shape=(frame.nr_channels, frame.nr_subbands), dtype = np.int32)
90ad470863SMilanka Ringwald    scalefactor = np.zeros(shape=(frame.nr_channels, frame.nr_subbands), dtype = np.int32)
91ad470863SMilanka Ringwald
92ad470863SMilanka Ringwald    for sb in range(frame.nr_subbands-1):
93ad470863SMilanka Ringwald        for blk in range(frame.nr_blocks):
94ad470863SMilanka Ringwald             sb_sample[blk][0][sb] = (frame.sb_sample_f[blk][0][sb] +  frame.sb_sample_f[blk][1][sb]) >> 1
95ad470863SMilanka Ringwald             sb_sample[blk][1][sb] = (frame.sb_sample_f[blk][0][sb] -  frame.sb_sample_f[blk][1][sb]) >> 1
96ad470863SMilanka Ringwald
97ad470863SMilanka Ringwald    for ch in range(frame.nr_channels):
98ad470863SMilanka Ringwald        for sb in range(frame.nr_subbands-1):
99ad470863SMilanka Ringwald            frame.scale_factor[ch][sb] = 0
100ad470863SMilanka Ringwald            frame.scalefactor[ch][sb] = 2
101ad470863SMilanka Ringwald            for blk in range(frame.nr_blocks):
102ad470863SMilanka Ringwald                while frame.scalefactor[ch][sb] < abs(frame.sb_sample[blk][ch][sb]):
103ad470863SMilanka Ringwald                    frame.scale_factor[ch][sb]+=1
104ad470863SMilanka Ringwald                    frame.scalefactor[ch][sb] *= 2
105ad470863SMilanka Ringwald
106ad470863SMilanka Ringwald    for sb in range(frame.nr_subbands-1):
107ad470863SMilanka Ringwald        if (frame.scalefactor[0][sb] + frame.scalefactor[1][sb]) > (scalefactor[0][sb] + scalefactor[1][sb]):
108ad470863SMilanka Ringwald            frame.join[sb] = 1
109ad470863SMilanka Ringwald            frame.scale_factor[0][sb] = scale_factor[0][sb]
110ad470863SMilanka Ringwald            frame.scale_factor[1][sb] = scale_factor[1][sb]
111ad470863SMilanka Ringwald            frame.scalefactor[0][sb]  = scalefactor[0][sb]
112ad470863SMilanka Ringwald            frame.scalefactor[1][sb]  = scalefactor[1][sb]
113ad470863SMilanka Ringwald            for blk in range(frame.nr_blocks):
114ad470863SMilanka Ringwald                frame.sb_sample[blk][0][sb] = sb_sample[blk][0][sb]
115ad470863SMilanka Ringwald                frame.sb_sample[blk][1][sb] = sb_sample[blk][1][sb]
116c21a9c2fSMilanka Ringwald
117c21a9c2fSMilanka Ringwald
118c21a9c2fSMilanka Ringwalddef sbc_quantization(frame):
119*1522543dSMilanka Ringwald    calculate_scalefactors_and_channel_mode(frame)
120c21a9c2fSMilanka Ringwald    frame.bits = sbc_bit_allocation(frame)
121c21a9c2fSMilanka Ringwald
122c21a9c2fSMilanka Ringwald    # Reconstruct the Audio Samples
123c21a9c2fSMilanka Ringwald    frame.levels = np.zeros(shape=(frame.nr_channels, frame.nr_subbands), dtype = np.int32)
124c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
125c21a9c2fSMilanka Ringwald        for sb in range(frame.nr_subbands):
126ad470863SMilanka Ringwald            frame.levels[ch][sb] = (1 << frame.bits[ch][sb]) - 1 #pow(2.0, frame.bits[ch][sb]) - 1
127c21a9c2fSMilanka Ringwald
128c21a9c2fSMilanka Ringwald    frame.syncword = 156
129c21a9c2fSMilanka Ringwald    frame.crc_check = calculate_crc(frame)
130c21a9c2fSMilanka Ringwald
131ad470863SMilanka Ringwald    frame.join = np.zeros(frame.nr_subbands, dtype = np.uint8)
132*1522543dSMilanka Ringwald
133*1522543dSMilanka Ringwald
134ad470863SMilanka Ringwald    if frame.channel_mode == JOINT_STEREO:
135ad470863SMilanka Ringwald        calculate_joint_stereo_signal(frame)
136ad470863SMilanka Ringwald
137c21a9c2fSMilanka Ringwald    for blk in range(frame.nr_blocks):
138c21a9c2fSMilanka Ringwald        for ch in range(frame.nr_channels):
139c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
140c21a9c2fSMilanka Ringwald                if frame.levels[ch][sb] > 0:
141c21a9c2fSMilanka Ringwald                    SB = frame.sb_sample[blk][ch][sb]
142c21a9c2fSMilanka Ringwald                    L  = frame.levels[ch][sb]
143ad470863SMilanka Ringwald                    SF = frame.scalefactor[ch][sb]
14441a4a18dSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = np.uint16(((SB * L / SF    + L) - 1.0)/2.0)
145c21a9c2fSMilanka Ringwald                else:
146c21a9c2fSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = 0
147c21a9c2fSMilanka Ringwald
148c21a9c2fSMilanka Ringwald    return 0
149c21a9c2fSMilanka Ringwald
150ad470863SMilanka Ringwalddef sbc_write_frame(fout, sbc_encoder_frame):
151ad470863SMilanka Ringwald    stream = frame_to_bitstream(sbc_encoder_frame)
152ad470863SMilanka Ringwald    barray = bytearray(stream)
153ad470863SMilanka Ringwald    fout.write(barray)
154ad470863SMilanka Ringwald
155ba114a98SMatthias Ringwaldif __name__ == "__main__":
156ba114a98SMatthias Ringwald    usage = '''
1575665ea35SMilanka Ringwald    Usage:      ./sbc_encoder.py input.wav blocks subbands bitpool allocation_method[0-LOUDNESS,1-SNR]
1585665ea35SMilanka Ringwald    Example:    ./sbc_encoder.py fanfare.wav 16 4 31 0
159ba114a98SMatthias Ringwald    '''
160ba114a98SMatthias Ringwald    nr_blocks = 0
161ba114a98SMatthias Ringwald    nr_subbands = 0
162c21a9c2fSMilanka Ringwald
163ad470863SMilanka Ringwald
1645665ea35SMilanka Ringwald    if (len(sys.argv) < 6):
165ba114a98SMatthias Ringwald        print(usage)
166ba114a98SMatthias Ringwald        sys.exit(1)
167ba114a98SMatthias Ringwald    try:
168ba114a98SMatthias Ringwald        infile = sys.argv[1]
169ba114a98SMatthias Ringwald        if not infile.endswith('.wav'):
170ba114a98SMatthias Ringwald            print(usage)
171ba114a98SMatthias Ringwald            sys.exit(1)
172ad470863SMilanka Ringwald        sbcfile = infile.replace('.wav', '-encoded.sbc')
173ad470863SMilanka Ringwald
174ba114a98SMatthias Ringwald        nr_blocks = int(sys.argv[2])
175ba114a98SMatthias Ringwald        nr_subbands = int(sys.argv[3])
176ba114a98SMatthias Ringwald        bitpool = int(sys.argv[4])
1775665ea35SMilanka Ringwald        allocation_method = int(sys.argv[5])
178c21a9c2fSMilanka Ringwald
179ba114a98SMatthias Ringwald        fin = wave.open(infile, 'rb')
180ad470863SMilanka Ringwald        nr_channels = fin.getnchannels()
181ad470863SMilanka Ringwald        sampling_frequency = fin.getframerate()
182ad470863SMilanka Ringwald        nr_audio_frames = fin.getnframes()
183c21a9c2fSMilanka Ringwald
184ad470863SMilanka Ringwald        subband_frame_count = 0
185ba114a98SMatthias Ringwald        audio_frame_count = 0
186ad470863SMilanka Ringwald        nr_samples = nr_blocks * nr_subbands
187ad470863SMilanka Ringwald        fout = open(sbcfile, 'wb')
188ad470863SMilanka Ringwald        while audio_frame_count < nr_audio_frames:
189ad470863SMilanka Ringwald            if subband_frame_count % 200 == 0:
190ad470863SMilanka Ringwald                print("== Frame %d ==" % (subband_frame_count))
191c21a9c2fSMilanka Ringwald
1925665ea35SMilanka Ringwald            sbc_encoder_frame = SBCFrame(nr_blocks, nr_subbands, nr_channels, bitpool, sampling_frequency, allocation_method)
193ad470863SMilanka Ringwald            fetch_samples_for_next_sbc_frame(fin, sbc_encoder_frame)
194c21a9c2fSMilanka Ringwald
195ad470863SMilanka Ringwald            sbc_encode(sbc_encoder_frame)
196ad470863SMilanka Ringwald            sbc_write_frame(fout, sbc_encoder_frame)
197c21a9c2fSMilanka Ringwald
198ad470863SMilanka Ringwald            audio_frame_count += nr_samples
199ad470863SMilanka Ringwald            subband_frame_count += 1
200c21a9c2fSMilanka Ringwald
201ad470863SMilanka Ringwald        fin.close()
202ad470863SMilanka Ringwald        fout.close()
203ad470863SMilanka Ringwald        print("DONE, WAV file %s encoded into SBC file %s " % (infile, sbcfile))
204c21a9c2fSMilanka Ringwald
205c21a9c2fSMilanka Ringwald
206ba114a98SMatthias Ringwald    except IOError as e:
207ba114a98SMatthias Ringwald        print(usage)
208ba114a98SMatthias Ringwald        sys.exit(1)
209c21a9c2fSMilanka Ringwald
210c21a9c2fSMilanka Ringwald
211c21a9c2fSMilanka Ringwald
212c21a9c2fSMilanka Ringwald
213c21a9c2fSMilanka Ringwald
214