Vendo o Som 2 - Think (Information Society)

2024-12-21

"""2024-12-21
Vendo o Som 2 - Think (Information Society)
Exercício de visualização de som com círculos
png
Sketch,py5,CreativeCoding
"""

import math

import numpy as np
import py5
import sounddevice as sd

from utils import helpers

sketch = helpers.info_for_sketch(__file__, __doc__)

MARGEM_X = 200
MARGEM_Y = 200


CIRCULOS = 4
ALL_DATA = []
DEVICE = 0
GAIN = 20
BLOCK_DURATION = 10

CAPTURAS = 360


def capture_sound(total_points: int):
    data = []
    samplerate = sd.query_devices(DEVICE, "input")["default_samplerate"]
    low, high = [100, 2000]
    delta_f = (high - low) / (80 - 1)
    fftsize = math.ceil(samplerate / delta_f)

    def callback(indata, frames, time, status):
        if any(indata):
            magnitude = np.abs(np.fft.rfft(indata[:, 0], n=fftsize))
            magnitude *= 10 / fftsize
            data.append(magnitude)

    with sd.InputStream(
        device=DEVICE,
        channels=1,
        callback=callback,
        blocksize=int(samplerate * BLOCK_DURATION / 1000),
        samplerate=samplerate,
    ):
        while len(data) < total_points:
            print(f"{len(data)}/{total_points} ({samplerate})")
    return data


def circulo(mult, data, frame_max, frame_min):
    pontos = []
    total = len(data)
    passo = 360 / total
    for idx in range(0, total):
        direcao = 1
        pb = data[idx]
        diff = py5.remap(pb, frame_min, frame_max, 1, 1.3) * direcao
        angulo = idx * passo
        x0 = np.cos(py5.radians(angulo)) * mult
        x = x0 * diff
        y0 = np.sin(py5.radians(angulo)) * mult
        y = y0 * diff
        pontos.append((x0, y0, x, y))
    return pontos


def setup():
    global ALL_DATA
    py5.size(helpers.LARGURA, helpers.ALTURA, py5.P3D)
    py5.color_mode(py5.HSB, 360, 100, 100)
    py5.ellipse_mode(py5.CENTER)
    ALL_DATA = capture_sound(CAPTURAS)


def draw():
    py5.background(0)
    numero = py5.frame_count
    data_idx = MARGEM_Y
    passo = 60
    with py5.push_style():
        with py5.push_matrix():
            py5.translate(py5.width // 2, py5.height // 2, -30)
            for i in range(0, CIRCULOS):
                py5.rotate(py5.radians(numero * (1.5 + 2 * i)))
                idc = ((numero * CIRCULOS) + i) % CAPTURAS
                data = ALL_DATA[idc]
                frame_min = np.min(data)
                frame_max = np.max(data)
                pontos = circulo(((i + 1) * passo), data, frame_max, frame_min)
                for idx, (x0, y0, x, y) in enumerate(pontos):
                    h = idx % 360
                    py5.stroke(py5.color(h, 80, 80))
                    py5.line(x0, y0, x, y)
                    data_idx += 1

    helpers.write_legend(sketch=sketch, frame="#000")


def key_pressed():
    key = py5.key
    if key == " ":
        save_and_close()


def save_and_close():
    py5.no_loop()
    helpers.save_sketch_image(sketch)
    py5.exit_sketch()


if __name__ == "__main__":
    py5.run_sketch()