Working on music-pitch

42 views
Skip to first unread message

Hans Höglund

unread,
Jun 21, 2014, 5:53:02 PM6/21/14
to Edward Lilley, music-sui...@googlegroups.com
Hi Edward (and list),

Thanks for looking into this. Some questions:

- I understand that you propose to represent common intervals as pair of chromatic and diatonic steps (m2 and A2), and remove the separate representation of octaves. The general notion of combining two scale types (with large and small steps) seem more general than just common/western pitch. Could we abstract this pattern out as a type constructor, of which Common.Interval is a specification?

-  I like the simplicity of your (^+^) and negateV definitions, and the connection to basis/linear combinations. However the definitions of mkInterval and extractQuality seems ad-hoc too me, though there may be a pattern that I am missing. Maybe this has to do with the specific nature of the chromatic/diatonic scales (which would imply that a generalization as per above could mitigate the problem).

- Could we use Data.Basis (from vector-space) as a general interface to diatonic/chromatic? I.e. the basis for Common.Interval would be a type (data CommonBasis = Diatonic | Chromatic) or similar.

What do you think?

Hans


On 21 jun 2014, at 18:32, Edward Lilley wrote:

I've rewritten parts of Intervals.hs (from
music-pitch/src/Music/Pitch/Common) to use generalised 'vectors' as the
internal implementation of intervals. It's a bit rough-and-ready at the
moment, so I'll take some time to tidy it up shortly. Currently the Show
and AdditiveGroup instances should work. It's in my fork of music-pitch
under my GitHub account.

The guts of the new implementation are in the new extractQuality and
mkInterval functions, which are analogous to faInt and faIntToQual in
Music.hs in my own AbstractMusic project.

I've tried to keep the external interface to Intervals.hs as unchanged
as possible; the one concern I have is the Quality datatype -- note that
I've resurrected the Augmentable Quality instance, purely for the use of
the extractQuality function, to make the implementation of
extractQuality recursive (and hence more elegant). However, Quality is still a
bit dodgy for general usage (e.g. augment (Diminish 1) == ???).

thanks,
Edward



Hans Höglund

unread,
Jun 21, 2014, 5:55:26 PM6/21/14
to Edward Lilley, music-sui...@googlegroups.com
I meant (A1 and d2) of course. Sorry for the typo.

H

Edward Lilley

unread,
Jun 28, 2014, 8:00:30 PM6/28/14
to Hans Höglund, music-sui...@googlegroups.com
My branch is updated, and mkInterval should actually work now...

> - I understand that you propose to represent common intervals as pair
> of chromatic and diatonic steps (d2 and A1), and remove the separate
> representation of octaves. The general notion of combining two scale
> types (with large and small steps) seem more general than just
> common/western pitch. Could we abstract this pattern out as a type
> constructor, of which Common.Interval is a specification?

I'm not sure it's the idea of *two* scale types that's general -- I
think it's the fact that the intervals are generated by a certain
number, n, of generators (akin to the number of generators of the tuning
system, and I will try to make that clear when I get started on some
tuning-related code). For common Western notation, n = 2, while e.g. for
midi, n = 1 (notes are just integers in midi).

For n > 2, some new interval names have to be invented, see for example
http://en.wikipedia.org/wiki/List_of_intervals_in_5-limit_just_intonation
and also the file FiveLimit.hs in my own AbstractMusic, where I've
fiddled around and tried to get something consistent (with n = 3)
working (it's still buggy, probably).

There's a general risk here of confusing interval *nomenclature* with
interval *tuning*, particularly if we try to generalise to non-Western
scales. We could of course represent any scale as a list of frequency
ratios, but we have to admit the possibility of the scale being tuned in
different ways, but the terminology not changing (just like in Western
music). So we would invent a datatype whose structure reflects the
interval naming system in (presumably) the language of the culture from
which the scale comes. With this two-generator A1/d2 business I think
I've found the right abstraction for Western music, but it might require
some serious thought (and maybe even linguistics!) for other
systems. Or, maybe other systems are all much simpler, and don't have
lots of pesky accidentals, who knows! I'll start looking into it.

> - I like the simplicity of your (^+^) and negateV definitions, and the
> connection to basis/linear combinations. However the definitions of
> mkInterval and extractQuality seems ad-hoc too me, though there may be
> a pattern that I am missing. Maybe this has to do with the specific
> nature of the chromatic/diatonic scales (which would imply that a
> generalization as per above could mitigate the problem).

There is a pattern if you squint a bit! The apparent ad-hocness of
extractQuality comes from the fact that the "base" intervals (Perfect,
Major, Minor) are unevenly distributed throughout the octave, so there
has to be a base case of recursion for each one -- at least the way I've
written it. Perhaps the big pattern-matching blob could be simplified
slightly, but that might make it even harder to read!

The exact way these two functions have ended up being written is
partially due to the fact that I've chosen A1/d2 as the basis
vectors. Two others could be chosen, but I think this choice gives as
neat results as we're going to get (though actually A1/m2 would probably
be "nice" too).

mkInterval just builds up the Perfect/Major/Minor intervals of the scale
step by step, then uses recursion to reduce diminished/augmented
intervals to the base cases.

The main problem here is beyond our control: expressions like "Perfect
Third" and "Major Fourth" have no meaning, and there are also
redundancies e.g. the fact that "Augmented Minor Third" === "Major
Third". As a result, datatypes like Quality (from Interval.hs) will
always be a bit tricky to define. In AbstractMusic I get around this by
requiring that the user say things like "Diminished Minor Third"
explicitly.

> - Could we use Data.Basis (from vector-space) as a general interface
> to diatonic/chromatic? I.e. the basis for Common.Interval would be a
> type (data CommonBasis = Diatonic | Chromatic) or similar.

That sounds like an interesting idea, though unfortunately I'm not
familiar with how to use Data.Basis & I couldn't find any documentation
either. Perhaps you could give some simple examples? At the bottom of
Interval.hs (in my branch) I've put a basis-conversion function
(convertBasis), which I imagine would be useful in this situation.

Edward

Hans Höglund

unread,
Jun 29, 2014, 7:21:00 PM6/29/14
to Edward Lilley, music-sui...@googlegroups.com

On 29 jun 2014, at 01:49, Edward Lilley wrote:

> I'm not sure it's the idea of *two* scale types that's general -- I
> think it's the fact that the intervals are generated by a certain
> number, n, of generators (akin to the number of generators of the tuning
> system, and I will try to make that clear when I get started on some
> tuning-related code). For common Western notation, n = 2, while e.g. for
> midi, n = 1 (notes are just integers in midi).

That seems reasonable. In other words, a pitch system compromises one or more scales, which may be intoned in various ways. As the goal of this library (in my view) is to provide CMN as a *special case*, I suggest we concentrate on the cases (n = 1 and n = 2), which will allow us to define all common equal temperament scales (n = 1), unevenly spaced scales such as common pentatonic, church modes etc (n = 1) as well as CM/Western classical (n = 2).

I do not know enough about other pitch systems to know whether there is any (n > 2) system in common use, but that may be something to explore later.

> For n > 2, some new interval names have to be invented, see for example
> http://en.wikipedia.org/wiki/List_of_intervals_in_5-limit_just_intonation
> and also the file FiveLimit.hs in my own AbstractMusic, where I've
> fiddled around and tried to get something consistent (with n = 3)
> working (it's still buggy, probably).
>
> There's a general risk here of confusing interval *nomenclature* with
> interval *tuning*, particularly if we try to generalise to non-Western

> scales. We could of course represent any scale as a list of frequency
> ratios, but we have to admit the possibility of the scale being tuned in
> different ways, but the terminology not changing (just like in Western
> music). So we would invent a datatype whose structure reflects the
> interval naming system in (presumably) the language of the culture from
> which the scale comes.

This is a big issue for me: how do we make these concepts distinct (but interoperable) in the library design? I think the key is to use different types just as you say. I.e. one category of types for *nominal* pitch/intervals, another one for *sounding* pitch/frequency ratios. The module hierarchy was intended to reflect this, though this is probably not obvious from the current docs. (That is, Pitch.Absolute contains sounding pitch, Pitch.Common et al is nominal pitch, and Pitch.Intonation converts the former to the latter).

However there are many grey areas. For example it seems reasonable to have a type representing a 24-TET scale, and even though this is a nominal pitch type it clearly presupposes a specific intonation, and could thus be said to be a sounding pitch type as well.

> With this two-generator A1/d2 business I think
> I've found the right abstraction for Western music, but it might require
> some serious thought (and maybe even linguistics!) for other
> systems.

I agree, though think A1 and d2 are not particularly special except for the fact that they represent (1,0) and (0,1) in the (Chromatic, Diatonic) space. This is of course nice because:

- It is convenient to think of the representation as "number of chromatic/diatonic steps"
- It happens to be the standard basis for a two-dimensional vector space

>> - I like the simplicity of your (^+^) and negateV definitions, and the
>> connection to basis/linear combinations. However the definitions of
>> mkInterval and extractQuality seems ad-hoc too me, though there may be
>> a pattern that I am missing. Maybe this has to do with the specific
>> nature of the chromatic/diatonic scales (which would imply that a
>> generalization as per above could mitigate the problem).
>
> There is a pattern if you squint a bit! The apparent ad-hocness of
> extractQuality comes from the fact that the "base" intervals (Perfect,
> Major, Minor) are unevenly distributed throughout the octave, so there
> has to be a base case of recursion for each one -- at least the way I've
> written it.
...
> The exact way these two functions have ended up being written is
> partially due to the fact that I've chosen A1/d2 as the basis
> vectors.
...

As I said, i think the choice of basis is fine. The ad-hocness is simply due to the nature of the chromatic/diatonic scales. Intuitively I think *both* mkInterval and extractQuality could be generated from the list [2,2,1,2,2,2,1] as that list fully describes the relationship between the two scales.

> The main problem here is beyond our control: expressions like "Perfect
> Third" and "Major Fourth" have no meaning, and there are also
> redundancies e.g. the fact that "Augmented Minor Third" === "Major
> Third".
> As a result, datatypes like Quality (from Interval.hs) will
> always be a bit tricky to define. In AbstractMusic I get around this by
> requiring that the user say things like "Diminished Minor Third"
> explicitly.

I think a good way to do it here is to view (Quality, Number) as a form of *expression* that describes intervals. We can then define a *normal form* of these expressions and define the a one-to-one relationship between interval expression and intervals (represented internally as pairs). I might try to sketch this in a separate branch.

>> - Could we use Data.Basis (from vector-space) as a general interface
>> to diatonic/chromatic? I.e. the basis for Common.Interval would be a
>> type (data CommonBasis = Diatonic | Chromatic) or similar.
>
> That sounds like an interesting idea, though unfortunately I'm not
> familiar with how to use Data.Basis & I couldn't find any documentation
> either. Perhaps you could give some simple examples? At the bottom of
> Interval.hs (in my branch) I've put a basis-conversion function
> (convertBasis), which I imagine would be useful in this situation.

For Data.Basis docs, see https://hackage.haskell.org/package/vector-space/docs/Data-Basis.html

I think it would be as easy as

> data CommonBasis = Chromatic | Diatonic
>
> instance HasBasis Interval where
> type Basis = CommonBasis
>
> basisValue Chromatic = _A1 -- zero diatonic, one chromatic steps
> basisValue Diatonic = d2 -- one diatonic, zero chromatic steps
> decompose (Interval (d, c)) = [(Diatonic, d), (Chromatic, c)]
> decompose' v b = fromJust $ lookup b (decompose v)

See if you can make that work.

I will try to leave music-pitch alone for some time and concentrate on other things, but let me know if you need any more feedback or want me to merge something.

Sincerly,
Hans
Reply all
Reply to author
Forward
0 new messages