From 85917383823ac92a0310d4c32effc105ec581f26 Mon Sep 17 00:00:00 2001 From: Brian McFee Date: Tue, 30 Aug 2016 14:34:58 -0400 Subject: [PATCH] added multisegment sonification --- jams/sonify.py | 31 ++++++++++++++++++++++++++++++- jams/tests/sonify_test.py | 19 ++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/jams/sonify.py b/jams/sonify.py index 28d7163b..530eaee4 100644 --- a/jams/sonify.py +++ b/jams/sonify.py @@ -10,12 +10,13 @@ sonify ''' +from itertools import product from collections import OrderedDict import six import numpy as np import mir_eval.sonify from mir_eval.util import filter_kwargs -from .eval import coerce_annotation +from .eval import coerce_annotation, hierarchy_flatten from .exceptions import NamespaceError __all__ = ['sonify'] @@ -79,6 +80,29 @@ def downbeat(annotation, sr=22050, length=None, **kwargs): return y +def multi_segment(annotation, sr=22050, length=None, **kwargs): + '''Sonify multi-level segmentations''' + + # Pentatonic scale, because why not + PENT = [1, 32./27, 4./3, 3./2, 16./9] + DURATION = 0.1 + + h_int, _ = hierarchy_flatten(annotation) + + if length is None: + length = int(sr * (max(np.max(_) for _ in h_int) + 1. / DURATION) + 1) + + y = 0.0 + for ints, (oc, scale) in zip(h_int, product(range(3, 3 + len(h_int)), + PENT)): + click = mkclick(440.0 * scale * oc, sr=sr, duration=DURATION) + y = y + filter_kwargs(mir_eval.sonify.clicks, + np.unique(ints), + fs=sr, length=length, + click=click) + return y + + def chord(annotation, sr=22050, length=None, **kwargs): '''Sonify chords @@ -153,6 +177,7 @@ def piano_roll(annotation, sr=22050, length=None, **kwargs): SONIFY_MAPPING = OrderedDict() SONIFY_MAPPING['beat_position'] = downbeat SONIFY_MAPPING['beat'] = clicks +SONIFY_MAPPING['multi_segment'] = multi_segment SONIFY_MAPPING['segment_open'] = clicks SONIFY_MAPPING['onset'] = clicks SONIFY_MAPPING['chord'] = chord @@ -189,6 +214,10 @@ def sonify(annotation, sr=22050, duration=None, **kwargs): ''' length = None + + if duration is None: + duration = annotation.duration + if duration is not None: length = int(duration * sr) diff --git a/jams/tests/sonify_test.py b/jams/tests/sonify_test.py index 7f3b0845..fecb7f2f 100644 --- a/jams/tests/sonify_test.py +++ b/jams/tests/sonify_test.py @@ -2,11 +2,12 @@ # CREATED:2016-02-11 12:07:58 by Brian McFee """Sonification tests""" -import six import numpy as np from nose.tools import raises, eq_ +from eval_test import create_hierarchy + import jams @@ -82,7 +83,6 @@ def test_contour(): 'index': 0, 'voiced': (t < 3 or t > 4)}) - def __test(ann, duration): y = jams.sonify.sonify(ann, sr=8000, duration=duration) if duration is not None: @@ -98,7 +98,7 @@ def __test(namespace, value): ann = jams.Annotation(namespace=namespace) ann.append(time=0.5, duration=1.0, value=value) y = jams.sonify.sonify(ann, sr=8000, duration=2.0) - + eq_(len(y), 8000 * 2) yield __test, 'chord', 'C:maj/5' @@ -136,3 +136,16 @@ def __test(ann, sr, duration): for length in [None, 5, 15]: yield __test, ann, 8000, length + + +def test_multi_segment(): + ann = create_hierarchy(values=['AB', 'abac', 'xxyyxxzz'], duration=30) + sr = 8000 + + def __test(ann, duration): + y = jams.sonify.sonify(ann, sr=sr, duration=duration) + if duration: + eq_(len(y), duration * sr) + + for duration in [None, 15, 30]: + yield __test, ann, duration