Category : Files from Magazines
Archive   : VOL11N15.ZIP
Filename : MIDREC.C
MIDREC.C -- MIDI Recorder and Player
(c) Charles Petzold, 1992
---------------------------------------*/
#include
#include
#include
#include
#include "midbuf.h"
#include "midrec.h"
#define BUFFER_SIZE 4096 // Should be multiple of 8
BOOL FAR PASCAL _export DlgProc (HWND, UINT, UINT, LONG) ;
static char szAppName [] = "MidRec" ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
FARPROC lpDlgProc ;
lpDlgProc = MakeProcInstance ((FARPROC) DlgProc, hInstance) ;
DialogBox (hInstance, szAppName, NULL, lpDlgProc) ;
FreeProcInstance (lpDlgProc) ;
return 0 ;
}
// Functions to allocate and free MIDIHDR structures and buffers
// -------------------------------------------------------------
LPMIDIHDR AllocMidiHeader (HANDLE hMidi, LPMIDIHDR pmhRoot)
{
LPMIDIHDR pmhNew, pmhNext ;
// Allocate memory for the new MIDIHDR
pmhNew = (LPMIDIHDR) GlobalAllocPtr (GHND | GMEM_SHARE, sizeof (MIDIHDR));
if (pmhNew == NULL)
return NULL ;
// Allocate memory for the buffer
pmhNew->lpData = (LPSTR) GlobalAllocPtr (GHND | GMEM_SHARE, BUFFER_SIZE) ;
if (pmhNew->lpData == NULL)
{
GlobalFreePtr (pmhNew) ;
return NULL ;
}
pmhNew->dwBufferLength = BUFFER_SIZE ;
// Prepare the header
if (midiInPrepareHeader (hMidi, pmhNew, sizeof (MIDIHDR)))
{
GlobalFreePtr (pmhNew->lpData) ;
GlobalFreePtr (pmhNew) ;
return NULL ;
}
// Attach new header to end of chain
if (pmhRoot != NULL)
{
pmhNext = pmhRoot ;
while (pmhNext->dwUser != NULL)
pmhNext = (LPMIDIHDR) pmhNext->dwUser ;
pmhNext->dwUser = (DWORD) pmhNew ;
}
return pmhNew ;
}
LPMIDIHDR CleanUpMidiHeaderChain (HANDLE hMidi, LPMIDIHDR pmhRoot)
{
LPMIDIHDR pmhCurr, pmhLast, pmhNext, pmhRetn ;
pmhRetn = pmhRoot ;
pmhCurr = pmhRoot ;
pmhLast = NULL ;
while (pmhCurr != NULL)
{
pmhNext = (LPMIDIHDR) pmhCurr->dwUser ;
if (pmhCurr->dwBytesRecorded == 0)
{
midiInUnprepareHeader (hMidi, pmhCurr, sizeof (MIDIHDR)) ;
GlobalFreePtr (pmhCurr->lpData) ;
GlobalFreePtr (pmhCurr) ;
if (pmhCurr == pmhRoot)
pmhRetn = NULL ;
if (pmhLast != NULL)
pmhLast->dwUser = (DWORD) pmhNext ;
pmhCurr = pmhLast ;
}
else if (pmhCurr->dwBytesRecorded < BUFFER_SIZE)
{
midiInUnprepareHeader (hMidi, pmhCurr, sizeof (MIDIHDR)) ;
GlobalReAllocPtr (pmhCurr->lpData,
pmhCurr->dwBytesRecorded, 0) ;
midiInPrepareHeader (hMidi, pmhCurr, sizeof (MIDIHDR)) ;
pmhCurr->dwBufferLength = pmhCurr->dwBytesRecorded ;
}
pmhLast = pmhCurr ;
pmhCurr = pmhNext ;
}
return pmhRetn ;
}
VOID FreeMidiHeaderChain (HANDLE hMidi, LPMIDIHDR pmhRoot)
{
LPMIDIHDR pmhNext, pmhTemp ;
pmhNext = pmhRoot ;
while (pmhNext != NULL)
{
pmhTemp = (LPMIDIHDR) pmhNext->dwUser ;
midiInUnprepareHeader (hMidi, pmhNext, sizeof (MIDIHDR)) ;
GlobalFreePtr (pmhNext->lpData) ;
GlobalFreePtr (pmhNext) ;
pmhNext = pmhTemp ;
}
}
// Add MIDI device lists to the program's menu
// -------------------------------------------
WORD AddDevicesToMenu (HWND hwnd, int iNumInpDevs, int iNumOutDevs)
{
HMENU hMenu, hMenuInp, hMenuMon, hMenuOut ;
int i ;
MIDIINCAPS mic ;
MIDIOUTCAPS moc ;
WORD wDefaultOut ;
hMenu = GetMenu (hwnd) ;
// Create "Input" popup menu
hMenuInp = CreateMenu () ;
for (i = 0 ; i < iNumInpDevs ; i++)
{
midiInGetDevCaps (i, &mic, sizeof (MIDIINCAPS)) ;
AppendMenu (hMenuInp, MF_STRING, ID_DEV_INP + i, mic.szPname) ;
}
CheckMenuItem (hMenuInp, 0, MF_BYPOSITION | MF_CHECKED) ;
ModifyMenu (hMenu, ID_DEV_INP, MF_POPUP, hMenuInp, "&Input") ;
// Create "Monitor" and "Output" popup menus
hMenuMon = CreateMenu () ;
hMenuOut = CreateMenu () ;
AppendMenu (hMenuMon, MF_STRING, ID_DEV_MON, "&None") ;
if (!midiOutGetDevCaps (MIDIMAPPER, &moc, sizeof (moc)))
{
AppendMenu (hMenuMon, MF_STRING, ID_DEV_MON + 1, moc.szPname) ;
AppendMenu (hMenuOut, MF_STRING, ID_DEV_OUT , moc.szPname) ;
wDefaultOut = 0 ;
}
else
wDefaultOut = 1 ;
// Add the rest of the MIDI devices
for (i = 0 ; i < iNumOutDevs ; i++)
{
midiOutGetDevCaps (i, &moc, sizeof (moc)) ;
AppendMenu (hMenuMon, MF_STRING, ID_DEV_MON + i + 2, moc.szPname) ;
AppendMenu (hMenuOut, MF_STRING, ID_DEV_OUT + i + 1, moc.szPname) ;
}
CheckMenuItem (hMenuMon, 0, MF_BYPOSITION | MF_CHECKED) ;
CheckMenuItem (hMenuOut, 0, MF_BYPOSITION | MF_CHECKED) ;
ModifyMenu (hMenu, ID_DEV_MON, MF_POPUP, hMenuMon, "&Monitor") ;
ModifyMenu (hMenu, ID_DEV_OUT, MF_POPUP, hMenuOut, "&Output") ;
return wDefaultOut ;
}
BOOL FAR PASCAL _export DlgProc (HWND hwnd, UINT message, UINT wParam,
LONG lParam)
{
static BOOL bRecording, bPlaying, bEnding, bPaused, bTerminating ;
static char szInpError[] = { "Error opening MIDI input port!" } ;
static char szOutError[] = { "Error opening MIDI output port!" } ;
static char szMonError[] = { "Error opening MIDI output port "
"for monitoring input! Continuing." } ;
static char szMemError[] = { "Error allocating memory!" } ;
static HMIDIIN hMidiIn ;
static HMIDIOUT hMidiOut ;
static int iNumInpDevs, iNumOutDevs ;
static LPMIDIHDR pMidiHdrRoot, pMidiHdrNext, pMidiHdr ;
static WORD wDeviceInp, wDeviceMon, wDeviceOut ;
HMENU hMenu ;
int i ;
switch (message)
{
case WM_INITDIALOG:
if (0 == (iNumInpDevs = midiInGetNumDevs ()))
{
MessageBox (hwnd, "No MIDI Input Devices!", szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
DestroyWindow (hwnd) ;
}
if (0 == (iNumOutDevs = midiOutGetNumDevs ()))
{
MessageBox (hwnd, "No MIDI Output Devices!", szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
DestroyWindow (hwnd) ;
}
wDeviceOut = AddDevicesToMenu (hwnd, iNumInpDevs, iNumOutDevs) ;
return TRUE ;
case WM_COMMAND:
hMenu = GetMenu (hwnd) ;
switch (wParam)
{
case ID_RECORD_BEG:
// Open MIDI In port for recording
if (midiInOpen (&hMidiIn, wDeviceInp, hwnd, 0L,
CALLBACK_WINDOW))
{
MessageBox (hwnd, szInpError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// Open MIDI Out port for monitoring
// (continue if unable to open it)
if (wDeviceMon > 0)
{
if (midiOutOpen (&hMidiOut, wDeviceMon - 2,
0L, 0L, 0L))
{
hMidiOut = NULL ;
MessageBox (hwnd, szMonError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
}
}
else
hMidiOut = NULL ;
return TRUE ;
case ID_RECORD_END:
// Reset and close input
bEnding = TRUE ;
midiInReset (hMidiIn) ;
midiInClose (hMidiIn) ;
return TRUE ;
case ID_PLAY_BEG:
// Open MIDI Out port for playing
if (midiOutOpen (&hMidiOut, wDeviceOut - 1,
hwnd, 0L, CALLBACK_WINDOW))
{
MessageBox (hwnd, szOutError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
}
return TRUE ;
case ID_PLAY_PAUSE:
// Pause or restart output
if (!bPaused)
{
midiOutPause (hMidiOut) ;
// All Notes Off message
for (i = 0 ; i < 16 ; i++)
midiOutShortMsg (hMidiOut, 0x7BB0 + i) ;
SetDlgItemText (hwnd, ID_PLAY_PAUSE, "Resume") ;
bPaused = TRUE ;
}
else
{
midiOutRestart (hMidiOut) ;
SetDlgItemText (hwnd, ID_PLAY_PAUSE, "Pause") ;
bPaused = FALSE ;
}
return TRUE ;
case ID_PLAY_END:
// Reset the port and close it
bEnding = TRUE ;
midiOutReset (hMidiOut) ;
midiOutClose (hMidiOut) ;
return TRUE ;
default:
break ;
}
if (wParam >= ID_DEV_INP & wParam < ID_DEV_MON)
{
CheckMenuItem (hMenu, wDeviceInp + ID_DEV_INP,
MF_UNCHECKED) ;
wDeviceInp = wParam - ID_DEV_INP ;
CheckMenuItem (hMenu, wDeviceInp + ID_DEV_INP,
MF_CHECKED) ;
return 0 ;
}
else if (wParam >= ID_DEV_MON & wParam < ID_DEV_OUT)
{
CheckMenuItem (hMenu, wDeviceMon + ID_DEV_MON,
MF_UNCHECKED) ;
wDeviceMon = wParam - ID_DEV_MON ;
CheckMenuItem (hMenu, wDeviceMon + ID_DEV_MON,
MF_CHECKED) ;
return 0 ;
}
if (wParam >= ID_DEV_OUT)
{
CheckMenuItem (hMenu, wDeviceOut + ID_DEV_OUT,
MF_UNCHECKED) ;
wDeviceOut = wParam - ID_DEV_OUT ;
CheckMenuItem (hMenu, wDeviceOut + ID_DEV_OUT,
MF_CHECKED) ;
return 0 ;
}
break ;
case MM_MIM_OPEN:
hMidiIn = wParam ;
// Free existing headers
FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
// Allocate root header
if (NULL == (pMidiHdrRoot = AllocMidiHeader (hMidiIn, NULL)))
{
midiInClose (hMidiIn) ;
MessageBox (hwnd, szMemError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// Allocate next header
if (NULL == (pMidiHdrNext = AllocMidiHeader (hMidiIn,
pMidiHdrRoot)))
{
FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
midiInClose (hMidiIn) ;
MessageBox (hwnd, szMemError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// Enable and disable buttons
EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), TRUE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_END), FALSE) ;
SetFocus (GetDlgItem (hwnd, ID_RECORD_END)) ;
// Submit the buffers for receiving data
midiInShortBuffer (hMidiIn, pMidiHdrRoot, sizeof (MIDIHDR)) ;
midiInShortBuffer (hMidiIn, pMidiHdrNext, sizeof (MIDIHDR)) ;
// Begin recording
midiInStart (hMidiIn) ;
bRecording = TRUE ;
bEnding = FALSE ;
return TRUE ;
case MM_MIM_DATA:
if (hMidiOut)
{
midiOutShortMsg (hMidiOut, lParam) ;
}
return TRUE ;
case MM_MIM_LONGDATA:
if (bEnding)
return TRUE ;
pMidiHdrNext = AllocMidiHeader (hMidiIn, pMidiHdrRoot) ;
if (pMidiHdrNext == NULL)
{
midiInReset (hMidiIn) ;
midiInClose (hMidiIn) ;
MessageBox (hwnd, szMemError, szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
midiInShortBuffer (hMidiIn, pMidiHdrNext, sizeof (MIDIHDR));
return TRUE ;
case MM_MIM_CLOSE:
// Close the monitoring output port
if (hMidiOut)
{
midiOutReset (hMidiOut) ;
midiOutClose (hMidiOut) ;
}
// Enable and Disable Buttons
EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), FALSE) ;
SetFocus (GetDlgItem (hwnd, ID_RECORD_BEG)) ;
pMidiHdrRoot = CleanUpMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
if (pMidiHdrRoot != NULL)
{
EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_END), FALSE) ;
SetFocus (GetDlgItem (hwnd, ID_PLAY_BEG)) ;
}
bRecording = FALSE ;
if (bTerminating)
{
FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L) ;
}
return TRUE ;
case MM_MOM_OPEN:
hMidiOut = wParam ;
// Enable and Disable Buttons
EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), TRUE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_END), TRUE) ;
SetFocus (GetDlgItem (hwnd, ID_PLAY_END)) ;
// Submit the root buffer to begin playing
midiOutShortBuffer (hMidiOut, pMidiHdrRoot, sizeof (MIDIHDR)) ;
// If there's a second buffer, submit that also
if (NULL != (pMidiHdr = (LPMIDIHDR) pMidiHdrRoot->dwUser))
midiOutShortBuffer (hMidiOut, pMidiHdr, sizeof (MIDIHDR)) ;
bEnding = FALSE ;
bPlaying = TRUE ;
return TRUE ;
case MM_MOM_DONE:
// If stopping playback, just return
if (bEnding)
return TRUE ;
// Get header of buffer just finished playing
pMidiHdr = (LPMIDIHDR) lParam ;
// Get header of next buffer (already submitted)
pMidiHdr = (LPMIDIHDR) pMidiHdr->dwUser ;
// Get header of next buffer to submit now
if (pMidiHdr != NULL)
pMidiHdr = (LPMIDIHDR) pMidiHdr->dwUser ;
if (pMidiHdr != NULL)
midiOutShortBuffer (hMidiOut, pMidiHdr, sizeof (MIDIHDR)) ;
else
{
midiOutReset (hMidiOut) ;
midiOutClose (hMidiOut) ;
}
return TRUE ;
case MM_MOM_CLOSE:
// Enable and Disable Buttons
EnableWindow (GetDlgItem (hwnd, ID_RECORD_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, ID_RECORD_END), TRUE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_BEG), TRUE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_PAUSE), FALSE) ;
EnableWindow (GetDlgItem (hwnd, ID_PLAY_END), FALSE) ;
SetFocus (GetDlgItem (hwnd, ID_PLAY_BEG)) ;
SetDlgItemText (hwnd, ID_PLAY_PAUSE, "Pause") ;
bPaused = FALSE ;
bPlaying = FALSE ;
if (bTerminating)
{
FreeMidiHeaderChain (hMidiIn, pMidiHdrRoot) ;
SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L) ;
}
return TRUE ;
case WM_SYSCOMMAND:
switch (wParam)
{
case SC_CLOSE:
if (bRecording)
{
bTerminating = TRUE ;
bEnding = TRUE ;
midiInReset (hMidiIn) ;
midiInClose (hMidiIn) ;
return TRUE ;
}
if (bPlaying)
{
bTerminating = TRUE ;
bEnding = TRUE ;
midiOutReset (hMidiOut) ;
midiOutClose (hMidiOut) ;
return TRUE ;
}
EndDialog (hwnd, 0) ;
return TRUE ;
}
break ;
}
return FALSE ;
}
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/