/* $Id: midistrm.cpp 205 2007-06-14 01:33:51Z stevenhl $ */ /* SCCSID = %W% %E% */ /**************************************************************************** * * * Copyright (c) IBM Corporation 1994 - 1997. * * * * The following IBM OS/2 source code is provided to you solely for the * * the purpose of assisting you in your development of OS/2 device drivers. * * You may use this code in accordance with the IBM License Agreement * * provided in the IBM Device Driver Source Kit for OS/2. * * * ****************************************************************************/ /**@internal %W% * @notes * MIDISTREAM class implementation. The Midi Stream class is derived * from the Stream class. * @version %I% * @context Unless otherwise noted, all interfaces are Ring-0, 16-bit, * kernel stack. * @history * */ #define INCL_NOPMAPI #define INCL_DOSERRORS // for ERROR_INVALID_FUNCTION #include #include #include // for #define MIDI #include #include #include "midistrm.hpp" #include "event.hpp" #include "maudio.hpp" // An array to map integer 0..15 to the corresponding bit number in a USHORT. USHORT MIDISTREAM::_usBitNumber[ NUM_MidiChannels ] = { 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000 }; /**@internal CalcDelay * @param None * @return None * @notes * 600,000,000 microseconds/10 minutes *---------------------------------------------------------- == X microseconds/clock * usCPQNnum * (ulTempo beats/10 min) * ( 24 * ------------- clocks/beat ) * usCPQNden * * * 25,000,000 * usCPQNden *== -------------------------- * ulTempo * usCPQNnum * * where * usCPQNden = ((usCPQN & 0x3F) + 1) * 3 * usCPQNnum = 1 if bit 6 of usCPQN is set * * or * * usCPQNden = 1 * usCPQNnum = usCPQN + 1 if bit 6 is not set */ void MIDISTREAM::CalcDelay(void) { ULONG ul; if (usCPQN & 0x40) { // bit 6 is set if it's a denominator ul = 25000000 * ((usCPQN & 0x3F) + 1); ulPerClock = ul / ulTempo; ulPerClock *= 3; } else { ul = ulTempo * (usCPQN+1); ulPerClock = 25000000 / ul; } } /**@external MIDISTRM::Process * Consume MIDI bytes from the MMPM/2 stream buffers and send * them off to the MIDI parser to be interpreted. * @param void * @return void * @notes Runs at Task time on a global context hook; does not run * on an interrupt level. Interacts with the Timer object defined * for this stream to obtain current time and to request next Stream * time to be scheduled. */ void MIDISTREAM::Process( void ) { ULONG ulNewTime; // Time, in mSec, on entry. ULONG ulElapsedTime; // Elapsed time, last tick to this one. // Update time variables. ulNewTime = ((MIDIAUDIO*) pahw)->getTimer()->ulGetTime(); if ( ulNewTime > _ulLastProcess ) ulElapsedTime = ulNewTime - _ulLastProcess; else ulElapsedTime = 0; _ulLastProcess = ulNewTime; ulCurrentTime = ulNewTime; if (qhInProcess.IsElements() == 0) // no buffers to process? return; if (ulStreamState == STREAM_PAUSED) // is the stream paused? return; ProcessEvents(); lWait -= ulElapsedTime * 1000; PSTREAMBUFFER pstreambuff = (PSTREAMBUFFER) qhInProcess.Head(); PSTREAMBUF pbuff = pstreambuff->pBuffptr; ULONG buffsz = pstreambuff->ulBuffsz; while (lWait <= 0 && qhInProcess.IsElements()) { parse(*(pbuff + (pstreambuff->ulBuffpos)++)); if (pstreambuff->ulBuffpos >= buffsz) { qhDone.PushOnTail(qhInProcess.PopHead()); pstreambuff = (PSTREAMBUFFER) qhInProcess.Head(); pbuff = pstreambuff->pBuffptr; buffsz = pstreambuff->ulBuffsz; } } while (qhDone.IsElements()) ReturnBuffer(); // Determine next time to run. If we submit a time that has already // passed, we'll get scheduled for the next tick. ULONG ulTimeNextRun = ulCurrentTime + (lWait / 1000); ((MIDIAUDIO*) pahw)->getTimer()->vSchedule( ulTimeNextRun ); } // ### By getting Timer time, we're assuming that this stream owns the HW. ULONG MIDISTREAM::GetCurrentTime(void) { return ((MIDIAUDIO*) pahw)->getTimer()->ulGetTime() ; } // ### By setting Timer time, we're assuming that this stream owns the HW. void MIDISTREAM::SetCurrentTime(ULONG time) { ((MIDIAUDIO*) pahw)->getTimer()->vSetTime( time ); ulCurrentTime = time; } ULONG MIDISTREAM::Read(PSTREAMBUF, unsigned) { return(ERROR_INVALID_FUNCTION); } ULONG MIDISTREAM::StartStream(void) { if (!pahw->Start(this)) return ERROR_START_STREAM; state = S_Init; // Reset parser state. message.clear(); // Clear current message. lWait = 0; //SvL, reset this too ulStreamState = STREAM_STREAMING; return NO_ERROR; } /**@internal MIDISTREAM::_allNotesOff * Shut off all notes that are currently playing. * @param None. * @return void * @notes This function walks the _notesOn array and shuts off any note * that is flagged as being actively played. */ void MIDISTREAM::_allNotesOff( void ) { for ( BYTE noteNum=0; noteNum < NUM_MidiNotes; ++noteNum) if (_notesOn[noteNum]) // This note number is playing on one or more channels. // Shut the note off on all channels on which it is playing. for ( BYTE mchan=0; mchan < NUM_MidiChannels; ++mchan) if (_notesOn[noteNum] & _usBitNumber[mchan]) { ((MIDIAUDIO*) pahw)->noteOff( mchan, noteNum, 0 ); _notesOn[noteNum] &= ~(_usBitNumber[mchan]); } } ULONG MIDISTREAM::StopStream(PCONTROL_PARM pControl) { ulStreamState = STREAM_STOPPED; pahw->Stop(this); _allNotesOff(); ReturnBuffers(); pControl->ulTime = GetCurrentTime(); return NO_ERROR; } ULONG MIDISTREAM::PauseStream(PCONTROL_PARM pControl) { if (ulStreamState == STREAM_PAUSED) // is the stream paused? return ERROR_INVALID_SEQUENCE; pahw->Pause(this); _allNotesOff(); pControl->ulTime = GetCurrentTime(); ulStreamState = STREAM_PAUSED; return NO_ERROR; } ULONG MIDISTREAM::ResumeStream(void) { if (ulStreamState != STREAM_PAUSED) // is the stream paused? return ERROR_INVALID_SEQUENCE; state = S_Init; // Reset parser state. message.clear(); // Clear current message. pahw->Resume(this); ulStreamState = STREAM_STREAMING; return NO_ERROR; } MIDISTREAM::MIDISTREAM(ULONG streamtype, USHORT filesysnum): STREAM(streamtype, filesysnum) { // Initialize tempo & scheduling information. ulTempo = 1200; usCPQN = 0; CalcDelay(); lWait = 0; _ulLastProcess = 0; // Reset the parser. state = S_Init; message.clear(); // Reset our tracking of which notes are currently on. for (int i=0; i