이것은 1999년 하이텔 게임 제작 동호회에 올렸던 강좌 4회분 입니다.

                                              바이너리 데이터

#####################################################

아래의 글은 제가 Direct X 6.0 에 있는 DirectMusic 에 대한 Document를 그냥 끄
적 끄적 개요만 추려서 번역해본 겁니다.

만약 MCI를 사용하여 MIDI를 배경음악으로 연주 하는데 질리셨던 분들은 한 번 읽
어 보시면 약간 도움이 되실 것이라고 봅니다.

그럼.. SMgal...


<< DirectMusic Document >>

▶ DirectMusic 이란 ?

* DirectMusic은 DirectX의 음악 컴포넌트 API 이다.
* DirectSound와는 달리 메시지 기반의 음악 데이터를 가지고 작업한다.
* 게다가 MIDI 와 DLS (Downloadable sounds) 표준을 지원한다.
* 역시 다른 DirectX 의 컴포넌트와 마찬가지로 COM 을 기반으로 한다.
* DirectMusic의 모든 기능은 윈도우 95, 윈도우 98, 윈도우 NT 5.0 에서 모두 사
  용 이 가능하지만, 윈도우 95 의 DLS 에 대한 하드웨어 지원은 되지 않는다.


▶ 왜 DirectMusic을 사용하는가 ?


1. DirectMusic API 는 플랫폼에 관한 기본적인 요구사항을 대변한다.

  * downloadable sounds를 사용할 때 애플리케이션은 모든 기종에서 같은 카운트
    를 가지고, 원래 디자인하고자 했던 속도에 동기화 시킨다.
  * MIDI 가 생성해 내는 음악의 PlayBack은 2ms 이다.
  * 하드웨어 가속이 지원하지 않는 기기에서는 최소 능률로 수행된다.
  * DirectMusic은 저 수준의 특성을 제어함에 있어서 제한을 두지 않으므로 확장
    성이 용이하다.

2. 게다가 DirectMusic은 쉬운  프로그램 개발과 사용자의 경험을  풍부하게 하기
위한 중요한 특징들을 제공해 준다.

  * DirectMusic은 표준 MIDI 파일과 interactive music segment와 서드 파티 기
    술을 동등하게 지원한다.
  * 동시에 여러 개의 음악을 연주하는 기능이 있다.
  * DirectMusic은 16 개의 MIDI 채널의 제한을 뛰어넘어, 어떤 수의 음색이라도
    가상적으로 동시에 연주할 수 있다.
  * DLS 의 악기를 자동으로 관리한다.
  * DirectMusic의 연주 엔진은 동적 사운드트랙을 생성하여 사용되어질 수 있고
    동적인 상호 playback이 가능하다.

▶ DirectMusic의 구조 (생략)

  * Core and Performance Layers
  * Interfaces and Objects
  * DirectMusic Object Types
  * DirectMusic Messages
  * Downloadable Sounds
  * Microsoft Software Synthesizer

▶ DirectMusic Tutorials

1. MIDI 파일 연주

  1) COM의 초기화

     DirectMusic의호출을 하기 전에 다음과 같은 COM의 초기화가 필요하다.

     if (FAILED(CoInitialize(NULL)))
     {
       // 애플리케이션 종료
     } // 그렇지 않다면 계속


  2) 퍼포먼스 생성

     DirectMusic 애플리케이션의 중심 객체가 '퍼포먼스'이다. 그리고 그것은 세
     그먼트의 playback을 관리한다.
     이것은 COM의 CoCreateInstance 함수를 사용하여 생성된다.

     다음은 예제 함수이다.

     IDirectMusicPerformance* CreatePerformance(void)
     {
         IDirectMusicPerformance* pPerf;
 
         if (FAILED(CoCreateInstance(
                 CLSID_DirectMusicPerformance,
                 NULL,
                 CLSCTX_INPROC,
                 IID_IDirectMusicPerformance,
                 (void**)&pPerf
             )))
         {
             pPerf = NULL;
         }
         return pPerf;
     }

     그리고 다음과 같이 사용하여 전역 퍼포먼스 포인터를 초기화시키는 함수를
     사용할 수 있다.

     IDirectMusicPerformance* g_pPerf = CreatePerformance();

     퍼포먼스가 생성되고 나면, IDirectMusicPerformance::Init 메소드를 호출하
     여 초기화시킬 필요가 있다.
     이 메소드는 디폴트 포트를 관리하는 DirectMusic 객체를 생성한다.
     사용자는 직접 이 메소드에 접근 할 필요가 없기 때문에 Init 에는 NULL 을
     넘겨주면 된다.

     g_pPerf->Init(NULL);

     이제는 퍼포먼스에 포트를 추가하는 것이 필요하다.  

     IDirectMusicPerformance::AddPort 메소드에 NULL 파라메터를 넘겨줘서 호출
     하게 되면 자동적으로 하나의 채널 그룹을 가진 마이크로소프트 소프트웨어
     신디사이저를 추가한다.
     그리고 이것은 0-15까지의 채널을 지정한다.

     다음의 예제에서는 퍼포먼스가 성공적으로 초기화되었을 때 TRUE를 넘겨주는
     구조로 되어 있다.

     BOOL InitializeSynth(IDirectMusicPerformance* pPerf)
     {
         BOOL fReturn = FALSE;
         if (SUCCEEDED( pPerf->AddPort(NULL)))
         {
             fReturn = TRUE;
         }
         return fReturn;
     }


  3) 로더의 생성

     디스크로부터 어떠한 객체를 로드하기 위해서 DirectMusicLoader 객체가 우
     선적으로 생성될 필요가 있다.
     다음의 함수에서 보이듯이, 이것은 다른 COM 객체들과 같다.

     IDirectMusicLoader* CreateLoader(void)
     {
         IDirectMusicLoader* pLoader;
 
         if (FAILED(CoCreateInstance(
                 CLSID_DirectMusicLoader,
                 NULL,
                 CLSCTX_INPROC,
                 IID_IDirectMusicLoader,
                 (void**)&pLoader
             )))
         {
             pLoader = NULL;
         }
         return pLoader;
     }

     그리고 다음과 같이 사용하여 전역 변수를 초기화시키는 함수를 사용한다.

     IDirectMusicLoader* g_pLoader = CreateLoader();


  4) MIDI 파일의 로딩

     LoadSegment는 마지막 단계에서 생성되어지며 세그먼트 객체를 로드하는데
     사용되는 DirectMusicLoader에 대한 포인터를 가진다.
     전역 세그먼트 포인터를 초기화하는데 그것이 사용되고, 세그먼트를 연주하
     기 위해서 나중에 필요하게 될 것이다.

     if (g_pLoader)
     {
         IDirectMusicSegment* g_pMIDIseg = LoadSegment(g_pLoader);
     }

     약간의 지역 변수를 초기화한 후에, 그 함수는 로더의 현재 찾기 디렉토리를
     애플리케이션이 현재 작업하고 있는 디렉토리로 바꾸어 준다.

     IDirectMusicSegment* LoadSegment(IDirectMusicLoader* pLoader)
     {
         DMUS_OBJECTDESC ObjDesc;
         IDirectMusicObject* pObjectSeg = NULL;
         IDirectMusicSegment* pSegment = NULL;
         char szDir[_MAX_PATH];
         WCHAR wszDir[_MAX_PATH];

 
         if( _getcwd( szDir, _MAX_PATH ) == NULL )
         {
             // 에러가 발생.. NULL 을 리턴 한다.
             return NULL;
         }
         MULTI_TO_WIDE(wszDir, szDir);
         pLoader->SetSearchDirectory(GUID_DirectMusicAllTypes,
                 wszDir, FALSE );
 
     DMUS_OBJECTDESC 구조로 로드되어지는 객체를 묘사하자면...

         ObjDesc.guidClass = CLSID_DirectMusicSegment;
         ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
         wcscpy( ObjDesc.wszFileName, "Tune.mid" );
         ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME;
 
   이제 객체를 로드한다. IDirectMusicSegment 인터페이스에 대한 쿼리를 거치
     고 다시 해제된다.
     로딩하는 객체 역시 세그먼트를 생성하고, 트랙의 초기화하며, 그 외의
     playback에 대한 MIDI 데이터를 준비하기 위한 모든 것를 해야한다는 점에
     주의하자.

         pLoader->GetObject( &ObjDesc,
                 IID_IDirectMusicSegment, (void**) &pSegment );
         return pSegment;
 
     } // LoadSegment() 의 끝


5) MIDI 파일의 연주

     모든 준비 작업은 이전의 과정에서 모두 끝냈다. 그래서 음악을 연주하는 부
     분은 간단하게 다음과 같다.

     IDirectMusicSegmentState* g_pSegState;
     g_pPerf->PlaySegment(g_pMIDISeg, 0, 0, &g_pSegState);

     IDirectMusicPerformance::PlaySegment 호출 시의 파라메터는 다음과 같다.

     * 이전 과정에서 만들었던 세그먼트
     * 타이밍 플래그의 집합과 시작 시간
     * 세그먼트 스테이트 객체의 포인터 주소. ( 세그먼트에 관한 정보를 가져오
       기 위해 리턴된 포인터를 사용할 수 있다. 만약 세그먼트 스테이트가 필요
       없다면 NULL로한다. )

     만약 한 번 이상으로 파일을 연주하려면 PlaySegment를 호출하기 이전에
     IDirectMusicSegment::SetRepeats 메소드를 먼저 호출해야 한다.

     또한 연주가 끝나기 전이나 반복 연주를 멈추게 하려면 IDirectMusicPerform
     ance::Stop 메소드를 호출하면 된다.
     이 메소드를 가장 간단한 방법으로 사용하면, 현재 연주되는 모든 음악을 바
     로 멈추게 한다.

     g_pPerf->Stop(NULL, NULL, 0, 0)

     하지만, 단지 하나의 세그먼트 또는 그 세그먼트의 하나의 인스턴스를 멈추
     게 하기 위해서는 현재의 세그먼트 또는 세그먼트 스테이트에 대한 포인터를
     넘겨주면 된다.

6) DirectMusic 종료

     종료하기 이전에, 프로그램은 퍼포먼스를 중단시켜야 하고 생성된 모든 객체
     들을 해제하여야 하며 COM 레퍼런스 해제를 해야한다.
     ( CoInitialize를 호출한 만큼 대응하는 CoUninitialize 가 있어야 한다는
       것을 명심하자. )

     다음의 함수는 필요한 제거 과정을 수행한다.

     HRESULT FreeDirectMusic()
     {
         // 세그먼트 해제
         g_pMIDISeg->Release();

         // 연주되고 있는 음악이 있다면 모두 멈춘다.
         g_pPerf->Stop( NULL, NULL, 0, 0 );

         // 퍼포먼스 객체를 닫고 해제한다.
         g_pPerf->CloseDown();
         g_pPerf->Release();

         // 로더 객체를 해제한다.
         g_pLoader->Release();

         // COM을 해제한다.
         CoUninitialize();

         return S_OK;
     }

 

#####################################################

 ------------------------------------------------------------------------      
### 델파이로 하는 Direct X - Direct Music (1/3)

    1. Direct Music 의 개요
    ~~~~~~~~~~~~~~~~~~~~~~~
    2. Direct Music 초기화와 MIDI 읽기
    3. Direct Music 출력

------------------------------------------------------------------------
                                    작성자 : 안영기 ( HiTEL ID : SMgal )

------------------------------------------------------------------------

(1) Direct Music 이란 무엇인가 ?

아래의 글은 제가 Direct X 6.0 에 있는 Direct Music 에 대한 Document를 개
요만 추려서 번역해 본 것이다. 그리고 그 안의 C 소스는 다시 파스칼로 번역
했다. ( 제일 처음 Direct Music 이 소개되었던 당시에 쓴 글이라 6.0 기준임
)

<< Direct Music Document >>


▶ Direct Music 이란 ?

  - Direct Music 은 DirectX 의 음악 컴포넌트 API 이다.
  - Direct Sound 와는 달리 메시지 기반의 음악 데이터를 가지고 작업한다.
  - 게다가 MIDI 와 DLS (Downloadable sounds) 표준을 지원한다.
  - 역시 다른 DirectX 의 컴포넌트와 마찬가지로 COM 을 기반으로 한다.
  - Direct Music 의  모든 기능은  윈도우즈 95,  윈도우즈 98, 윈도우즈 NT
    5.0 에서 모두 사용이 가능하지만, 윈도우즈 95 의 DLS 에 대한 하드웨어  
    지원은 되지 않는다.


▶ 왜 Direct Music을 사용하는가 ?

1. Direct Music API 는 플랫폼에 관한 기본적인 요구사항을 대변한다.

  - downloadable sounds를 사용할 때 애플리케이션은 모든 기종에서 같은 카  
    운트를 가지고, 원래 디자인하고자 했던 속도에 동기화 시킨다.
  - MIDI 가 생성해 내는 음악의 Play Back 은 2ms 이다.
  - 하드웨어 가속이 지원하지 않는 기기에서는 최소 능률로 수행된다.
  - Direct Music 은  저 수준의 특성을 제어함에 있어서 제한을 두지 않으므
    로 확장성이 용이하다.

2. 게다가 Direct Music 은 쉬운  프로그램  개발과 사용자의 경험을  풍부하
   게 하기 위한 중요한 특징들을 제공해 준다.

  - Direct Music은 표준 MIDI 파일과 interactive music segment 와 서드 파
    티 기술을 동등하게 지원한다.
  - 동시에 여러 개의 음악을 연주하는 기능이 있다.
  - Direct Music은 16 개의 MIDI 채널의 제한을 뛰어넘어, 어떤 수의 음색이  
    라도 가상적으로 동시에 연주할 수 있다.
  - DLS 의 악기를 자동으로 관리한다.
  - Direct Music 의 연주 엔진은 동적 사운드트랙을 생성하여 사용되어질 수  
    있고 동적인 상호 playback 이 가능하다.


▶ DirectMusic Tutorials

1. MIDI 파일 연주

  1) COM의 초기화

     Direct Music의 호출을 하기 전에 다음과 같은 COM의 초기화가 필요하다.

     if CoInitialize(nil) <> S_OK then begin
        // 애플리케이션 종료
     end;
        // 그렇지 않다면 계속


  2) 퍼포먼스 생성

     Direct Music 애플리케이션의 중심 객체가 '퍼포먼스'이다. 그리고 그것
     은 세그먼트의 play back을 관리한다.
     이것은 COM의 CoCreateInstance 함수를 사용하여 생성된다.

     다음은 예제 함수이다.

     function CreatePerformance : IDirectMusicPerformance;
     var
        pPerf : IDirectMusicPerformance;
     begin
         if CoCreateInstance(
                 CLSID_DirectMusicPerformance,
                 nil,
                 CLSCTX_INPROC,
                 IID_IDirectMusicPerformance,
                 pPerf
             ) <> S_OK then begin
             pPerf := nil;
         end;
         CreatePerformance := pPerf;
     end;

     그리고 다음과 같이 사용하여  전역 퍼포먼스 포인터를 초기화시키는 함
     수를 사용할 수 있다.
    
     var
        g_pPerf : IDirectMusicPerformance;
     ........
     g_pPerf := CreatePerformance;

     퍼포먼스가 생성되고 나면, IDirectMusicPerformance.Init 메쏘드를  호
     출하여 초기화시킬 필요가 있다.  이 메쏘드는 디폴트 포트를  관리하는
     Direct Music 객체를 생성한다. 사용자는 직접 이 메쏘드에 접근 할  필
     요가 없기 때문에 Init 에는 nilL 을 넘겨주면 된다.

     g_pPerf.Init(nil);

     이제는 퍼포먼스에 포트를 추가하는 것이 필요하다.  

     IDirectMusicPerformance.AddPort 메쏘드에 nil 파라메터를 넘겨줘서 호
     출하게 되면 자동적으로 하나의 채널 그룹을 가진 마이크로소프트  소프
     트웨어 신디사이저를 추가한다. 그리고 이것은 0-15까지의 채널을  지정
     한다.

     다음의 예제에서는 퍼포먼스가 성공적으로 초기화되었을 때 TRUE를 넘겨
     주는 구조로 되어 있다.

     function InitializeSynth(pPerf : IDirectMusicPerformance)
                                                              : Boolean;
     var
        fReturn : Boolean;
     begin
         fReturn := FALSE;
         if pPerf->AddPort(nil) = S_OK then begin
             fReturn := TRUE;
         end;
         InitializeSynth := fReturn;
     end;

  3) 로더의 생성

     디스크로부터 어떠한 객체를 로드하기 위해서 DirectMusicLoader 객체가
     우선적으로 생성될 필요가 있다.
     다음의 함수에서 보이듯이, 이것은 다른 COM 객체들과 같다.

     function CreateLoader : IDirectMusicLoader;
     var
         pLoader : IDirectMusicLoader;
     begin
         if CoCreateInstance(
                 CLSID_DirectMusicLoader,
                 nil,
                 CLSCTX_INPROC,
                 IID_IDirectMusicLoader,
                 pLoader
             ) <> S_OK then begin
             pLoader := nil;
         end;
         CreateLoader := pLoader;
     end;

     그리고 다음과 같이 사용하여  전역 변수를 초기화시키는 함수를 사용한
     다.

     var     
        g_pLoader : IDirectMusicLoader;
     ........
        g_pLoader := CreateLoader;


  4) MIDI 파일의 로딩

     LoadSegment는 마지막 단계에서 생성되어지며 세그먼트 객체를 로드하는
     데 사용되는 DirectMusicLoader에 대한 포인터를 가진다.
     전역 세그먼트 포인터를 초기화하는데 그것이 사용되고,  세그먼트를 연  
     주하기 위해서 나중에 필요하게 될 것이다.

     var
        g_pMIDIseg : IDirectMusicSegment;
     ..........
     if Assigned(g_pLoader) then begin
        g_pMIDIseg := LoadSegment(g_pLoader);
     end;

     약간의 지역 변수를 초기화한 후에, 그 함수는 로더의 현재 찾기 디렉토
     리를 애플리케이션이 현재 작업하고 있는 디렉토리로 바꾸어 준다.

     function LoadSegment(pLoader : IDirectMusicLoader)
                                                  : IDirectMusicSegment;
     var
         ObjDesc    : TDMUS_OBJECTDESC;
         pObjectSeg : IDirectMusicObject;
         pSegment   : IDirectMusicSegment;
         szDir      : array [0.._MAX_PATH] of CHAR;
         wszDir     : array [0.._MAX_PATH] of WCHAR;
     begin
         pObjectSeg := nil;
         pSegment   := nil;
 
         if _getcwd(szDir,_MAX_PATH) = nil then begin
         (* 역자주. _getcwd() 는 C 에서 path를 구하는 표준 함수이다.
            델파이에서는  ExtractFilePath() 함수를 사용하면  같은 결과를
            얻는다 *)
             // 에러가 발생.. NULL 을 리턴 한다.
             LoadSegment := nil;
             Exit;
         end;
         MULTI_TO_WIDE(wszDir,szDir);
         pLoader.SetSearchDirectory(GUID_DirectMusicAllTypes,
                                                         wszDir,FALSE );
 
     DMUS_OBJECTDESC 구조로 로드되어지는 객체를 묘사하자면...

         ObjDesc.guidClass   = CLSID_DirectMusicSegment;
         ObjDesc.dwSize      = sizeof(TDMUS_OBJECTDESC);
         wcscpy(ObjDesc.wszFileName,'Tune.mid');
         ObjDesc.dwValidData = DMUS_OBJ_CLASSor or DMUS_OBJ_FILENAME;
 
     이제 객체를 로드한다.  IDirectMusicSegment 인터페이스에 대한 쿼리를
     거치고 다시 해제된다. 로딩하는 객체 역시 세그먼트를 생성하고,  트랙
     의 초기화하며, 그 외의 playback에 대한 MIDI 데이터를 준비하기  위한
     모든 것을 해야한다는 점에 주의하자.

         pLoader.GetObject(ObjDesc,IID_IDirectMusicSegment,pSegment );
         LoadSegment := pSegment;
 
     } // LoadSegment() 의 끝

  5) MIDI 파일의 연주

     모든 준비 작업은 이전의 과정에서 모두 끝냈다.  그래서 음악을 연주하
     는 부분은 간단하게 다음과 같다.

     var
        g_pSegState : IDirectMusicSegmentState;
     begin
        g_pPerf.PlaySegment(g_pMIDISeg,0,0,g_pSegState);

     IDirectMusicPerformance.PlaySegment() 호출 시의   파라메터는 다음과
     같다.

     * 이전 과정에서 만들었던 세그먼트
     * 타이밍 플래그의 집합과 시작 시간
     * 세그먼트 스테이트 객체의 포인터 주소. ( 세그먼트에 관한 정보를 가
       져오기 위해 리턴된 포인터를 사용할 수 있다.  만약 세그먼트 스테이
       트가 필요 없다면 nil 로한다. )

     만약 한 번 이상으로 파일을 연주하려면 PlaySegment를 호출하기 이전에
     IDirectMusicSegment.SetRepeats() 메쏘드를 먼저 호출해야 한다.

     또한   연주가    끝나기   전이나   반복   연주를    멈추게   하려면
     DirectMusicPerformance.Stop() 메쏘드를 호출하면 된다.
     이 메쏘드를 가장 간단한 방법으로 사용하면,  현재 연주되는 모든 음악
     을 바로 멈추게 한다.

     g_pPerf.Stop(nil,nil,0,0);

     하지만,  단지 하나의 세그먼트 또는  그 세그먼트의 하나의 인스턴스를
     멈추게 하기 위해서는 현재의 세그먼트 또는 세그먼트 스테이트에  대한
     포인터를 넘겨주면 된다.

6) DirectMusic 종료

     종료하기 이전에, 프로그램은 퍼포먼스를 중단시켜야 하고  생성된 모든
     객체들을 해제하여야 하며 COM 레퍼런스 해제를 해야한다.
     ( CoInitialize를 호출한 만큼 대응하는 CoUninitialize 가 있어야 한다
       는 것을 명심하자. )

     다음의 함수는 필요한 제거 과정을 수행한다.

     funtion FreeDirectMusic : HRESULT;
     begin
         // 세그먼트 해제
         g_pMIDISeg.Release;

         // 연주되고 있는 음악이 있다면 모두 멈춘다.
         g_pPerf.Stop(nil,nil,0,0);

         // 퍼포먼스 객체를 닫고 해제한다.
         g_pPerf.CloseDown;
         g_pPerf.Release;

         // 로더 객체를 해제한다.
         g_pLoader.Release;

         // COM을 해제한다.
         CoUninitialize;

         FreeDirectMusic := S_OK;
     end;

------------------------------------------------------------------------

(2) DLS 란 무엇인가 ?

예전에는 대부분의 컴퓨터 음악은 전혀 다른 두 가지의 방법 중의 하나로  만
들어 졌다. 그 두 가지는 각각 장점과 단점이 있었는데...

웨이브 형식은 디지탈 샘플링으로부터 재생산되어지며 전형적으로 *.wav 라는
파일이나 CD 트랙의 음악 같은 것이다.  디지탈 샘플들은  어떠한 사운드라도
재생산 할 수 있으며, 출력 역시 모든 사운드 카드에서 유사한 방식으로 출력
가능하다. 그러나 웨이브 형식은 저장 공간이 많이 필요하고 리소스가 커진다
는 단점이 있다.

악기 음악의 MIDI 방식은 보통 하드웨어적으로 합성되어지며 메세지에 반응하
는 방식으로 구성되어 있다.  MIDI 파일은 용량도 작고 리소스도 매우 작아진
다.  그러나 신디사이저 내의 제너럴 MIDI 로는 악기의 수에 제한이 있고, 다
른 시스템으로 가면 출력 음이 달라져 버리는 경우가 발생한다.

MIDI 의 간결성과 유연성을 가지면서 디지탈 샘플링의 장점을 결합하는 한 가
지 방법에는  디지탈 샘플링으로  MIDI 의 악기를 웨이브로 합성하는 것이다.
이들 샘플들은 실제 악기의 녹음으로 얻을 수가 있는데 그것은 하드웨어 상에
저장된다. 이 샘플들은 음의 피치 길이와 볼륨을 가변적으로 생성하는 방법으
로 조절되어 진다.

웨이브 테이블 합성 방식은 FM ( 주파수 변조 ) 합성보다도 더 사실적인 음색
을 만들어 낸다. 그러나 여전히 악기 종류와 개수에는 제한이 따른다. 그래서
특이한 악기일 수록 하드웨어에 따라 음이 다르게 나올 수가 있는데, 주로 제
조 회사의 악기에 따라 달라진다.

이런 제한을 극복하기 위해서  DLS ( downloadable sounds ) 의 표준이  MIDI
제조업자 협회에서 제정되어 졌다.  DLS 는 시스템 상의 하드웨어적 구성보다
는, 실행 시간에 제공되는 샘플에 기초해서 웨이브를 합성하는 방식을 취하고
있다.

악기의 데이터 세부는 신디사이즈에서 다운로딩 받는다. 그래서 그 악기는 다
른 MIDI 악기와 유사하게 연주될 수 있다. 왜냐하면 DLS 데이터는 애플리케이
션의 한 부분으로 배포될 수가 있고,  개발자들은 사운드 트랙이 모든 시스템
에서 동일한 음을 낼 수 있다는 보장을 받는 것이다. 그리고 더 이상 악기 선
택에 있어서의 제한을 받지 않게 된다.

DLS 악기는 하나 또는 그 이상의 디지탈 샘플로부터 생성되어 지는데, 일반적
으로 단일 피치로 구성되며  다른 피치를 생성하는 합성기에 의해서 조절되어
진다.

다중 샘플은 현실적인 넓은 범위의 피치의 악기 음을 만드는데 주로 사용된다.
DLS 악기가 다운로딩 되었을 때, 각각의 샘플은 피치의 어떠한 범위로 지정되
는데 이것은 영역(region)이라고 하며 적어도 16 개의 영역이 있다.

게다가,  샘플들은 조음이 주어질 수 있는데  attack ( 빠르게 최대 볼륨으로
음정이 뛰는 정도 ) 이나  decay ( 빠르게 최대 볼륨에서 떨어지는 정도 ) 같
은 것을 정의 할 수 있다.  그리고 다른 특성들은 실제 악기에 의해서 만들어
지는 것과 유사한 방법으로 음을 만든다.

DLS 는 신디사이저에서 다운로딩한 악기의 집합으로 저장된다.

DLS 악기는 패치 넘버가 주어지며 다른 MIDI 악기가 하는 것처럼 MIDI 메시지
에 응답한다.  그러나 DLS 악기는 제너럴 MIDI 세트에 한정 지어져서는 안 된
다.  사실, 그것이 악기로서만 대표되어져서는 안되기 때문이다. 왜냐하면 음
성의 부분이나,  완전히 음악으로 구성된 것이나 할 것 없이 모두 DLS 악기로
바뀌어질 수 있기 때문이다.

DLS 에 대한 더 많은 정보를 원하거나 어떻게 악기가 만들어지는 가를 보려면
Direct Music Producer 의 도큐멘트를 보면 된다.


------------------------------------------------------------------------

(3) Direct Music 과 기존의 방식 비교

DLS 라고 하는 말은 생소한 말이지만 그 방식은 전혀 생소한 것이 아니다. 예
전에 모듈 음악이라는 것을 들어보신 분은 알겠지만, 실제의 악기의 샘플링을
실 연주에 이용하는 방식은 오래 전부터 있었다.  MOD, S3M, IT 등등의  모듈
음악 파일들이 그것인데  이것들은 그 파일 자체에 그 샘플링 된 악기의 데이
터를 가지고 있었다. 그리고 또한, DLS 처럼 악기를 따로 모아쓰는 개념도 있
었는데, 그것이 BNK 확장자를 가진 뱅크라는 개념이었고 ROL, IMS 등의 FM 방
식의 파일 포맷에서 사용되었다. ( 그 당시의 뱅크는  악기 이름으로  악기를
구분했기 때문에 이름만 다르다면 악기의 개수에는 제한이 없었다. )

위에서 장황하게 말한 DLS 는  위의 두 가지 방식을 합한 것이다.  FM 방식의
뱅크의 장점인 '악기는 따로 모은다'는 것과, 모듈 음악의 장점인 '실제 샘플
링 데이터'를 쓴다는 두 가지를 합한 것이다. 그리고 FM 의 단점인 음질 저하
나, 모듈 음악의 단점인 용량 증가는 그런 대로 해결이 된 것이다. ( MIDI 파
일의 크기는 모듈 음악에 배해 훨씬 적은 용량이다. )

그리고 Direct Music 은 MIDI 를 표방하고 있지만 실제는 Direct Sound 가 발
전한 형태로  보면 된다.  Direct Music 이 단독으로  쓰일 때는  내부적으로
Direct Sound 를 생성하며, 이미 Direct Sound 이 생성되어 있다면 그 객체를
Direct Music 쪽으로 넘겨주어야 한다. 음의 데이터만 MIDI 파일에서 읽을 뿐,
음의 합성이나 피치 변형 ( 피치 주파수가 커지면 그 악기의 음도 올라간다 )
에는 Direct Sound 버퍼를 이용하기 때문이다. 사운드 카드의 '미디 매퍼' 나
'소프트웨어 신디사이저' 나 'MPU-401 모듈' 은  모두 legacy port 라고 해서
윈도우즈 2000 부터는  아예 지원도  안 할 예정이라고 한다.  아마 윈도우즈
2000 미디어 플레이어에서의  MIDI 연주는  무조건 Direct Music 을 통해서만
가능해 질 것 같다.

물론 요새의 게임 제작은 아예 CD (CDDA) 로 가고 있지만  어차피 CD 도 신디
사이저에서 사용한 모듈의 음을 녹음한 것에 불과하므로, 자신의 음원을 다운
로딩 해서 DLS 상태로 게임을 배포한다면 CD 의 트랙으로 배포할 때와 똑같은
결과를 얻는다. 아니 실제로는 용량도 훨씬 줄고 CD-ROM 에 대한 부하도 줄어
든다.
( CD 로 배경 음을 연주하는 게임의 가장 큰 단점이라면, 게임 데이터를 항상
  하드디스크에 복사를 해두고 실행해야 한다는 것이다. CD 에서 게임을 바로
  실행시키면 CD 음이 뚝뚝 끊기기 때문이다.  당연하지만 CD-ROM 의 읽기 헤
  더는 하나이기 때문이다. )

-----------------------------------------------------------------------

                                                          1999 / 11 / 10

#####################################################

------------------------------------------------------------------------  
    
### 델파이로 하는 Direct X - Direct Music (2/3)

    1. Direct Music 의 개요
    2. Direct Music 초기화와 MIDI 읽기
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3. Direct Music 출력

------------------------------------------------------------------------
                                    작성자 : 안영기 ( HiTEL ID : SMgal )

------------------------------------------------------------------------

(1) Direct Music 초기화


1) COM 의 초기화

|   if CoInitialize(nil) <> S_OK then exit;

제일 처음의 과정은 COM 의 초기화이다.


2) Direct Music Performance 의 생성

|   if CoCreateInstance(CLSID_DirectMusicPerformance,nil,CLSCTX_INPROC,
|               IID_IDirectMusicPerformance,MIDIPerf) <> S_OK then exit;

Performance   는   Direct  Music   의   가장   기본적인   인터페이스인데,
CoCreateInstance() 함수를 통해 그 인터페이스를 얻어 온다.

Direct Music 부터는 Create 계열의 초기화 함수를 지원하지 않으므로 COM 의
방식대로 Direct Music Performance  의 클래스 아이디를  통해 인터페이스를
돌려주도록 만들었다. 여기서 MIDIPerf 라는 변수가 Performance 객체이다.

역시 실패하면 S_OK 가 아닌 값이 들어온다.


3) Performance 의 초기화

|   if MIDIPerf.Init(DirectMusic,DirectSound,Handle) <> S_OK then exit;

IDirectMusicPerformance 의 메쏘드인 Init() 를 호출하면  되는데 그 파라메
터는 다음과 같다.

첫  번째   파라메터는 IDirectMusic   객체이다.   만약 nil  을   주었다면
IDirectMusic 객체는 밖으로 드러나지 않게 된다.

두 번째 파라메터는 IDirectSound 객체이다. 만약 Direct Sound 와 같이 사용
하려면 미리 생성되어 있는 Direct Sound 객체를 넘겨줘야 한다. 같이 사용하
지 않는다면 nil 로 인자를 주면 되는데, 이  때는 Direct Music 이 내부적으
로 Direct Sound 객체를 생성하게 된다.

세 번째 파라메터는 Direct Sound 를 만들기 위한  핸들이다. ( Direct Sound
초기화에 핸들이 필요하다는 것을 앞의 Direct Sound  강좌에서 했었다. ) 만
약 nil 로 인자를 주었다면 메인 윈도우의 핸들이 들어간다.


4) Performance 의 포트 추가

|   if MIDIPerf.AddPort(nil) <> S_OK then exit;

포트란  미디   출력  디바이스를   말한다.  사운드   카드에  따라  다르며
IDirectMusic 객체에 있는 EnumPort() 라는 메쏘드로  사용 가능한 포트를 찾
을 수가  있다. 지금처럼  nil 로   인자를 주면 Direct  Music 의  디폴트인
'Microsoft Software Synthesizer' 로 선택하는 것이 된다.

|   var
|      PortCaps   : TDMus_PortCaps;

|   PortCaps.dwSize := Sizeof(PortCaps);
|   i := 0;
|   while DirectMusic.EnumPort(i,PortCaps) = S_OK do begin
|      Inc(i);
|   end;

현재의 사용 가능한 포트를 알아오기 위해서는  IDirectMusic.EnumPort 을 사
용하면 된다. 제일 처음 값은 0 으로 하여  S_FALSE 메세지가 나올 때까지 루
프를 돌리면서 1 씩 증가시켜 대입하면 모든 포트를 찾을 수 있다.

PortCaps.wszDescription 에는 포트의 이름이  들어 있고, PortCaps.guidPort
에 그 포트의 GUID 가 들어 있다.

디폴트인 'Microsoft Software Synthesizer' 이외의 MIDI 장치로 플레이 하기
위해서는 아래와 같이 하면 된다.

|    var
|       GUID       : TGUID;
|       PortParams : TDMus_PortParams;

|   FillChar(PortParams,Sizeof(PortParams),0);
|   PortParams.dwSize        := Sizeof(PortParams);
|   PortParams.dwValidParams := DMUS_PORTPARAMS_EFFECTS;
|   PortParams.dwEffectFlags := DMUS_EFFECT_REVERB;
|      // reverb 예를 든 것이다. 실제에서는 자신에게 맞게 하면 된다.
|   HR := DirectMusic.CreatePort(GUID,PortParams,MIDIPort,nil);
|      // GUID 에는 자기가 쓰고자 하는 포트의 GUID 를 넣으면 된다.
|      // IDirectMusic 의 GetDefaultPort 로  GUID 를 얻는다면  Microsoft
|      // Software Synthesizer 가 선택된다.
|   if (HR <> S_OK) and (HR <> S_FALSE) then exit;
|      // 성공하면 S_OK 나 S_FALSE 가 돌아온다.
|      // S_FALSE 가 돌아 올 때는, 함수가 성공 했지만 요구 조건을 만족시
|      // 키지 못할 때이다.  이 경우에는 그 장치가 reverb 를 지원하지 않
|      // 으면 S_FALSE 가 돌아온다.
|
|   DirectMusic.Activate(TRUE);
|      // 포트를 활성화 시킨다.
|      // MIDIPort.Activate(TRUE); 를 써도 같은 결과이다.
|
|   if MIDIPerf.AddPort(MIDIPort) <> S_OK then exit;
|      // 포트를 추가한다. nil 이 아닌 값이 쓰였다.
|
|   MIDIPerf.AssignPChannelBlock(0,MIDIPort,1);
|      // 첫 번째 파라메터가  0 이면 채널 0-15를, 1 이면 채널 16-31을, 2
|      // 이면 채널 32-47을.... 이런 식으로 증가한다.
|      // 마지막 파라메터는 포트상의 채널 그룹인데 1 이상의 값이 온다.


5) Notification 추가

|   GUID := GUID_NOTIFICATION_SEGMENT;
|   MIDIPerf.AddNotificationType(GUID);

Performance 에 Notification 을 추가한다. Direct Music 에서는 자신이 원하
는 이벤트 메세지만 받을 수 있도록 위와 같이 받고 싶은 것만 추가한다.

가능한 이벤트의 목록은 다음과 같다.

 GUID_NOTIFICATION_SEGMENT        - Segment 에 대한 알림 기능
 GUID_NOTIFICATION_PERFORMANCE    - Performance 에 대한 알림 기능
 GUID_NOTIFICATION_MEASUREANDBEAT - Segment 에 대한 알림 기능
 GUID_NOTIFICATION_CHORD          -  에 대한 알림 기능
 GUID_NOTIFICATION_COMMAND        - Segment 에 대한 알림 기능


6) Direct Music Loader 의 생성

|   if CoCreateInstance(CLSID_DirectMusicLoader,nil,CLSCTX_INPROC,
|                  IID_IDirectMusicLoader,MIDILoader) <> S_OK then exit;
|   if not Assigned(MIDILoader) then exit;

위의 Performance 의 생성과  대동소이하다. 클래스 ID 와  인터페이스 ID 만
바뀐 것뿐이다.

여기까지 하면 일단 Direct Music 에 대한 초기화는 끝났다.


-----------------------------------------------------------------------

(2) MIDI 파일 읽기

1) Direct Music Segment 생성

IDirectMusicSegment 객체는 MIDI 파일의 세그먼트를 가진다.
아래는 MIDI 파일에서 Segment 객체를 만드는 함수이다.

| function MULTI_TO_WIDE(x : PWideChar; y : PChar) : integer;
| // 이 함수는 C 로된 예제에 #define 으로 되어 있던 것을 풀어 쓴 것이다.
| // y 라는 Char 형 배열을 x 라는 WideChar 형의 배열로 바꾼다.
| begin
|    MULTI_TO_WIDE :=
|            MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,y,-1,x,MAX_PATH);
| end;

| function LoadMIDISegment(MIDIPerf    : IDirectMusicPerformance;
|                          MIDILoader  : IDirectMusicLoader;
|                          FileName    : String)  : IDirectMusicSegment;
| var
|    ObjDesc     : TDMUS_OBJECTDESC;
|    MIDISegment : IDirectMusicSegment;
|    wPath       : array [0..DMUS_MAX_FILENAME-1] of WCHAR;
|    HR          : HResult;
| begin
|    LoadMIDISegment := nil;
|    // Segment 가 지정되지 않은 상태
|
|    MULTI_TO_WIDE(wPath,PChar(ExtractFilePath(FileName)));
|    // FileName 의 Path 만 wPath 라는 WideChar 형 배열로 복사
|
|    HR := MIDILoader.SetSearchDirectory(GUID_DirectMusicAllTypes,wPath,
|                                                                FALSE);
|    if (HR <> S_OK) and (HR <> S_FALSE) then exit;
|    // MIDI 파일을 찾을 디렉토리를 설정한다.
|
|    FillChar(ObjDesc,Sizeof(TDMUS_OBJECTDESC),0);
|    ObjDesc.dwSize      := sizeof(TDMUS_OBJECTDESC);
|    ObjDesc.dwValidData := DMUS_OBJ_CLASS or DMUS_OBJ_FILENAME;
|    ObjDesc.guidClass   := CLSID_DirectMusicSegment;
|    // DirectMusicSegment 의 클래스 ID 를 지정
|    MULTI_TO_WIDE(ObjDesc.wszFileName,
|                                     PChar(ExtractFileName(FileName)));
|    // 파일의 이름을 지정
|
|    Pointer(MIDISegment) := nil;
|    HR := MIDILoader.GetObject(ObjDesc,IID_IDirectMusicSegment,
|                                                          MIDISegment);
|    if HR <> S_OK then exit;
|    // IDirectMusicSegment 의 인터페이스를 얻는다.
|
|    if Assigned(MIDISegment) then begin
|       HR := MIDISegment.SetParam(GUID_StandardMIDIFile,
|                                      $FFFFFFFF,0,0,Pointer(MIDIPerf));
|       if HR <> S_OK then exit;
|       // 이 Segment 는 표준 MIDI 파일로 지정
|       HR := MIDISegment.SetParam(GUID_Download,
|                                      $FFFFFFFF,0,0,Pointer(MIDIPerf));
|       if (HR <> S_OK) and (HR <> S_FALSE) then exit;
|       // 이 Segment 는 'DLS 를 사용하여 악기를 다운로딩한다'라고 지정
|    end;
|
|    LoadMIDISegment     := MIDISegment;
| end;

위의 소스는 Direct X 6.1 이후의 Direct Music  에서 제대로 동작한다. 이렇
게 말고, Direct X 7.0 의 Direct Music 방식으로  하면 아래처럼도 할 수 있
다.

|    LoadMIDISegment := nil;
|
|    FillChar(ObjDesc,Sizeof(TDMUS_OBJECTDESC),0);
|    ObjDesc.dwSize      := sizeof(TDMUS_OBJECTDESC);
|    ObjDesc.dwValidData := DMUS_OBJ_CLASS or DMUS_OBJ_FULLPATH;
|    ObjDesc.guidClass   := CLSID_DirectMusicSegment;
|    MULTI_TO_WIDE(ObjDesc.wszFileName,PChar(FileName));
|
|    Pointer(MIDISegment) := nil;
|    HR := MIDILoader.GetObject(ObjDesc,IID_IDirectMusicSegment,
|                                                          MIDISegment);
|    if HR <> S_OK then exit;
|    ........

이 방식은, 디렉토리를 지정해 놓고 파일을 정의하던  그 위의 방법과는 달리
한번에 Full Path 로  MIDI 파일을 읽는다.  그리고 Direct X 6.1  의 Direct
Music 에는 치명적인 버그가 하나 있는데, 그것은 Path 이름에 공백이 들어가
면 Path 를 제대로 찾지 못한다는 것이다.  특히 한글 윈도우즈는 c:\windows
\바탕 화면\test.Mid 와 같이 Path 에 많은  공백이 존재한다. 그래서 필자는
Direct Music 을 위해서라면 Direct X 7.0 버전을 사용할 것을 권유한다.

위의 함수의 사용법을 보면 아래와 같다.

|    MIDISegment := LoadMIDISegment(MIDIPerf,MIDILoader,FileName);


-----------------------------------------------------------------------

                                                          1999 / 11 / 11

#####################################################

------------------------------------------------------------------------
    
### 델파이로 하는 Direct X - Direct Music (3/3)

    1. Direct Music 의 개요
    2. Direct Music 초기화와 MIDI 읽기
    3. Direct Music 출력
    ~~~~~~~~~~~~~~~~~~~~

------------------------------------------------------------------------
                                    작성자 : 안영기 ( HiTEL ID : SMgal )

------------------------------------------------------------------------

(1) Direct Music 의 출력

|   if Assigned(MIDISegment) then begin
|      HR := MIDIPerf.PlaySegment(MIDISegment,0,ZERO_INT64,MIDISegStat);
|      if HR <> S_OK then Exit;

연주에 쓰이는 IDirectMusicPerformance.PlaySegment() 는 4 개의 파라메터가
있다.

첫 번째 파라메터는 연주할 세그먼트이다.

두 번째 파라메터는 이 메쏘드의 행동을 정의하는 것인데 DMUS_SEGF_FLAGS 중
에 하나가 들어 갈 수 있다.  ( C 에서는 열거형 (enum) 이지만 델파이에서는
상수로 정의하고 있다. ) 주로 primary segment 와 control segment, 또는 세
그먼트의 바운더리 ( 경계 ) 에 대한 플래그가 있다. 자세한 것은 SDK 헬프를
참조하기 바란다.

세 번째 파라메터는 그 세그먼트에서 연주를 시작 할 곳의 시간이다. 두 번째
파라메터에서 DMUS_SEGF_REFTIME 가 정의되어 있지 않다면

네 번째 파라메터는 연주될 세그먼트 인스턴스에 대한  세그먼트 상태를 나타
내는 객체를  넣는다.  nil  이  아닌 값이  들어갔다면 IDirectMusicSegment
State 객체의 포인터가 돌아온다.  물론  이 객체의 Release 는 프로그래머의
몫이다.


ZERO_INT64 는  이전 버전인 델파이 3 와 호환을 위해서 만들어 본  상수인데
실제 정의는 아래와 같이 되어 있다.  ( 이건 필자가 Direct Music 테스트 당
시에 Delphi 3 를 사용하고 있었기 때문에 만든 것이지, 그 이상의 버전을 사
용하는 사람에게는 전혀 필요가 없다. )

| {$IFDEF VER100}
| type  Int64 = array [0..1] of DWORD;
| const ZERO_INT64 : Int64 = (0,0);
|
{$ELSE}
| const ZERO_INT64 : Int64 = 0;
|
{$ENDIF}

이 컴파일러가 델파이3 이면 DWORD 두 개로 만들어지고,  그 이상이라면 원래
부터 있는 64 bit 정수형인 Int64 로 형이 정의된다. ( 물론 델파이 2 버전이
라면 에러를 낼 것이다. )


------------------------------------------------------------------------

(2) Direct Music 의 종료

| procedure CloseMIDI;
| begin
|    if Assigned(MIDISegment) then begin
|       if Assigned(MIDIPerf)    then MIDIPerf.Stop(nil,nil,0,0);
|          // MIDI 의 연주를 멈춘다.
|       if Assigned(MIDISegment) then MIDISegment.SetParam(
|                          GUID_Unload,$FFFFFFFF,0,0,Pointer(MIDIPerf));
|          // 트랙의 밴드에 대한 악기 데이터를 제거한다.
|       if Assigned(MIDISegment) then MIDISegment._Release;
|          // 세그먼트 객체를 해제한다.
|       Pointer(MIDISegment) := nil;
|          // 다시 초기화..
|    end;
| end;

단지 Stop 만 했다면 PlaySegment 로 다시 연주가 가능하다.  ( Pause 계열의
메쏘드는 보이지 않는다. )


아래는 Direct Music 을 해제하는 부분이다.


| procedure FinalizeDMusic;
| begin
|
|    if Assigned(MIDIPerf)    then MIDIPerf.Stop(nil,nil,0,0);
|    if Assigned(MIDISegment) then MIDISegment.SetParam(GUID_Unload,
|                                      $FFFFFFFF,0,0,Pointer(MIDIPerf));
|    if Assigned(MIDIPerf)    then MIDIPerf.CloseDown;
|       // MIDIPerf.Init() 에 대응하는 메쏘드이다.
|
|    if Assigned(MIDIPort)    then MIDIPort._Release;
|    if Assigned(MIDISegStat) then MIDISegStat._Release;
|    if Assigned(MIDISegment) then MIDISegment._Release;
|    if Assigned(MIDILoader)  then MIDILoader._Release;
|    if Assigned(MIDIPerf)    then MIDIPerf._Release;
|    if Assigned(DirectMusic) then DirectMusic._Release;
|       // 자신이 생성한 Direct Music 의 객체는 모두 해제한다.
|       // 필자가 사용하는 소스에서 바로 가져 왔기 때문에 이 강좌에서 다
|       // 루지 않은 객체도 있다.
|
|    Pointer(MIDIPort)    := nil;
|    Pointer(MIDISegStat) := nil;
|    Pointer(MIDISegment) := nil;
|    Pointer(MIDILoader ) := nil;
|    Pointer(MIDIPerf   ) := nil;
|    Pointer(DirectMusic) := nil;
|       // 역시 nil 로 초기화
|
|    CoUninitialize;
|       // COM 해제
|
| end;

다른 Direct X 의 해제와 별 다를 것이 없다.

------------------------------------------------------------------------

(3) Direct Music 의 Notification

이것은 Direct Music 의 상태를 알려주는 부분이다. 멀티미디어 API 와 는 달
리 CallBack 방식이 아닌 직접 유저가 불러 주어야 한다.

| procedure TimeProcedure;
| var
|    pMsg  : PDMus_Notification_PMsg;
| begin
|
|    if Assigned(MIDIPerf) then begin
|       while (MIDIPerf.GetNotificationPMsg
|                       (PDMus_Notification_PMsg(pMsg)) = S_OK) do begin
|          case pMsg.dwNotificationOption of
|             DMUS_NOTIFICATION_SEGSTART :
|             begin
|                // MIDI 연주를 시작한다.
|             end;
|             DMUS_NOTIFICATION_SEGEND   :
|             begin
|                // MIDI 연주가 끝났다.
|             end;
|          end;
|          MIDIPerf.FreePMsg(PDMus_PMsg(pMsg));
|       end;
|    end;
| end;

이것 이외에 모든 알림 플래그를 나열해 보면...

1) AddNotificationType 에 GUID_NOTIFICATION_SEGMENT 를 넣었을 때

   DMUS_NOTIFICATION_SEGSTART
      // MIDI 연주를 시작한다.
   DMUS_NOTIFICATION_SEGEND
      // MIDI 연주가 끝났다.
   DMUS_NOTIFICATION_SEGALMOSTEND
      // MIDI 연주가 거의 끝났다. 버퍼 용량만큼만 남았을 때
   DMUS_NOTIFICATION_SEGLOOP
      // MIDI 연주가 다시 재연주 되었다.
   DMUS_NOTIFICATION_SEGABORT
      // Stop 명령에 의해 멈추어 졌다.

2) AddNotificationType 에 GUID_NOTIFICATION_PERFORMANCE 를 넣었을 때

   DMUS_NOTIFICATION_MUSICSTARTED
      // Playback 을 시작했다.
   DMUS_NOTIFICATION_MUSICSTOPPED
      // Playback 이 끝났다.

3) AddNotificationType 에 GUID_NOTIFICATION_MEASUREANDBEAT 를 넣었을 때

   DMUS_NOTIFICATION_MEASUREBEAT
      // Help 에도 용도 안 적혀 있음

4) AddNotificationType 에 GUID_NOTIFICATION_CHORD 를 넣었을 때

   DMUS_NOTIFICATION_CHORD
      // Help 에도 용도 안 적혀 있음

5) AddNotificationType 에 GUID_NOTIFICATION_COMMAND 를 넣었을 때

   DMUS_NOTIFICATION_GROOVE
      // Groove change.
   DMUS_NOTIFICATION_EMBELLISHMENT
      // Embellishment command (intro, fill, break, or end).

위의 플래그들 중, 아래의 4 개는 Help 에서 조차도 제대로 설명이 안 되어있
다. 그냥 알아서 사용하기 바란다.

------------------------------------------------------------------------


<< 에필로그 >>

지금까지 설명한 것은 Direct X  의 Direct Music 부분이었습니다.  델파이로
게임 만드시려는 분들께 조금이나마 도움이 되었으면 좋겠습니다.

한가지 주의 하실 점은,  배포되는 델파이 헤더 중에 Direct Music 만은 제대
로 실행이 안됩니다. 컨버팅 한 사람조차 제대로 실행을 안 시켜 보았던 것인
지는 모르겠지만 선언 부분에 약간의 오류가 있어서  그 부분을 제가 다시 수
정했습니다. 그 수정본은 VTOOL 자료실에 올리겠습니다.

이번의 강좌 ( 특히 이번 3 편 ) 는 너무 날림으로 해서 죄송합니다. Direct
Music 은 Direct Draw 정도의 분량이라  ( 인터페이스의 종류는 훨씬 많음 )
더 많은 설명이 필요한대도 불구하고  이 정도로 강좌를 마치게 되었습니다.
다른 Direct X 를 써본 사람이라면 Direct Music 역시 헬프 도큐멘트를 보면
충분히 알 수 있는 내용들로 되어 있습니다.

예상외로 일이 많이 겹치게 되어서  제가하는 Direct X 강좌는 여기서 마칠까
합니다. 강좌 쓰는데 매달린다고 다른 쪽에 소홀했더니 그쪽에서 말썽이 생기
는군요...

지금까지 델파이로 Direct  Draw, 멀티미디어  타이머, DIB,  Direct Input,
MMX, Direct Sound, Direct Music 까지의 강좌를 했는데... 이 모든 강좌들은
제 홈페이지에 오시면 바이너리 자료도 같이 구하실 수가 있습니다.
( 물론 바이너리 자료가 있는 것만.. )

   < http://smgal.com/ >

그럼 마지막으로 한마디 남기겠습니다.

          " A mountain is a mountain, Water is water. "
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                                              copyright SMgal 1999/11/13