Unfortunately I cannot remember exactly how I configured everything, but I have managed to locate the configuration files.
The first thing was to amend the asound.conf file to:
.asoundrc
ctl.equal {
type equal
}
pcm.plugequal1 {
type equal
slave.pcm “plughw:0,0”
#slave.pcm “turntable2” #no sound
}
pcm.equal {
type plug;
slave.pcm plugequal1;
#slave.pcm “turntable” #no difference
}
pcm.splitter1 {
type softvol
slave.pcm mono
control { name “PCM”; card 0;}
max_dB 50.0
}
pcm.mono {
type route
slave.pcm splitter2
ttable.0.0 1
ttable.1.1 1
}
pcm.splitter2 {
type plug;
slave.pcm {
type multi;
slaves.a.pcm “plugequal1”;
#slaves.a.pcm “turntable”; #no sound
slaves.b.pcm “loopout”;
#slaves.b.pcm “turntable2”; #no difference
slaves.a.channels 2;
slaves.b.channels 2;
bindings.0.slave a;
bindings.0.channel 0;
bindings.1.slave a;
bindings.1.channel 1;
bindings.2.slave b;
bindings.2.channel 0;
bindings.3.slave b;
bindings.3.channel 1;
}
ttable.0.0 1;
ttable.1.1 1;
ttable.0.2 1;
ttable.1.3 1;
}
pcm.output {
type hw
card ALSA
}
pcm.loopin {
type plug
slave.pcm “hw:Loopback,0”
#slave.pcm “turntable”
}
pcm.loopdevice {
type hw
card Loopback
device 1
}
pcm.turntable {
type hw
card Controller
device 0
}
pcm.turntable2 {
type plug
#slave.pcm turntable
#slave.channels 1
#ttable.0.0 0.5
#ttable.0.0 0.5
#ttable.1.0 0.5
#ttable.1.0 0.5
#type dsnoop
slave.pcm turntable
#slave.channels 2
#slave.period_size 1024
#slave.buffer_size 4096
#slave.rate 44100
#slave.periods 0
#slave.period_time 0
}
pcm.loopout {
type plug
slave.pcm “loopdevice”
#slave.pcm “turntable”
}
Then I wrote a python script to read the audio and produce an output.
#!/usr/bin/env python3
# BEGIN INIT INFO
# Provides: dickie_audio_service.py
# Required-Start: $all
# Required-Stop: $remote_fs $syslog
# Default-Start: 5
# Default-Stop: 0 1 6
# Short-Description: LED lighting script
# Description: DICKIE innovation addon to volumio Simple Equaliser Plugin
### END INIT INFO
# -*- coding: utf-8 -*-
"""
ÐICKIE innovation
Created on Tue Oct 23 20:55:56 2018
Copyright Dickie Innovation Ltd
"""
import numpy
import math
import pyaudio
import wave
import serial
import sys
import time
import struct
import datetime
freq_low = 20 #Hz
freq_high =20000 #Hz
scale = 50 # Change if too dim/bright
exponent = 5 # Change if too little/too much difference between loud and quiet sounds
main_exit = False
def serial_init():
print('Openning port at {0} baud'.format(19200))
try:
ser = serial.Serial('/dev/ttyACM0',19200, timeout=1 )
print('\tOpen on port /dev/ttyACM0')
except serial.SerialException:
ser = serial.Serial('/dev/ttyACM1', 19200, timeout=1 )
print('\tOpen on port /dev/ttyACM1')
time.sleep(1)
if not ser.isOpen():
ser.open()
ser.flushInput()
ser.flushOutput()
ser.write(b'\n')
#inline = ser.readline()
return ser
class Lighting():
def __init__(self, serial_link):
self.serial_link = serial_link
self.chunk_n = 11
self.chunk = 2**self.chunk_n # Change if too fast/slow, never less than 2**11
self.device = 1
self.samplerate = 22050 #44100
self.band_no = 8
self.frame_count = 0
self.freqs = numpy.fft.fftfreq(self.chunk,1/self.samplerate)
self.idxs = numpy.argsort(self.freqs)
self.drange = 1600
#self.drange[1] *= 0
#self.drange[2] *= 100
try:
print('\nOpenning Device:', self.device)
self.p = pyaudio.PyAudio()
self.dev = self.p.get_device_info_by_index(self.device)
print('No of Input channels', self.dev['maxInputChannels'])
print('No of Output channels', self.dev['maxOutputChannels'])
self.stream = self.p.open(format= pyaudio.paInt16,
#channels=dev['maxInputChannels'],
channels=1,
rate=44100,
input=True,
frames_per_buffer = self.chunk,
input_device_index = self.device,
stream_callback=self.callback)
except:
self.device = 2
print('\nOpenning Device:', self.device)
self.p = pyaudio.PyAudio()
self.dev = self.p.get_device_info_by_index(self.device)
print('No of Input channels', self.dev['maxInputChannels'])
print('No of Output channels', self.dev['maxOutputChannels'])
self.stream = self.p.open(format= pyaudio.paInt16,
#channels=dev['maxInputChannels'],
channels=1,
rate=44100,
input=True,
frames_per_buffer = self.chunk,
input_device_index = self.device,
stream_callback=self.callback)
start = datetime.datetime.now()
self.stream.start_stream()
while self.stream.is_active() :
time.sleep(1)
end = datetime.datetime.now()
rate = (end-start).seconds/self.frame_count
print('elapsed {0:5.4f} seconds per run, or {1:5.2f}Hz'.format(rate, 1/rate))
print('total count', self.frame_count)
#print('min/max', self.drange[1:])
def callback(self,in_data, frame_size, time_info, status):
self.frame_size = frame_size
#print('current status', status, len(in_data))
#data = wf.readframes(frame_count)
self.data = in_data
#print('frame count', frame_count)
self.calculate_levels()
levels_list = self.levels.tolist()
if status == pyaudio.paComplete:
print('pyaudio terminated')
elif status == pyaudio.paAbort:
print('pyaudio aborted- ignoring')
status = pyaudio.paContinue
if sum(levels_list) == 0:
print('levels zero')
main_exit = True
status = pyaudio.paComplete
levels_list.append(10)
#print(levels_list)
self.serial_link.write(bytearray(levels_list))
#inline = ser.readline()[:-2]
return (self.data, status)
def calculate_levels(self):
# Use FFT to calculate volume for each frequency
datalen = len(self.data)
# Convert raw sound data to Numpy array
fmt = "{0}H".format(datalen//2)
data2 = struct.unpack(fmt, self.data)
data2 = numpy.array(data2, dtype=numpy.int32)
# Apply FFT
fourier = numpy.fft.fft(data2)
#print(len(data),fourier.shape, chunk)
gaps = [0] + [2**x for x in range(self.chunk_n-self.band_no, self.chunk_n)]
fft = numpy.abs(fourier[:self.chunk]) #/ (128*chunk)
fft = fft[self.idxs]
levels = numpy.zeros((self.band_no+4), dtype=numpy.int32)
for band in range(self.band_no):
levels[band] = numpy.mean(fft[gaps[band]:gaps[band+1]])
levels[band] = numpy.max([1,levels[band]])
levels[8] = levels[0]
levels[9] = levels[1]
levels[10] = levels[0]
levels[11] = levels[2]
levels_a = (numpy.log(levels)-12)*150
#print([int(a) for a in levels_a])
#self.drange[1] = numpy.min(self.drange, axis=0)
#self.drange[2] = numpy.max(self.drange, axis=0)
#range_running = self.drange[2]-self.drange[1]
#levels = (levels_a)*255/self.drange
levels = numpy.array(levels_a, dtype=numpy.int16)
levels[levels<10] = 0
levels[levels>255 ] =255
#levels = levels.astype(numpy.uint8)
levels[levels==10] = 9
self.levels = levels
self.frame_count +=1
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
print('stopping stream')
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
del self.stream
del self.p
#del self.callback
if __name__ == "__main__":
ser = serial_init()
while True:
with Lighting(ser) as buray:
while buray.stream.is_active():
print('Buray running')
time.sleep(1)
Which is run as a service as soon as the raspberry Pi/ Volumio is started:
[Unit]
Description=DICKIEs lighting
After=volsimpleequal.service
[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/volumio/dickie_audio_service.py > /home/volumio/log.text 2>&1
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=volumio
User=volumio
Group=volumio
[Install]
WantedBy=multi-user.target
I hope this helps.