Skip to content

Commit

Permalink
Merge pull request #169 from CPJKU/refactor_online_mode
Browse files Browse the repository at this point in the history
replace online parameter of FramedSignalProcessor by origin
  • Loading branch information
Sebastian Böck authored Jul 18, 2016
2 parents 489f5e0 + 7d7df9a commit 3bf7cb4
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 80 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ API relevant changes:
* Reorderd the dimensions of comb_filters to time, freq, tau (#135)
* `write_notes` uses `delimiter` instead of `sep` to seperate columns (#155)
* `LSTMLayer` takes `Gate`s as arguments, all layers are callable (#161)
* replaced `online` parameter of `FramedSignalProcessor` by `origin` (#169)

Other changes:

Expand Down
6 changes: 2 additions & 4 deletions bin/ComplexFlux
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def main():
io_arguments(p, output_suffix='.onsets.txt')
ActivationsProcessor.add_arguments(p)
SignalProcessor.add_arguments(p, norm=False, gain=0)
FramedSignalProcessor.add_arguments(p, fps=200, online=False)
FramedSignalProcessor.add_arguments(p, fps=200)
FilterbankProcessor.add_arguments(p, num_bands=24, fmin=30, fmax=17000,
norm_filters=False)
LogarithmicSpectrogramProcessor.add_arguments(p, log=True, mul=1, add=1)
Expand All @@ -69,9 +69,7 @@ def main():
combine=0.03, delay=0)
# parse arguments
args = p.parse_args()
# switch to offline mode
if args.norm:
args.online = False

# print arguments
if args.verbose:
print(args)
Expand Down
6 changes: 2 additions & 4 deletions bin/LogFiltSpecFlux
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def main():
io_arguments(p, output_suffix='.onsets.txt')
ActivationsProcessor.add_arguments(p)
SignalProcessor.add_arguments(p, norm=False, gain=0)
FramedSignalProcessor.add_arguments(p, fps=100, online=False)
FramedSignalProcessor.add_arguments(p, fps=100)
FilterbankProcessor.add_arguments(p, num_bands=12, fmin=30, fmax=17000,
norm_filters=False)
LogarithmicSpectrogramProcessor.add_arguments(p, log=True, mul=1, add=1)
Expand All @@ -68,9 +68,7 @@ def main():
combine=0.03, delay=0)
# parse arguments
args = p.parse_args()
# switch to offline mode
if args.norm:
args.online = False

# print arguments
if args.verbose:
print(args)
Expand Down
5 changes: 2 additions & 3 deletions bin/OnsetDetectorLL
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,14 @@ def main():
io_arguments(p, output_suffix='.onsets.txt')
ActivationsProcessor.add_arguments(p)
# signal processing arguments
SignalProcessor.add_arguments(p, norm=False, gain=0)
SignalProcessor.add_arguments(p, gain=0)
# peak picking arguments
PeakPickingProcessor.add_arguments(p, threshold=0.23)

# parse arguments
args = p.parse_args()

# set immutable defaults
args.online = True
args.fps = 100
args.pre_max = 1. / args.fps
args.post_max = 0
Expand All @@ -90,7 +89,7 @@ def main():
in_processor = ActivationsProcessor(mode='r', **vars(args))
else:
# use a RNN to predict the onsets
in_processor = RNNOnsetProcessor(online=args.online)
in_processor = RNNOnsetProcessor(online=True)

# output processor
if args.save:
Expand Down
9 changes: 6 additions & 3 deletions bin/SpectralOnsetDetection
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ def main():
combine=0.03, delay=0)
# parse arguments
args = p.parse_args()
# switch to offline mode
if args.norm:
args.online = False

# set online mode parameters
if args.origin == 'online':
args.post_avg = 0
args.post_max = 0

# add circular shift for correct phase and remove filterbank if needed
if args.onset_method in ('phase_deviation', 'weighted_phase_deviation',
'normalized_weighted_phase_deviation',
Expand Down
6 changes: 2 additions & 4 deletions bin/SuperFlux
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def main():
io_arguments(p, output_suffix='.onsets.txt')
ActivationsProcessor.add_arguments(p)
SignalProcessor.add_arguments(p, norm=False, gain=0)
FramedSignalProcessor.add_arguments(p, fps=200, online=False)
FramedSignalProcessor.add_arguments(p, fps=200)
FilterbankProcessor.add_arguments(p, num_bands=24, fmin=30, fmax=17000,
norm_filters=False)
LogarithmicSpectrogramProcessor.add_arguments(p, log=True, mul=1, add=1)
Expand All @@ -68,9 +68,7 @@ def main():
combine=0.03, delay=0)
# parse arguments
args = p.parse_args()
# switch to offline mode
if args.norm:
args.online = False

# print arguments
if args.verbose:
print(args)
Expand Down
1 change: 0 additions & 1 deletion bin/SuperFluxNN
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ def main():
# set immutable defaults
args.num_channels = 1
args.fps = 100
args.online = False
args.onset_method = 'superflux'

# print arguments
Expand Down
92 changes: 51 additions & 41 deletions madmom/audio/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,15 @@ def load_audio_file(filename, sample_rate=None, num_channels=None, start=None,


# signal classes
SAMPLE_RATE = None
NUM_CHANNELS = None
START = None
STOP = None
NORM = False
GAIN = 0.
DTYPE = None


class Signal(np.ndarray):
"""
The :class:`Signal` class represents a signal as a (memory-mapped) numpy
Expand Down Expand Up @@ -568,13 +577,14 @@ class Signal(np.ndarray):
# pylint: disable=super-init-not-called
# pylint: disable=attribute-defined-outside-init

def __init__(self, data, sample_rate=None, num_channels=None, start=None,
stop=None, norm=False, gain=0, dtype=None):
def __init__(self, data, sample_rate=SAMPLE_RATE,
num_channels=NUM_CHANNELS, start=START, stop=STOP, norm=NORM,
gain=GAIN, dtype=DTYPE):
# this method is for documentation purposes only
pass

def __new__(cls, data, sample_rate=None, num_channels=None, start=None,
stop=None, norm=False, gain=0, dtype=None):
def __new__(cls, data, sample_rate=SAMPLE_RATE, num_channels=NUM_CHANNELS,
start=START, stop=STOP, norm=NORM, gain=GAIN, dtype=DTYPE):
# try to load an audio file if the data is not a numpy array
if not isinstance(data, np.ndarray):
data, sample_rate = load_audio_file(data, sample_rate=sample_rate,
Expand Down Expand Up @@ -654,15 +664,9 @@ class SignalProcessor(Processor):
dtypes use the complete value range, float dtypes the range [-1, +1].
"""
SAMPLE_RATE = None
NUM_CHANNELS = None
START = None
STOP = None
NORM = False
GAIN = 0.

def __init__(self, sample_rate=SAMPLE_RATE, num_channels=NUM_CHANNELS,
start=None, stop=None, norm=NORM, gain=GAIN, **kwargs):
start=START, stop=STOP, norm=NORM, gain=GAIN, **kwargs):
# pylint: disable=unused-argument
self.sample_rate = sample_rate
self.num_channels = num_channels
Expand Down Expand Up @@ -855,6 +859,14 @@ def signal_frame(signal, index, frame_size, hop_size, origin=0):
return signal[start:stop]


FRAME_SIZE = 2048
HOP_SIZE = 441.
FPS = None
ORIGIN = 0
END_OF_SIGNAL = 'normal'
NUM_FRAMES = None


# classes for splitting a signal into frames
class FramedSignal(object):
"""
Expand Down Expand Up @@ -924,8 +936,9 @@ class FramedSignal(object):
"""

def __init__(self, signal, frame_size=2048, hop_size=441., fps=None,
origin=0, end='normal', num_frames=None, **kwargs):
def __init__(self, signal, frame_size=FRAME_SIZE, hop_size=HOP_SIZE,
fps=FPS, origin=ORIGIN, end=END_OF_SIGNAL,
num_frames=NUM_FRAMES, **kwargs):
# signal handling
if not isinstance(signal, Signal):
# try to instantiate a Signal
Expand Down Expand Up @@ -1061,8 +1074,8 @@ class FramedSignalProcessor(Processor):
fps : float, optional
Use given frames per second; if set, this computes and overwrites the
given `hop_size` value.
online : bool, optional
Operate in online mode (see notes below).
origin : int, optional
Location of the window relative to the reference sample of a frame.
end : int or str, optional
End of signal handling (see :class:`FramedSignal`).
num_frames : int, optional
Expand All @@ -1071,31 +1084,30 @@ class FramedSignalProcessor(Processor):
If no :class:`Signal` instance was given, one is instantiated with
these additional keyword arguments.
Notes
-----
The location of the window relative to its reference sample can be set
with the `online` parameter:
- 'False': the window is centered on its reference sample,
- 'True': the window is located to the left of its reference sample
(including the reference sample), i.e. only past information is used.
See Also
--------
:class:`FramedSignal` for a detailed description of the parameters.
"""
FRAME_SIZE = 2048
HOP_SIZE = 441.
FPS = 100.
START = 0
END_OF_SIGNAL = 'normal'

def __init__(self, frame_size=FRAME_SIZE, hop_size=HOP_SIZE, fps=None,
online=False, end=END_OF_SIGNAL, **kwargs):

def __init__(self, frame_size=FRAME_SIZE, hop_size=HOP_SIZE, fps=FPS,
origin=ORIGIN, end=END_OF_SIGNAL, num_frames=NUM_FRAMES,
online=None, **kwargs):
# pylint: disable=unused-argument
self.frame_size = frame_size
self.hop_size = hop_size
self.fps = fps # do not convert here, pass it to FramedSignal
self.online = online
self.origin = origin
self.end = end
self.num_frames = num_frames
if online is not None:
import warnings
warnings.warn('`online` is deprecated as of version 0.14 and will '
'be removed in version 0.15. Use `origin` instead.')
if online:
self.origin = 'online'
else:
self.origin = 'offline'

def process(self, data, **kwargs):
"""
Expand All @@ -1115,15 +1127,11 @@ def process(self, data, **kwargs):
FramedSignal instance
"""
# translate online / offline mode
if self.online:
origin = 'online'
else:
origin = 'offline'
# instantiate a FramedSignal from the data and return it
return FramedSignal(data, frame_size=self.frame_size,
hop_size=self.hop_size, fps=self.fps,
origin=origin, end=self.end, **kwargs)
origin=self.origin, end=self.end,
num_frames=self.num_frames, **kwargs)

@staticmethod
def add_arguments(parser, frame_size=FRAME_SIZE, fps=FPS,
Expand Down Expand Up @@ -1172,10 +1180,12 @@ def add_arguments(parser, frame_size=FRAME_SIZE, fps=FPS,
g.add_argument('--fps', action='store', type=float, default=fps,
help='frames per second [default=%(default).1f]')
if online is False:
g.add_argument('--online', dest='online', action='store_true',
g.add_argument('--online', dest='origin', action='store_const',
const='online', default='offline',
help='operate in online mode [default=offline]')
elif online is True:
g.add_argument('--offline', dest='online', action='store_false',
g.add_argument('--offline', dest='origin', action='store_const',
const='offline', default='online',
help='operate in offline mode [default=online]')
# return the argument group so it can be modified if needed
return g
4 changes: 3 additions & 1 deletion madmom/features/onsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,9 +692,11 @@ def __init__(self, online=False, **kwargs):

# choose the appropriate models and set frame sizes accordingly
if online:
origin = 'online'
nn_files = ONSETS_RNN
frame_sizes = [512, 1024, 2048]
else:
origin = 'offline'
nn_files = ONSETS_BRNN
frame_sizes = [1024, 2048, 4096]

Expand All @@ -704,7 +706,7 @@ def __init__(self, online=False, **kwargs):
multi = ParallelProcessor([])
for frame_size in frame_sizes:
frames = FramedSignalProcessor(frame_size=frame_size, fps=100,
online=online)
origin=origin)
filt = FilteredSpectrogramProcessor(
num_bands=6, fmin=30, fmax=17000, norm_filters=True)
spec = LogarithmicSpectrogramProcessor(mul=5, add=1)
Expand Down
4 changes: 2 additions & 2 deletions madmom/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ def process(self, data):
"""
raise NotImplementedError('must be implemented by subclass.')

def __call__(self, *args):
def __call__(self, *args, **kwargs):
# this magic method makes a Processor callable
return self.process(*args)
return self.process(*args, **kwargs)


class OutputProcessor(Processor):
Expand Down
21 changes: 4 additions & 17 deletions tests/test_audio_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,8 +1282,9 @@ def test_values(self):
self.assertTrue(self.processor.frame_size == 2048)
self.assertTrue(self.processor.hop_size == 441.)
self.assertTrue(self.processor.fps is None)
self.assertTrue(self.processor.online is False)
self.assertTrue(self.processor.origin == 0)
self.assertTrue(self.processor.end == 'normal')
self.assertTrue(self.processor.num_frames is None)

def test_process(self):
result = self.processor.process(sample_file)
Expand All @@ -1308,8 +1309,8 @@ def test_rewrite_values(self):

def test_process_online(self):
# set online
self.processor.online = True
self.assertTrue(self.processor.online)
self.processor.origin = 'online'
self.assertEqual(self.processor.origin, 'online')
result = self.processor.process(sample_file)
self.assertTrue(np.allclose(result[0][-1], -2494))
self.assertTrue(len(result) == 281)
Expand Down Expand Up @@ -1359,17 +1360,3 @@ def test_process_end(self):
# reset end
self.processor.end = 'normal'
self.assertTrue(self.processor.end == 'normal')

def test_constant_types(self):
self.assertIsInstance(FramedSignalProcessor.FRAME_SIZE, int)
self.assertIsInstance(FramedSignalProcessor.HOP_SIZE, float)
self.assertIsInstance(FramedSignalProcessor.FPS, float)
self.assertIsInstance(FramedSignalProcessor.START, int)
self.assertIsInstance(FramedSignalProcessor.END_OF_SIGNAL, str)

def test_constant_values(self):
self.assertTrue(FramedSignalProcessor.FRAME_SIZE == 2048)
self.assertTrue(FramedSignalProcessor.HOP_SIZE == 441.)
self.assertTrue(FramedSignalProcessor.FPS == 100.)
self.assertTrue(FramedSignalProcessor.START == 0)
self.assertTrue(FramedSignalProcessor.END_OF_SIGNAL == 'normal')

0 comments on commit 3bf7cb4

Please sign in to comment.