Re: XQuery and music information retrieval

Michael Good wrote:
> 
> Thank you for letting me elaborate the problem - I left out an important
> aspect. We want to match
> 
> <part name="voice">
>   <measure number="1">
>     <rest/>
>     <rest/>
>     <note>C</note>
>     <note>D</note>
>   </measure>
>   <measure number="2">
>     <note>E</note>
>     <note>C</note>
>     <rest/>
>     <rest/>
>   </measure>
> </part>
> 
> But not
> 
> <part name="voice">
>   <measure number="120">
>     <rest/>
>     <rest/>
>     <note>C</note>
>     <note>D</note>
>   </measure>
> </part>
> <part name="piano">
>   <measure number="1">
>     <note>E</note>
>     <note>C</note>
>     <rest/>
>     <rest/>
>   </measure>
> </part>
> 
> So I don't think the following::note XPath will work to exclude the
> latter example.

Nope, you're right, it won't.

> Sorry I did not include that aspect in my original problem.
> This motivated the general request to restrict the sequence to a
> certain level of ancestor - in this case, we a common grandparent -
> a common parent is too restrictive, a common great-grandparent too
> general.

Well, how about this XQuery:

  FOR
    # Find a C note at any level:
    $note1 IN //note[.="C"],

    # Remember its grandparent-element:
    $common_ancestor IN $note1/ancestor::*[2], 

    # Find the next-following note...
    $note2 IN $note1/following::note[1]
          # and require it to be a D note...
          [.="D"]
          # and to have the same grandparent: 
          [./ancestor::*[2] == $common_ancestor],

    # Then find the next note after *that*...
    $note3 IN $note2/following::note[1]
          # and require it to be an E note...
          [.="E"]
          # and to have the same grandparent:
          [./ancestor::*[2] == $common_ancestor],

    # And similarly, the last note is another "C":
    $note4 IN $note3/following::note[1]
          [.="C"]
          [./ancestor::*[2] == $common_ancestor]

    RETURN
      ... something involving $node1 etc ...

You could put this in a function and parameterize it by the level of
the common ancestor (here "2").  Parameterizing by melody (of arbitary
length) would be a little harder: you'd have to recast the nested
for-loops into a recursive function. 

Another (somewhat more elegant) approach would be:

    # Go to each potential common-ancestor:
    FOR $part in //part

      # and compile a list of its notes: 
      LET $notes := $part//note

        # then generate each subsequence of 4 notes:
        FOR $i IN 1 TO count($notes)-3
          LET $four_notes := $notes[$i TO $i+3]

            # and check that the 4 notes have the right pitch:
            WHERE string($four_notes) = ( "C", "D", "E", "C" )

              RETURN ... something involving $four_notes ...

(The condition in the where-clause assumes various semantics that haven't
been specified yet. If necessary, it could be written more explicitly.)
This XQuery would be easy to parameterize over any sequence of strings
denoting pitch values.

-Michael Dyck

Received on Thursday, 19 July 2001 02:30:46 UTC