示例#1
0
/* The load_voice method loads a sample into the driver's memory. The voice's
   'streaming' field will be set to false for these voices, and it's
   'buffer_size' field will be the total length in bytes of the sample data.
   The voice's attached sample's looping mode should be honored, and loading
   must fail if it cannot be. */
static int _dsound_load_voice(ALLEGRO_VOICE *voice, const void *_data)
{
   ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
   HRESULT hr;
   LPVOID ptr1, ptr2;
   DWORD block1_bytes, block2_bytes;
   MAKE_UNION(&ex_data->ds8_buffer, LPDIRECTSOUNDBUFFER8 *);

   ALLEGRO_DEBUG("Loading voice\n");

   ex_data->wave_fmt.wFormatTag = WAVE_FORMAT_PCM;
   ex_data->wave_fmt.nChannels = ex_data->channels;
   ex_data->wave_fmt.nSamplesPerSec = voice->frequency;
   ex_data->wave_fmt.nBlockAlign = ex_data->channels * (ex_data->bits_per_sample/8);
   ex_data->wave_fmt.nAvgBytesPerSec = ex_data->wave_fmt.nBlockAlign * voice->frequency;
   ex_data->wave_fmt.wBitsPerSample = ex_data->bits_per_sample;
   ex_data->wave_fmt.cbSize = 0;

   ex_data->desc.dwSize = sizeof(DSBUFFERDESC);
   ex_data->desc.dwFlags = DSBCAPS_LOCSOFTWARE | DSBCAPS_GLOBALFOCUS; /* FIXME: software mixing for now */
   ex_data->desc.dwBufferBytes = voice->buffer_size;
   ex_data->desc.dwReserved = 0;
   ex_data->desc.lpwfxFormat = &ex_data->wave_fmt;
   ex_data->desc.guid3DAlgorithm = DS3DALG_DEFAULT;

   hr = device->CreateSoundBuffer(&ex_data->desc, &ex_data->ds_buffer, NULL);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("CreateSoundBuffer failed: %s\n", ds_get_error(hr));
      return 1;
   }

   ex_data->ds_buffer->QueryInterface(_al_IID_IDirectSoundBuffer8, u.v);

   hr = ex_data->ds8_buffer->Lock(0, voice->buffer_size,
      &ptr1, &block1_bytes, &ptr2, &block2_bytes, 0);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("Locking buffer failed: %s\n", ds_get_error(hr));
      return 1;
   }

   unsigned char *data = (unsigned char *) _data;
   memcpy(ptr1, data, block1_bytes);
   memcpy(ptr2, data + block1_bytes, block2_bytes);

   ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes);

   return 0;
}
示例#2
0
/* The open method starts up the driver and should lock the device, using the
   previously set parameters, or defaults. It shouldn't need to start sending
   audio data to the device yet, however. */
static int _dsound_open()
{
   HRESULT hr;
   ALLEGRO_INFO("Starting DirectSound...\n");

   /* FIXME: Use default device until we have device enumeration */
   hr = DirectSoundCreate8(NULL, &device, NULL);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("DirectSoundCreate8 failed: %s\n", ds_get_error(hr));
      return 1;
   }

   ALLEGRO_DEBUG("DirectSoundCreate8 succeeded\n");

   hr = device->SetCooperativeLevel(get_window(), DSSCL_PRIORITY);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("SetCooperativeLevel failed: %s\n", ds_get_error(hr));
      return 1;
   }

   return 0;
}
示例#3
0
/* The get_voice_position method should return the current sample position of
   the voice (sample_pos = byte_pos / (depth/8) / channels). This should never
   be called on a streaming voice. */
static unsigned int _dsound_get_voice_position(const ALLEGRO_VOICE *voice)
{
   ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
   DWORD play_pos;
   HRESULT hr;

   hr = ex_data->ds8_buffer->GetCurrentPosition(&play_pos, NULL);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("GetCurrentPosition failed: %s\n", ds_get_error(hr));
      return 0;
   }

   return play_pos / (ex_data->channels * (ex_data->bits_per_sample/8));
}
示例#4
0
/* The set_voice_position method should set the voice's playback position,
   given the value in samples. This should never be called on a streaming
   voice. */
static int _dsound_set_voice_position(ALLEGRO_VOICE *voice, unsigned int val)
{
   ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
   HRESULT hr;

   val *= ex_data->channels * (ex_data->bits_per_sample/8);

   hr = ex_data->ds8_buffer->SetCurrentPosition(val);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("SetCurrentPosition failed: %s\n", ds_get_error(hr));
      return 1;
   }

   return 0;
}
/* The start_voice should, surprise, start the voice. For streaming voices, it
   should start polling the device and call _al_voice_update for audio data.
   For non-streaming voices, it should resume playing from the last set
   position */
static int _dsound_start_voice(ALLEGRO_VOICE *voice)
{
   ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA *)voice->extra;
   HRESULT hr;
   MAKE_UNION(&ex_data->ds8_buffer, LPDIRECTSOUNDBUFFER8 *);

   ALLEGRO_DEBUG("Starting voice\n");

   if (!voice->is_streaming) {
      ex_data->ds8_buffer->SetCurrentPosition(0);
      hr = ex_data->ds8_buffer->Play(0, 0, 0);
      if (FAILED(hr)) {
         ALLEGRO_ERROR("Streaming voice failed to start\n");
         return 1;
      }
      ALLEGRO_INFO("Streaming voice started\n");
      return 0;
   }

   if (ex_data->stop_voice != 0) {
      ex_data->wave_fmt.wFormatTag = WAVE_FORMAT_PCM;
      ex_data->wave_fmt.nChannels = ex_data->channels;
      ex_data->wave_fmt.nSamplesPerSec = voice->frequency;
      ex_data->wave_fmt.nBlockAlign = ex_data->channels * (ex_data->bits_per_sample/8);
      ex_data->wave_fmt.nAvgBytesPerSec = ex_data->wave_fmt.nBlockAlign * voice->frequency;
      ex_data->wave_fmt.wBitsPerSample = ex_data->bits_per_sample;
      ex_data->wave_fmt.cbSize = 0;

      ex_data->desc.dwSize = sizeof(DSBUFFERDESC);
      ex_data->desc.dwFlags = DSBCAPS_LOCSOFTWARE | DSBCAPS_GLOBALFOCUS; /* FIXME: software mixing for now */
      ex_data->desc.dwBufferBytes = buffer_size;
      ex_data->desc.dwReserved = 0;
      ex_data->desc.lpwfxFormat = &ex_data->wave_fmt;
      ex_data->desc.guid3DAlgorithm = DS3DALG_DEFAULT;

      ALLEGRO_DEBUG("CreateSoundBuffer\n");

      hr = device->CreateSoundBuffer(&ex_data->desc, &ex_data->ds_buffer, NULL);
      if (FAILED(hr)) {
         ALLEGRO_ERROR("CreateSoundBuffer failed: %s\n", ds_get_error(hr));
         al_free(ex_data);
         return 1;
      }

      ALLEGRO_DEBUG("CreateSoundBuffer succeeded\n");

      ex_data->ds_buffer->QueryInterface(_al_IID_IDirectSoundBuffer8, u.v);

      ex_data->ds8_buffer->SetVolume(DSBVOLUME_MAX);

      ALLEGRO_DEBUG("Starting _dsound_update thread\n");

      ex_data->stop_voice = 0;
      ex_data->thread = al_create_thread(_dsound_update, (void*) voice);
      al_start_thread(ex_data->thread);
   }
   else {
      ALLEGRO_WARN("stop_voice == 0\n");
   }

   ALLEGRO_INFO("Voice started\n");
   return 0;
}
/* Custom routine which runs in another thread to periodically check if DirectSound
   wants more data for a stream */
static void* _dsound_update(ALLEGRO_THREAD *self, void *arg)
{
   ALLEGRO_VOICE *voice = (ALLEGRO_VOICE *)arg;
   ALLEGRO_DS_DATA *ex_data = (ALLEGRO_DS_DATA*)voice->extra;
   const int bytes_per_sample = ex_data->bits_per_sample / 8;
   DWORD play_cursor = 0;
   DWORD write_cursor;
   DWORD saved_play_cursor = 0;
   unsigned int samples;
   LPVOID ptr1, ptr2;
   DWORD block1_bytes, block2_bytes;
   unsigned char *data;
   HRESULT hr;

   (void)self;

   unsigned char *silence = (unsigned char *)al_malloc(buffer_size);
   int silence_value = _al_kcm_get_silence(voice->depth);
   memset(silence, silence_value, buffer_size);

   /* Fill buffer */
   hr = ex_data->ds8_buffer->Lock(0, buffer_size,
      &ptr1, &block1_bytes, &ptr2, &block2_bytes,
      DSBLOCK_ENTIREBUFFER);
   if (!FAILED(hr)) {
      samples = buffer_size / bytes_per_sample / ex_data->channels;
      data = (unsigned char *) _al_voice_update(voice, &samples);
      memcpy(ptr1, data, block1_bytes);
      memcpy(ptr2, data + block1_bytes, block2_bytes);
      ex_data->ds8_buffer->Unlock(ptr1, block1_bytes, ptr2, block2_bytes);
   }

   ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING);

   do {
      if (!_dsound_voice_is_playing(voice)) {
         ex_data->ds8_buffer->Play(0, 0, DSBPLAY_LOOPING);
      }

      ex_data->ds8_buffer->GetCurrentPosition(&play_cursor, &write_cursor);

      /* We try to fill the gap between the saved_play_cursor and the
       * play_cursor.
       */
      int d = play_cursor - saved_play_cursor;
      if (d < 0)
         d += buffer_size;

      /* Don't fill small gaps.  Let it accumulate to amortise the cost of
       * mixing the samples and locking/unlocking the buffer.
       */
      if (d < MIN_FILL) {
         al_rest(0.005);
         continue;
      }

      /* Don't generate too many samples at once.  The buffer may underrun
       * while we wait for _al_voice_update to complete.
       */
      samples = d / bytes_per_sample / ex_data->channels;
      if (samples > MAX_FILL) {
         samples = MAX_FILL;
      }

      /* Generate the samples. */
      data = (unsigned char *) _al_voice_update(voice, &samples);
      if (data == NULL) {
         data = silence;
      }

      hr = ex_data->ds8_buffer->Lock(saved_play_cursor,
         samples * bytes_per_sample * ex_data->channels,
         &ptr1, &block1_bytes, &ptr2, &block2_bytes, 0);
      if (!FAILED(hr)) {
         memcpy(ptr1, data, block1_bytes);
         memcpy(ptr2, data + block1_bytes, block2_bytes);
         hr = ex_data->ds8_buffer->Unlock(ptr1, block1_bytes,
            ptr2, block2_bytes);
         if (FAILED(hr)) {
            ALLEGRO_ERROR("Unlock failed: %s\n", ds_get_error(hr));
         }
      }
      saved_play_cursor += block1_bytes + block2_bytes;
      saved_play_cursor %= buffer_size;
   } while (!ex_data->stop_voice);

   ex_data->ds8_buffer->Stop();

   al_free(silence);

   ex_data->stop_voice = 0;
   al_broadcast_cond(voice->cond);

   return NULL;
}
示例#7
0
static int _dsound_open_recorder(ALLEGRO_AUDIO_RECORDER *r)
{
   HRESULT hr;

   if (capture_device != NULL) {
      /* FIXME: It's wrong to assume only a single recording device, but since
                there is no enumeration of devices, it doesn't matter for now. */
      ALLEGRO_ERROR("Already recording.\n");
      return 1;
   }

   ALLEGRO_INFO("Creating default capture device.\n");

   /* FIXME: Use default device until we have device enumeration */
   hr = DirectSoundCaptureCreate8(NULL, &capture_device, NULL);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("DirectSoundCaptureCreate8 failed: %s\n", ds_get_error(hr));
      return 1;
   }
   
   hr = device->SetCooperativeLevel(get_window(), DSSCL_PRIORITY);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("SetCooperativeLevel failed: %s\n", ds_get_error(hr));
      return 1;
   }

   DSOUND_RECORD_DATA *extra = (DSOUND_RECORD_DATA *) al_calloc(1, sizeof(*extra));

   DSCCAPS dsccaps;   
   dsccaps.dwSize = sizeof(DSCCAPS);
   hr = capture_device->GetCaps(&dsccaps);
   if (FAILED(hr)) {
      ALLEGRO_ERROR("DirectSoundCaptureCreate8::GetCaps failed: %s\n", ds_get_error(hr));
   }
   else {
      ALLEGRO_INFO("caps: %lu %lu\n", dsccaps.dwFormats, dsccaps.dwFormats & WAVE_FORMAT_2M16);
   }

   memset(&extra->wave_fmt, 0, sizeof(extra->wave_fmt));
   extra->wave_fmt.wFormatTag = WAVE_FORMAT_PCM;
   extra->wave_fmt.nChannels = (WORD)al_get_channel_count(r->chan_conf);
   extra->wave_fmt.nSamplesPerSec = r->frequency;
   extra->wave_fmt.wBitsPerSample = (WORD)al_get_audio_depth_size(r->depth) * 8;
   extra->wave_fmt.nBlockAlign = extra->wave_fmt.nChannels * extra->wave_fmt.wBitsPerSample / 8;
   extra->wave_fmt.nAvgBytesPerSec = extra->wave_fmt.nSamplesPerSec * extra->wave_fmt.nBlockAlign;
   
   memset(&extra->desc, 0, sizeof(extra->desc));
   extra->desc.dwSize = sizeof(extra->desc);
   extra->desc.lpwfxFormat = &extra->wave_fmt;
   extra->desc.dwBufferBytes = extra->wave_fmt.nAvgBytesPerSec * 5;

   hr = capture_device->CreateCaptureBuffer(&extra->desc, &extra->buffer, NULL);
   if (FAILED(hr)) {
      al_free(extra);
      ALLEGRO_ERROR("Unable to create Capture Buffer\n");
      return 1;
   }

   extra->buffer->QueryInterface(_al_IID_IDirectSoundCaptureBuffer8, (void **) &extra->buffer8);
   
   r->extra = extra;
   r->thread = al_create_thread(_dsound_update_recorder, r);

   return 0;
}