le traitement de l'audio d'un fichier wav avec C
Je suis en train de travailler sur le traitement de l'amplitude d'un fichier wav et mise à l'échelle par un décimal facteur. Je suis d'essayer d'envelopper ma tête autour de la façon de lire et de re-écrire le fichier dans un mémoire de manière efficace tout en essayant de relever les nuances de la langue (je suis novice en C). Le fichier peut être soit en 8 ou 16 bits. La façon dont j'ai pensé à faire c'est de commencer par la lecture de la données d'en-tête dans certains pré-définis struct, puis le traitement des données réelles dans une boucle où je vais lire un bloc de données dans une mémoire tampon, faire tout ce qui est nécessaire pour elle, et puis les écrire dans la sortie.
#include <stdio.h>
#include <stdlib.h>
typedef struct header
{
char chunk_id[4];
int chunk_size;
char format[4];
char subchunk1_id[4];
int subchunk1_size;
short int audio_format;
short int num_channels;
int sample_rate;
int byte_rate;
short int block_align;
short int bits_per_sample;
short int extra_param_size;
char subchunk2_id[4];
int subchunk2_size;
} header;
typedef struct header* header_p;
void scale_wav_file(char * input, float factor, int is_8bit)
{
FILE * infile = fopen(input, "rb");
FILE * outfile = fopen("outfile.wav", "wb");
int BUFSIZE = 4000, i, MAX_8BIT_AMP = 255, MAX_16BIT_AMP = 32678;
//used for processing 8-bit file
unsigned char inbuff8[BUFSIZE], outbuff8[BUFSIZE];
//used for processing 16-bit file
short int inbuff16[BUFSIZE], outbuff16[BUFSIZE];
//header_p points to a header struct that contains the file's metadata fields
header_p meta = (header_p)malloc(sizeof(header));
if (infile)
{
//read and write header data
fread(meta, 1, sizeof(header), infile);
fwrite(meta, 1, sizeof(meta), outfile);
while (!feof(infile))
{
if (is_8bit)
{
fread(inbuff8, 1, BUFSIZE, infile);
} else {
fread(inbuff16, 1, BUFSIZE, infile);
}
//scale amplitude for 8/16 bits
for (i=0; i < BUFSIZE; ++i)
{
if (is_8bit)
{
outbuff8[i] = factor * inbuff8[i];
if ((int)outbuff8[i] > MAX_8BIT_AMP)
{
outbuff8[i] = MAX_8BIT_AMP;
}
} else {
outbuff16[i] = factor * inbuff16[i];
if ((int)outbuff16[i] > MAX_16BIT_AMP)
{
outbuff16[i] = MAX_16BIT_AMP;
} else if ((int)outbuff16[i] < -MAX_16BIT_AMP) {
outbuff16[i] = -MAX_16BIT_AMP;
}
}
}
//write to output file for 8/16 bit
if (is_8bit)
{
fwrite(outbuff8, 1, BUFSIZE, outfile);
} else {
fwrite(outbuff16, 1, BUFSIZE, outfile);
}
}
}
//cleanup
if (infile) { fclose(infile); }
if (outfile) { fclose(outfile); }
if (meta) { free(meta); }
}
int main (int argc, char const *argv[])
{
char infile[] = "file.wav";
float factor = 0.5;
scale_wav_file(infile, factor, 0);
return 0;
}
Je suis de différentes tailles de fichier à la fin (par 1k ou alors, pour un fichier de 40 mo), et je soupçonne que cela est dû au fait que je suis en train d'écrire un ensemble de tampon à la sortie, même si le fichier a peut-être mis fin, avant de remplir la totalité de la taille de la mémoire tampon. Aussi, le fichier de sortie est foiré - ne pas jouer ou ouvrez - donc, je suis probablement faire la chose de mal. Des conseils sur les endroits où je suis gâcher sera grande. Merci!
- Quand vous dites que l'entrée et la sortie des fichiers de tailles différentes, est le fichier de sortie plus grande ou plus petite que celle de l'entrée?
- de sortie est plus grande
Vous devez vous connecter pour publier un commentaire.
1 Vous êtes en train de lire des octets au lieu de 16 bits des échantillons dans cette branche else:
2 Vous n'avez pas saturer les valeurs lors de la mise à l'échelle, par exemple, d'origine échantillon de 16 bits = 32000 et facteur = 1.5 enrouler autour de la valeur de l'entier au lieu de serrage au maximum de 32767.
3 Vous ne regardez pas le RIFF et d'autres en-têtes à tous. En fichiers WAV, il est possible que les données audio est suivie par une information des pieds de page ou précédé par d'autres en-têtes. Ou en d'autres termes: Votre
header
struct est trop statique. Vous devriez aussi lire le format WAV à partir du fichier au lieu d'avoir un paramètre de dire que c'est 8 bits des échantillons.4 Cela ne marchera pas:
8-bit/16-bit valeurs ne sera jamais supérieure à 255/32768 sauf si votre ordinateur insère un peu de magie de bits dans la mémoire lorsque les entiers de débordement 😛
Et échantillons audio sont signés, alors, les plages sont de -128;127 -32768;32767. Vérification de dépassement doit se produire dans la multiplication de l'expression. Vous êtes également à faire des hypothèses sur le floating-point entier de mode d'arrondi, qui est configurable et doivent être considérés. Quelque chose comme
if(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768)
, peut-être.5 Vous n'avez pas stocker le résultat de
fread
, si vous écrivez trop d'échantillons pour le fichier de sortie.6 Et comme un dernier point, vous êtes en train de réinventer la roue. Tant que c'est pour apprendre, c'est d'accord. Autre chose que vous devriez utiliser des bibliothèques existantes.
Il est beaucoup mieux d'utiliser les bibliothèques pour la lecture et l'écriture des fichiers audio. E. g.
libsndfile
. Cette page web a une liste de "d'autres projets semblables", vous pouvez aussi regarder. Lesndfile-tools
pourraient être de bons exemples de code pour apprendre à utiliser la bibliothèque.Je recommande de regarder le fichier original et le fichier de sortie dans un éditeur hexadécimal pour voir si vous êtes à la ré-écriture des données correctement. Si le fichier ne sera pas jouer ou ouverte, les chances sont l'en-tête du fichier de sortie n'est pas correct.
Une autre option est de supprimer vos fichiers audio logique de traitement et il suffit de lire dans le fichier source de votre tampon interne et l'écrire dans un fichier. Si votre code peut générer un permis de travail de sortie de fichier de cette manière, vous pouvez réduire le problème à votre code de traitement.
Vous pouvez aussi commencer avec un fichier plus petit que 40 mo. Si rien d'autre, prendre une copie de ce fichier d'entrée et la réduire à un couple de secondes de données audio. Un fichier plus petit sera plus facile d'inspecter manuellement.
Edit: Les appels à
fread()
etfwrite()
besoin d'avoir leurs valeurs de retour vérifiée. Ces fonctions renvoient le nombre d'éléments lus ou écrits, et si un appel à la fonction renvoie une valeur de moins que prévu, alors que cela pourrait être la source de votre fichier différence de taille.Aussi, le deuxième paramètre à
fread
est exprimée en octets. Donc, si vous voulez lire-remplir un tampon, vous avez besoin de dire quelque chose de plus commefread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);
. Le code actuel ne lire dansBUFSIZE
octets (qui travaille pour le 8-bits au cas par coïncidence, mais je vous recommande de le changer trop pour plus de clarté).Cette ligne suivante n'est pas nécessaire pour la lecture de WAV les en-têtes (en fait l'en-tête de 48 octets de long, au lieu de "standard" 44):
Si possible, vous voudrez peut-être chercher à un langage différent de C, sauf si c'est spécifiquement pour une application C.