Skip to content

File: ShaderFlow/Modules/Waveform.py

ShaderFlow.Modules.Waveform

WaveformReducer

Bases: BrokenEnum

Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
15
16
17
18
19
20
21
22
23
class WaveformReducer(BrokenEnum):
    def Average(x: numpy.ndarray) -> numpy.ndarray:
        return numpy.sqrt(numpy.mean(numpy.abs(x), axis=2))

    def RMS(x: numpy.ndarray) -> numpy.ndarray:
        return numpy.sqrt(numpy.sqrt(numpy.mean(x**2, axis=2))*(2**0.5))

    def STD(x: numpy.ndarray) -> numpy.ndarray:
        return numpy.sqrt(numpy.std(x, axis=2))

Average

Average(x: numpy.ndarray) -> numpy.ndarray
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
16
17
def Average(x: numpy.ndarray) -> numpy.ndarray:
    return numpy.sqrt(numpy.mean(numpy.abs(x), axis=2))

RMS

RMS(x: numpy.ndarray) -> numpy.ndarray
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
19
20
def RMS(x: numpy.ndarray) -> numpy.ndarray:
    return numpy.sqrt(numpy.sqrt(numpy.mean(x**2, axis=2))*(2**0.5))

STD

STD(x: numpy.ndarray) -> numpy.ndarray
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
22
23
def STD(x: numpy.ndarray) -> numpy.ndarray:
    return numpy.sqrt(numpy.std(x, axis=2))

ShaderWaveform

Bases: ShaderModule

Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@define
class ShaderWaveform(ShaderModule):

    name: str = "iWaveform"
    """Prefix name and Texture name of the Shader Variables"""

    audio: BrokenAudio = None
    """Audio class to read the data from"""

    length: Seconds = 3
    """Horizontal length of the Waveform content"""

    samplerate: Hertz = 60
    """Number of bars per second"""

    reducer: WaveformReducer = WaveformReducer.Average
    """How to convert a (channels, length, samples) chunks into (channels, length)"""

    smooth: bool = True
    """Enables Linear interpolation on the Texture, not much useful for Bars mode"""

    texture: ShaderTexture = None
    """Internal managed Texture"""

    @property
    def length_samples(self) -> Samples:
        return int(max(1, self.length*self.scene.fps))

    def build(self):
        self.texture = ShaderTexture(
            scene=self.scene,
            name=self.name,
            height=1,
            mipmaps=False,
            dtype=numpy.float32,
        ).repeat(False)

    @property
    def chunk_size(self) -> Samples:
        return max(1, int(self.length*self.audio.samplerate/self._points))

    @property
    def _points(self) -> int:
        return self.length*self.samplerate

    @property
    def _offset(self) -> int:
        return self.audio.tell % self.chunk_size

    @property
    def _cutoff(self) -> Samples:
        return nearest(
            number=self.audio.buffer_size,
            multiple=self.chunk_size,
            operator=math.floor,
            cast=int,
        )

    _same: SameTracker = Factory(SameTracker)

    def update(self):
        if self._same(self.audio.tell):
            return
        self.texture.filter     = ("linear" if self.smooth else "nearest")
        self.texture.components = self.audio.channels
        self.texture.width      = self._points
        start  = -int(self.chunk_size*self._points + self._offset + 1)
        end    = -int(self._offset + 1)
        chunks = self.audio.data[:, start:end]
        chunks = chunks.reshape(self.audio.channels, -1, self.chunk_size)
        chunks = self.reducer(chunks)
        chunks = numpy.ascontiguousarray(chunks.T)
        self.texture.write(chunks)

    def pipeline(self) -> Iterable[ShaderVariable]:
        yield Uniform("int", f"{self.name}Length", self.length_samples)

name

name: str = 'iWaveform'

Prefix name and Texture name of the Shader Variables

audio

audio: BrokenAudio = None

Audio class to read the data from

length

length: Seconds = 3

Horizontal length of the Waveform content

samplerate

samplerate: Hertz = 60

Number of bars per second

reducer

How to convert a (channels, length, samples) chunks into (channels, length)

smooth

smooth: bool = True

Enables Linear interpolation on the Texture, not much useful for Bars mode

texture

texture: ShaderTexture = None

Internal managed Texture

length_samples

length_samples: Samples

build

build()
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
53
54
55
56
57
58
59
60
def build(self):
    self.texture = ShaderTexture(
        scene=self.scene,
        name=self.name,
        height=1,
        mipmaps=False,
        dtype=numpy.float32,
    ).repeat(False)

chunk_size

chunk_size: Samples

update

update()
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
85
86
87
88
89
90
91
92
93
94
95
96
97
def update(self):
    if self._same(self.audio.tell):
        return
    self.texture.filter     = ("linear" if self.smooth else "nearest")
    self.texture.components = self.audio.channels
    self.texture.width      = self._points
    start  = -int(self.chunk_size*self._points + self._offset + 1)
    end    = -int(self._offset + 1)
    chunks = self.audio.data[:, start:end]
    chunks = chunks.reshape(self.audio.channels, -1, self.chunk_size)
    chunks = self.reducer(chunks)
    chunks = numpy.ascontiguousarray(chunks.T)
    self.texture.write(chunks)

pipeline

pipeline() -> Iterable[ShaderVariable]
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Waveform.py
 99
100
def pipeline(self) -> Iterable[ShaderVariable]:
    yield Uniform("int", f"{self.name}Length", self.length_samples)