PDA

Visualizza la versione completa : [C/C++] Problema con tipi double


carminio
28-11-2008, 15:36
Ciao a tutti,
sono un pò di giorni che ormai sbatto la testa per fare una cosa che inizialmente mi sembrava abbastanza semplice, e cioè estrarre la sezione raw-data da un file wav in formato double per poi elaborare il file così ottenuto successivamente. In pratica fare, in ambiente c/c++, quello che banalmente fa il comando wavread di matlab.
Premetto che sono un novellino con c/c++, tuttavia guardando in internet ho trovato un piccolo codice che sembrava fosse adatto per i miei scopi. Il problema è dovuto al fatto che i dati estratti (per quanto io mi sia sforzato di fare dei casting a double) sono sempre restituiti in singola precisione (float). Di seguito allego il codice in questione e una parte dell'output con la speranza che possiate aiutarmi... le ho provate davvero tutte!
Grazie in anticipo :(

------------------------------------BEGIN-----------------------------------------------
// parser.cpp : definisce il punto di ingresso dell'applicazione console.
//

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <iostream>
#include <limits>
#include <cfloat>
#include <iostream>
#include <iomanip>


using namespace std;


#include "parser.h"

int _tmain(int argc, _TCHAR* argv[])
{
binaryToAscii();
return 0;
}

void binaryToAscii()
{
FILE *infile, *outfile;
char prefix[4];
char fileFormat[8];
unsigned char ch;
double byteValue;
unsigned int i;
char ckID[5];
unsigned long nChunkSize;
short int wFormatTag;
short int nChannels;
unsigned long nSamplesPerSecond;
unsigned long nAvgBytesPerSecond;
short int nBlockAlign;
short int nBitsPerSample;

/* Open source for binary read (will fail if file does not exist) */
infile = fopen( "C:\\temp\\parser files\\file1.wav", "rb" );
{
if (infile==NULL)
//printf( "The source file %s was not opened!\n");
exit(2);
}

/* Open output for write */
outfile = fopen("C:\\temp\\parser files\\file1.txt", "w" );
{
if (outfile==NULL)
//printf( "The output file %s was not opened\n");
exit(3);
}

// Read the header bytes.
fscanf( infile, "%4c", prefix );
fscanf( infile, "%4c", &nChunkSize );
fscanf( infile, "%8c", fileFormat );
fscanf( infile, "%4c", &nChunkSize );
fscanf( infile, "%2c", &wFormatTag );
fscanf( infile, "%2c", &nChannels );
fscanf( infile, "%4c", &nSamplesPerSecond );
fscanf( infile, "%4c", &nAvgBytesPerSecond );
fscanf( infile, "%2c", &nBlockAlign );
fscanf( infile, "%2c", &nBitsPerSample );
fscanf( infile, "%4s", &ckID );
fscanf( infile, "%4c", &nChunkSize );

// Testing on the file format variables would go here.

// Scan and convert the bytes.
for( i = 0; (i < nChunkSize);i++ ) {
fscanf( infile, "%c", &ch );
byteValue = ((double)ch-128)/128;
fprintf_s(outfile,"%1.16lf\n",byteValue);
}

/* All files are closed: */
fclose(infile);
fclose(outfile);
}

------------------------------------END-----------------------------------------------

OUTPUT RESTITUITO
-0.7031250000000000
-1.0000000000000000
-0.7812500000000000
-1.0000000000000000
-0.8906250000000000
-1.0000000000000000
0.9843750000000000
0.9921875000000000
0.8593750000000000
0.9921875000000000
0.8906250000000000
0.9921875000000000
0.9843750000000000
0.9921875000000000
-0.9531250000000000
-1.0000000000000000
-0.9843750000000000
-1.0000000000000000
-0.9531250000000000
-1.0000000000000000
-0.8906250000000000
-1.0000000000000000
-0.8906250000000000
-1.0000000000000000
-0.8750000000000000
-1.0000000000000000
-0.8281250000000000
-1.0000000000000000
-0.7968750000000000

oregon
28-11-2008, 21:13
Questa parte di codice



// Scan and convert the bytes.
for( i = 0; (i < nChunkSize);i++ ) {
fscanf( infile, "%c", &ch );
byteValue = ((double)ch-128)/128;
fprintf_s(outfile,"%1.16lf\n",byteValue);
}


non ha senso, rispetto a quello che vuoi fare.

Cosi' leggi "byte per byte" e, con quella formula, lo converti in double ... ma cosa c'entra?

Un double, all'interno di file, e' fatto da 8 byte ... devi leggere 8 byte per volta e utilizzarli per il tuo double ...

carminio
28-11-2008, 22:00
La cosa che dici non mi torna.
Io posso sempre fare una divisione tra due numeri rappresentati da un numero di byte inferiore a quelli usati per la rappresentazione di un double e poi ottenere il risultato della divisione con una rappresentazione a 8 byte. In sintesi, se prendo due numeri 1 e 3 rappresentati con un solo byte e dico che voglio il risultato della divisione rappresentato da un double otterrei:
1/3=0.3333333333333333.
Ed è quello che vorrei.

oregon
28-11-2008, 22:40
Originariamente inviato da carminio
se prendo due numeri 1 e 3 rappresentati con un solo byte e dico che voglio il risultato della divisione rappresentato da un double otterrei:
1/3=0.3333333333333333.

Questo vale per 1 diviso 3.

Con quel ciclo stai effettuando tutte le operazioni comprese tra

-128 / 128

e

127 / 128

e ottieni i risultati double corretti con il numero di decimali necessari.

Per capire meglio

127 / 128 fa 0,9921875

e il risultato e' questo senza altri decimali ...

Per quanto riguarda la "logica" del programma inoltre, continuo a dirti che non ha senso leggere dei singoli byte (unsigned char) da un file in cui si pensa ci siano una serie di double ... ripeto, non devi leggere un singolo byte ma gli 8 byte dei vari double ...

carminio
29-11-2008, 15:53
Ops! Hai perfettamente ragione Oregon! Mi concentravo sulla sintassi e non mi ero soffermato sui numeri, per cui quando vedevo dei risultati in singola precisione pensavo di aver sbagliato qualcosa.
Per quel che riguarda la lettura del file va bene così, poichè nel file wav non ci sono dei double. La lettura va fatta byte per byte, è il risultato della normalizzazione che mi interessava avere in doppia precisione.
Ti ringrazio tanto per avermi aiutato.
Ciao. :D :D :D

carminio
30-11-2008, 01:21
Sempre a proposito del problema con il file wav...
Dopo aver letto bene la descrizione della formattazione dei files wav, ho capito che nel mio caso il blocco di dati del wav è costituito da campioni con precisione a 2 byte, quindi a rigor di logica dovrei leggere 2 byte alla volta. Come posso fare? Ho provato con fscanf( infile, "%d", &ch1 ), con ch1 di tipo intero, oppure short, ma il risultato è stato disastroso...il file che scrivevo con questi valori letti, dopo la normalizzazione "(ch-128)/128" aveva tutti numeri uguali. Dove sbaglio?
Insomma sto sbattendo la testa per leggere sto benedetto file wav da ormai troppi giorni, mi dite come posso fare? Ripeto che vorrei ottenere lo stesso risultato della funzione wavread di matlab e ormai mi sembra di essere vicino alla soluzione, ma evidentemente, sebbene ho isolato il blocco dati dal resto della struttura del wav, non leggo questi valori in modo corretto.
Help me! :confused: :confused: :confused:

menphisx
30-11-2008, 07:47
Vuoi leggere due byte ?


typedef union _Word {

short value;

struct {

char byte[2];

}sBytes;

}uWord;


Così sei sicuro siano due byte.
Poi usi fread (che è meglio visto che usi file binari ...):


uWord word;

fread(&word.sBytes, sizeof(word.sBytes), 1, file_pointer);


e accedi al valore, con:


printf("%d\n", word.value);


e puoi accedere ai singoli byte, con:


printf("Byte1: %d, Byte2: %d\n", word.sBytes.byte[0], word.sBytes.byte[1]);


:D

oregon
30-11-2008, 09:54
Se non ti servono i singoli byte e ti serve un valore con il segno, puoi semplicemente usare



short int v;

fread(&v, sizeof(v), 1, infile);

carminio
30-11-2008, 12:42
OK! Problema risolto...finalmente. Grazie a tutti voi per i preziosi consigli. Ciao. :D

Loading