void native_midi_start(NativeMidiSong *song)
{
  MMRESULT merr;
  MIDIPROPTIMEDIV mptd;

  native_midi_stop();
  if (!hMidiStream)
  {
    merr=midiStreamOpen(&hMidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION);
    if (merr!=MMSYSERR_NOERROR)
    {
      hMidiStream=0;
      return;
    }
    //midiStreamStop(hMidiStream);
    currentsong=song;
    currentsong->NewPos=0;
    currentsong->MusicPlaying=1;
    mptd.cbStruct=sizeof(MIDIPROPTIMEDIV);
    mptd.dwTimeDiv=currentsong->ppqn;
    merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV);
    BlockOut(song);
    merr=midiStreamRestart(hMidiStream);
  }
}
int WinMIDIDevice::Open(MidiCallback callback, void *userdata)
{
	MMRESULT err;

	Callback = callback;
	CallbackData = userdata;
	if (MidiOut == nullptr)
	{
		err = midiStreamOpen(&MidiOut, &DeviceID, 1, (DWORD_PTR)CallbackFunc, (DWORD_PTR)this, CALLBACK_FUNCTION);

		if (err == MMSYSERR_NOERROR)
		{
			if (IgnoreMIDIVolume(DeviceID))
			{
				VolumeWorks = false;
			}
			else
			{
				// Set master volume to full, if the device allows it on this interface.
				VolumeWorks = (MMSYSERR_NOERROR == midiOutGetVolume((HMIDIOUT)MidiOut, &SavedVolume));
				if (VolumeWorks)
				{
					VolumeWorks &= (MMSYSERR_NOERROR == midiOutSetVolume((HMIDIOUT)MidiOut, 0xffffffff));
				}
			}
		}
		else
		{
			return 1;
		}
	}
	return 0;
}
int WinMIDIStreamer::OpenStream(void)
{
    MMRESULT            mmres;
    MIDIPROPTIMEDIV     tdiv;

    devId = MIDI_MAPPER;
    if(MMSYSERR_NOERROR != (mmres =
        midiStreamOpen(&midiStr, &devId, 1, (DWORD_PTR) Callback,
                       (DWORD_PTR) this, CALLBACK_FUNCTION)))
    {
        printf("WinMIDIStreamer::OpenStream: midiStreamOpen error %i.\n", mmres);
        return FALSE;
    }

    // Set stream time format, 140 ticks per quarter note.
    tdiv.cbStruct = sizeof(tdiv);
    tdiv.dwTimeDiv = 140;
    if(MMSYSERR_NOERROR != (mmres =
        midiStreamProperty(midiStr, (BYTE *) & tdiv,
                           MIDIPROP_SET | MIDIPROP_TIMEDIV)))
    {
        printf("WinMIDIStreamer::OpenStream: time format! %i\n", mmres);
        return FALSE;
    }

    return TRUE;
}
int native_midi_detect()
{
  MMRESULT merr;
  HMIDISTRM MidiStream;

  merr=midiStreamOpen(&MidiStream,&MidiDevice,(DWORD)1,(unsigned long)MidiProc,(unsigned long)0,CALLBACK_FUNCTION);
  if (merr!=MMSYSERR_NOERROR)
    return 0;
  midiStreamClose(MidiStream);
  return 1;
}
int native_midi_detect()
{
	MMRESULT merr;
	HMIDISTRM MidiStream;
	
	merr=midiStreamOpen(&MidiStream,&MidiDevice,1,(DWORD)&MidiProc,0,CALLBACK_FUNCTION);
	if (merr!=MMSYSERR_NOERROR)
		MidiStream=0;
	midiStreamClose(MidiStream);
	if (!MidiStream)
		return 0;
	else
		return 1;
}
Exemple #6
0
static PyObject *Winmidi_New(PyObject *self, PyObject *args) {
	WinmidiObject *obj;
	UINT		retVal;
	UINT		numDevs;
	UINT		devNum;

	UNUSED(self);

	if(!PyArg_ParseTuple(args, "i:Winmidi", &devNum)) {
	return NULL;
	}

	/* Initialize the new object. */
	obj = PyObject_NEW(WinmidiObject, &s_winmidiObjectType);
	if(!obj) {
	return NULL;
	}
	obj->m_midiOut = 0;
	obj->m_initialized = 0;
	obj->m_playNode = 0;
	obj->m_list = 0;
	obj->m_listEnd = 0;

	/* Get the number of MIDI devices on the system. */
	numDevs = midiOutGetNumDevs();
	if(numDevs == 0) {
	PyErr_SetString(PyExc_RuntimeError, 
		"No MIDI output devices found.");
	Py_DECREF(obj);
	return NULL;
	}

	/* Open the MIDI output device. */
	obj->m_midiOut = 0;
	retVal = midiStreamOpen(&(obj->m_midiOut), &devNum, 1,
		(DWORD) s_MidiCallback, (DWORD) obj, CALLBACK_FUNCTION); //lint !e620
	if(retVal != MMSYSERR_NOERROR) {
	s_SetMidiError("opening a MIDI stream", retVal);
	Py_DECREF(obj);
	return NULL;
	}
	assert(obj->m_midiOut);

	/* printf("Winmidi object created.\n"); */
	return (PyObject *) obj;
}
Exemple #7
0
int MPU_Init
   (
   int addr
   )

   {
	int i;

	for (i=0;i<NUMBUFFERS;i++) eventcnt[i]=0;
	
	mididevice = addr;

	if (midiOutGetDevCaps(mididevice, &midicaps, sizeof(MIDIOUTCAPS)) != MMSYSERR_NOERROR) return MPU_Error;

	if (midiStreamOpen(&hmido,&mididevice,1,(DWORD)MPU_MIDICallback,0L,CALLBACK_FUNCTION) != MMSYSERR_NOERROR) return(MPU_Error);

	return( MPU_Ok );
   }
int WinMMDrv_MIDI_Init(midifuncs * funcs)
{
    MMRESULT rv;

    if (midiInstalled) {
        WinMMDrv_MIDI_Shutdown();
    }

    memset(funcs, 0, sizeof(midifuncs));

    LL_Reset( (MidiBuffer*) &activeMidiBuffers, next, prev );
    LL_Reset( (MidiBuffer*) &spareMidiBuffers, next, prev );

    midiMutex = CreateMutex(0, FALSE, 0);
    if (!midiMutex) {
        ErrorCode = WinMMErr_MIDICreateMutex;
        return WinMMErr_Error;
    }

    rv = midiStreamOpen(&midiStream, &midiDeviceID, 1, (DWORD_PTR) 0, (DWORD_PTR) 0, CALLBACK_NULL);
    if (rv != MMSYSERR_NOERROR) {
        CloseHandle(midiMutex);
        midiMutex = 0;

        midi_error(rv, "WinMM MIDI_Init midiStreamOpen");
        ErrorCode = WinMMErr_MIDIStreamOpen;
        return WinMMErr_Error;
    }
    
    funcs->NoteOff = Func_NoteOff;
    funcs->NoteOn  = Func_NoteOn;
    funcs->PolyAftertouch = Func_PolyAftertouch;
    funcs->ControlChange = Func_ControlChange;
    funcs->ProgramChange = Func_ProgramChange;
    funcs->ChannelAftertouch = Func_ChannelAftertouch;
    funcs->PitchBend = Func_PitchBend;
    funcs->SysEx = Func_SysEx;

    midiInstalled = TRUE;
    
    return WinMMErr_Ok;
}
Exemple #9
0
int s_ResetMidiStream(WinmidiObject *obj, UINT devNum) {
	UINT retVal;

	assert(obj);

	(void)midiOutReset((HMIDIOUT)obj->m_midiOut);
	if(!s_CleanUpBlockNodes(obj)) {
	return 0;
	}
	(void)midiStreamClose(obj->m_midiOut);
	if(!s_FreeNodes(obj)) {
	return 0;
	}

	retVal = midiStreamOpen(&(obj->m_midiOut), &devNum, 1,
		(DWORD) s_MidiCallback, (DWORD) obj, CALLBACK_FUNCTION); //lint !e620
	if(retVal != MMSYSERR_NOERROR) {
	s_SetMidiError("opening a MIDI stream", retVal);
	return 0;
	}
	assert(obj->m_midiOut);
	
	return 1;
}
Exemple #10
0
static void test_midiStream(UINT udev, HWND hwnd)
{
    HMIDISTRM hm;
    MMRESULT rc, rc2;
    MIDIHDR mhdr;
    union {
        MIDIPROPTEMPO tempo;
        MIDIPROPTIMEDIV tdiv;
    } midiprop;

    if (hwnd)
        rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)hwnd, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
    else
        rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)callback_func, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
    if (rc == MMSYSERR_NOTSUPPORTED)
    {
        skip( "MIDI stream not supported\n" );
        return;
    }
    ok(!rc, "midiStreamOpen(dev=%d) rc=%s\n", udev, mmsys_error(rc));
    if (rc) return;

    test_notification(hwnd, "midiStreamOpen", MOM_OPEN, 0);

    midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
    rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
    ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
    ok(midiprop.tempo.dwTempo==500000, "default stream tempo %u microsec per quarter note\n", midiprop.tempo.dwTempo);

    midiprop.tdiv.cbStruct = sizeof(midiprop.tdiv);
    rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TIMEDIV);
    ok(!rc, "midiStreamProperty TIMEDIV rc=%s\n", mmsys_error(rc));
    todo_wine ok(24==LOWORD(midiprop.tdiv.dwTimeDiv), "default stream time division %u\n", midiprop.tdiv.dwTimeDiv);

    memset(&mhdr, 0, sizeof(mhdr));
    mhdr.dwFlags = 0;
    mhdr.dwUser   = 0x56FA552C;
    mhdr.dwOffset = 1234567890;
    mhdr.dwBufferLength = sizeof(strmEvents);
    mhdr.dwBytesRecorded = mhdr.dwBufferLength;
    mhdr.lpData = (LPSTR)&strmEvents[0];
    if (mhdr.lpData) {
        rc = midiOutLongMsg((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
        ok(rc==MIDIERR_UNPREPARED, "midiOutLongMsg unprepared rc=%s\n", mmsys_error(rc));
        test_notification(hwnd, "midiOutLong unprepared", 0, WHATEVER);

        rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset)-1);
        ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare tiny rc=%s\n", mmsys_error(rc));
        rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiOutPrepare old size rc=%s\n", mmsys_error(rc));
        ok(mhdr.dwFlags & MHDR_PREPARED, "MHDR.dwFlags when prepared %x\n", mhdr.dwFlags);

        /* The device is still in paused mode and should queue the message. */
        rc = midiStreamOut(hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiStreamOut old size rc=%s\n", mmsys_error(rc));
        rc2 = rc;
        trace("MIDIHDR flags=%x when submitted\n", mhdr.dwFlags);
        /* w9X/me does not set MHDR_ISSTRM when StreamOut exits,
         * but it will be set on all systems after the job is finished. */

        Sleep(90);
        /* Wine <1.1.39 started playing immediately */
        test_notification(hwnd, "midiStream still paused", 0, WHATEVER);

    /* MSDN asks to use midiStreamRestart prior to midiStreamOut()
     * because the starting state is 'pause', but some apps seem to
     * work with the inverse order: queue everything, then play.
     */

        rc = midiStreamRestart(hm);
        ok(!rc, "midiStreamRestart rc=%s\n", mmsys_error(rc));

        if (!rc2) while(mhdr.dwFlags & MHDR_INQUEUE) {
            trace("async MIDI still queued\n");
            Sleep(100);
        } /* Checking INQUEUE is not the recommended way to wait for the end of a job, but we're testing. */
        /* MHDR_ISSTRM is not necessarily set when midiStreamOut returns
         * rather than when the queue is eventually processed. */
        ok(mhdr.dwFlags & MHDR_ISSTRM, "MHDR.dwFlags %x no ISSTRM when out of queue\n", mhdr.dwFlags);
        if (!rc2) while(!(mhdr.dwFlags & MHDR_DONE)) {
            /* Never to be seen except perhaps on multicore */
            trace("async MIDI still not done\n");
            Sleep(100);
        }
        ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags %x not DONE when out of queue\n", mhdr.dwFlags);
        test_notification(hwnd, "midiStream callback", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
        test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);

        /* Native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
        ok(1234567890!=mhdr.dwOffset, "play left MIDIHDR.dwOffset at %u\n", mhdr.dwOffset);

        rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
        rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiOutUnprepare #2 rc=%s\n", mmsys_error(rc));

        trace("MIDIHDR stream flags=%x when finished\n", mhdr.dwFlags);
        ok(mhdr.dwFlags & MHDR_DONE, "MHDR.dwFlags when done %x\n", mhdr.dwFlags);

        test_position(hm, TIME_MS,      TIME_MS);
        test_position(hm, TIME_TICKS,   TIME_TICKS);
        todo_wine test_position(hm, TIME_MIDI,    TIME_MIDI);
        test_position(hm, TIME_SMPTE,   TIME_MS);
        test_position(hm, TIME_SAMPLES, TIME_MS);
        test_position(hm, TIME_BYTES,   TIME_MS);

        Sleep(400); /* Hear note */

        midiprop.tempo.cbStruct = sizeof(midiprop.tempo);
        rc = midiStreamProperty(hm, (void*)&midiprop, MIDIPROP_GET|MIDIPROP_TEMPO);
        ok(!rc, "midiStreamProperty TEMPO rc=%s\n", mmsys_error(rc));
        ok(0x0493E0==midiprop.tempo.dwTempo, "stream set tempo %u\n", midiprop.tdiv.dwTimeDiv);

        rc = midiStreamRestart(hm);
        ok(!rc, "midiStreamRestart #2 rc=%s\n", mmsys_error(rc));

        mhdr.dwFlags |= MHDR_ISSTRM;
        /* Preset flags (e.g. MHDR_ISSTRM) do not disturb. */
        rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiOutPrepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));
        rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, offsetof(MIDIHDR,dwOffset));
        ok(!rc, "midiOutUnprepare used flags %x rc=%s\n", mhdr.dwFlags, mmsys_error(rc));

        rc = midiStreamRestart(hm);
        ok(!rc, "midiStreamRestart #3 rc=%s\n", mmsys_error(rc));
    }
    ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);
    ok(0==((MIDISHORTEVENT*)&strmEvents)[0].dwStreamID, "dwStreamID set to %x\n", ((LPMIDIEVENT)&strmEvents[0])->dwStreamID);

    /* dwBytesRecorded controls how much is played, not dwBufferLength
     * allowing to immediately forward packets from midiIn to midiOut */
    mhdr.dwOffset = 1234123123;
    mhdr.dwBufferLength = sizeof(strmNops);
    trace("buffer: %u\n", mhdr.dwBufferLength);
    mhdr.dwBytesRecorded = 0;
    mhdr.lpData = (LPSTR)&strmNops[0];
    strmNops[0].dwEvent |= MEVT_F_CALLBACK;
    strmNops[1].dwEvent |= MEVT_F_CALLBACK;

    rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
    ok(!rc, "midiOutPrepare rc=%s\n", mmsys_error(rc));

    rc = playStream(hm, &mhdr);
    ok(!rc, "midiStreamOut 0 bytes recorded rc=%s\n", mmsys_error(rc));

    test_notification(hwnd, "midiStreamOut", MOM_DONE, (DWORD_PTR)&mhdr);
    test_notification(hwnd, "0 bytes recorded", 0, WHATEVER);

    /* FIXME: check dwOffset within callback
     * instead of the unspecified value afterwards */
    ok(1234123123==mhdr.dwOffset || broken(0==mhdr.dwOffset), "play 0 set MIDIHDR.dwOffset to %u\n", mhdr.dwOffset);
    /* w2k and later only set dwOffset when processing MEVT_T_CALLBACK,
     * while w9X/me/nt always sets it.  Have Wine behave like w2k because the
     * dwOffset slot does not exist in the small size MIDIHDR. */

    mhdr.dwOffset = 1234123123;
    mhdr.dwBytesRecorded = 1*sizeof(MIDISHORTEVENT);

    rc = playStream(hm, &mhdr);
    ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));

    test_notification(hwnd, "1 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
    test_notification(hwnd, "1 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
    test_notification(hwnd, "1 of 2 events", 0, WHATEVER);
    ok(0==mhdr.dwOffset, "MIDIHDR.dwOffset 1/2 changed to %u\n", mhdr.dwOffset);

    mhdr.dwOffset = 1234123123;
    mhdr.dwBytesRecorded = 2*sizeof(MIDISHORTEVENT);

    rc = playStream(hm, &mhdr);
    ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));

    test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
    test_notification(hwnd, "2 of 2 events", MOM_POSITIONCB, (DWORD_PTR)&mhdr);
    test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
    test_notification(hwnd, "2 of 2 events", 0, WHATEVER);
    ok(sizeof(MIDISHORTEVENT)==mhdr.dwOffset, "MIDIHDR.dwOffset 2/2 changed to %u\n", mhdr.dwOffset);
    ok(mhdr.dwBytesRecorded == 2*sizeof(MIDISHORTEVENT), "dwBytesRecorded changed to %u\n", mhdr.dwBytesRecorded);

    strmNops[0].dwEvent &= ~MEVT_F_CALLBACK;
    strmNops[1].dwEvent &= ~MEVT_F_CALLBACK;
    mhdr.dwOffset = 1234123123;
    rc = playStream(hm, &mhdr);
    ok(!rc, "midiStreamOut 1 event out of 2 rc=%s\n", mmsys_error(rc));

    test_notification(hwnd, "0 CB in 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
    test_notification(hwnd, "0 CB in 2 events", 0, WHATEVER);
    /* w9X/me/nt set dwOffset to the position played last */
    ok(1234123123==mhdr.dwOffset || broken(sizeof(MIDISHORTEVENT)==mhdr.dwOffset), "MIDIHDR.dwOffset nocb changed to %u\n", mhdr.dwOffset);

    mhdr.dwBytesRecorded = mhdr.dwBufferLength-1;
    rc = playStream(hm, &mhdr);
    ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBytesRecorded modulo MIDIEVENT rc=%s\n", mmsys_error(rc));
    if (!rc) {
         test_notification(hwnd, "2 of 2 events", MOM_DONE, (DWORD_PTR)&mhdr);
    }

    mhdr.dwBytesRecorded = mhdr.dwBufferLength+1;
    rc = playStream(hm, &mhdr);
    ok(rc==MMSYSERR_INVALPARAM,"midiStreamOut dwBufferLength<dwBytesRecorded rc=%s\n", mmsys_error(rc));
    test_notification(hwnd, "past MIDIHDR tests", 0, WHATEVER);

    rc = midiStreamStop(hm);
    ok(!rc, "midiStreamStop rc=%s\n", mmsys_error(rc));
    ok(mhdr.dwUser==0x56FA552C, "MIDIHDR.dwUser changed to %lx\n", mhdr.dwUser);

    rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
    ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));
    ok(0==strmNops[0].dwStreamID, "dwStreamID[0] set to %x\n", strmNops[0].dwStreamID);
    ok(0==strmNops[1].dwStreamID, "dwStreamID[1] set to %x\n", strmNops[1].dwStreamID);

    mhdr.dwBufferLength = 70000; /* > 64KB! */
    mhdr.lpData = HeapAlloc(GetProcessHeap(), 0 , mhdr.dwBufferLength);
    ok(mhdr.lpData!=NULL, "No %d bytes of memory!\n", mhdr.dwBufferLength);
    if (mhdr.lpData) {
        mhdr.dwFlags = 0;
        /* PrepareHeader detects the too large buffer is for a stream. */
        rc = midiOutPrepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
        todo_wine ok(rc==MMSYSERR_INVALPARAM, "midiOutPrepare stream too large rc=%s\n", mmsys_error(rc));

        rc = midiOutUnprepareHeader((HMIDIOUT)hm, &mhdr, sizeof(mhdr));
        ok(!rc, "midiOutUnprepare rc=%s\n", mmsys_error(rc));

        HeapFree(GetProcessHeap(), 0, mhdr.lpData);
    }

    rc = midiStreamClose(hm);
    ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
    test_notification(hwnd, "midiStreamClose", MOM_CLOSE, 0);
    test_notification(hwnd, "midiStream over", 0, WHATEVER);

    rc = midiStreamOpen(&hm, &udev, 1, 0, (DWORD_PTR)MYCBINST, CALLBACK_FUNCTION);
    ok(!rc /*w2k*/|| rc==MMSYSERR_INVALPARAM/*w98*/, "midiStreamOpen NULL function rc=%s\n", mmsys_error(rc));
    if (!rc) {
        trace("Device %d accepts NULL CALLBACK_FUNCTION\n", udev);
        rc = midiStreamClose(hm);
        ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
    }

    rc = midiStreamOpen(&hm, &udev, 1, (DWORD_PTR)0xDEADBEEF, (DWORD_PTR)MYCBINST, CALLBACK_WINDOW);
    ok(rc==MMSYSERR_INVALPARAM, "midiStreamOpen bad window rc=%s\n", mmsys_error(rc));
    if (!rc) {
        rc = midiStreamClose(hm);
        ok(!rc, "midiStreamClose rc=%s\n", mmsys_error(rc));
    }
}
Exemple #11
0
static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
{
    DWORD dwDevice;
    int i = midi->device_id;
    midiwinmm_type m;
    MIDIPROPTEMPO propdata;
    MIDIPROPTIMEDIV divdata;
    int max_sysex_len = midi->buffer_len * 4;
    int output_buffer_len;
    int num_buffers;
    dwDevice = (DWORD) descriptors[i].descriptor;

    /* create system dependent device data */
    m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */
    midi->descriptor = m;
    if (!m) goto no_memory;
    m->handle.out = NULL;
    m->buffers = NULL;
    m->num_buffers = 0;
    m->max_buffers = 0;
    m->buffers_expanded = FALSE;
    m->next_buffer = 0;
    m->last_time = 0;
    m->first_message = TRUE; /* we treat first message as special case */
    m->sysex_mode = FALSE;
    m->sysex_word = 0;
    m->sysex_byte_count = 0;
    m->hdr = NULL;
    m->sync_time = 0;
    m->delta = 0;
    m->error = MMSYSERR_NOERROR;

    /* create a signal */
    m->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);

    /* this should only fail when there are very serious problems */
    assert(m->buffer_signal);

    /* open device */
    if (midi->latency == 0) {
        /* use simple midi out calls */
        pm_hosterror = midiOutOpen(
                (LPHMIDIOUT) & m->handle.out,  /* device Handle */
		dwDevice,  /* device ID  */
		/* note: same callback fn as for StreamOpen: */
		(DWORD_PTR) winmm_streamout_callback, /* callback fn */
		(DWORD_PTR) midi,  /* callback instance data */
		CALLBACK_FUNCTION); /* callback type */
    } else {
        /* use stream-based midi output (schedulable in future) */
        pm_hosterror = midiStreamOpen(
	        &m->handle.stream,  /* device Handle */
		(LPUINT) & dwDevice,  /* device ID pointer */
		1,  /* reserved, must be 1 */
		(DWORD_PTR) winmm_streamout_callback,
		(DWORD_PTR) midi,  /* callback instance data */
		CALLBACK_FUNCTION);
    }
    if (pm_hosterror != MMSYSERR_NOERROR) {
        goto free_descriptor;
    }

    if (midi->latency == 0) {
        num_buffers = NUM_SIMPLE_SYSEX_BUFFERS;
        output_buffer_len = max_sysex_len / num_buffers;
        if (output_buffer_len < MIN_SIMPLE_SYSEX_LEN)
            output_buffer_len = MIN_SIMPLE_SYSEX_LEN;
    } else {
        long dur = 0;
        num_buffers = max(midi->buffer_len, midi->latency / 2);
        if (num_buffers < MIN_STREAM_BUFFERS)
            num_buffers = MIN_STREAM_BUFFERS;
        output_buffer_len = STREAM_BUFFER_LEN;

        propdata.cbStruct = sizeof(MIDIPROPTEMPO);
        propdata.dwTempo = 480000; /* microseconds per quarter */
        pm_hosterror = midiStreamProperty(m->handle.stream,
                                          (LPBYTE) & propdata,
                                          MIDIPROP_SET | MIDIPROP_TEMPO);
        if (pm_hosterror) goto close_device;

        divdata.cbStruct = sizeof(MIDIPROPTEMPO);
        divdata.dwTimeDiv = 480;   /* divisions per quarter */
        pm_hosterror = midiStreamProperty(m->handle.stream,
                                          (LPBYTE) & divdata,
                                          MIDIPROP_SET | MIDIPROP_TIMEDIV);
        if (pm_hosterror) goto close_device;
    }
    /* allocate buffers */
    if (allocate_buffers(m, output_buffer_len, num_buffers)) 
        goto free_buffers;
    /* start device */
    if (midi->latency != 0) {
        pm_hosterror = midiStreamRestart(m->handle.stream);
        if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers;
    }
    return pmNoError;

free_buffers:
    /* buffers are freed below by winmm_out_delete */
close_device:
    midiOutClose(m->handle.out);
free_descriptor:
    midi->descriptor = NULL;
    winmm_out_delete(midi); /* frees buffers and m */
no_memory:
    if (pm_hosterror) {
        int err = midiOutGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
                                      PM_HOST_ERROR_MSG_LEN);
        assert(err == MMSYSERR_NOERROR);
        return pmHostError;
    }
    return pmInsufficientMemory;
}