Thread: Grouping Question

  1. #1
    Registered User
    Join Date
    Jan 2010
    Posts
    19

    Grouping Question

    Hello all,

    I'm trying to write code that groups together the members existing within a multi-dimensional array.

    What I've written so far takes in a midi file and spits out the following list:

    note: 41 velocity: 56 time: 0.00833333 duration: 3.98958 interval: 0
    note: 53 velocity: 70 time: 0.00833333 duration: 3.98958 interval: 12
    note: 60 velocity: 56 time: 0.00833333 duration: 3.98958 interval: 7
    note: 63 velocity: 56 time: 0.00833333 duration: 3.98958 interval: 3
    note: 65 velocity: 56 time: 0.00833333 duration: 3.98958 interval: 2
    note: 69 velocity: 70 time: 0.00833333 duration: 3.98958 interval: 4
    note: 34 velocity: 54 time: 4.00208 duration: 3.99583 interval: -35
    note: 46 velocity: 56 time: 4.00208 duration: 3.99583 interval: 12
    note: 53 velocity: 56 time: 4.00208 duration: 3.99583 interval: 7
    note: 58 velocity: 56 time: 4.00208 duration: 3.99583 interval: 5
    note: 58 velocity: 70 time: 4.00208 duration: 3.99583 interval: 0
    note: 65 velocity: 56 time: 4.00208 duration: 3.99583 interval: 7
    note: 70 velocity: 56 time: 4.00208 duration: 3.99583 interval: 5
    note: 74 velocity: 56 time: 4.00208 duration: 3.99583 interval: 4
    note: 77 velocity: 70 time: 4.00208 duration: 3.99583 interval: 3
    note: 34 velocity: 54 time: 8.00208 duration: 3.99583 interval: -43
    note: 46 velocity: 56 time: 8.00208 duration: 3.99583 interval: 12
    note: 53 velocity: 56 time: 8.00208 duration: 3.99583 interval: 7
    note: 58 velocity: 56 time: 8.00208 duration: 3.99583 interval: 5
    note: 58 velocity: 70 time: 8.00208 duration: 3.99583 interval: 0
    note: 65 velocity: 56 time: 8.00208 duration: 3.99583 interval: 7
    note: 70 velocity: 56 time: 8.00208 duration: 3.99583 interval: 5
    note: 74 velocity: 56 time: 8.00208 duration: 3.99583 interval: 4
    note: 77 velocity: 70 time: 8.00208 duration: 3.99583 interval: 3
    note: 41 velocity: 56 time: 12.0021 duration: 3.99583 interval: -36
    note: 53 velocity: 56 time: 12.0021 duration: 3.99583 interval: 12
    note: 57 velocity: 70 time: 12.0021 duration: 3.99583 interval: 4
    note: 60 velocity: 56 time: 12.0021 duration: 3.99583 interval: 3
    note: 65 velocity: 56 time: 12.0021 duration: 3.99583 interval: 5
    note: 69 velocity: 56 time: 12.0021 duration: 3.99583 interval: 4
    note: 72 velocity: 70 time: 12.0021 duration: 3.99583 interval: 3


    NoteInfo[0Each parameter is stored in a multidimensional array as follows:

    i = each event

    NoteInfo[0][i] = note = midi pitch
    NoteInfo[1][i] = velocity = how strong the note is played
    NoteInfo[2][i] = time = when the note occurs
    NoteInfo[3][i] = duration = how long the note lasts
    (please ignore interval. It's not significant right now)

    As you can see on the list, many of the notes occur at the same time, because these notes represent a sequence of chords. What I'm trying is to group together the notes that occur at the same time into another multidimensional array:

    where "prog" represents current chord, or the number of groups of notes that occur at the same time:
    Chords[prog][note]
    i.e. all of the notes that occur at 0.00833.. will be in Chords[0][note], the ones at 4.00208 in Chords[1][note] and so forth.

    I've tried using for loops and while loops but can't seem to get it to work. This may be a novice question but I had to learn C for a job very quickly and probably have skipped a lot of the basics.

    Please give me some words of advice. Thanks.

  2. #2
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    You may want to take a look at this...
    MIDI Technical Fanatic's Brainwashing Center

    As for your problem of collecting tabs ... it should be a fairly simple matter to scan the file and identify a list of multiple notes, in the same channel, at the same time... think about a construct similar to "If this time is the same as the previous time..."

    However, you should know that in most midi files (the good ones anyway) a lot of care is taken to "humanize" the performance by offsetting noteon and noteoff events by a few ticks. The tempo is also vaired up and down by some percentage in track0, This is done because (for example) strumming a guitar does not activate all strings at once and it sounds horridly unnatrual if they are all turned on at once in a midi file.

    What this means is that finding chords is a lot more complex than just tracking tick counts. You will have to actually look at "how many notes are currently on in this channel", decide what level of polyphony you wish and track from there... the best method is to break each channel out into a "stack" like structure of live notes. Whenever you get more than 1 on your "stack" it's possibly a chord.

    I've done this before, to create guitar tabs and believe me it's not a trivial task.

    Your best approach (well, the one that worked best for me) is to define a "midi event" struct and create an array of these structs for each channel in the midi file. You can then add a array of polyphony counters to each channel that will reflect how many notes are currently on. From there you can extract tabs by looking at when the polyphony number changes, rather than when a note is triggered.

    Gees... I hope that isn't too confusing...
    Last edited by CommonTater; 10-23-2010 at 06:57 PM.

  3. #3
    Registered User
    Join Date
    Jan 2010
    Posts
    19
    Thanks for the info.

    As for the midi files, the ones I'm using are formatted the same way so they will be starting at the same time.

    But you're right, if they were all at different times and still had to figure out the chords then that would not be n easy task to take on.

  4. #4
    Gawking at stupidity
    Join Date
    Jul 2004
    Location
    Oregon, USA
    Posts
    3,218
    I would suggest using a struct instead of a multi-dimensional array. Maybe something like this:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    struct note
    {
      int tone;
      int velocity;
      double time;
      double duration;
      int interval;
    };
    
    struct note *get_notes_at(double time, struct note *all_notes, int num_notes, int *num_concurrent_notes)
    {
      int i;
    
      struct note *notes = NULL;
      *num_concurrent_notes = 0;
    
      for(i = 0;i < num_notes;++i)
      {
        if(all_notes[i].time == time)
        {
          if(!notes)
            notes = malloc(sizeof(struct note));
          else
          {
            struct note *tmp = realloc(notes, sizeof(struct note) * ((*num_concurrent_notes) + 1));
            if(!tmp)
            {
              /* Memory allocation failure. Take appropriate measures */
              return NULL;
            }
            notes = tmp;
          }
          notes[(*num_concurrent_notes)++] = all_notes[i];
        }
      }
    
      return notes;
    }
    
    int main(void)
    {
      struct note notes[] = { { 41, 56, 0.00833333, 3.98958, 0 },
                              { 53, 70, 0.00833333, 3.98958, 12 },
                              { 60, 56, 0.00833333, 3.98958, 7 },
                              { 63, 56, 0.00833333, 3.98958, 3 },
                              { 65, 56, 0.00833333, 3.98958, 2 },
                              { 69, 70, 0.00833333, 3.98958, 4 },
                              { 34, 54, 4.00208,    3.99583, -35 },
                              { 46, 56, 4.00208,    3.99583, 12 },
                              { 53, 56, 4.00208,    3.99583, 7 },
                              { 58, 56, 4.00208,    3.99583, 5 }
                            };
      struct note *concurrent_notes;
      int n_concurrent_notes;
      int i;
    
      concurrent_notes = get_notes_at(4.00208, notes, sizeof(notes) / sizeof(struct note), &n_concurrent_notes);
      for(i = 0;i < n_concurrent_notes;++i)
        printf("%d\n", concurrent_notes[i].tone);
      free(concurrent_notes);
    
      return 0;
    }
    My output:
    Code:
    34
    46
    53
    58
    If you understand what you're doing, you're not learning anything.

  5. #5
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    I agree, a struct (in an array of structs), will be far easier to work with.

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Adak View Post
    I agree, a struct (in an array of structs), will be far easier to work with.
    It's the only way to go in this case.

    Take a look at Geff Glatt's excellent breakdown of midi file content... it's not a simple file by any means. In fact, midi files were some of the first to work with data compression... a "byte" of midi data can vary from 3 to 7 bits in length. It's a real picnic to decode and load into an array. In fact, I think that's where half of my gray hair came from...

    The actual polyphony counter is a pretty simple mechanism...
    If you have 5,000 note events in a channel (which is not uncommon) you make a parallel array of bytes. For each noteon you add 1, for each noteoff you subtract one and record the count beside each event.

    To extract a tab, you simply start with polyphony > 1 and record the on notes (not noteon events) until polphony returns to < 2. (A really geat test piece for this is "Chopsticks")

    You should also note that most modern day midi enabled keyboards are capable of 16 or more simultaneous channels (each having one instrument sound assigned to it) and up to 16 simultaneous notes per channel... so polphony can reach 256 simultaneous notes. In a midi file it is common to have 64 or more simultaneous notes to sort into tabs, with not all combinations revealing a "humanly possible" tab.

    It is a fascinating thing to play with... but I'm glad the format was left behind.

  7. #7
    Registered User
    Join Date
    Jan 2010
    Posts
    19
    Thanks for all your helpful responses. I haven't worked much with structs and I'll definitely study the example and learn it.

    I kept working with multidimensional arrays and ended up figuring out what I needed:

    Code:
    	prog = 0;
    	int event = 0;
    	for(int i = 1; i < q + 1 ; i++){
    		if(NoteInfo[2][i-1] == NoteInfo[2][i]){//Separate each note by progression 			
    			Chords[prog][event] = NoteInfo[0][i-1];
    			ctone++;
    			NumNotes[prog] = ctone;
    			event++;
    		}
    		
    		else if(NoteInfo[2][i-1] != NoteInfo[2][i]){
    			Chords[prog][event] = NoteInfo[0][i-1];
    			ctone++;
    			NumNotes[prog] = ctone;
    			prog++;
    			ctone = 0;
    			event = 0;
    		}
    	}
    q represents the number of events stored in each dimension of the array that was acquired when the midi info was read into the array.

    Now I need to figure out a way to code a chord recognizer for each progression...

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Structs would probably make this much simpler for you... You would be working with a single dimension array, progressing numerically through time and each struct would contain one midi event so you could look at channel and eventcode to extract tabs. The actual struct needent be all that complex. Something like this, might work...

    Code:
    typedef struct t_midi
        {  int Time;           // time value in milliseconds
            int Channel;     // channel number
            int  Event;        // midi event code
            int  Note;         // note value 
            int  Velocity;    //  event byte 2
            int  Poly;  }      // current polyphony
            Midi, *pMidi;
    Building an array of events is not that difficult... Make 1 pass through the file and simply count events... then use malloc to create an array... pMidi Song = calloc(events,sizeof(Midi); From there access is by event number Song[12] ....

    Accessing the array would look like song[event].Channel song[event].Velocity etc. Much easier to keep track of than multidimensional arrays.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Question bout my work
    By SirTalksAlots in forum C Programming
    Replies: 4
    Last Post: 07-18-2010, 03:23 PM
  2. A question about a question
    By hausburn in forum C++ Programming
    Replies: 3
    Last Post: 04-25-2010, 05:24 AM
  3. Alice....
    By Lurker in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 06-20-2005, 02:51 PM
  4. Question...
    By TechWins in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 07-28-2003, 09:47 PM
  5. Question, question!
    By oskilian in forum A Brief History of Cprogramming.com
    Replies: 5
    Last Post: 12-24-2001, 01:47 AM