| # Copyright (c) 2021 Arm Limited. All rights reserved. |
| # SPDX-License-Identifier: Apache-2.0 |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """ |
| Utility script to convert a set of audio clip in a given location into |
| corresponding cpp files and a single hpp file referencing the vectors |
| from the cpp files. |
| """ |
| import datetime |
| import glob |
| import math |
| import os |
| |
| import numpy as np |
| from os import path |
| from argparse import ArgumentParser |
| from jinja2 import Environment, FileSystemLoader |
| from gen_utils import AudioUtils |
| |
| parser = ArgumentParser() |
| parser.add_argument("--audio_path", type=str, help="path to audio folder to convert.") |
| parser.add_argument("--source_folder_path", type=str, help="path to source folder to be generated.") |
| parser.add_argument("--header_folder_path", type=str, help="path to header folder to be generated.") |
| parser.add_argument("--sampling_rate", type=int, help="target sampling rate.", default=16000) |
| parser.add_argument("--mono", type=bool, help="convert signal to mono.", default=True) |
| parser.add_argument("--offset", type=float, help="start reading after this time (in seconds).", default=0) |
| parser.add_argument("--duration", type=float, help="only load up to this much audio (in seconds).", default=0) |
| parser.add_argument("--res_type", type=AudioUtils.res_data_type, help=f"Resample type: {AudioUtils.res_type_list()}.", |
| default='kaiser_best') |
| parser.add_argument("--min_samples", type=int, help="Minimum sample number.", default=16000) |
| parser.add_argument("--license_template", type=str, help="Header template file", |
| default="header_template.txt") |
| parser.add_argument("-v", "--verbosity", action="store_true") |
| args = parser.parse_args() |
| |
| env = Environment(loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), 'templates')), |
| trim_blocks=True, |
| lstrip_blocks=True) |
| |
| |
| def write_hpp_file(header_filepath, cc_filepath, header_template_file, num_audios, audio_filenames, audio_array_namesizes): |
| print(f"++ Generating {header_filepath}") |
| |
| header_template = env.get_template(header_template_file) |
| hdr = header_template.render(script_name=os.path.basename(__file__), |
| gen_time=datetime.datetime.now(), |
| year=datetime.datetime.now().year) |
| env.get_template('AudioClips.hpp.template').stream(common_template_header=hdr, |
| clips_count=num_audios, |
| varname_size=audio_array_namesizes |
| ) \ |
| .dump(str(header_filepath)) |
| |
| print(f"++ Generating {cc_filepath}") |
| |
| env.get_template('AudioClips.cc.template').stream(common_template_header=hdr, |
| clips_count=num_audios, |
| var_names=(name for name, _ in audio_array_namesizes), |
| clip_sizes=(size for _, size in audio_array_namesizes), |
| clip_names=audio_filenames) \ |
| .dump(str(cc_filepath)) |
| |
| |
| def write_individual_audio_cc_file(clip_dirpath, clip_filename, |
| cc_filename, header_template_file, array_name, |
| sampling_rate_value, mono_value, offset_value, |
| duration_value, res_type_value, min_len): |
| print(f"++ Converting {clip_filename} to {path.basename(cc_filename)}") |
| audio_filepath = path.join(clip_dirpath, clip_filename) |
| clip_data, samplerate = AudioUtils.load_resample_audio_clip(audio_filepath, |
| sampling_rate_value, mono_value, |
| offset_value, duration_value, |
| res_type_value, min_len) |
| |
| # Change from [-1, 1] fp32 range to int16 range. |
| clip_data = np.clip((clip_data * (1 << 15)), |
| np.iinfo(np.int16).min, |
| np.iinfo(np.int16).max).flatten().astype(np.int16) |
| |
| header_template = env.get_template(header_template_file) |
| hdr = header_template.render(script_name=os.path.basename(__file__), |
| gen_time=datetime.datetime.now(), |
| file_name=clip_filename, |
| year=datetime.datetime.now().year) |
| |
| hex_line_generator = (', '.join(map(hex, sub_arr)) |
| for sub_arr in np.array_split(clip_data, math.ceil(len(clip_data)/20))) |
| |
| env.get_template('audio.cc.template').stream(common_template_header=hdr, |
| size=len(clip_data), |
| var_name=array_name, |
| audio_data=hex_line_generator) \ |
| .dump(str(cc_filename)) |
| |
| return len(clip_data) |
| |
| |
| def main(args): |
| # Keep the count of the audio files converted |
| audioclip_idx = 0 |
| audioclip_filenames = [] |
| audioclip_array_names = [] |
| header_filename = "InputFiles.hpp" |
| common_cc_filename = "InputFiles.cc" |
| header_filepath = path.join(args.header_folder_path, header_filename) |
| common_cc_filepath = path.join(args.source_folder_path, common_cc_filename) |
| |
| if os.path.isdir(args.audio_path): |
| filepaths = sorted(glob.glob(path.join(args.audio_path, '**/*.wav'), recursive=True)) |
| elif os.path.isfile(args.audio_path): |
| filepaths = [args.audio_path] |
| else: |
| raise OSError("Directory or file does not exist.") |
| |
| for filepath in filepaths: |
| filename = path.basename(filepath) |
| clip_dirpath = path.dirname(filepath) |
| try: |
| audioclip_filenames.append(filename) |
| |
| # Save the cc file |
| cc_filename = path.join(args.source_folder_path, |
| (filename.rsplit(".")[0]).replace(" ", "_") + ".cc") |
| array_name = "audio" + str(audioclip_idx) |
| array_size = write_individual_audio_cc_file(clip_dirpath, filename, cc_filename, args.license_template, array_name, |
| args.sampling_rate, args.mono, args.offset, |
| args.duration, args.res_type, args.min_samples) |
| |
| audioclip_array_names.append((array_name, array_size)) |
| # Increment audio index |
| audioclip_idx = audioclip_idx + 1 |
| except: |
| if args.verbosity: |
| print(f"Failed to open {filename} as an audio.") |
| |
| if len(audioclip_filenames) > 0: |
| write_hpp_file(header_filepath, common_cc_filepath, args.license_template, |
| audioclip_idx, audioclip_filenames, audioclip_array_names) |
| else: |
| raise FileNotFoundError("No valid audio clip files found.") |
| |
| |
| if __name__ == '__main__': |
| main(args) |