2010-01-22 10 views
3

À droite, je travaille sur l'implémentation de DirectSound dans notre application voip Delphi (l'application permet aux radios d'être utilisées par plusieurs utilisateurs via une connexion réseau) Les données arrivent via les diffusions UDP. Comme c'est le cas maintenant, nous descendons sur un niveau de données brutes et faisons le mixage audio de plusieurs sources nous-mêmes et avons un composant centralisé qui est utilisé pour jouer tout ce retour. L'application elle-même est une application Delphi 5 et je suis chargé de la porter vers Delphi 2010. Une fois que j'ai eu cette partie de lecture audio, nous avons conclu qu'il est préférable de se débarrasser de cet ancien code et de remplacer avec directsound. Donc, l'idée est d'avoir un SecondaryBuffer par radio (nous avons un 'panneau' par connexion radio, basé sur un ensemble de composants que nous créons pour chaque radio spécifique) et nous laissons simplement ces données ajouter leurs données respectives SecondaryBuffers chaque fois qu'ils obtiennent des données, s'arrêtant seulement pour remplir une demi-seconde de données audio dans le tampon s'il manque de données. Maintenant, je suis bloqué sur la partie où j'ajoute des données aux tampons dans mon application de test où j'essaye simplement de faire fonctionner correctement ceci avant que je commence à écrire un composant pour l'utiliser comme je le souhaite. nous voulons.Directsound avec le tampon de diffusion - Le verrouillage ne s'enroule pas! Utilisation des en-têtes DirectX portés pour Delphi

J'utilise les en-têtes DirectX portés pour Delphi (http://www.clootie.ru/delphi/download_dx92.html)

Le point de ces en-têtes est au port sur l'interface DirectSound régulière à Delphes, alors je espère que les programmeurs non Delphi avec DirectSound peuvent savoir ce que la cause de mon problème est aussi bien.

Mon SecondaryBuffer (IDirectSoundBuffer) a été créé comme suit:

var 
    BufferDesc: DSBUFFERDESC; 
    wfx: tWAVEFORMATEX; 


wfx.wFormatTag := WAVE_FORMAT_PCM; 
wfx.nChannels := 1; 
wfx.nSamplesPerSec := 8000; 
wfx.wBitsPerSample := 16; 
wfx.nBlockAlign := 2; // Channels * (BitsPerSample/2) 
wfx.nAvgBytesPerSec := 8000 * 2; // SamplesPerSec * BlockAlign 

BufferDesc.dwSize := SizeOf(DSBUFFERDESC); 
BufferDesc.dwFlags := (DSBCAPS_GLOBALFOCUS or DSBCAPS_GETCURRENTPOSITION2 or DSBCAPS_CTRLPOSITIONNOTIFY); 
BufferDesc.dwBufferBytes := wfx.nAvgBytesPerSec * 4; //Which should land at 64000 
BufferDesc.lpwfxFormat := @wfx; 


case DSInterface.CreateSoundBuffer(BufferDesc, DSCurrentBuffer, nil) of 
    DS_OK: ; 
    DSERR_BADFORMAT: ShowMessage('DSERR_BADFORMAT'); 
    DSERR_INVALIDPARAM: ShowMessage('DSERR_INVALIDPARAM'); 
    end; 

je suis parti sur les parties où je définissais mon PrimaryBuffer (il est mis à jouer avec le drapeau en boucle et a été créé exactement comme MSDN dit qu'il devrait être) et la DSInterface, mais c'est comme vous pouvez l'imaginer une IDirectSoundInterface.

Maintenant, chaque fois que je reçois un message audio (détecté, décodé et converti au format audio appropriée par d'autres composants que nous avons fait qui ont été confirmés au travail depuis plus de sept ans), je fais ce qui suit:

AudioData contient les données dans mon message.
DSCurrentBuffer.Lock(0, 512, @FirstPart, @FirstLength, @SecondPart, @SecondLength, DSBLOCK_FROMWRITECURSOR); 
Move(AudioData, FirstPart^, FirstLength); 
if SecondLength > 0 then 
    Move(AudioData[FirstLength], SecondPart^, SecondLength); 

DSCurrentBuffer.GetStatus(Status); 
DSCurrentBuffer.GetCurrentPosition(@PlayCursorPosition, @WriteCursorPosition); 
if (FirstPart <> nil) or (SecondPart <> nil) then 
    begin 
    Memo1.Lines.Add('FirstLength = ' + IntToStr(FirstLength)); 
    Memo1.Lines.Add('PlayCursorPosition = ' + IntToStr(PlayCursorPosition)); 
    Memo1.Lines.Add('WriteCursorPosition = ' + IntToStr(WriteCursorPosition)); 
    end; 
DSCurrentBuffer.Unlock(@FirstPart, FirstLength, @SecondPart, SecondLength); 

Messages contient toujours 512 octets de données audio. J'ai ajouté les lignes Memo1.Lines.Add pour être en mesure d'obtenir une sortie de débogage (puisque l'utilisation de breakpoints ne fonctionne pas tout à fait, car DirectSounds continue de lire le contenu du buffer primaire indépendamment)

Maintenant, quand je suis Jouer mon DSCurrentBuffer en utilisant l'indicateur de bouclage (qui, selon le MSDN docs est suffisant pour en faire un Streaming Buffer) et avoir ce code qui fonctionne comme il veut, mon texte de sortie dans le mémo montre que je suis autorisé à écrire jusqu'à la fin du tampon ... Mais ça ne s'enroule pas.

SecondPart est toujours nul. Il ne tourne jamais au début de la mémoire tampon, ce qui signifie que je reçois les mêmes quelques secondes de données audio à jouer encore et encore.

Et oui, j'ai récuré le net pour les composants qui peuvent faire ce genre de choses pour nous et avons conclu que le seul moyen fiable est de le faire nous-mêmes comme ça.

Et oui, les données audio que cette application joue est saccadée.Je m'attends à écrire le code tampon d'une demi-seconde jusqu'à ce que je puisse obtenir le code write-to-buffer comme il se doit:/

J'ai lu que les gens suggèrent de garder une trace de leur propre écriture curseur, mais à partir de ce que je lis Lock and Unlock devrait m'aider à contourner ce besoin. Je préférerais aussi éviter d'avoir deux tampons que j'alterne d'avant en arrière (ou un split-buffer, ce qui serait essentiellement la même chose, seulement un peu plus complexe en écriture)

Toute aide grandement appréciée !

+0

Avez-vous regardé les composants lakeofsoft? Je les ai déjà utilisés pour une application audio sur Internet, et cela vient avec la source. Je les aimais. OTOH pour nous c'était juste un travail de côté (audio sur webapp) –

+0

Ouais, je l'ai fait. Nous n'avons pas besoin d'aide pour diffuser les données - nous le faisons déjà. Nous avons besoin des fonctionnalités de mixage automatisé de DirectSound. La commodité de chaque thread séparé étant capable de jouer comme il le souhaite sans avoir à prêter attention à toute autre partie de l'application. DirectSound semble avoir une certaine forme d'annulation d'écho quelque part dans ce qui serait un très bon bonus. –

+0

Avez-vous déjà essayé l'audio BASS? http://www.un4seen.com/bass.html Il est à faible latence, très rapide et il y a des wrappers Delphi corrects disponibles. J'ai fait quelques bonnes choses avec BASS dans .NET ... –

Répondre

2

Je compris le problème ^^;

Assez simple aussi. Je pensais que je devais simplement transmettre les mêmes pointeurs aux pointeurs que Lock() avait demandés.

Changer à

DSCurrentBuffer.Unlock(FirstPart, FirstLength, SecondPart, SecondLength); 

Résolu le problème et le tampon enveloppe maintenant correctement.

Désolé pour perdre votre temps, mais merci quand même ^^;

1

Quelques choses qui pourraient faire en sorte que cela:

  1. Memo1.Lines.Add ne doit être lancé à partir du thread principal (le thread qui a initialisé l'interface utilisateur graphique VCL). Utilisez TThread.Synchronize pour cela (plus facile), ou un tampon intermédiaire qui est thread-safe et de préférence lock-free (plus rapide, merci mghie pour cet indice). Déverrouiller doit être dans une section finally comme ci-dessous, car si une exception se lève, vous ne déverrouillez jamais le tampon, voir l'exemple de code ci-dessous.

  2. Vous devez enregistrer toutes les exceptions qui se produisent.

Exemple de code:

DSCurrentBuffer.Lock(0, 512, @FirstPart, @FirstLength, @SecondPart, @SecondLength, DSBLOCK_FROMWRITECURSOR); 
    try 
    //... 
    finally 
    DSCurrentBuffer.Unlock(@FirstPart, FirstLength, @SecondPart, SecondLength); 
    end; 

--jeroen

+2

Il y a une faute de frappe dans le code (enfin). Et je resterais loin de Synchronize() '. Dans tous les cas, mais surtout dans celui-ci, car il nie les avantages de faire du son dans un fil de fond. Il n'y a aucun contrôle sur combien de temps cela prendra. Il est préférable d'ajouter les messages de journal à une liste de thread-safe et que le thread VCL ajoute les messages mis en file d'attente au mémo de manière asynchrone. Cela conservera toujours l'ordre du message et sera ** beaucoup ** meilleur avec plusieurs threads. – mghie

+0

thx pour le commentaire; réponse modifiée à cause de cela. –

+0

Ah, merci et désolé, j'ai compris pourquoi il ne voulait pas envelopper le tampon. Il était incorrect de lui donner des pointeurs sur FirstPart et SecondPart dans l'appel Unlock(). Alors maintenant, il s'enroule et fonctionne comme il se doit. Ceci est une application de test btw. Le mémo est seulement là parce qu'il n'y a aucun fil à jouer avec cette application de test. Pas de soucis, je sais comment garder les choses en sécurité quand ça compte ^^ Je devrais en effet faire un essai/attraper là. Vraiment faire une habitude de ça. –