def visualize_once(self, falloff: float = 0.8, rows: float = 25, col: int = 20, top: int = 100): """ Visualize current levels of audio on the razer devices, and render it :param falloff: Substraction coefficient to apply to last frame of the animation :param rows: Number of rows to have in visualization :param col: Number of columns to have in visualization :param top: Maximum number expected from the visualization """ r = self.device_controller.r * falloff g = self.device_controller.g * falloff b = self.device_controller.b * falloff # audio data from stream (after fft) data = self.audio.read_once(rows, col) logging.pprint(str(data[4]), 5) # render colors new_coef = (data[4] - self.dampen) / (self.ceiling - self.dampen) if (new_coef >= self.device_controller.coef): self.device_controller.coef = new_coef else: self.device_controller.coef = self.device_controller.coef * falloff if (self.device_controller.coef < self.ambient_brightness_coef): self.device_controller.coef = self.ambient_brightness_coef
def set(self, r, g, b): """ Instantly set color """ self.r = r self.g = g self.b = b self.update_color(self.r, self.g, self.b) def update_color(self, r, g, b): """ Update and render colors """ logging.pprint(f"setting color to {r},{g},{b}", 5) #if(self.delay): # time.sleep(self.delay) r = int(r * self.coef) g = int(g * self.coef) b = int(b * self.coef) if (r > 255): r = 255 if (g > 255): g = 255 if (b > 255): b = 255 if (r < 0): r = 0 if (g < 0): g = 0 if (b < 0): b = 0 self.set_ECIO_color(r, g, b)
def send(self, data): """ Send feature report to HID device """ logging.pprint(f"Sending: {data}", 6) return self.hid_dll.HidD_SetFeature(int(self.device.hid_handle), create_string_buffer(data), len(data))
def __del__(self): """ Stop stream and close. Terminate pyaudio. """ logging.pprint("Stopping audio stream", 2) self.stream.stop_stream() self.stream.close() self.p.terminate()
def dfft_reduce(dfft: list, count: int = 25, reduction_coef: float = 50, top: int = 100, skip: int = 30) -> list: """ Reduce an fft result to "count" elements. This is done by summing "reduction_coef" many results into a single value. Whatever remains is ignored after count*reduction_coef elements. :param dfft: DFFT data array to reduce :param count: Number of partitions to split the spectrum into. Number of steps on the graph from 0hz to max hz. Default is 25. :param reduction_coef: Coefficient to to scale the output with. Default is 50. Sensitivity. :param top: Maximum value the graph will be scaled up to. Default is 100. :param skip: Size of partitions to skip between each partition. Larger is faster, but less precise. :rtype: list :return: A list of audio output after fast fourier transform """ simplified = [] sumt = 0 t = int(top / count) for j in range(count): for k in range(reduction_coef): if dfft[skip + t * j + k] > 0: sumt += dfft[skip + t * j + k] simplified.append(sumt) sumt = 0 logging.pprint(f"Simplified FFT: {simplified}", 6) return simplified
def generateKeyboardAnimation(self, binary_file: BinaryFile): logging.pprint(f"Generating Keyboard Animation", 4) try: animation = ChromaAnimation() animation.FPS = binary_file.BHeader.hFPS for i in range(0, len(binary_file.FrameList)): for j in range(0, len(binary_file.FrameList[i].DeviceList)): temp = [[ChromaColor(red=0, green=0, blue=0) for x in range(22)] for y in range(6)] if (i > 0): for row in range(0, len(animation.Frames[i - 1])): for col in range(0, len(animation.Frames[i - 1][row])): red, green, blue = animation.Frames[i - 1][row][col].getRGB() temp[row][col].set(red=red, blue=blue, green=green) if (binary_file.FrameList[i].DeviceList[j].DeviceHeader.dhDevice == 1): for k in range(0, len(binary_file.FrameList[i].DeviceList[j].DeviceDataList)): color = binary_file.FrameList[i].DeviceList[j].DeviceDataList[k].dABGR red = (color >> 0) & 255 green = (color >> 8) & 255 blue = (color >> 16) & 255 temp[binary_file.FrameList[i].DeviceList[j].DeviceDataList[k].dRow][ binary_file.FrameList[i].DeviceList[j].DeviceDataList[k].dCol].set(red=red, green=green, blue=blue) animation.Frames.append(temp) return animation except: # TODO Add proper exception handling logging.pprint('Unexpected Error!') raise
def __init__(self, uri: str, maxLED: int = 0): logging.pprint(f"Initializing {self.__class__.__name__}", 2) self._MaxLED = maxLED self._ColorGrid = [ ChromaColor(red=0, green=0, blue=0) for x in range(self._MaxLED) ] self.base_URI = uri self._URI = ""
def reset_colors(self): """ Reset colors to default """ logging.pprint(f"Resetting colors", 2) self.send(rgb_to_hid_buf(0xff, 0x00, 0x00, 1)) self.send(rgb_to_hid_buf(0xff, 0x1e, 0x00, 2)) self.send(rgb_to_hid_buf(0xff, 0x64, 0x00, 3)) self.send(rgb_to_hid_buf(0x00, 0x64, 0x00, 4)) self.send(rgb_to_hid_buf(0x00, 0x00, 0x50, 5)) self.send(rgb_to_hid_buf(0x00, 0x64, 0x00, 6)) self.send(rgb_to_hid_buf(0xff, 0x00, 0x50, 7))
def raw_read(self) -> list: """Read chunk sized data from stream :return: audio data array in format np.int16 """ logging.pprint("Reading data...", 6) in_data = self.stream.read(self.CHUNK) # Format audio data logging.pprint("Formatting data...", 6) audio_data = np.fromstring(in_data, np.int16) return audio_data
def render(self, r=None, g=None, b=None): """Set keyboard to flat color. If either of r/g/b values are given, all the keys will be set to the same color. If not, stored colors will be used. For each row of keyboard, send feature request: b"\\x00\\x16\\x00" + bytes([i]) + b"\\x00\\x00\\x00\\x00\\x00" And send the corresponding color data through output report. Finalize with a render request: b"\\x00\\x16\\x12\\x00\\x00\\x08\\x01\\x00\\x00\\x00" """ logging.pprint(f"Setting flat color", 4) #set r/g/b values if specified if (r): logging.pprint(f"Red color given, will fill the keyboard", 5) for i in range(6): for j in range(21): self.red[i][j] = r if (g): logging.pprint(f"Green color given, will fill the keyboard", 5) for i in range(6): for j in range(21): self.green[i][j] = g if (b): logging.pprint(f"Blue color given, will fill the keyboard", 5) for i in range(6): for j in range(21): self.blue[i][j] = b #send set color request self.device.send( mode_to_hid_buf(mode=LightingMode.FLAT_COLOR, speed=48, brightness=48)) #for each of the keyboard rows, send notification via feature request, and send the corresponding color data d = self.make_keyboard_buffer() for i in range(6): self.device.send(b"\x00\x16\x00" + bytes([i]) + b"\x00\x00\x00\x00\x00") logging.pprint(f"col {i}: {d[i]}", 5) self.device.output.send(d[i]) #render colors self.device.send(b"\x00\x16\x12\x00\x00\x08\x01\x00\x00\x00")
def update_color(self, r, g, b): """ Update and render colors """ #print(r,g,b) logging.pprint(f"setting color to {r},{g},{b}", 5) #if(self.delay): # time.sleep(self.delay) r = int(r * self.coef) g = int(g * self.coef) b = int(b * self.coef) if (r > 255): r = 255 if (g > 255): g = 255 if (b > 255): b = 255 if (r < 0): r = 0 if (g < 0): g = 0 if (b < 0): b = 0 self.set_ECIO_color(r, g, b)
def make_keyboard_buffer(self): """Convert self.red, self.green and self.blue to keyboard buffer needed for interface key values are written to output report, while updates are done via features report. Following will be written over USB for each row (bottom-up): b"\\x00\\x00" + bytes(self.blue[i]) + bytes(self.green[i]) + bytes(self.red[i]) """ ret = [] logging.pprint(f"Creating Keyboard RGB Matrix", 5) # set 6x21 array for return buffer buf = b"" for i in range(6): buf = b"\x00\x00" + bytes(self.blue[i]) + bytes( self.green[i]) + bytes(self.red[i]) ret.append(buf) return ret
def find_device(self, name: str) -> object: """ Find device with the given name :param name: Name of the audio device to find. :return: Audio device :rtype: object """ # Enum sound devices info = self.p.get_host_api_info_by_index(0) numdevices = info.get('deviceCount') for i in range(0, numdevices): # print all device names if (self.p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0: logging.pprint( f"Input Device id {i} - {self.p.get_device_info_by_host_api_device_index(0, i).get('name')}", 6) for i in range(self.p.get_device_count()): # check if device name starts with expected device name if (self.p.get_device_info_by_index(i)["name"].startswith(name)): return self.p.get_device_info_by_index(i)["index"]
def process_audio(self, audio_data: list, count: int, reduction: float) -> list: """ Process raw audio data,apply the reduction and partition steps and perform fast fourier on it. :param audio_data: Audio data to process. :param count: Number of partitions to split the data in :param reduction: Reduction coefficient :return: DFFT audio data. """ try: # Fast Fourier Transform, 10*log10(abs) is to scale it to dB # and make sure it's not imaginary logging.pprint("Performing FFT...", 6) dfft = 10. * np.log10(abs(np.fft.rfft(audio_data) / len(audio_data))) dfft = self.dampen(dfft) except: # if something went wrong (most likely division by 0) send last output and hope it doesn't happen again. # Yes. This module runs on hopes and dreams. return self.last self.last = AudioController.dfft_reduce(dfft, count, reduction) return self.last
def __init__(self, aud_format: int = pyaudio.paInt16, channels: int = 1, rate: int = 48000, device_name: str = "pulse", dampen: float = -25) -> object: # Pyaudio parameters self.FORMAT = aud_format self.CHANNELS = channels self.RATE = rate self.CHUNK = 1024 self.device_name = device_name self.p = pyaudio.PyAudio() self.last = [] self.dampen_coef = dampen logging.pprint("Starting audio stream", 4) self.stream = self.p.open(format=self.FORMAT, channels=self.CHANNELS, rate=self.RATE, input=True, input_device_index=self.find_device(device_name), frames_per_buffer=self.CHUNK) self.stream.start_stream()
async def rainbow_fade(self, c): """ Rainbow Fade effect """ logging.pprint(f"Starting rainbow fade effect.", 2) r = 0xff g = 0 b = 0 while True: for g in range(0, 0x1e): self.render(r=r, g=g, b=b) for g in range(0x1e, 0x50): self.render(r=r, g=g, b=b) for r in range(0xff, 0x00, -1): self.render(r=r, g=g, b=b) for b in range(0x00, 0x50): self.render(r=r, g=g - b, b=b) g = 0 for r in range(0x00, 0xff): self.render(r=r, g=g, b=b) for b in range(0x50, 0x00, -1): self.render(r=r, g=g, b=b)
def setCustomKey(self, key=None, keys=None): try: if keys is not None: for item in keys: row = int(item._Key, 16) >> 8 col = int(item._Key, 16) & 0xFF red, green, blue = item._Color.getRGB() self._ColorGrid[row][col].set(red=red, green=green, blue=blue) if key is not None: row = int(int(key._Key, 16) >> 8) col = int(int(key._Key, 16) & 0xFF) red, green, blue = key._Color.getRGB() self._ColorGrid[row][col].set(red=red, green=green, blue=blue) return True except: # TODO Add proper exception handling logging.pprint('Unexpected Error!') raise
def Version(self): try: logging.pprint("Getting Version", 4) v = requests.get( url='http://localhost:54235/razer/chromasdk').json()['version'] logging.pprint(f"Chroma SDK Version: {v}", 4) return except: # TODO Add proper exception handling logging.pprint('Unexpected Error!') raise
def setEffect(self, effect: str, param=None): logging.pprint(f"Setting {self.__class__.__name__} effect to {effect}", 5) logging.pprint(f"Param: {param}", 6) try: data = {"effect": effect} if (param): data["param"] = param return checkresult(requests.put(url=self.URI, json=data).json()) except: # TODO Add proper exception handling logging.pprint('Unexpected Error!') raise
def await_session(self): self.negotiate_session(self.data) #attempt 3 times at session negotiation and give up if it fails. for i in range(3): try: requests.get(self.URI) logging.pprint("Session started", 1) time.sleep(0.25) return except: logging.pprint("Timeout reached while waiting for session.") return self.await_session() logging.pprint( "All renegotiations failed. Cannot start chromaApp session.")
import allogate as logging logging.VERBOSITY = 1 print(f"Verbosity = {logging.VERBOSITY}") logging.pprint(f"Hello, this is a failure", 0) logging.pprint(f"Hello, this is a success", 1) logging.pprint(f"Hello, this is a warning", 2) logging.pprint(f"Hello, this is an info", 3) logging.pprint(f"Hello, this is verbose", 4) logging.pprint(f"Hello, this is very verbose", 12) logging.VERBOSITY = 3 print(f"Verbosity = {logging.VERBOSITY}") logging.pprint(f"Hello, this is a failure", 0) logging.pprint(f"Hello, this is a success", 1) logging.pprint(f"Hello, this is a warning", 2) logging.pprint(f"Hello, this is an info", 3) logging.pprint(f"Hello, this is verbose", 4) logging.pprint(f"Hello, this is very verbose", 12) logging.VERBOSITY = 5 print(f"Verbosity = {logging.VERBOSITY}") logging.pprint(f"Hello, this is a failure", 0) logging.pprint(f"Hello, this is a success", 1) logging.pprint(f"Hello, this is a warning", 2) logging.pprint(f"Hello, this is an info", 3) logging.pprint(f"Hello, this is verbose", 4) logging.pprint(f"Hello, this is very verbose", 12) logging.VERBOSITY = 15 print(f"Verbosity = {logging.VERBOSITY}")
def __init__(self, vendor_id=0x048d, product_id=0xce00, index=1): logging.pprint( f"vendor_id:{hex(vendor_id)}, product_id:{hex(product_id)}", 3) #find hid device filter = hid.HidDeviceFilter(vendor_id=vendor_id, product_id=product_id) self.hid_devices = filter.get_devices() logging.pprint("HID devices (filtered): ", 3) for device in self.hid_devices: logging.pprint(f"\t{device}", 3) self.device = self.hid_devices[index] #hook hid.dll from system32 self.hid_dll = WinDLL("hid") #target_usage = hid.get_full_usage_id(0xff03, 0x01) #print(hex(target_usage)) self.device.open() logging.pprint( "Opened HID device with handle: {self.device.hid_handle}", 3) #open all reports self.report = self.device.find_any_reports() self.output = self.report[1][0] logging.pprint(f"Input reports:{self.report[0]}", 3) logging.pprint(f"Output reports:{self.report[1]}", 3) logging.pprint(f"Feature reports:{self.report[2]}", 3) logging.pprint(f"{self.report[0][0]}", 3) logging.pprint(f"{self.report[1][0]}", 3) logging.pprint(f"{self.report[2][0]}", 3)
def negotiate_session(self, data): logging.pprint("Sending request to /razer/chromasdk", 4) response = requests.post(url=self.url, json=data) logging.pprint("Received response from /razer/chromasdk", 4) self.SessionID, self.URI = response.json()['sessionid'], response.json( )['uri']
def __del__(self): logging.pprint("Shutting down Chroma App.", 6) self.heartbeat.stop() requests.delete(self.URI)
def __init__(self, Info: ChromaAppInfo): try: self.url = 'http://localhost:54235/razer/chromasdk' self.data = { "title": Info.Title, "description": Info.Description, "author": { "name": Info.DeveloperName, "contact": Info.DeveloperContact }, "device_supported": Info.SupportedDevices, "category": Info.Category } #wait for session to fully initialize self.await_session() logging.pprint(f"URI: {self.URI}", 5) logging.pprint("Initializing heartbeat", 4) self.heartbeat = Heartbeat(self.URI) logging.pprint("Initializing keyboard", 4) self.Keyboard = Keyboard(self.URI) logging.pprint("Initializing mouse", 4) self.Mouse = Mouse(self.URI) logging.pprint("Initializing mousepad", 4) self.Mousepad = Mousepad(self.URI) logging.pprint("Initializing headset", 4) self.Headset = Headset(self.URI) logging.pprint("Initializing chromalink", 4) self.ChromaLink = ChromaLink(self.URI) logging.pprint("Initializing chromaBcaHandler", 4) self.BcaHandler = ChromaBcaHandler() except: logging.pprint("ChromaApp Crashed.", 0) raise
def decode(self, filename: str): logging.pprint(f"Decode called for {filename}", 4) try: with open(filename, "rb") as f: binary_file = BinaryFile() """Reading FileHeader""" logging.pprint(f"Unpacking fileHeader: {filename}", 5) binary_file.FHeader.ftype = struct.unpack("<H", f.read(2))[0] binary_file.FHeader.fsize = struct.unpack("<L", f.read(4))[0] binary_file.FHeader.fReserved = struct.unpack("<L", f.read(4))[0] binary_file.FHeader.fBcaOffset = struct.unpack("<L", f.read(4))[0] """Reading BCAHeader""" logging.pprint(f"Unpacking BCAHeader: {filename}", 5) binary_file.BHeader.hSize = struct.unpack("<L", f.read(4))[0] binary_file.BHeader.hVersion = struct.unpack("<H", f.read(2))[0] binary_file.BHeader.hFrameOffset = struct.unpack("<L", f.read(4))[0] binary_file.BHeader.hFPS = struct.unpack("<H", f.read(2))[0] binary_file.BHeader.hFrameCount = struct.unpack("<L", f.read(4))[0] binary_file.BHeader.hReserved = struct.unpack("<H", f.read(2))[0] """Reading Frame""" logging.pprint(f"Reading Frame: {filename}", 5) for h in range(0, int(binary_file.BHeader.hFrameCount)): binary_file.FrameList.append(BinaryFrame()) for i in range(0, int(binary_file.BHeader.hFrameCount)): """Reading FrameHeader""" binary_file.FrameList[i].FrameHeader.fhSize = struct.unpack("<H", f.read(2))[0] binary_file.FrameList[i].FrameHeader.fhDeviceCount = struct.unpack("<H", f.read(2))[0] for j in range(0, binary_file.FrameList[i].FrameHeader.fhDeviceCount): binary_file.FrameList[i].DeviceList.append(BinaryDevice()) binary_file.FrameList[i].FrameHeader.fhDataSize = struct.unpack("<H", f.read(2))[0] """Reading Frame Data""" for j in range(0, len(binary_file.FrameList[i].DeviceList)): binary_file.FrameList[i].DeviceList[j].DeviceHeader.dhSize = struct.unpack("<B", f.read(1))[0] binary_file.FrameList[i].DeviceList[j].DeviceHeader.dhDatatype = struct.unpack("<B", f.read(1))[0] binary_file.FrameList[i].DeviceList[j].DeviceHeader.dhDevice = struct.unpack("<H", f.read(2))[0] binary_file.FrameList[i].DeviceList[j].DeviceHeader.dhDataSize = struct.unpack("<H", f.read(2))[0] dataCount = int(binary_file.FrameList[i].DeviceList[j].DeviceHeader.dhDataSize / 6) for k in range(0, dataCount): binary_file.FrameList[i].DeviceList[j].DeviceDataList.append(BinaryFile.DeviceData()) """Reading Device Data""" for k in range(0, len(binary_file.FrameList[i].DeviceList[j].DeviceDataList)): binary_file.FrameList[i].DeviceList[j].DeviceDataList[k].dRow = struct.unpack("<B", f.read(1))[0] binary_file.FrameList[i].DeviceList[j].DeviceDataList[k].dCol = struct.unpack("<B", f.read(1))[0] binary_file.FrameList[i].DeviceList[j].DeviceDataList[k].dABGR = struct.unpack("<L", f.read(4))[0] return binary_file except: # TODO Add proper exception handling logging.pprint('Unexpected Error!') raise
def test_function(): logging.pprint(f"Hello, this is a failure", 0) logging.pprint(f"Hello, this is a success", 1) logging.pprint(f"Hello, this is a warning", 2) logging.pprint(f"Hello, this is an info", 3) logging.pprint(f"Hello, this is verbose", 4) logging.pprint(f"Hello, this is very verbose", 12)