2010年8月4日 星期三

USB Audio Class Driver 開發

在開發的過程中, USBDeviceAttach 被呼叫了四次(第三個參數usb interface 都不相同,因為總共有4個 interface,USBAudio算是 interface driver,所以第一個interface 載入成功的話會載入下個interface。詳情請看 LoadDeviceDrivers() in usbd.cpp。
雖然被載入4次,但不會重複載入DLL (也就是DLLMain PROCESS_ATTACH 只會CALL 一次,之後的應該是THREAD_ATTACH)。
如果你的USB Client Driver 不是 interface driver 的話,則USBDeviceAttach第三個參數為 NULL。
還有 LoadDeviceDrivers 的載入interface driver 部份有點問題,它總共會載入 bNumInterfaces 次, 這個bNumInterfaces 是 active configuration 的 descriptor (USB_CONFIGURATION_DESCRIPTOR) 但 interface++ 的部份有點不對,因為每個interface又有可能有alternate setting ,所以總共的interface (含alternate setting) 應該是 dwNumInterfaces 個 ( USB_CONFIGURATION ) 。這一點我發現在 WINCE 6 usbd.cpp中改了過來,有把alternate setting 的狀況考慮進去。



USB Audio Output procedure

1. Set Interface  ( Interface 1, Alternate Setting 1 )
2. GetEndpoint
3. OpenPipe
4. IssueIsochTransfer

而 USB Mic Recording 基本上跟 Audio Out 一樣,不過怎麼試
都得不到正確資料,而是抓到整個 Configuration Descriptor

想破腦袋後,並從Bus Hound,發現Set Interface 後有 SET_CUR
而這個SET_CUR 就是設定 Audio In Endpoint 的 frequency,果然再 issue 這個
SET_CUR 的 VendorTransfer 後就可以正常讀取 Audio Stream 了。(請參考 Audio Class spec
第五章 request ),當然 bits 數 ,frequency, channel 都要正確才行。


USB Audio Input procedure

1. Set Interface  ( Interface 2, Alternate Setting 2 )
2. GetEndpoint
3. Send Endpoint Frequency Request
4. OpenPipe
5. IssueIsochTransfer


Note: 在 IssueIsochTransfer 的 callback function 中呼叫 closepipe的話,
會再多出一次 callback

Note!! 在 IssueIsochTransfer 的 callback function 中呼叫 WriteFile,而這個檔案又位在
USB Disk上時,這個WriteFile 會block住。所以,要再Create專門WriteFile 的Thread
不能在 callback中 WriteFile

如果要做 Isoch Transfer 這篇文章很重要
http://support.microsoft.com/?scid=kb;en-us;317434&x=14&y=7

DRVM_MAPPER_PREFERRED_SET 
設定 default waveout device
http://msdn.microsoft.com/en-us/library/dd187525.aspx
http://msdn.microsoft.com/en-us/library/aa908376.aspx
http://relatedterms.com/thread/2314671/Multiple%20Audio%20Devices%20Win%20CE



MMRESULT Err = waveOutMessage((HWAVEOUT)WAVE_MAPPER,
DRVM_MAPPER_PREFERRED_SET,
CurDeviceID,
0
);

    把指定的 audio driver id 設成 0 (也就是 default audio driver )



MMRESULT Err = waveOutMessage((HWAVEOUT)WAVE_MAPPER,
DRVM_MAPPER_PREFERRED_SET,
CurDeviceID,
(DWORD)-1
); 

 把指定的 audio driver id 設成-1 (變成最後一個,比如說總共有3個driver, 0 1 2 那就會變成 2 )




USB Audio Class Driver 除了 USB 的部份還有Audio Driver的部份
之後有2個 audio device,所以才會想到用 DRVM_MAPPER_PREFERRED_SET 來設定 default audio device。DRVM_MAPPER_PREFERRED_SET 在 WINCE 5 NavReady 2009 ( ARM Only )和在 WINCE 6 R3之後才有支援。不支援的話呼叫會傳回 8 (MMSYSERR_NOTSUPPORTED)




除了不支援設定哪個default 之外,要load 2 個 audio driver 也遇到了問題
http://www.pocketpcjunkies.com/Uwe/Forum.aspx/wince-pb/10473/how-to-install-the-second-audio-driver
 search   "load audio driver wince"






但是看了WINCE 5 的help 之後,發現Audio Driver 有兩種,一種是 MDD+PDD 另一種是 UAM
(Unified Driver Model )。
可惜的是,MDD+PDD 只support only one device ( waveOutGetNumDevs always returns 1 )
新發現第三種 wavedev2 audio model ( Sample available in WINCE 6 ) (or WINCE 5 NMD Pack )
http://msdn.microsoft.com/en-us/library/ee485233.aspx
wavedev2 好像有support S/PDIF
http://www.ms-news.net/f3868/question-about-audio-driver-wince5-0-a-11725972.html

google wavedev2 waveapi.dll
http://news.rdeasy.cn/yingyong/20100208/7921.html


Writing a Network Audio Driver in WINCE
http://blogs.msdn.com/b/cenet/archive/2005/10/10/479282.aspx

Audio MDD PDD
在使用 MDD (wavemdd.lib) 的時候,PDD 部份要定義 gIntrAudio,並InterruptConnect 此 gIntrAudio。


Audio Stack 架構圖 ( described in wavedev.h )


//  @topic  Wave Audio API Manager Device Interface |
//          The COREDLL.DLL Dynamic Link Library gives user applications
//          access to the Waveform Audio functions.
//
//          The Waveform Audio API Manager exports the waveXXX functions via the
//          device manager's IOCTL calls. IOCTL_XXX constants are defined in
//          wavedev.h.
//
//  @ex     Software Layers |
//  
// *       ===============    ===============
// *       | Application |    | Application |  Module(s)
// *       ===============    ===============
// *    
// *         --------- MMSYSTEM.H ---------    Interface
// *    
// *       ==================================
// *       |          COREDLL.DLL           |  Module  (coredll.dll)
// *       ==================================
// *    
// *         ---------- WAVEDEV.H ---------    Interface
// *    
// *         ------ DeviceIoControl() -----    PSL API
// *    
// *       ==================================
// *       |     WAPI (Wave API Manager)    |  Module (waveapi.dll)
// *       ==================================
// *    
// *         ---------- WAVEDEV.H ---------    Interface
// *    
// *         ------ DeviceIoControl() -----    PSL API
// *    
// *       ==================================
// *       |            WAVEMDD             |  Module (wavedev.dll)
// *       ==================================
// *    
// *         --------- WAVEDDSI.H ---------    Interface
// *    
// *       ==================================
// *       |            WAVEPDD             |  Module (wavedev.dll)
// *       ==================================





DRVM_MAPPER_PREFERRED_SET and DRVM_MAPPER_PREFERRED_GET are not defined by Windows Embedded CE. To use these messages, you must first define them in your code, as follows:


#define DRVM_MAPPER 0x2000
#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) 
#define DRVM_MAPPER_PREFERRED_SET (DRVM_MAPPER+22) 

2010年7月27日 星期二

Run Control Panel .cpl in command prompt

在WINCE下面,有 touchpanel 的control panel .cpl
因為不想重新 make image。
所以試著直接copy .cpl檔案 到機器上 run。

在Windows 底下, run control.exe 或 rundll32.exe shell32.dll,Control_RunDLL hdwwiz.cpl
control   --- 會叫出control panel window
control hdwwiz.cpl  會叫出相對應的control panel。

...而在WINCE 5 下面,打 control xxx.cpl 並不會呼叫相對應的 control panel,
只會叫出 control panel的 window。
還有有些 .cpl可以直接  double click 執行,有些不行。
還有 WINCE 底下沒有 rundll32.exe ....

DEBUGZONE

善用DEBUGZONE是很重要的
這樣程式的DEBUG輸出才會條理不容易亂
不過常常忘記DEBUGZONE 的用法
現在把它整理後寫在這邊


1.  Define DEBUGZONE



#define ZONE_INIT   DEBUGZONE(0)
#define ZONE_TEST   DEBUGZONE(1)
....


2. 定義 dpCurSettings

如以下


#ifdef DEBUG
DBGPARAM dpCurSettings = {
    TEXT("USBPRN"), {
    TEXT("Errors"),    TEXT("Warnings"),  TEXT("Init"),        TEXT("Trace"),
    TEXT("LPT_INIT"),  TEXT("LPT_READ"),  TEXT("LPT_WRITE"),   TEXT("LPT_IOCTL"),
    TEXT("USB_PARSE"), TEXT("USB_INIT"),  TEXT("USB_CONTROL"), TEXT("USB_BULK"),
    TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"),   TEXT("USBCLIENT")
    },
     0x0003 // ZONE_WRN|ZONE_ERR
};
#endif  // DEBUG


DBGPARAM 的第三個參數 zonemask 決定哪個DEBUG MSG打開

3. 使用 DEBUGREGISTER macro
在程式進入點的地方 call DEBUGREGISTER



BOOL
DllEntry(
   HANDLE hDllHandle,
   DWORD  dwReason,
   LPVOID lpreserved
   )
{
    UNREFERENCED_PARAMETER(hDllHandle);
    UNREFERENCED_PARAMETER(lpreserved);
    switch (dwReason) {

      case DLL_PROCESS_ATTACH:
           DEBUGREGISTER((HINSTANCE)hDllHandle);
  DisableThreadLibraryCalls((HMODULE) hDllHandle);
           break;

      case DLL_PROCESS_DETACH:
           break;

      default:
        break;
    }
    return TRUE;
}

一般程式(非DLL)的話,使用DEBUGREGISTER(NULL);

DEBUGREGISTER 只有在 DEBUG BUILD 中才有定義
而 RETAILREGISTERZONES 用在DEBUG 及 RETAIL BUILD 中

4. 完成

2010年7月23日 星期五

USB 研究

正在研究走 USB 還是走 SPDIF Optical 的音質哪一個比較好的時候
找到以下的網頁
http://msdn.microsoft.com/en-us/library/ff540194(VS.85).aspx

關於SPDIF Wikipedia 已經有詳細的說明
就我目前的了解
SPDIF 發送端的品質影響很大。因為接收端不能控制Data Rate。
高等的 Amplifier 可能會有特別的處理,如 clock lock, jitter free。


相對的,USB方面可能有兩種傳送方式。
第一種最常用的就是走 Isochronous Transfer。
這個可能會有問題,跟SPDIF比也不知道誰好誰壞。go
就我使用Digital Music SX的經驗,聽音樂時,再使用其它USB裝置如隨身碟傳大檔案,
有時會破音。(也或許是 creative 的driver沒有寫好)

第二種就是把Audio Stream 當成data 來傳,這樣就有 error correction 及 data resend。
好像 emu0404 是採用此種方式。

看了上面那篇文章,USB Protocol 對bandwidth allocation好像是 first-come first-serve
如此看來 USB Isoch Transfer 如果用得好的話,應該會比SPDIF好?
http://www.my-hiend.com/vbb/showthread.php?t=1965

http://forum.audiogon.com/cgi-bin/fr.pl?ddgtl&1179056058&openflup&5

請再search asynchronous USB

Ayre QB-9 vs Lavry DA11

2010年6月23日 星期三

GetPixel too slow & CreateDIBSection in WINCE

在做 Text 的Overlay 的時候,有人用 GetPixel。但是GetPixel 實在很慢,查了一下網上的資料,
可能的替代的方法有
use GetDIBits
use GetBitmapBits
use LockBits ( C#, .NET FrameWork )
use LockBits ( ImagingFactory )

但是,很可惜的,在WINCE底下,前兩個函式都沒有。
所以只能用.NET CF或 ImagingFactory。

或是使用 DDB ( via CBitmap in MFC) 或者 DIBSection.
很可惜的,在MFC中 也沒有提供 DIBSection Wrapper.
詳情請參照
http://www.codeproject.com/KB/graphics/dibsection.aspx

所以說 最簡單的方式 就是使用 CreateDIBSection
然後直接對 pvBits 做存取。
對bitmap 直接存取,改善之後的速度大幅提升。
真不知道 GetPixel內部是怎麼做的,那麼慢!

2010年5月25日 星期二

Link error: unresolved external symbol __imp_CommandBar_XXXXXX

在編譯 CE下的 format程式時,選擇 windows program, 並且編譯 沒有問題。
因為要link storeapi.lib
但是當我在lib path 中include C:\WINCE\PUBLIC\COMMON\OAK\LIB\MIPS\RETAIL 時
會出現以下錯



1>ceformatw.obj : error LNK2019: unresolved external symbol __imp_CommandBar_AddAdornments referenced in function "long __cdecl WndProc(struct HWND__ *,unsigned int,unsigned int,long)" (?WndProc@@YAJPAUHWND__@@IIJ@Z)
1>ceformatw.obj : error LNK2019: unresolved external symbol __imp_CommandBar_InsertMenubar referenced in function "long __cdecl WndProc(struct HWND__ *,unsigned int,unsigned int,long)" (?WndProc@@YAJPAUHWND__@@IIJ@Z)
1>ceformatw.obj : error LNK2019: unresolved external symbol __imp_CommandBar_Create referenced in function "long __cdecl WndProc(struct HWND__ *,unsigned int,unsigned int,long)" (?WndProc@@YAJPAUHWND__@@IIJ@Z)
1>ceformatw.obj : error LNK2019: unresolved external symbol __imp_CommandBar_Show referenced in function "int __cdecl InitInstance(struct HINSTANCE__ *,int)" (?InitInstance@@YAHPAUHINSTANCE__@@H@Z)

調查後發現 在Standard SDK下的 Commctrl.lib 和 PUBLIC\COMMON\OAK\LIB下的 Commctrl.lib 並不相同

SDK下的 Commctrl.lib 只是一個 dll import library
OAK\LIB 下的則為實際的 static library

但是Standard SDK 下的Commctrl.h 和 PUBLIC\COMMON\SDK\INC 下的 Commctrl.h
是一樣的。

在 commctrl.h

//
// Define API decoration for direct importing of DLL references.
//
#ifndef WINCOMMCTRLAPI
#if !defined(_COMCTL32_) && defined(_WIN32)
#define WINCOMMCTRLAPI DECLSPEC_IMPORT
#else
#define WINCOMMCTRLAPI
#endif
#endif // WINCOMMCTRLAPI



IN winnt.h
#if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC) || defined(_M_IA64)) && !defined(MIDL_PASS)
#define DECLSPEC_IMPORT __declspec(dllimport)
#else
#define DECLSPEC_IMPORT
#endif

所以 __imp_ 是Microsoft 為 dllimport 所加上的修飾詞

目前能想到的暫時解決方法(非完美)為
把Standard SDK 的Lib 也加入 在PUBLIC\COMMON\OAK\LIB 的前面





主要是 WINCOMMCTRLAPI 的定義
不應該跑到上面 DECLSPEC_IMPORT



2010年5月17日 星期一

Warning C4512 assignment operator could not be generated

The compiler will also generate an assignment operator function for a class that does not define one. This assignment operator is simply a member wise copy of the data members of an object. Because const data items cannot be modified after initialization, if the class contains a const item, the default assignment operator would not work.




You can resolve the C4512 warning for this code in one of three ways:



•Explicitly define an assignment operator for the class.



•Remove const from the data item in the class.



•Use the #pragma warning statement to suppress the warning.


這個Warning (Level 4)是因為編譯器會自動產生 default assignment operator
但是如果 class 裡面有 const data member,  因為 const data member 在 initialize 之後
就無法改變( assign )其值,所以沒有辦法產生default assignment operator。