C++, char vers integer, binaire

Résolu/Fermé
Colin - 17 janv. 2009 à 21:54
 Colin - 19 janv. 2009 à 22:21
Bonjour,

Pour un projet, j'ai dans une grande chaine de caractère des données brutes d'un son Wave.
L'avantage de le mettre dans une chaine de caractère est que 1 ou 2 caractère (ou plus) donnent une note (8bits per sample ou 16bits per sample - un caractère fait 8 bits). Pour traiter ce son (en l'occurence, faire un effet d'echo), je dois prendre les valeurs, les atenuer, les sommer et les remettre en format caractère.

Je cherche donc comment utiliser les fonctions existantes ou quelle fonction ecrire pour passer de 1 ou 2 caractères à une valeur entière : en fait relire les bits mais au format entier et non caractère... Un cast suffit-il ?

Je dois faire cela sachant que les données à récupérer sont signées (-127 a 127 en 8 bits).

Je vous remercie... dites moi si il manque des détails pour répondre.

Colin
A voir également:

9 réponses

fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
17 janv. 2009 à 21:59
Salut,
Si c'est un seul caractère, une simple soustraction par le code ascii '0' marchera.
Par contre si tu veux plusieurs caractères, il y a la fonction atoi ou strtol qui pourrait sûrement t'aider. Elles récupéreront le plus gros nombre.
int nb=atoi(chaine);

Cdlt
0
Bonjour,

Je crois que ce n'est pas ce qu'il me faut, j'obtiendrais ainsi la valeur entière qui est écrite dans le char non ?
Je veux dire, si mon char est '8' mon int sera 8
char 8 bin: 00111000(56 en ascii) mais int: 8
Ce que je veux c'est
char 8 bin: 00111000(56 en ascii) et donc 56 en sortie

sur 16 bits c'est autre chose.

Je vais vérifier tu as peut être raison. Dans ce cas puis-je aussi utiliser atoI et strtoI avec une chaine de 1 caractère ?
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835 > Colin
17 janv. 2009 à 22:17
Avec strtol, tu peux préciser la base.
Par exemple : si ta chaine vaut "1001",
strtol(chaine,NULL,2);
renverra 9 et non 1001. Et ça prendra le plus long nombre possible, donc si ta chaîne fait "10000000000000001", ça marchera aussi. Tant que ça dépasse pas long du moins ^^.
Sinon pour une chaîne d'un caractère, oui ça marche. Mais pas sur un caractère simple. Pour le caractère simple, une simple soustraction.
char a='5'. int b=a-'0';

0
Bonjour

Quel langage de programmation utilises-tu ? Tous les langages ont une fonction pour transformer un caractère d'une chaîne en nombre et réciproquement.
Fais bien attention quand tu manipuleras ces nombres : quand tu les additionnes ou que tu les soustrais, tu vas avoir de dépassements de capacité.
Et du son en 8 bits... je ne sais pas ce que tu veux en faire, mais ça ne va pas être de la hifi !
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
17 janv. 2009 à 22:04
C'est écrit C++ dans le titre ;)
0
J'essaye de lire attentivement le message, j'oublie le titre...
Dans ce cas, il n'y a aucune sorte d'intérêt à mettre les données dans une chaine de caractères : un bon vieil array de signed char ou short est très très nettement préférable. Il n'y a aucune sorte de transcodage à faire par rapport aux données lues depuis un fichier wav. Toutes les opérations d'addition, soustraction se font naturellement (re-attention aux dépassements de capacité !).
0
Pour le tableau de char : ce sont les fonctions de l'API windows qui te demandent de donner ça pour le retour :-p
0
Très bien, j'essaye tout ça rapidement demain.
En revanche, si je traites mes données en décimal (integer), et que je veux ensuite répasser ces valeurs en caractère (string), j'ai les fonctions inverses ? ( Itostr )
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
17 janv. 2009 à 22:30
Si tu l'as en décimal, et que tu veux le mettre en binaire, non pas de fonctions inverses. Tu devras te la coder, mais c'est pas dur ;) En revanche si tu veux mettre le nombre en décimal tu peux utiliser sprintf ou mieux snprintf.
snprintf(chaine,sizeof chaine,"%d",nb);
0
J'oubliais une autre contrainte : les bits sont stockés en Big Endian (bit de poids faible en premier ?), donc il faut inverser le binaire avant de le lire en chiffre.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
18 janv. 2009 à 16:03
Bonjour,

Colin, le samedi 17 janvier 2009 à 21:54:19
Un cast suffit-il ?


Selon moi, oui.

Un point devient flou au fur et à meusure des posts :
Prenons un échantillon 8 bit signé, donc entre -128 et 127 : Je pense que cet échantillon n'est pas écrit en texte dans ta chaîne de caractères mais bien en données binaires.

Une précision est nécessaire : L'échantillon de valeur 127 est-il écrit dans ta chaîne de caractère avec le caractère '1' puis le caractère '2' puis le '7', donc trois caractères et au format texte ou est-il codé sur un seul caractère dont la valeur est 127 ? (Comme dans un fichier .wav par exemple)

1) Format binaire :

Il n'existe pas de base un type numérique codé sur 8 bits (peut-être le short short int, je n'en suis même pas sûr) Il est donc courant d'utiliser le type char comme un nombre en lui retirant toute sa valeur de caractère.

// On simplifie en définissant deux formats
typedef signed char  Sample8;   // type 8 bits pour la définition du Sample8
typedef signed short Sample16;  // type 16 bits pour la définition du Sample16

// (éventuellement, lorsque que le code sera compilé sur un ordinateur ayant des tailles différentes pour les types de base, seuls ces deux typedef seront à changer.)

// Les échantillons : (Au format binaire)
char *pData; // Je suppose qu'il sont dedans, je ne sais pas comment ils sont obtenus.

// 8 bits par échantillon
if ( bitsPerSample == 8 )
{
    // variable locale et temporaire pour éviter de mettre des casts partout
    Sample8 *pSamples = (Sample8 *) pData;    // Je change l'interprétation des données
    unsigned int numSamples = dataSize / sizeof( Sample8 );

    // Traitement
    ...
    pSamples[i] =  ...;
    ...
}
// 16 bits par échantillon
else if ( bitsPerSample == 16 )
{
    Sample16 *pSamples = (Sample16 *) pData;
    unsigned int numSamples = dataSize / sizeof( Sample16 );    // 2 octets par échantillons

    ...
    pSamples[i] = ...;
    ...
}

L'usage de templates serait une très bonne idée

Le type char est souvent utilisé parce qu'il fait 8 bits, l'ordinateur ne fait pas de différence entre un nombre et le caractère qu'il représente. (c'est d'ailleurs pour cela que l'on a 'A' - 32 == 'a' et que l'ordi le comprend très bien)

2) Format texte
Si les échantillons sont au format texte, il manque une précision : admettons que j'ai la chaîne de caractères suivante "127127127", je peux très bien avoir entre 3 et 9 échantillons, comment le sait-on ?
Pour repasser du format numérique au format texte il y a sprintf.

Merci donc de préciser le format que tu as en entrée, binaire ou texte.

Si tes données sont en Big Endian et que ton ordinateur ne l'est pas, oui il faudra transformer.

M.
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
18 janv. 2009 à 16:33
Un char ne fait pas forcément 8 bits bien que ce soit souvent le cas.
0
Bonjour Mahmah,

Le format est bien au format binaire, ton post est donc très interessant.

Je tente de reprendre le code : j'ai ma variable de taille adaptée pSamples[i], en gros 8 ou 16 bits, je veux l'attenuer et la remettre à sa place :

...

// attenuation de la valeur
pSamples[i] =  (int)pSample[i]*0,5;

...


Et.. cela suffit-il ? Mon tableau original est-il modifié comme je le souhaite ?
Je manque un peu de fraicheur pour comprendre rapidement ce 18 janvier...
0
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
18 janv. 2009 à 17:27
Oui, le passage en nombre flottant n'est forcément pas nécessaire.

// attenuation de la valeur
pSamples[i] = pSample[i] / 2;

Ou, si on souhaite appliquer un coefficient, on convertit effectivement en float

pSample[i] = (Sample16) ( (float) pSample[i] * 0.5f );


Il faut se méfier des opérations en nombres entiers tel que :
pSample[i] = pSample[i] * 1 / 2;
En nombre entier 1 / 2 vaut 0.

M.
0
Merci pour ces infos, ça avance, et c'est beaucoup plus simple comme ça !

Je ne fais que l'attenuation pour le moment : ça fonctionne avec pSamples[i] = pSample[i] / 2; , mais comme cela :
if ( projectFormat.wBitsPerSample == 16 )
                {
                        Sample16 *pSamples = (Sample16 *) WaveData;
                        unsigned int numSamples = DataSize / 2;    // 2 octets par échantillons
                        
                        for(unsigned int i=0; i<numSamples; i++)
                                pSamples[i] = (Sample16)((float)pSamples[i]*ap);
                }

ça ne fonctionne pas, sortie muette, donc le resultat obtenu est nul.
Dans ta synthaxe tu met pSample[i] = (Sample16) ( (float) pSample[i] * 0.5f ); A quoi sert le f derrière 0.5 ?
0
Colin > Colin
18 janv. 2009 à 17:53
Non, je n'ai rien dit, mon ap etait déclaré en int...
0
Cela fonction sans problème en 16bits, cependant, en 8bits j'ai :

pour
typedef unsigned char Sample8; // type 8 bits for Sample8
un son atténué mais avec un petit bruit aigu ajouté

typedef signed char Sample8; // type 8 bits for Sample8
le son de base clairement reconaissable mais avec un gros bruit au dessus (parasites)

Si ce sont les caractèrs ANSI utilisés, quel doit etre le type utilisé dans le typedef ?
0
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 125
18 janv. 2009 à 21:06
Toi seul sais si les échantillons 8 bits qui te sont fournis sont signés ou non.
C'est la première chose a fixer.

Ensuite il faut essayer de voir ce qu'il se passe pour que le son soit déformé.
1) Sans toucher au son
2) En lisant puis ré-écrivant sans modifier les échantillons
3) En modifiant les échantillons
Pour voir quand les parasites arrivent. Si possible, les observer avec un logiciel audio.

Je vérifierais l'endianness en premier à ta place.

M.

ANSI se rapporte a des caractères, tes char ne sont que des nombres, pas de véritables caractères.
(Si j'ai bien compris jusque là)
0
Oui oui, c'est bien ça, seul la façon de lire les bits importe.
Je donne des nouvelles quand j'ai observé un peu...
0
Ok, l'erreur vient du traitement en 8bits qui est non signé. L'attenuation se porte sur l'echelle 0 - 255 et non pas sur -127 - 127. Il faut donc que je traite le 8 bits specialement comme cela :
if ( projectFormat.wBitsPerSample == 8 )
        {
                // local temporary variable to avoid cast everywhere
                Sample8 *pSamples = (Sample8 *) echoWaveData;    // change data interpretation

                // Traitment
                // take care of 8 bits format
                for(unsigned int i=0; i<DataSize+dp; i++)
                        pSamples[i] = (Sample8)((((int)pSamples[i]-127) * ap) + 127);
        }

On calcule la valeur en soustrayant 127 et on la re-range en rajoutant les 17 (après traitement)
0
C'est bon, j'ai trouvé ce qu'il me faut : voici la fonction qui ajoute de l'echo sur un son Wave stocké dans une chaîne de caractères :

projectFormat est un WAVEFORMATEX :

void Echo(float ap_float_user, int dp_bytes_user)
{
        // parameter attenuation : "ap"
        // parameter delay : "dp"
        if(projectFormat.wBitsPerSample!=8 && projectFormat.wBitsPerSample!=16)
        {
                ShowMessage("Sorry, format not supported for echo effect");
                return;
        }
        
        float ap;
        int dp;
        
        ap = ap_float_user;               // atenuation parameter
        dp = dp_bytes_user * projectFormat.nChannels;       
                        // delay in bytes, multiple of 2
                        // this value has to be multiple of nbChannels, here 2 or 1

        
        char* echoWaveData = new char[DataSize+dp];
        char* effectWaveData = new char[DataSize+dp];
                        
        // copy WaveData into echoWaveData with decalage
        for(unsigned int i=0; i<DataSize+dp; i++)
        {
                if(i<(unsigned int)dp && projectFormat.wBitsPerSample == 8)
                        echoWaveData[i]= (Sample8)128;         // silent 128
                else
                        if(i<(unsigned int)dp && projectFormat.wBitsPerSample == 16) 
                                echoWaveData[i]=(Sample16)0;
                        else
                                echoWaveData[i]= WaveData[i-dp];
        }
        // end of copy with decalage // OK 16bits, OK 8bits
        
        // attenuation of echoWaveData
        // 8 bits per sample       
        if ( projectFormat.wBitsPerSample == 8 )
        {
                // local temporary variable to avoid cast everywhere
                Sample8 *pSamples = (Sample8 *) echoWaveData;    // change data interpretation

                // Traitment
                // take care of 8 bits format
                for(unsigned int i=0; i<DataSize+dp; i++)
                        pSamples[i] = (Sample8)((((int)pSamples[i]-127) * ap) + 127);
        }
        // 16 bits per sample
        else
                if ( projectFormat.wBitsPerSample == 16 )
                {
                        Sample16 *pSamples = (Sample16 *) echoWaveData;
                        unsigned int numSamples = (DataSize+dp) / 2;    // 2 bytes per sample
                        
                        for(unsigned int i=0; i<numSamples; i++)
                                pSamples[i] = (Sample16)((float)pSamples[i] * ap);
                }
        // end attenuation // OK 16bits, OK 8bits

        
        // addition of two signals
        if ( projectFormat.wBitsPerSample == 8 )
        {
                // local variable to change interpretation
                Sample8 *pSamplesOri = (Sample8 *) WaveData;
                Sample8 *pSamplesEco = (Sample8 *) echoWaveData;
                Sample8 *pSamplesEffect = (Sample8 *) effectWaveData;

                // Traitement
                // have to treat case of sum > 127
                // we will add wave divide by 2
                // and multiply it by 2 at the end
                for(unsigned int i=0; i<DataSize+dp; i++)
                {
                        // do not copy data from WaveDatz[DataSize_or_more]
                        // no existing data here
                        if(i>=DataSize)
                        {
                                pSamplesEffect[i] = (Sample8)((int)64 + (int)pSamplesEco[i]/2); // because we divide all by 2
                        }
                        else
                        {
                                pSamplesEffect[i] = (Sample8)((int)pSamplesOri[i]/2 + (int)pSamplesEco[i]/2);
                        }
                        
                }
                // Now, amplification of signal by 1.5
                // take care of 8 bits format
                for(unsigned int i=0; i<DataSize+dp; i++)
                        pSamplesEffect[i] = (Sample8)((((int)pSamplesEffect[i]-127) * 1.4) + 127);
                
        }
        // 16 bits per sample
        else
                if ( projectFormat.wBitsPerSample == 16 )
                {
                        Sample16 *pSamplesOri = (Sample16 *) WaveData;
                        Sample16 *pSamplesEco = (Sample16 *) echoWaveData;
                        Sample16 *pSamplesEffect = (Sample16 *) effectWaveData;
                        unsigned int numSamples = (DataSize+dp) / 2;    // 2 octets par échantillons
                        
                        for(unsigned int i=0; i<numSamples; i++)
                        {
                                // do not copy data from WaveDatz[DataSize_or_more]
                                // no existing data here
                                if(i>=DataSize/2)
                                        pSamplesEffect[i] = (Sample16)((int)0 + (int)pSamplesEco[i]/2);
                                else
                                        pSamplesEffect[i] = (Sample16)((int)pSamplesOri[i]/2 + (int)pSamplesEco[i]/2);
                                
                        }
                        
                        for(unsigned int i=0; i<numSamples; i++)
                                pSamplesEffect[i] = (Sample16)((float)pSamplesEffect[i] * 1.4);
                        
                }
        // end addition of two signals
        // 16 bits : OK
        // 8 bits : not at all, data more than +/-127

        // copying result into WaveData
        if(WaveData)
                delete[] WaveData;
        WaveData = new char[DataSize+dp];


        for(unsigned int i=0; i<DataSize+dp; i++)
                WaveData[i] = effectWaveData[i];
        // end copying
         
        // destroying buffer
        delete[] echoWaveData;
        delete[] effectWaveData;

        // reconfigure DataSize
        DataSize = DataSize + dp;

}


Merci pour votre aide, ça m'a bien servi !
0