changeset 13460:70d8f1975fc8

directsound audio output plugin, patch by Gabor Szecsi <deje at miki.hu> some minor modifications by me
author faust3
date Sat, 25 Sep 2004 15:34:42 +0000
parents 5634013c47c8
children 9654998c3577
files configure libao2/ao_dsound.c libao2/audio_out.c
diffstat 3 files changed, 499 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Sat Sep 25 13:46:21 2004 +0000
+++ b/configure	Sat Sep 25 15:34:42 2004 +0000
@@ -4025,6 +4025,7 @@
   cat > $TMPC << EOF
 #include <windows.h>
 #include <ddraw.h>
+#include <dsound.h>
 int main(void) { return 0; }
 EOF
   _directx=no
@@ -4035,9 +4036,12 @@
   _ld_win32libs="-lgdi32 $_ld_win32libs"
   _vosrc="$_vosrc vo_directx.c"
   _vomodules="directx $_vomodules"
+  _aosrc="$_aosrc ao_dsound.c"
+  _aomodules="dsound $_aomodules"
 else
   _def_directx='#undef HAVE_DIRECTX'
   _novomodules="directx $_novomodules"
+  _noaomodules="dsound $_noaomodules"
 fi
 echores "$_directx"
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libao2/ao_dsound.c	Sat Sep 25 15:34:42 2004 +0000
@@ -0,0 +1,489 @@
+/******************************************************************************
+ * ao_dsound.c: Windows DirectSound interface for MPlayer
+ * Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *
+ *****************************************************************************/
+/**
+\todo verify/extend multichannel support
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <dsound.h>
+
+#include "afmt.h"
+#include "audio_out.h"
+#include "audio_out_internal.h"
+#include "../mp_msg.h"
+#include "../libvo/fastmemcpy.h"
+#include "osdep/timer.h"
+
+
+static ao_info_t info =
+{
+	"Windows DirectSound audio output",
+	"dsound",
+	"Gabor Szecsi <deje@miki.hu>",
+	""
+};
+
+LIBAO_EXTERN(dsound)
+
+/**
+\todo use the definitions from the win32 api headers when they define these
+*/
+#if 1
+#define WAVE_FORMAT_IEEE_FLOAT 0x0003
+#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
+#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
+
+static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
+
+#define SPEAKER_FRONT_LEFT             0x1
+#define SPEAKER_FRONT_RIGHT            0x2
+#define SPEAKER_FRONT_CENTER           0x4
+#define SPEAKER_LOW_FREQUENCY          0x8
+#define SPEAKER_BACK_LEFT              0x10
+#define SPEAKER_BACK_RIGHT             0x20
+#define SPEAKER_FRONT_LEFT_OF_CENTER   0x40
+#define SPEAKER_FRONT_RIGHT_OF_CENTER  0x80
+#define SPEAKER_BACK_CENTER            0x100
+#define SPEAKER_SIDE_LEFT              0x200
+#define SPEAKER_SIDE_RIGHT             0x400
+#define SPEAKER_TOP_CENTER             0x800
+#define SPEAKER_TOP_FRONT_LEFT         0x1000
+#define SPEAKER_TOP_FRONT_CENTER       0x2000
+#define SPEAKER_TOP_FRONT_RIGHT        0x4000
+#define SPEAKER_TOP_BACK_LEFT          0x8000
+#define SPEAKER_TOP_BACK_CENTER        0x10000
+#define SPEAKER_TOP_BACK_RIGHT         0x20000
+#define SPEAKER_RESERVED               0x80000000
+
+#define DSSPEAKER_HEADPHONE         0x00000001
+#define DSSPEAKER_MONO              0x00000002
+#define DSSPEAKER_QUAD              0x00000003
+#define DSSPEAKER_STEREO            0x00000004
+#define DSSPEAKER_SURROUND          0x00000005
+#define DSSPEAKER_5POINT1           0x00000006
+
+#ifndef _WAVEFORMATEXTENSIBLE_
+typedef struct {
+    WAVEFORMATEX    Format;
+    union {
+        WORD wValidBitsPerSample;       /* bits of precision  */
+        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
+        WORD wReserved;                 /* If neither applies, set to zero. */
+    } Samples;
+    DWORD           dwChannelMask;      /* which channels are */
+                                        /* present in stream  */
+    GUID            SubFormat;
+} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
+#endif
+
+#endif
+
+static const int channel_mask[] = {
+  SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_RIGHT  | SPEAKER_LOW_FREQUENCY,
+  SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_RIGHT  | SPEAKER_BACK_LEFT    | SPEAKER_BACK_RIGHT,
+  SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_RIGHT  | SPEAKER_BACK_LEFT    | SPEAKER_BACK_RIGHT   | SPEAKER_LOW_FREQUENCY,
+  SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT  | SPEAKER_BACK_LEFT    | SPEAKER_BACK_RIGHT     | SPEAKER_LOW_FREQUENCY
+};
+
+static HINSTANCE hdsound_dll = NULL;      ///handle to the dll
+static LPDIRECTSOUND hds = NULL;          ///direct sound object 
+static LPDIRECTSOUNDBUFFER hdsbuf = NULL; ///direct sound buffer
+static int buffer_size = 0;               ///size in bytes of the direct sound buffer   
+static int write_offset = 0;              ///offset of the write cursor in the direct sound buffer
+static int min_free_space = 4096;         ///if the free space is below this value get_space() will return 0
+
+#define BUFFERSIZE 32767		          /// in samples - at 48khz 0.6 sec buffer, gets multiplied with nBlockAlign
+
+/***************************************************************************************/
+
+/**
+\brief output error message
+\param err error code
+\return string with the error message
+*/
+static char * dserr2str(int err)
+{
+	switch (err) {
+		case DS_OK: return "DS_OK";
+		case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
+		case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
+		case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
+		case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
+		case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
+		case DSERR_GENERIC: return "DSERR_GENERIC";
+		case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
+		case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
+		case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
+		case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
+		case DSERR_NODRIVER: return "DSERR_NODRIVER";
+		case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
+		case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
+		case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
+		case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
+		case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
+		case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
+		case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
+		default: return "unknown";
+	}
+}
+
+/**
+\brief uninitialize direct sound
+*/
+static void UninitDirectSound(void)
+{
+    // finally release the DirectSound object
+    if (hds) {
+    	IDirectSound_Release(hds);
+    	hds = NULL;
+    }
+    // free DSOUND.DLL
+    if (hdsound_dll) {
+    	FreeLibrary(hdsound_dll);
+    	hdsound_dll = NULL;
+    }
+	mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound uninitialized\n");
+}
+
+/**
+\brief initilize direct sound
+\return 0 if error, 1 if ok
+*/
+static int InitDirectSound(void)
+{
+	DSCAPS dscaps;
+
+	// initialize directsound
+    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
+	hdsound_dll = LoadLibrary("DSOUND.DLL");
+	if (hdsound_dll == NULL) {
+		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot load DSOUND.DLL\n");
+		return 0;
+	}
+	OurDirectSoundCreate = (void*)GetProcAddress(hdsound_dll, "DirectSoundCreate");
+
+	if (OurDirectSoundCreate == NULL) {
+		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: GetProcAddress FAILED\n");
+		FreeLibrary(hdsound_dll);
+		return 0;
+	}
+
+	// Create the direct sound object
+	if FAILED(OurDirectSoundCreate(NULL, &hds, NULL )) {
+		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create a DirectSound device\n");
+		FreeLibrary(hdsound_dll);
+		return 0;
+	}
+
+	/* Set DirectSound Cooperative level, ie what control we want over Windows
+	 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
+	 * settings of the primary buffer, but also that only the sound of our
+	 * application will be hearable when it will have the focus.
+	 * !!! (this is not really working as intended yet because to set the
+	 * cooperative level you need the window handle of your application, and
+	 * I don't know of any easy way to get it. Especially since we might play
+	 * sound without any video, and so what window handle should we use ???
+	 * The hack for now is to use the Desktop window handle - it seems to be
+	 * working */
+	if (IDirectSound_SetCooperativeLevel(hds, GetDesktopWindow(), DSSCL_EXCLUSIVE)) {
+		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot set direct sound cooperative level\n");
+		IDirectSound_Release(hds);
+		FreeLibrary(hdsound_dll);
+		return 0;
+	}
+	mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound initialized\n");
+
+	memset(&dscaps, 0, sizeof(DSCAPS));
+	dscaps.dwSize = sizeof(DSCAPS);
+	if (DS_OK == IDirectSound_GetCaps(hds, &dscaps)) {
+		if (dscaps.dwFlags & DSCAPS_EMULDRIVER) mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
+	} else {
+		mp_msg(MSGT_AO, MSGL_V, "ao_dsound: cannot get device capabilities\n");
+	}
+
+	return 1;
+}
+
+/**
+\brief destroy the direct sound buffer
+*/
+static void DestroyBuffer(void)
+{
+	if (hdsbuf) {
+		IDirectSoundBuffer_Release(hdsbuf);
+		hdsbuf = NULL;
+	}
+}
+
+/**
+\brief fill sound buffer
+\param data pointer to the sound data to copy
+\param len length of the data to copy in bytes
+\return number of copyed bytes
+*/
+static int write_buffer(unsigned char *data, int len)
+{
+  HRESULT res;
+  LPVOID lpvPtr1; 
+  DWORD dwBytes1; 
+  LPVOID lpvPtr2; 
+  DWORD dwBytes2; 
+	
+  // Lock the buffer
+  res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); 
+  // If the buffer was lost, restore and retry lock. 
+  if (DSERR_BUFFERLOST == res) 
+  { 
+    IDirectSoundBuffer_Restore(hdsbuf);
+	res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+  }
+ 
+  
+  if (SUCCEEDED(res)) 
+  {
+  	// Write to pointers. 
+	memcpy(lpvPtr1,data,dwBytes1);
+    if (NULL != lpvPtr2 )memcpy(lpvPtr2,data+dwBytes1,dwBytes2);
+	write_offset+=dwBytes1+dwBytes2;
+    if(write_offset>=buffer_size)write_offset-=buffer_size;
+	
+   // Release the data back to DirectSound. 
+    res = IDirectSoundBuffer_Unlock(hdsbuf,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2);
+    if (SUCCEEDED(res)) 
+    { 
+	  // Success. 
+	  DWORD status;
+	  IDirectSoundBuffer_GetStatus(hdsbuf, &status);
+      if (!(status & DSBSTATUS_PLAYING)){
+	    res = IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
+	  }
+	  return dwBytes1+dwBytes2; 
+    } 
+  } 
+  // Lock, Unlock, or Restore failed. 
+  return 0;
+}
+
+/***************************************************************************************/
+
+/**
+\brief handle control commands
+\param cmd command
+\param arg argument
+\return CONTROL_OK or -1 in case the command can't be handled
+*/
+static int control(int cmd, void *arg)
+{
+	DWORD volume;
+	switch (cmd) {
+		case AOCONTROL_GET_VOLUME: {
+			ao_control_vol_t* vol = (ao_control_vol_t*)arg;
+			IDirectSoundBuffer_GetVolume(hdsbuf, &volume);
+			vol->left = vol->right = (float)(volume+10000) / 100.0;
+			//printf("ao_dsound: volume: %f\n",vol->left);
+			return CONTROL_OK;
+		}
+		case AOCONTROL_SET_VOLUME: {
+			ao_control_vol_t* vol = (ao_control_vol_t*)arg;
+			volume = (vol->right * 100.0)-10000;
+			IDirectSoundBuffer_SetVolume(hdsbuf, volume);
+			//printf("ao_dsound: volume: %f\n",vol->left);
+			return CONTROL_OK;
+		}
+	}
+	return -1;
+}
+
+/** 
+\brief setup sound device
+\param rate samplerate
+\param channels number of channels
+\param format format
+\param flags unused
+\return 1=success 0=fail
+*/
+static int init(int rate, int channels, int format, int flags)
+{
+    int res;
+	if (!InitDirectSound()) return 0;
+
+	// ok, now create the primary buffer
+	WAVEFORMATEXTENSIBLE wformat;
+	DSBUFFERDESC dsbdesc;
+
+	//fill global ao_data
+	ao_data.channels = channels;
+	ao_data.samplerate = rate;
+	ao_data.format = format;
+	ao_data.bps = channels * rate * (audio_out_format_bits(format)>>3);
+	if(ao_data.buffersize==-1)
+	{
+		ao_data.buffersize = audio_out_format_bits(format) >> 3;
+		ao_data.buffersize *= channels;
+		ao_data.buffersize *= BUFFERSIZE;
+	}
+	mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, audio_out_format_name(format));
+	mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, BUFFERSIZE * 1000 / rate);
+
+	//fill waveformatex
+	ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
+	wformat.Format.cbSize          = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE) : 0;
+	wformat.Format.nChannels       = channels;
+	wformat.Format.nSamplesPerSec  = rate;
+	if (format == AFMT_AC3) {
+		wformat.Format.wFormatTag      = WAVE_FORMAT_DOLBY_AC3_SPDIF;
+		wformat.Format.wBitsPerSample  = 16;
+		wformat.Format.nBlockAlign     = 4;
+	} else {
+		wformat.Format.wFormatTag      = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
+		wformat.Format.wBitsPerSample  = audio_out_format_bits(format);
+		wformat.Format.nBlockAlign     = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
+	}
+
+    // fill in the direct sound buffer descriptor
+	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
+	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
+	dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
+	                | DSBCAPS_GLOBALFOCUS         /** Allows background playing */
+	                | DSBCAPS_CTRLVOLUME;         /** volume control enabled */
+
+	if (channels > 2) {
+		wformat.dwChannelMask = channel_mask[channels - 3];
+		wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+		wformat.Samples.wValidBitsPerSample = audio_out_format_bits(format);
+		// Needed for 5.1 on emu101k - shit soundblaster
+		dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
+	}
+	wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign;
+
+	dsbdesc.dwBufferBytes = wformat.Format.nBlockAlign * BUFFERSIZE;
+	dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
+	buffer_size = dsbdesc.dwBufferBytes;
+	ao_data.outburst = wformat.Format.nBlockAlign * 512;
+
+	// now create the sound buffer
+
+	res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
+	if (res != DS_OK) {
+		if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
+			// Try without DSBCAPS_LOCHARDWARE
+			dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
+			res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
+		}
+		if (res != DS_OK) {
+			UninitDirectSound();
+			mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary buffer (%s)\n", dserr2str(res));
+			return 0;
+		}
+	}
+	mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary buffer created\n");
+	return 1;
+}
+
+
+
+/**
+\brief stop playing and empty buffers (for seeking/pause)
+*/
+static void reset()
+{
+	IDirectSoundBuffer_Stop(hdsbuf);
+	// reset directsound buffer
+	IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
+	write_offset=0;
+}
+
+/**
+\brief stop playing, keep buffers (for pause)
+*/
+static void audio_pause()
+{
+	IDirectSoundBuffer_Stop(hdsbuf);
+}
+
+/**
+\brief resume playing, after audio_pause()
+*/
+static void audio_resume()
+{
+	IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
+}
+
+/** 
+\brief close audio device
+\param immed stop playback immediately, currently not supported
+*/
+static void uninit(int immed)
+{
+	reset();
+	DestroyBuffer();
+	UninitDirectSound();
+}
+
+/**
+\brief find out how many bytes can be written into the audio buffer without
+\return free space in bytes, has to return 0 if the buffer is almost full
+*/
+static int get_space()
+{
+	int space;
+	DWORD play_offset;
+	IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
+	space=buffer_size-(write_offset-play_offset);                                             
+	//                |                                                      | <-- const --> |                |                 |
+	//                buffer start                                           play_cursor     write_cursor     write_offset      buffer end
+	// play_cursor is the actual postion of the play cursor
+	// write_cursor is the position after which it is assumed to be save to write data
+	// write_offset is the postion where we actually write the data to
+	if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
+	if(space < min_free_space)return 0;
+	return space;
+}
+
+/**
+\brief play 'len' bytes of 'data'
+\param data pointer to the data to play
+\param len size in bytes of the data buffer, gets rounded down to outburst*n
+\param flags currently unused
+\return number of played bytes
+*/
+static int play(void* data, int len, int flags)
+{
+	len = (len / ao_data.outburst) * ao_data.outburst;
+	return write_buffer(data, len);
+}
+
+/**
+\brief get the delay between the first and last sample in the buffer
+\return delay in seconds
+*/
+static float get_delay()
+{
+	DWORD play_offset;
+	int space;
+	IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
+	space=play_offset-write_offset;                                             
+	if(space <= 0)space += buffer_size;
+	return (float)(buffer_size - space) / (float)ao_data.bps;
+}
--- a/libao2/audio_out.c	Sat Sep 25 13:46:21 2004 +0000
+++ b/libao2/audio_out.c	Sat Sep 25 15:34:42 2004 +0000
@@ -53,6 +53,9 @@
 #ifdef HAVE_WIN32WAVEOUT
 extern ao_functions_t audio_out_win32;
 #endif
+#ifdef HAVE_DIRECTX
+extern ao_functions_t audio_out_dsound;
+#endif
 #ifdef HAVE_DXR2
 extern ao_functions_t audio_out_dxr2;
 #endif
@@ -69,6 +72,9 @@
         &audio_out_dxr2,
 #endif
 // native:
+#ifdef HAVE_DIRECTX
+        &audio_out_dsound,
+#endif
 #ifdef HAVE_WIN32WAVEOUT
         &audio_out_win32,
 #endif