2 * MIDI driver for OSS (unixlib)
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
6 * Copyright 1998, 1999 Eric POUECH
7 * Copyright 2022 Huw Davies
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
38 #include <sys/types.h>
41 #include <sys/ioctl.h>
42 #include <sys/soundcard.h>
46 #define WIN32_NO_STATUS
48 #include "audioclient.h"
51 #include "wine/debug.h"
52 #include "wine/unixlib.h"
59 MIDIOPENDESC midiDesc
;
63 void *lpExtra
; /* according to port type (MIDI, FM...), extra data when needed */
70 int state
; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
71 MIDIOPENDESC midiDesc
;
74 unsigned char incoming
[3];
75 unsigned char incPrev
;
82 static pthread_mutex_t in_buffer_mutex
= PTHREAD_MUTEX_INITIALIZER
;
84 static unsigned int num_dests
, num_srcs
, num_synths
, seq_refs
;
85 static struct midi_dest dests
[MAX_MIDIOUTDRV
];
86 static struct midi_src srcs
[MAX_MIDIINDRV
];
87 static int load_count
;
89 static unsigned int num_midi_in_started
;
90 static int rec_cancel_pipe
[2];
91 static pthread_t rec_thread_id
;
93 static pthread_mutex_t notify_mutex
= PTHREAD_MUTEX_INITIALIZER
;
94 static pthread_cond_t notify_read_cond
= PTHREAD_COND_INITIALIZER
;
95 static pthread_cond_t notify_write_cond
= PTHREAD_COND_INITIALIZER
;
96 static BOOL notify_quit
;
97 #define NOTIFY_BUFFER_SIZE 64 + 1 /* + 1 for the sentinel */
98 static struct notify_context notify_buffer
[NOTIFY_BUFFER_SIZE
];
99 static struct notify_context
*notify_read
= notify_buffer
, *notify_write
= notify_buffer
;
101 typedef struct sVoice
103 int note
; /* 0 means not used */
105 unsigned cntMark
: 30,
108 #define sVS_PLAYING 1
109 #define sVS_SUSTAINED 2
112 typedef struct sChannel
119 int bank
; /* CTL_BANK_SELECT */
120 int volume
; /* CTL_MAIN_VOLUME */
121 int balance
; /* CTL_BALANCE */
122 int expression
; /* CTL_EXPRESSION */
123 int sustain
; /* CTL_SUSTAIN */
125 unsigned char nrgPmtMSB
; /* Non register Parameters */
126 unsigned char nrgPmtLSB
;
127 unsigned char regPmtMSB
; /* Non register Parameters */
128 unsigned char regPmtLSB
;
131 typedef struct sFMextra
135 sChannel channel
[16]; /* MIDI has only 16 channels */
136 sVoice voice
[1]; /* dyn allocated according to sound card */
137 /* do not append fields below voice[1] since the size of this structure
138 * depends on the number of available voices on the FM synth...
142 #define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn)))
144 WINE_DEFAULT_DEBUG_CHANNEL(midi
);
146 static int oss_to_win_device_type(int type
)
148 /* MOD_MIDIPORT output port
149 * MOD_SYNTH generic internal synth
150 * MOD_SQSYNTH square wave internal synth
151 * MOD_FMSYNTH FM internal synth
152 * MOD_MAPPER MIDI mapper
153 * MOD_WAVETABLE hardware wavetable internal synth
154 * MOD_SWSYNTH software internal synth
157 /* FIXME Is this really the correct equivalence from UNIX to
158 Windows Sound type */
162 case SYNTH_TYPE_FM
: return MOD_FMSYNTH
;
163 case SYNTH_TYPE_SAMPLE
: return MOD_SYNTH
;
164 case SYNTH_TYPE_MIDI
: return MOD_MIDIPORT
;
166 ERR("Cannot determine the type of this midi device. "
167 "Assuming FM Synth\n");
172 static void in_buffer_lock(void)
174 pthread_mutex_lock(&in_buffer_mutex
);
177 static void in_buffer_unlock(void)
179 pthread_mutex_unlock(&in_buffer_mutex
);
182 static uint64_t get_time_msec(void)
184 struct timespec now
= {0, 0};
186 #ifdef CLOCK_MONOTONIC_RAW
187 if (!clock_gettime(CLOCK_MONOTONIC_RAW
, &now
))
188 return (uint64_t)now
.tv_sec
* 1000 + now
.tv_nsec
/ 1000000;
190 clock_gettime(CLOCK_MONOTONIC
, &now
);
191 return (uint64_t)now
.tv_sec
* 1000 + now
.tv_nsec
/ 1000000;
195 * notify buffer: The notification ring buffer is implemented so that
196 * there is always at least one unused sentinel before the current
197 * read position in order to allow detection of the full vs empty
200 static struct notify_context
*notify_buffer_next(struct notify_context
*notify
)
202 if (++notify
>= notify_buffer
+ ARRAY_SIZE(notify_buffer
))
203 notify
= notify_buffer
;
208 static BOOL
notify_buffer_empty(void)
210 return notify_read
== notify_write
;
213 static BOOL
notify_buffer_full(void)
215 return notify_buffer_next(notify_write
) == notify_read
;
218 static BOOL
notify_buffer_add(struct notify_context
*notify
)
220 if (notify_buffer_full()) return FALSE
;
222 *notify_write
= *notify
;
223 notify_write
= notify_buffer_next(notify_write
);
227 static BOOL
notify_buffer_remove(struct notify_context
*notify
)
229 if (notify_buffer_empty()) return FALSE
;
231 *notify
= *notify_read
;
232 notify_read
= notify_buffer_next(notify_read
);
236 static void notify_post(struct notify_context
*notify
)
238 pthread_mutex_lock(¬ify_mutex
);
242 while (notify_buffer_full())
243 pthread_cond_wait(¬ify_write_cond
, ¬ify_mutex
);
245 notify_buffer_add(notify
);
247 else notify_quit
= TRUE
;
248 pthread_cond_signal(¬ify_read_cond
);
250 pthread_mutex_unlock(¬ify_mutex
);
253 static void set_in_notify(struct notify_context
*notify
, struct midi_src
*src
, WORD dev_id
, WORD msg
,
254 UINT_PTR param_1
, UINT_PTR param_2
)
256 notify
->send_notify
= TRUE
;
257 notify
->dev_id
= dev_id
;
259 notify
->param_1
= param_1
;
260 notify
->param_2
= param_2
;
261 notify
->callback
= src
->midiDesc
.dwCallback
;
262 notify
->flags
= src
->wFlags
;
263 notify
->device
= src
->midiDesc
.hMidi
;
264 notify
->instance
= src
->midiDesc
.dwInstance
;
267 static int seq_open(void)
269 static int midi_warn
= 1;
274 const char* device
= getenv("MIDIDEV");
276 if (!device
) device
= "/dev/sequencer";
277 fd
= open(device
, O_RDWR
, 0);
282 WARN("Can't open MIDI device '%s' ! (%s). If your program needs this (probably not): %s\n",
283 device
, strerror(errno
),
284 errno
== ENOENT
? "create it ! (\"man MAKEDEV\" ?)" :
285 errno
== ENODEV
? "load MIDI sequencer kernel driver !" :
286 errno
== EACCES
? "grant access ! (\"man chmod\")" : "");
291 fcntl(fd
, F_SETFD
, 1); /* set close on exec flag */
292 ioctl(fd
, SNDCTL_SEQ_RESET
);
298 static int seq_close(int fd
)
306 static UINT
oss_midi_init(void)
308 int i
, status
, synth_devs
= 255, midi_devs
= 255, fd
, len
;
309 struct synth_info sinfo
;
310 struct midi_info minfo
;
311 struct midi_dest
*dest
;
312 struct midi_src
*src
;
314 TRACE("(%i)\n", load_count
);
319 /* try to open device */
324 /* find how many Synth devices are there in the system */
325 status
= ioctl(fd
, SNDCTL_SEQ_NRSYNTHS
, &synth_devs
);
328 ERR("ioctl for nr synth failed.\n");
333 if (synth_devs
> MAX_MIDIOUTDRV
)
335 ERR("MAX_MIDIOUTDRV (%d) was enough for the number of devices (%d). "
336 "Some FM devices will not be available.\n", MAX_MIDIOUTDRV
, synth_devs
);
337 synth_devs
= MAX_MIDIOUTDRV
;
340 for (i
= 0, dest
= dests
; i
< synth_devs
; i
++, dest
++)
342 /* Manufac ID. We do not have access to this with soundcard.h
343 * Does not seem to be a problem, because in mmsystem.h only
344 * Microsoft's ID is listed.
346 dest
->caps
.wMid
= 0x00FF;
347 dest
->caps
.wPid
= 0x0001; /* FIXME Product ID */
348 /* Product Version. We simply say "1" */
349 dest
->caps
.vDriverVersion
= 0x001;
350 /* The following are mandatory for MOD_MIDIPORT */
351 dest
->caps
.wChannelMask
= 0xFFFF;
352 dest
->caps
.wVoices
= 0;
353 dest
->caps
.wNotes
= 0;
354 dest
->caps
.dwSupport
= 0;
357 status
= ioctl(fd
, SNDCTL_SYNTH_INFO
, &sinfo
);
362 ERR("ioctl for synth info failed on %d, disabling it.\n", i
);
364 sprintf(buf
, "Wine OSS Midi Out #%d disabled", i
);
365 len
= ntdll_umbstowcs(buf
, strlen(buf
) + 1, dest
->caps
.szPname
, ARRAY_SIZE(dest
->caps
.szPname
));
366 dest
->caps
.szPname
[len
- 1] = '\0';
367 dest
->caps
.wTechnology
= MOD_MIDIPORT
;
368 dest
->bEnabled
= FALSE
;
372 len
= ntdll_umbstowcs(sinfo
.name
, strlen(sinfo
.name
) + 1, dest
->caps
.szPname
, ARRAY_SIZE(dest
->caps
.szPname
));
373 dest
->caps
.szPname
[len
- 1] = '\0';
374 dest
->caps
.wTechnology
= oss_to_win_device_type(sinfo
.synth_type
);
376 if (dest
->caps
.wTechnology
!= MOD_MIDIPORT
)
378 /* FIXME Do we have this information?
379 * Assuming the soundcards can handle
380 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
381 * not MIDICAPS_CACHE.
383 dest
->caps
.dwSupport
= MIDICAPS_VOLUME
| MIDICAPS_LRVOLUME
;
384 dest
->caps
.wVoices
= sinfo
.nr_voices
;
386 /* FIXME Is it possible to know the maximum
387 * number of simultaneous notes of a soundcard ?
388 * I believe we don't have this information, but
389 * it's probably equal or more than wVoices
391 dest
->caps
.wNotes
= sinfo
.nr_voices
;
393 dest
->bEnabled
= TRUE
;
395 /* We also have the information sinfo.synth_subtype, not used here
397 if (sinfo
.capabilities
& SYNTH_CAP_INPUT
)
398 FIXME("Synthesizer supports MIDI in. Not yet supported.\n");
400 TRACE("SynthOut[%d]\tOSS info: synth type=%d/%d capa=%x\n",
401 i
, sinfo
.synth_type
, sinfo
.synth_subtype
, (unsigned)sinfo
.capabilities
);
404 TRACE("SynthOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n",
405 i
, wine_dbgstr_w(dest
->caps
.szPname
), dest
->caps
.wTechnology
,
406 dest
->caps
.wVoices
, dest
->caps
.wNotes
, dest
->caps
.wChannelMask
,
407 (unsigned)dest
->caps
.dwSupport
);
410 /* find how many MIDI devices are there in the system */
411 status
= ioctl(fd
, SNDCTL_SEQ_NRMIDIS
, &midi_devs
);
414 ERR("ioctl on nr midi failed.\n");
419 /* FIXME: the two restrictions below could be loosened in some cases */
420 if (synth_devs
+ midi_devs
> MAX_MIDIOUTDRV
)
422 ERR("MAX_MIDIOUTDRV was not enough for the number of devices. "
423 "Some MIDI devices will not be available.\n");
424 midi_devs
= MAX_MIDIOUTDRV
- synth_devs
;
427 if (midi_devs
> MAX_MIDIINDRV
)
429 ERR("MAX_MIDIINDRV (%d) was not enough for the number of devices (%d). "
430 "Some MIDI devices will not be available.\n", MAX_MIDIINDRV
, midi_devs
);
431 midi_devs
= MAX_MIDIINDRV
;
434 dest
= dests
+ synth_devs
;
436 for (i
= 0; i
< midi_devs
; i
++, dest
++, src
++)
439 status
= ioctl(fd
, SNDCTL_MIDI_INFO
, &minfo
);
440 if (status
== -1) WARN("ioctl on midi info for device %d failed.\n", i
);
442 /* Manufacturer ID. We do not have access to this with soundcard.h
443 Does not seem to be a problem, because in mmsystem.h only Microsoft's ID is listed
445 dest
->caps
.wMid
= 0x00FF;
446 dest
->caps
.wPid
= 0x0001; /* FIXME Product ID */
447 /* Product Version. We simply say "1" */
448 dest
->caps
.vDriverVersion
= 0x001;
453 sprintf(buf
, "Wine OSS Midi Out #%d disabled", synth_devs
+ i
);
454 len
= ntdll_umbstowcs(buf
, strlen(buf
) + 1, dest
->caps
.szPname
, ARRAY_SIZE(dest
->caps
.szPname
));
455 dest
->caps
.szPname
[len
- 1] = '\0';
456 dest
->bEnabled
= FALSE
;
460 len
= ntdll_umbstowcs(minfo
.name
, strlen(minfo
.name
) + 1, dest
->caps
.szPname
, ARRAY_SIZE(dest
->caps
.szPname
));
461 dest
->caps
.szPname
[len
- 1] = '\0';
462 dest
->bEnabled
= TRUE
;
464 dest
->caps
.wTechnology
= MOD_MIDIPORT
;
465 dest
->caps
.wVoices
= 0;
466 dest
->caps
.wNotes
= 0;
467 dest
->caps
.wChannelMask
= 0xFFFF;
468 dest
->caps
.dwSupport
= 0;
470 /* Manufac ID. We do not have access to this with soundcard.h
471 Does not seem to be a problem, because in mmsystem.h only
472 Microsoft's ID is listed */
473 src
->caps
.wMid
= 0x00FF;
474 src
->caps
.wPid
= 0x0001; /* FIXME Product ID */
475 /* Product Version. We simply say "1" */
476 src
->caps
.vDriverVersion
= 0x001;
479 char buf
[ARRAY_SIZE(dest
->caps
.szPname
)];
481 sprintf(buf
, "Wine OSS Midi In #%d disabled", synth_devs
+ i
);
482 len
= ntdll_umbstowcs(buf
, strlen(buf
) + 1, src
->caps
.szPname
, ARRAY_SIZE(src
->caps
.szPname
));
483 src
->caps
.szPname
[len
- 1] = '\0';
488 len
= ntdll_umbstowcs(minfo
.name
, strlen(minfo
.name
) + 1, src
->caps
.szPname
, ARRAY_SIZE(src
->caps
.szPname
));
489 src
->caps
.szPname
[len
- 1] = '\0';
492 src
->caps
.dwSupport
= 0; /* mandatory with MIDIINCAPS */
494 TRACE("OSS info: midi[%d] dev-type=%d capa=%x\n"
495 "\tMidiOut[%d] name='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
496 "\tMidiIn [%d] name='%s' support=%d\n",
497 i
, minfo
.dev_type
, (unsigned)minfo
.capabilities
,
498 synth_devs
+ i
, wine_dbgstr_w(dest
->caps
.szPname
), dest
->caps
.wTechnology
,
499 dest
->caps
.wVoices
, dest
->caps
.wNotes
, dest
->caps
.wChannelMask
, (unsigned)dest
->caps
.dwSupport
,
500 i
, wine_dbgstr_w(src
->caps
.szPname
), (unsigned)src
->caps
.dwSupport
);
504 /* windows does not seem to differentiate Synth from MIDI devices */
505 num_synths
= synth_devs
;
506 num_dests
= synth_devs
+ midi_devs
;
507 num_srcs
= midi_devs
;
509 /* close file and exit */
515 static UINT
midi_exit(void)
517 TRACE("(%i)\n", load_count
);
525 NTSTATUS
oss_midi_release(void *args
)
527 /* stop the notify_wait thread */
530 return STATUS_SUCCESS
;
533 /* FIXME: this is a bad idea, it's even not static... */
536 /* FIXME: this is not reentrant, not static - because of global variable
539 /**************************************************************************
540 * seqbuf_dump [internal]
542 * Used by SEQ_DUMPBUF to flush the buffer.
545 void seqbuf_dump(void)
549 /* The device is already open, but there's no way to pass the
550 fd to this function. Rather than rely on a global variable
551 we pretend to open the seq again. */
555 if (write(fd
, _seqbuf
, _seqbufptr
) == -1)
557 WARN("Can't write data to sequencer %d, errno %d (%s)!\n",
558 fd
, errno
, strerror(errno
));
560 /* FIXME: In any case buffer is lost so that if many errors occur the buffer
561 * will not overrun */
567 extern const unsigned char midiFMInstrumentPatches
[16 * 128];
568 extern const unsigned char midiFMDrumsPatches
[16 * 128];
570 static int midi_out_fm_load(WORD dev_id
, int fd
)
572 struct sbi_instrument sbi
;
578 memset(sbi
.operators
+ 16, 0, 16);
579 for (i
= 0; i
< 128; i
++)
582 memcpy(sbi
.operators
, midiFMInstrumentPatches
+ i
* 16, 16);
584 if (write(fd
, &sbi
, sizeof(sbi
)) == -1)
586 WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi
.channel
, errno
, strerror(errno
));
590 for (i
= 0; i
< 128; i
++)
592 sbi
.channel
= 128 + i
;
593 memcpy(sbi
.operators
, midiFMDrumsPatches
+ i
* 16, 16);
595 if (write(fd
, &sbi
, sizeof(sbi
)) == -1)
597 WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi
.channel
, errno
, strerror(errno
));
604 static void midi_out_fm_reset(WORD dev_id
)
606 struct midi_dest
*dest
= dests
+ dev_id
;
607 sFMextra
*extra
= dest
->lpExtra
;
608 sVoice
*voice
= extra
->voice
;
609 sChannel
*channel
= extra
->channel
;
612 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
614 if (voice
[i
].status
!= sVS_UNUSED
)
615 SEQ_STOP_NOTE(dev_id
, i
, voice
[i
].note
, 64);
616 SEQ_KEY_PRESSURE(dev_id
, i
, 127, 0);
617 SEQ_CONTROL(dev_id
, i
, SEQ_VOLMODE
, VOL_METHOD_LINEAR
);
619 voice
[i
].channel
= -1;
620 voice
[i
].cntMark
= 0;
621 voice
[i
].status
= sVS_UNUSED
;
623 for (i
= 0; i
< 16; i
++)
625 channel
[i
].program
= 0;
626 channel
[i
].bender
= 8192;
627 channel
[i
].benderRange
= 2;
629 channel
[i
].volume
= 127;
630 channel
[i
].balance
= 64;
631 channel
[i
].expression
= 0;
632 channel
[i
].sustain
= 0;
635 extra
->drumSetMask
= 1 << 9; /* channel 10 is normally drums, sometimes 16 also */
639 static void set_out_notify(struct notify_context
*notify
, struct midi_dest
*dest
, WORD dev_id
, WORD msg
,
640 UINT_PTR param_1
, UINT_PTR param_2
)
642 notify
->send_notify
= TRUE
;
643 notify
->dev_id
= dev_id
;
645 notify
->param_1
= param_1
;
646 notify
->param_2
= param_2
;
647 notify
->callback
= dest
->midiDesc
.dwCallback
;
648 notify
->flags
= dest
->wFlags
;
649 notify
->device
= dest
->midiDesc
.hMidi
;
650 notify
->instance
= dest
->midiDesc
.dwInstance
;
653 static UINT
midi_out_open(WORD dev_id
, MIDIOPENDESC
*midi_desc
, UINT flags
, struct notify_context
*notify
)
655 struct midi_dest
*dest
;
658 TRACE("(%04X, %p, %08X);\n", dev_id
, midi_desc
, flags
);
659 if (midi_desc
== NULL
)
661 WARN("Invalid Parameter !\n");
662 return MMSYSERR_INVALPARAM
;
664 if (dev_id
>= num_dests
)
666 TRACE("MAX_MIDIOUTDRV reached !\n");
667 return MMSYSERR_BADDEVICEID
;
669 dest
= dests
+ dev_id
;
670 if (dest
->midiDesc
.hMidi
!= 0)
672 WARN("device already open !\n");
673 return MMSYSERR_ALLOCATED
;
677 WARN("device disabled !\n");
678 return MIDIERR_NODEVICE
;
680 if ((flags
& ~CALLBACK_TYPEMASK
) != 0)
683 return MMSYSERR_INVALFLAG
;
686 dest
->lpExtra
= NULL
;
688 switch (dest
->caps
.wTechnology
)
694 extra
= malloc(offsetof(struct sFMextra
, voice
[dest
->caps
.wVoices
]));
697 WARN("can't alloc extra data !\n");
698 return MMSYSERR_NOMEM
;
700 dest
->lpExtra
= extra
;
704 dest
->lpExtra
= NULL
;
706 return MMSYSERR_ERROR
;
708 if (midi_out_fm_load(dev_id
, fd
) < 0)
711 dest
->lpExtra
= NULL
;
713 return MMSYSERR_ERROR
;
715 midi_out_fm_reset(dev_id
);
722 return MMSYSERR_ALLOCATED
;
725 WARN("Technology not supported (yet) %d !\n", dest
->caps
.wTechnology
);
726 return MMSYSERR_NOTENABLED
;
729 dest
->runningStatus
= 0;
730 dest
->wFlags
= HIWORD(flags
& CALLBACK_TYPEMASK
);
732 dest
->lpQueueHdr
= NULL
;
733 dest
->midiDesc
= *midi_desc
;
736 set_out_notify(notify
, dest
, dev_id
, MOM_OPEN
, 0, 0);
737 TRACE("Successful !\n");
738 return MMSYSERR_NOERROR
;
741 static UINT
midi_out_close(WORD dev_id
, struct notify_context
*notify
)
743 struct midi_dest
*dest
;
745 TRACE("(%04X);\n", dev_id
);
747 if (dev_id
>= num_dests
)
749 TRACE("MAX_MIDIOUTDRV reached !\n");
750 return MMSYSERR_BADDEVICEID
;
752 dest
= dests
+ dev_id
;
754 if (dest
->midiDesc
.hMidi
== 0)
756 WARN("device not opened !\n");
757 return MMSYSERR_ERROR
;
759 /* FIXME: should test that no pending buffer is still in the queue for
764 WARN("can't close !\n");
765 return MMSYSERR_ERROR
;
768 switch (dest
->caps
.wTechnology
)
776 WARN("Technology not supported (yet) %d !\n", dest
->caps
.wTechnology
);
777 return MMSYSERR_NOTENABLED
;
781 dest
->lpExtra
= NULL
;
784 set_out_notify(notify
, dest
, dev_id
, MOM_CLOSE
, 0, 0);
785 dest
->midiDesc
.hMidi
= 0;
786 return MMSYSERR_NOERROR
;
789 static UINT
midi_out_fm_data(WORD dev_id
, UINT data
)
791 struct midi_dest
*dest
= dests
+ dev_id
;
792 BYTE evt
= LOBYTE(LOWORD(data
)), d1
, d2
;
793 sFMextra
*extra
= dest
->lpExtra
;
794 sVoice
*voice
= extra
->voice
;
795 sChannel
*channel
= extra
->channel
;
796 int chn
= (evt
& 0x0F), i
, nv
;
800 d1
= HIBYTE(LOWORD(data
));
801 d2
= LOBYTE(HIWORD(data
));
803 dest
->runningStatus
= evt
;
805 else if (dest
->runningStatus
)
807 evt
= dest
->runningStatus
;
808 d1
= LOBYTE(LOWORD(data
));
809 d2
= HIBYTE(LOWORD(data
));
813 FIXME("ooch %x\n", data
);
814 return MMSYSERR_NOERROR
;
817 /* FIXME: chorus depth controller is not used */
822 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
824 /* don't stop sustained notes */
825 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
&& voice
[i
].note
== d1
)
827 voice
[i
].status
= sVS_UNUSED
;
828 SEQ_STOP_NOTE(dev_id
, i
, d1
, d2
);
833 if (d2
== 0) /* note off if velocity == 0 */
835 for (i
= 0; i
< dest
->caps
.wVoices
; i
++) /* don't stop sustained notes */
837 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
&& voice
[i
].note
== d1
)
839 voice
[i
].status
= sVS_UNUSED
;
840 SEQ_STOP_NOTE(dev_id
, i
, d1
, 64);
845 /* finding out in this order :
847 * - if replaying the same note on the same channel
848 * - the older voice (LRU)
850 for (i
= nv
= 0; i
< dest
->caps
.wVoices
; i
++)
852 if (voice
[i
].status
== sVS_UNUSED
|| (voice
[i
].note
== d1
&& voice
[i
].channel
== chn
))
857 if (voice
[i
].cntMark
< voice
[0].cntMark
)
860 TRACE("playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, bender=0x%02X, note=0x%02X, vel=0x%02X\n",
861 nv
, channel
[chn
].program
, channel
[chn
].balance
, channel
[chn
].volume
, channel
[chn
].bender
, d1
, d2
);
863 SEQ_SET_PATCH(dev_id
, nv
, IS_DRUM_CHANNEL(extra
, chn
) ?
864 (128 + d1
) : channel
[chn
].program
);
865 SEQ_BENDER_RANGE(dev_id
, nv
, channel
[chn
].benderRange
* 100);
866 SEQ_BENDER(dev_id
, nv
, channel
[chn
].bender
);
867 SEQ_CONTROL(dev_id
, nv
, CTL_PAN
, channel
[chn
].balance
);
868 SEQ_CONTROL(dev_id
, nv
, CTL_EXPRESSION
, channel
[chn
].expression
);
869 SEQ_START_NOTE(dev_id
, nv
, d1
, d2
);
870 voice
[nv
].status
= channel
[chn
].sustain
? sVS_SUSTAINED
: sVS_PLAYING
;
872 voice
[nv
].channel
= chn
;
873 voice
[nv
].cntMark
= extra
->counter
++;
875 case MIDI_KEY_PRESSURE
:
876 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
877 if (voice
[i
].status
!= sVS_UNUSED
&& voice
[i
].channel
== chn
&& voice
[i
].note
== d1
)
878 SEQ_KEY_PRESSURE(dev_id
, i
, d1
, d2
);
880 case MIDI_CTL_CHANGE
:
883 case CTL_BANK_SELECT
: channel
[chn
].bank
= d2
; break;
884 case CTL_MAIN_VOLUME
: channel
[chn
].volume
= d2
; break;
885 case CTL_PAN
: channel
[chn
].balance
= d2
; break;
886 case CTL_EXPRESSION
: channel
[chn
].expression
= d2
; break;
887 case CTL_SUSTAIN
: channel
[chn
].sustain
= d2
;
890 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
891 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
)
892 voice
[i
].status
= sVS_SUSTAINED
;
896 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
898 if (voice
[i
].status
== sVS_SUSTAINED
&& voice
[i
].channel
== chn
)
900 voice
[i
].status
= sVS_UNUSED
;
901 SEQ_STOP_NOTE(dev_id
, i
, voice
[i
].note
, 64);
906 case CTL_NONREG_PARM_NUM_LSB
: channel
[chn
].nrgPmtLSB
= d2
; break;
907 case CTL_NONREG_PARM_NUM_MSB
: channel
[chn
].nrgPmtMSB
= d2
; break;
908 case CTL_REGIST_PARM_NUM_LSB
: channel
[chn
].regPmtLSB
= d2
; break;
909 case CTL_REGIST_PARM_NUM_MSB
: channel
[chn
].regPmtMSB
= d2
; break;
911 switch ((channel
[chn
].regPmtMSB
<< 8) | channel
[chn
].regPmtLSB
)
914 if (channel
[chn
].benderRange
!= d2
)
916 channel
[chn
].benderRange
= d2
;
917 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
918 if (voice
[i
].channel
== chn
)
919 SEQ_BENDER_RANGE(dev_id
, i
, channel
[chn
].benderRange
);
924 channel
[chn
].benderRange
= 2;
925 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
926 if (voice
[i
].channel
== chn
)
927 SEQ_BENDER_RANGE(dev_id
, i
, channel
[chn
].benderRange
);
930 TRACE("Data entry: regPmt=0x%02x%02x, nrgPmt=0x%02x%02x with %x\n",
931 channel
[chn
].regPmtMSB
, channel
[chn
].regPmtLSB
,
932 channel
[chn
].nrgPmtMSB
, channel
[chn
].nrgPmtLSB
, d2
);
937 case 0x78: /* all sounds off */
938 /* FIXME: I don't know if I have to take care of the channel for this control? */
939 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
941 if (voice
[i
].status
!= sVS_UNUSED
&& voice
[i
].channel
== chn
)
943 voice
[i
].status
= sVS_UNUSED
;
944 SEQ_STOP_NOTE(dev_id
, i
, voice
[i
].note
, 64);
948 case 0x7B: /* all notes off */
949 /* FIXME: I don't know if I have to take care of the channel for this control? */
950 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
952 if (voice
[i
].status
== sVS_PLAYING
&& voice
[i
].channel
== chn
)
954 voice
[i
].status
= sVS_UNUSED
;
955 SEQ_STOP_NOTE(dev_id
, i
, voice
[i
].note
, 64);
960 TRACE("Dropping MIDI control event 0x%02x(%02x) on channel %d\n", d1
, d2
, chn
);
964 case MIDI_PGM_CHANGE
:
965 channel
[chn
].program
= d1
;
967 case MIDI_CHN_PRESSURE
:
968 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
969 if (voice
[i
].status
!= sVS_UNUSED
&& voice
[i
].channel
== chn
)
970 SEQ_KEY_PRESSURE(dev_id
, i
, voice
[i
].note
, d1
);
973 case MIDI_PITCH_BEND
:
974 channel
[chn
].bender
= (d2
<< 7) + d1
;
975 for (i
= 0; i
< dest
->caps
.wVoices
; i
++)
976 if (voice
[i
].channel
== chn
)
977 SEQ_BENDER(dev_id
, i
, channel
[chn
].bender
);
979 case MIDI_SYSTEM_PREFIX
:
982 case 0x0F: /* Reset */
983 midi_out_fm_reset(dev_id
);
984 dest
->runningStatus
= 0;
987 WARN("Unsupported (yet) system event %02x\n", evt
& 0x0F);
990 dest
->runningStatus
= 0;
993 WARN("Internal error, shouldn't happen (event=%08x)\n", evt
& 0xF0);
994 return MMSYSERR_NOTENABLED
;
998 return MMSYSERR_NOERROR
;
1001 static UINT
midi_out_port_data(WORD dev_id
, UINT data
)
1003 struct midi_dest
*dest
= dests
+ dev_id
;
1004 BYTE evt
= LOBYTE(LOWORD(data
)), d1
, d2
;
1005 int dev
= dev_id
- num_synths
;
1009 WARN("Internal error on devID (%u) !\n", dev_id
);
1010 return MIDIERR_NODEVICE
;
1015 d1
= HIBYTE(LOWORD(data
));
1016 d2
= LOBYTE(HIWORD(data
));
1018 else if (dest
->runningStatus
)
1020 evt
= dest
->runningStatus
;
1021 d1
= LOBYTE(LOWORD(data
));
1022 d2
= HIBYTE(LOWORD(data
));
1026 FIXME("ooch %x\n", data
);
1027 return MMSYSERR_NOERROR
;
1034 case MIDI_KEY_PRESSURE
:
1035 case MIDI_CTL_CHANGE
:
1036 case MIDI_PITCH_BEND
:
1037 if (LOBYTE(LOWORD(data
)) >= 0x80)
1039 SEQ_MIDIOUT(dev
, evt
);
1040 dest
->runningStatus
= evt
;
1042 SEQ_MIDIOUT(dev
, d1
);
1043 SEQ_MIDIOUT(dev
, d2
);
1045 case MIDI_PGM_CHANGE
:
1046 case MIDI_CHN_PRESSURE
:
1047 if (LOBYTE(LOWORD(data
)) >= 0x80)
1049 SEQ_MIDIOUT(dev
, evt
);
1050 dest
->runningStatus
= evt
;
1052 SEQ_MIDIOUT(dev
, d1
);
1054 case MIDI_SYSTEM_PREFIX
:
1057 case 0x00: /* System Exclusive, don't do it on MODM_DATA, should require MODM_LONGDATA */
1058 case 0x04: /* Undefined. */
1059 case 0x05: /* Undefined. */
1060 case 0x07: /* End of Exclusive. */
1061 case 0x09: /* Undefined. */
1062 case 0x0D: /* Undefined. */
1064 case 0x06: /* Tune Request */
1065 case 0x08: /* Timing Clock. */
1066 case 0x0A: /* Start. */
1067 case 0x0B: /* Continue */
1068 case 0x0C: /* Stop */
1069 case 0x0E: /* Active Sensing. */
1070 SEQ_MIDIOUT(dev
, evt
);
1072 case 0x0F: /* Reset */
1073 SEQ_MIDIOUT(dev
, MIDI_SYSTEM_PREFIX
);
1074 SEQ_MIDIOUT(dev
, 0x7e);
1075 SEQ_MIDIOUT(dev
, 0x7f);
1076 SEQ_MIDIOUT(dev
, 0x09);
1077 SEQ_MIDIOUT(dev
, 0x01);
1078 SEQ_MIDIOUT(dev
, 0xf7);
1079 dest
->runningStatus
= 0;
1081 case 0x01: /* MTC Quarter frame */
1082 case 0x03: /* Song Select. */
1083 SEQ_MIDIOUT(dev
, evt
);
1084 SEQ_MIDIOUT(dev
, d1
);
1086 case 0x02: /* Song Position Pointer. */
1087 SEQ_MIDIOUT(dev
, evt
);
1088 SEQ_MIDIOUT(dev
, d1
);
1089 SEQ_MIDIOUT(dev
, d2
);
1091 if (evt
<= 0xF7) /* System Exclusive, System Common Message */
1092 dest
->runningStatus
= 0;
1097 return MMSYSERR_NOERROR
;
1100 static UINT
midi_out_data(WORD dev_id
, UINT data
)
1102 struct midi_dest
*dest
;
1104 TRACE("(%04X, %08X);\n", dev_id
, data
);
1106 if (dev_id
>= num_dests
) return MMSYSERR_BADDEVICEID
;
1107 dest
= dests
+ dev_id
;
1108 if (!dest
->bEnabled
) return MIDIERR_NODEVICE
;
1112 WARN("can't play !\n");
1113 return MIDIERR_NODEVICE
;
1115 switch (dest
->caps
.wTechnology
)
1118 return midi_out_fm_data(dev_id
, data
);
1120 return midi_out_port_data(dev_id
, data
);
1123 WARN("Technology not supported (yet) %d !\n", dest
->caps
.wTechnology
);
1124 return MMSYSERR_NOTENABLED
;
1127 static UINT
midi_out_long_data(WORD dev_id
, MIDIHDR
*hdr
, UINT hdr_size
, struct notify_context
*notify
)
1129 struct midi_dest
*dest
;
1133 TRACE("(%04X, %p, %08X);\n", dev_id
, hdr
, hdr_size
);
1135 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
1136 * but it seems to be used only for midi input.
1137 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
1140 if (dev_id
>= num_dests
) return MMSYSERR_BADDEVICEID
;
1141 dest
= dests
+ dev_id
;
1142 if (!dest
->bEnabled
) return MIDIERR_NODEVICE
;
1146 WARN("can't play !\n");
1147 return MIDIERR_NODEVICE
;
1150 data
= (BYTE
*)hdr
->lpData
;
1153 return MIDIERR_UNPREPARED
;
1154 if (!(hdr
->dwFlags
& MHDR_PREPARED
))
1155 return MIDIERR_UNPREPARED
;
1156 if (hdr
->dwFlags
& MHDR_INQUEUE
)
1157 return MIDIERR_STILLPLAYING
;
1158 hdr
->dwFlags
&= ~MHDR_DONE
;
1159 hdr
->dwFlags
|= MHDR_INQUEUE
;
1161 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1162 * data, or can it also contain raw MIDI data, to be split up and sent to
1164 * If the latter is true, then the following WARNing will fire up
1166 if (data
[0] != 0xF0 || data
[hdr
->dwBufferLength
- 1] != 0xF7)
1167 WARN("The allegedly system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1169 TRACE("dwBufferLength=%u !\n", (unsigned)hdr
->dwBufferLength
);
1170 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1171 data
[0], data
[1], data
[2], data
[hdr
->dwBufferLength
- 3],
1172 data
[hdr
->dwBufferLength
- 2], data
[hdr
->dwBufferLength
- 1]);
1174 switch (dest
->caps
.wTechnology
)
1177 /* FIXME: I don't think there is much to do here */
1180 if (data
[0] != 0xF0)
1182 /* Send end of System Exclusive */
1183 SEQ_MIDIOUT(dev_id
- num_synths
, 0xF0);
1184 WARN("Adding missing 0xF0 marker at the beginning of system exclusive byte stream\n");
1186 for (count
= 0; count
< hdr
->dwBufferLength
; count
++)
1187 SEQ_MIDIOUT(dev_id
- num_synths
, data
[count
]);
1188 if (data
[count
- 1] != 0xF7)
1190 /* Send end of System Exclusive */
1191 SEQ_MIDIOUT(dev_id
- num_synths
, 0xF7);
1192 WARN("Adding missing 0xF7 marker at the end of system exclusive byte stream\n");
1197 WARN("Technology not supported (yet) %d !\n", dest
->caps
.wTechnology
);
1198 return MMSYSERR_NOTENABLED
;
1201 dest
->runningStatus
= 0;
1202 hdr
->dwFlags
&= ~MHDR_INQUEUE
;
1203 hdr
->dwFlags
|= MHDR_DONE
;
1204 set_out_notify(notify
, dest
, dev_id
, MOM_DONE
, (UINT_PTR
)hdr
, 0);
1205 return MMSYSERR_NOERROR
;
1208 static UINT
midi_out_prepare(WORD dev_id
, MIDIHDR
*hdr
, UINT hdr_size
)
1210 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
1212 if (hdr_size
< offsetof(MIDIHDR
, dwOffset
) || !hdr
|| !hdr
->lpData
)
1213 return MMSYSERR_INVALPARAM
;
1214 if (hdr
->dwFlags
& MHDR_PREPARED
)
1215 return MMSYSERR_NOERROR
;
1218 hdr
->dwFlags
|= MHDR_PREPARED
;
1219 hdr
->dwFlags
&= ~(MHDR_DONE
| MHDR_INQUEUE
);
1220 return MMSYSERR_NOERROR
;
1223 static UINT
midi_out_unprepare(WORD dev_id
, MIDIHDR
*hdr
, UINT hdr_size
)
1225 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
1227 if (hdr_size
< offsetof(MIDIHDR
, dwOffset
) || !hdr
|| !hdr
->lpData
)
1228 return MMSYSERR_INVALPARAM
;
1229 if (!(hdr
->dwFlags
& MHDR_PREPARED
))
1230 return MMSYSERR_NOERROR
;
1231 if (hdr
->dwFlags
& MHDR_INQUEUE
)
1232 return MIDIERR_STILLPLAYING
;
1234 hdr
->dwFlags
&= ~MHDR_PREPARED
;
1235 return MMSYSERR_NOERROR
;
1238 static UINT
midi_out_get_devcaps(WORD dev_id
, MIDIOUTCAPSW
*caps
, UINT size
)
1240 TRACE("(%04X, %p, %08X);\n", dev_id
, caps
, size
);
1242 if (dev_id
>= num_dests
) return MMSYSERR_BADDEVICEID
;
1243 if (!caps
) return MMSYSERR_INVALPARAM
;
1245 memcpy(caps
, &dests
[dev_id
].caps
, min(size
, sizeof(*caps
)));
1247 return MMSYSERR_NOERROR
;
1250 static UINT
midi_out_get_volume(WORD dev_id
, UINT
*volume
)
1252 if (!volume
) return MMSYSERR_INVALPARAM
;
1253 if (dev_id
>= num_dests
) return MMSYSERR_BADDEVICEID
;
1255 *volume
= 0xFFFFFFFF;
1256 return (dests
[dev_id
].caps
.dwSupport
& MIDICAPS_VOLUME
) ? 0 : MMSYSERR_NOTSUPPORTED
;
1259 static UINT
midi_out_reset(WORD dev_id
)
1261 struct midi_dest
*dest
;
1264 TRACE("(%04X);\n", dev_id
);
1266 if (dev_id
>= num_dests
) return MMSYSERR_BADDEVICEID
;
1267 dest
= dests
+ dev_id
;
1268 if (!dest
->bEnabled
) return MIDIERR_NODEVICE
;
1270 /* stop all notes */
1271 for (chn
= 0; chn
< 16; chn
++)
1273 /* turn off every note */
1274 midi_out_data(dev_id
, 0x7800 | MIDI_CTL_CHANGE
| chn
);
1275 /* remove sustain on all channels */
1276 midi_out_data(dev_id
, (CTL_SUSTAIN
<< 8) | MIDI_CTL_CHANGE
| chn
);
1278 dest
->runningStatus
= 0;
1279 /* FIXME: the LongData buffers must also be returned to the app */
1280 return MMSYSERR_NOERROR
;
1283 static void handle_sysex_data(struct midi_src
*src
, unsigned char value
, UINT time
)
1285 struct notify_context notify
;
1294 hdr
= src
->lpQueueHdr
;
1297 BYTE
*data
= (BYTE
*)hdr
->lpData
;
1299 data
[hdr
->dwBytesRecorded
++] = value
;
1300 if (hdr
->dwBytesRecorded
== hdr
->dwBufferLength
)
1304 if (value
== 0xf7) /* end */
1312 src
->lpQueueHdr
= hdr
->lpNext
;
1313 hdr
->dwFlags
&= ~MHDR_INQUEUE
;
1314 hdr
->dwFlags
|= MHDR_DONE
;
1315 set_in_notify(¬ify
, src
, src
- srcs
, MIM_LONGDATA
, (UINT_PTR
)hdr
, time
);
1316 notify_post(¬ify
);
1322 static void handle_regular_data(struct midi_src
*src
, unsigned char value
, UINT time
)
1324 struct notify_context notify
;
1327 #define IS_CMD(_x) (((_x) & 0x80) == 0x80)
1328 #define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
1330 if (!IS_CMD(value
) && src
->incLen
== 0) /* try to reuse old cmd */
1332 if (IS_CMD(src
->incPrev
) && !IS_SYS_CMD(src
->incPrev
))
1334 src
->incoming
[0] = src
->incPrev
;
1339 /* FIXME: should generate MIM_ERROR notification */
1343 src
->incoming
[(int)src
->incLen
++] = value
;
1344 if (src
->incLen
== 1 && !IS_SYS_CMD(src
->incoming
[0]))
1345 /* store new cmd, just in case */
1346 src
->incPrev
= src
->incoming
[0];
1351 switch (src
->incoming
[0] & 0xF0)
1355 case MIDI_KEY_PRESSURE
:
1356 case MIDI_CTL_CHANGE
:
1357 case MIDI_PITCH_BEND
:
1358 if (src
->incLen
== 3)
1359 to_send
= (src
->incoming
[2] << 16) | (src
->incoming
[1] << 8) |
1362 case MIDI_PGM_CHANGE
:
1363 case MIDI_CHN_PRESSURE
:
1364 if (src
->incLen
== 2)
1365 to_send
= (src
->incoming
[1] << 8) | src
->incoming
[0];
1367 case MIDI_SYSTEM_PREFIX
:
1368 if (src
->incLen
== 1)
1369 to_send
= src
->incoming
[0];
1376 set_in_notify(¬ify
, src
, src
- srcs
, MIM_DATA
, to_send
, time
);
1377 notify_post(¬ify
);
1381 static void handle_midi_data(unsigned char *buffer
, unsigned int len
)
1383 unsigned int time
= get_time_msec(), i
;
1384 struct midi_src
*src
;
1385 unsigned char value
;
1388 for (i
= 0; i
< len
; i
+= (buffer
[i
] & 0x80) ? 8 : 4)
1390 if (buffer
[i
] != SEQ_MIDIPUTC
) continue;
1392 dev_id
= buffer
[i
+ 2];
1393 value
= buffer
[i
+ 1];
1395 if (dev_id
>= num_srcs
) continue;
1396 src
= srcs
+ dev_id
;
1397 if (src
->state
<= 0) continue;
1399 if (value
== 0xf0 || src
->state
& 2) /* system exclusive */
1400 handle_sysex_data(src
, value
, time
- src
->startTime
);
1402 handle_regular_data(src
, value
, time
- src
->startTime
);
1406 static void *rec_thread_proc(void *arg
)
1408 int fd
= PtrToLong(arg
);
1409 unsigned char buffer
[256];
1411 struct pollfd pollfd
[2];
1413 pollfd
[0].fd
= rec_cancel_pipe
[0];
1414 pollfd
[0].events
= POLLIN
;
1416 pollfd
[1].events
= POLLIN
;
1420 /* Check if an event is present */
1421 if (poll(pollfd
, ARRAY_SIZE(pollfd
), -1) <= 0)
1424 if (pollfd
[0].revents
& POLLIN
) /* cancelled */
1427 len
= read(fd
, buffer
, sizeof(buffer
));
1429 if (len
> 0 && len
% 4 == 0)
1430 handle_midi_data(buffer
, len
);
1435 static UINT
midi_in_open(WORD dev_id
, MIDIOPENDESC
*desc
, UINT flags
, struct notify_context
*notify
)
1437 struct midi_src
*src
;
1440 TRACE("(%04X, %p, %08X);\n", dev_id
, desc
, flags
);
1444 WARN("Invalid Parameter !\n");
1445 return MMSYSERR_INVALPARAM
;
1449 * how to check that content of lpDesc is correct ?
1451 if (dev_id
>= num_srcs
)
1453 WARN("wDevID too large (%u) !\n", dev_id
);
1454 return MMSYSERR_BADDEVICEID
;
1456 src
= srcs
+ dev_id
;
1457 if (src
->state
== -1)
1459 WARN("device disabled\n");
1460 return MIDIERR_NODEVICE
;
1462 if (src
->midiDesc
.hMidi
!= 0)
1464 WARN("device already open !\n");
1465 return MMSYSERR_ALLOCATED
;
1467 if ((flags
& MIDI_IO_STATUS
) != 0)
1469 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
1470 flags
&= ~MIDI_IO_STATUS
;
1472 if ((flags
& ~CALLBACK_TYPEMASK
) != 0)
1474 FIXME("Bad flags\n");
1475 return MMSYSERR_INVALFLAG
;
1480 return MMSYSERR_ERROR
;
1482 if (num_midi_in_started
++ == 0)
1484 pipe(rec_cancel_pipe
);
1485 if (pthread_create(&rec_thread_id
, NULL
, rec_thread_proc
, LongToPtr(fd
)))
1487 close(rec_cancel_pipe
[0]);
1488 close(rec_cancel_pipe
[1]);
1489 num_midi_in_started
= 0;
1490 WARN("Couldn't create thread for midi-in\n");
1492 return MMSYSERR_ERROR
;
1494 TRACE("Created thread for midi-in\n");
1497 src
->wFlags
= HIWORD(flags
& CALLBACK_TYPEMASK
);
1499 src
->lpQueueHdr
= NULL
;
1500 src
->midiDesc
= *desc
;
1506 set_in_notify(notify
, src
, dev_id
, MIM_OPEN
, 0, 0);
1507 return MMSYSERR_NOERROR
;
1510 static UINT
midi_in_close(WORD dev_id
, struct notify_context
*notify
)
1512 struct midi_src
*src
;
1514 TRACE("(%04X);\n", dev_id
);
1516 if (dev_id
>= num_srcs
)
1518 WARN("dev_id too big (%u) !\n", dev_id
);
1519 return MMSYSERR_BADDEVICEID
;
1521 src
= srcs
+ dev_id
;
1522 if (src
->midiDesc
.hMidi
== 0)
1524 WARN("device not opened !\n");
1525 return MMSYSERR_ERROR
;
1527 if (src
->lpQueueHdr
!= 0)
1528 return MIDIERR_STILLPLAYING
;
1533 return MMSYSERR_ERROR
;
1535 if (--num_midi_in_started
== 0)
1537 TRACE("Stopping thread for midi-in\n");
1538 write(rec_cancel_pipe
[1], "x", 1);
1539 pthread_join(rec_thread_id
, NULL
);
1540 close(rec_cancel_pipe
[0]);
1541 close(rec_cancel_pipe
[1]);
1542 TRACE("Stopped thread for midi-in\n");
1547 set_in_notify(notify
, src
, dev_id
, MIM_CLOSE
, 0, 0);
1548 src
->midiDesc
.hMidi
= 0;
1550 return MMSYSERR_NOERROR
;
1553 static UINT
midi_in_add_buffer(WORD dev_id
, MIDIHDR
*hdr
, UINT hdr_size
)
1555 struct midi_src
*src
;
1558 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
1560 if (dev_id
>= num_srcs
) return MMSYSERR_BADDEVICEID
;
1561 src
= srcs
+ dev_id
;
1562 if (src
->state
== -1) return MIDIERR_NODEVICE
;
1564 if (!hdr
|| hdr_size
< offsetof(MIDIHDR
, dwOffset
) || !hdr
->dwBufferLength
)
1565 return MMSYSERR_INVALPARAM
;
1566 if (hdr
->dwFlags
& MHDR_INQUEUE
) return MIDIERR_STILLPLAYING
;
1567 if (!(hdr
->dwFlags
& MHDR_PREPARED
)) return MIDIERR_UNPREPARED
;
1571 hdr
->dwFlags
&= ~WHDR_DONE
;
1572 hdr
->dwFlags
|= MHDR_INQUEUE
;
1573 hdr
->dwBytesRecorded
= 0;
1576 next
= &src
->lpQueueHdr
;
1577 while (*next
) next
= &(*next
)->lpNext
;
1582 return MMSYSERR_NOERROR
;
1585 static UINT
midi_in_prepare(WORD dev_id
, MIDIHDR
*hdr
, UINT hdr_size
)
1587 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
1589 if (hdr_size
< offsetof(MIDIHDR
, dwOffset
) || !hdr
|| !hdr
->lpData
)
1590 return MMSYSERR_INVALPARAM
;
1591 if (hdr
->dwFlags
& MHDR_PREPARED
)
1592 return MMSYSERR_NOERROR
;
1595 hdr
->dwFlags
|= MHDR_PREPARED
;
1596 hdr
->dwFlags
&= ~(MHDR_DONE
| MHDR_INQUEUE
);
1598 return MMSYSERR_NOERROR
;
1601 static UINT
midi_in_unprepare(WORD dev_id
, MIDIHDR
*hdr
, UINT hdr_size
)
1603 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
1605 if (hdr_size
< offsetof(MIDIHDR
, dwOffset
) || !hdr
|| !hdr
->lpData
)
1606 return MMSYSERR_INVALPARAM
;
1607 if (!(hdr
->dwFlags
& MHDR_PREPARED
))
1608 return MMSYSERR_NOERROR
;
1609 if (hdr
->dwFlags
& MHDR_INQUEUE
)
1610 return MIDIERR_STILLPLAYING
;
1612 hdr
->dwFlags
&= ~MHDR_PREPARED
;
1614 return MMSYSERR_NOERROR
;
1617 static UINT
midi_in_get_devcaps(WORD dev_id
, MIDIINCAPSW
*caps
, UINT size
)
1619 TRACE("(%04X, %p, %08X);\n", dev_id
, caps
, size
);
1621 if (dev_id
>= num_srcs
) return MMSYSERR_BADDEVICEID
;
1622 if (!caps
) return MMSYSERR_INVALPARAM
;
1624 memcpy(caps
, &srcs
[dev_id
].caps
, min(size
, sizeof(*caps
)));
1626 return MMSYSERR_NOERROR
;
1629 static UINT
midi_in_start(WORD dev_id
)
1631 struct midi_src
*src
;
1633 TRACE("(%04X);\n", dev_id
);
1635 if (dev_id
>= num_srcs
) return MMSYSERR_BADDEVICEID
;
1636 src
= srcs
+ dev_id
;
1637 if (src
->state
== -1) return MIDIERR_NODEVICE
;
1640 src
->startTime
= get_time_msec();
1641 return MMSYSERR_NOERROR
;
1644 static UINT
midi_in_stop(WORD dev_id
)
1646 struct midi_src
*src
;
1648 TRACE("(%04X);\n", dev_id
);
1650 if (dev_id
>= num_srcs
) return MMSYSERR_BADDEVICEID
;
1651 src
= srcs
+ dev_id
;
1652 if (src
->state
== -1) return MIDIERR_NODEVICE
;
1655 return MMSYSERR_NOERROR
;
1658 static UINT
midi_in_reset(WORD dev_id
, struct notify_context
*notify
)
1660 UINT cur_time
= get_time_msec();
1661 UINT err
= MMSYSERR_NOERROR
;
1662 struct midi_src
*src
;
1665 TRACE("(%04X);\n", dev_id
);
1667 if (dev_id
>= num_srcs
) return MMSYSERR_BADDEVICEID
;
1668 src
= srcs
+ dev_id
;
1669 if (src
->state
== -1) return MIDIERR_NODEVICE
;
1673 if (src
->lpQueueHdr
)
1675 hdr
= src
->lpQueueHdr
;
1676 src
->lpQueueHdr
= hdr
->lpNext
;
1677 hdr
->dwFlags
&= ~MHDR_INQUEUE
;
1678 hdr
->dwFlags
|= MHDR_DONE
;
1679 set_in_notify(notify
, src
, dev_id
, MIM_LONGDATA
, (UINT_PTR
)hdr
, cur_time
- src
->startTime
);
1680 if (src
->lpQueueHdr
) err
= ERROR_RETRY
; /* ask the client to call again */
1688 NTSTATUS
oss_midi_out_message(void *args
)
1690 struct midi_out_message_params
*params
= args
;
1692 params
->notify
->send_notify
= FALSE
;
1694 switch (params
->msg
)
1697 *params
->err
= oss_midi_init();
1700 *params
->err
= midi_exit();
1704 /* FIXME: Pretend this is supported */
1705 *params
->err
= MMSYSERR_NOERROR
;
1708 *params
->err
= midi_out_open(params
->dev_id
, (MIDIOPENDESC
*)params
->param_1
, params
->param_2
, params
->notify
);
1711 *params
->err
= midi_out_close(params
->dev_id
, params
->notify
);
1714 *params
->err
= midi_out_data(params
->dev_id
, params
->param_1
);
1717 *params
->err
= midi_out_long_data(params
->dev_id
, (MIDIHDR
*)params
->param_1
, params
->param_2
, params
->notify
);
1720 *params
->err
= midi_out_prepare(params
->dev_id
, (MIDIHDR
*)params
->param_1
, params
->param_2
);
1722 case MODM_UNPREPARE
:
1723 *params
->err
= midi_out_unprepare(params
->dev_id
, (MIDIHDR
*)params
->param_1
, params
->param_2
);
1725 case MODM_GETDEVCAPS
:
1726 *params
->err
= midi_out_get_devcaps(params
->dev_id
, (MIDIOUTCAPSW
*)params
->param_1
, params
->param_2
);
1728 case MODM_GETNUMDEVS
:
1729 *params
->err
= num_dests
;
1731 case MODM_GETVOLUME
:
1732 *params
->err
= midi_out_get_volume(params
->dev_id
, (UINT
*)params
->param_1
);
1734 case MODM_SETVOLUME
:
1738 *params
->err
= midi_out_reset(params
->dev_id
);
1741 TRACE("Unsupported message\n");
1742 *params
->err
= MMSYSERR_NOTSUPPORTED
;
1745 return STATUS_SUCCESS
;
1748 NTSTATUS
oss_midi_in_message(void *args
)
1750 struct midi_in_message_params
*params
= args
;
1752 params
->notify
->send_notify
= FALSE
;
1754 switch (params
->msg
)
1757 *params
->err
= oss_midi_init();
1760 *params
->err
= midi_exit();
1764 /* FIXME: Pretend this is supported */
1765 *params
->err
= MMSYSERR_NOERROR
;
1768 *params
->err
= midi_in_open(params
->dev_id
, (MIDIOPENDESC
*)params
->param_1
, params
->param_2
, params
->notify
);
1771 *params
->err
= midi_in_close(params
->dev_id
, params
->notify
);
1773 case MIDM_ADDBUFFER
:
1774 *params
->err
= midi_in_add_buffer(params
->dev_id
, (MIDIHDR
*)params
->param_1
, params
->param_2
);
1777 *params
->err
= midi_in_prepare(params
->dev_id
, (MIDIHDR
*)params
->param_1
, params
->param_2
);
1779 case MIDM_UNPREPARE
:
1780 *params
->err
= midi_in_unprepare(params
->dev_id
, (MIDIHDR
*)params
->param_1
, params
->param_2
);
1782 case MIDM_GETDEVCAPS
:
1783 *params
->err
= midi_in_get_devcaps(params
->dev_id
, (MIDIINCAPSW
*)params
->param_1
, params
->param_2
);
1785 case MIDM_GETNUMDEVS
:
1786 *params
->err
= num_srcs
;
1789 *params
->err
= midi_in_start(params
->dev_id
);
1792 *params
->err
= midi_in_stop(params
->dev_id
);
1795 *params
->err
= midi_in_reset(params
->dev_id
, params
->notify
);
1798 TRACE("Unsupported message\n");
1799 *params
->err
= MMSYSERR_NOTSUPPORTED
;
1802 return STATUS_SUCCESS
;
1805 NTSTATUS
oss_midi_notify_wait(void *args
)
1807 struct midi_notify_wait_params
*params
= args
;
1809 pthread_mutex_lock(¬ify_mutex
);
1811 while (!notify_quit
&& notify_buffer_empty())
1812 pthread_cond_wait(¬ify_read_cond
, ¬ify_mutex
);
1814 *params
->quit
= notify_quit
;
1817 notify_buffer_remove(params
->notify
);
1818 pthread_cond_signal(¬ify_write_cond
);
1820 pthread_mutex_unlock(¬ify_mutex
);
1822 return STATUS_SUCCESS
;
1829 struct notify_context32
1842 static void notify_to_notify32(struct notify_context32
*notify32
,
1843 const struct notify_context
*notify
)
1845 notify32
->send_notify
= notify
->send_notify
;
1846 notify32
->dev_id
= notify
->dev_id
;
1847 notify32
->msg
= notify
->msg
;
1848 notify32
->param_1
= notify
->param_1
;
1849 notify32
->param_2
= notify
->param_2
;
1850 notify32
->callback
= notify
->callback
;
1851 notify32
->flags
= notify
->flags
;
1852 notify32
->device
= PtrToUlong(notify
->device
);
1853 notify32
->instance
= notify
->instance
;
1856 struct midi_open_desc32
1863 MIDIOPENSTRMID rgIds
;
1869 UINT dwBufferLength
;
1870 UINT dwBytesRecorded
;
1879 static UINT
wow64_midi_out_prepare(WORD dev_id
, struct midi_hdr32
*hdr
, UINT hdr_size
)
1881 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
1883 if (hdr_size
< offsetof(struct midi_hdr32
, dwOffset
) || !hdr
|| !hdr
->lpData
)
1884 return MMSYSERR_INVALPARAM
;
1885 if (hdr
->dwFlags
& MHDR_PREPARED
)
1886 return MMSYSERR_NOERROR
;
1889 hdr
->dwFlags
|= MHDR_PREPARED
;
1890 hdr
->dwFlags
&= ~(MHDR_DONE
| MHDR_INQUEUE
);
1891 return MMSYSERR_NOERROR
;
1894 static UINT
wow64_midi_out_unprepare(WORD dev_id
, struct midi_hdr32
*hdr
, UINT hdr_size
)
1896 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
1898 if (hdr_size
< offsetof(struct midi_hdr32
, dwOffset
) || !hdr
|| !hdr
->lpData
)
1899 return MMSYSERR_INVALPARAM
;
1900 if (!(hdr
->dwFlags
& MHDR_PREPARED
))
1901 return MMSYSERR_NOERROR
;
1902 if (hdr
->dwFlags
& MHDR_INQUEUE
)
1903 return MIDIERR_STILLPLAYING
;
1905 hdr
->dwFlags
&= ~MHDR_PREPARED
;
1906 return MMSYSERR_NOERROR
;
1909 NTSTATUS
oss_wow64_midi_out_message(void *args
)
1921 struct notify_context32
*notify32
= ULongToPtr(params32
->notify
);
1922 struct midi_open_desc32
*desc32
;
1923 struct midi_hdr32
*hdr32
;
1924 struct notify_context notify
;
1925 MIDIOPENDESC open_desc
;
1927 struct midi_out_message_params params
=
1929 .dev_id
= params32
->dev_id
,
1930 .msg
= params32
->msg
,
1931 .user
= params32
->user
,
1932 .param_1
= params32
->param_1
,
1933 .param_2
= params32
->param_2
,
1934 .err
= ULongToPtr(params32
->err
),
1937 notify32
->send_notify
= FALSE
;
1939 switch (params32
->msg
)
1942 desc32
= ULongToPtr(params32
->param_1
);
1944 open_desc
.hMidi
= ULongToPtr(desc32
->hMidi
);
1945 open_desc
.dwCallback
= desc32
->dwCallback
;
1946 open_desc
.dwInstance
= desc32
->dwInstance
;
1947 open_desc
.dnDevNode
= desc32
->dnDevNode
;
1948 open_desc
.cIds
= desc32
->cIds
;
1949 open_desc
.rgIds
.dwStreamID
= desc32
->rgIds
.dwStreamID
;
1950 open_desc
.rgIds
.wDeviceID
= desc32
->rgIds
.wDeviceID
;
1952 params
.param_1
= (UINT_PTR
)&open_desc
;
1956 hdr32
= ULongToPtr(params32
->param_1
);
1958 memset(&hdr
, 0, sizeof(hdr
));
1959 hdr
.lpData
= ULongToPtr(hdr32
->lpData
);
1960 hdr
.dwBufferLength
= hdr32
->dwBufferLength
;
1961 hdr
.dwFlags
= hdr32
->dwFlags
;
1963 params
.param_1
= (UINT_PTR
)&hdr
;
1964 params
.param_2
= sizeof(hdr
);
1967 case MODM_PREPARE
: /* prepare and unprepare are easier to handle explicitly */
1968 hdr32
= ULongToPtr(params32
->param_1
);
1970 *params
.err
= wow64_midi_out_prepare(params32
->dev_id
, hdr32
, params32
->param_2
);
1971 return STATUS_SUCCESS
;
1973 case MODM_UNPREPARE
:
1974 hdr32
= ULongToPtr(params32
->param_1
);
1976 *params
.err
= wow64_midi_out_unprepare(params32
->dev_id
, hdr32
, params32
->param_2
);
1977 return STATUS_SUCCESS
;
1980 oss_midi_out_message(¶ms
);
1982 switch (params32
->msg
)
1985 hdr32
= ULongToPtr(params32
->param_1
);
1987 hdr32
->dwFlags
= hdr
.dwFlags
;
1991 if (notify
.send_notify
)
1993 notify_to_notify32(notify32
, ¬ify
);
1995 if (notify
.msg
== MOM_DONE
)
1996 notify32
->param_1
= params32
->param_1
; /* restore the 32-bit hdr */
1998 return STATUS_SUCCESS
;
2001 static UINT
wow64_midi_in_prepare(WORD dev_id
, struct midi_hdr32
*hdr
, UINT hdr_size
)
2003 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
2005 if (hdr_size
< offsetof(struct midi_hdr32
, dwOffset
) || !hdr
|| !hdr
->lpData
)
2006 return MMSYSERR_INVALPARAM
;
2007 if (hdr
->dwFlags
& MHDR_PREPARED
)
2008 return MMSYSERR_NOERROR
;
2011 hdr
->dwFlags
|= MHDR_PREPARED
;
2012 hdr
->dwFlags
&= ~(MHDR_DONE
| MHDR_INQUEUE
);
2014 return MMSYSERR_NOERROR
;
2017 static UINT
wow64_midi_in_unprepare(WORD dev_id
, struct midi_hdr32
*hdr
, UINT hdr_size
)
2019 TRACE("(%04X, %p, %d);\n", dev_id
, hdr
, hdr_size
);
2021 if (hdr_size
< offsetof(struct midi_hdr32
, dwOffset
) || !hdr
|| !hdr
->lpData
)
2022 return MMSYSERR_INVALPARAM
;
2023 if (!(hdr
->dwFlags
& MHDR_PREPARED
))
2024 return MMSYSERR_NOERROR
;
2025 if (hdr
->dwFlags
& MHDR_INQUEUE
)
2026 return MIDIERR_STILLPLAYING
;
2028 hdr
->dwFlags
&= ~MHDR_PREPARED
;
2030 return MMSYSERR_NOERROR
;
2033 NTSTATUS
oss_wow64_midi_in_message(void *args
)
2045 struct notify_context32
*notify32
= ULongToPtr(params32
->notify
);
2046 struct midi_open_desc32
*desc32
;
2047 struct midi_hdr32
*hdr32
;
2048 struct notify_context notify
;
2049 MIDIOPENDESC open_desc
;
2050 MIDIHDR
*hdr
= NULL
;
2051 struct midi_in_message_params params
=
2053 .dev_id
= params32
->dev_id
,
2054 .msg
= params32
->msg
,
2055 .user
= params32
->user
,
2056 .param_1
= params32
->param_1
,
2057 .param_2
= params32
->param_2
,
2058 .err
= ULongToPtr(params32
->err
),
2061 notify32
->send_notify
= FALSE
;
2063 switch (params32
->msg
)
2066 desc32
= ULongToPtr(params32
->param_1
);
2068 open_desc
.hMidi
= ULongToPtr(desc32
->hMidi
);
2069 open_desc
.dwCallback
= desc32
->dwCallback
;
2070 open_desc
.dwInstance
= desc32
->dwInstance
;
2071 open_desc
.dnDevNode
= desc32
->dnDevNode
;
2072 open_desc
.cIds
= desc32
->cIds
;
2073 open_desc
.rgIds
.dwStreamID
= desc32
->rgIds
.dwStreamID
;
2074 open_desc
.rgIds
.wDeviceID
= desc32
->rgIds
.wDeviceID
;
2076 params
.param_1
= (UINT_PTR
)&open_desc
;
2079 case MIDM_ADDBUFFER
:
2080 hdr32
= ULongToPtr(params32
->param_1
);
2082 hdr
= calloc(1, sizeof(*hdr
));
2083 hdr
->lpData
= ULongToPtr(hdr32
->lpData
);
2084 hdr
->dwBufferLength
= hdr32
->dwBufferLength
;
2085 hdr
->dwFlags
= hdr32
->dwFlags
;
2086 hdr
->dwReserved
[7] = params32
->param_1
; /* keep hdr32 for MIM_LONGDATA notification */
2088 params
.param_1
= (UINT_PTR
)hdr
;
2089 params
.param_2
= sizeof(*hdr
);
2092 case MIDM_PREPARE
: /* prepare and unprepare are easier to handle explicitly */
2093 hdr32
= ULongToPtr(params32
->param_1
);
2095 *params
.err
= wow64_midi_in_prepare(params32
->dev_id
, hdr32
, params32
->param_2
);
2096 return STATUS_SUCCESS
;
2098 case MIDM_UNPREPARE
:
2099 hdr32
= ULongToPtr(params32
->param_1
);
2101 *params
.err
= wow64_midi_in_unprepare(params32
->dev_id
, hdr32
, params32
->param_2
);
2102 return STATUS_SUCCESS
;
2105 oss_midi_in_message(¶ms
);
2107 switch (params32
->msg
)
2109 case MIDM_ADDBUFFER
:
2110 hdr32
= ULongToPtr(params32
->param_1
);
2114 hdr32
->dwFlags
= hdr
->dwFlags
;
2115 hdr32
->dwBytesRecorded
= hdr
->dwBytesRecorded
;
2123 if (notify
.send_notify
)
2125 notify_to_notify32(notify32
, ¬ify
);
2127 if (notify
.msg
== MIM_LONGDATA
)
2129 hdr
= (MIDIHDR
*)notify
.param_1
;
2130 notify32
->param_1
= hdr
->dwReserved
[7];
2131 hdr32
= ULongToPtr(notify32
->param_1
);
2132 hdr32
->dwBytesRecorded
= hdr
->dwBytesRecorded
;
2133 hdr32
->dwFlags
= hdr
->dwFlags
;
2137 return STATUS_SUCCESS
;
2140 NTSTATUS
oss_wow64_midi_notify_wait(void *args
)
2147 struct notify_context32
*notify32
= ULongToPtr(params32
->notify
);
2148 struct midi_hdr32
*hdr32
;
2149 struct notify_context notify
;
2151 struct midi_notify_wait_params params
=
2153 .quit
= ULongToPtr(params32
->quit
),
2156 notify32
->send_notify
= FALSE
;
2158 oss_midi_notify_wait(¶ms
);
2160 if (!*params
.quit
&& notify
.send_notify
)
2162 notify_to_notify32(notify32
, ¬ify
);
2164 if (notify
.msg
== MIM_LONGDATA
)
2166 hdr
= (MIDIHDR
*)notify
.param_1
;
2167 notify32
->param_1
= hdr
->dwReserved
[7];
2168 hdr32
= ULongToPtr(notify32
->param_1
);
2169 hdr32
->dwBytesRecorded
= hdr
->dwBytesRecorded
;
2170 hdr32
->dwFlags
= hdr
->dwFlags
;
2174 return STATUS_SUCCESS
;