dodanie folderu

This commit is contained in:
Mariusz Pacek committed 2013-06-23 20:32:40 +02:00
1 parent e4ce65e6f8
commit 377da0fb5c
3 files changed
+1734

No files matched your search

+696
View File
@@ -0,0 +1,696 @@
//=====================================================================================
// wxMidi: A MIDI interface based on PortMidi, the Portable Real-Time MIDI Library
// --------------------------------------------------------------------------------
//
// Author: Cecilio Salmeron
// Copyright: (c) 2005-2007 Cecilio Salmeron
// Licence: wxWidgets licence, version 3.1 or later at your choice.
//=====================================================================================
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "wxMidi.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wxMidi.h"
/*
To do, when a new version of portmidi:
1. Review error messages (method GetErrorText(), copied and adapted from pormidi.c)
*/
// Private global functions
//---------------------------------------------------------------------------------
wxMidiTimestamp wxMidiGetTime() { return Pt_Time(); }
//Define the new command event to inform that MIDI input data is available
DEFINE_EVENT_TYPE(wxEVT_MIDI_INPUT)
//================================================================================
// Implementation of classes wxMidiPmEvent, wxMidiShortEvent and wxMidiSysExEvent
//================================================================================
#define SYSEX_CHUNK_SIZE 4 // PmEvent size
#define SYSEX_BUFFER_SIZE (SYSEX_CHUNK_SIZE * 1024) // alloc in 4K chunks
wxMidiSysExMessage::wxMidiSysExMessage()
: wxMidiMessage()
{
m_type = wxMIDI_SYSEX_MSG;
m_pMessage = (wxByte*) NULL;
m_nSize = 0;
m_nError = wxMIDI_NO_ERROR;
m_timestamp = 0;
}
wxMidiSysExMessage::wxMidiSysExMessage(wxByte* msg, wxMidiTimestamp timestamp)
: wxMidiMessage()
{
m_type = wxMIDI_SYSEX_MSG;
m_pMessage = (wxByte*) NULL;
m_nSize = 0;
m_nError = wxMIDI_NO_ERROR;
m_timestamp = timestamp;
//verify start of message
if (*msg != 0xF0) {
m_nError = wxMIDI_ERROR_BadSysExMsg_Start;
return;
}
//determine size of message
wxByte* pData = msg;
for (; m_nSize < 10000 && *pData != 0xF7; pData++) {
m_nSize++;
}
if (m_nSize == 10000) {
m_nError = wxMIDI_ERROR_BadSysExMsg_Length;
return;
}
// allocate the buffer
m_nSize++; //include the end of sysex byte 0xF7
m_pMessage = new wxByte[m_nSize];
// copy the data
memcpy(m_pMessage, msg, m_nSize);
}
wxMidiSysExMessage::~wxMidiSysExMessage()
{
if (m_pMessage) {
delete [] m_pMessage;
m_pMessage = (wxByte*) NULL;
}
}
//================================================================================
// Implementation of class wxMidiDevice
//================================================================================
wxMidiDevice::wxMidiDevice(wxMidiDeviceID nDevice)
{
m_nDevice = nDevice;
int devices = Pm_CountDevices();
if (m_nDevice > devices-1) m_nDevice = 0;
m_pInfo = Pm_GetDeviceInfo(m_nDevice);
m_stream = (PortMidiStream*)NULL;
}
wxMidiDevice::~wxMidiDevice()
{
/*
WARNING: Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure
referring to the device specified by id. The returned structure is owned by
the PortMidi implementation and must not be manipulated or freed.
*/
//if (m_pInfo) delete m_pInfo;
}
const wxString wxMidiDevice::DeviceName()
{
if (m_pInfo) {
#if defined(_UNICODE) || defined(UNICODE)
return wxString(m_pInfo->name, wxConvUTF8);
#else
return m_pInfo->name;
#endif
}
else {
wxString sMsg = _("Error: Device not properly created");
return sMsg;
}
}
const wxString wxMidiDevice::InterfaceUsed()
{
if (m_pInfo) {
#if defined(_UNICODE) || defined(UNICODE)
return wxString(m_pInfo->interf, wxConvUTF8);
#else
return m_pInfo->interf;
#endif
}
else {
wxString sMsg = _("Error: Device not properly created");
return sMsg;
}
}
bool wxMidiDevice::IsInputPort()
{
if (m_pInfo)
return (m_pInfo->input != 0);
else
return false;
}
bool wxMidiDevice::IsOutputPort()
{
if (m_pInfo)
return (m_pInfo->output != 0);
else
return false;
}
//================================================================================
// Class wxMidiOutDevice implementation
//================================================================================
wxMidiError wxMidiOutDevice::Open(long latency, void *DriverInfo)
{
// bufferSize = 0 How to set up an optimum value?
return (wxMidiError)Pm_OpenOutput(&m_stream, m_nDevice, DriverInfo,
0, NULL, NULL, latency );
}
wxMidiError wxMidiOutDevice::Write(wxMidiShortMessage* pMsg)
{
return (wxMidiError)Pm_Write(m_stream, pMsg->GetBuffer(), 1);
}
wxMidiError wxMidiOutDevice::Write(wxMidiSysExMessage* pMsg)
{
wxMidiError nError = pMsg->Error();
if (nError != wxMIDI_NO_ERROR)
return nError;
else
return (wxMidiError)Pm_WriteSysEx(m_stream,
pMsg->GetTimestamp(),
(unsigned char *)pMsg->GetMessage() );
}
wxMidiError wxMidiOutDevice::Write(wxByte* msg, wxMidiTimestamp when)
{
return (wxMidiError)Pm_WriteSysEx(m_stream,
when,
(unsigned char *)msg );
}
wxMidiError wxMidiOutDevice::NoteOff(int channel, int note, int velocity)
{
//0x80?0x8F Note off
wxMidiShortMessage msg(0x80+channel, note, velocity);
return Write(&msg);
}
wxMidiError wxMidiOutDevice::NoteOn(int channel, int note, int velocity)
{
//0x90?0x9F Note on
wxMidiShortMessage msg(0x90+channel, note, velocity);
return Write(&msg);
}
wxMidiError wxMidiOutDevice::ProgramChange(int channel, int instrument)
{
//0xC0?0xCF Program change
wxMidiShortMessage msg(0xC0+channel, instrument, 0);
return Write(&msg);
}
wxMidiError wxMidiOutDevice::AllSoundsOff()
{
wxMidiPmEvent buffer[32];
wxMidiTimestamp now = ::wxMidiGetTime();
int channel, i;
for (i=0, channel=0; channel < 16; channel++) {
buffer[i].message = Pm_Message(0xB0+channel, 0x78, 0);
buffer[i].timestamp = now;
i++;
buffer[i].message = Pm_Message(0xB0+channel, 0x7B, 0);
buffer[i].timestamp = now;
i++;
}
return (wxMidiError)Pm_Write(m_stream, buffer, 32);
}
//================================================================================
// Class wxMidiInDevice implementation
//================================================================================
wxMidiInDevice::wxMidiInDevice(wxMidiDeviceID nDevice)
: wxMidiDevice(nDevice)
{
m_pThread = (wxMidiThread*)NULL;
m_SysexBuffer = (wxByte*)NULL;
m_fReadingSysex = false;
m_fEventPending = false;
}
wxMidiInDevice::~wxMidiInDevice()
{
if (m_pThread) {
StopListening();
}
if (m_SysexBuffer) delete [] m_SysexBuffer;
}
wxMidiError wxMidiInDevice::Open(void *DriverInfo)
{
// bufferSize = 50 How to set up an optimum value?
return (wxMidiError)Pm_OpenInput(&m_stream, m_nDevice, DriverInfo,
50, NULL, NULL);
}
wxMidiError wxMidiInDevice::Read(wxMidiPmEvent *buffer, long* length )
{
/*
If no error, the real number of buffers read is returned in "length" and
value PmNoError is returned.
In case of error, the error type is returned.
*/
int nErr = (int) Pm_Read(m_stream, buffer, *length);
/* Pm_Read() retrieves midi data into a buffer, and returns the number
of events read. Result is a non-negative number unless an error occurs,
in which case a wxMidiError value will be returned. */
if (nErr < 0)
//error
return (wxMidiError)nErr;
else {
*length = (long)nErr;
return wxMIDI_NO_ERROR;
}
}
wxMidiMessage* wxMidiInDevice::Read(wxMidiError* pError)
{
/*
According to pormidi documentation, portmidi is allowed to pass real-time MIDI
messages embedded within the chunks of a sysex message, and it is up to the client
to detect, process, and remove these messages as they arrive.
To deal with this, xMidiInDevice will maintain buffers with data not yet delivered.
Flag m_fReadingSysex will signal that a sysex message was interrupted by a real-time one
Flag m_fEventPending will signal that there is a PmEvent pending to be processed and
delivered, as consecuence of a previous truncated sysex message.
*/
// get data from midi stream
PmEvent buffer;
wxMidiError nError;
if (m_fEventPending) {
buffer = m_event;
m_fEventPending = false;
}
else {
nError = (wxMidiError) Pm_Read( m_stream, &buffer, 1 );
// if read was not successful return the error
if (nError < wxMIDI_NO_ERROR) {
*pError = nError;
return (wxMidiMessage*)NULL;
}
// check if something read
if (nError == 0) {
*pError = wxMIDI_ERROR_NoDataAvailable;
return (wxMidiMessage*)NULL;
}
}
// check what type of message we are receiving
if (m_fReadingSysex || (Pm_MessageStatus( buffer.message ) == 0xF0))
{
// Start or continuation of a Sysex message. Move data to buffer and
// continue reading until end of message
//create the message object
wxMidiSysExMessage* pSysExMsg = new wxMidiSysExMessage();
if (!m_fReadingSysex)
{
//store timestamp
m_timestamp = buffer.timestamp;
// alloc a new buffer
m_SizeOfSysexBuffer = SYSEX_BUFFER_SIZE;
m_SysexBuffer = new wxByte[ m_SizeOfSysexBuffer ];
m_CurSysexDataPtr = m_SysexBuffer;
m_fReadingSysex = true;
}
//move data to buffer and continue reading until end of sysex message
bool fEndSysex = MoveDataToSysExBuffer(buffer.message);
while(!fEndSysex) {
nError = (wxMidiError) Pm_Read( m_stream, &buffer, 1 );
if (nError < wxMIDI_NO_ERROR) {
*pError = nError;
delete pSysExMsg;
delete [] m_SysexBuffer;
m_SysexBuffer = (wxByte*)NULL;
m_fReadingSysex = false;
return (wxMidiMessage*)NULL;
}
// check if it is a real-time message inserted into the sysex chunks stream
if (Pm_MessageStatus( buffer.message ) == 0xF8) {
//it is a real time message. Deliver it inmediately and save sysex buffer
*pError = wxMIDI_NO_ERROR;
wxMidiShortMessage* pShortMsg = new wxMidiShortMessage(
Pm_MessageStatus( buffer.message ),
0, 0 );
pShortMsg->SetTimestamp( buffer.timestamp );
delete pSysExMsg;
return pShortMsg;
}
/*
When receiving sysex messages, if you get a non-real-time status byte
but there was no EOX byte, it means the sysex message was somehow truncated.
This is not considered an error; e.g., a missing EOX can result from the user
disconnecting a MIDI cable during sysex transmission.
*/
// lets check if there is a status byte different from end-of-sysex
if (Pm_MessageStatus( buffer.message ) != 0xF7 &&
Pm_MessageStatus( buffer.message ) & 0x80 )
{
//The systex message is somehow truncated. Return the sysex message
// and store the new message received
// save the PmEvent
m_fEventPending = true;
m_event.message = buffer.message;
m_event.timestamp = buffer.timestamp;
//terminate the current sysex message
fEndSysex = true;
}
else {
// it is a chunck of the sysex message. Move data to the sysex buffer
fEndSysex = MoveDataToSysExBuffer(buffer.message);
}
}
//prepare the sysex message to return it
pSysExMsg->SetTimestamp( m_timestamp );
pSysExMsg->SetBuffer( m_SysexBuffer );
pSysExMsg->SetLength( m_CurSysexDataPtr - m_SysexBuffer );
*pError = wxMIDI_NO_ERROR;
//reset the wxMidiInDevice buffer
m_SysexBuffer = (wxByte*)NULL;
m_CurSysexDataPtr = (wxByte*)NULL;
m_fReadingSysex = false;
return pSysExMsg;
}
else
{
//it is not a sysex message
*pError = wxMIDI_NO_ERROR;
wxMidiShortMessage* pShortMsg = new wxMidiShortMessage(
Pm_MessageStatus( buffer.message ),
Pm_MessageData1( buffer.message ),
Pm_MessageData2( buffer.message ) );
pShortMsg->SetTimestamp( buffer.timestamp );
return pShortMsg;
}
}
bool wxMidiInDevice::MoveDataToSysExBuffer(PmMessage message)
{
/*
Moves the chunk received to the sysex buffer, allocating a greater buffer
if necessary.
Returns true if with this chunk the sysex message is ended, false otherwise
*/
// if we are at the end of the buffer
if( m_CurSysexDataPtr > (m_SysexBuffer + m_SizeOfSysexBuffer - SYSEX_CHUNK_SIZE) )
{
// keep previous size for usage
long oldSize = m_SizeOfSysexBuffer;
// increment size
m_SizeOfSysexBuffer += SYSEX_BUFFER_SIZE;
// allocate the new buffer
wxByte* newSysexBuffer = new wxByte[ m_SizeOfSysexBuffer ];
//wxLogMessage(wxString::Format(
// _T("Increasing buffer size from %d to %d"), oldSize, m_SizeOfSysexBuffer ));
// move the data from old buffer
memcpy( newSysexBuffer, m_SysexBuffer, oldSize );
// delete the old buffer
delete [] m_SysexBuffer;
// point to the new buffer
m_SysexBuffer = newSysexBuffer;
// set the write data pointer
m_CurSysexDataPtr = m_SysexBuffer + oldSize;
}
// move data to the buffer. See portmidi/pm_test/sysex.c function receive_sysex()
int shift = 0;
int data = 0;
for (shift = 0; shift < 32 && (data != 0xF7); shift += 8)
{
data = (message >> shift) & 0xFF;
*m_CurSysexDataPtr = data;
++m_CurSysexDataPtr;
/* portmidi:
When receiving sysex messages, the sysex message is terminated
by either an EOX status byte (anywhere in the 4 byte messages) or
by a non-real-time status byte in the low order byte of the message.
If you get a non-real-time status byte but there was no EOX byte, it
means the sysex message was somehow truncated. This is not
considered an error; e.g., a missing EOX can result from the user
disconnecting a MIDI cable during sysex transmission.
*/
// if this is a status byte that is not EOX, the sysex
// message is incomplete and there is no more sysex data
//
// WARNING: Is this true? Shouldn't portmidi send the status byte
// in a new PmEvent? See portmidi/pm_test/sysex.c function receive_sysex()
//
// TODO: if we break without doing anything else, the status byte and
// remaining bytes will be lost !!!
if (data & 0x80 && data != 0xF0 && data != 0xF7) break;
}
return (data & 0x80 && data != 0xF0);
}
void wxMidiInDevice::Flush()
{
// empty the buffer
wxMidiPmEvent buffer[1];
long nNumEvents;
while (Poll()) {
nNumEvents = 1; //initialize with buffer size before calling Read
Read(buffer, &nNumEvents);
}
}
wxMidiError wxMidiInDevice::StartListening(wxWindow* pWindow, unsigned long nPollingRate)
{
if (m_pThread) return wxMIDI_ERROR_AlreadyListening;
//Create a new thread. The thread object is created in the suspended state
m_pThread = new wxMidiThread(this, pWindow, nPollingRate);
if ( m_pThread->Create() != wxTHREAD_NO_ERROR )
{
return wxMIDI_ERROR_CreateThread; //Can't create thread!
}
//wxCriticalSectionLocker enter(wxGetApp().m_critsect);
//Start the thread execution
if (m_pThread->Run() != wxTHREAD_NO_ERROR )
{
return wxMIDI_ERROR_StartThread; //Can't start thread!
}
return wxMIDI_NO_ERROR;
}
wxMidiError wxMidiInDevice::StopListening()
{
//stop the thread and wait for its termination
m_pThread->Delete();
delete m_pThread;
m_pThread = (wxMidiThread*)NULL;
return wxMIDI_NO_ERROR;
}
//================================================================================
// Class wxMidiThread implementation
//================================================================================
// In MSWin32 all threads are joinable. So for platform independence I will
// always use joinable threads
wxMidiThread::wxMidiThread(wxMidiInDevice* pDev, wxWindow* pWindow, unsigned long milliseconds)
: wxThread(wxTHREAD_JOINABLE)
{
m_pDev = pDev;
m_pWindow = pWindow;
m_nMilliseconds = milliseconds;
}
wxMidiThread::~wxMidiThread()
{
}
void* wxMidiThread::Entry()
{
while(true)
{
// check if the thread was asked to exit and do it
if (TestDestroy()) break;
// check if Midi data is available
if ( m_pDev->Poll() ) {
// Data available. Create a Midi event
wxCommandEvent event( wxEVT_MIDI_INPUT );
::wxPostEvent( m_pWindow, event );
//m_pWindow->GetEventHandler()->AddPendingEvent(event); // Add it to the queue
}
// pause the thread execution during polling rate interval
wxThread::Sleep(m_nMilliseconds);
}
return NULL;
}
//================================================================================
// Class wxMidiSystem implementation
//
// Acording to documentation Pm_Initialize and Pm_Terminate return a error code, but
// looking at the source code they always return pmNoError. So it is useless to
// try to preserve the return code by forcing the user to explicitly call
// these methods. It is easier to initialize and terminate in
// the constructor and destructor, respectively
//================================================================================
wxMidiSystem* wxMidiSystem::m_pInstance = (wxMidiSystem*)NULL;
wxMidiSystem::~wxMidiSystem()
{
Terminate();
wxMidiDatabaseGM* pGM = wxMidiDatabaseGM::GetInstance();
delete pGM;
}
wxMidiError wxMidiSystem::Initialize()
{
Pt_Start(1, 0, 0); // start the timer
return (wxMidiError)Pm_Initialize(); // initialize portmidi package
}
wxMidiError wxMidiSystem::Terminate()
{
Pt_Stop(); //stop the timer
return (wxMidiError)Pm_Terminate();
}
const wxString wxMidiSystem::GetErrorText( wxMidiError errnum )
{
wxString sError;
switch(errnum)
{
case wxMIDI_NO_ERROR:
sError = _T("wxMidi: 'No error'");
return sError;
case wxMIDI_ERROR_HostError:
sError = _("PortMidi: `Host error'");
return sError;
case wxMIDI_ERROR_InvalidDeviceId:
sError = _("PortMidi: `Invalid device ID'");
return sError;
case wxMIDI_ERROR_InsufficientMemory:
sError = _("PortMidi: `Insufficient memory'");
return sError;
case wxMIDI_ERROR_BufferTooSmall:
sError = _("PortMidi: 'Buffer too small'");
return sError;
case wxMIDI_ERROR_BadPtr:
sError = _("PortMidi: `Bad pointer'");
return sError;
case wxMIDI_ERROR_InternalError:
sError = _("PortMidi: 'Internal PortMidi Error'");
return sError;
case wxMIDI_ERROR_BufferOverflow:
sError = _("PortMidi: 'Buffer overflow'");
return sError;
case wxMIDI_ERROR_BadData:
sError = _("PortMidi: 'Invalid MIDI message Data'");
return sError;
case wxMIDI_ERROR_BufferMaxSize:
sError = _("PortMidi: 'Buffer is already as large as it can be'");
return sError;
// Additional error messages for wxMidi
case wxMIDI_ERROR_AlreadyListening:
sError = _("wxMidi: 'Already lisening!'");
return sError;
case wxMIDI_ERROR_CreateThread:
sError = _("wxMidi: 'Error while creating the thread object'");
return sError;
case wxMIDI_ERROR_StartThread:
sError = _("wxMidi: 'Error while starting the thread execution'");
return sError;
case wxMIDI_ERROR_BadSysExMsg_Start:
sError = _("wxMidi. 'Bad sysex msg: It does not start with 0xF0'");
return sError;
case wxMIDI_ERROR_BadSysExMsg_Length:
sError = _("wxMidi. 'Bad sysex msg: Length greater than 10000 or no final byte 0xF7'");
return sError;
case wxMIDI_ERROR_NoDataAvailable:
sError = _("wxMidi. 'There are no MIDI messages pending to be read'");
return sError;
default:
sError = _("wxMidi: 'Illegal error number'");
return sError;
}
}
wxString wxMidiSystem::GetHostErrorText()
{
//TODO: review this
char msg[1000];
Pm_GetHostErrorText(&msg[0], 1000);
return wxString( (const wxChar *)&msg[0] );
}
wxMidiSystem* wxMidiSystem::GetInstance()
{
if (!m_pInstance) {
m_pInstance = new wxMidiSystem();
m_pInstance->Initialize();
}
return m_pInstance;
}
+401
View File
@@ -0,0 +1,401 @@
//=====================================================================================
// wxMidi: A MIDI interface based on PortMidi, the Portable Real-Time MIDI Library
// --------------------------------------------------------------------------------
//
// Author: Cecilio Salmeron
// Copyright: (c) 2005-2011 Cecilio Salmeron
// Licence: wxWidgets licence, version 3.1 or later at your choice.
//=====================================================================================
#ifdef __GNUG__
#pragma interface "wxMidi.cpp"
#endif
#ifndef __WXMIDI_H__ //to avoid nested includes
#define __WXMIDI_H__
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
/*
To do, when a new version of portmidi:
1. Review wxMidiError enumeration (copied and adapted from portmidi.h)
2. Review filter codes taken from portmidi.h
*/
// MIDI support throgh Portmidi lib
#include "portmidi.h"
#include "porttime.h"
//Constants
#define wxMIDI_VERSION _T("1.5")
#define wxMidiDeviceID PmDeviceID
#define wxMidiTimestamp PmTimestamp
#define wxMidiPmMessage PmMessage
#define wxMidiPmEvent PmEvent
typedef enum {
wxMIDI_NO_ERROR = 0,
//error codes from portmidi. Name changed from pmXxxx to wxMIDI_ERROR_Xxxx
wxMIDI_ERROR_HostError = -10000,
wxMIDI_ERROR_InvalidDeviceId, /* out of range or output device when input is requested or vice versa */
wxMIDI_ERROR_InsufficientMemory,
wxMIDI_ERROR_BufferTooSmall,
wxMIDI_ERROR_BufferOverflow,
wxMIDI_ERROR_BadPtr,
wxMIDI_ERROR_BadData, /* illegal midi data, e.g. missing EOX */
wxMIDI_ERROR_InternalError,
wxMIDI_ERROR_BufferMaxSize, /* buffer is already as large as it can be */
//Additional error codes not in portmidi
wxMIDI_ERROR_AlreadyListening,
wxMIDI_ERROR_CreateThread,
wxMIDI_ERROR_StartThread,
wxMIDI_ERROR_BadSysExMsg_Start,
wxMIDI_ERROR_BadSysExMsg_Length,
wxMIDI_ERROR_NoDataAvailable
} wxMidiError;
//Macros
//----------------------------------------------------------------------------------
//macro Pm_Channel renamed as wxMIDI_CHANNEL
/*
Pm_SetChannelMask() filters incoming messages based on channel.
The mask is a 16-bit bitfield corresponding to appropriate channels
The Pm_Channel macro can assist in calling this function.
i.e. to set receive only input on channel 1, call with
Pm_SetChannelMask(Pm_Channel(1));
Multiple channels should be OR'd together, like
Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11))
All channels are allowed by default
*/
#define wxMIDI_CHANNEL(channel) (1<<(channel))
// Filter codes
#define wxMIDI_NO_FILTER 0x00
#define wxMIDI_FILT_ACTIVE PM_FILT_ACTIVE
#define wxMIDI_FILT_SYSEX PM_FILT_SYSEX
#define wxMIDI_FILT_CLOCK PM_FILT_CLOCK
#define wxMIDI_FILT_PLAY PM_FILT_PLAY
#define wxMIDI_FILT_F9 PM_FILT_F9
#define wxMIDI_FILT_TICK PM_FILT_TICK
#define wxMIDI_FILT_FD PM_FILT_FD
#define wxMIDI_FILT_UNDEFINED PM_FILT_UNDEFINED
#define wxMIDI_FILT_RESET PM_FILT_RESET
#define wxMIDI_FILT_REALTIME PM_FILT_REALTIME
#define wxMIDI_FILT_NOTE PM_FILT_NOTE
#define wxMIDI_FILT_CHANNEL_AFTERTOUCH PM_FILT_CHANNEL_AFTERTOUCH
#define wxMIDI_FILT_POLY_AFTERTOUCH PM_FILT_POLY_AFTERTOUCH
#define wxMIDI_FILT_AFTERTOUCH PM_FILT_AFTERTOUCH
#define wxMIDI_FILT_PROGRAM PM_FILT_PROGRAM
#define wxMIDI_FILT_CONTROL PM_FILT_CONTROL
#define wxMIDI_FILT_PITCHBEND PM_FILT_PITCHBEND
#define wxMIDI_FILT_MTC PM_FILT_MTC
#define wxMIDI_FILT_SONG_POSITION PM_FILT_SONG_POSITION
#define wxMIDI_FILT_SONG_SELECT PM_FILT_SONG_SELECT
#define wxMIDI_FILT_TUNE PM_FILT_TUNE
#define wxMIDI_FILT_SYSTEMCOMMON PM_FILT_SYSTEMCOMMON
//Declare a new command event to inform that MIDI input data is available
DECLARE_EVENT_TYPE(wxEVT_MIDI_INPUT, -1)
enum wxMidiMsgType
{
wxMIDI_UNDEFINED_MSG = 0,
wxMIDI_SHORT_MSG,
wxMIDI_SYSEX_MSG
};
//forward declarations
class wxMidiThread;
class wxControlWithItems;
class wxMidiMessage
{
public:
wxMidiMessage() { m_type = wxMIDI_UNDEFINED_MSG; }
virtual ~wxMidiMessage() {}
virtual void SetTimestamp(wxMidiTimestamp timestamp) = 0;
virtual wxMidiTimestamp GetTimestamp() = 0;
wxMidiMsgType GetType() { return m_type; }
virtual wxByte GetStatus() = 0;
protected:
wxMidiMsgType m_type;
};
class wxMidiShortMessage : public wxMidiMessage
{
public:
wxMidiShortMessage(wxByte status, wxByte data1, wxByte data2)
: wxMidiMessage()
{
m_type = wxMIDI_SHORT_MSG;
m_buffer.message = (((data2) << 16) & 0xFF0000) |
(((data1) << 8) & 0xFF00) |
((status) & 0xFF);
m_buffer.timestamp = 0;
}
~wxMidiShortMessage() {}
//timestamp
void SetTimestamp(wxMidiTimestamp timestamp) { m_buffer.timestamp = timestamp; }
wxMidiTimestamp GetTimestamp() { return m_buffer.timestamp; }
// message data
wxByte GetStatus() { return ((m_buffer.message) & 0xFF); }
wxByte GetData1() { return (((m_buffer.message) >> 8) & 0xFF); }
wxByte GetData2() { return (((m_buffer.message) >> 16) & 0xFF); }
//internal usage
PmEvent* GetBuffer() { return &m_buffer; }
private:
PmEvent m_buffer;
};
class wxMidiSysExMessage : public wxMidiMessage
{
public:
wxMidiSysExMessage(wxByte* msg, wxMidiTimestamp timestamp=0);
wxMidiSysExMessage();
~wxMidiSysExMessage();
//timestamp
void SetTimestamp(wxMidiTimestamp timestamp) { m_timestamp = timestamp; }
wxMidiTimestamp GetTimestamp() { return m_timestamp; }
// message data
wxByte GetStatus() { return *m_pMessage; }
wxByte* GetMessage() { return m_pMessage; }
// information
wxMidiError Error() { return m_nError; }
int Length() { return m_nSize; }
//two steps construction
void SetBuffer(wxByte* pBuffer) { m_pMessage = pBuffer; }
void SetLength(int lenght) { m_nSize = lenght; }
private:
wxByte* m_pMessage;
wxMidiTimestamp m_timestamp;
wxMidiError m_nError;
int m_nSize;
};
class wxMidiDevice
{
public:
wxMidiDevice(wxMidiDeviceID nDevice);
virtual ~wxMidiDevice();
wxMidiError Close() { return (wxMidiError)Pm_Close(m_stream); }
//
// Device information
//
const wxString DeviceName();
const wxString InterfaceUsed();
bool IsInputPort();
bool IsOutputPort();
//
// Errors
//
int HasHostError() { return Pm_HasHostError(m_stream); }
protected:
wxMidiDeviceID m_nDevice;
const PmDeviceInfo* m_pInfo;
PortMidiStream* m_stream;
};
class wxMidiOutDevice : public wxMidiDevice
{
public:
wxMidiOutDevice(wxMidiDeviceID nDevice) : wxMidiDevice(nDevice) {}
~wxMidiOutDevice() {}
wxMidiError Open(long latency, void *DriverInfo=NULL);
//
// Write operations
//
wxMidiError Write(wxMidiShortMessage* pMsg);
wxMidiError Write(wxMidiSysExMessage* pMsg);
wxMidiError Write(wxByte* msg, wxMidiTimestamp when=0);
//TODO: remove this when the interface is more ellaborated
wxMidiError Write(PmEvent *buffer, long length) {
return (wxMidiError)Pm_Write(m_stream, buffer, length);
}
//
// Very common channel voice commands
//
wxMidiError NoteOn(int channel, int note, int velocity);
wxMidiError NoteOff(int channel, int note, int velocity);
wxMidiError ProgramChange(int channel, int instrument);
wxMidiError AllSoundsOff();
// miscellaneous
wxMidiError Abort() { return (wxMidiError)Pm_Abort(m_stream); }
private:
};
class wxMidiInDevice : public wxMidiDevice
{
public:
wxMidiInDevice(wxMidiDeviceID nDevice);
~wxMidiInDevice();
wxMidiError Open(void *DriverInfo = NULL);
wxMidiError Read(wxMidiPmEvent *buffer, long* length);
wxMidiMessage* Read(wxMidiError* pError);
wxMidiError SetFilter(long filters )
{ return (wxMidiError)Pm_SetFilter(m_stream, filters); }
wxMidiError SetChannelMask(long mask )
{ return (wxMidiError)Pm_SetChannelMask(m_stream, mask); }
wxMidiError Poll() { return (wxMidiError)Pm_Poll(m_stream); }
void Flush();
wxMidiError StartListening(wxWindow* pWindow, unsigned long nPollingRate=50);
wxMidiError StopListening();
private:
bool MoveDataToSysExBuffer(PmMessage message);
wxMidiThread* m_pThread; //thread for polling
//buffer for sysex messages
long m_SizeOfSysexBuffer;
wxByte* m_SysexBuffer;
wxByte* m_CurSysexDataPtr;
bool m_fReadingSysex; // sysex message interrupted by real-time message
wxMidiTimestamp m_timestamp; // timestamp of the interrupted sysex message
bool m_fEventPending; // sysex message ended without EOX. There is a PmEvent
// .. read but not delivered
PmEvent m_event; // event pending to be processed
};
//Database for Midi GM (General MIDI Standard)
class wxMidiDatabaseGM
{
public:
~wxMidiDatabaseGM();
static wxMidiDatabaseGM* GetInstance();
void PopulateWithInstruments(wxControlWithItems* pCtrol, int nSection, int nInstr=0,
bool fAddNumber=false);
void PopulateWithPercusionInstr(wxControlWithItems* pCtrol, int iSel=0);
int PopulateWithSections(wxControlWithItems* pCtrol, int nSelInstr=-1);
void PopulateWithAllInstruments(wxControlWithItems* pCtrol, int nInstr=0);
int GetNumSections();
int GetNumInstrumentsInSection(int nSect);
int GetInstrFromSection(int nSect, int i);
wxString GetInstrumentName(int nInstr);
wxString GetSectionName(int nSect);
private:
wxMidiDatabaseGM();
void Initialize();
#define NUM_SECTIONS 16 //number of sections
#define NUM_INSTRS 8 //max. number of instruments per section
int m_nSectInstr[NUM_SECTIONS][NUM_INSTRS]; //instruments in each section
int m_nNumInstrInSection[NUM_SECTIONS]; //points to last instrument in section
wxString m_sSectName[NUM_SECTIONS]; //section names
static wxMidiDatabaseGM* m_pInstance; //the only instance of this class
};
class wxMidiSystem
{
public:
~wxMidiSystem();
static wxMidiSystem* GetInstance();
wxMidiTimestamp GetTime() { return Pt_Time(); }
const wxString GetErrorText( wxMidiError errnum );
wxString GetHostErrorText();
int CountDevices() { return Pm_CountDevices(); }
protected:
wxMidiSystem() {}
private:
wxMidiError Initialize();
wxMidiError Terminate();
static wxMidiSystem* m_pInstance; //the only instance of this class
};
class wxMidiThread : public wxThread
{
public:
wxMidiThread(wxMidiInDevice* pDev, wxWindow* pWindow, unsigned long milliseconds);
~wxMidiThread();
// thread execution starts here
void *Entry();
// called when the thread exits
void OnExit() {}
public:
wxMidiInDevice* m_pDev; //owner Midi device
wxWindow* m_pWindow; //the window tha will receive the events
unsigned long m_nMilliseconds; //Midi in polling interval, in milliseconds
};
#endif // __WXMIDI_H__
+637
View File
@@ -0,0 +1,637 @@
//=====================================================================================
// wxMidi: A MIDI interface based on PortMidi, the Portable Real-Time MIDI Library
// --------------------------------------------------------------------------------
//
// Author: Cecilio Salmeron
// Copyright: (c) 2005-2011 Cecilio Salmeron
// Licence: wxWidgets licence, version 3.1 or later at your choice.
//=====================================================================================
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "wxMidiDatabase.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wxMidi.h"
/*
wxMidiDatabaseGM
Methods to load and manage midi instruments names and codes
The General MIDI (GM) Specification, published by the International MIDI Association,
defines a set of general capabilities for General MIDI Instruments. The General MIDI
Specification includes the definition of a General MIDI Sound Set (a patch map), a General
MIDI Percussion map (mapping of percussion sounds to note numbers), and a set of General
MIDI Performance capabilities (number of voices, types of MIDI messages recognized, etc.).
A MIDI sequence which has been generated for use on a General MIDI Instrument should play
correctly on any General MIDI synthesizer or sound module.
The General MIDI system utilizes MIDI channels 1-9 and 11-16 for chromatic instrument sounds,
while channel number 10 is utilized for "key-based" percussion sounds.
The General MIDI Sound set for channels 1-9 and 11-16 is given in table 1.
These instrument sounds are grouped into "sets" of related sounds. For example, program
numbers 1-8 are piano sounds, 6-16 are chromatic percussion sounds, 17-24 are organ sounds,
25-32 are guitar sounds, etc.
For the instrument sounds on channels 1-9 and 11-16, the note number in a Note On message
is used to select the pitch of the sound which will be played. For example if the Vibraphone
instrument (program number 12) has been selected on channel 3, then playing note number 60
on channel 3 would play the middle C note (this would be the default note to pitch assignment
on most instruments), and note number 59 on channel 3 would play B below middle C.
Both notes would be played using the Vibraphone sound.
The General MIDI percussion map used for channel 10 is given in table 2. For these "key-based"
sounds, the note number data in a Note On message is used differently. Note numbers on
channel 10 are used to select which drum sound will be played. For example, a Note On
message on channel 10 with note number 60 will play a Hi Bongo drum sound. Note number 59
on channel 10 will play the Ride Cymbal 2 sound.
It should be noted that the General MIDI system specifies sounds using program numbers 1
through 128. The MIDI Program Change message used to select these sounds uses an 8-bit byte,
which corresponds to decimal numbering from 0 through 127, to specify the desired program
number. Thus, to select GM sound number 10, the Glockenspiel, the Program Change message
will have a data byte with the decimal value 9.
The General MIDI system specifies which instrument or sound corresponds with each
program/patch number, but General MIDI does not specify how these sounds are produced.
Thus, program number 1 should select the Acoustic Grand Piano sound on any General MIDI
instrument. However, the Acoustic Grand Piano sound on two General MIDI synthesizers
which use different synthesis techniques may sound quite different.
*/
//instruments table
static bool m_fStringsInitialized = false;
static wxString m_sInstrName[128];
//initialize the only instance
wxMidiDatabaseGM* wxMidiDatabaseGM::m_pInstance = (wxMidiDatabaseGM*)NULL;
// ----------------------------------------------------------------------------
// wxMidiDatabaseGM ctor/dtor
// ----------------------------------------------------------------------------
wxMidiDatabaseGM::wxMidiDatabaseGM()
{
//language dependent strings. Can not be statically initiallized because
//then they do not get translated
if (!m_fStringsInitialized) {
//instrument names
m_sInstrName[0] = _("Acoustic Grand Piano");
m_sInstrName[1] = _("Bright Acoustic Piano");
m_sInstrName[2] = _("Electric Grand Piano");
m_sInstrName[3] = _("Honky-tonk Piano");
m_sInstrName[4] = _("Electric Piano 1");
m_sInstrName[5] = _("Electric Piano 2");
m_sInstrName[6] = _("Harpsichord");
m_sInstrName[7] = _("Clavinet");
m_sInstrName[8] = _("Celesta");
m_sInstrName[9] = _("Glockenspiel");
m_sInstrName[10] = _("Music Box");
m_sInstrName[11] = _("Vibraphone");
m_sInstrName[12] = _("Marimba");
m_sInstrName[13] = _("Xylophone");
m_sInstrName[14] = _("Tubular Bells");
m_sInstrName[15] = _("Dulcimer");
m_sInstrName[16] = _("Drawbar Organ");
m_sInstrName[17] = _("Percussive Organ");
m_sInstrName[18] = _("Rock Organ");
m_sInstrName[19] = _("Church Organ");
m_sInstrName[20] = _("Reed Organ");
m_sInstrName[21] = _("Accordion");
m_sInstrName[22] = _("Harmonica");
m_sInstrName[23] = _("Tango Accordion");
m_sInstrName[24] = _("Acoustic Guitar (nylon)");
m_sInstrName[25] = _("Acoustic Guitar (steel)");
m_sInstrName[26] = _("Electric Guitar (jazz)");
m_sInstrName[27] = _("Electric Guitar (clean)");
m_sInstrName[28] = _("Electric Guitar (muted)");
m_sInstrName[29] = _("Overdriven Guitar");
m_sInstrName[30] = _("Distortion Guitar");
m_sInstrName[31] = _("Guitar harmonics");
m_sInstrName[32] = _("Acoustic Bass");
m_sInstrName[33] = _("Electric Bass (finger)");
m_sInstrName[34] = _("Electric Bass (pick)");
m_sInstrName[35] = _("Fretless Bass");
m_sInstrName[36] = _("Slap Bass 1");
m_sInstrName[37] = _("Slap Bass 2");
m_sInstrName[38] = _("Synth Bass 1");
m_sInstrName[39] = _("Synth Bass 2");
m_sInstrName[40] = _("Violin");
m_sInstrName[41] = _("Viola");
m_sInstrName[42] = _("Cello");
m_sInstrName[43] = _("Contrabass");
m_sInstrName[44] = _("Tremolo Strings");
m_sInstrName[45] = _("Pizzicato Strings");
m_sInstrName[46] = _("Orchestral Harp");
m_sInstrName[47] = _("Timpani");
m_sInstrName[48] = _("String Ensemble 1");
m_sInstrName[49] = _("String Ensemble 2");
m_sInstrName[50] = _("SynthStrings 1");
m_sInstrName[51] = _("SynthStrings 2");
m_sInstrName[52] = _("Choir Aahs");
m_sInstrName[53] = _("Voice Oohs");
m_sInstrName[54] = _("Synth Voice");
m_sInstrName[55] = _("Orchestra Hit");
m_sInstrName[56] = _("Trumpet");
m_sInstrName[57] = _("Trombone");
m_sInstrName[58] = _("Tuba");
m_sInstrName[59] = _("Muted Trumpet");
m_sInstrName[60] = _("French Horn");
m_sInstrName[61] = _("Brass Section");
m_sInstrName[62] = _("SynthBrass 1");
m_sInstrName[63] = _("SynthBrass 2");
m_sInstrName[64] = _("Soprano Sax");
m_sInstrName[65] = _("Alto Sax");
m_sInstrName[66] = _("Tenor Sax");
m_sInstrName[67] = _("Baritone Sax");
m_sInstrName[68] = _("Oboe");
m_sInstrName[69] = _("English Horn");
m_sInstrName[70] = _("Bassoon");
m_sInstrName[71] = _("Clarinet");
m_sInstrName[72] = _("Piccolo");
m_sInstrName[73] = _("Flute");
m_sInstrName[74] = _("Recorder");
m_sInstrName[75] = _("Pan Flute");
m_sInstrName[76] = _("Blown Bottle");
m_sInstrName[77] = _("Shakuhachi");
m_sInstrName[78] = _("Whistle");
m_sInstrName[79] = _("Ocarina");
m_sInstrName[80] = _("Lead 1 (square)");
m_sInstrName[81] = _("Lead 2 (sawtooth)");
m_sInstrName[82] = _("Lead 3 (calliope)");
m_sInstrName[83] = _("Lead 4 (chiff)");
m_sInstrName[84] = _("Lead 5 (charang)");
m_sInstrName[85] = _("Lead 6 (voice)");
m_sInstrName[86] = _("Lead 7 (fifths)");
m_sInstrName[87] = _("Lead 8 (bass + lead)");
m_sInstrName[88] = _("Pad 1 (new age)");
m_sInstrName[89] = _("Pad 2 (warm)");
m_sInstrName[90] = _("Pad 3 (polysynth)");
m_sInstrName[91] = _("Pad 4 (choir)");
m_sInstrName[92] = _("Pad 5 (bowed)");
m_sInstrName[93] = _("Pad 6 (metallic)");
m_sInstrName[94] = _("Pad 7 (halo)");
m_sInstrName[95] = _("Pad 8 (sweep)");
m_sInstrName[96] = _("FX 1 (rain)");
m_sInstrName[97] = _("FX 2 (soundtrack)");
m_sInstrName[98] = _("FX 3 (crystal)");
m_sInstrName[99] = _("FX 4 (atmosphere)");
m_sInstrName[100] = _("FX 5 (brightness)");
m_sInstrName[101] = _("FX 6 (goblins)");
m_sInstrName[102] = _("FX 7 (echoes)");
m_sInstrName[103] = _("FX 8 (sci-fi)");
m_sInstrName[104] = _("Sitar");
m_sInstrName[105] = _("Banjo");
m_sInstrName[106] = _("Shamisen");
m_sInstrName[107] = _("Koto");
m_sInstrName[108] = _("Kalimba");
m_sInstrName[109] = _("Bag pipe");
m_sInstrName[110] = _("Fiddle");
m_sInstrName[111] = _("Shanai");
m_sInstrName[112] = _("Tinkle Bell");
m_sInstrName[113] = _("Agogo");
m_sInstrName[114] = _("Steel Drums");
m_sInstrName[115] = _("Woodblock");
m_sInstrName[116] = _("Taiko Drum");
m_sInstrName[117] = _("Melodic Tom");
m_sInstrName[118] = _("Synth Drum");
m_sInstrName[119] = _("Reverse Cymbal");
m_sInstrName[120] = _("Guitar Fret Noise");
m_sInstrName[121] = _("Breath Noise");
m_sInstrName[122] = _("Seashore");
m_sInstrName[123] = _("Bird Tweet");
m_sInstrName[124] = _("Telephone Ring");
m_sInstrName[125] = _("Helicopter");
m_sInstrName[126] = _("Applause");
m_sInstrName[127] = _("Gunshot");
m_fStringsInitialized = true;
}
}
wxMidiDatabaseGM::~wxMidiDatabaseGM()
{
}
wxMidiDatabaseGM* wxMidiDatabaseGM::GetInstance()
{
if (!m_pInstance) {
m_pInstance = new wxMidiDatabaseGM();
m_pInstance->Initialize();
}
return m_pInstance;
}
int wxMidiDatabaseGM::GetNumSections()
{
return NUM_SECTIONS;
}
wxString wxMidiDatabaseGM::GetInstrumentName(int nInstr)
{
wxASSERT(nInstr >=0 && nInstr < 128);
return m_sInstrName[nInstr];
}
wxString wxMidiDatabaseGM::GetSectionName(int nSect)
{
wxASSERT(nSect >=0 && nSect < NUM_SECTIONS);
return m_sSectName[nSect];
}
int wxMidiDatabaseGM::GetNumInstrumentsInSection(int nSect)
{
wxASSERT(nSect >=0 && nSect < NUM_SECTIONS);
return m_nNumInstrInSection[nSect];
}
int wxMidiDatabaseGM::GetInstrFromSection(int nSect, int i)
{
wxASSERT(nSect >=0 && nSect < NUM_SECTIONS);
wxASSERT(i >=0 && i < m_nNumInstrInSection[nSect]);
return m_nSectInstr[nSect][i];
}
void wxMidiDatabaseGM::PopulateWithInstruments(wxControlWithItems* pCtrol, int nSection,
int nInstr, bool fAddNumber)
{
//Populate a wxControlWithItems (wxListBox, wxCheckListBox, wxChoice, wxComboBox, ...)
//with the list of intruments in a section.
//Leave selected instrument number nInstr (default: the first one of the section)
//If fAddNumber is true, added name is preceeded by instrument number
pCtrol->Clear();
int iSel=0;
if (nSection < 0 || nSection > NUM_SECTIONS) {
//error in section number. Leave control cleared
return;
} else {
//populate control
int nCurInstr;
for (int i=0; i < m_nNumInstrInSection[nSection]; i++ )
{
nCurInstr = m_nSectInstr[nSection][i];
wxString sName = (fAddNumber ? wxString::Format(_T("%d - "), nCurInstr) : _T(""));
sName += m_sInstrName[nCurInstr];
pCtrol->Append(sName);
if (nCurInstr == nInstr) iSel = i;
}
}
pCtrol->SetStringSelection( pCtrol->GetString(iSel) );
}
void wxMidiDatabaseGM::PopulateWithAllInstruments(wxControlWithItems* pCtrol, int nInstr)
{
/*
Populate a wxControlWithItems (wxListBox, wxCheckListBox, wxChoice, wxComboBox, ...)
with the list of all intruments in GM standard.
Leave selected instrument number nInstr.
*/
pCtrol->Clear();
int iSel = 0;
if (nInstr >=0 && nInstr < 128) iSel = nInstr;
//populate control
for (int i=0; i < 128; i++ ) {
pCtrol->Append( m_sInstrName[i] );
}
pCtrol->SetStringSelection( pCtrol->GetString(iSel) );
}
int wxMidiDatabaseGM::PopulateWithSections(wxControlWithItems* pCtrol, int nSelInstr)
{
/*
Populate a wxControlWithItems (wxListBox, wxCheckListBox, wxChoice, wxComboBox, ...)
with the list of section names.
If nSelInstr != -1 select the section which includes instrument nSelInstr. Otherwise,
select first section
Returns the number of the section selected
*/
//load section names
for (int i=0; i < NUM_SECTIONS; i++) {
pCtrol->Append( m_sSectName[i] );
}
//Find section for instrument nSelInstr
;
int iSel = 0;
if (nSelInstr != -1) {
bool fFound = false;
for (int iSec=0; !fFound && iSec < NUM_SECTIONS; iSec++) {
for (int j=0; j < m_nNumInstrInSection[iSec]; j++) {
if (m_nSectInstr[iSec][j] == nSelInstr) {
iSel = iSec;
fFound = true;
break;
}
}
}
}
pCtrol->SetStringSelection( pCtrol->GetString(iSel) );
return iSel;
}
void wxMidiDatabaseGM::PopulateWithPercusionInstr(wxControlWithItems* pCtrol, int iSel)
{
pCtrol->Clear();
pCtrol->Append( _("35-Acoustic Bass Drum") );
pCtrol->Append( _("36-Bass Drum 1") );
pCtrol->Append( _("37-Side Stick") );
pCtrol->Append( _("38-Acoustic Snare") );
pCtrol->Append( _("39-Hand clap") );
pCtrol->Append( _("40-Electric Snare") );
pCtrol->Append( _("41_Low Floor Tom") );
pCtrol->Append( _("42_Closed High-Hat") );
pCtrol->Append( _("43-High Floor Tom") );
pCtrol->Append( _("44-Pedal High-Hat") );
pCtrol->Append( _("45-Low Tom") );
pCtrol->Append( _("46-Open High-Hat") );
pCtrol->Append( _("47-Low-Mid Tom") );
pCtrol->Append( _("48-High-Mid Tom") );
pCtrol->Append( _("49-Crash Cymbal 1") );
pCtrol->Append( _("50-High Tom") );
pCtrol->Append( _("51-Ride Cymbal 1") );
pCtrol->Append( _("52-Chinese Cymbal") );
pCtrol->Append( _("53-Ride Bell") );
pCtrol->Append( _("54-Tambourine") );
pCtrol->Append( _("55-Splash Cymbal") );
pCtrol->Append( _("56-Cowbell") );
pCtrol->Append( _("57-Crash Cymbal 2") );
pCtrol->Append( _("58-Vibraslap") );
pCtrol->Append( _("59-Ride Cymbal 2") );
pCtrol->Append( _("60-High Bongo") );
pCtrol->Append( _("61-Low Bongo") );
pCtrol->Append( _("62-Mute High Conga") );
pCtrol->Append( _("63-Open High Conga") );
pCtrol->Append( _("64-Low Conga") );
pCtrol->Append( _("65-High Timbale") );
pCtrol->Append( _("66-Low Timbale") );
pCtrol->Append( _("67-High Agogo") );
pCtrol->Append( _("68-Low Agogo") );
pCtrol->Append( _("69-Casaba") );
pCtrol->Append( _("70-Maracas") );
pCtrol->Append( _("71-Short Whistle") );
pCtrol->Append( _("72-Long Whistle") );
pCtrol->Append( _("73-Short Guiro") );
pCtrol->Append( _("74-Long Guiro") );
pCtrol->Append( _("75-Claves") );
pCtrol->Append( _("76-High Wood Block") );
pCtrol->Append( _("77-Low Wood Block") );
pCtrol->Append( _("78-Mute Cuica") );
pCtrol->Append( _("79-Open Cuica") );
pCtrol->Append( _("80-Mute Triangle") );
pCtrol->Append( _("81-Open Triangle") );
//select item
int i = iSel - 35;
if (i < 0 || i > 46) i=0;
pCtrol->SetStringSelection( pCtrol->GetString(i) );
}
void wxMidiDatabaseGM::Initialize()
{
//organize instruments into sections
//All code is prepared to deal with an arbitray number of sections, each
//containing a variable number of instruments. But for now lets use the
//MIDI groups with 8 instruments in each group
// WARNING: If you change this, modify constants NUM_SECTIONS and NUM_INSTRS in
// header file!!!
//section "Keyboards"
m_sSectName[0] = _("Keyboards");
m_nSectInstr[0][0] = 0; //0 - Acoustic Grand Piano
m_nSectInstr[0][1] = 1; //1 - Bright Acoustic Piano
m_nSectInstr[0][2] = 2; //2 - Electric Grand Piano
m_nSectInstr[0][3] = 3; //3 - Honky-tonk Piano
m_nSectInstr[0][4] = 4; //4 - Electric Piano 1
m_nSectInstr[0][5] = 5; //5 - Electric Piano 2
m_nSectInstr[0][6] = 6; //6 - Harpsichord
m_nSectInstr[0][7] = 7; //7 - Clavinet
m_nNumInstrInSection[0] = 8; //number of instruments in this section
//section "Chromatic percussion"
m_sSectName[1] = _("Chromatic percussion");
m_nSectInstr[1][0] = 8; //8 - Celesta"); //"Celesta"
m_nSectInstr[1][1] = 9; //9 - Glockenspiel"); //"Glockenspiel"
m_nSectInstr[1][2] = 10; //10 - Music Box"); //"Caja de música"
m_nSectInstr[1][3] = 11; //11 - Vibraphone"); //"Vibráfono"
m_nSectInstr[1][4] = 12; //12 - Marimba"); //"Marimba"
m_nSectInstr[1][5] = 13; //13 - Xylophone"); //"Xilófono"
m_nSectInstr[1][6] = 14; //14 - Tubular Bells"); //"Campanas"
m_nSectInstr[1][7] = 15; //15 - Dulcimer"); //"Dulcemer (salterio alemán)"
m_nNumInstrInSection[1] = 8; //number of instruments in this section
//section "Organs and harmonicas"
m_sSectName[2] = _("Organs and harmonicas");
m_nSectInstr[2][0] = 16; //16 - Drawbar Organ"); //"Organo Hammond"
m_nSectInstr[2][1] = 17; //17 - Percussive Organ"); //"Organo de percusión"
m_nSectInstr[2][2] = 18; //18 - Rock Organ"); //"Organo de rock"
m_nSectInstr[2][3] = 19; //19 - Church Organ"); //"Organo de iglesia"
m_nSectInstr[2][4] = 20; //20 - Reed Organ"); //"Armonio"
m_nSectInstr[2][5] = 21; //21 - Accordion"); //"Acordeón"
m_nSectInstr[2][6] = 22; //22 - Harmonica"); //"Armónica"
m_nSectInstr[2][7] = 23; //23 - Tango Accordion"); //"Bandoneón"
m_nNumInstrInSection[2] = 8; //number of instruments in this section
//section "Guitars"
m_sSectName[3] = _("Guitars");
m_nSectInstr[3][0] = 24; //24 - Acoustic Guitar (nylon)"); //"Guitarra cuerdas de nylon"
m_nSectInstr[3][1] = 25; //25 - Acoustic Guitar (steel)"); //"Guitarra cuerdas metálicas"
m_nSectInstr[3][2] = 26; //26 - Electric Guitar (jazz)"); //"Guitarra eléctrica de jazz"
m_nSectInstr[3][3] = 27; //27 - Electric Guitar (clean)"); //"Guitarra eléctrica"
m_nSectInstr[3][4] = 28; //28 - Electric Guitar (muted)"); //"Guitarra eléctrica (con sordina)"
m_nSectInstr[3][5] = 29; //29 - Overdriven Guitar"); //"Guitarra eléctrica (overdriven)"
m_nSectInstr[3][6] = 30; //30 - Distortion Guitar"); //"Guitarra eléctrica (con distorsión)"
m_nSectInstr[3][7] = 31; //31 - Guitar harmonics"); //"Guitarra de armónicos"
m_nNumInstrInSection[3] = 8; //number of instruments in this section
//section "Bass"
m_sSectName[4] = _("Basses");
m_nSectInstr[4][0] = 32; //32 - Acoustic Bass"); //"Bajo acústico"
m_nSectInstr[4][1] = 33; //33 - Electric Bass (finger)"); //"Bajo eléctrico (finger)"
m_nSectInstr[4][2] = 34; //34 - Electric Bass (pick)"); //"Bajo eléctrico (pick)"
m_nSectInstr[4][3] = 35; //35 - Fretless Bass"); //"Bajo sin trastes"
m_nSectInstr[4][4] = 36; //36 - Slap Bass 1"); //"Slap Bass 1"
m_nSectInstr[4][5] = 37; //37 - Slap Bass 2"); //"Slap Bass 2"
m_nSectInstr[4][6] = 38; //38 - Synth Bass 1"); //"Synth Bass 1"
m_nSectInstr[4][7] = 39; //39 - Synth Bass 2"); //"Synth Bass 2"
m_nNumInstrInSection[4] = 8; //number of instruments in this section
//section "Strings"
m_sSectName[5] = _("Strings");
m_nSectInstr[5][0] = 40; //40 - Violin"); //"Violín"
m_nSectInstr[5][1] = 41; //41 - Viola"); //"Viola"
m_nSectInstr[5][2] = 42; //42 - Cello"); //"Violoncelo"
m_nSectInstr[5][3] = 43; //43 - Contrabass"); //"Contrabajo"
m_nSectInstr[5][4] = 44; //44 - Tremolo Strings"); //"Tremolo strings"
m_nSectInstr[5][5] = 45; //45 - Pizzicato Strings"); //"Pizzicato strings"
m_nSectInstr[5][6] = 46; //46 - Orchestral Harp"); //"Arpa"
m_nSectInstr[5][7] = 47; //47 - Timpani"); //"Timbales"
m_nNumInstrInSection[5] = 8; //number of instruments in this section
//section "Ensembles"
m_sSectName[6] = _("Ensembles");
m_nSectInstr[6][0] = 48; //48 - String Ensemble 1"); //"Cuerdas"
m_nSectInstr[6][1] = 49; //49 - String Ensemble 2"); //"Slow Str."
m_nSectInstr[6][2] = 50; //50 - SynthStrings 1"); //"Syn Str. 1"
m_nSectInstr[6][3] = 51; //51 - SynthStrings 2"); //"Syn Str. 2"
m_nSectInstr[6][4] = 52; //52 - Choir Aahs"); //"Coro de ¡aahs!"
m_nSectInstr[6][5] = 53; //53 - Voice Oohs"); //"Coro de Oohs"
m_nSectInstr[6][6] = 54; //54 - Synth Voice"); //"Voces sintetizadas"
m_nSectInstr[6][7] = 55; //55 - Orchestra Hit"); //"Orchestra Hit"
m_nNumInstrInSection[6] = 8; //number of instruments in this section
//section "Brass"
m_sSectName[7] = _("Brass");
m_nSectInstr[7][0] = 56; //56 - Trumpet"); //"Trompeta"
m_nSectInstr[7][1] = 57; //57 - Trombone"); //"Trombón"
m_nSectInstr[7][2] = 58; //58 - Tuba"); //"Tuba"
m_nSectInstr[7][3] = 59; //59 - Muted Trumpet"); //"Trompeta con sordina"
m_nSectInstr[7][4] = 60; //60 - French Horn"); //"Trompa"
m_nSectInstr[7][5] = 61; //61 - Brass Section"); //"Grupo de viento-metal"
m_nSectInstr[7][6] = 62; //62 - SynthBrass 1"); //"Synth Br.1"
m_nSectInstr[7][7] = 63; //63 - SynthBrass 2"); //"Synth Br.2"
m_nNumInstrInSection[7] = 8; //number of instruments in this section
//section "Reed"
m_sSectName[8] = _("Reed");
m_nSectInstr[8][0] = 64; //64 - Soprano Sax"); //"Saxofón soprano"
m_nSectInstr[8][1] = 65; //65 - Alto Sax"); //"Saxofón contralto"
m_nSectInstr[8][2] = 66; //66 - Tenor Sax"); //"Saxofón tenor"
m_nSectInstr[8][3] = 67; //67 - Baritone Sax"); //"Saxofón barítono"
m_nSectInstr[8][4] = 68; //68 - Oboe"); //"Oboe"
m_nSectInstr[8][5] = 69; //69 - English Horn"); //"Corno ingles"
m_nSectInstr[8][6] = 70; //70 - Bassoon"); //"Fagot"
m_nSectInstr[8][7] = 71; //71 - Clarinet"); //"Clarinete"
m_nNumInstrInSection[8] = 8; //number of instruments in this section
//section "Pipe"
m_sSectName[9] = _("Pipe");
m_nSectInstr[9][0] = 72; //72 - Piccolo"); //"Falutín"
m_nSectInstr[9][1] = 73; //73 - Flute"); //"Flauta travesera"
m_nSectInstr[9][2] = 74; //74 - Recorder"); //"Flauta dulce"
m_nSectInstr[9][3] = 75; //75 - Pan Flute"); //"Flauta de Pan"
m_nSectInstr[9][4] = 76; //76 - Blown Bottle"); //"Botella soplada"
m_nSectInstr[9][5] = 77; //77 - Shakuhachi"); //"Shakuhachi"
m_nSectInstr[9][6] = 78; //78 - Whistle"); //"Silbato"
m_nSectInstr[9][7] = 79; //79 - Ocarina"); //"Ocarina"
m_nNumInstrInSection[9] = 8; //number of instruments in this section
//section "Synth. lead"
m_sSectName[10] = _("Synth. lead");
m_nSectInstr[10][0] = 80; //80 - Lead 1 (square)"); //"Onda cuadrada"
m_nSectInstr[10][1] = 81; //81 - Lead 2 (sawtooth)"); //"Onda en diente de sierra"
m_nSectInstr[10][2] = 82; //82 - Lead 3 (calliope)"); //"Calliope"
m_nSectInstr[10][3] = 83; //83 - Lead 4 (chiff)"); //"Chiffer Lead"
m_nSectInstr[10][4] = 84; //84 - Lead 5 (charang)"); //"Charanga"
m_nSectInstr[10][5] = 85; //85 - Lead 6 (voice)"); //"Voz"
m_nSectInstr[10][6] = 86; //86 - Lead 7 (fifths)"); //"Quintas"
m_nSectInstr[10][7] = 87; //87 - Lead 8 (bass + lead)"); //"Bass& Lead"
m_nNumInstrInSection[10] = 8; //number of instruments in this section
//section "Synth. pad"
m_sSectName[11] = _("Synth. pad");
m_nSectInstr[11][0] = 88; //88 - Pad 1 (new age)"); //"Fantasia"
m_nSectInstr[11][1] = 89; //89 - Pad 2 (warm)"); //"Warm Pad"
m_nSectInstr[11][2] = 90; //90 - Pad 3 (polysynth)"); //"PolySynth"
m_nSectInstr[11][3] = 91; //91 - Pad 4 (choir)"); //"Space Voice"
m_nSectInstr[11][4] = 92; //92 - Pad 5 (bowed)"); //"Bowed Glass"
m_nSectInstr[11][5] = 93; //93 - Pad 6 (metallic)"); //"Metal Pad"
m_nSectInstr[11][6] = 94; //94 - Pad 7 (halo)"); //"Halo Pad"
m_nSectInstr[11][7] = 95; //95 - Pad 8 (sweep)"); //"Sweep Pad"
m_nNumInstrInSection[11] = 8; //number of instruments in this section
//section "Synth. F/X"
m_sSectName[12] = _("Synth. F/X");
m_nSectInstr[12][0] = 96; //96 - FX 1 (rain)"); //"Ice Rain"
m_nSectInstr[12][1] = 97; //97 - FX 2 (soundtrack)"); //"Sound Track"
m_nSectInstr[12][2] = 98; //98 - FX 3 (crystal)"); //"Crystal"
m_nSectInstr[12][3] = 99; //99 - FX 4 (atmosphere)"); //"Atmosphere"
m_nSectInstr[12][4] = 100; //100 - FX 5 (brightness)"); //"Brightness"
m_nSectInstr[12][5] = 101; //101 - FX 6 (goblins)"); //"Goblin"
m_nSectInstr[12][6] = 102; //102 - FX 7 (echoes)"); //"Echo Drops"
m_nSectInstr[12][7] = 103; //103 - FX 8 (sci-fi)"); //"Star Theme"
m_nNumInstrInSection[12] = 8; //number of instruments in this section
//section "Ethnic"
m_sSectName[13] = _("Ethnic");
m_nSectInstr[13][0] = 104; //104 - Sitar"); //"Sitar"
m_nSectInstr[13][1] = 105; //105 - Banjo"); //"Banjo"
m_nSectInstr[13][2] = 106; //106 - Shamisen"); //"Shamisen"
m_nSectInstr[13][3] = 107; //107 - Koto"); //"Koto"
m_nSectInstr[13][4] = 108; //108 - Kalimba"); //"Kalimba"
m_nSectInstr[13][5] = 109; //109 - Bag pipe"); //"Gaita"
m_nSectInstr[13][6] = 110; //110 - Fiddle"); //"Fiddle"
m_nSectInstr[13][7] = 111; //111 - Shanai"); //"Shanai"
m_nNumInstrInSection[13] = 8; //number of instruments in this section
//section "Percussive"
m_sSectName[14] = _("Percussive");
m_nSectInstr[14][0] = 112; //112 - Tinkle Bell"); //"Tinkle Bell"
m_nSectInstr[14][1] = 113; //113 - Agogo"); //"Agogo"
m_nSectInstr[14][2] = 114; //114 - Steel Drums"); //"Steel Drums"
m_nSectInstr[14][3] = 115; //115 - Woodblock"); //"Woodblock"
m_nSectInstr[14][4] = 116; //116 - Taiko Drum"); //"Taiko"
m_nSectInstr[14][5] = 117; //117 - Melodic Tom"); //"Melo Tom 1"
m_nSectInstr[14][6] = 118; //118 - Synth Drum"); //"Synth Drum"
m_nSectInstr[14][7] = 119; //119 - Reverse Cymbal"); //"Reverse Cym."
m_nNumInstrInSection[14] = 8; //number of instruments in this section
//section "Sound F/X"
m_sSectName[15] = _("Sound F/X");
m_nSectInstr[15][0] = 120; //120 - Guitar Fret Noise"); //"Gt.FretNoise"
m_nSectInstr[15][1] = 121; //121 - Breath Noise"); //"Breath Noise"
m_nSectInstr[15][2] = 122; //122 - Seashore"); //"Seashore"
m_nSectInstr[15][3] = 123; //123 - Bird Tweet"); //"Bird"
m_nSectInstr[15][4] = 124; //124 - Telephone Ring"); //"Telephone 1"
m_nSectInstr[15][5] = 125; //125 - Helicopter"); //"Helicopter"
m_nSectInstr[15][6] = 126; //126 - Applause"); //"Applause"
m_nSectInstr[15][7] = 127; //127 - Gunshot"); //"Gun Shot"
m_nNumInstrInSection[15] = 8; //number of instruments in this section
}
/*
The Roland General Synthesizer (GS) Standard
The Roland General Synthesizer (GS) functions are a superset of those specified for
General MIDI. The GS system includes all of the GM sounds (which are referred to as
"capital instrument" sounds), and adds new sounds which are organized as variations
of the capital instruments.
Variations are selected using the MIDI Control Change message in conjunction with the
Program Change message. The Control Change message is sent first, and it is used to set
controller number 0 to some specified nonzero value indicating the desired variation
(some capital sounds have several different variations). The Control Change message
is followed by a MIDI Program Change message which indicates the program number of the
related capital instrument. For example, Capital instrument number 25 is the Nylon String
Guitar. The Ukulele is a variation of this instrument. The Ukulele is selected by sending
a Control Change message which sets controller number 0 to a value of 8, followed by a
program change message on the same channel which selects program number 25. Sending
the Program change message alone would select the capital instrument, the Nylon String
Guitar. Note also that a Control Change of controller number 0 to a value of 0 followed
by a Program Change message would also select the capital instrument.
The GS system also includes adjustable reverberation and chorus effects. The effects
depth for both reverb and chorus may be adjusted on an individual MIDI channel basis
using Control Change messages. The type of reverb and chorus sounds employed may also
be selected using System Exclusive messages.
*/