Προς το περιεχόμενο

Αντιστροφή wav αρχείου στη C


nosmantra

Προτεινόμενες αναρτήσεις

Καλησπέρα, ο λόγος που κάνω αυτό το post είναι επειδή έχω κολλήσει σε κάτι απλό εδώ και πολλές ώρες. Αυτό που προσπαθώ να κάνω είναι να αντιστρέψω ένα αρχείο .wav ώστε να ακούγεται ανάποδα. Παίρνω ως input το όνομα του αρχείου wav που θέλω να αντιστραφεί καθώς και το όνομα του αρχείου στο οποίο θα μπεi. Πρώτα αντιγράφω έυκολα το header μιας και είναι τα πρώτα 44 bytes. Το πρόβλημα είναι πως προσπαθώ να αντιγράψω από το τέλος τα δείγματα(το πόσα byte είναι το κάθε δείγμα εξαρτάται από τα κανάλια και το bits ανά δείγμα στο αρχείο το οποίο χρησιμοποιώ για δοκιμη είναι 2*16=32 32/8= 4bytes) αλλά για κάποιο λόγο αντιγράφει σίγουρα κάτι άλλο από αυτό που θέλω. Σας γράφω τη συνάρτηση μήπως δείτε κάτι που δεν βλέπω.

Αυτό που κάνω είναι να υπολογίσω το πόσα bytes έχει ο φάκελος το οποίο μπαίνει στο chunkSize, αντιγράφω το header και έπειτα στο do-while προσπαθώ να πάρω δείγματα (count= bytes των δειγμάτων) από το τέλος χρησιμοποιόντας κάθε φορά την fseek για να πάω τον δείκτη count(στο αρχείο που τεστάρω 4) θέσεις πιο κοντά στην αρχή, με την fread να βάλω τα count byte σε ένα πίνακα τύπου unsigned char count θέσεων και έπειτα με την fwrite να τα γράψω στο άλλο αρχείο(δε χρειάζεται fseek μίας και γράφω κάθε φορά στο τέλος του).

Το προβληματικό κομμάτι είναι μάλλον στη do-while.

void reverse(char **array)
{
    int sum=0;
    int fileSize;
    int count;
    word numberChannels;//unsigned shot int = word
    word bitsPerSample;
    dword sampleRate; //unsigned int = dword
    dword chunkSize;
    FILE *pFile;
    FILE *pOutFile;
    byte head[44]; // unsigned char = byte
    byte *rev;
    if(checkFileName(array[2]) == 0 || checkFileName(array[3]) == 0)
    {
        printf("lathos onoma arxeiou\n");
        exit(1);
    }
    pFile = fopen (array[2] ,"rb");
    fseek(pFile, 4, SEEK_SET);
    fread(&chunkSize, sizeof(dword), 1, pFile);
    chunkSize = chunkSize + 8;
    fseek(pFile, 22, SEEK_SET);
    fread(&numberChannels, sizeof(word), 1, pFile);
    fseek(pFile, 24, SEEK_SET);
    fread(&sampleRate, sizeof(dword), 1, pFile);
    fseek(pFile, 34, SEEK_SET);
    fread(&bitsPerSample, sizeof(word), 1, pFile);
    count = numberChannels * bitsPerSample;
    fileSize = count * sampleRate;
    printf("%s%d\n", "count: ", count);
    printf("%s%d\n", "chunkSize: ", chunkSize);
    printf("%s%d\n", "fileSize: ", fileSize);
    rewind(pFile);
    fread(head, sizeof(head), 1, pFile);
    pOutFile = fopen (array[3] ,"wb");
    fwrite(head, sizeof(head), 1, pOutFile);//grafei to header
    count = count/8;//4
    rev = (byte*)malloc(sizeof(byte) * 14);//pinakas typou unsigned char me #count theseis
    do
    {
        sum = sum + count;    
        fseek(pFile, (chunkSize-sum), SEEK_SET);    
        fread(rev, count, 1, pFile);
        fwrite(rev, count, 1, pOutFile);
    }while(chunkSize-sum != 44);
    fclose(pFile);
    fclose(pOutFile);
}
 
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Όταν λες δεν αντιγράφει αυτό που θέλεις τι εννοείς ? Κοίταξες τα δύο αρχεία σε Hex Editor και δεν ήταν το ένα το αντίστροφο του άλλου ?

 

Ο κώδικας δεν μου αρέσει βέβαια έτσι που τον έγραψες αλλά δείχνει να λειτουργεί με τη σύντομη δοκιμή που έκανα. Κατέβασα στη τύχη ένα video από το youtube (το Du Hast), απομόνωσα τον ήχο και τον έκανα wav και τον πέταξα στην συνάρτηση.

 

% ./a.out in.wav in.wav out.wav
count: 64
chunkSize: 91938860
fileSize: 2822400
% ls -al in.wav out.wav 
-rw-r--r-- 91938860 Ιούν 28 22:56 in.wav
-rw-r--r-- 91938860 Ιούν 28 22:56 out.wav
Το out.wav ακουστικά έδειχνε να είναι ανάποδο αλλά για σιγουριά το πέταξα στο audacity και έκανα χρονική αναστροφή. Ακούγοντας το κομμάτι που προέκυψε άκουγα το αρχικό Du Hast.

 

Άσχετο αλλά γιατί λες ότι count (δηλαδή numchannels * bitspersample) * samplerate είναι το filesize ? Πολλαπλασιάζοντας αυτά τα τρία μεγέθη δεν παίρνουμε πόσο είναι ένα sample ?

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Μπορείς να το κάνεις και με την βοήθεια του fseek SEEK_END διαβάζοντας δηλαδή ανάποδα το stream καθώς το γράφεις με την νέα του σειρά.

 

 

 

[..]
	// Ανάποδη αντιγραφή fIn στο fOut
        long lOfst = -1;

	if(!fseek(fIn, lOfst, SEEK_END))
	{
	   while((c = fgetc(fIn)) != EOF)
	     if(ftell(fIn) - 1 > 44)
	     {
	        fputc(c, fOut);
		fseek(fIn, lOfst--, SEEK_END);
	     }
             else
		 break;
	}
	else
	{
	  // Η fseek απέτυχε!
	}
[..]

Καλή συνέχεια!!

Επεξ/σία από Directx
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ευχαριστώ πολύ

 

Όταν λες δεν αντιγράφει αυτό που θέλεις τι εννοείς ? Κοίταξες τα δύο αρχεία σε Hex Editor και δεν ήταν το ένα το αντίστροφο του άλλου ?

Ο κώδικας δεν μου αρέσει βέβαια έτσι που τον έγραψες αλλά δείχνει να λειτουργεί με τη σύντομη δοκιμή που έκανα. Κατέβασα στη τύχη ένα video από το youtube (το Du Hast), απομόνωσα τον ήχο και τον έκανα wav και τον πέταξα στην συνάρτηση.
 

% ./a.out in.wav in.wav out.wav
count: 64
chunkSize: 91938860
fileSize: 2822400
% ls -al in.wav out.wav 
-rw-r--r-- 91938860 Ιούν 28 22:56 in.wav
-rw-r--r-- 91938860 Ιούν 28 22:56 out.wav
Το out.wav ακουστικά έδειχνε να είναι ανάποδο αλλά για σιγουριά το πέταξα στο audacity και έκανα χρονική αναστροφή. Ακούγοντας το κομμάτι που προέκυψε άκουγα το αρχικό Du Hast.

Άσχετο αλλά γιατί λες ότι count (δηλαδή numchannels * bitspersample) * samplerate είναι το filesize ? Πολλαπλασιάζοντας αυτά τα τρία μεγέθη δεν παίρνουμε πόσο είναι ένα sample ?

 

Συγνώμη γιατί θα αρχίσω να βαράω το κεφάλι μου στον τοίχο. Μου λες ότι το τσέκαρες και είναι εντάξει? αυτό που κάνω για να το ελένξω είναι να τρέξω το πρόγραμμα. και να δημιουργίσω το output.wav (πχ) έπειτα στο τερματικό γράφω hexdump -C output.wav > output.txt  . Το ίδιο έχω κάνει και για το input.wav , τσεκάρω τα 10 τελευταία bytes του input.txt με τα 10 πρώτα(μετά το header) στο output.txt και δεν είναι τα ίδια. Νομίζα πως με την εντολή fseek(pFile, (chunkSize-sum), SEEK_SET); με chunkSize to chunkSize του header + 8 και με sum=4 πηγαίνει κάπου αλλόυ και όχι 4 bytes από το τέλος.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ευχαριστώ πολύ

 

Συγνώμη γιατί θα αρχίσω να βαράω το κεφάλι μου στον τοίχο. Μου λες ότι το τσέκαρες και είναι εντάξει? αυτό που κάνω για να το ελένξω είναι να τρέξω το πρόγραμμα. και να δημιουργίσω το output.wav (πχ) έπειτα στο τερματικό γράφω hexdump -C output.wav > output.txt  . Το ίδιο έχω κάνει και για το input.wav , τσεκάρω τα 10 τελευταία bytes του input.txt με τα 10 πρώτα(μετά το header) στο output.txt και δεν είναι τα ίδια. Νομίζα πως με την εντολή fseek(pFile, (chunkSize-sum), SEEK_SET); με chunkSize to chunkSize του header + 8 και με sum=4 πηγαίνει κάπου αλλόυ και όχι 4 bytes από το τέλος.

Πριν το δοκίμασα μόνο ακουστικά χωρίς hex editor αλλά και σε hex editor φαίνεται να παίζει.

 

in:
00000020  01 00 08 00 64 61 74 61  f8 a2 00 00 80 7f 7f 7f  |....data........|
00000030  7f 7f 7f 7f 7f 7f 7f 7f  7f 7f 7f 80 80 80 80 80  |................|
...
0000a320  7f 7f 7f 7f 4c 49 53 54  4a 00 00 00 49 4e 46 4f  |....LISTJ...INFO|
0000a330  49 53 46 54 3e 00 00 00  46 69 6c 65 20 63 72 65  |ISFT>...File cre|
0000a340  61 74 65 64 20 62 79 20  47 6f 6c 64 57 61 76 65  |ated by GoldWave|
0000a350  2e 20 20 47 6f 6c 64 57  61 76 65 20 63 6f 70 79  |.  GoldWave copy|
0000a360  72 69 67 68 74 20 28 43  29 20 43 68 72 69 73 20  |right (C) Chris |
0000a370  43 72 61 69 67 00                                 |Craig.|

out:
00000020  01 00 08 00 64 61 74 61  f8 a2 00 00 00 67 69 61  |....data.....gia|
00000030  72 43 20 73 69 72 68 43  20 29 43 28 20 74 68 67  |rC sirhC )C( thg|
00000040  69 72 79 70 6f 63 20 65  76 61 57 64 6c 6f 47 20  |irypoc evaWdloG |
00000050  20 2e 65 76 61 57 64 6c  6f 47 20 79 62 20 64 65  | .evaWdloG yb de|
00000060  74 61 65 72 63 20 65 6c  69 46 00 00 00 3e 54 46  |taerc eliF...>TF|
00000070  53 49 4f 46 4e 49 00 00  00 4a 54 53 49 4c 7f 7f  |SIOFNI...JTSIL..|
..
0000a360  80 80 80 80 80 80 80 7f  7f 7f 7f 7f 7f 7f 7f 7f  |................|
0000a370  7f 7f 7f 7f 7f 80                                 |......|
Το Bond Martini που χρησιμοποίησε ο DirectX. Από 4 bytes μετά το data και μετά είναι αντεστραμμένο. Πειράζει βέβαια και τον header που υπάρχει στο τέλος με τις πληροφορίες αλλά το παραβλέπουμε αυτό.

 

in:
00002020  00 00 00 00 00 00 00 00  00 00 00 00 5c c2 54 b0  |............\.T.|
00002030  cd 01 1e 2e 03 33 de b0  3c 60 f5 30 dd c9 86 30  |.....3..<`.0...0|
...
057a4010  b0 7d 43 2f e0 fe a1 31  7e 60 83 b0 6e 1f 9b b1  |.}C/...1~`..n...|
057a4020  67 78 7b af 10 dd 51 b1  39 a1 a9 30 00 00 00 00  |gx{...Q.9..0....|

out:
0000a020  00 00 00 80 00 00 00 00  00 00 00 00 10 dd 51 b1  |..............Q.|
0000a030  39 a1 a9 30 6e 1f 9b b1  67 78 7b af e0 fe a1 31  |9..0n...gx{....1|
0000a040  7e 60 83 b0 12 f1 0e 32  b0 7d 43 2f 66 af 0a 32  |~`.....2.}C/f..2|
...
057ac010  f4 4f fe b1 dd c9 86 30  a9 c1 ef b0 03 33 de b0  |.O.....0.....3..|
057ac020  3c 60 f5 30 5c c2 54 b0  cd 01 1e 2e 00 00 00 00  |<`.0\.T.........|
Το Du Hast στην αρχή και στο τέλος είχε πολλά μηδενικά τα οποία για συντομία δεν τα έκανα paste οπότε δεν φαίνεται τόσο καλά όσο το προηγούμενο παράδειγμα αλλά και εδώ υπάρχει αντιστροφή.
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

struct riff_s {
	uint32_t chunksize; // 4
	uint16_t channels; // 22
	uint16_t bitspersample; // 34
	uint32_t sc2size; // 40
	uint8_t raw[44];
};
Μια δομή με τα απαραίτητα πεδία. Σε μια πρότυπη υλοποίηση που κάλυπτε τα πάντα θα χρειαζόμασταν όλα τα πεδία για ελέγχους αλλά για τη παρούσα φάση μας φτάνουν αυτά.

 

static inline uint32_t read_le32(uint8_t *array, uint8_t offset)
{
	uint32_t dest;
	uint8_t *p = array;
	p += offset;
	dest = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
	return dest;
}

static inline uint16_t read_le16(uint8_t *array, uint8_t offset)
{
	uint16_t dest;
	uint8_t *p = array;
	p += offset;
	dest = p[0] | (p[1] << 8);
	return dest;
}
Συναρτήσεις για να διαβάζουν σωστά τα πεδία ανεξάρτητα από την endianness του επεξεργαστή μας. Άχρηστες μια και το πρόγραμμα θα τρέξει μόνο σε x86 δηλαδή little-endian αλλά είναι τόσο μικρές που δεν πειράζει να υπάρχουν. Σημειωτέον ότι τα wav δεν είναι απαραίτητο ότι χρησιμοποιούν little-endian στους headers αλλά μπορούν να χρησιμοποιούν και big.

 

int main(int argc, char *argv[])
{
    int ret = 0;
    struct riff_s riff;
    uint8_t *buf, *tmp;
    uint32_t ssize, samples, listsize;
    uint32_t i = 0;
    FILE *fin, *fout;

    fin = fopen(argv[1], "rb");
    if (fin == NULL) {
	    ret = -1;
	    goto in;
    }
    fread(riff.raw, sizeof(riff.raw), 1, fin);
    /* mege8os olou tou arxeiou - 8 bytes */
    riff.chunksize = read_le32(riff.raw, 4);
    riff.channels = read_le16(riff.raw, 22);
    riff.bitspersample = read_le16(riff.raw, 34);
    /* mege8os tou data chunk diladi tou hxou */
    riff.sc2size = read_le32(riff.raw, 40);
Άνοιγμα του αρχείου και ανάγνωση των απαραίτητων πεδίων.

 

    ssize = riff.channels * riff.bitspersample / 8;
    /* an to chunksize einai megalutero tou sc2size shmainei oti exoume
     * header me plhrofories opws to id3 tag twn mp3 */
    listsize = riff.chunksize + 8 - 44 - riff.sc2size;
Υπολογισμός του μεγέθους ενός sample (είχα την εντύπωση ότι στο αρχείο πρώτα είναι όλο το ένα channel και μετά όλο το άλλο αλλά φαίνεται να είναι πλεγμένα) και έλεγχος αν υπάρχει στο τέλος header με πληροφορίες όπως υπήρχε στο αρχείο Bond Martini που έδωσε ο DirectX. Αν το μέγεθος του data κομματιού μαζί με 44 που είναι ο header είναι διαφορετικό από το chunksize, τότε περισσεύουν bytes που λογικά θα πρέπει να είναι κάποιο "id3" με πληροφορίες.

 

    buf = malloc(riff.sc2size);
    tmp = malloc(ssize);
    if ((buf == NULL) || (tmp == NULL)) {
	    fprintf(stderr, "Me want more cookie\n");
	    goto in;
    }
    fread(buf, riff.sc2size, 1, fin);
    samples = riff.sc2size / ssize;
    
    while (i < samples) {
	    samples--;
	    memcpy(tmp, &buf[samples * ssize], ssize);
	    memcpy(&buf[samples * ssize], &buf[i * ssize], ssize);
	    memcpy(&buf[i * ssize], tmp, ssize);
	    i++;
    }
Ανάγνωση όλου του ήχου με την μία και αντιστροφή του στη μνήμη. Έχει το πλεονέκτημα ότι είναι πολύ πιο γρήγορο επειδή δεν χρειάζεται τα seek στο αρχείο (για το 9MB Du Hast το δικό σου ήθελε 2.75sec ενώ αυτό θέλει 0.49sec) αλλά μειονέκτημα ότι θέλει πολύ περισσότερη μνήμη από το δικό σου.

 

    fout = fopen(argv[2], "wb");
    if (fout == NULL) {
	    ret = -2;
	    goto out;
    }
    fwrite(riff.raw, sizeof(riff.raw), 1, fout);
    fwrite(buf, riff.sc2size, 1, fout);
    if (listsize != 0) {
	    if (listsize > riff.sc2size)
		    buf = realloc(buf, listsize);
	    fread(buf, listsize, 1, fin);
	    fwrite(buf, listsize, 1, fout);
    }
Εγγραφή του raw header όπως τον διαβάσαμε και του αντιστραμμένου buffer στο αρχείο εξόδου. Επίσης, αν έχουμε έξτρα header τον γράφουμε και αυτόν όπως είναι.

 

    free(tmp);
    free(buf);
out:
    fclose(fout);
in:
    fclose(fin);

    return ret;
}
Housekeeping και τέλος της main.

 

Εννοείται πως ο κώδικας για να είναι δόκιμος θέλει 100 πράγματα ακόμη. Τα ονόματα των μεταβλητών και η δόμηση δεν είναι και τόσο καλά, δεν υποστηρίζει wav με big-endian, δεν ελέγχει καν αν αυτό που του δώθηκε είναι wav ή περιέχει σκουπίδια (πχ διαιρώ με το ssize το οποίο μπορεί να πάρει τιμή 0 αν το αρχείο δεν είναι σωστό wav), χρησιμοποιεί μαγικές τιμές για τα offset και επίσης θεωρεί ότι το μέγεθος του header είναι πάντα 44, δεν ελέγχει την realloc και τις fread,fwrite και ένα κάρο άλλα πράγματα.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...