blob: b8928f0d4ea13661937ff8bc7ffd0ad3c9fc45e7 [file] [log] [blame]
alexander3c798932021-03-26 21:42:19 +00001# Copyright (c) 2021 Arm Limited. All rights reserved.
2# SPDX-License-Identifier: Apache-2.0
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""
17Utility script to convert a set of audio clip in a given location into
18corresponding cpp files and a single hpp file referencing the vectors
19from the cpp files.
20"""
21import datetime
22import glob
23import math
Richard Burton17069622022-03-17 10:54:26 +000024from pathlib import Path
25from argparse import ArgumentParser
alexander3c798932021-03-26 21:42:19 +000026
27import numpy as np
alexander3c798932021-03-26 21:42:19 +000028from jinja2 import Environment, FileSystemLoader
Richard Burton17069622022-03-17 10:54:26 +000029
alexander3c798932021-03-26 21:42:19 +000030from gen_utils import AudioUtils
31
32parser = ArgumentParser()
33parser.add_argument("--audio_path", type=str, help="path to audio folder to convert.")
34parser.add_argument("--source_folder_path", type=str, help="path to source folder to be generated.")
35parser.add_argument("--header_folder_path", type=str, help="path to header folder to be generated.")
36parser.add_argument("--sampling_rate", type=int, help="target sampling rate.", default=16000)
37parser.add_argument("--mono", type=bool, help="convert signal to mono.", default=True)
38parser.add_argument("--offset", type=float, help="start reading after this time (in seconds).", default=0)
39parser.add_argument("--duration", type=float, help="only load up to this much audio (in seconds).", default=0)
40parser.add_argument("--res_type", type=AudioUtils.res_data_type, help=f"Resample type: {AudioUtils.res_type_list()}.",
41 default='kaiser_best')
42parser.add_argument("--min_samples", type=int, help="Minimum sample number.", default=16000)
43parser.add_argument("--license_template", type=str, help="Header template file",
44 default="header_template.txt")
45parser.add_argument("-v", "--verbosity", action="store_true")
46args = parser.parse_args()
47
Richard Burton17069622022-03-17 10:54:26 +000048env = Environment(loader=FileSystemLoader(Path(__file__).parent / 'templates'),
alexander3c798932021-03-26 21:42:19 +000049 trim_blocks=True,
50 lstrip_blocks=True)
51
52
53def write_hpp_file(header_filepath, cc_filepath, header_template_file, num_audios, audio_filenames, audio_array_namesizes):
54 print(f"++ Generating {header_filepath}")
55
56 header_template = env.get_template(header_template_file)
Richard Burton17069622022-03-17 10:54:26 +000057 hdr = header_template.render(script_name=Path(__file__).name,
alexander3c798932021-03-26 21:42:19 +000058 gen_time=datetime.datetime.now(),
59 year=datetime.datetime.now().year)
60 env.get_template('AudioClips.hpp.template').stream(common_template_header=hdr,
61 clips_count=num_audios,
62 varname_size=audio_array_namesizes
63 ) \
64 .dump(str(header_filepath))
65
66 print(f"++ Generating {cc_filepath}")
67
68 env.get_template('AudioClips.cc.template').stream(common_template_header=hdr,
69 clips_count=num_audios,
70 var_names=(name for name, _ in audio_array_namesizes),
71 clip_sizes=(size for _, size in audio_array_namesizes),
72 clip_names=audio_filenames) \
73 .dump(str(cc_filepath))
74
75
76def write_individual_audio_cc_file(clip_dirpath, clip_filename,
77 cc_filename, header_template_file, array_name,
Kshitij Sisodia659fcd92021-05-19 10:30:06 +010078 sampling_rate_value, mono_value, offset_value,
alexander3c798932021-03-26 21:42:19 +000079 duration_value, res_type_value, min_len):
Richard Burton17069622022-03-17 10:54:26 +000080 print(f"++ Converting {clip_filename} to {Path(cc_filename).name}")
81 audio_filepath = Path(clip_dirpath) / clip_filename
alexander3c798932021-03-26 21:42:19 +000082 clip_data, samplerate = AudioUtils.load_resample_audio_clip(audio_filepath,
83 sampling_rate_value, mono_value,
84 offset_value, duration_value,
85 res_type_value, min_len)
86
87 # Change from [-1, 1] fp32 range to int16 range.
Kshitij Sisodia659fcd92021-05-19 10:30:06 +010088 clip_data = np.clip((clip_data * (1 << 15)),
89 np.iinfo(np.int16).min,
alexander3c798932021-03-26 21:42:19 +000090 np.iinfo(np.int16).max).flatten().astype(np.int16)
91
92 header_template = env.get_template(header_template_file)
Richard Burton17069622022-03-17 10:54:26 +000093 hdr = header_template.render(script_name=Path(__file__).name,
alexander3c798932021-03-26 21:42:19 +000094 gen_time=datetime.datetime.now(),
95 file_name=clip_filename,
96 year=datetime.datetime.now().year)
97
98 hex_line_generator = (', '.join(map(hex, sub_arr))
99 for sub_arr in np.array_split(clip_data, math.ceil(len(clip_data)/20)))
100
101 env.get_template('audio.cc.template').stream(common_template_header=hdr,
102 size=len(clip_data),
103 var_name=array_name,
104 audio_data=hex_line_generator) \
105 .dump(str(cc_filename))
106
107 return len(clip_data)
108
109
110def main(args):
111 # Keep the count of the audio files converted
112 audioclip_idx = 0
113 audioclip_filenames = []
114 audioclip_array_names = []
115 header_filename = "InputFiles.hpp"
116 common_cc_filename = "InputFiles.cc"
Richard Burton17069622022-03-17 10:54:26 +0000117 header_filepath = Path(args.header_folder_path) / header_filename
118 common_cc_filepath = Path(args.source_folder_path) / common_cc_filename
alexander3c798932021-03-26 21:42:19 +0000119
Richard Burton17069622022-03-17 10:54:26 +0000120 if Path(args.audio_path).is_dir():
121 filepaths = sorted(glob.glob(str(Path(args.audio_path) / '**/*.wav'), recursive=True))
122 elif Path(args.audio_path).is_file():
alexander3c798932021-03-26 21:42:19 +0000123 filepaths = [args.audio_path]
124 else:
125 raise OSError("Directory or file does not exist.")
126
127 for filepath in filepaths:
Richard Burton17069622022-03-17 10:54:26 +0000128 filename = Path(filepath).name
129 clip_dirpath = Path(filepath).parent
alexander3c798932021-03-26 21:42:19 +0000130 try:
131 audioclip_filenames.append(filename)
132
133 # Save the cc file
Richard Burton17069622022-03-17 10:54:26 +0000134 cc_filename = Path(args.source_folder_path) / (Path(filename).stem.replace(" ", "_") + ".cc")
alexander3c798932021-03-26 21:42:19 +0000135 array_name = "audio" + str(audioclip_idx)
136 array_size = write_individual_audio_cc_file(clip_dirpath, filename, cc_filename, args.license_template, array_name,
137 args.sampling_rate, args.mono, args.offset,
138 args.duration, args.res_type, args.min_samples)
139
140 audioclip_array_names.append((array_name, array_size))
141 # Increment audio index
142 audioclip_idx = audioclip_idx + 1
143 except:
144 if args.verbosity:
145 print(f"Failed to open {filename} as an audio.")
146
Kshitij Sisodia659fcd92021-05-19 10:30:06 +0100147 if len(audioclip_filenames) > 0:
148 write_hpp_file(header_filepath, common_cc_filepath, args.license_template,
149 audioclip_idx, audioclip_filenames, audioclip_array_names)
150 else:
151 raise FileNotFoundError("No valid audio clip files found.")
alexander3c798932021-03-26 21:42:19 +0000152
153
154if __name__ == '__main__':
155 main(args)