精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>C/C++>>未整理到对应目录的文章>>新版本钢琴模拟器来了

主题:新版本钢琴模拟器来了
发信人: lzzzl(lzzzl)
整理人: yangcs(2004-10-14 14:22:35), 站内信件
支持手工弹和自动演奏了,多线程有小BUG,未处理好

.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
include \masm32\include\winmm.inc
include \MASM32\INCLUDE\masm32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\winmm.lib
includelib \MASM32\LIB\masm32.lib

.data
  ClassName db "Line5PlayerClass",0
  AppName   db "五线谱播放播放器 [版本:1.0 作者:周仲俐 2004.02.03]",0
  szText      db '高低音{j,k,l,s,d,f,^,v} 速度{<-,->} 音符{1,2,3,4,5,6,7}',0h

.data?
  hInstance     HINSTANCE ?   ;程序实例句柄
  hdc           HDC       ?   ;midi句柄
  midiScale     DWORD     ?   ;音符{1,2,3,4,5,6,7}
  midiTone      DWORD     ?   ;音调{s,d,f}={低,中,高}
  midiBase      DWORD     ?   ;基调{j,k,l}={低,中,高}
  midiFlag      BYTE      ?
  midiPlayFlag  BYTE      ?
  delay         DWORD     ?
  lastmidi      DWORD     ?
  
.code
  start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke WinMain, hInstance,NULL,NULL,SW_SHOWDEFAULT
    invoke ExitProcess,eax

    ; ########################################################################
    ; 读取文件内容
    
    ReadFileIn proc lpszDiskFile:DWORD
    
      LOCAL hFile :DWORD ;文件句柄
      LOCAL hMem$ :DWORD ;BSTR指针
      LOCAL ln    :DWORD ;文件长度
      LOCAL br    :DWORD ;实际读取到的字节数
      LOCAL brw    :DWORD ;实际读取到的字节数
      local skipping  :DWORD      ;是否正在跳过注释
      local last  :DWORD          ;前一字符
      local  @playf:WORD
      local tc  :DWORD
      local skipnext  :DWORD
      local txtBuffer[1024]:BYTE
        
      invoke CreateFile,lpszDiskFile,GENERIC_READ,FILE_SHARE_READ,
                         NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
      mov hFile, eax
  
      invoke GetFileSize,hFile,NULL
      mov ln, eax
  
      .if ln > 32767
        invoke CloseHandle,hFile
        ;szText tooBig,"乐谱文件太大,系统无法处理!"
        ;invoke MessageBox,hWnd,ADDR tooBig,"dd",MB_OK
        xor eax, eax
  
        ret
      .endif

      mov skipping,0
      mov last,0
      mov skipnext,0h
      
      .while TRUE
        invoke ReadFile,hFile,addr brw,1,ADDR br,NULL
        and brw,0ffh            ;也许不必
        
        .if br==0
          .break
        .elseif brw==3bh        ;";",判断注释是否开始
          mov skipping,1
        .elseif skipping==1
          .if brw==0ah
          .elseif brw==0dh
          .else
            .if last==0ah
              mov skipping,0    ;注释行结束
            .elseif last==0dh
              mov skipping,0
            .else
            .endif
          .endif
        .else
        .endif

        .if skipping==1
        .elseif brw==0ah
        .elseif brw==0dh
        .else
          ;invoke MessageBox,NULL,addr brw,addr AppName,MB_OK
          
          mov @playf,0h
          push brw
          pop midiScale
         
          ;==================== 基调 =======================
          .if midiScale==6ch     ;l
            mov midiBase,12     ;24,12,0
          .elseif midiScale==6bh     ;k
            mov midiBase,0     ;12,0,-12(默认)
          .elseif midiScale==6ah     ;j
            mov midiBase,-12     ;0,-12,-24
          .else
          .endif
          
          ;==================== 音调 =======================
          .if midiScale==60h              ;“`”,高,相对+12
            add midiTone,12
          .elseif midiScale==64h          ;中
          .elseif midiScale==2eh          ;“.”,低,相对-12
            add midiTone,-12
          .else
          .endif
          
          ;==================== 全音 =======================
          .if midiScale==31h  ;1
            mov midiScale,3ch       ;注意可能又误用于后面的判断
            mov @playf,1
          .elseif midiScale==32h  ;2
            mov midiScale,3eh
            mov @playf,1
          .elseif midiScale==33h  ;3
            mov midiScale,40h

            mov @playf,1
          .elseif midiScale==34h  ;4
            mov midiScale,41h
            mov @playf,1
          .elseif midiScale==35h  ;5
            mov midiScale,43h
            mov @playf,1
          .elseif midiScale==36h  ;6
            mov midiScale,45h
            mov @playf,1
          .elseif midiScale==37h  ;7
            mov midiScale,47h
            mov @playf,1
          .elseif midiScale==7eh    ;“~”,滑音
            mov skipnext,1
          .else
          .endif
          
          ;===================== 半音 ======================
          .if  midiScale==71h         ;q
            mov midiScale,3dh
            mov @playf,1
          .elseif  midiScale==77h     ;w
            mov midiScale,3fh
            mov @playf,1
          .elseif  midiScale==65h     ;e
            mov midiScale,42h
            mov @playf,1
          .elseif  midiScale==72h     ;r
            mov midiScale,44h
            mov @playf,1
          .elseif  midiScale==74h     ;t
            mov midiScale,46h
            mov @playf,1
          .endif

          ;===================== 发声 ======================
          .if @playf==1
            mov eax,midiTone
            
            add midiScale,eax       ;调整音调
            mov cl,8
            shl midiScale,cl       ;左移一个字节
            and midiScale,0ff00h
            add midiScale,680090h       ;使之成为乐音

            mov eax,midiScale
            .if skipnext==0
            .elseif lastmidi==eax
            .else
              mov skipnext,0
            .endif
            
            .if skipnext==1
              mov skipnext,0
            .else
              invoke midiOutShortMsg,hdc,midiScale
              
              ;复位
              mov  midiPlayFlag,0h
              push midiBase
              pop midiTone
            .endif
    
            invoke GetTickCount   ; get a time reference
            mov tc, eax
            mov edx,delay
            add tc, edx;250  ; add 1/4 seconds to the time reference
            ; ------------------------------------------------
            ; loop until Tick count catches up with added time
            ; ------------------------------------------------
            @@:
            invoke GetTickCount
            .if tc > eax
              jmp @B
            .endif

            push midiScale
            pop lastmidi
          .elseif skipping==1
          .elseif brw==2dh
            invoke GetTickCount   ; get a time reference
            mov tc, eax
            add tc, 250  ; add 1/4 seconds to the time reference
            ; ------------------------------------------------
            ; loop until Tick count catches up with added time
            ; ------------------------------------------------
            @@:
            invoke GetTickCount
            .if tc > eax
              jmp @B
            .endif
          .endif
        .endif

        .if skipping==1
        .elseif brw==28h
          shr delay,1
        .elseif brw==29h
          shl delay,1
        .else
        .endif
        
        push brw
        pop last
      .endw

      invoke CloseHandle,hFile
  
      ret
    ReadFileIn endp

    play proc Param:DWORD
      LOCAL clBuffer[128]:BYTE
      
      invoke GetCL,1, ADDR clBuffer
      invoke ReadFileIn,ADDR clBuffer
    play endp
    
    ;入口(主)函数
    WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
      LOCAL wc:WNDCLASSEX               ;(本地)局部变量
      LOCAL msg:MSG
      LOCAL hwnd:HWND
      local x:DWORD
      local y:DWORD
      local w:DWORD
      local h:DWORD
      local txtBuffer[1024]:BYTE
            
      ;窗口用户界面
      mov   wc.cbSize,SIZEOF WNDCLASSEX
      mov   wc.style, CS_HREDRAW or CS_VREDRAW
      mov   wc.lpfnWndProc, OFFSET WndProc
      mov   wc.cbClsExtra,NULL
      mov   wc.cbWndExtra,NULL
      push  hInst           ;变量->变量,比用mov指令及寄存器简单
      pop   wc.hInstance
      mov   wc.hbrBackground,COLOR_WINDOW+1
      mov   wc.lpszMenuName,NULL
      mov   wc.lpszClassName,OFFSET ClassName
      invoke LoadIcon,NULL,IDI_APPLICATION
      mov   wc.hIcon,eax
      mov   wc.hIconSm,eax
      invoke LoadCursor,NULL,IDC_ARROW
      mov   wc.hCursor,eax
      
      mov w,420
      mov x,800
      mov eax,w
      sub x,eax
      shr x,1
      
      mov h,180
      mov y,600
      mov eax,h
      sub y,eax
      shr y,1
      
      invoke RegisterClassEx, addr wc   ;地址传递
      INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
        WS_OVERLAPPEDWINDOW,x,\
        y,w,h,NULL,NULL,\
        hInst,NULL
      mov   hwnd,eax
      INVOKE ShowWindow, hwnd,SW_SHOWNORMAL
      INVOKE UpdateWindow, hwnd
      
      ;音效设备
      invoke midiOutOpen,ADDR hdc,-1,NULL,NULL,NULL
      
      ;全局变量
      mov   midiBase,0h
      mov   midiTone,0h
      mov   midiPlayFlag,1h
      mov   delay,500
      
      ;消息循环
      .WHILE TRUE
        INVOKE GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        INVOKE TranslateMessage, ADDR msg
        INVOKE DispatchMessage, ADDR msg
      .ENDW        
      mov     eax,msg.wParam
      
      ret
    WinMain endp
    
    ;消息处理(回调)函数
    WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
      local  @stPs:PAINTSTRUCT      ;前缀@何意?
      local  @stRect:RECT
      local  @hDc
      local  @playf:WORD
      LOCAL clBuffer[128]:BYTE
      local    id:DWORD
      
      .IF uMsg==WM_DESTROY
        invoke midiOutClose,hdc       ;如何句柄无效会怎样?
        invoke PostQuitMessage,NULL
      .ELSEIF uMsg ==WM_PAINT
        invoke BeginPaint,hWnd,addr @stPs
        mov       @hDc,eax
        invoke GetClientRect,hWnd,addr @stRect
        invoke DrawText,@hDc,addr szText,-1,\
        addr @stRect,\
        DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,hWnd,addr @stPs
      .ELSEIF uMsg==WM_CHAR               ;好象相当于KeyPress,KeyUp时不产生此事件
        push wParam
        pop midiScale
        ;===============ESC键退出================
        .if midiScale==WM_DEVMODECHANGE   ;1bh(27)
          invoke midiOutClose,hdc
          invoke PostQuitMessage,NULL
        .elseif midiScale==70h             ;"p" to play
            ; start compressing in a thread
            invoke CreateThread,NULL,NULL,OFFSET play,NULL,0,ADDR id
            invoke CloseHandle,addr id
        .else
        .ENDIF
        
        mov @playf,0h
        push wParam
        pop midiScale
        
        ;===============ESC键退出================
        .if midiScale==WM_DEVMODECHANGE   ;1bh(27)
          invoke midiOutClose,hdc
          invoke PostQuitMessage,NULL
        .ENDIF
        
        ;==================== 基调 =======================
        .if midiScale==6ch     ;l
          mov midiBase,12     ;24,12,0
        .elseif midiScale==6bh     ;k
          mov midiBase,0     ;12,0,-12(默认)
        .elseif midiScale==6ah     ;j
          mov midiBase,-12     ;0,-12,-24
        .else
        .endif
        
        ;==================== 音调 =======================
        ;按下高音键后不能松手,一松手系统就恢复为正常音调。低音键类似
        .if midiScale==66h              ;f(高,相对+12)
          push midiBase
          pop midiTone
          add midiTone,12
        .elseif midiScale==64h          ;d(中)
          push midiBase
          pop midiTone
          ;add midiTone,0
        .elseif midiScale==73h          ;s(低,相对-12)
          push midiBase
          pop midiTone
          add midiTone,-12
        .else
        .endif
        
        ;==================== 全音 =======================
        .if midiScale==31h        ;1
          mov midiScale,3ch
          mov @playf,1
        .elseif midiScale==32h  ;2
          mov midiScale,3eh
          mov @playf,1
        .elseif midiScale==33h  ;3
          mov midiScale,40h
          mov @playf,1
        .elseif midiScale==34h  ;4
          mov midiScale,41h
          mov @playf,1
        .elseif midiScale==35h  ;5
          mov midiScale,43h
          mov @playf,1
        .elseif midiScale==36h  ;6
          mov midiScale,45h
          mov @playf,1
        .elseif midiScale==37h  ;7
          mov midiScale,47h
          mov @playf,1
        .endif
        
        ;===================== 半音 ======================
        .if  midiScale==71h         ;q
          mov midiScale,3dh
          mov @playf,1
        .elseif  midiScale==77h     ;w
          mov midiScale,3fh
          mov @playf,1
        .elseif  midiScale==65h     ;e
          mov midiScale,42h
          mov @playf,1
        .elseif  midiScale==72h     ;r
          mov midiScale,44h
          mov @playf,1
        .elseif  midiScale==74h     ;t
          mov midiScale,46h
          mov @playf,1
        .endif
        
        ;===================== 发声 ======================
        .if @playf==1
          mov eax,midiTone
          add midiScale,eax       ;调整音调
          mov cl,8
          shl midiScale,cl       ;左移一个字节
          and midiScale,0ff00h
          add midiScale,680090h ;使之成为乐音
          
          .if midiPlayFlag==1h
            invoke midiOutShortMsg,hdc,midiScale
            
            ;复位
            mov  midiPlayFlag,0h
          .endif
        .endif
      .ELSEIF uMsg==WM_KEYUP
        mov midiPlayFlag,1h
        
        ;只要松开高音或低音键,就恢复为正常
        .if wParam==46h           ;f
          push midiBase
          pop midiTone
        .elseif wParam==53h ;s
          push midiBase
          pop midiTone
        .else
        .endif

        ;用于自动演奏
        .if wParam==25h         ;<--
shl delay,1
.elseif wParam==26h ;^
add midiBase,12
.elseif wParam==27h ;-->
          shr delay,1
        .elseif wParam==28h     ;V
          add midiBase,-12
        .else
        .endif
      .ELSE
        ;转发不能处理的系统消息
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
      .ENDIF
      xor    eax,eax     ;why not mov eax,0h?
      
      ret
    WndProc endp
  end start



----
不想计较得失,却总在计较得失     

[关闭][返回]