Skip to content

Commit

Permalink
Added support for realtime MIDI output with classes MuPlayer and MuRe…
Browse files Browse the repository at this point in the history
…corder; tranferred time related methods to MuUtil as static functions; created MuMIDI.h and transferred all MIDI related definitions, types and constants to that file.
  • Loading branch information
carlosemello committed Mar 14, 2019
1 parent 32778bf commit 2d8e806
Show file tree
Hide file tree
Showing 11 changed files with 2,258 additions and 23 deletions.
99 changes: 99 additions & 0 deletions MuMIDI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//*********************************************
//***************** NCM-UnB *******************
//******** (c) Carlos Eduardo Mello ***********
//*********************************************
// This softwre may be freely reproduced,
// copied, modified, and reused, as long as
// it retains, in all forms, the above credits.
//*********************************************

/** @file MuMIDI.h
*
* @brief MIDI definitions
*
* @author Carlos Eduardo Mello
* @date 3/3/2019
*
* @details
*
* This file describes the MIDI related data structures and definitions
* which are use throughout the MuM Library for input and output within the
* new Realtime playback and input functionality. Some of the definitions
* here were previously found in other class header files. They were
* transfered to this place to facilitate project organization and #include
* directives.
*
**/

#ifndef MuMIDI_H
#define MuMIDI_H

/**
* @brief MIDI event structure
*
* @details
*
* This structure is used to describe a typical MIDI event
* associated with a time stamp. MIDI events are ued by
* MuM to output note-on and note-off info, for playback and
* sequencing. See MuNote::MIDIOn() and MuNote::MIDIOff()
* for more details. This structure is also used by MuPlayer
* in output queues and by MuRecorder in input ring buffers
**/
struct MuMIDIMessage
{
//! @brief MIDI status byte: [1XXXCCCC]
unsigned char status;
//! @brief MIDI data byte: pitch number (0-127) [0VVVVVVV]
unsigned char data1;
//! @brief MIDI data byte: key velocity (0-127) [0VVVVVVV]
unsigned char data2;
//! @brief time stamp in seconds
float time;
};
typedef struct MuMIDIMessage MuMIDIMessage;

/**
* @brief MIDI Buffer structure
*
* @details
*
* MuMIDIBuffer is a structure containing a buffer of
* MuMIDIMessages. The also structure contains two
* variables to keep track of the number of messages
* in the buffer.
*
* The 'data' field in this structure is a pointer to
* receive a dynamically allocated array of type MuMIDIMessage.
* Normally, code passing this struture is responsible for
* allocating this memory, while code in the receiving end
* should free the buffer when it is no longer needed. The
* other two fields, 'max' and 'count' control buffer size.
* 'max' should contain the maximum number of elements in
* the array. This value should be modified only once, when
* memory is allocated. 'count' stores the number of elements
* currently in use. For obvious reasons, 'count' should
* allways be less or equal to 'max'. This simple
* vector of MIDI messages is used to copy and pass
* blocks of MIDI data between various parts of MuM,
* particularly in the input class (see MuRecorder).
*
**/
struct MuMIDIBuffer
{
//! @brief pointer to the first message in the buffer
MuMIDIMessage * data;
//! @brief maximum number of messages allowed in the buffer
long max;
//! @brief number of used/valid messages in the buffer
long count;
};
typedef struct MuMIDIMessage MuMIDIMessage;

//! @brief default size for MIDI message buffers
const long DEFAULT_BUFFER_SIZE = 1024;




#endif /* MuMIDI_H */
176 changes: 175 additions & 1 deletion MuMaterial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ MuMaterial MuMaterial::GetNotes( int voiceNumber, long from, long through )
// if requested range is valid...
if( ( (from >= 0) && (from < n) ) &&
( (through >=0) && (through < n) ) &&
( (through > from) )
( (through >= from) )
)
{
for( i = from; i <= through; i++ )
Expand Down Expand Up @@ -1131,6 +1131,57 @@ MuMaterial MuMaterial::GetNotesSoundingAt(int voiceNumber, float time)
return outMaterial;
}

MuMaterial MuMaterial::GetFrase(int voiceNumber, long from)
{
MuMaterial mat;
MuNote note;
long i, through;
bool foundRest = false;

if(voiceNumber >= NumberOfVoices())
{
lastError.Set(MuERROR_INVALID_VOICE_NUMBER);
return mat;
}

long n = NumberOfNotes(voiceNumber);

if(from >= n)
{
lastError.Set(MuERROR_INVALID_PARAMETER);
return mat;
}

// Starting at the requested note...
for(i = from; i < n; i++)
{
// check every note...
note = GetNote(i);

// if we find a rest...
if(note.Amp() == 0.0 || note.Pitch() == 0)
{
// we point to the last note before the rest...
through = (i - 1);
// and extract the frase up to that point...
mat = GetNotes(0, from, through);
// then flag that we found the rest...
foundRest = true;
// and get out of the loop...
break;
}
}

// if no rest was found...
if(!foundRest)
{
// return the notes through the end of the voice...
mat = GetNotes(0, from, (n-1));
}

return mat;
}

bool MuMaterial::Contains( int voiceNumber, int pitch )
{
lastError.Set(MuERROR_NONE);
Expand Down Expand Up @@ -1870,6 +1921,10 @@ void MuMaterial::CycleRhythm(int voiceNumber, int times)
Sort(0);
}

void MuMaterial::AddRestToNote(int voiceNumber, long noteNumber, float ratio)
{

}

// Segmentation

Expand Down Expand Up @@ -2807,6 +2862,125 @@ void MuMaterial::LoadScore(string fileName, short mode) // [PUBLIC]
}
}

// populates the receiving material with data from a MIDI buffer...
void MuMaterial::LoadMIDIBuffer(MuMIDIBuffer inBuffer, short mode)
{
lastError.Set(MuERROR_NONE);
long i,j,n;
MuNote note;
MuMIDIMessage firstEvent,secondEvent;
bool foundNote = false;

// Go through every noteOn event, comparing it to the remaining
// events, until we find a suitable note termination...
n = inBuffer.count;
for(i = 0; i < n; i++)
{
firstEvent = inBuffer.data[i];
foundNote = false;

// if this is a noteOn event and key velocity is not zero...
if( ( (firstEvent.status & 0xF0) == 0x90) && (firstEvent.data2 != 0) )
{
// we look ahead for its corresponding noteOff event...
for(j = i+1; j < n; j++)
{
secondEvent = inBuffer.data[j];

// if next event is for the same channel and same pitch...
if(((firstEvent.status & 0x0F) == (secondEvent.status & 0x0F)) &&
(firstEvent.data1 == secondEvent.data1) )
{
// if this is a noteOff for the same pitch...
if( ((secondEvent.status & 0xF0) == 0x80) ||
// or if it is a noteOn and key velocity is 0...
(((secondEvent.status & 0xF0) == 0x90) && (secondEvent.data2 == 0))
)
{
// we store the complete note...
note.SetFromMIDI(firstEvent, secondEvent);
AddNote(note);
note.Clear();
// if we found a complete note, we skip the inner loop
// and move on to the next noteOn...
foundNote = true;
break;
}
}
}

// after comparing to all remaining events, if we couldn't find
// a note termination, we store the event without duration...
if (!foundNote)
{
note.SetStart(firstEvent.time);
note.SetPitch(firstEvent.data1);
note.SetAmp(firstEvent.data2/128.0);
note.SetInstr((firstEvent.status & 0x0F) + 1);
note.SetDur(0);
AddNote(note);
note.Clear();

}
}
}

// After checking the entire buffer for notes,
// decide what to do with the notes that have duration 0.
n = NumberOfNotes();
for(i = 0; i < n; i++)
{
note = GetNote(i);
if(note.Dur() == 0)
{
switch(mode)
{
// in purge mode, remove all incomplete notes...
case MIDI_BUFFER_MODE_PURGE:
{
RemoveNote(i);
i--;
n--;
break;
}

// in extend mode, incomplete notes last until the end
// of material. if note starts beyond the duration of
// all other notes, it gets purged.
case MIDI_BUFFER_MODE_EXTEND:
{
float dur = Dur()-note.Start();
if (dur != 0)
{
note.SetDur(dur);
SetNote(i, note);
}
else
{
RemoveNote(i);
}
break;
}
// in melodic mode, incomlete notes last until
// the begining of next note. if note is the last
case MIDI_BUFFER_MODE_MELODIC:
{
if(i == n-1)
{
RemoveNote(i);
}
else
{
MuNote nextNote = GetNote(i+1);
note.SetDur(nextNote.Start() - note.Start());
SetNote(i, note);
}
break;
}
}
}
}
}

// Generates Orchestra Definitions...
string MuMaterial::Orchestra(void) // [PUBLIC]
Expand Down
Loading

0 comments on commit 2d8e806

Please sign in to comment.