#!/usr/bin/env python3 import sys import os import threading import queue import time import math import pyaudio unbufferedStdout = os.fdopen(sys.stdout.fileno(),"wb",0) # Make unbuffered stdout audioQueue = queue.Queue() lastTime = 0 def legacyTimeMethod(): currentTime = time.clock() if lastTime == 0: lastTime = currentTime return 0 timeSpent = currentTime - lastTime lastTime = currentTime return timeSpent timeMethod = legacyTimeMethod try: timeMethod = time.perf_counter except Exception: pass class inThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): try: while True: b = sys.stdin.buffer.read(audioFrameSize) if b == b"": return audioQueue.put(b) except KeyboardInterrupt: pass def getAudioFrame(): global lastFrame global audioStore data = lastFrame #print(audioQueue.qsize()) if audioQueue.qsize() > 0: while audioQueue.qsize() > audioStore + 1: data = audioQueue.get(False) data = audioQueue.get(False) lastFrame = data return data def streamHandler(in_data, frame_count, time_info, status): data = b"" data += getAudioFrame() return (data, pyaudio.paContinue) def parseFlags(): rtn = [] for arg in sys.argv[1:]: arg = arg.replace(" ","") arg = arg.replace("\t","") arg = arg.replace("\r","") arg = arg.replace("\n","") if arg[0] != "-": continue if arg == "-": continue if arg[:2] == "--": rtn.append(arg[2:]) continue for i in arg[1:]: rtn.append(i) return rtn def parseSettings(): rtn = {} for arg in sys.argv[1:]: arg = arg.split("=",1) if len(arg) < 2: arg.append("") arg[0] = arg[0].strip(" \t\r\n") arg[1] = arg[1].strip(" \t\r\n") rtn[arg[0]] = arg[1] return rtn def getSetting(lst,tp,keys,default = None): for key in keys: if key in lst: return tp(lst[key]) return default flags = parseFlags() if "l" in flags: devices = {} audio = pyaudio.PyAudio() defaultDevice = audio.get_default_host_api_info()["defaultOutputDevice"] devices["default"] = defaultDevice for i in range(0, audio.get_device_count()): info = audio.get_device_info_by_index(i) if info["maxOutputChannels"] == 0: continue devices[i] = { "name": info["name"], "api": audio.get_host_api_info_by_index(info["hostApi"])["name"] } if not "h" in flags: import json print(json.dumps(devices)) else: print("\nAvailable output devices:") for i in devices: if i == "default": continue st = str(i)+ ": " +devices[i]["name"]+ " [" +devices[i]["api"]+ "]" if i == defaultDevice: st = st + " *" print(st) sys.exit(0) settings = parseSettings() audioFormat = getSetting(settings,str,["f","format"],"paUInt8") audioChannels = getSetting(settings,int,["c","channel","channels"],1) audioRate = getSetting(settings,int,["r","rate","bitrate"],8000) audioBuffer = getSetting(settings,float,["b","buffer","buffersize"],0.05) audioStore = getSetting(settings,float,["s","store"],0.3) audioDevice = getSetting(settings,int,["d","device"],None) if audioDevice == -1: audioDevice = None letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" audioBitrate = audioFormat audioFormat = getattr(pyaudio,audioFormat) for letter in letters: audioBitrate = audioBitrate.replace(letter,"") audioBitrate = int(audioBitrate) audioBuffer = round(audioRate * audioBuffer) audioFrameSize = int(float(audioChannels) * (audioBitrate / 8) * audioBuffer) audioStorePerSecond = audioBuffer / audioRate audioStore = math.ceil(audioStore / audioStorePerSecond) lastFrame = bytearray(int(audioFrameSize)) kwargs = { "output": True, "output_device_index": audioDevice, "format": audioFormat, "channels": audioChannels, "frames_per_buffer": audioBuffer, "rate": audioRate, "stream_callback": streamHandler } audio = pyaudio.PyAudio() stream = audio.open(**kwargs) inThread().start() try: while stream.is_active(): time.sleep(0.1) except KeyboardInterrupt: pass