Power Sequencer: fix package name and import error
Fixes an error with not passing the python tests.
This commit is contained in:
parent
c9f09d722a
commit
55b820186e
|
@ -22,14 +22,18 @@ def get_operator_classes():
|
|||
"""Returns the list of operators in the add-on"""
|
||||
this_file = os.path.dirname(__file__)
|
||||
module_files = [
|
||||
f for f in os.listdir(this_file) if f.endswith(".py") and not f.startswith("__init__")
|
||||
f
|
||||
for f in os.listdir(this_file)
|
||||
if f.endswith(".py") and not f.startswith("__init__")
|
||||
]
|
||||
module_paths = ["." + os.path.splitext(f)[0] for f in module_files]
|
||||
classes = []
|
||||
print(__name__)
|
||||
for path in module_paths:
|
||||
module = importlib.import_module(path, package="blender_power_sequencer.operators")
|
||||
operator_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")]
|
||||
module = importlib.import_module(path, package="power_sequencer.operators")
|
||||
operator_names = [
|
||||
entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")
|
||||
]
|
||||
classes.extend([getattr(module, name) for name in operator_names])
|
||||
return classes
|
||||
|
||||
|
@ -38,7 +42,9 @@ doc = {
|
|||
"sequencer.refresh_all": {
|
||||
"name": "Refresh All",
|
||||
"description": "",
|
||||
"shortcuts": [({"type": "R", "value": "PRESS", "shift": True}, {}, "Refresh All")],
|
||||
"shortcuts": [
|
||||
({"type": "R", "value": "PRESS", "shift": True}, {}, "Refresh All")
|
||||
],
|
||||
"demo": "",
|
||||
"keymap": "Sequencer",
|
||||
}
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import bpy
|
||||
import subprocess
|
||||
|
||||
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
|
||||
|
||||
|
||||
class POWER_SEQUENCER_OT_align_audios(bpy.types.Operator):
|
||||
"""*brief* Align two audio strips
|
||||
|
||||
|
||||
Tries to synchronize the selected audio strip to the active audio strip by comparing the sound.
|
||||
Useful to synchronize audio of the same event recorded with different microphones.
|
||||
|
||||
To use this feature, you must have [ffmpeg](https://www.ffmpeg.org/download.html) and
|
||||
[scipy](https://www.scipy.org/install.html) installed on your computer and available on the PATH (command line) to work.
|
||||
|
||||
The longer the audio files, the longer the tool can take to run, as it has to convert, analyze,
|
||||
and compare the audio sources to work.
|
||||
"""
|
||||
|
||||
doc = {
|
||||
"name": doc_name(__qualname__),
|
||||
"demo": "https://i.imgur.com/xkBUzDj.gif",
|
||||
"description": doc_description(__doc__),
|
||||
"shortcuts": [],
|
||||
"keymap": "Sequencer",
|
||||
}
|
||||
bl_idname = doc_idname(__qualname__)
|
||||
bl_label = doc["name"]
|
||||
bl_description = doc_brief(doc["description"])
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.scene:
|
||||
return False
|
||||
|
||||
active = context.scene.sequence_editor.active_strip
|
||||
selected = context.selected_sequences
|
||||
ok = (
|
||||
len(selected) == 2
|
||||
and active in selected
|
||||
and all(map(lambda s: s.type == "SOUND", selected))
|
||||
)
|
||||
return ok
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
import scipy
|
||||
except ImportError:
|
||||
self.report({"ERROR"}, "Scipy must be installed to align audios")
|
||||
return {"FINISHED"}
|
||||
|
||||
if not is_ffmpeg_available():
|
||||
self.report({"ERROR"}, "ffmpeg must be installed to align audios")
|
||||
return {"FINISHED"}
|
||||
|
||||
# This import is here because otherwise, it slows down blender startup
|
||||
from .audiosync import find_offset
|
||||
|
||||
scene = context.scene
|
||||
|
||||
active = scene.sequence_editor.active_strip
|
||||
active_filepath = bpy.path.abspath(active.sound.filepath)
|
||||
|
||||
selected = context.selected_sequences
|
||||
selected.pop(selected.index(active))
|
||||
|
||||
align_strip = selected[0]
|
||||
align_strip_filepath = bpy.path.abspath(align_strip.sound.filepath)
|
||||
|
||||
offset, score = find_offset(align_strip_filepath, active_filepath)
|
||||
|
||||
initial_offset = active.frame_start - align_strip.frame_start
|
||||
|
||||
fps = scene.render.fps / scene.render.fps_base
|
||||
frames = int(offset * fps)
|
||||
|
||||
align_strip.frame_start -= frames - initial_offset
|
||||
|
||||
self.report({"INFO"}, "Alignment score: " + str(round(score, 1)))
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def is_ffmpeg_available():
|
||||
"""
|
||||
Returns true if ffmpeg is installed and available from the PATH
|
||||
"""
|
||||
try:
|
||||
subprocess.call(["ffmpeg", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
|
@ -1,17 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from .find_offset import find_offset
|
|
@ -1,60 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
def convert_and_trim(audio_filepath, freq, dur):
|
||||
"""
|
||||
Uses ffmpeg to convert an audio file to a temporary wav file for use
|
||||
in finding offset.
|
||||
|
||||
Args
|
||||
:audio: path to the audiofile to convert (string)
|
||||
:freq: Samples / second in the output wav (int)
|
||||
:dur: Max duration of the output wav in seconds (float)
|
||||
|
||||
Returns
|
||||
:outpath: path to the output wav file
|
||||
"""
|
||||
|
||||
tmp = tempfile.NamedTemporaryFile(mode="r+b", prefix="offset_", suffix=".wav")
|
||||
outpath = tmp.name
|
||||
tmp.close()
|
||||
|
||||
channel_count = "1"
|
||||
|
||||
subprocess.call(
|
||||
[
|
||||
"ffmpeg",
|
||||
"-loglevel",
|
||||
"panic",
|
||||
"-i",
|
||||
audio_filepath,
|
||||
"-ac",
|
||||
channel_count,
|
||||
"-ar",
|
||||
str(freq),
|
||||
"-t",
|
||||
str(dur),
|
||||
"-acodec",
|
||||
"pcm_s16le",
|
||||
outpath,
|
||||
]
|
||||
)
|
||||
|
||||
return outpath
|
|
@ -1,33 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import numpy as np
|
||||
|
||||
|
||||
def cross_correlation(mfcc1, mfcc2, nframes):
|
||||
n1, mdim1 = mfcc1.shape
|
||||
# n2, mdim2 = mfcc2.shape
|
||||
|
||||
n = n1 - nframes + 1
|
||||
|
||||
if n < 0:
|
||||
return None
|
||||
|
||||
c = np.zeros(n)
|
||||
for k in range(n):
|
||||
cc = np.sum(np.multiply(mfcc1[k : k + nframes], mfcc2[:nframes]), axis=0)
|
||||
c[k] = np.linalg.norm(cc)
|
||||
return c
|
|
@ -1,28 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import numpy as np
|
||||
|
||||
|
||||
def ensure_non_zero(signal):
|
||||
"""
|
||||
Adds a little bit of static to avoid
|
||||
'divide by zero encountered in log' during MFCC computation.
|
||||
"""
|
||||
|
||||
signal += np.random.random(len(signal)) * 10 ** -10
|
||||
|
||||
return signal
|
|
@ -1,87 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
This code is an adaptation of 'audio-offset-finder' by BBC
|
||||
"""
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
from .mfcc import mfcc
|
||||
from .convert_and_trim import convert_and_trim
|
||||
from .std_mfcc import std_mfcc
|
||||
from .cross_correlation import cross_correlation
|
||||
from .ensure_non_zero import ensure_non_zero
|
||||
|
||||
|
||||
def find_offset(file1, file2, freq=8000, trim=60 * 15, correl_nframes=1000):
|
||||
"""
|
||||
Determine the offset (in seconds) between 2 audio files
|
||||
|
||||
Uses cross-correlation of standardised Mel-Frequency Cepstral
|
||||
Coefficients
|
||||
"""
|
||||
from scipy.io import wavfile
|
||||
|
||||
file1 = os.path.abspath(file1)
|
||||
file2 = os.path.abspath(file2)
|
||||
|
||||
wav1_path = convert_and_trim(file1, freq, trim)
|
||||
wav2_path = convert_and_trim(file2, freq, trim)
|
||||
|
||||
rate1, data1 = wavfile.read(wav1_path, mmap=True)
|
||||
data1 = data1 / (2.0 ** 15)
|
||||
|
||||
rate2, data2 = wavfile.read(wav2_path, mmap=True)
|
||||
data2 = data2 / (2.0 ** 15)
|
||||
|
||||
data1 = ensure_non_zero(data1)
|
||||
data2 = ensure_non_zero(data2)
|
||||
|
||||
mfcc1 = mfcc(data1, nwin=256, nfft=512, fs=freq, nceps=13)[0]
|
||||
mfcc2 = mfcc(data2, nwin=256, nfft=512, fs=freq, nceps=13)[0]
|
||||
|
||||
mfcc1 = std_mfcc(mfcc1)
|
||||
mfcc2 = std_mfcc(mfcc2)
|
||||
|
||||
frames1 = mfcc1.shape[0]
|
||||
frames2 = mfcc2.shape[0]
|
||||
|
||||
if frames1 > frames2:
|
||||
flip = 1
|
||||
|
||||
else:
|
||||
flip = -1
|
||||
mfcc1, mfcc2 = mfcc2, mfcc1
|
||||
|
||||
c = cross_correlation(mfcc1, mfcc2, nframes=correl_nframes)
|
||||
try:
|
||||
c.any()
|
||||
except AttributeError:
|
||||
os.remove(wav1_path)
|
||||
os.remove(wav2_path)
|
||||
|
||||
return 0, 0
|
||||
|
||||
max_k_index = np.argmax(c)
|
||||
|
||||
offset = max_k_index * 160.0 / float(freq)
|
||||
score = (c[max_k_index] - np.mean(c)) / np.std(c)
|
||||
|
||||
os.remove(wav1_path)
|
||||
os.remove(wav2_path)
|
||||
|
||||
return offset * flip, score
|
|
@ -1,17 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from .mfcc import mfcc
|
|
@ -1,91 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import numpy as np
|
||||
|
||||
from scipy.signal import hamming, lfilter
|
||||
from scipy.fftpack import fft
|
||||
from scipy.fftpack.realtransforms import dct
|
||||
|
||||
from .trfbank import trfbank
|
||||
from .segment_axis import segment_axis
|
||||
|
||||
|
||||
def mfcc(input, nwin=256, nfft=512, fs=16000, nceps=13):
|
||||
"""Compute Mel Frequency Cepstral Coefficients.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input: ndarray
|
||||
input from which the coefficients are computed
|
||||
|
||||
Returns
|
||||
-------
|
||||
ceps: ndarray
|
||||
Mel-cepstrum coefficients
|
||||
mspec: ndarray
|
||||
Log-spectrum in the mel-domain.
|
||||
|
||||
Notes
|
||||
-----
|
||||
MFCC are computed as follows:
|
||||
* Pre-processing in time-domain (pre-emphasizing)
|
||||
* Compute the spectrum amplitude by windowing with a Hamming window
|
||||
* Filter the signal in the spectral domain with a triangular
|
||||
filter-bank, whose filters are approximatively linearly spaced on the
|
||||
mel scale, and have equal bandwidth in the mel scale
|
||||
* Compute the DCT of the log-spectrum
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] S.B. Davis and P. Mermelstein, "Comparison of parametric
|
||||
representations for monosyllabic word recognition in continuously
|
||||
spoken sentences", IEEE Trans. Acoustics. Speech, Signal Proc.
|
||||
ASSP-28 (4): 357-366, August 1980."""
|
||||
|
||||
# MFCC parameters: taken from auditory toolbox
|
||||
over = nwin - 160
|
||||
# Pre-emphasis factor (to take into account the -6dB/octave rolloff of the
|
||||
# radiation at the lips level)
|
||||
prefac = 0.97
|
||||
|
||||
# lowfreq = 400 / 3.
|
||||
lowfreq = 133.33
|
||||
# highfreq = 6855.4976
|
||||
linsc = 200 / 3.0
|
||||
logsc = 1.0711703
|
||||
|
||||
nlinfil = 13
|
||||
nlogfil = 27
|
||||
|
||||
w = hamming(nwin, sym=0)
|
||||
|
||||
fbank = trfbank(fs, nfft, lowfreq, linsc, logsc, nlinfil, nlogfil)[0]
|
||||
|
||||
# ------------------
|
||||
# Compute the MFCC
|
||||
# ------------------
|
||||
extract = lfilter([1.0, -prefac], 1, input)
|
||||
framed = segment_axis(extract, nwin, over) * w
|
||||
|
||||
# Compute the spectrum magnitude
|
||||
spec = np.abs(fft(framed, nfft, axis=-1))
|
||||
# Filter the spectrum through the triangle filterbank
|
||||
mspec = np.log10(np.dot(spec, fbank.T))
|
||||
# Use the DCT to 'compress' the coefficients (spectrum -> cepstrum domain)
|
||||
ceps = dct(mspec, type=2, norm="ortho", axis=-1)[:, :nceps]
|
||||
|
||||
return ceps, mspec, spec
|
|
@ -1,110 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import numpy as np
|
||||
import warnings
|
||||
|
||||
|
||||
def segment_axis(a, length, overlap=0, axis=None, end="cut", endvalue=0):
|
||||
"""Generate a new array that chops the given array along the given axis
|
||||
into overlapping frames.
|
||||
|
||||
example:
|
||||
>>> segment_axis(arange(10), 4, 2)
|
||||
array([[0, 1, 2, 3],
|
||||
[2, 3, 4, 5],
|
||||
[4, 5, 6, 7],
|
||||
[6, 7, 8, 9]])
|
||||
|
||||
arguments:
|
||||
a The array to segment
|
||||
length The length of each frame
|
||||
overlap The number of array elements by which the frames should overlap
|
||||
axis The axis to operate on; if None, act on the flattened array
|
||||
end What to do with the last frame, if the array is not evenly
|
||||
divisible into pieces. Options are:
|
||||
|
||||
'cut' Simply discard the extra values
|
||||
'wrap' Copy values from the beginning of the array
|
||||
'pad' Pad with a constant value
|
||||
|
||||
endvalue The value to use for end='pad'
|
||||
|
||||
The array is not copied unless necessary (either because it is unevenly
|
||||
strided and being flattened or because end is set to 'pad' or 'wrap').
|
||||
"""
|
||||
|
||||
if axis is None:
|
||||
a = np.ravel(a) # may copy
|
||||
axis = 0
|
||||
|
||||
l = a.shape[axis]
|
||||
|
||||
if overlap >= length:
|
||||
raise ValueError("frames cannot overlap by more than 100%")
|
||||
if overlap < 0 or length <= 0:
|
||||
raise ValueError("overlap must be nonnegative and length must " "be positive")
|
||||
|
||||
if l < length or (l - length) % (length - overlap):
|
||||
if l > length:
|
||||
roundup = length + (1 + (l - length) // (length - overlap)) * (length - overlap)
|
||||
rounddown = length + ((l - length) // (length - overlap)) * (length - overlap)
|
||||
else:
|
||||
roundup = length
|
||||
rounddown = 0
|
||||
assert rounddown < l < roundup
|
||||
assert roundup == rounddown + (length - overlap) or (roundup == length and rounddown == 0)
|
||||
a = a.swapaxes(-1, axis)
|
||||
|
||||
if end == "cut":
|
||||
a = a[..., :rounddown]
|
||||
elif end in ["pad", "wrap"]: # copying will be necessary
|
||||
s = list(a.shape)
|
||||
s[-1] = roundup
|
||||
b = np.empty(s, dtype=a.dtype)
|
||||
b[..., :l] = a
|
||||
if end == "pad":
|
||||
b[..., l:] = endvalue
|
||||
elif end == "wrap":
|
||||
b[..., l:] = a[..., : roundup - l]
|
||||
a = b
|
||||
|
||||
a = a.swapaxes(-1, axis)
|
||||
|
||||
l = a.shape[axis]
|
||||
if l == 0:
|
||||
raise ValueError(
|
||||
"Not enough data points to segment array in 'cut' mode; " "try 'pad' or 'wrap'"
|
||||
)
|
||||
assert l >= length
|
||||
assert (l - length) % (length - overlap) == 0
|
||||
n = 1 + (l - length) // (length - overlap)
|
||||
s = a.strides[axis]
|
||||
newshape = a.shape[:axis] + (n, length) + a.shape[axis + 1 :]
|
||||
newstrides = a.strides[:axis] + ((length - overlap) * s, s) + a.strides[axis + 1 :]
|
||||
|
||||
try:
|
||||
return np.ndarray.__new__(
|
||||
np.ndarray, strides=newstrides, shape=newshape, buffer=a, dtype=a.dtype
|
||||
)
|
||||
except TypeError:
|
||||
warnings.warn("Problem with ndarray creation forces copy.")
|
||||
a = a.copy()
|
||||
# Shape doesn't change but strides does
|
||||
newstrides = a.strides[:axis] + ((length - overlap) * s, s) + a.strides[axis + 1 :]
|
||||
return np.ndarray.__new__(
|
||||
np.ndarray, strides=newstrides, shape=newshape, buffer=a, dtype=a.dtype
|
||||
)
|
|
@ -1,51 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import numpy as np
|
||||
|
||||
|
||||
def trfbank(fs, nfft, lowfreq, linsc, logsc, nlinfilt, nlogfilt):
|
||||
"""Compute triangular filterbank for MFCC computation."""
|
||||
# Total number of filters
|
||||
nfilt = nlinfilt + nlogfilt
|
||||
|
||||
# ------------------------
|
||||
# Compute the filter bank
|
||||
# ------------------------
|
||||
# Compute start/middle/end points of the triangular filters in spectral
|
||||
# domain
|
||||
freqs = np.zeros(nfilt + 2)
|
||||
freqs[:nlinfilt] = lowfreq + np.arange(nlinfilt) * linsc
|
||||
freqs[nlinfilt:] = freqs[nlinfilt - 1] * logsc ** np.arange(1, nlogfilt + 3)
|
||||
heights = 2.0 / (freqs[2:] - freqs[0:-2])
|
||||
|
||||
# Compute filterbank coeff (in fft domain, in bins)
|
||||
fbank = np.zeros((nfilt, nfft))
|
||||
# FFT bins (in Hz)
|
||||
nfreqs = np.arange(nfft) / (1.0 * nfft) * fs
|
||||
for i in range(nfilt):
|
||||
low = freqs[i]
|
||||
cen = freqs[i + 1]
|
||||
hi = freqs[i + 2]
|
||||
|
||||
lid = np.arange(np.floor(low * nfft / fs) + 1, np.floor(cen * nfft / fs) + 1, dtype=np.int)
|
||||
lslope = heights[i] / (cen - low)
|
||||
rid = np.arange(np.floor(cen * nfft / fs) + 1, np.floor(hi * nfft / fs) + 1, dtype=np.int)
|
||||
rslope = heights[i] / (hi - cen)
|
||||
fbank[i][lid] = lslope * (nfreqs[lid] - low)
|
||||
fbank[i][rid] = rslope * (hi - nfreqs[rid])
|
||||
|
||||
return fbank, freqs
|
|
@ -1,21 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
|
||||
#
|
||||
# This file is part of Power Sequencer.
|
||||
#
|
||||
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
|
||||
# not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import numpy as np
|
||||
|
||||
|
||||
def std_mfcc(mfcc):
|
||||
return (mfcc - np.mean(mfcc, axis=0)) / np.std(mfcc, axis=0)
|
Loading…
Reference in New Issue