Cygwin support, using SDL.
[btb/d2x.git] / unused / win95 / winmidi.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14
15 #pragma off (unreferenced)
16 static char rcsid[] = "$Id: winmidi.c,v 1.1.1.1 2001-01-19 03:30:15 bradleyb Exp $";
17 #pragma on (unreferenced)
18
19
20
21
22 #define _WIN32
23 #define WIN95
24 #define WIN32_LEAN_AND_MEAN
25
26 #include <windows.h>
27 #include <windowsx.h>
28 #include <mmsystem.h>
29 #include <mmreg.h>
30
31 #include "pstypes.h"
32 #include "mono.h"
33 #include "error.h"
34 #include "mem.h"
35 #include "winapp.h"
36 #include "winregs.h"
37
38
39 #include "midifile.h"
40 #include "midiseq.h"
41 #include "winmidi.h"
42
43
44 //      ----------------------------------------------------------------------------
45 //      Data
46 //      ----------------------------------------------------------------------------
47
48 BOOL    WMidi_NewStream = 0;
49
50 static BOOL                             WMidi_initialized = FALSE;
51 static SEQ                              MSeq;
52 static DWORD                    wmidi_volume = 0x80008000;
53 static BOOL                             LoopSong = FALSE;
54 static BOOL                             MidiVolChanges = TRUE;
55
56
57 void wmidi_deamp_song(DWORD vol);
58 int wmidi_get_tech();
59 void CALLBACK wmidi_seq_callback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
60
61
62 //      ----------------------------------------------------------------------------
63 //      Sequencer Initialization and destruction
64 //      ----------------------------------------------------------------------------
65
66 int wmidi_init(uint mmdev)
67 {
68         MIDIOUTCAPS midicaps;
69         //BYTE *hdr;
70         //DWORD bufsize;
71         //int i;
72         UINT ui;
73         
74         memset(&MSeq, 0, sizeof(SEQ));
75
76         WMidi_initialized = TRUE;
77
78 //      Initialize stream buffers
79         MSeq.uState = SEQ_S_NOFILE;
80         MSeq.lpmhFree = NULL;
81         MSeq.lpbAlloc = NULL;
82         MSeq.hmidi = NULL;
83
84         MSeq.cBuffer = 4;
85         MSeq.cbBuffer = 512;
86
87         if (seqAllocBuffers(&MSeq) != MMSYSERR_NOERROR)
88         return FALSE;
89
90 //      Setup sequencer some more
91         ui = midiOutGetDevCaps(mmdev, &midicaps, sizeof(midicaps));
92         if ( ui != MMSYSERR_NOERROR) {
93                 mprintf((1, "WMIDI:(%x) Unable to find requested device.\n", MSeq.mmrcLastErr));
94                 return 0;
95         }
96         
97         if ((midicaps.dwSupport & MIDICAPS_VOLUME) || (midicaps.dwSupport & MIDICAPS_LRVOLUME)) 
98                 MidiVolChanges = TRUE;
99         else MidiVolChanges = FALSE;
100
101         MSeq.uDeviceID = mmdev;
102
103 //      Now we must do a stupid Microsoft trick to determine whether we are
104 //      using WAVE TABLE volume or OPL volume.
105         MSeq.uMCIDeviceID = wmidi_get_tech();
106
107         logentry("MIDI technology: %d.\n", MSeq.uMCIDeviceID);
108         
109         return 1;
110 }
111                                 
112
113 void wmidi_close()
114 {
115         //MIDIHDR *hdr;
116
117         Assert(WMidi_initialized == TRUE);
118
119         seqFreeBuffers(&MSeq);
120         
121         WMidi_initialized = FALSE;
122 }
123
124
125 int wmidi_get_tech()
126 {
127         HKEY hKey;
128         long lres;
129         char key[32];   
130         char buf[256];
131         char *pstr;
132         DWORD len,type;
133
134
135         lres = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap",
136                                         0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_EXECUTE,
137                                         &hKey);
138         if (lres != ERROR_SUCCESS) return 0;
139
140         len = 32;
141         lres = RegQueryValueEx(hKey, "CurrentInstrument", NULL, &type, key, &len);
142         if (lres != ERROR_SUCCESS) {
143                 RegCloseKey(hKey);
144                 return 0;
145         }
146
147         RegCloseKey(hKey);
148
149         strcpy(buf, "System\\CurrentControlSet\\control\\MediaResources\\midi\\");
150         strcat(buf, key);
151         lres = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 
152                                         0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_EXECUTE,
153                                         &hKey);
154         if (lres != ERROR_SUCCESS) return 0;
155         
156         len =128;
157         lres = RegQueryValueEx(hKey, "Description", NULL, &type, buf, &len);
158         if (lres != ERROR_SUCCESS) {
159                 RegCloseKey(hKey);
160                 return 0;
161         }
162         RegCloseKey(hKey);
163
164 //      Okay, look for 'wave' or 'fm' or 'opl'
165         pstr = strlwr(buf);     
166         if (strstr(pstr, "wave")) return 1;
167
168         return 0;
169 }
170
171
172 int wmidi_support_volchange() {
173         return (int)MidiVolChanges;
174 }
175
176
177 //      ----------------------------------------------------------------------------
178 //      High level midi song play/stop/pause interface
179 //      ----------------------------------------------------------------------------
180
181 int wmidi_init_song(WMIDISONG *song)
182 {
183         DWORD bufsize;
184         SMF *smf;
185         MMRESULT mmrc;
186
187         if (MSeq.uState != SEQ_S_NOFILE) return 0;
188
189         Assert (song->data != NULL);
190
191    mmrc = seqStop(&MSeq);
192         if (mmrc != MMSYSERR_NOERROR)
193         {
194                 mprintf((1, "WMIDI: Stop song failed.\n"));;
195                 return 0;
196         }
197
198         wmidi_close_song();
199
200         if (song->looping) LoopSong = TRUE;
201         else LoopSong = FALSE;
202
203         if (smfOpenFile(song->data, song->length, (HSMF *)&smf) != SMF_SUCCESS) return 0;
204
205 //      song is confirmed to be a MIDI v1.0 compatible file
206         MSeq.hSmf = smf;
207         MSeq.dwTimeDivision = smf->dwTimeDivision;
208         MSeq.tkLength = smf->tkLength;
209         MSeq.cTrk = smf->dwTracks;
210
211         bufsize = min(MSeq.cbBuffer, smfGetStateMaxSize());
212
213         MSeq.uState = SEQ_S_OPENED;
214         
215         return 1;
216 }
217
218
219 int wmidi_play()
220 {
221         PREROLL         preroll;
222         MMRESULT        mmrc;
223         DWORD           vol;
224
225         preroll.tkBase = 0;
226         preroll.tkEnd = MSeq.tkLength;
227
228         mprintf((1, "wmidi_play: beginning playback.\n"));
229
230         if ((mmrc = seqPreroll(&MSeq, &preroll)) != MMSYSERR_NOERROR) {
231                 mprintf((1, "wmidi_play: preroll failed! (%x)\n", mmrc));
232                 return 0;
233         }
234
235         
236         Assert(MSeq.hmidi != NULL);
237
238 //      wmidi_deamp_song(wmidi_volume);
239
240         mmrc = midiOutSetVolume((HMIDIOUT)MSeq.hmidi, wmidi_volume);    
241         if (mmrc != MMSYSERR_NOERROR) mprintf((1, "MIDI VOL ERR: %d\n", mmrc));
242         midiOutGetVolume((HMIDIOUT)MSeq.hmidi, &vol);
243
244         if (seqStart(&MSeq) != MMSYSERR_NOERROR) return 0; 
245
246         return 1;
247 }
248
249
250 int wmidi_stop()
251 {
252         if (seqStop(&MSeq) != MMSYSERR_NOERROR) return 0;
253         return 1;
254 }
255
256
257 int wmidi_pause()
258 {
259         if (MSeq.uState != SEQ_S_PAUSED) {
260                 if (seqPause(&MSeq) != MMSYSERR_NOERROR) return 0;
261         }
262         return 1;
263 }
264                 
265 int wmidi_resume()
266 {
267         if (MSeq.uState == SEQ_S_PAUSED) {
268                 if (seqRestart(&MSeq) != MMSYSERR_NOERROR) return 0;
269         }
270         return 1;
271 }
272                 
273
274 int wmidi_close_song()
275 {
276
277         LPMIDIHDR               lpmh;
278     
279         if (SEQ_S_OPENED != MSeq.uState)
280                 return MCIERR_UNSUPPORTED_FUNCTION;
281
282         LoopSong = FALSE;
283     
284         if ((HSMF)NULL != MSeq.hSmf)
285         {
286                 smfCloseFile(MSeq.hSmf);
287                 MSeq.hSmf = (HSMF)NULL;
288    }
289
290 /* take care of any stream buffers or MIDI resources left open by
291         the sequencer */
292
293         if (MSeq.hmidi) {
294
295                 for (lpmh = MSeq.lpmhFree; lpmh; lpmh = lpmh->lpNext)
296                         midiOutUnprepareHeader(MSeq.hmidi, lpmh, sizeof(MIDIHDR));
297
298                 if (MSeq.lpmhPreroll) 
299                         midiOutUnprepareHeader(MSeq.hmidi, MSeq.lpmhPreroll, sizeof(MIDIHDR));
300
301                 midiStreamClose(MSeq.hmidi);
302                 MSeq.hmidi = NULL;
303         }
304
305         MSeq.uState = SEQ_S_NOFILE;
306         
307         return 1;
308 }
309
310
311 static uint funky_vol_log_table [] = {
312         0x00000,                                // 0
313         0x0b000,                                // 1
314         0x0c000,                                // 2
315         0x0c800,                                // 3
316         0x0e000,                                // 4
317         0x0e800,                                // 5
318         0x0f000,                                //      6
319         0x0f800,                                //      7
320         0x0ffff                         // 8
321 };
322
323
324
325 int wmidi_set_volume(uint volume)
326 {
327         DWORD vol;
328         //int i;
329         MMRESULT mmrc;
330         
331
332         Assert(volume < 128);
333         if (volume == 127) volume++;
334
335         volume = volume /16;
336
337         if (!volume) vol = 0;
338         else if (MSeq.uMCIDeviceID == 1)                                // WAVETABLE 
339                 vol = funky_vol_log_table[volume]|0x00ff;
340         else {                                                                                          // OPL2/OPL3 and others
341                 vol = volume * 0x2000;                          
342                 if (vol > 0) vol--;
343         }
344
345         vol = (vol<<16)+vol;
346
347         wmidi_volume = vol;
348
349
350         if (MSeq.hmidi) {
351 //              wmidi_deamp_song(wmidi_volume);
352                 mmrc = midiOutSetVolume((HMIDIOUT)MSeq.hmidi, wmidi_volume);    
353                 if (mmrc != MMSYSERR_NOERROR) mprintf((1, "MIDI VOL ERR: %d\n", mmrc));
354                 midiOutGetVolume((HMIDIOUT)MSeq.hmidi, &vol);
355         }
356
357         return 1;
358 }
359
360
361 #define MIDI_CTRLCHANGE ((BYTE)0xB0)        // + ctrlr + value
362 #define MIDICTRL_VOLUME ((BYTE)0x07)
363  
364
365 void wmidi_deamp_song(DWORD vol)
366 {
367         float ptvol;
368         DWORD ptdvol;
369         DWORD dwvol, dwstatus, dwevent;
370         MMRESULT mmrc;
371         int i;  
372
373
374         vol = vol & 0x0000ffff;
375
376         ptvol = (float)(vol)*100.0/65536.0;
377         ptdvol = (DWORD)(ptvol);
378
379         mprintf((0, "Deamp = %d percent.\n", ptdvol));
380
381         for (i = 0, dwstatus = MIDI_CTRLCHANGE; i < 16; i++, dwstatus++)
382         {
383                 dwvol = ( 100 * ptdvol ) / 1000;
384            dwevent = dwstatus | ((DWORD)MIDICTRL_VOLUME << 8)
385             | ((DWORD)dwvol << 16);
386                 if(( mmrc= midiOutShortMsg( (HMIDIOUT)MSeq.hmidi, dwevent ))
387                             != MMSYSERR_NOERROR )
388       {
389                         mprintf((1, "WINMIDI::BAD MIDI VOLUME CHANGE!\n"));
390                 return;
391       }
392         }
393 }
394
395
396
397 /*****************************************************************************
398 *
399 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
400 *  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
401 *  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR
402 *  A PARTICULAR PURPOSE.
403 *
404 *  Copyright (C) 1993-1995 Microsoft Corporation. All Rights Reserved.
405 *
406 ******************************************************************************/
407
408
409 PRIVATE void FAR PASCAL seqMIDICallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
410 PRIVATE MMRESULT FNLOCAL XlatSMFErr(SMFRESULT smfrc);
411
412 /***************************************************************************
413 *  
414 * seqAllocBuffers
415 *
416 * Allocate buffers for this instance.
417 *
418 ***************************************************************************/
419 MMRESULT FNLOCAL seqAllocBuffers(
420     PSEQ                    pSeq)
421 {
422     DWORD                   dwEachBufferSize;
423     DWORD                   dwAlloc;
424     UINT                    i;
425     LPBYTE                  lpbWork;
426
427     Assert(pSeq != NULL);
428
429     pSeq->uState    = SEQ_S_NOFILE;
430     pSeq->lpmhFree  = NULL;
431     pSeq->lpbAlloc  = NULL;
432     pSeq->hSmf      = (HSMF)NULL;
433     
434     /* First make sure we can allocate the buffers they asked for
435     */
436     dwEachBufferSize = sizeof(MIDIHDR) + (DWORD)(pSeq->cbBuffer);
437     dwAlloc          = dwEachBufferSize * (DWORD)(pSeq->cBuffer);
438     
439     pSeq->lpbAlloc = GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE, dwAlloc);
440     if (NULL == pSeq->lpbAlloc)
441         return MCIERR_OUT_OF_MEMORY;
442
443     /* Initialize all MIDIHDR's and throw them into a free list
444     */
445     pSeq->lpmhFree = NULL;
446
447     lpbWork = pSeq->lpbAlloc;
448     for (i=0; i < pSeq->cBuffer; i++)
449     {
450         ((LPMIDIHDR)lpbWork)->lpNext            = pSeq->lpmhFree;
451
452         ((LPMIDIHDR)lpbWork)->lpData            = lpbWork + sizeof(MIDIHDR);
453         ((LPMIDIHDR)lpbWork)->dwBufferLength    = pSeq->cbBuffer;
454         ((LPMIDIHDR)lpbWork)->dwBytesRecorded   = 0;
455         ((LPMIDIHDR)lpbWork)->dwUser            = (DWORD)(UINT)pSeq;
456         ((LPMIDIHDR)lpbWork)->dwFlags           = 0;
457
458         pSeq->lpmhFree = (LPMIDIHDR)lpbWork;
459
460         lpbWork += dwEachBufferSize;
461     }
462
463     return MMSYSERR_NOERROR;
464 }
465
466 /***************************************************************************
467 *  
468 * seqFreeBuffers
469 *
470 * Free buffers for this instance.
471 *
472 ****************************************************************************/
473 VOID FNLOCAL seqFreeBuffers(
474     PSEQ                    pSeq)
475 {
476     LPMIDIHDR               lpmh;
477     
478     Assert(pSeq != NULL);
479
480     if (NULL != pSeq->lpbAlloc)
481     {
482         lpmh = (LPMIDIHDR)pSeq->lpbAlloc;
483         Assert(!(lpmh->dwFlags & MHDR_PREPARED));
484         
485         GlobalFreePtr(pSeq->lpbAlloc);
486     }
487 }
488
489
490 /***************************************************************************
491 *  
492 * seqPreroll
493 *
494 * Prepares the file for playback at the given position.
495 *
496 ****************************************************************************/
497 MMRESULT FNLOCAL seqPreroll(
498     PSEQ                    pSeq,
499     LPPREROLL               lpPreroll)
500 {
501     SMFRESULT           smfrc;
502     MMRESULT            mmrc        = MMSYSERR_NOERROR;
503     MIDIPROPTIMEDIV     mptd;
504     LPMIDIHDR           lpmh = NULL;
505     LPMIDIHDR           lpmhPreroll = NULL;
506     DWORD               cbPrerollBuffer;
507     UINT                uDeviceID;
508
509     Assert(pSeq != NULL);
510
511     pSeq->mmrcLastErr = MMSYSERR_NOERROR;
512
513     if (pSeq->uState != SEQ_S_OPENED &&
514         pSeq->uState != SEQ_S_PREROLLED)
515         return MCIERR_UNSUPPORTED_FUNCTION;
516
517         pSeq->tkBase = lpPreroll->tkBase;
518         pSeq->tkEnd = lpPreroll->tkEnd;
519
520     if (pSeq->hmidi)
521     {
522         // Recollect buffers from MMSYSTEM back into free queue
523         //
524         pSeq->uState = SEQ_S_RESET;
525         midiOutReset(pSeq->hmidi);
526
527                 while (pSeq->uBuffersInMMSYSTEM)
528                         Sleep(0);
529     }
530     
531     pSeq->uBuffersInMMSYSTEM = 0;
532     pSeq->uState = SEQ_S_PREROLLING;
533     
534     //
535     // We've successfully opened the file and all of the tracks; now
536     // open the MIDI device and set the time division.
537     //
538     // NOTE: seqPreroll is equivalent to seek; device might already be open
539     //
540     if (NULL == pSeq->hmidi)
541     {
542         uDeviceID = pSeq->uDeviceID;
543         if ((mmrc = midiStreamOpen(&pSeq->hmidi,
544                                    &uDeviceID,
545                                    1,
546                                    (DWORD)seqMIDICallback,
547                                    0,
548                                    CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
549         {
550             pSeq->hmidi = NULL;
551             goto seq_Preroll_Cleanup;
552         }
553         
554         mptd.cbStruct  = sizeof(mptd);
555         mptd.dwTimeDiv = pSeq->dwTimeDivision;
556         if ((mmrc = midiStreamProperty(
557                                        (HMIDI)pSeq->hmidi,
558                                        (LPBYTE)&mptd,
559                                        MIDIPROP_SET|MIDIPROP_TIMEDIV)) != MMSYSERR_NOERROR)
560         {
561             mprintf((1, "midiStreamProperty() -> %04X\n", (WORD)mmrc));
562             midiStreamClose(pSeq->hmidi);
563             pSeq->hmidi = NULL;
564             mmrc = MCIERR_DEVICE_NOT_READY;
565             goto seq_Preroll_Cleanup;
566         }
567     }
568
569     mmrc = MMSYSERR_NOERROR;
570
571     //
572     //  Allocate a preroll buffer.  Then if we don't have enough room for
573     //  all the preroll info, we make the buffer larger.  
574     //
575     if (!pSeq->lpmhPreroll)
576     {
577         cbPrerollBuffer = 4096;
578         lpmhPreroll = (LPMIDIHDR)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE,
579                                                             cbPrerollBuffer);
580     }
581     else
582     {
583         cbPrerollBuffer = pSeq->cbPreroll;
584         lpmhPreroll = pSeq->lpmhPreroll;
585     }
586
587     lpmhPreroll->lpNext            = pSeq->lpmhFree;
588     lpmhPreroll->lpData            = (LPBYTE)lpmhPreroll + sizeof(MIDIHDR);
589     lpmhPreroll->dwBufferLength    = cbPrerollBuffer - sizeof(MIDIHDR);
590     lpmhPreroll->dwBytesRecorded   = 0;
591     lpmhPreroll->dwUser            = (DWORD)(UINT)pSeq;
592     lpmhPreroll->dwFlags           = 0;
593
594     do
595     {
596         smfrc = smfSeek(pSeq->hSmf, pSeq->tkBase, lpmhPreroll);
597         if( SMF_SUCCESS != smfrc )
598         {
599             if( ( SMF_NO_MEMORY != smfrc )  ||
600                 ( cbPrerollBuffer >= 32768L ) )
601             {
602                 mprintf((1, "smfSeek() returned %lu\n", (DWORD)smfrc));
603
604                 GlobalFreePtr(lpmhPreroll);
605                 pSeq->lpmhPreroll = NULL;
606
607                 mmrc = XlatSMFErr(smfrc);
608                 goto seq_Preroll_Cleanup;
609             }
610             else   //  Try to grow buffer.
611             {
612                 cbPrerollBuffer *= 2;
613                 lpmh = (LPMIDIHDR)GlobalReAllocPtr( lpmhPreroll, cbPrerollBuffer, 0 );
614                 if( NULL == lpmh )
615                 {
616                     mprintf((1,"seqPreroll - realloc failed, aborting preroll.\n"));
617                     mmrc = MCIERR_OUT_OF_MEMORY;
618                     goto seq_Preroll_Cleanup;
619                 }
620
621                 lpmhPreroll = lpmh;
622                 lpmhPreroll->lpData = (LPBYTE)lpmhPreroll + sizeof(MIDIHDR);
623                 lpmhPreroll->dwBufferLength = cbPrerollBuffer - sizeof(MIDIHDR);
624
625                 pSeq->lpmhPreroll = lpmhPreroll;
626                 pSeq->cbPreroll = cbPrerollBuffer;
627             }
628         }
629     } while( SMF_SUCCESS != smfrc );
630
631     if (MMSYSERR_NOERROR != (mmrc = midiOutPrepareHeader(pSeq->hmidi, lpmhPreroll, sizeof(MIDIHDR))))
632     {
633         mprintf((1, "midiOutPrepare(preroll) -> %lu!\n", (DWORD)mmrc));
634
635         mmrc = MCIERR_DEVICE_NOT_READY;
636         goto seq_Preroll_Cleanup;
637     }
638
639     ++pSeq->uBuffersInMMSYSTEM;
640
641     if (MMSYSERR_NOERROR != (mmrc = midiStreamOut(pSeq->hmidi, lpmhPreroll, sizeof(MIDIHDR))))
642     {
643         mprintf((1, "midiStreamOut(preroll) -> %lu!\n", (DWORD)mmrc));
644
645         mmrc = MCIERR_DEVICE_NOT_READY;
646         --pSeq->uBuffersInMMSYSTEM;
647         goto seq_Preroll_Cleanup;
648     }
649     mprintf((1,"seqPreroll: midiStreamOut(0x%x,0x%lx,%u) returned %u.\n",pSeq->hmidi,lpmhPreroll,sizeof(MIDIHDR),mmrc));
650
651     pSeq->fdwSeq &= ~SEQ_F_EOF;
652     while (pSeq->lpmhFree)
653     {
654         lpmh = pSeq->lpmhFree;
655         pSeq->lpmhFree = lpmh->lpNext;
656
657         smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
658         if (SMF_SUCCESS != smfrc && SMF_END_OF_FILE != smfrc)
659         {
660             mprintf((1, "SFP: smfReadEvents() -> %u\n", (UINT)smfrc));
661             mmrc = XlatSMFErr(smfrc);
662             goto seq_Preroll_Cleanup;
663         }
664
665         if (MMSYSERR_NOERROR != (mmrc = midiOutPrepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
666         {
667             mprintf((1, "SFP: midiOutPrepareHeader failed\n"));
668             goto seq_Preroll_Cleanup;
669         }
670
671         if (MMSYSERR_NOERROR != (mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh))))
672         {
673             mprintf((1, "SFP: midiStreamOut failed\n"));
674             goto seq_Preroll_Cleanup;
675         }
676
677         ++pSeq->uBuffersInMMSYSTEM; 
678
679         if (SMF_END_OF_FILE == smfrc)
680         {
681             pSeq->fdwSeq |= SEQ_F_EOF;
682             break;
683         }
684     } 
685
686 seq_Preroll_Cleanup:
687     if (MMSYSERR_NOERROR != mmrc)
688     {
689         pSeq->uState = SEQ_S_OPENED;
690         pSeq->fdwSeq &= ~SEQ_F_WAITING;
691     }
692     else
693     {
694         pSeq->uState = SEQ_S_PREROLLED;
695     }
696
697     return mmrc;
698 }
699
700 /***************************************************************************
701 *  
702 * seqStart
703 *
704 * Starts playback at the current position.
705 *       
706 ***************************************************************************/
707 MMRESULT FNLOCAL seqStart(
708     PSEQ                    pSeq)
709 {
710     Assert(NULL != pSeq);
711
712     if (SEQ_S_PREROLLED != pSeq->uState)
713     {
714         mprintf((1, "seqStart(): State is wrong! [%u]\n", pSeq->uState));
715         return MCIERR_UNSUPPORTED_FUNCTION;
716     }
717
718     pSeq->uState = SEQ_S_PLAYING;
719
720     return midiStreamRestart(pSeq->hmidi);
721 }
722
723 /***************************************************************************
724 *  
725 * seqPause
726 *
727 ***************************************************************************/
728 MMRESULT FNLOCAL seqPause(
729     PSEQ                    pSeq)
730 {
731     Assert(NULL != pSeq);
732     
733     if (SEQ_S_PLAYING != pSeq->uState)
734         return MCIERR_UNSUPPORTED_FUNCTION;
735
736     pSeq->uState = SEQ_S_PAUSED;
737     midiStreamPause(pSeq->hmidi);
738     
739     return MMSYSERR_NOERROR;
740 }
741
742 /***************************************************************************
743 *  
744 * seqRestart
745 *
746 ***************************************************************************/
747 MMRESULT FNLOCAL seqRestart(
748     PSEQ                    pSeq)
749 {
750     Assert(NULL != pSeq);
751
752     if (SEQ_S_PAUSED != pSeq->uState)
753         return MCIERR_UNSUPPORTED_FUNCTION;
754
755     pSeq->uState = SEQ_S_PLAYING;
756     midiStreamRestart(pSeq->hmidi);
757
758     return MMSYSERR_NOERROR;
759 }
760
761 /***************************************************************************
762 *  
763 * seqStop
764 *
765 * Totally stops playback of an instance.
766 *
767 ***************************************************************************/
768 MMRESULT FNLOCAL seqStop(
769     PSEQ                    pSeq)
770 {
771     Assert(NULL != pSeq);
772
773     /* Automatic success if we're already stopped
774     */
775     if (SEQ_S_PLAYING != pSeq->uState &&
776         SEQ_S_PAUSED != pSeq->uState)
777     {
778         pSeq->fdwSeq &= ~SEQ_F_WAITING;
779         return MMSYSERR_NOERROR;
780     }
781
782     pSeq->uState = SEQ_S_STOPPING;
783     pSeq->fdwSeq |= SEQ_F_WAITING;
784     
785     if (MMSYSERR_NOERROR != (pSeq->mmrcLastErr = midiStreamStop(pSeq->hmidi)))
786     {
787         mprintf((1, "midiOutStop() returned %lu in seqStop()!\n", (DWORD)pSeq->mmrcLastErr));
788         
789         pSeq->fdwSeq &= ~SEQ_F_WAITING;
790         return MCIERR_DEVICE_NOT_READY;
791     }
792
793         while (pSeq->uBuffersInMMSYSTEM)
794                 Sleep(0);
795     
796     return MMSYSERR_NOERROR;
797 }
798
799 /***************************************************************************
800 *  
801 * seqTime
802 *
803 ***************************************************************************/
804 MMRESULT FNLOCAL seqTime(
805     PSEQ                    pSeq,
806     PTICKS                  pTicks)
807 {
808     MMRESULT                mmr;
809     MMTIME                  mmt;
810     
811     Assert(pSeq != NULL);
812
813     if (SEQ_S_PLAYING != pSeq->uState &&
814         SEQ_S_PAUSED != pSeq->uState &&
815         SEQ_S_PREROLLING != pSeq->uState &&
816         SEQ_S_PREROLLED != pSeq->uState &&
817         SEQ_S_OPENED != pSeq->uState)
818     {
819         mprintf((1, "seqTime(): State wrong! [is %u]\n", pSeq->uState));
820         return MCIERR_UNSUPPORTED_FUNCTION;
821     }
822
823     *pTicks = 0;
824     if (SEQ_S_OPENED != pSeq->uState)
825     {
826         *pTicks = pSeq->tkBase;
827         if (SEQ_S_PREROLLED != pSeq->uState)
828         {
829             mmt.wType = TIME_TICKS;
830             mmr = midiStreamPosition(pSeq->hmidi, &mmt, sizeof(mmt));
831             if (MMSYSERR_NOERROR != mmr)
832             {
833                 mprintf((1, "midiStreamPosition() returned %lu\n", (DWORD)mmr));
834                 return MCIERR_DEVICE_NOT_READY;
835             }
836
837             *pTicks += mmt.u.ticks;
838         }
839     }
840
841     return MMSYSERR_NOERROR;
842 }
843                               
844 /***************************************************************************
845 *  
846 * seqMillisecsToTicks
847 *
848 * Given a millisecond offset in the output stream, returns the associated
849 * tick position.
850 *
851 ***************************************************************************/
852 TICKS FNLOCAL seqMillisecsToTicks(
853     PSEQ                    pSeq,
854     DWORD                   msOffset)
855 {
856     return smfMillisecsToTicks(pSeq->hSmf, msOffset);
857 }
858
859 /***************************************************************************
860 *  
861 * seqTicksToMillisecs
862 *
863 * Given a tick offset in the output stream, returns the associated
864 * millisecond position.
865 *
866 ***************************************************************************/
867 DWORD FNLOCAL seqTicksToMillisecs(
868     PSEQ                    pSeq,
869     TICKS                   tkOffset)
870 {
871     return smfTicksToMillisecs(pSeq->hSmf, tkOffset);
872 }
873
874 /***************************************************************************
875 *  
876 * seqMIDICallback
877 *
878 * Called by the system when a buffer is done
879 *
880 * dw1                       - The buffer that has completed playback.
881 *
882 ***************************************************************************/
883
884 PRIVATE void FAR PASCAL seqMIDICallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
885 {
886         LPMIDIHDR                                       lpmh            = (LPMIDIHDR)dw1;
887     PSEQ                    pSeq;
888     MMRESULT                mmrc;
889     SMFRESULT               smfrc;
890         //DWORD vol;
891
892         if (uMsg != MOM_DONE)
893                 return;
894
895         Assert(NULL != lpmh);
896
897     pSeq = (PSEQ)(lpmh->dwUser);
898
899     Assert(pSeq != NULL);
900
901     --pSeq->uBuffersInMMSYSTEM;
902     
903         if (pSeq->uState == SEQ_S_NOFILE) 
904                 mprintf((1, "seqCallback: No file!!\n"));
905
906     if (SEQ_S_RESET == pSeq->uState)
907     {
908         // We're recollecting buffers from MMSYSTEM
909         //
910                 if (lpmh != pSeq->lpmhPreroll)
911                 {
912                 lpmh->lpNext   = pSeq->lpmhFree;
913                 pSeq->lpmhFree = lpmh;
914                 }
915
916         return;
917     }
918     
919
920     if ((SEQ_S_STOPPING == pSeq->uState) || (pSeq->fdwSeq & SEQ_F_EOF))
921     {
922         /*
923         ** Reached EOF, just put the buffer back on the free
924         ** list 
925         */
926                 if (lpmh != pSeq->lpmhPreroll)
927                 {
928                 lpmh->lpNext   = pSeq->lpmhFree;
929                 pSeq->lpmhFree = lpmh;
930                 }
931                         if (!pSeq->hmidi) 
932                                 mprintf((1, "seqCallback: NULL MIDI HANDLE.\n"));
933
934         if (MMSYSERR_NOERROR != (mmrc = midiOutUnprepareHeader(pSeq->hmidi, lpmh, sizeof(*lpmh))))
935         {
936             mprintf((1, "midiOutUnprepareHeader failed in seqBufferDone! (%lu)\n", (DWORD)mmrc));
937         }
938
939         if (0 == pSeq->uBuffersInMMSYSTEM)
940         {
941                                 int stop_loop=0;
942             mprintf((1, "seqBufferDone: normal sequencer shutdown.\n"));
943             
944             /* Totally done! Free device and notify.
945             */
946             midiStreamClose(pSeq->hmidi);
947             
948                                 if ((SEQ_S_STOPPING == pSeq->uState) && (pSeq->fdwSeq & SEQ_F_EOF) && LoopSong) {
949                                         stop_loop = 1;
950                                         mprintf((1, "seqMIDICallback: cancelling loop.\n"));
951                                 }
952             pSeq->hmidi = NULL;
953             pSeq->uState = SEQ_S_OPENED;
954             pSeq->mmrcLastErr = MMSYSERR_NOERROR;
955             pSeq->fdwSeq &= ~SEQ_F_WAITING;
956
957                                 if ((pSeq->fdwSeq & SEQ_F_EOF) && LoopSong && !stop_loop)
958                                         wmidi_play(); 
959         }
960     }
961     else
962     {
963         /*
964         ** Not EOF yet; attempt to fill another buffer
965         */
966                   DWORD vol;
967
968         smfrc = smfReadEvents(pSeq->hSmf, lpmh, pSeq->tkEnd);
969         
970         switch(smfrc)
971         {
972             case SMF_SUCCESS:
973                 break;
974
975             case SMF_END_OF_FILE:
976                 pSeq->fdwSeq |= SEQ_F_EOF;
977                 smfrc = SMF_SUCCESS;
978                 break;
979
980             default:
981                 mprintf((1, "smfReadEvents returned %lu in callback!\n", (DWORD)smfrc));
982                 pSeq->uState = SEQ_S_STOPPING;
983                 break;
984         }
985
986         if (SMF_SUCCESS == smfrc)
987         {
988             ++pSeq->uBuffersInMMSYSTEM;
989
990                                 Assert(pSeq->hmidi != NULL);
991
992 //                              wmidi_deamp_song(wmidi_volume);
993                                 mmrc = midiOutSetVolume((HMIDIOUT)MIDI_MAPPER, wmidi_volume);   
994                                 if (mmrc != MMSYSERR_NOERROR) mprintf((1, "MIDI VOL ERR: %d\n", mmrc));
995                                 midiOutGetVolume((HMIDIOUT)MIDI_MAPPER, &vol);
996
997             mmrc = midiStreamOut(pSeq->hmidi, lpmh, sizeof(*lpmh));
998                                 WMidi_NewStream++;
999             if (MMSYSERR_NOERROR != mmrc)
1000             {
1001                 mprintf((1, "seqBufferDone(): midiStreamOut() returned %lu!\n", (DWORD)mmrc));
1002                 
1003                 --pSeq->uBuffersInMMSYSTEM;
1004                 pSeq->uState = SEQ_S_STOPPING;
1005          --pSeq->uBuffersInMMSYSTEM;
1006                 pSeq->uState = SEQ_S_STOPPING;
1007             }
1008         }
1009     }
1010
1011 }
1012
1013 /***************************************************************************
1014 *  
1015 * XlatSMFErr
1016 *
1017 * Translates an error from the SMF layer into an appropriate MCI error.
1018 *
1019 ***************************************************************************/
1020 PRIVATE MMRESULT FNLOCAL XlatSMFErr(
1021     SMFRESULT               smfrc)
1022 {
1023     switch(smfrc)
1024     {
1025         case SMF_SUCCESS:
1026             return MMSYSERR_NOERROR;
1027
1028         case SMF_NO_MEMORY:
1029             return MCIERR_OUT_OF_MEMORY;
1030
1031         case SMF_INVALID_FILE:
1032         case SMF_OPEN_FAILED:
1033         case SMF_INVALID_TRACK:
1034             return MCIERR_INVALID_FILE;
1035
1036         default:
1037             return MCIERR_UNSUPPORTED_FUNCTION;
1038     }
1039 }
1040
1041
1042
1043
1044
1045