# Scroll pHat HD Spectrum Analyzer

**URL:** <https://forums.pimoroni.com/t/scroll-phat-hd-spectrum-analyzer/13266>\
**Category:** Projects\
**Created:** [March 10, 2020, 9:03pm UTC](https://forums.pimoroni.com/t/scroll-phat-hd-spectrum-analyzer/13266 "2020-03-10T21:03:47Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![colinj](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/colinj/32/969_2.png) [@colinj](https://forums.pimoroni.com/u/colinj)\
**Post date:** [March 10, 2020, 9:03pm UTC](https://forums.pimoroni.com/t/scroll-phat-hd-spectrum-analyzer/13266/1 "2020-03-10T21:03:47Z")

</div>

I recently ran across the [Build a spectrum analyser with Scroll pHAT and pHAT DAC](https://learn.pimoroni.com/tutorial/sandyj/scroll-phat-spectrum-analyser) tutorial and since I had the component parts I thought I would put one together. The one difference was that I had a Scroll pHat HD and so I had a lot more LEDs to play with. So I wanted to share, in case anyone is interested, my updated code for the Scroll pHat HD. I had to do some experimenting with the weighting and I had to guess about how to break up the power\_index ranges for the additional LEDs. I really don’t know much about Fast Fourier Transforms or audio so if folks have any thoughts on how I might break things up differently that would be awesome.

Mostly I just like the blinky lights

```
import sys
import wave
from struct import unpack

import alsaaudio as aa
import numpy as np
import scrollphathd

wavfile = wave.open(sys.argv[1], 'r')

sample_rate = wavfile.getframerate()
no_channels = wavfile.getnchannels()
chunk = 4096

output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
output.setchannels(no_channels)
output.setrate(sample_rate)
output.setformat(aa.PCM_FORMAT_S16_LE)
output.setperiodsize(chunk)

matrix = [0] * scrollphathd.DISPLAY_WIDTH
power = []
weighting = [1, 1, 2, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16]
#
# weighting using a Fibonacci sequence. Makes the right end very active
# weighting = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597]
#
scrollphathd.set_brightness(0.75)
scrollphathd.rotate(degrees=180)

def power_index(val):
    return int(2 * chunk * val / sample_rate)

def compute_fft(data, chunk, sample_rate):
    global matrix
    data = unpack("%dh" % (len(data) / 2), data)
    data = np.array(data, dtype='h')

    fourier = np.fft.rfft(data)
    fourier = np.delete(fourier, len(fourier) - 1)

    power = np.abs(fourier)
    matrix[0] = int(np.mean(power[power_index(0):power_index(156):1]))
    matrix[1] = int(np.mean(power[power_index(156):power_index(313):1]))
    matrix[2] = int(np.mean(power[power_index(313):power_index(625):1]))
    matrix[3] = int(np.mean(power[power_index(625):power_index(1000):1]))
    matrix[4] = int(np.mean(power[power_index(1000):power_index(1500):1]))
    matrix[5] = int(np.mean(power[power_index(1500):power_index(2000):1]))
    matrix[6] = int(np.mean(power[power_index(2000):power_index(2500):1]))
    matrix[7] = int(np.mean(power[power_index(2500):power_index(3000):1]))
    matrix[8] = int(np.mean(power[power_index(3000):power_index(3500):1]))
    matrix[9] = int(np.mean(power[power_index(3500):power_index(4000):1]))
    matrix[10] = int(np.mean(power[power_index(4000):power_index(4500):1]))
    matrix[11] = int(np.mean(power[power_index(4500):power_index(5000):1]))
    matrix[12] = int(np.mean(power[power_index(5000):power_index(5500):1]))
    matrix[13] = int(np.mean(power[power_index(5500):power_index(6000):1]))
    matrix[14] = int(np.mean(power[power_index(6000):power_index(6500):1]))
    matrix[15] = int(np.mean(power[power_index(6500):power_index(7000):1]))
    matrix[16] = int(np.mean(power[power_index(7000):power_index(8000):1]))

    matrix = np.divide(np.multiply(matrix, weighting), 1000000)
    matrix = matrix.clip(0, scrollphathd.DISPLAY_HEIGHT)
    matrix = [float(m) for m in matrix]

    return matrix

data = wavfile.readframes(chunk)

while data != '':
    output.write(data)
    matrix = compute_fft(data, chunk, sample_rate)
    scrollphathd.set_graph(matrix, 0, scrollphathd.DISPLAY_HEIGHT)
    scrollphathd.show()
    data = wavfile.readframes(chunk)
```

---

<div class="post-metadata">

**Author:** ![Frank](https://avatars.discourse-cdn.com/v4/letter/f/3d9bf3/32.png) [@Frank](https://forums.pimoroni.com/u/Frank)\
**Post date:** [December 30, 2020, 10:30pm UTC](https://forums.pimoroni.com/t/scroll-phat-hd-spectrum-analyzer/13266/2 "2020-12-30T22:30:30Z")

</div>

Hi Colin,

This is so cool. I was thinking of adapting your script so rather than using an audio file the spectrum analyzer would use the output from a text to speech

Here is my text to speech code. Any ideas where to start

from gtts import gTTS  
import os

#Reading the text file and store into object  
file = open(“draft.txt”, “r”).read().replace("\n", " ")

language = ‘en’

#Passing the text file into gTTS module and store into speech  
speech = gTTS(text = str(file), lang = language, slow = False)

#Saving the converted audio in a mp3 file named called ‘voice.mp3’  
speech.save(“voice.mp3”)

#Playing the mp3 file  
os.system(“start voice.mp3”)

---

<div class="post-metadata">

**Author:** ![colinj](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/colinj/32/969_2.png) [@colinj](https://forums.pimoroni.com/u/colinj)\
**Post date:** [August 10, 2024, 6:03am UTC](https://forums.pimoroni.com/t/scroll-phat-hd-spectrum-analyzer/13266/3 "2024-08-10T06:03:38Z")

</div>

Frank,

Apologies for not responding in a timely manner. Life happened in many ways ;-)

If I want to say that there might be a problem with reading mp3 files and that you might want to have it save it out to a WAV file.

colin j.
