""" Not all output rates are going to be supported by the system sound card. Choosing from a limited subset of preferred output rates is how we guarantee that the system will be able to play the output audio. Sample rates stolen from: https://en.wikipedia.org/wiki/Sampling_(signal_processing) """ supported_ouput_rates = [ 8000, # Telephone, P25 audio (sufficient for speech, some consonants unintelligble) 16000, # Modern telephone/VoIP, good quality speech 32000, # FM radio 44100, # CD audio quality 48000, 50000, # Uncommon but supported 88200, 96000, # DVD/Blu-ray audio 192000, # Too much. ] def score(pair, target_output=32000, target_ratio=10): """ Heuristic for scoring input & output sample rates. The criteria are: - closest to 44.1 kHz is better; - closer to a ratio of 10 is better, - additionally penalising ratios lower than 10. Lower scores are better. This should result in selected sample rates that give good audio quality without wasting CPU cycles on a needlessly high input rate. """ # Give the worst score possible for impossible pairs. if pair[0] % pair[1] != 0: return float("inf") ratio = pair[0] // pair[1] return ( abs(pair[1] - target_output) / 2500 + max(0, target_output - pair[1]) / 2500 + abs(ratio - target_ratio) ** 0.8 + max(0, target_ratio - ratio) ** 2 ) def flatten(l): return [item for sublist in l for item in sublist] def flatten_dict(d): return [(key, value) for key, rates in d.items() for value in rates] def get_pairs(input_rate): return [ (input_rate, rate) for rate in supported_ouput_rates if (input_rate % rate == 0) ] def supported_sample_rates(supported_input_rates): return { in_rate: [ out_rate for out_rate in supported_ouput_rates if in_rate % out_rate == 0 ] for in_rate in supported_input_rates } def preferred_sample_rates(supported_input_rates): return sorted( flatten_dict(supported_sample_rates(supported_input_rates)), key=score )