""" 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)