Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Contact info
Forum
 
Other projects
   Altirra

Search

Archives

01 Dec - 31 Dec 2013
01 Oct - 31 Oct 2013
01 Aug - 31 Aug 2013
01 May - 31 May 2013
01 Mar - 31 Mar 2013
01 Feb - 29 Feb 2013
01 Dec - 31 Dec 2012
01 Nov - 30 Nov 2012
01 Oct - 31 Oct 2012
01 Sep - 30 Sep 2012
01 Aug - 31 Aug 2012
01 June - 30 June 2012
01 May - 31 May 2012
01 Apr - 30 Apr 2012
01 Dec - 31 Dec 2011
01 Nov - 30 Nov 2011
01 Oct - 31 Oct 2011
01 Sep - 30 Sep 2011
01 Aug - 31 Aug 2011
01 Jul - 31 Jul 2011
01 June - 30 June 2011
01 May - 31 May 2011
01 Apr - 30 Apr 2011
01 Mar - 31 Mar 2011
01 Feb - 29 Feb 2011
01 Jan - 31 Jan 2011
01 Dec - 31 Dec 2010
01 Nov - 30 Nov 2010
01 Oct - 31 Oct 2010
01 Sep - 30 Sep 2010
01 Aug - 31 Aug 2010
01 Jul - 31 Jul 2010
01 June - 30 June 2010
01 May - 31 May 2010
01 Apr - 30 Apr 2010
01 Mar - 31 Mar 2010
01 Feb - 29 Feb 2010
01 Jan - 31 Jan 2010
01 Dec - 31 Dec 2009
01 Nov - 30 Nov 2009
01 Oct - 31 Oct 2009
01 Sep - 30 Sep 2009
01 Aug - 31 Aug 2009
01 Jul - 31 Jul 2009
01 June - 30 June 2009
01 May - 31 May 2009
01 Apr - 30 Apr 2009
01 Mar - 31 Mar 2009
01 Feb - 29 Feb 2009
01 Jan - 31 Jan 2009
01 Dec - 31 Dec 2008
01 Nov - 30 Nov 2008
01 Oct - 31 Oct 2008
01 Sep - 30 Sep 2008
01 Aug - 31 Aug 2008
01 Jul - 31 Jul 2008
01 June - 30 June 2008
01 May - 31 May 2008
01 Apr - 30 Apr 2008
01 Mar - 31 Mar 2008
01 Feb - 29 Feb 2008
01 Jan - 31 Jan 2008
01 Dec - 31 Dec 2007
01 Nov - 30 Nov 2007
01 Oct - 31 Oct 2007
01 Sep - 30 Sep 2007
01 Aug - 31 Aug 2007
01 Jul - 31 Jul 2007
01 June - 30 June 2007
01 May - 31 May 2007
01 Apr - 30 Apr 2007
01 Mar - 31 Mar 2007
01 Feb - 29 Feb 2007
01 Jan - 31 Jan 2007
01 Dec - 31 Dec 2006
01 Nov - 30 Nov 2006
01 Oct - 31 Oct 2006
01 Sep - 30 Sep 2006
01 Aug - 31 Aug 2006
01 Jul - 31 Jul 2006
01 June - 30 June 2006
01 May - 31 May 2006
01 Apr - 30 Apr 2006
01 Mar - 31 Mar 2006
01 Feb - 29 Feb 2006
01 Jan - 31 Jan 2006
01 Dec - 31 Dec 2005
01 Nov - 30 Nov 2005
01 Oct - 31 Oct 2005
01 Sep - 30 Sep 2005
01 Aug - 31 Aug 2005
01 Jul - 31 Jul 2005
01 June - 30 June 2005
01 May - 31 May 2005
01 Apr - 30 Apr 2005
01 Mar - 31 Mar 2005
01 Feb - 29 Feb 2005
01 Jan - 31 Jan 2005
01 Dec - 31 Dec 2004
01 Nov - 30 Nov 2004
01 Oct - 31 Oct 2004
01 Sep - 30 Sep 2004
01 Aug - 31 Aug 2004

Stuff

Powered by Pivot  
XML: RSS feed 
XML: Atom feed 

§ MIDI startup delays under Windows XP

Anyone who has tried running old Windows programs that use MIDI music under Windows XP knows what a frustrating experience it is: every time the program switches to a MIDI track, it locks up solid for a couple of seconds. This behavior wasn't present in Windows 95/98, and although it is avoided by playing MIDI files through DirectShow, that doesn't help old programs that use Media Control Interface (MCI) to play their music. Interestingly, enough, the pause doesn't get any shorter on a faster system.

This behavior annoyed me enough that I attached a debugger to figure out what was causing the pauses. The cause turned out to be rather surprising.

The way I track down pauses of a few seconds or more is simple: attach a debugger and then hit F12 when the pause occurs, causing the NT kernel to inject a breakpoint into the debugged process. Here's the stack I got:

ntdll.dll!_KiFastSystemCallRet@0()
ntdll.dll!_NtDelayExecution@8()  + 0xc bytes 
kernel32.dll!_SleepEx@8()  + 0x51 bytes 
kernel32.dll!_Sleep@4()  + 0xf bytes 
mciseq.dll!_LeaveSeq@0()  + 0x13 bytes 
mciseq.dll!_msOpen@16()  + 0x144 bytes 
mciseq.dll!_mciDriverEntry@16()  + 0x7f bytes 
mciseq.dll!_DriverProc@20()  + 0xa4 bytes 
winmm.dll!_InternalBroadcastDriverMessage@20()  + 0xa8 bytes 
winmm.dll!_DrvSendMessage@16()  + 0x18 bytes 
winmm.dll!_mciSendSingleCommand@28()  + 0x5c bytes 
winmm.dll!_mciSendCommandInternal@20()  + 0x10f bytes 
winmm.dll!_mciSendCommandW@16()  + 0x4e bytes 
winmm.dll!_mciLoadDevice@16()  + 0x37b bytes 
winmm.dll!_mciOpenDevice@12()  + 0x267 bytes 

That's odd... why is the MCI MIDI driver burning time in Sleep()? Let's look at the disassembly of mciseq.dll!_LeaveSeq():

_LeaveSeq@0:
61D71529 68 E0 60 D7 61    push offset _SeqCritSec (61D760E0h) 
61D7152E FF 15 60 10 D7 61 call dword ptr [__imp__LeaveCriticalSection@4 (61D71060h)] 
61D71534 6A 04             push 4 
61D71536 FF 15 64 10 D7 61 call dword ptr [__imp__Sleep@4 (61D71064h)] 
61D7153C C3                ret 

WTF is there a Sleep(4) call after a call to LeaveCriticalSection()??? Well, let's look at the caller, _msOpen():

61D716C0 E8 64 FE FF FF   call        _LeaveSeq@0 (61D71529h) 
61D716C5 E8 4E FE FF FF   call        _EnterSeq@0 (61D71518h) 
61D716CA 53               push        ebx  
61D716CB 8D 45 C4         lea         eax,[ebp-3Ch] 
61D716CE 50               push        eax  
61D716CF 8B 45 C0         mov         eax,dword ptr [ebp-40h] 
61D716D2 6A 0B            push        0Bh  
61D716D4 FF B0 00 01 00 00 push        dword ptr [eax+100h] 
61D716DA E8 BA 30 00 00   call        _midiSeqMessage@16 (61D74799h) 
61D716DF 39 5D D4         cmp         dword ptr [ebp-2Ch],ebx 
61D716E2 75 0A            jne         _msOpen@16+16Dh (61D716EEh) 
61D716E4 38 5D F8         cmp         byte ptr [ebp-8],bl 
61D716E7 74 05            je          _msOpen@16+16Dh (61D716EEh) 
61D716E9 38 5D F9         cmp         byte ptr [ebp-7],bl 
61D716EC 75 D2            jne         _msOpen@16+13Fh (61D716C0h) 

Even more strange.

This loop is leaving and then reentering a critical section (mutex) in the middle of a loop, presumably to allow other threads a chance to grab the critical section. Anyone who has written multithreaded code knows that this is a dangerous construct to use because you don't necessarily have a guarantee that the mutex is fair in passing ownership to other threads that may be waiting on it; in the absence of such a guarantee from the mutex design the flow of control between the threads is nondeterministic. This is particularly a problem if the threads are running at different priorities. It looks like someone tried to solve this problem by adding a Sleep(4) call to give other threads, presumably the MIDI sequencer thread, a chance to run.

I'll say it again: Sleep() is not a synchronization mechanism.

I didn't bother to analyze the control flow further, so I tried a quick hack instead. I simply changed the Sleep(4) call to Sleep(0). Suddenly all of the annoying pauses on MIDI track changes were gone, with seemingly no new problems introduced. This surprised me a bit since I don't have a dual-CPU system (it's a laptop, after all) and I would have expected either timing problems on the MIDI playback or CPU usage hitting the roof. Unfortunately, I don't know anyone on the multimedia team at Microsoft, but I am curious as to what that loop is doing. Maybe it's pre-decoding the MIDI file to determine its length? Who knows.

Comments

Comments posted:


Try Larry Osterman (http://blogs.msdn.com/larryosterman/) --- his team owns almost all audio APIs.

Eugene Lazutkin (link) - 14 11 05 - 03:24


can you tell us what to patch and where so we can get rid too of that pause? thanks!

isidro - 14 11 05 - 10:47


Get a hex editor and patch 0x534-0x53B to nop (90), or patch 0x535 to 0. Oh, and make sure SFC is off first.

foxyshadis (link) - 14 11 05 - 18:04


Usual warnings about patching system DLLs on disk apply may interfere with Microsoft updates, voids warranty, diminishes soul, etc. mciseq.dll also has a checksum in its PE header that theoretically should be zeroed or fixed up. The preferred method is to patch in-memory for affected apps, but doing so is left as an exercise for the reader. :)

Also, I would avoid patching the CALL instruction at 536-53B. mciseq.dll is a relocatable DLL, so there will be a fixup pointing at the address in the instruction. Normally this would work because PE modules are pre-linked at their preferred load address, but if it collides on load and has to be relocated, the NOPs that were in that spot will be turned to garbage. A short JMP or simply a RET at 534 would fix the problem.

Phaeron - 15 11 05 - 02:05


I hadn't realized the crc was there, and forgot about fixups. Sorry about that, you can tell it's been a while. ^^;

foxyshadis - 15 11 05 - 07:10


I'd prefer if you didn't post specific addresses but rather hex strings to find and change, so that non-US versions of the DLL could be edited, too...

Wizard of Oz - 20 11 05 - 17:23


The code that foxyshadis patched is in the LeaveSeq() function whose disassembly is above so you are looking for a hex pattern of 68 __ __ __ __ FF 15 __ __ __ __ 6A 04 __ __ __ __ C3. Underscores represent bytes that may be different due to a different base address. The 04 should be patched to 00, and then the PE header checksum at 00000130 should be cleared by patching to 00 00 00 00.

Unfortunately, the code string you are looking for is rather generic without the addresses, so you may get false hits searching for that pattern. It's much faster to look for mciseq!_LeaveSeq@0 using the symbols from the Microsoft public symbol server, if you have the setup for that. The hex patch might actually work for international versions as well, since I doubt that the MIDI driver differs between different editions of XP. Microsoft has been unifying localized versions of their OS over time; far east versions of Windows 98 are very different than the US version, but the core of Windows XP is common across all languages.

Phaeron - 21 11 05 - 00:43


I read this thread and remembered reading an article someone wrote about a week ago regarding a very similar issue (http://www.codeproject.com/system/NoDele..) Mentioning this as the author found a way to patch a protected .dll file in memory without requiring modification of any files on disk, without requiring file protection to be disabled, and without breaking any future service packs (other than the fact that your patch may not work with future versions)

Erik Anderson - 25 11 05 - 03:00


One possible explanation I found with some ancient Dell machines. They had a Yahama part-software-part-hardware MIDI synthesizer, and it needed a .5 sec spin up time for some reason (sample spooling?) between when you initialized it and when you started playing notes. Now that everyone has DirectX 9 and almost everyone has Microsoft DirectMusic's pure-sw synth (and even the DirectMusic API is going away, so no more DLS), it isn't strictly needed, but perhaps it's there for legacy hardware support.

One thing's for sure, I'm patching my stuff. :)

Andrew Cook - 26 07 07 - 10:42

Comment form


Please keep comments on-topic for this entry. If you have unrelated comments about VirtualDub, the forum is a better place to post them.
Name:  
Remember personal info?

Email (Optional):
Your email address is only revealed to the blog owner and is not shown to the public.
URL (Optional):
Comment: /

An authentication dialog may appear when you click Post Comment. Simply type in "post" as the user and "now" as the password. I have had to do this to stop automated comment spam.



Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.