Envelope
envelope
¶
In this directory:
- skyline returns a score with the highest sounding notes at any given point.
- extreme is similar, and designed to match the MIDI toolkit as exactly as possible.
- [this module] envelope is a variant on skyline that could be said to constitute a "smoothed" form,
and (currently) operates on pitch-onset pairs only (analysis only, no score return).
- superlative is the most reductive, returning only the single highest/lowest/sharpest/flattest value.
The skyline.py module demonstrates a strict case of retrieving the
highest sounding notes at any given point (with caveats as noted there).
Here, we implement a variant that could be said to constitute a "smoothed" form of the same.
Consider a musical line in which only one note sounds at any time. Musically, this may still outline more than one 'voice'. Fugues for solo violin provide an example. Notwithstanding some simultaneities, most of the 'different line' are interleaves sequentially. The clue to these different lines is often in the relative separation of pitch-onset pairs in pitch-time space. Highly relevant here is the field of auditory stream segregation.
Moreover, even in the absence of this implied polyphony, a single monodic melody often outlines a simpler shape with elaborations. Mozart's first piano sonata (K279) begins with the line C B D C E D F E G F A which clearly outlines (or is an elaborated form of) C D E F G A.
Both of these scenarios are captures by the envelope.
In the language of data science, we seek the "upper and lower envelope for sequential data".
Terminology
The skyline is used here to denote the upper envelope (aka the "ceiling", or "roofline").
The valleyline refers to the lower envelope (aka the "valley floor").
The algorithm
A point belongs to the upper envelope if and only if it is NOT a strict local minimum — i.e. it is not strictly below BOTH of its nearest retained neighbours. Equivalently: we iteratively remove strict local minima until none remain.
This produces a piecewise-linear boundary that: - passes through actual data points (not a fitted curve unlike in some other approaches to contour) - retains an arbitrary number of direction changes - excludes only points that genuinely dip below (or rise above) their context - is parameter-free at tolerance=0 (a tolerance>0 also removes near-flat dips)
The valleyline is the symmetric pair: all of the above applies to the removal of strict local maxima.
Supported input forms
- String of digit characters e.g. "313131" as a toy shorthand for test cases
- Sequence of scalar values e.g. [3, 1, 3, 1, ...] which is more usable for MIDI pitch numbers for instance (in this case a fake onset sequence is created with natural numbers 1, 2, 3, ...)
- Sequence of (onset, pitch) pairs which data source gives us user- or score-specified x-spacing.
- A
Scoreobject or equivalent, in which case we internally use the.find_all(Note)functionality to get the above data.
Author: Mark Gotham
skyline_envelope
¶
skyline_envelope(source, tolerance: float = 0.0) -> PointList
Upper envelope of the data (not to be confused with polyphony.skyline).
Returns the subset of input points that form the "roofline": no omitted point lies above the piecewise-linear boundary between its neighbours.
Parameters:
-
source–(See "Supported input forms" at the top of this module.)
- str of digits,
- sequence of scalars,
- sequence of (onset, pitch) pairs,
- score object with .find_all(Note)
-
tolerance(float, default:0.0) –points that dip no more than this amount below their neighbours are also removed (default 0 = exact envelope)
Returns:
-
PointList–list of (onset, pitch) float pairs on the upper envelope
Examples:
Test some inpute types.
>>> test_case = skyline_envelope("31513")
>>> test_case
[(1.0, 3.0), (3.0, 5.0), (5.0, 3.0)]
>>> skyline_envelope([3, 1, 5, 1, 3]) == test_case
True
>>> skyline_envelope([(1.0, 3.0), (2.0, 1.0), (3.0, 5.0), (4.0, 1.0), (5.0, 3.0)]) == test_case
True
Test de-duplication of simultaneities
>>> skyline_envelope([(1.0, 5.0), (1.0, 2.0), (2.0, 3.0)])
[(1.0, 5.0), (2.0, 3.0)]
Source code in amads/polyphony/envelope.py
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | |
valleyline_envelope
¶
valleyline_envelope(source, tolerance: float = 0.0) -> PointList
Lower envelope of the data.
Returns the subset of input points that form the "valley floor": no omitted point lies below the piecewise-linear boundary between its neighbours.
Parameters:
-
source–(See "Supported input forms" at the top of this module.)
- str of digits,
- sequence of scalars,
- sequence of (onset, pitch) pairs,
- score object with .find_all(Note)
-
tolerance(float, default:0.0) –points that dip no more than this amount below their neighbours are also removed (default 0 = exact envelope)
Returns:
-
PointList–list of (onset, pitch) float pairs on the upper envelope
Examples:
Test some inpute types.
>>> test_case = valleyline_envelope("31513")
>>> test_case
[(1.0, 3.0), (2.0, 1.0), (4.0, 1.0), (5.0, 3.0)]
>>> valleyline_envelope([3, 1, 5, 1, 3]) == test_case
True
>>> valleyline_envelope([(1.0, 3.0), (2.0, 1.0), (3.0, 5.0), (4.0, 1.0), (5.0, 3.0)]) == test_case
True
Test de-duplication of simultaneities
>>> valleyline_envelope([(1.0, 5.0), (1.0, 2.0), (2.0, 3.0)])
[(1.0, 2.0), (2.0, 3.0)]
Source code in amads/polyphony/envelope.py
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | |
skyline_values
¶
skyline_values(source, tolerance: float = 0.0) -> list[float]
Upper envelope as a plain list of y-values (onset coordinates omitted).
Parameters:
-
source–(See "Supported input forms" at the top of this module.)
- str of digits,
- sequence of scalars,
- sequence of (onset, pitch) pairs,
- score object with .find_all(Note)
-
tolerance(float, default:0.0) –points that dip no more than this amount below their neighbours are also removed (default 0 = exact envelope)
Returns:
-
PointList–list of (onset, pitch) float pairs on the upper envelope
Examples:
>>> skyline_values("31513")
[3.0, 5.0, 3.0]
>>> skyline_values([1, 3, 1])
[1.0, 3.0, 1.0]
Source code in amads/polyphony/envelope.py
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | |
valleyline_values
¶
valleyline_values(source, tolerance: float = 0.0) -> list[float]
Lower envelope as a plain list of y-values (onset coordinates omitted).
Parameters:
-
source–(See "Supported input forms" at the top of this module.)
- str of digits,
- sequence of scalars,
- sequence of (onset, pitch) pairs,
- score object with .find_all(Note)
-
tolerance(float, default:0.0) –points that dip no more than this amount below their neighbours are also removed (default 0 = exact envelope)
Returns:
-
PointList–list of (onset, pitch) float pairs on the upper envelope
Examples:
>>> valleyline_values("31513")
[3.0, 1.0, 1.0, 3.0]
Source code in amads/polyphony/envelope.py
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 | |