xref: /btstack/test/sbc/sbc_encoder.py (revision 41a4a18d9c7d4387c9d8f9bad69f2031a5a1cd38)
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
8*41a4a18dSMilanka RingwaldX = np.zeros(80)
9c21a9c2fSMilanka Ringwald
10c21a9c2fSMilanka Ringwalddef fetch_samples_for_next_sbc_frame(fin, nr_audio_frames, frame):
11c21a9c2fSMilanka Ringwald    raw_data = fin.readframes(nr_audio_frames) # Returns byte data
12c21a9c2fSMilanka Ringwald
13c21a9c2fSMilanka Ringwald    total_samples = nr_audio_frames * frame.nr_channels
14c21a9c2fSMilanka Ringwald    fmt = "%ih" % total_samples # read signed 2 byte shorts
15c21a9c2fSMilanka Ringwald
16c21a9c2fSMilanka Ringwald    frame.pcm =  np.array(struct.unpack(fmt, raw_data))
17c21a9c2fSMilanka Ringwald    del raw_data
18c21a9c2fSMilanka Ringwald
19c21a9c2fSMilanka Ringwald
20c21a9c2fSMilanka Ringwalddef sbc_analyse(frame, ch, blk, C, debug):
21c21a9c2fSMilanka Ringwald    global X
22c21a9c2fSMilanka Ringwald    M = frame.nr_subbands
23c21a9c2fSMilanka Ringwald    L = 10 * M
24c21a9c2fSMilanka Ringwald    M2 = 2*M
25c21a9c2fSMilanka Ringwald    L2 = 2*L
26c21a9c2fSMilanka Ringwald
27c21a9c2fSMilanka Ringwald    Z = np.zeros(L)
28c21a9c2fSMilanka Ringwald    Y = np.zeros(M2)
29c21a9c2fSMilanka Ringwald    W = np.zeros(shape=(M, M2))
30c21a9c2fSMilanka Ringwald    S = np.zeros(M)
31c21a9c2fSMilanka Ringwald
32c21a9c2fSMilanka Ringwald    for i in range(L-1, M-1, -1):
33c21a9c2fSMilanka Ringwald        X[i] = X[i-M]
34c21a9c2fSMilanka Ringwald    for i in range(M-1, -1, -1):
35c21a9c2fSMilanka Ringwald        X[i] = frame.EX[M-1-i]
36c21a9c2fSMilanka Ringwald
37c21a9c2fSMilanka Ringwald    for i in range(L):
38c21a9c2fSMilanka Ringwald        Z[i] = X[i] * C[i]
39c21a9c2fSMilanka Ringwald
40c21a9c2fSMilanka Ringwald    for i in range(M2):
41c21a9c2fSMilanka Ringwald        for k in range(5):
42c21a9c2fSMilanka Ringwald            Y[i] += Z[i+k*8]
43c21a9c2fSMilanka Ringwald
44c21a9c2fSMilanka Ringwald    for i in range(M):
45c21a9c2fSMilanka Ringwald        for k in range(M2):
46c21a9c2fSMilanka Ringwald            W[i][k] = np.cos((i+0.5)*(k-2)*np.pi/M)
47c21a9c2fSMilanka Ringwald            S[i] += W[i][k] * Y[k]
48c21a9c2fSMilanka Ringwald
49c21a9c2fSMilanka Ringwald    if debug:
50c21a9c2fSMilanka Ringwald        #print "EX:", frame.EX
51c21a9c2fSMilanka Ringwald        print "X:", X
52c21a9c2fSMilanka Ringwald        print "Z:"
53c21a9c2fSMilanka Ringwald        print "Y:", Y
54c21a9c2fSMilanka Ringwald        print "W:", W
55c21a9c2fSMilanka Ringwald        print "S:", S
56c21a9c2fSMilanka Ringwald
57c21a9c2fSMilanka Ringwald    for sb in range(M):
58c21a9c2fSMilanka Ringwald        frame.sb_sample[blk][ch][sb] = S[sb]
59c21a9c2fSMilanka Ringwald
60c21a9c2fSMilanka Ringwald
61c21a9c2fSMilanka Ringwalddef sbc_encode(frame,debug):
62c21a9c2fSMilanka Ringwald    if frame.nr_subbands == 4:
63c21a9c2fSMilanka Ringwald        proto_table = Proto_4_40
64c21a9c2fSMilanka Ringwald    elif frame.nr_subbands == 8:
65c21a9c2fSMilanka Ringwald        proto_table = Proto_8_80
66c21a9c2fSMilanka Ringwald    else:
67c21a9c2fSMilanka Ringwald        return -1
68c21a9c2fSMilanka Ringwald
69c21a9c2fSMilanka Ringwald    frame.sb_sample = np.ndarray(shape=(frame.nr_blocks, frame.nr_channels, frame.nr_subbands))
70c21a9c2fSMilanka Ringwald    index = 0
71c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
72c21a9c2fSMilanka Ringwald        for blk in range(frame.nr_blocks):
73c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
74*41a4a18dSMilanka Ringwald                frame.EX[sb] = frame.pcm[index]
75c21a9c2fSMilanka Ringwald                index+=1
76c21a9c2fSMilanka Ringwald            sbc_analyse(frame, ch, blk, proto_table,debug)
77c21a9c2fSMilanka Ringwald    sbc_quantization(frame)
78c21a9c2fSMilanka Ringwald
79c21a9c2fSMilanka Ringwald
80c21a9c2fSMilanka Ringwalddef should_use_joint_coding(frame):
81*41a4a18dSMilanka Ringwald    # TODO: implement this
82c21a9c2fSMilanka Ringwald    return False
83c21a9c2fSMilanka Ringwald
84c21a9c2fSMilanka Ringwalddef calculate_scalefactor(max_subbandsample):
85c21a9c2fSMilanka Ringwald    x = 0
86c21a9c2fSMilanka Ringwald    while True:
87c21a9c2fSMilanka Ringwald        y = 1 << x + 1
88c21a9c2fSMilanka Ringwald        if y > max_subbandsample:
89c21a9c2fSMilanka Ringwald            break
90c21a9c2fSMilanka Ringwald        x += 1
91c21a9c2fSMilanka Ringwald    return (x,y)
92c21a9c2fSMilanka Ringwald
93c21a9c2fSMilanka Ringwald
94c21a9c2fSMilanka Ringwalddef frame_to_bitstream(frame):
95c21a9c2fSMilanka Ringwald    global bitstream, bitstream_bits_available
96c21a9c2fSMilanka Ringwald    init_bitstream()
97c21a9c2fSMilanka Ringwald
98c21a9c2fSMilanka Ringwald    add_bits(frame.syncword, 8)
99c21a9c2fSMilanka Ringwald    add_bits(frame.sampling_frequency, 2)
100c21a9c2fSMilanka Ringwald    add_bits(frame.nr_blocks/4-1, 2)
101c21a9c2fSMilanka Ringwald    add_bits(frame.channel_mode, 2)
102c21a9c2fSMilanka Ringwald    add_bits(frame.allocation_method, 1)
103c21a9c2fSMilanka Ringwald    add_bits(frame.nr_subbands/4-1, 1)
104c21a9c2fSMilanka Ringwald    add_bits(frame.bitpool, 8)
105c21a9c2fSMilanka Ringwald    add_bits(frame.crc_check, 8)
106c21a9c2fSMilanka Ringwald
107c21a9c2fSMilanka Ringwald    for sb in range(frame.nr_subbands):
108c21a9c2fSMilanka Ringwald        add_bits(frame.join[sb],1)
109c21a9c2fSMilanka Ringwald
110c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
111c21a9c2fSMilanka Ringwald        for sb in range(frame.nr_subbands):
112c21a9c2fSMilanka Ringwald            add_bits(frame.scale_factor[ch][sb], 4)
113c21a9c2fSMilanka Ringwald
114c21a9c2fSMilanka Ringwald    for blk in range(frame.nr_blocks):
115c21a9c2fSMilanka Ringwald        for ch in range(frame.nr_channels):
116c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
117c21a9c2fSMilanka Ringwald                add_bits(frame.audio_sample[blk][ch][sb], frame.bits[ch][sb])
118c21a9c2fSMilanka Ringwald
119c21a9c2fSMilanka Ringwald    return bitstream
120c21a9c2fSMilanka Ringwald
121c21a9c2fSMilanka Ringwalddef sbc_quantization(frame):
122c21a9c2fSMilanka Ringwald    frame.join = np.zeros(frame.nr_subbands, dtype = np.uint8)
123c21a9c2fSMilanka Ringwald    if should_use_joint_coding(frame):
124c21a9c2fSMilanka Ringwald        return
125c21a9c2fSMilanka Ringwald
126c21a9c2fSMilanka Ringwald    max_subbandsample = np.zeros(shape = (frame.nr_channels, frame.nr_subbands))
127c21a9c2fSMilanka Ringwald
128c21a9c2fSMilanka Ringwald    for blk in range(frame.nr_blocks):
129c21a9c2fSMilanka Ringwald        for ch in range(frame.nr_channels):
130c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
131c21a9c2fSMilanka Ringwald                m = abs(frame.sb_sample[blk][ch][sb])
132c21a9c2fSMilanka Ringwald                if max_subbandsample[ch][sb] < m:
133c21a9c2fSMilanka Ringwald                    max_subbandsample[ch][sb] = m
134c21a9c2fSMilanka Ringwald
135c21a9c2fSMilanka Ringwald
136c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
137c21a9c2fSMilanka Ringwald        for sb in range(frame.nr_subbands):
138c21a9c2fSMilanka Ringwald            (frame.scale_factor[ch][sb], frame.scalefactor[ch][sb]) = calculate_scalefactor(max_subbandsample[ch][sb])
139c21a9c2fSMilanka Ringwald
140c21a9c2fSMilanka Ringwald    frame.bits = sbc_bit_allocation(frame)
141c21a9c2fSMilanka Ringwald
142c21a9c2fSMilanka Ringwald    # Reconstruct the Audio Samples
143c21a9c2fSMilanka Ringwald    frame.levels = np.zeros(shape=(frame.nr_channels, frame.nr_subbands), dtype = np.int32)
144c21a9c2fSMilanka Ringwald    for ch in range(frame.nr_channels):
145c21a9c2fSMilanka Ringwald        for sb in range(frame.nr_subbands):
146c21a9c2fSMilanka Ringwald            frame.levels[ch][sb] = pow(2.0, frame.bits[ch][sb]) - 1
147c21a9c2fSMilanka Ringwald
148c21a9c2fSMilanka Ringwald    frame.syncword = 156
149c21a9c2fSMilanka Ringwald    frame.crc_check = calculate_crc(frame)
150c21a9c2fSMilanka Ringwald
151c21a9c2fSMilanka Ringwald    for blk in range(frame.nr_blocks):
152c21a9c2fSMilanka Ringwald        for ch in range(frame.nr_channels):
153c21a9c2fSMilanka Ringwald            for sb in range(frame.nr_subbands):
154c21a9c2fSMilanka Ringwald                if frame.levels[ch][sb] > 0:
155c21a9c2fSMilanka Ringwald                    SB = frame.sb_sample[blk][ch][sb]
156c21a9c2fSMilanka Ringwald                    SF = frame.scalefactor[ch][sb]
157c21a9c2fSMilanka Ringwald                    L  = frame.levels[ch][sb]
158*41a4a18dSMilanka Ringwald
159*41a4a18dSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = np.uint16(((SB * L / SF    + L) - 1.0)/2.0)
160c21a9c2fSMilanka Ringwald                else:
161c21a9c2fSMilanka Ringwald                    frame.audio_sample[blk][ch][sb] = 0
162c21a9c2fSMilanka Ringwald
163c21a9c2fSMilanka Ringwald
164c21a9c2fSMilanka Ringwald    return 0
165c21a9c2fSMilanka Ringwald
166c21a9c2fSMilanka Ringwald
167c21a9c2fSMilanka Ringwald# usage = '''
168c21a9c2fSMilanka Ringwald# Usage: ./sbc_encoder.py input.wav block_size nr_subbands bitpool
169c21a9c2fSMilanka Ringwald# '''
170c21a9c2fSMilanka Ringwald# nr_blocks = 0
171c21a9c2fSMilanka Ringwald# nr_subbands = 0
172c21a9c2fSMilanka Ringwald
173c21a9c2fSMilanka Ringwald# if (len(sys.argv) < 5):
174c21a9c2fSMilanka Ringwald#     print(usage)
175c21a9c2fSMilanka Ringwald#     sys.exit(1)
176c21a9c2fSMilanka Ringwald# try:
177c21a9c2fSMilanka Ringwald#     infile = sys.argv[1]
178c21a9c2fSMilanka Ringwald#     if not infile.endswith('.wav'):
179c21a9c2fSMilanka Ringwald#         print(usage)
180c21a9c2fSMilanka Ringwald#         sys.exit(1)
181c21a9c2fSMilanka Ringwald#     nr_blocks = int(sys.argv[2])
182c21a9c2fSMilanka Ringwald#     nr_subbands = int(sys.argv[3])
183c21a9c2fSMilanka Ringwald#     bitpool = int(sys.argv[4])
184c21a9c2fSMilanka Ringwald#     sbcfile = infile.replace('.wav', '-encoded.sbc')
185c21a9c2fSMilanka Ringwald
186c21a9c2fSMilanka Ringwald#     fin = wave.open(infile, 'rb')
187c21a9c2fSMilanka Ringwald
188c21a9c2fSMilanka Ringwald#     wav_nr_channels = fin.getnchannels()
189c21a9c2fSMilanka Ringwald#     wav_sample_rate = fin.getframerate()
190c21a9c2fSMilanka Ringwald#     wav_nr_frames = fin.getnframes()
191c21a9c2fSMilanka Ringwald#     sbc_sampling_frequency = sbc_sampling_frequency_index(wav_sample_rate)
192c21a9c2fSMilanka Ringwald
193c21a9c2fSMilanka Ringwald#     sbc_frame_count = 0
194c21a9c2fSMilanka Ringwald#     audio_frame_count = 0
195c21a9c2fSMilanka Ringwald
196c21a9c2fSMilanka Ringwald#     while audio_frame_count < wav_nr_frames:
197c21a9c2fSMilanka Ringwald#         if sbc_frame_count % 200 == 0:
198c21a9c2fSMilanka Ringwald#             print "== Frame %d ==" % (sbc_frame_count)
199c21a9c2fSMilanka Ringwald
200c21a9c2fSMilanka Ringwald#         sbc_encoder_frame = SBCFrame(nr_blocks, nr_subbands, wav_nr_channels, sbc_sampling_frequency, bitpool)
201c21a9c2fSMilanka Ringwald
202c21a9c2fSMilanka Ringwald#         wav_nr_audio_frames = sbc_encoder_frame.nr_blocks * sbc_encoder_frame.nr_subbands
203c21a9c2fSMilanka Ringwald#         fetch_samples_for_next_sbc_frame(fin, wav_nr_audio_frames, sbc_encoder_frame)
204c21a9c2fSMilanka Ringwald#         sbc_encode(sbc_encoder_frame)
205c21a9c2fSMilanka Ringwald
206c21a9c2fSMilanka Ringwald#         # stream = frame_to_bitstream(frame)
207c21a9c2fSMilanka Ringwald#         audio_frame_count += wav_nr_audio_frames
208c21a9c2fSMilanka Ringwald#         sbc_frame_count += 1
209c21a9c2fSMilanka Ringwald
210c21a9c2fSMilanka Ringwald#     # except TypeError:
211c21a9c2fSMilanka Ringwald#     #     fin.close()
212c21a9c2fSMilanka Ringwald#     #     print "DONE, WAV file %s encoded into SBC file %s ", (infile, sbcfile)
213c21a9c2fSMilanka Ringwald
214c21a9c2fSMilanka Ringwald#     #channels, num_audio_frames, wav_nr_channels, wav_sample_rate = read_waw_file(wavfile)
215c21a9c2fSMilanka Ringwald
216c21a9c2fSMilanka Ringwald
217c21a9c2fSMilanka Ringwald# except IOError as e:
218c21a9c2fSMilanka Ringwald#     print(usage)
219c21a9c2fSMilanka Ringwald#     sys.exit(1)
220c21a9c2fSMilanka Ringwald
221c21a9c2fSMilanka Ringwald
222c21a9c2fSMilanka Ringwald
223c21a9c2fSMilanka Ringwald
224c21a9c2fSMilanka Ringwald
225