xref: /aosp_15_r20/external/liblc3/python/tools/specgram.py (revision 49fe348c0058011ee60b6957cdd9d52742df84bc)
1*49fe348cSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*49fe348cSAndroid Build Coastguard Worker#
3*49fe348cSAndroid Build Coastguard Worker# Copyright 2024 Google LLC
4*49fe348cSAndroid Build Coastguard Worker#
5*49fe348cSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*49fe348cSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*49fe348cSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*49fe348cSAndroid Build Coastguard Worker#
9*49fe348cSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*49fe348cSAndroid Build Coastguard Worker#
11*49fe348cSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*49fe348cSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*49fe348cSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*49fe348cSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*49fe348cSAndroid Build Coastguard Worker# limitations under the License.
16*49fe348cSAndroid Build Coastguard Worker#
17*49fe348cSAndroid Build Coastguard Worker
18*49fe348cSAndroid Build Coastguard Workerimport argparse
19*49fe348cSAndroid Build Coastguard Workerimport lc3
20*49fe348cSAndroid Build Coastguard Workerimport matplotlib
21*49fe348cSAndroid Build Coastguard Workerimport matplotlib.pyplot as plt
22*49fe348cSAndroid Build Coastguard Workerimport numpy as np
23*49fe348cSAndroid Build Coastguard Workerimport scipy.signal as signal
24*49fe348cSAndroid Build Coastguard Worker
25*49fe348cSAndroid Build Coastguard Workermatplotlib.use('QtAgg')
26*49fe348cSAndroid Build Coastguard Worker
27*49fe348cSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(description='LC3 Encoder')
28*49fe348cSAndroid Build Coastguard Worker
29*49fe348cSAndroid Build Coastguard Workerparser.add_argument(
30*49fe348cSAndroid Build Coastguard Worker    '--frame_duration', help='Frame duration (ms)', type=float, default=10)
31*49fe348cSAndroid Build Coastguard Worker
32*49fe348cSAndroid Build Coastguard Workerparser.add_argument(
33*49fe348cSAndroid Build Coastguard Worker    '--sample_rate', help='Sampling frequency (Hz)', type=float, default=48000)
34*49fe348cSAndroid Build Coastguard Worker
35*49fe348cSAndroid Build Coastguard Workerparser.add_argument(
36*49fe348cSAndroid Build Coastguard Worker    '--hrmode', help='Enable high-resolution mode', action='store_true')
37*49fe348cSAndroid Build Coastguard Worker
38*49fe348cSAndroid Build Coastguard Workerparser.add_argument(
39*49fe348cSAndroid Build Coastguard Worker    '--bitrate', help='Bitrate (bps)', type=int, default=96000)
40*49fe348cSAndroid Build Coastguard Worker
41*49fe348cSAndroid Build Coastguard Workerparser.add_argument(
42*49fe348cSAndroid Build Coastguard Worker    '--libpath', help='LC3 Library path')
43*49fe348cSAndroid Build Coastguard Worker
44*49fe348cSAndroid Build Coastguard Workerargs = parser.parse_args()
45*49fe348cSAndroid Build Coastguard Worker
46*49fe348cSAndroid Build Coastguard Worker# --- Setup encoder + decoder ---
47*49fe348cSAndroid Build Coastguard Worker
48*49fe348cSAndroid Build Coastguard Workerfs = args.sample_rate
49*49fe348cSAndroid Build Coastguard Worker
50*49fe348cSAndroid Build Coastguard Workerenc = lc3.Encoder(
51*49fe348cSAndroid Build Coastguard Worker    args.frame_duration, fs, hrmode=args.hrmode, libpath=args.libpath)
52*49fe348cSAndroid Build Coastguard Workerdec = lc3.Decoder(
53*49fe348cSAndroid Build Coastguard Worker    args.frame_duration, fs, hrmode=args.hrmode, libpath=args.libpath)
54*49fe348cSAndroid Build Coastguard Worker
55*49fe348cSAndroid Build Coastguard Workerframe_len = enc.get_frame_samples()
56*49fe348cSAndroid Build Coastguard Workerdelay_len = enc.get_delay_samples()
57*49fe348cSAndroid Build Coastguard Worker
58*49fe348cSAndroid Build Coastguard Worker# --- Generate 10 seconds chirp ---
59*49fe348cSAndroid Build Coastguard Worker
60*49fe348cSAndroid Build Coastguard Workern = 10 * int(fs)
61*49fe348cSAndroid Build Coastguard Workert = np.arange(n - (n % frame_len)) / fs
62*49fe348cSAndroid Build Coastguard Workers = signal.chirp(t, f0=10, f1=fs/2, t1=t[-1], phi=-90, method='linear')
63*49fe348cSAndroid Build Coastguard Worker
64*49fe348cSAndroid Build Coastguard Worker# --- Encoding / decoding loop ---
65*49fe348cSAndroid Build Coastguard Worker
66*49fe348cSAndroid Build Coastguard Workerframe_size = enc.get_frame_bytes(args.bitrate)
67*49fe348cSAndroid Build Coastguard Workerbitrate = enc.resolve_bitrate(frame_size)
68*49fe348cSAndroid Build Coastguard Worker
69*49fe348cSAndroid Build Coastguard Workery = np.empty(len(s) + frame_len)
70*49fe348cSAndroid Build Coastguard Worker
71*49fe348cSAndroid Build Coastguard Workerfor i in range(0, len(s), frame_len):
72*49fe348cSAndroid Build Coastguard Worker    y[i:i+frame_len] = dec.decode(enc.encode(s[i:i+frame_len], frame_size))
73*49fe348cSAndroid Build Coastguard Worker
74*49fe348cSAndroid Build Coastguard Workery[len(s):] = dec.decode(enc.encode(np.zeros(frame_len), frame_size))
75*49fe348cSAndroid Build Coastguard Workery = y[delay_len:len(s)+delay_len]
76*49fe348cSAndroid Build Coastguard Worker
77*49fe348cSAndroid Build Coastguard Worker# --- Plot spectrograms ---
78*49fe348cSAndroid Build Coastguard Worker
79*49fe348cSAndroid Build Coastguard Workerfig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
80*49fe348cSAndroid Build Coastguard Worker
81*49fe348cSAndroid Build Coastguard WorkerNFFT = 512
82*49fe348cSAndroid Build Coastguard Workerfor (ax, s) in [(ax1, s), (ax2, y)]:
83*49fe348cSAndroid Build Coastguard Worker    ax.specgram(s, Fs=fs, NFFT=NFFT, pad_to=4*NFFT, noverlap=NFFT//2,
84*49fe348cSAndroid Build Coastguard Worker                vmin=-160, vmax=0)
85*49fe348cSAndroid Build Coastguard Worker
86*49fe348cSAndroid Build Coastguard Workerax1.set_title('Input signal')
87*49fe348cSAndroid Build Coastguard Workerax1.set_ylabel('Frequency (Hz)')
88*49fe348cSAndroid Build Coastguard Worker
89*49fe348cSAndroid Build Coastguard Workerax2.set_title(('Processed signal (%.1f kbps)' % (bitrate/1000)))
90*49fe348cSAndroid Build Coastguard Workerax2.set_ylabel('Frequency (Hz)')
91*49fe348cSAndroid Build Coastguard Worker
92*49fe348cSAndroid Build Coastguard Workerplt.show()
93