Sommaire
- Introduction ý DirectSound
- CharactÈristiques du son numerique, de la carte son et des buffers son
- Utilisation de DirectSound
- Utilisation de 3D Sound
- DirectSound & DirectMusic 8.1
Introduction au DirectSound
Qu'est ce qu'on peut faire avec DirectSound ?
- Jouer des sons statiques
- Sortie audio temps-réel
- Spatialiser le son en 3D
- Entrée audio
Charactéristiques du son numerique, de la carte son et des buffers son
Charactéristiques
- Taux d'échantillonnage (samples per second, sample rate)
- Nombre de canaux : mono, stéréo, ... (channels)
- Format des échantillons : PCM (Pulse Code Modulation), muLaw, ... (format)
- Taille d'un bloc d'échantillons (bloc align, frame size)
Charactéristiques format PCM
Taille des échantillons (8 bits, 16 bits, ...)
Taille d'un bloc = nombre de canaux x taille d'un échantillon en octets
8 ou 16 bits per sample -> 1 ou 2 octets
Types primitives à utiliser pour DirectSound
- son 8 bits : unsigned char
- son 16 bits : signed short (little endian)
Charactéristiques de la carte son
- Taux d'échantillonnage
- Nombre de canaux
- Format des échantillons
- Mémoire RAM sur la carte
- Traitement de son sur la carte (mixage, 3D, ...)
- Nombre de buffers maximale
- Taille du buffer interne
- Pilote DirectSound (WDM) ou pilote WAVE (MME)
Charactéristiques des buffers son
- Statique ou dynamique
- Primaire ou sécondaire
- Software ou hardware
Buffers statiques
Un buffer statique contient un son entièrement.
Buffers dynamiques
L'application fournit les échantillons au
furs et à mesure pendant l'éxecution. L'application et
le pilote de la carte son accèdent au buffer de manière
concurrente : l'application remplie le buffer, le pilote le vide. Les
buffers dynamique sont aussi appelés des "streaming
buffers". Ils sont réalisé comme des buffers
circulaires.
Buffer primaire
Dans DirectSound, il n'existe qu'un seul buffer
primaire, qui est partagé par toutes les applications. Le buffer
primaire est un buffer dynamique.
Buffer sécondaire
Les applications créent des buffers
sécondaires pour l'utilisation interne. Les buffers
sécondaires peuvent être statiques ou dynamiques. Tous
les buffers sécondaires sont mixés dans le buffer
primaire avant la sortie. En général, il est
consillé que les buffers sécondaires ont le même
format que le buffer primaire. Cela évite des conversion de
format pendant le mixage.
Buffers software
Le buffer est alloué dans la mémoire
vive de l'ordinateur. Le buffer est gÈrÈ par le pilote en
"software".
Buffers hardware
Le buffer est gÈrÈ par la carte.
Utilisation de DirectSound
Résumé
- Fichier d'entête à inclure : #include <dsound.h>
- Bibliothèque à utiliser : dsound.lib
- #define INITGUID en haut du fichier
- Récupérer la liste des devices
- Créer l'objet DirectSound : DirectSoundCreate()
- Création du buffer primaire
- Création des buffers sécondaires
Récupérer la liste des devices audio disponibles
Utiliser la function
DirectSoundEnumerate(LPDSENUMCALLBACK callback, LPVOID NULL).
/* The dsdevice_t structure contains the GUID and the name of a DirectSound
* device. The structures are chained together in a linked list. */
typedef struct _dsdevice_t dsdevice_t;
struct _dsdevice_t {
dsdevice_t* next;
LPGUID guid;
char* description;
};
/* The global list of DirectSound devices */
static dsdevice_t* dsdevice_list = NULL;
/* Allocate and inialize a new dsdevice_t. The newly allocated object
will be * inserted in the global list of DirectSound devices. */
dsdevice_t* new_dsdevice(LPGUID guid, const char* description)
{
dsdevice_t* dev = (dsdevice_t*) malloc(sizeof(dsdevice_t));
dev->next = NULL;
dev->guid = NULL;
dev->description = (description ? strdup(description)
: strdup("unknown device"));
if (guid != NULL) {
dev->guid = malloc(sizeof(GUID));
memcpy(dev->guid, guid, sizeof(GUID));
}
return dev;
}
/* The callback function used in the enumaration of the DirectSound
* devices. */
static BOOL
ds_enum_callback(LPGUID guid, LPCSTR description, LPCSTR module, LPVOID
context)
{
dsdevice_t* dev = new_dsdevice(guid, description);
dev->next = dsdevice_list;
dsdevice_list = dev;
return TRUE;
}
dsdevice_t* ds_get_device_list()
{
if (dsdevice_list == NULL) {
/* Enumarate the list of devices. For every device, the function
* ds_enum_callback * will be called. */
DirectSoundEnumerate((LPDSENUMCALLBACK) ds_enum_callback, NULL);
}
return dsdevice_list;
}
Créer l'object DirectSound
Résumé :
- DirectSoundCreate : crée l'objet DirectSound
- SetCooperativeLevel : spécifie les droits d'accès au buffer primaire
- GetCaps : renvoit les information sur les capacités du device
- SetSpeakerConfig : définie la configuration des haut-parleurs (stéréo, 5.1, ...)
Note importante : Quand on crée l'objet
DirectSound avec GUID égale à NULL, on ouvre le device
par défaut. Le device par défaut est celui qui est
spécifié dans les panneaux de contrôle de
Windows.
Pour la création de l'objet DirectSound, il
faut passer la référence d'une fenêtre en
argument. (Je ne comprend pas trop pourquoi.)
#define INITGUID
#include
typedef struct {
LPGUID guid;
LPDIRECTSOUND direct_sound;
} audioport_t;
audioport_t* new_audioport(LPGUID guid, HWND wnd)
{
audioport_t* audioport;
DSCAPS caps;
HRESULT hr;
/* Allocate space for the audio port object */
audioport = (audioport_t*) malloc(sizeof(audioport_t));
if (audioport == NULL) {
/* ... */
}
/* Initialize the audio port object */
audioport->guid = guid;
audioport->direct_sound = NULL;
/* Create the DirectSound object with the given GUID */
hr = DirectSoundCreate(audioport->guid, &audioport->direct_sound, NULL);
if (hr != DS_OK) {
/* Handle error */
}
/* Set the cooperative level */
hr = IDirectSound_SetCooperativeLevel(audioport->direct_sound, wnd,
DSSCL_PRIORITY);
if (hr != DS_OK) {
/* Handle error */
}
/* Get the capabilities of the device */
caps.dwSize = sizeof(caps);
hr = IDirectSound_GetCaps(audioport->direct_sound, &caps);
if (hr == DS_OK) {
/* Use the information about the device capabilities */
}
return audioport;
}
void delete_audioport(audioport_t* audioport)
{
/* Release the DirectSound object */
if (audioport->direct_sound != NULL) {
IDirectSound_Release(audioport->direct_sound);
}
free(audioport);
}
Différences entre C et C++
En C, les objets DirectSound sont des tableaux de
fonctions (interfaces). Pour appeler la function GetCaps() sur un
objet de type IDirectSound, on utilise
IDirectSound_GetCaps(direct_sound). Le premier argument est toujours
la reference retourné à la création de
l'objet.
En C++, les objets DirectSound utilisent la
notation standard, à savoir :
referenceObjet->nomMethode(arguments). Pour appeler la function
GetCaps(), on utilise directSound->GetCaps().
L'exemple précédent,
réécrit en C++, est donné ci-dessous :
#define INITGUID
#include
class AudioPort
{
public:
AudioPort(LPGUID guid, HWND wnd);
virtual ~AudioPort();
private:
LPGUID _guid;
LPDIRECTSOUND _directSound;
};
AudioPort::AudioPort(LPGUID guid, HWND wnd)
{
DSCAPS caps;
/* Initialize the audio port object */
_guid = guid;
_directSound = NULL;
/* Create the DirectSound object with the given GUID */
hr = DirectSoundCreate(_guid, &_directSound, NULL);
if (hr != DS_OK) {
/* Handle error */
}
/* Set the cooperative level */
hr = _directSound->SetCooperativeLevel(wnd, DSSCL_PRIORITY);
if (hr != DS_OK) {
/* Handle error */
}
/* Get the capabilities of the device */
caps.dwSize = sizeof(caps);
hr = _directSound->GetCaps(&caps);
if (hr == DS_OK) {
/* Use the information about the device capabilities */
}
}
AudioPort::~AudioPort()
{
/* Release the DirectSound object */
if (_directSound != NULL) {
_directSound->Release();
}
}
Les niveaux de coopération de DirectSound
- DSSCL_WRITEPRIMARY
- DSSCL_EXCLUSIVE, DSSCL_PRIORITY
- DSSCL_NORMAL
Analyser les capacités du device
- IDirectSound::GetCaps()
- Nombre de buffers maximales (2D et 3D)
- Taille mémoire RAM
- ...
Création du buffer primaire
Résumé :
- CreateSoundBuffer : crée un buffer. Il faut passer
l'option DSBCAPS_PRIMARYBUFFER pour créer un buffer primaire.
- SetFormat : définie le format du buffer
primaire. Pour changer le format, il faut avoir un accès
priviligié au buffer primaire.
- Play : démarre le buffer primaire. Il faut passer
l'option DSBPLAY_LOOPING en argument.
La création d'un buffer prend en argument la
référence d'une structure de type DSBUFFERDESC. Pour créer un buffer
primaire, le champ le plus important est :
- dwBufferBytes : la taille du buffer en octets.
- lpwfxFormat : référence vers une structure de type
WAVEFORMATEX, qui décrit le format du son
- dwFlags : des options pour le buffers
Pour la création d'un buffer primaire, on ne
spécifie pas dwBufferBytes (définie par la carte son), lpwfxFormat
doit être égale à NULL, et dwFlags doit contenir
DSBCAPS_PRIMARYBUFFER.
typedef struct {
LPGUID guid;
LPDIRECTSOUND direct_sound;
LPDIRECTSOUNDBUFFER primary_buffer;
} audioport_t;
audioport_t* new_audioport(LPGUID guid, HWND wnd)
{
audioport_t* audioport;
DSCAPS caps;
HRESULT hr;
DSBUFFERDESC desc;
WAVEFORMATEX format;
/* ... */
/* Create the primary buffer */
/* Initialize the description of the primary buffer. */
ZeroMemory(&desc, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
/* If you want to allocate the primary buffer on the sound card, you
* have to set the DSBCAPS_LOCHARDWARE flag */
if (caps.dwFreeHwMixingStreamingBuffers > 0) {
desc.dwFlags |= DSBCAPS_LOCHARDWARE;
}
hr = IDirectSound_CreateSoundBuffer(audioport->direct_sound, &desc,
&audioport->primary_buffer, NULL);
if (hr != DS_OK) {
/* Handle error */
}
/* Initalize de format structure for CD quality audio: 2 channels,
* 44100 Hz sample rate, 16 bits per sample. */
ZeroMemory(&format, sizeof(WAVEFORMATEX));
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = 2;
format.nSamplesPerSec = 44100;
format.wBitsPerSample = 16;
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
format.cbSize = sizeof(WAVEFORMATEX);
/* Set the audio format of the primary sound buffer. */
hr = IDirectSoundBuffer_SetFormat(audioport->primary_buffer, &format);
if (hr != DS_OK) {
/* Handle error */
}
/* Start playing the primary buffer. */
hr = IDirectSoundBuffer_Play(audioport->primary_buffer, 0, 0,
DSBPLAY_LOOPING);
if (hr != DS_OK) {
/* Handle error */
}
/* ... */
}
Création d'un buffer sécondaire pour un son statique
Résumé :
- CreateSoundBuffer : crée un buffer.
- Lock : bloquer le buffer pour l'Ècriture.
- Ecrire les Èchantillons dans le buffer.
- Unlock : rel’cher le buffer.
La création d'un buffer sécondaire prend en argument
la référence d'une structure de type DSBUFFERDESC. Les champs les plus
importants sont :
- dwBufferBytes : la taille du buffer en octets.
- lpwfxFormat : référence vers une structure de type
WAVEFORMATEX qui décrit le format du son.
- dwFlags : des options du buffer.
Les options incluent :
- le choix d'allouer le buffer sur la carte ou en
mémoire vive (DSBCAPS_LOCHARDWARE, DSBCAPS_LOCSOFTWARE,
DSBCAPS_LOCDEFER, DSBCAPS_STATIC)
- le choix de continuer à jouer le buffer quand
l'application perd le premier plan (DSBCAPS_GLOBALFOCUS,
DSBCAPS_STICKYFOCUS)
- le choix de pouvoir contrôler certain paramètres
(DSBCAPS_CTRLPAN, DSBCAPS_VOLUME, DSBCAPS_FREQUENCY)
- les options 3D (voir plus bas)
LPDIRECTSOUND audioport_get_direct_sound(audioport_t* audioport)
{
return audioport->direct_sound;
}
typedef struct {
audioport_t* audioport;
LPDIRECTSOUNDBUFFER buffer;
} static_sound_t;
static_sound_t*
new_static_sound(audioport_t* audioport, int size, short* sample_data)
{
LPDIRECTSOUND direct_sound;
DSBUFFERDESC desc;
WAVEFORMATEX format;
void *buf;
static_sound_t* snd;
snd = (static_sound_t*) calloc(sizeof(static_sound_t));
snd->audioport = audioport;
direct_sound = audioport_get_direct_sound(audioport);
/* Initalize de format structure for CD quality audio: 2 channels,
* 44100 Hz sample rate, 16 bits per sample. */
ZeroMemory(&format, sizeof(WAVEFORMATEX));
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = 2;
format.nSamplesPerSec = 44100;
format.wBitsPerSample = 16;
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
format.cbSize = sizeof(WAVEFORMATEX);
/* Initialize the description of the secondary buffer. */
ZeroMemory(&desc, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = 0;
/* DSBCAPS_GLOBALFOCUS specifies that the buffer continues playing,
* even when the application is in the background.*/
desc.dwFlags |= DSBCAPS_GLOBALFOCUS;
/* Try to allocate the buffer in hardware, if possible. Otherwise,
allocate it in RAM memory. */
desc.dwFlags |= DSBCAPS_STATIC;
if (caps.dwFreeHwMixingStreamingBuffers > 0) {
desc.dwFlags |= DSBCAPS_LOCHARDWARE;
}
desc.lpwfxFormat = &format;
desc.dwBufferBytes = size * format.nBlockAlign;
desc.dwReserved = 0;
/* Create the secondary buffer. */
hr = IDirectSound_CreateSoundBuffer(direct_sound, &desc,
&snd->buffer, NULL);
if (hr != DS_OK) {
/* Handle error */
}
/* Lock the buffer to have write access to it. */
IDirectSoundBuffer_Lock(snd->buffer, 0, desc.dwBufferBytes, &buf,
&bytes, NULL, NULL, 0);
/* Copy the samples to the buffer. */
memcpy(buf, sample_data, desc.dwBufferBytes);
/* Unlock the buffer again. */
IDirectSoundBuffer_Unlock(snd->buffer, 0, desc.dwBufferBytes, &buf,
&bytes, NULL, NULL, 0);
return snd;
}
void delete_static_sound(static_sound_t* snd)
{
IDirectSoundBuffer_Stop(snd->buffer);
IDirectSoundBuffer_Release(snd->buffer);
free(snd);
}
void static_sound_play(static_sound_t* snd)
{
IDirectSoundBuffer_SetCurrentPosition(snd->buffer, 0);
IDirectSoundBuffer_Play(snd->buffer, 0, 0, 0);
}
void static_sound_stop(static_sound_t* snd)
{
IDirectSoundBuffer_Stop(snd->buffer);
}
Techniques de synchronisation
- EvÈnements de notification : l'interface DirectSoundNotify
- Position d'Ècriture : fonction GetPosition
EvÈnements de notification
Fonctionne seulement sur des buffers software.
- IDirectSoundBuffer::QueryInterface: rÈcupÈrer l'interface DirectSoundNotify
- CrÈer DSBPOSITIONNOTIFY; crÈer ÈvÈnements CreateEvent
- IDirectSoundNotify8::SetNotificationPositions: placer des ÈvÈnement ý des endroits prÈcis
Position d'Ècriture
GetPosition() renvoie la position de la tÍte de
lecture et la position ý laquelle un peut Ècrire les Èchantillons.
Buffers dynamiques
Résumé
- Création des buffers sécondaires
- Jouer le buffer en boucle (Play(DSBPLAY_LOOPING))
- Utiliser les ÈvÈnements de notification ou GetPosition() pour la synchronisation
- Lock et Unlock du buffer pour y accÈder et Ècrire les Èchantillons
Utilisation de 3D Sound
Concepts
- L'auditeur
- Les buffers 3D
cfr. OpenAL
- L'auditeur
- Les buffers
- Les sources
Résumé
- Création de l'auditeur
- Création des buffers sécondaires
- Création des interfaces spécialisées pour le 3D
Création de l'auditeur
Résumé
- Création du buffer primaire
- Récuperer l'interface de l'auditeur
- Spécifier les paramètres 3D'
Création du buffer primaire
Voir plus haut. Il faut créer le buffer avec
l'option DSBCAPS_3D. Allouér le buffer sur la carte (hardware
buffer).
Créer l'interface de l'auditeur
Récupérer l'interface IDirectSound3DListener du
buffer primaire.
LPDIRECTSOUNDBUFFER
audioport_get_primary_buffer(audioport_t* audioport)
{
return audioport->primary_buffer;
}
typedef struct {
LPDIRECTSOUNDLISTENER listener;
} listener_t;
listener_t* new_listener(audioport_t* audioport)
{
HRESULT hr;
LPDIRECTSOUNDBUFFER primary_buffer;
listener_t* l;
/* Allocate a new lister_t object. */
l = (listener_t*) malloc(sizeof(listener_t));
if (l == NULL) {
/* Handle error */
}
/* Obtain the primary buffer. */
primary_buffer = audioport_get_primary_buffer(audioport);
/* Get listener interface. The listener interface is a special
* interface of the primary buffer. */
hr = IDirectSoundBuffer_QueryInterface(primary_buffer,
IID_IDirectSound3DListener,
(LPVOID *)&lp3DListener);
if (hr != DS_OK) {
/* Handle error */
}
return l;
}
Spécifier les paramètres 3D de l'auditeur
- Position, orientation et vélocité
- Facteur de distance
- Facteur Doppler
- Facteur "rolloff"
Position
GetPosition, SetPosition
Vélocité
GetVelocity, SetVelocity
Orientation
GetOrientation, SetOrientation
Facteur de distance
Mètres par unité de vecteur, GetDistanceFactor, SetDistanceFactor
Facteur Doppler
GetDopplerFactor, SetDopplerFactor
Facteur "rolloff"
Attenuation due à la distance, GetRolloffFactor, SetRolloffFactor
Création des buffers 3D
Résumé
- Création du buffer secondaire
- Récuperer l'interface 3D du buffer
- Spécifier les paramètres 3D
Création du buffer sécondaire
Pour chaque source sonore, il faut créer un buffer
sécondaire. Il faut spécifier l'option DSBCAPS_3D à la création. De
préférence allouer le buffer sur la carte. Ne pas utiliser
DSBCAPS_PAN.
On peut spécifier l'algorithme 3D à
utiliser. Cela à surtout une importance quand le buffer est
alloué en "software" (DS3DALG_NO_VIRTUALIZATION,
DS3DALG_HRTF_FULL, DS3DALG_HRTF_LIGHT).
Récuperer l'interface 3D du buffer
- Créer un buffer sécondaire avec l'option DSBCAPS_3D.
- Récuperer l'interface 3D avec la function QueryInterface.
typedef struct {
audioport_t* audioport;
LPDIRECTSOUNDBUFFER buffer;
LPDIRECTSOUND3DBUFFER buffer_3d;
} static_sound_t;
static_sound_t*
new_static_sound(audioport_t* audioport, int size, short* sample_data)
{
/* Create secondary buffer */
/* ... */
hr = IDirectSoundBuffer_QueryInterface(snd->buffer, (LPVOID*) &snd->buffer_3d)
}
Spécifier les paramètres 3D
- Position et vélocité
- Distance minimale et maximale
- Cones
- Mode
Position et vélocité
SetPosition, GetPosition, SetVelocity, GetVelocity
Distance minimale et maximale
SetMaxDistance, SetMinDistance, GetMaxDistance, GetMinDistance
Quand le buffer a été créé avec l'option
DSBCAPS_MUTE3DATMAXDISTANCE, le buffer est désactivé quand la distance
maximale est atteinte (gain de CPU).
Cones
- Orientation
- Angles
- Volume externe
Mode
- DS3DMODE_DISABLE : pas de 3D
- DS3DMODE_HEADRELATIVE : position et velocité relatives à l'auditeur
- DS3DMODE_NORMAL : position et velocité absolues
Examples
- jMax
- NeL
- MusicSpace
DirectSound 8.1
- DirectMusic : DirectMusic est devenu beaucoup plus
important et englobe DirectSound. Selon Microsoft, c'est l'API
conseillé.
- Différences dans la nomenclature : Toutes les
interface ont un suffix de "8". Les anciens noms continuent de
fonctionner.
- Les effets : Reverbe, chorus, ...
DLS2 : Downloadable sounds
Autres
- Band
- Chordmaps
- Transition
|