0
votes

Envoi de voix sur UDP avec API Java Sound

J'ai développé une application Java à l'aide de Java Sound API. Ce qu'il fait, c'est qu'il capture les données provenant du microphone et l'envoie sur UDP à un autre ordinateur pour y être joué. Maintenant, je reçois des problèmes de volume, de qualité et de rapidité. Je ne peux pas comprendre la source des problèmes, alors j'ai besoin d'aide pour déterminer ce qui ne va pas avec le programme.


Mise à jour

Il semble que les problèmes de vitesse soient dus à Java Sound API Juste être ralentir. J'ai essayé le programme sans sockets UDP et il y avait le même type de retard que l'UDP n'introduit pas de retard supplémentaire dans le réseau local. Le problème d'écho disparaît lorsque le programme est utilisé avec des écouteurs. La qualité du son n'est pas trop mauvaise dans l'ensemble. Seul problème restant est le volume.

ci-dessous est l'expéditeur : xxx

ci-dessous est le récepteur : xxx


0 commentaires

3 Réponses :


0
votes

Voice Over Network est un sujet complexe et a beaucoup de problèmes difficiles.

Je pense que votre problème provient de l'irrégularité de votre couche de réseau.

Si votre réseau n'est pas 100% Constant (il est très difficile avec les interférences d'une autre application dans le tampon de votre ordinateur / de votre boîte / etc.) Votre voix sera une petite robotique.

Un moyen d'éviter qu'il s'agit d'ajouter un petit tampon qui retardera la communication entre le récepteur et l'expéditeur. Si le retard est trop faible, l'effet de problème de performance continuera de se produire. Si c'est trop long, c'est trop long;) ...

Un autre problème qui peut survivre (le plus probable dans le réseau WAN) est la perte du paquet qui peut troubler la communication. Pour résoudre ce problème .. Je n'ai pas une bonne solution, cela dépend du réseau ou / et du cas d'utilisation.


7 commentaires

Donc, je me suis débarrassé des sockets UDP et il suffit d'utiliser TargetDataline et SourceDataline sur un tampon pour voir si le problème a été causé par des sockets UDP. Il s'avère que les sockets UDP ne provoquent pas le délai, mais c'est plutôt l'API Java Sound. J'ai besoin d'une API plus rapide | que ceux proposés par Java. Quelles sont mes alternatives? Je peux le réécrire à C ++ aussi, mais je n'ai trouvé aucune bibliothèque en C ++ pour atteindre le microphone et les haut-parleurs.


Peut-être perles ou jsyn pour la bibliothèque Java


Python est une possibilité ... mais je trouve étrange que Java ne soit pas assez rapide pour jouer au son


Je dirais que c'est assez rapide, mais j'ai besoin de quelque chose de proche en temps réel.


J'ai essayé votre code avec 2 ordinateur et cela fonctionne bien (j'ai un peu d'écho mais c'est tout))


Il y a un retard notable entre les deux. Je veux en faire presque en temps réel et me débarrasser de l'écho.


À ce moment-là, vous devriez définir le temps réel (combien de MS? Je suis d'accord, l'écho est gênant)



2
votes

Certaines pensées, avec une mise en garde que je n'ai pas d'expérience directe pour essayer d'utiliser UDP.

Il me semble que des paquets manquants et hors de commande (supposés avec UDP) devraient être «traités» ou bien les discontinuités attendues créeraient continuellement des clics perturbateurs. Mais idk comment cela est normalement fait. Filtres? Tampon? Encapsulation des données de paquets dans Windowing (Hann ou Hamming?) Cadres pour combler les discontinuités de paquets?

javax.sound.samplé est assez proche du son natal. Voici un bon article à faire référence à des considérations relatives à la teneur en temps réel, à faible latence Audio basé sur Java.


5 commentaires

J'ai réussi à en faire presque en temps réel avec ALSA Sound API.


Heureux d'entendre une solution a été trouvé! Comment accédez-vous à l'API ALSA? Est-ce que vous le faites via Java, par exemple, en sélectionnant des lignes alsa? (Toujours en utilisant la cibledataline / sourcecedataline?) Ou une autre manière?


Envisagez d'écrire votre solution et de la marquer comme la réponse. Ce serait une contribution précieuse.


J'utilise C à Linux (ALSA API n'est disponible que sous Linux). Le délai entre la capture audio et la lecture n'est presque pas comparé à l'API de Java Sound mais il y a du bruit à l'arrière-plan - que je ne pouvais pas me débarrasser de. Une fois que je résout le problème de bruit, je peux poster la solution ici. Mais ce n'est pas encore prêt.


J'ai également essayé d'utiliser Pullevodio pour capture et lecture. Il est beaucoup plus facile d'utiliser que d'utiliser ALSA API, mais il y a le même type de délai entre la capture et la lecture, comme il s'agit d'une API Java Sound.



1
votes

Enfin, je suis capable de répondre à ma propre question ici. J'ai utilisé alsa API fort> (Linux), pulseaudio strong> (Linux) et jackaudio fort> (Linux) pour rendre le programme presque en temps réel (moins de 1 SEC Delay). L'API de blocage de Pulseaudio était beaucoup plus agréable à utiliser que Alsa et Jackaudio, mais j'ai expérimenté le même type de retard que j'avais connu avec Java Sound API. Jackaudio est un serveur audio à faible latence pour le travail audio professionnel. J'ai vu cet exemple: https://github.com/jackaudio/example -Clients / blob / maître / simple_client.c et s'est vite rendu compte que c'était pour la sortie mono. J'ai modifié le code un peu pour le rendre stéréo, puis ajouté des sockets UDP et une deuxième fonction de rappel. Les fonctions de rappel fonctionnent comme si elles sont des threads séparés, il n'était donc pas nécessaire d'introduire des threads POSIX.

Le code n'est pas parfait, probablement pas un bon moyen d'utiliser Jackaudio et a des tonnes de bugs. Cependant, je pense que c'est un bon point de départ pour ceux qui sont intéressés par ce genre de choses. Pour l'utiliser, d'abord installer jackaudio code> serveur et fichiers de développement via le gestionnaire de packages de votre distribution. P>

Faites-moi savoir s'il y a un meilleur moyen de mettre en œuvre la même fonctionnalité avec tout ensemble d'outils Disponible là-bas. P>

Jackaudio H2>

Solution Jackaudio: strong> p>

#define ALSA_PCM_NEW_HW_PARAMS_API

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <alsa/asoundlib.h>

#define PORT 8080

int create_UDP_socket_receive()
{
    struct sockaddr_in server;
    int sock;
    if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("Socket");
        exit(EXIT_FAILURE);
    }

    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = INADDR_ANY;

    if (bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        perror("bind()");
        exit(EXIT_FAILURE);
    }

    return sock;
}

int main() {
    long loops;
    int rc;
    int size;
    snd_pcm_t *handle;
    snd_pcm_hw_params_t *params;
    unsigned int val;
    int dir;
    snd_pcm_uframes_t frames;

  /* Open PCM device for playback. */
    rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (rc < 0) {
        fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
        exit(1);
    }

  /* Allocate a hardware parameters object. */
    snd_pcm_hw_params_alloca(&params);

  /* Fill it in with default values. */
    snd_pcm_hw_params_any(handle, params);

  /* Set the desired hardware parameters. */

  /* Interleaved mode */
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

  /* Signed 16-bit little-endian format */
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

  /* Two channels (stereo) */
    snd_pcm_hw_params_set_channels(handle, params, 2);

  /* 44100 bits/second sampling rate (CD quality) */
    val = 44100;
    snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);

  /* Set period size to 32 frames. */
    frames = 32;
    snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

  /* Write the parameters to the driver */
    rc = snd_pcm_hw_params(handle, params);
    if (rc < 0) {
        fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
        exit(1);
    }

    /* Use a buffer large enough to hold one period */
    snd_pcm_hw_params_get_period_size(params, &frames, &dir);
    
    size = frames * 4; /* 2 bytes/sample, 2 channels */
    char buffer[size];

    int nread;
    int socket = create_UDP_socket_receive();

    snd_pcm_hw_params_get_period_time(params, &val, &dir);

    while (1) {

        if((nread = recvfrom(socket, buffer, sizeof(buffer), 0, NULL, NULL)) < 0)
        {
            perror("recvfrom()");
            exit(EXIT_FAILURE);
        }

        // write(1, buffer, sizeof(buffer));

        rc = snd_pcm_writei(handle, buffer, frames);
        if (rc == -EPIPE) {
        /* EPIPE means underrun */
            fprintf(stderr, "underrun occurred\n");
            snd_pcm_prepare(handle);
        } else if (rc < 0) {
            fprintf(stderr, "error from writei: %s\n", snd_strerror(rc));
        }  else if (rc != (int)frames) {
            fprintf(stderr, "short write, write %d frames\n", rc);
        }
    }

    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);

    return 0;
}


0 commentaires