Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: FFT à partir d'un sf::SoundBuffer  (Lu 9914 fois)

0 Membres et 1 Invité sur ce sujet

Tero

  • Newbie
  • *
  • Messages: 16
  • Digital artist : teroratsu.deviantart.com
    • Voir le profil
    • E-mail
FFT à partir d'un sf::SoundBuffer
« le: Octobre 14, 2015, 09:49:01 pm »
Bonsoir !

Je bosse en ce moment sur un petit logiciel audio qui se base sur la librairie audio de la SFML. J'essaye depuis quelques temps d'implémenter le spectre audio à partir du buffer mais sans succès. Je me suis tourné vers la classe FFT d'audacity mais le problème est que pour faire fonctionner l'algo il faut lui passer le buffer sous forme de float* et je ne vois aucun moyen de récupérer un tel tableau à partir du buffer :/

De plus j'ai quelques doutes quand à la taille du buffer à passer à la fonction dans l'exemple sur lequel je me base cette taille était de 256, c'est acceptable ? (l'exemple en question)

Voilà ma classe :

#ifndef SOUNDWAVE_H
#define SOUNDWAVE_H

#include <iostream>
#include <memory>
#include "SFML/Audio.hpp"
#include "FFT.h"

#define BUFFER_SIZE 256
#define NUM_WINDOWS 80

class SoundWave
{
    public:
        SoundWave();
        SoundWave(std::shared_ptr<sf::SoundBuffer> buffer); //!< must be the default ctor
        virtual ~SoundWave();

        void update();
    private:
    /**
           * Initialize the freq array.
           * @see SoundWave()
    */

    void init();

    std::shared_ptr<sf::SoundBuffer> _buffer; //!< audio buffer to work with

    FFT _fft;
    float magnitude[BUFFER_SIZE]; //!< contain the magntiude data from buffer once _fft.powerSpectrum() method is called
    float phase[BUFFER_SIZE]; //!< contain the phase data from buffer once _fft.powerSpectrum() method is called
    float power[BUFFER_SIZE]; //!< contain the power data from buffer once _fft.powerSpectrum() method is called

    float* freq[NUM_WINDOWS][BUFFER_SIZE/2];
    float* freq_phase[NUM_WINDOWS][BUFFER_SIZE/2];
};

#endif // SOUNDWAVE_H
 

#include "../header/SoundWave.h"

SoundWave::SoundWave()
{
    //ctor
}

SoundWave::SoundWave(std::shared_ptr<sf::SoundBuffer> buffer)
{
    _buffer = buffer;
    init();
}

SoundWave::~SoundWave()
{
    //dtor
}

void SoundWave::update()
{
    float avg_power = 0.0f;
    static int index = 0;

    if(index < NUM_WINDOWS)
        index += 1;
    else
        index = 0;

    _fft.powerSpectrum(0,BUFFER_SIZE/2,,BUFFER_SIZE,&magnitude[0],&phase[0],&power[0],&avg_power) //!< manque le buffer ici

    for (j=1; j<BUFFER_SIZE/2; j++)
    {
        freq[index][j] = magnitude[j]; //!< fill the array with corresponding data from the magnitude array
    }
}


void SoundWave::init()
{
    for(int i=0; i < NUM_WINDOWS; i++)
    {
        for(int j=0; j < NUM_WINDOWS; j++)
        {
            freq[i][j] = 0;
        }
    }
}
 


J'ai également vu pas mal de topic sur le même sujet ([1], [2]) et j'ai vu qu'ils utilisaient la structure sf::SoundBuffer::Chunk  sauf que je ne vois aps trop comment l'implémenter.

En clair j'aimerai que depuis mon pointeur vers le buffer je puisse jouer la musique et afficher le spectre !

Et pour pas faire deux topics j'ai une secondes question moins importantes :
Pour seek à un certain moment de la musique, y'a un meilleur moyen que celui-là ? :
m_currentSample = static_cast<std::size_t>(timeOffset.asSeconds() * getSampleRate() * getChannelCount());
Le soucis avec cette méthode c'est qu'il y a un décalage de 1 secondes entre l'appel de la méthode et le changement de sample réel, du coup c'est un peu bizarre..

merci d'avance !
French digital artist & programmer :

DeviantArt : teroratsu.deviantart.com
Facebook : facebook.com/Teroratsudeviantart

Cpl.Bator

  • Hero Member
  • *****
  • Messages: 540
    • Voir le profil
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #1 le: Octobre 14, 2015, 10:56:04 pm »
je ne suis pas un spécialiste du son / fft , mais si je devais le faire , je me pencherais sur une lib spécialisé la dedans : FFTW3 , une petite recherche sur google me donne ce résultat :
http://jirislaby.blogspot.fr/2013/03/fftw3-sound-spectral-analyzer-example.html
et pour ta seconde question, setPlayingOffset() ne fait pas l'affaire ?

Tero

  • Newbie
  • *
  • Messages: 16
  • Digital artist : teroratsu.deviantart.com
    • Voir le profil
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #2 le: Octobre 14, 2015, 11:39:32 pm »
Pour le setPlayingOffset(), un gros merci, je sais pas comment j'ai fais pour la raté en lisant la doc Oo, merci !

Par contre pour ce qui est du fft j'aimerai vraiment avoir un seul objet à gérer, mon buffer pour être synchro entre ce qui est joué et le spectre, histoire que ce soit cohérent.  J'aimerai juste réussir à faire le lien entre les données contenues dans les samples via la méthode :
const Int16* sf::SoundBuffer::GetSamples        ()  const
Je cherche juste à savoir comment extraire les donnée de chaque sample pour les envoyer à la fonction powerSpectrum de la class fft d'audacity pour le coup.

A vrai dire ça fait depuis début septembre que je cherche une solution et celle de se servir de la classe FFT d'audacity semble la plus simple à mettre en oeuvre !

Après le coup d'envoyer les chunk de données ça peut marcher mais en 2012 la sfml ne donnait aucun moyen de savoir à quel moment tel ou tel sample allait être joué. Du coup il n'y pas de synchronisation..
src : http://en.sfml-dev.org/forums/index.php?topic=7642.0

et en 2014 : http://en.sfml-dev.org/forums/index.php?topic=14815.0
Toujours ce même problème. Je me demande donc s'il y a aujourd'hui un moyen quelconque de visualiser un flux audio tout pendant qu'il est joué en synchronisation.

Si il n'y a vraiment pas moyen de faire quelque-chose simplement j'abandonnerai l'idée pour le moment, ce n'est pas ma priorité non plus.
Néanmoins si vous voyez une solution au problème faites moi signe ! :)

EDIT : Je pense (je peux me tromper, et je me trompe souvent :^) ) que le soucis vient de là, du fait que la lecture des sample n'est pas "Time based", mais aucune idée de comment faire ça proprement.

bool SoundStream::onGetData(Chunk& data)
{
    // number of samples to stream every time the function is called;
    // in a more robust implementation, it should be a fixed
    // amount of time rather than an arbitrary number of samples
    const int samplesToStream = 50000;

    // set the pointer to the next audio samples to be played
    data.samples = &m_samples[m_currentSample];

    // have we reached the end of the sound?
    if (m_currentSample + samplesToStream <= m_samples.size())
    {
        // end not reached: stream the samples and continue
        data.sampleCount = samplesToStream;
        m_currentSample += samplesToStream;
        return true;
    }
    else
    {
        // end of stream reached: stream the remaining samples and stop playback
        data.sampleCount = m_samples.size() - m_currentSample;
        m_currentSample = m_samples.size();
        return false;
    }
}
« Modifié: Octobre 14, 2015, 11:44:27 pm par Tero »
French digital artist & programmer :

DeviantArt : teroratsu.deviantart.com
Facebook : facebook.com/Teroratsudeviantart

Cpl.Bator

  • Hero Member
  • *****
  • Messages: 540
    • Voir le profil
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #3 le: Octobre 15, 2015, 12:19:16 am »
Pour le FTT et audacity je ne pourrais pas t'aider, en revanche pour synchronisé tes samples & ta musique , tu as le getPlayingOffset() qui te renvois un sf::Time , tu as aussi le getSampleRate() , qui te renvois le nombre de sample joué par secondes, il est donc facile de déterminé avec précision ou tu te trouve dans ton buffer de sample.

Tero

  • Newbie
  • *
  • Messages: 16
  • Digital artist : teroratsu.deviantart.com
    • Voir le profil
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #4 le: Octobre 15, 2015, 01:13:02 am »
C'est pas bête du tout ça, je me disais bien que getSampleRate() pouvait m'être utile mais j'avais pas pensé à utiliser getPlayingOffset(), j'essayerais ça !

Pour ce qui est de la fonction fft, je vais essayer de caster le int16* en float*, je sais pas si c'est très légal par contre xD (et puis je ne sais pas non plus si les données sont belles et bien contenu là):

(float*)_stream->getLastChunk().samples

La fonction getLastChunk renvoie le Chunk du OnGetData(Chun& data) de ma classe SoundStream. (qui hérite de la classe sf::SoundStream).
French digital artist & programmer :

DeviantArt : teroratsu.deviantart.com
Facebook : facebook.com/Teroratsudeviantart

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #5 le: Octobre 15, 2015, 07:51:20 am »
Citer
je vais essayer de caster le int16* en float*, je sais pas si c'est très légal par contre
Non. Il faut faire une copie pour convertir tes int16 en float, et à mon avis il faut aussi les renormaliser (diviser par 2^15 - 1).
Laurent Gomila - SFML developer

Cpl.Bator

  • Hero Member
  • *****
  • Messages: 540
    • Voir le profil
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #6 le: Octobre 15, 2015, 09:26:19 am »
c'est pas plutôt 2^16-1 ? soit 65535?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #7 le: Octobre 15, 2015, 09:46:24 am »
Non, c'est de l'entier 16 bits signé.
Laurent Gomila - SFML developer

Cpl.Bator

  • Hero Member
  • *****
  • Messages: 540
    • Voir le profil
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #8 le: Octobre 15, 2015, 10:11:27 am »
Bah voui , je suis bête ^^  :P

Tero

  • Newbie
  • *
  • Messages: 16
  • Digital artist : teroratsu.deviantart.com
    • Voir le profil
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #9 le: Octobre 15, 2015, 03:02:34 pm »
Un truc du genre ?

float* SoundWave::conversionChunk(const sf::Int16* samples, size_t sampleCount)
{
    int sampleC = static_cast<int>(sampleCount);
    float chunkf[sampleC];
    for(int i=0; i< sampleC; i++)
        {
            chunkf[i] = (float) samples[i];
            chunkf[i] = chunkf[i] / pow(2.0,15.0) -1;
        }
    return chunkf;
}

mais ça me fait un petit warning :

/home/tero/Documents/c++Folder/oggMaster/src/SoundWave.cpp:42:11: warning: address of local variable ‘chunkf’ returned [-Wreturn-local-addr]
     float chunkf[sampleC];
           ^

sinon la méthode est bonne, je devrais avoir accès aux données avec ça ?
French digital artist & programmer :

DeviantArt : teroratsu.deviantart.com
Facebook : facebook.com/Teroratsudeviantart

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #10 le: Octobre 15, 2015, 03:38:03 pm »
Oui, un truc du genre, mais avec du C++ correct, et pas aussi lourd ;)

- comme le dit le compilateur, tu ne peux pas renvoyer l'adresse d'une variable locale puisque celle-ci ne survit pas à l'appel de fonction
- créer un tableau à taille dynamique sur la pile n'est pas du C++ standard
- deux lignes de code pour une simple division, ça fait beaucoup ;)
- pow(2.0, 15.0) - 1 est une constante... en l'occurence, si tu ne veux pas de nombre magique dans ton code, utilise SHORT_MAX ou bien std::numeric_limits<sf::Int16>::max()
Laurent Gomila - SFML developer

Tero

  • Newbie
  • *
  • Messages: 16
  • Digital artist : teroratsu.deviantart.com
    • Voir le profil
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #11 le: Octobre 15, 2015, 04:08:29 pm »
Voilà voilà :)

J'ai pas encore eu le temps de tester, je reviens vers vous si ça coince, merci !

void SoundWave::conversionChunk(const sf::Int16* samples, size_t sampleCount)
{
    int sampleC = static_cast<int>(sampleCount);
    _chunkf = new float[sampleC];
    for(int i=0; i< sampleC; i++)
            _chunkf[i] = (float) samples[i] / std::numeric_limits<sf::Int16>::max(); // signed to unsigned
}
French digital artist & programmer :

DeviantArt : teroratsu.deviantart.com
Facebook : facebook.com/Teroratsudeviantart

Tero

  • Newbie
  • *
  • Messages: 16
  • Digital artist : teroratsu.deviantart.com
    • Voir le profil
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #12 le: Octobre 19, 2015, 11:45:12 pm »
Re-bonjour !

Après avoir pas mal galéré à avoir implémenter un pattern Observer pour la fonction j'ai enfin réussis à avoir ce que je voulais (enfin je crois)
J'ai bel et bien les données mais le buffer n'est pas toujours (voir jamais) remplit, j'ai un buffer de 256 bytes et les donnée que je récupère occupe entre 150 & 200 bytes le plus souvent, c'est normal ?

J'ai aussi remarqué qu'avec ce procédé je n'appelais ma fonction qu'à chaque changement de chunk, ce qui n'est pas forcément cool vu que les données changes toutes les secondes/demi/secondes, ma sortie va paraître saccadée ?

Y'a-t-il un autre moyen de faire ça mieux ? Une fois le chunk arrivé dans le onGetData j'aimerai bien savoir comment après les données extraites il est lu.. Comment ça se passe concrètement pour passer d'un sample à l'autre, le lire et passer au suivant.
Est-ce que je devrais aussi passer en paramètre le sampleRate et le m_currentsample ? J'essaye de trouver un moyen pour synchroniser la lecture du buffer par ma classe SoundStream et ma classe SoundWave qui ne le lit pas directement mais via le chunk.

Bref, ça fait un peu pavé, je me suis pas très bien exprimé, j'espère que vous avez saisis l'idée, si c'est pas le cas faite le moi savoir, j'essayerai de reformuler ça :)

(click to show/hide)
French digital artist & programmer :

DeviantArt : teroratsu.deviantart.com
Facebook : facebook.com/Teroratsudeviantart

Tero

  • Newbie
  • *
  • Messages: 16
  • Digital artist : teroratsu.deviantart.com
    • Voir le profil
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #13 le: Novembre 02, 2015, 11:34:27 pm »
nouvelle méthode..

Pas très concluant cette dernière du coup j'ai réfléchis à autre chose, au lieu de récupérer juste le chunk j'ai fais un observer avec deux méthodes update, une qui met à jour le chunk et le sample rate (vu que j'ai aucune idée de s'il peut varier au cours du temps) et une autre qui est appelée depuis l'interface pour forcer le soundStream à notifier  ma classe soundwave avec le "time elapsed" afin de pouvoir calculer avec précision le sample actuel.

J'ai une petite question vis à vis de ça d’ailleurs, depuis ma classe soundStream custom la fonction getplayingOffset() me donne le temps écoulé depuis le début du fichier audio, mais il n'y a qu'un chunk au mieux dans cette classe, le problème est que je peux calculer le sample auquel le flux est arrivé avec précision mais je n'ai aucun moyen de savoir à quel sample commence le chunk.

J'ai pensé à une solution mais elle me semble un peu tordue et bricolé de nul part.. Si au moment du onGetdata je passais à ma fonction update (pour le chunk et le time elapsed) le sample actuel (sample rate * playingOffset) ça me permettrait de faire le calcul depuis mon autre classe relativement à cette valeur et donc de déterminer la sample jouée dans le chunk précisément.

Du coup pour résumer les questions :

- Le sample rate d'un flux peut-il changé au court du temps (pendant sa lecture) ?
- Pour préciser ma question, le m_currentSample au moment du OnGetData est t'il le sample actuel relativement au flux en entier ? (et non relativement au chunk)

Merci d'avance !
« Modifié: Novembre 02, 2015, 11:46:36 pm par Tero »
French digital artist & programmer :

DeviantArt : teroratsu.deviantart.com
Facebook : facebook.com/Teroratsudeviantart

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32504
    • Voir le profil
    • SFML's website
    • E-mail
Re : FFT à partir d'un sf::SoundBuffer
« Réponse #14 le: Novembre 03, 2015, 07:54:01 am »
Citer
- Le sample rate d'un flux peut-il changé au court du temps (pendant sa lecture) ?
Non.

Citer
- Pour préciser ma question, le m_currentSample au moment du OnGetData est t'il le sample actuel relativement au flux en entier ? (et non relativement au chunk)
C'est quoi m_currentSample ?
Laurent Gomila - SFML developer