Browse Source

added classes

subDesTagesMitExtraKaese 4 years ago
parent
commit
5eff759970
10 changed files with 417 additions and 139 deletions
  1. 220 0
      audioHandler.py
  2. 116 0
      connection.py
  3. 29 0
      graphic-eq.py
  4. BIN
      image.png
  5. 3 3
      include/config.hpp
  6. 0 96
      serial-led.py
  7. 4 2
      src/config.cpp
  8. 10 12
      src/main.cpp
  9. 30 22
      src/redFlyClient.cpp
  10. 5 4
      src/serialClient.cpp

+ 220 - 0
audioHandler.py

@@ -0,0 +1,220 @@
+import pyaudio
+import wave
+import os
+
+import numpy as np
+import struct
+
+class Listener:
+	def __init__(self, dataTime = 1/20, agcTime = 10, input = False):
+		self._dataTime = dataTime
+		self._agcTime = agcTime
+		self._input = input
+		self.buffersize = 512
+		self.p = pyaudio.PyAudio()
+		
+		self.left = self.right = self.fft = []
+		
+		self._agcMaxima = [0]
+		self._agcIndex = 0
+		self._agcLen = 0
+		self._beatCb = None
+		self._sampleRate = 0
+		self._hasNewData = False
+		
+	def start(self, dev = None):
+		self._device = dev or self.getDefaultOutputDeviceInfo()
+			
+		if self._device == None:
+			print("no device found")
+			return
+			
+		print("device name: {} channels: {} defaultSampleRate: {}".format(self._device["name"], self._device["channels"], self._device["defaultSampleRate"]))
+			
+		if self._sampleRate != self._device["defaultSampleRate"]:
+			self._sampleRate = self._device["defaultSampleRate"]
+			self.buffersize = int(self._sampleRate * self._dataTime)
+			self.fft = self.right = self.left = np.ndarray((self.buffersize))
+			
+			self._agcLen = int(1 / self._dataTime * self._agcTime)
+			self._agcMaxima = np.ndarray((self._agcLen))
+			self._agcMaxima.fill(2**15 * 0.1)
+			self._agcIndex = 0
+			self._lastBeatTime = 0
+			self.meanAmp = 2**15 * 0.1
+		
+		try:
+			self._stream = self.openStream(self._device)
+		except OSError:
+			self._stream = None
+		
+		if not self._stream:
+			print("stream open failed")
+			return
+		
+		self._stream.start_stream()
+		
+		return self._stream.is_active()
+		
+	def stop(self):
+		if not self._stream:# or not self._stream.is_active():
+			return False
+		self._stream.stop_stream()
+		return True
+		
+	def setBeatCb(self, cb):
+		self._beatCb = cb
+	
+	def getDefaultOutputDeviceInfo(self):
+		#Set default to first in list or ask Windows
+		try:
+			self.p.terminate()
+			self.p.__init__()
+			if self._input:
+				info = self.p.get_default_input_device_info()
+			else:
+				info = self.p.get_default_output_device_info()
+		except IOError:
+			info = None
+		
+		#Handle no devices available
+		if info == None:
+				print ("No device available.")
+				return None
+
+		if (self.p.get_host_api_info_by_index(info["hostApi"])["name"]).find("WASAPI") == -1:
+			for i in range(0, self.p.get_device_count()):
+				x = self.p.get_device_info_by_index(i)
+				is_wasapi = (self.p.get_host_api_info_by_index(x["hostApi"])["name"]).find("WASAPI") != -1
+				if x["name"].find(info["name"]) >= 0 and is_wasapi:
+					info = x
+					break
+
+		#Handle no devices available
+		if info == None:
+			print ("Device doesn't support WASAPI")
+			return None
+			
+		info["channels"] = info["maxInputChannels"] if (info["maxOutputChannels"] < info["maxInputChannels"]) else info["maxOutputChannels"]
+			
+		return info
+		
+	def openStream(self, dev):
+	
+		is_input = dev["maxInputChannels"] > 0
+		is_wasapi = (self.p.get_host_api_info_by_index(dev["hostApi"])["name"]).find("WASAPI") != -1
+		
+		#print("is input: {} is wasapi: {}".format(is_input, is_wasapi))
+
+		if not is_input and not is_wasapi:
+			print ("Selection is output and does not support loopback mode.")
+			return None
+		if is_wasapi:
+			stream = self.p.open(
+				format = pyaudio.paInt16,
+				channels = (dev["channels"] if dev["channels"] < 2 else 2),
+				rate = int(self._sampleRate),
+				input = True,
+				frames_per_buffer = self.buffersize,
+				input_device_index = dev["index"],
+				stream_callback=self.streamCallback,
+				as_loopback = False if is_input else is_wasapi)
+		else:
+			stream = self.p.open(
+				format = pyaudio.paInt16,
+				channels = (dev["channels"] if dev["channels"] < 2 else 2),
+				rate = int(self._sampleRate),
+				input = True,
+				frames_per_buffer = self.buffersize,
+				input_device_index = dev["index"],
+				stream_callback=self.streamCallback)
+		return stream
+		
+	def closeStream(self):
+		if not self._stream:
+			return False
+		self._stream.close()
+		return True
+			
+	def streamCallback(self, buf, frame_count, time_info, flag):
+		self._buf = buf
+		arr = np.array(struct.unpack("%dh" % (len(buf)/2), buf))
+		
+		mx = arr.max()
+		self._agcIndex += 1
+		if self._agcIndex >= self._agcLen:
+			self._agcIndex = 0
+		self._agcMaxima[self._agcIndex] = mx
+			
+		self.meanAmp = np.mean(np.absolute(self._agcMaxima))
+		
+		if self.meanAmp > 2**15 * 0.02:
+			amp = 1 / self.meanAmp
+		else:
+			amp = 1 / (2**15 * 0.02)
+		
+		if self._device["channels"] >= 2:
+			self.left, self.right  = arr[::2]  * amp, arr[1::2] * amp
+			self.fft = self.fftCalc((self.left+self.right)/2)
+		else:
+			self.left = self.right = arr * amp
+			self.fft = self.fftCalc(self.left)
+		
+		self._hasNewData = True
+		
+		self.agcFFT = np.fft.rfft(self._agcMaxima, self.beatnFFT) / self.beatnFFT
+		
+		if self._beatCb and mx * (time_info["current_time"] - self._lastBeatTime) > m * 0.5:
+			self._lastBeatTime = time_info["current_time"]
+			self._beatCb(self.fft)
+			
+		return (None, pyaudio.paContinue)
+	
+	def hasNewData(self):
+		if not self._hasNewData:
+			return False
+		self._hasNewData = False
+		return True
+	
+	def getSampleRate(self):
+		return int(self._sampleRate)
+	
+	def getAgc(self):
+		return self.meanAmp / 2**15
+		
+	def getVolume(self):
+		if self._agcMaxima.sum() == 0:
+			return 0
+		return self._agcMaxima[self._agcIndex] / self.meanAmp
+	
+	def isActive(self):
+		if not self._stream:
+			return False
+		return self._stream.is_active()
+	
+	def fftSetLimits(self, nFFT, fMin, fMax):
+		self.nFFT = nFFT
+		self.fftMin = int(fMin / self._sampleRate * nFFT)
+		self.fftMax = int(fMax / self._sampleRate * nFFT)
+		print("nFFT: {} \tfftMin: {} \tfftMax: {}".format(self.nFFT, self.fftMin, self.fftMax))
+
+	def agcFftSetLimits(self, fMin, fMax):
+		self.beatnFFT = self._agcLen
+		self.beatFftMin = int(fMin * self._dataTime * self.beatnFFT)
+		self.beatFftMax = int(fMax * self._dataTime * self.beatnFFT)
+		print("beat nFFT: {} \tfftMin: {} \tfftMax: {}".format(self.beatnFFT, self.beatFftMin, self.beatFftMax))
+		
+	def fftCalc(self, data):
+		return abs(np.fft.rfft(data, self.nFFT)[self.fftMin:self.fftMax]) / self.nFFT
+							
+	def fftGroup(self, fft, limits):
+		groups = []
+		for freqs in zip(limits, limits[1:]):
+			a = int(freqs[0] / self._sampleRate * self.nFFT)
+			b = int(freqs[1] / self._sampleRate * self.nFFT)
+			#groups.append(sum(fft[a:b]) / (b-a) if (b-a) > 0 else 0)
+			if b != a:
+				groups.append(max(fft[a:b]))
+			else:
+				groups.append(fft[a])
+		return groups

+ 116 - 0
connection.py

@@ -0,0 +1,116 @@
+#!/bin/python3
+
+import sys
+import time
+import math
+import socket
+import serial
+
+N_ROWS = 5
+N_COLS = 10
+
+class SerialConnection:
+  def __init__(self):
+    self._ser = None
+    self.port = None
+    
+  def open(self, port = None):
+    self.port = port
+
+    if self.port == None:
+      for port in ["/dev/ttyUSB{}".format(p) for p in range(4)]:
+        try:
+          self._ser = serial.Serial(port, 38400)
+          print("connected to " + port)
+          self.port = port
+          break
+        except serial.SerialException:
+          pass
+      
+    if self.port == None:
+      for port in ["COM{}".format(p) for p in range(3,20)]:
+        try:
+          self._ser = serial.Serial(port, 38400)
+          print("connected to " + port)
+          self.port = port
+          break
+        except serial.SerialException:
+          pass
+    else:
+      try:
+        self._ser = serial.Serial(self.port, 38400)
+        print("connected to " + self.port)
+      except serial.SerialException:
+        pass
+    if self._ser:
+      self._ser.write(b'A' + b'\15' * N_ROWS*N_COLS)
+    else:
+      print("connection to port {} failed".format(port))
+    
+  def send(self, bytes):
+    if self._ser:
+      try:
+        return self._ser.write(bytes)
+      except serial.SerialException:
+        self._ser = None
+    if not self._ser:
+      self.open()
+      if not self._ser:
+        time.sleep(30)
+          
+  def read(self, n=64):
+    if self._ser:
+      try:
+        return self._ser.read(n)
+      except serial.SerialException:
+        self._ser = None
+    if not self._ser:
+      self.open()
+
+  def isConnected(self):
+    return self._ser != None
+
+  def close(self):
+    if self._ser:
+      self._ser.close()
+
+
+class IPConnection:
+  def __init__(self):
+    self.oldPing = time.time()
+    self._sock = None
+    self.server_address = None
+    self.client_address = None
+
+  def open(self, port = 2020):
+    self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    self.server_address = ('0.0.0.0', port)
+    print('binding {} port {}'.format(*self.server_address))
+    self._sock.bind(self.server_address)
+    self._sock.settimeout(30)
+
+  def send(self, bytes):
+    if not client_address:
+      try:
+        print('waiting...')
+        data, addr = self._sock.recvfrom(16)
+        print('new connection from {}'.format(data, addr))
+        client_address = addr
+      except socket.timeout:
+        pass
+    else:
+      self._sock.sendto(bytes, client_address)
+      if time.time() - oldPing > 30:
+        try:
+          data, addr = self._sock.recvfrom(16)
+          print('received {} from {}'.format(data, addr))
+          client_address = addr
+          oldPing = time.time()
+        except socket.timeout:
+          client_address = None
+
+  def isConnected(self):
+    return self.client_address != None
+
+  def close(self):
+    self._sock.close()

+ 29 - 0
graphic-eq.py

@@ -0,0 +1,29 @@
+from connection import *
+from audioHandler import Listener
+import numpy as np
+import time
+
+matrix = SerialConnection()
+matrix.open()
+
+pitches = [(2**(1/12))**(n/11*12*7-24) * 220 for n in range(11)]
+audio = Listener(1/30, 30)
+audio.start()
+nFFT = audio.buffersize
+audio.fftSetLimits(nFFT, min(pitches), max(pitches))
+audio.agcFftSetLimits(60/60, 300/60)
+
+while True:
+  if audio.hasNewData():
+    left, right, fft = np.copy(audio.left), np.copy(audio.right), np.copy(audio.fft)
+    
+    sums = audio.fftGroup(fft, pitches)
+    data = [0] * 50
+    for row in range(N_ROWS):
+      for col in range(N_COLS):
+        val = 15 if sums[col] > ((N_ROWS-row)/N_COLS/2) else 0
+        data[row*N_COLS+col]= val
+    print(data)
+    matrix.send(b'A' + bytes(data))
+
+    time.sleep(1/40)

BIN
image.png


+ 3 - 3
include/config.hpp

@@ -7,9 +7,9 @@
 #include <EEPROM.h>
 
 struct config_t {
-  char ssid[32];
-  char pass[32];
-  char host[32];
+  char ssid[16];
+  char pass[16];
+  char host[24];
   int port;
 };
 

+ 0 - 96
serial-led.py

@@ -1,96 +0,0 @@
-#!/bin/python3
-
-import sys
-import time
-import math
-import serial
-
-N_ROWS = 5
-N_COLS = 10
-
-class SerialConnection:
-	def __init__(self):
-		self._ser = None
-		self.port = None
-		
-	def connect(self, port = None):
-		if port == None:
-			port = self.port
-
-		if port == None:
-			for port in ["/dev/ttyUSB{}".format(p) for p in range(4)]:
-				try:
-					self._ser = serial.Serial(port, 38400)
-					print("connected to " + port)
-					self.port = port
-					break
-				except serial.SerialException:
-					pass
-			
-		if port == None:
-			for port in ["COM{}".format(p) for p in range(3,20)]:
-				try:
-					self._ser = serial.Serial(port, 38400)
-					print("connected to " + port)
-					self.port = port
-					break
-				except serial.SerialException:
-					pass
-		else:
-			try:
-				self._ser = serial.Serial(port, 38400)
-				print("connected to " + port)
-				self.port = port
-			except serial.SerialException:
-				pass
-		if self._ser:
-			self._ser.write(b'A' + b'\15' * N_ROWS*N_COLS)
-		else:
-			print("connection to port {} failed".format(port))
-		
-	def send(self, pixels):
-		if not self._ser:
-			return
-		try:
-			self._ser.write(b'A')
-			self._ser.write(bytes(pixels))
-		except serial.SerialException:
-			self._ser = None
-		
-	def isConnected(self):
-		return self._ser != None
-
-
-oldTime = time.time()
-oldPing = time.time()
-
-def getBytes():
-  pixels = [0] * (N_ROWS * N_COLS)
-  for row in range(N_ROWS):
-    for col in range(N_COLS):
-      pixels[row*N_COLS + col] = int(math.sin(row + col + time.time()*15) * 8 + 8)
-
-  pixels[49] = int(pixels[49]/2)
-  return bytes([0x41] + pixels)
-
-client_address = None
-try:
-  while True:
-    if not client_address:
-      data, addr = sock.recvfrom(16)
-      print('received {} from {}'.format(data, addr))
-      client_address = addr
-    else:
-      sock.sendto(getBytes(), client_address)
-      if time.time() - oldPing > 30:
-        data, addr = sock.recvfrom(16)
-        print('received {} from {}'.format(data, addr))
-        client_address = addr
-        oldPing = time.time()
-
-    oldTime = time.time()
-    time.sleep(1/40)
-
-finally:
-    print('closing socket')
-    sock.close()

+ 4 - 2
src/config.cpp

@@ -3,9 +3,11 @@
 config_t config;
 
 void config_init() {
-  EEPROM.get(0, config);
+  for(byte i=0; i<sizeof(config); i++)
+    *(config.ssid+i) = EEPROM.read(i);
 }
 
 void config_save() {
-  EEPROM.put(0, config);
+  for(byte i=0; i<sizeof(config); i++)
+    EEPROM.write(i, *(config.ssid+i));
 }

+ 10 - 12
src/main.cpp

@@ -8,36 +8,31 @@
 #include "redFlyClient.hpp"
 #include "serialClient.hpp"
 
+char mode = 0;
+unsigned long startS = 0, startW = 0;
+
 void setup() {
   pinMode(3, OUTPUT);
   digitalWrite(3, HIGH);
 
   Serial.begin(38400);
-  Serial.flush();
   printf("boot\n");
-
-  delay(100);
   
   wdt_enable(WDTO_8S);
-
-  matrix_init();
   config_init();
-
+  matrix_init();
   
+  delay(100);
 
   if(RedFly.init(38400, HIGH_POWER)) {
     debugout("INIT ERR\n"); //there are problems with the communication between the Arduino and the RedFly
-    matrix.bytes[0] ^= 0x0F;
-    delay(100);
-    asm volatile ("  jmp 0");
+    memset(matrix.bytes, 0xf, sizeof(matrix_t));
+    for(;;);
   }
 
   connect();
 }
 
-char mode = 0;
-unsigned long startS = 0, startW = 0;
-
 void loop() {
   if(mode == 0) {
     uint8_t rssi = RedFly.getrssi();
@@ -72,7 +67,10 @@ void loop() {
   if(mode == 0 || mode == 'C'){
     RedFly.disable();
     Serial.println("NO DATA");
+    matrix.bytes[30] ^= 0xF;
     startS = millis();
+    wdt_reset();
+
     while(millis()-startS < 5000) {
       int8_t rc = readSerial();
       if(rc == 0) {

+ 30 - 22
src/redFlyClient.cpp

@@ -14,20 +14,6 @@ void debugout(const char* fmt, ...) {
   RedFly.enable();
 }
 
-void debugMatrix(const matrix_t& matrix) {
-  RedFly.disable();
-  char buffer[10];
-  Serial.println();
-  for(uint8_t i=0; i<N_ROWS; i++) {
-    for(uint8_t k=0; k<N_COLS; k++) {
-      sprintf(buffer, "%2d ", matrix.rows[i].bytes[k]);
-      Serial.write(buffer);
-    }
-    Serial.println();
-  }
-  RedFly.enable();
-}
-
 
 int8_t extractIpAddress(char *sourceString, byte *ipAddress) {
   byte len=0;
@@ -55,9 +41,18 @@ int8_t connect() {
   //init the WiFi module on the shield
   // ret = RedFly.init(br, pwr) //br=9600|19200|38400|57600|115200|200000|230400, pwr=LOW_POWER|MED_POWER|HIGH_POWER
 
+  wdt_reset();
+  {
+    size_t len = strnlen(config.ssid, sizeof(config.ssid));
+    if(len == 0 && len == sizeof(config.ssid)) {
+      debugout("CONF ERR\n");
+      return 1;
+    }
+  }
   RedFly.scan();
 
   wdt_reset();
+  matrix.bytes[20] ^= 0x0F;
 
   if(RedFly.join(config.ssid, config.pass, INFRASTRUCTURE)) {
     debugout("JOIN ERR\n");
@@ -65,6 +60,7 @@ int8_t connect() {
   }
 
   wdt_reset();
+  matrix.bytes[21] ^= 0x0F;
 
   if(RedFly.begin()) {
     debugout("BEGIN ERR\n");
@@ -73,23 +69,26 @@ int8_t connect() {
   }
 
   wdt_reset();
+  matrix.bytes[22] ^= 0x0F;
 
-  RedFly.getlocalip(ip);
-  debugout("local ip: %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]);
   if(extractIpAddress(config.host, ip) != 0) {
     if(RedFly.getip(config.host, ip) != 0) {
       debugout("DNS ERR\n");
       return 4;
     }
   }
-  client.connectUDP(ip, config.port);
 
+  wdt_reset();
+  matrix.bytes[23] ^= 0x0F;
+
+  client.connectUDP(ip, config.port, 2021);
+  client.write('C');
   return 0;
 }
 
 void ping() {
   if(client.connected()) {
-    client.write("hello");
+    client.write('A');
   }
 }
 
@@ -100,9 +99,18 @@ int8_t readUDP() {
   wdt_reset();
 
   if(!client.connected()) {
-    debugout("udp disconnected\n");
+    debugout("DC\n");
     client.stop(); //stop and reset server
-    client.connectUDP(ip, config.port);
+    matrix.bytes[22] ^= 0x0F;
+
+    if(extractIpAddress(config.host, ip) != 0) {
+      if(RedFly.getip(config.host, ip) != 0) {
+        debugout("DNS ERR\n");
+        return 4;
+      }
+    }
+    client.connectUDP(ip, config.port, 2021);
+    client.write('C');
     return 2;
   }
   if(len < N_ROWS*N_COLS+1) {
@@ -111,11 +119,11 @@ int8_t readUDP() {
 
   client.read(buf, 1);
   if(buf[0] != 'A') {
-    debugout("A\n");
+    client.write(buf[0]);
     return 1;
   }
 
-  len = client.read(matrix.bytes, N_ROWS*N_COLS);
+  client.read(matrix.bytes, N_ROWS*N_COLS);
   //sprintf((char*)buf, "%d\n", len);
   //client.write(buf, 3);
 

+ 5 - 4
src/serialClient.cpp

@@ -3,12 +3,12 @@
 int8_t readSerial() {
   uint16_t len = Serial.available();
   uint8_t buf[6];
- 	
-  wdt_reset();
 
   if(len < N_ROWS*N_COLS+1) {
     return -1;
   }
+
+  wdt_reset();
   
   Serial.readBytes(buf, 1);
   if(buf[0] != 'A') {
@@ -24,10 +24,11 @@ int8_t readSerial() {
       config.port = Serial.parseInt();
       if(config.port > 0) {
         wdt_reset();
+        memset(matrix.bytes, 0xF, sizeof(matrix_t));
+        matrix.bytes[14] = 0x0F;
         config_save();
         printf("Config saved.\n");
-        delay(100);
-        asm volatile ("  jmp 0");
+        for(;;);
         return 0;
       } else {
         return 3;