DirectSound DirectSound 의 고도의 주제 1차 버퍼에의 쓰기   [목차열람] [주소복사] [슬롯비우기]
1차 버퍼에의 쓰기
 
Microsoft DirectX 9.0

1차 버퍼에의 쓰기

2차 버퍼가 지원 하고 있지 않는 특별한 믹싱이나 다른 이펙트를 필요로 하는 애플리케이션을 위해서(때문에), DirectSound 에서는1차 버퍼 에의 직접 액세스를이 용서되고 있다.

1차 버퍼에의 쓰기 액세스를 얻어온다와 DirectSound 외 기능은 이용할 수 없게 된다. 2차 버퍼의 믹싱도 행해지지 않기 때문에, 하드웨어 가속화 믹싱도 이용할 수 없게 된다.

애플리케이션은, 보통, 1차 버퍼에의 직접 액세스는 아니고, 2차 버퍼를 사용할 필요가 있다. 2차 버퍼에의 쓰기는 간단하게 실시할 수가 있다. 버퍼 사이즈가 크기 때문에, 다음의 데이터 블록의 쓰기까지의 시간이 길어져, 오디오의 소리가 중단되는 위험이 최소한으로 억제 당한다. 애플리케이션이 필요로 하는 오디오가, 믹싱을 필요로 하지 않는 1 개의 오디오 스트림과 같이 단순한 것이어도, 2차 버퍼를 사용해 오디오 데이터를 재생하는 것으로써, 보다 높은 성능을 실현될 수 있다.

주  1차 버퍼에의 직접적인 쓰기는, Windows Driver Model (WDM)에서는 이점이 없다. WDM 에서는, 1차 버퍼는, 사실상, 커널 믹서에 의해 믹싱 되는 2차 버퍼이다. WDM 의 더 자세한 정보는, 「DirectSound 드라이버 모델」을 참조할것.

1차 버퍼의 사이즈는 지정하지 못하고, 버퍼를 생성 한 후에 돌려받는 사이즈를 받아들이지 않으면 안 된다. 보통, 1차 버퍼는 지극히 작기 때문에, 애플리케이션이 이런 종류의 버퍼에 직접 기입하는 경우는, 짧은 간격으로 데이터 블록을 쓰기, 이전의 데이터가 한번 더 재생되는 것을 막지 않으면 안 된다.

1차 버퍼가 하드웨어상에 존재하고 있지 않는 한, 그 버퍼의 쓰기 액세스는 취득할 수 없다. 이것에 해당할지 어떨지를 확인하려면 ,IDirectSoundBuffer8::GetCaps 메서드를 호출해,DSBCAPS 구조체의 dwFlags 멤버로 DSBCAPS_LOCHARDWARE 플래그의 유무를 체크한다. 소프트웨어로 에뮬레이트 된 1차 버퍼를 잠그려고 하면, 호출은 실패한다.

액세스 가능한 1차 버퍼를 생성 하려면 ,DSBUFFERDESC 구조체에 DSBCAPS_PRIMARYBUFFER 플래그를 설정해 IDirectSound8::CreateSoundBuffer 메서드에 건네준다. 버퍼에 기입하는 경우, 협조 레벨은 DSSCL_WRITEPRIMARY 가 아니면 안된다.

1차 사운드 버퍼는, 루프 해 재생해야 한다. 그 때문에(위해)는, DSBPLAY_LOOPING 플래그를 반드시 설정한다.

다음 예는 1차 버퍼에의 쓰기 액세스를 얻어온다 방법을 나타내고 있다. 1차 버퍼는 IDirectSoundBuffer 인터페이스만을 지원 해,IDirectSoundBuffer8 를 지원 하고 있지 않는 것에 주의 해야 한다.

BOOL AppCreateWritePrimaryBuffer( 
  LPDIRECTSOUND8 lpDirectSound, 
  LPDIRECTSOUNDBUFFER *lplpDsb, 
  LPDWORD lpdwBufferSize, 
  HWND hwnd) 
{ 
  DSBUFFERDESC dsbdesc; 
  DSBCAPS dsbcaps; 
  HRESULT hr; 
  WAVEFORMATEX wf;
 
  // 웨이브 포맷의 구조체를 설정한다.
  memset(&wf, 0, sizeof(WAVEFORMATEX)); 
  wf.wFormatTag = WAVE_FORMAT_PCM; 
  wf.nChannels = 2; 
  wf.nSamplesPerSec = 22050; 
  wf.nBlockAlign = 4; 
  wf.nAvgBytesPerSec = 
      wf.nSamplesPerSec * wf.nBlockAlign; 
  wf.wBitsPerSample = 16; 
 
  // DSBUFFERDESC 구조체를 설정한다.
  memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
  dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
  dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; 
  // 버퍼 사이즈는, 사운드 하드웨어에 의해 결정된다.
  dsbdesc.dwBufferBytes = 0; 
  dsbdesc.lpwfxFormat = NULL; // 1차 버퍼의 경우는 NULL 이어야 한다.
 
  // 쓰기 우선 협조 레벨을 얻어온다.
  hr = lpDirectSound->SetCooperativeLevel(hwnd, DSSCL_WRITEPRIMARY); 
  if SUCCEEDED(hr) 
  { 
    // 버퍼의 생성을 시도한다.
    hr = lpDirectSound->CreateSoundBuffer(&dsbdesc, 
      lplpDsb, NULL); 
    if SUCCEEDED(hr) 
    { 
      // 1차 버퍼를 목적의 포맷으로 설정한다.
      hr = (*lplpDsb) ->SetFormat(&wf); 
      if SUCCEEDED(hr) 
      { 
        // 버퍼 사이즈를 알 필요가 있는 경우는, GetCaps 를 호출한다.
        dsbcaps.dwSize = sizeof(DSBCAPS); 
        (*lplpDsb) ->GetCaps(&dsbcaps); 
        *lpdwBufferSize = dsbcaps.dwBufferBytes; 
        return TRUE; 
      } 
    } 
  } 
  // 실패했다.
  *lplpDsb = NULL; 
  *lpdwBufferSize = 0; 
  return FALSE; 
} 
 

다음 예는 애플리케이션이 커스텀 믹서를 처리 하는 방법을 나타내고 있다. 사운드 장치가 데이터 블록의 재생을 반복할리가 없는 충분히 짧은 일정 간격으로, AppMixIntoPrimaryBuffer 샘플 함수를 호출해야 한다. CustomMixer 함수는 애플리케이션으로 정의된 함수이며, 애플리케이션으로 정의된 APPSTREAMINFO 구조체의 지정에 따라 복수의 스트림을 믹싱 해, 지정된 포인터에 결과를 기입한다.

BOOL AppMixIntoPrimaryBuffer( 
    APPSTREAMINFO* lpAppStreamInfo, 
    LPDIRECTSOUNDBUFFER lpDsbPrimary, 
    DWORD dwDataBytes, 
    DWORD dwOldPos, 
    LPDWORD lpdwNewPos) 
{ 
  LPVOID lpvPtr1; 
  DWORD dwBytes1; 
  LPVOID lpvPtr2; 
  DWORD dwBytes2; 
  HRESULT hr; 
  // 쓰기 포인터를 얻어온다.
  hr = lpDsbPrimary->Lock(dwOldPos, dwDataBytes, 
          &lpvPtr1, &dwBytes1, 
          &lpvPtr2, &dwBytes2, 0); 
 
  // DSERR_BUFFERLOST 가 돌아갔을 경우는, 복원해 잠금을 리트라이 한다.
 
  if (DSERR_BUFFERLOST == hr) 
  { 
    lpDsbPrimary->Restore(); 
    hr = lpDsbPrimary->Lock(dwOldPos, dwDataBytes,
            &lpvPtr1, &dwBytes1, 
            &lpvPtr2, &dwBytes2, 0); 
  } 
  if SUCCEEDED(hr) 
  { 
    // 데이터를 믹싱 해, 돌려주어진 포인터의 위치에 저장 한다.
    CustomMixer(lpAppStreamInfo, lpvPtr1, dwBytes1); 
    *lpdwNewPos = dwOldPos + dwBytes1; 
    if (NULL != lpvPtr2) 
    { 
      CustomMixer(lpAppStreamInfo, lpvPtr2, dwBytes2); 
      *lpdwNewPos = dwBytes2; // rack-around 했기 때문에
    } 
    // 데이터를 릴리즈 해 DirectSound 에 되돌린다.
    hr = lpDsbPrimary->Unlock(lpvPtr1, dwBytes1, 
            lpvPtr2, dwBytes2); 
    if SUCCEEDED(hr) 
    { 
      return TRUE; 
    } 
  } 
  // 잠금 또는 언락이 실패했다.
  return FALSE; 
} 
 
↑TOP