soundeditor/atsa/save-load-sound.c

395 lines
12 KiB
C
Raw Permalink Normal View History

/* save-load-sound.c
* atsa: ATS analysis implementation
* Oscar Pablo Di Liscia / Pete Moss / Juan Pampin
*/
#include "atsa.h"
#include <stdio.h>
#include <stdlib.h>
static int my_filelength(FILE *fpt);
static double byte_swap(double *in);
static void byte_swap_header(ATS_HEADER *head, int swapMagic);
/* ats_load
* ========
* loads an ATS_SOUND from disk (whatever little/big endian byte order).
* sound: pointer to ATS_SOUND structure
* infile: pointer to input ats file
* Returns the total file size in bytes (or <0 value upon error)
*/
int ats_load(ATS_SOUND *sound, const char *infile, int* sound_type)
{
FILE* pATSFile;
int nFileSize;
ATS_HEADER sHeader;
int bNeedByteSwap = 0;
int n1, n2;
int nTheoricalDataSize;
double dMaxAmp, dMaxFreq, dTmp;
int nFrameIdx, nPartIdx;
fprintf(stdout, "Loading ATS sound from '%s' ...\n", infile);
// Open the .ats file.
pATSFile = fopen(infile, "rb");
if (!pATSFile)
{
fprintf(stderr, "Error: Could not open '%s'\n", infile);
return -1;
}
// Determine real file length.
nFileSize = my_filelength(pATSFile);
// Load header from file.
fread(&sHeader, sizeof(ATS_HEADER), 1, pATSFile);
// Check file format and little/big endian (swap header byte if necessary).
if (sHeader.mag != ATS_MAGIC_NUMBER)
{
double dMagNum = sHeader.mag;
sHeader.mag = byte_swap(&dMagNum);
if ((int)sHeader.mag != (int)ATS_MAGIC_NUMBER)
{
fprintf(stderr, "Error: Could not find magic number in header\n");
fclose(pATSFile);
return -1;
}
else
{
bNeedByteSwap=1; //now we know that we must swap the bytes
byte_swap_header(&sHeader, 0); //swap bytes of header data first
}
}
// Check consistency between duration, sampling rate, frame size and number of frames.
if (sHeader.fra * sHeader.fs != sHeader.sr * sHeader.dur)
{
sHeader.dur = sHeader.fra * sHeader.fs / sHeader.sr;
fprintf(stdout, "Warning: Unconsistent sound duration ; "
"trusting in frame description => fixed to %f s\n", sHeader.dur);
}
// Dump sound properties.
fprintf(stdout, " File type = %d\n", (int)sHeader.typ);
fprintf(stdout, " File length = %d bytes\n", nFileSize);
fprintf(stdout, " Duration = %f s\n", sHeader.dur);
fprintf(stdout, " Sampling rate = %d Hz\n", (int)sHeader.sr);
fprintf(stdout, " Nb partials = %d\n", (int)sHeader.par);
fprintf(stdout, " Max. frequency = %f\n", sHeader.mf);
fprintf(stdout, " Max. amplitude = %f\n", sHeader.ma);
fprintf(stdout, " Noise ? = %s\n", FILE_HAS_NOISE(sHeader.typ) ? "true" : "false");
fprintf(stdout, " Phase ? = %s\n", FILE_HAS_PHASE(sHeader.typ) ? "true" : "false");
fprintf(stdout, " Nb frames = %d\n", (int)sHeader.fra);
fprintf(stdout, " Frame size = %d samples\n", (int)sHeader.fs);
fprintf(stdout, " Windows size = %d samples\n", (int)sHeader.ws);
// Get sound type.
*sound_type = (int)sHeader.typ;
// Get length of data from header and compare it to the real file length.
n1 = 2;
if (FILE_HAS_PHASE(sHeader.typ))
n1 += 1;
n2 = 0;
if (FILE_HAS_NOISE(sHeader.typ))
n2 += 1;
nTheoricalDataSize =
sizeof(ATS_HEADER)
+ ((int)sHeader.par * (int)sHeader.fra * sizeof(double) * n1)
+ (sizeof(double) * (int)sHeader.fra)
+ (ATSA_CRITICAL_BANDS * (int)sHeader.fra * sizeof(double) * n2);
if (nTheoricalDataSize != nFileSize)
{
fprintf(stderr, "Error: File size does not match header data\n");
fclose(pATSFile);
return -1;
}
// Reinitialize sound structure from loaded header contents.
init_sound(sound,
(int)sHeader.sr,
(int)sHeader.fs,
(int)sHeader.ws,
(int)sHeader.fra, // WARNING: mem_realloc() used fra+1 !!!!!??????
(double)sHeader.dur,
(int)sHeader.par,
FILE_HAS_NOISE(sHeader.typ));
// All right till now : load partials and residual ATS data.
fseek(pATSFile,sizeof(ATS_HEADER),SEEK_SET);
// For each frame :
dMaxAmp = 0.0;
dMaxFreq = 0.0;
for (nFrameIdx = 0; nFrameIdx < sound->frames; ++nFrameIdx)
{
// 1) Load partials.
fread(&dTmp,1,sizeof(double),pATSFile); //read just one time value per frame
if (bNeedByteSwap) {
sound->time[0][nFrameIdx]=byte_swap(&dTmp);
}
else {
sound->time[0][nFrameIdx]=dTmp;
}
// For each partial :
for (nPartIdx=0; nPartIdx<sound->partials; ++nPartIdx)
{
// Copy time value from 1st partial.
sound->time[nPartIdx][nFrameIdx] = sound->time[0][nFrameIdx];
// Read amp value for each partial
fread(&dTmp,1,sizeof(double),pATSFile);
if(bNeedByteSwap) {
sound->amp[nPartIdx][nFrameIdx]=byte_swap(&dTmp);
}
else {
sound->amp[nPartIdx][nFrameIdx]= dTmp;
}
// Update max amplitude if lower (ATSH did not save it !)
if (sound->amp[nPartIdx][nFrameIdx] > dMaxAmp)
dMaxAmp = sound->amp[nPartIdx][nFrameIdx];
// Read freq value for each partial
fread(&dTmp,1,sizeof(double),pATSFile);
if(bNeedByteSwap) {
sound->frq[nPartIdx][nFrameIdx]=byte_swap(&dTmp);
}
else {
sound->frq[nPartIdx][nFrameIdx]= dTmp;
}
// Update max frequency if lower (ATSH did not save it !)
if (sound->frq[nPartIdx][nFrameIdx] > dMaxFreq)
dMaxFreq = sound->frq[nPartIdx][nFrameIdx];
// Read phase data if any...
if(FILE_HAS_PHASE(sHeader.typ)) {
fread(&dTmp,1,sizeof(double),pATSFile);
if(bNeedByteSwap) {
sound->pha[nPartIdx][nFrameIdx]=byte_swap(&dTmp);
}
else {
sound->pha[nPartIdx][nFrameIdx]= dTmp;
}
}
}
// 2) Load residual if any.
if (FILE_HAS_NOISE(sHeader.typ)) {
int nBandIdx;
for (nBandIdx=0; nBandIdx<ATSA_CRITICAL_BANDS; nBandIdx++) {
fread(&dTmp,1,sizeof(double),pATSFile);
if(bNeedByteSwap) {
sound->band_energy[nBandIdx][nFrameIdx]=byte_swap(&dTmp);
}
else {
sound->band_energy[nBandIdx][nFrameIdx]=dTmp;
}
}
}
}
// Close the .ats file.
fclose(pATSFile);
// Set sound maxFreq and maxAmpl (as ATSH did not save them in the .ats file).
sound->ampmax = dMaxAmp;
sound->frqmax = dMaxFreq;
// Setup the av array (partial averages and order = increasing frequency,
// as enforced by ats_save, even in ATSH)
set_av(sound);
// Set optimized flag, as not saved in .ats file
// Note: Set to "true" because ats_save assumes the sound was optimized before.
sound->optimized = 1;
fprintf(stdout, "... done (%d bytes read).\n", nFileSize);
return nFileSize;
}
/* ats_save
* ========
* saves an optimized ATS_SOUND to disk.
* sound: pointer to ATS_SOUND structure
* outfile: pointer to output ats file
* SMR_thres: partials with average SMR under this are not written
* type: file type
* Returns the total file size (nb of written bytes), or -1 in case of any error
*/
int ats_save(ATS_SOUND *sound, const char *outfile, double SMR_thres, int type)
{
int frm, i, par, dead=0;
double daux;
ATS_HEADER header;
size_t written = 0;
FILE* ofile;
fprintf(stdout, "Saving ATS sound to '%s' ...\n", outfile);
/* refuse to save if sound was not optimized */
if( sound->optimized == NIL ){
fprintf(stderr, "Can not save a sound that has not been optimized before !\n");
return -1;
}
/* open output file */
ofile = fopen(outfile, "wb");
if (!ofile) {
fprintf(stderr, "Could not open %s for writing\n", outfile);
return -1;
}
/* count how many partials are dead */
for (i = 0; i < sound->partials; i++){
if (sound->av[i].frq <= 0.0 || sound->av[i].smr < SMR_thres){
dead++;
}
}
/* fill header up */
header.mag = ATS_MAGIC_NUMBER;
header.sr = (double)sound->srate;
header.fs = (double)sound->frame_size;
header.ws = (double)sound->window_size;
header.par = (double)(sound->partials - dead);
header.fra = (double)sound->frames;
header.ma = sound->ampmax;
header.mf = sound->frqmax;
header.dur = sound->dur;
header.typ = (double)type;
fprintf(stdout, " File type = %d\n", type);
fprintf(stdout, " Duration = %f s\n", sound->dur);
fprintf(stdout, " Sampling rate = %d Hz\n", sound->srate);
fprintf(stdout, " Nb kept partials = %d\n", sound->partials - dead);
fprintf(stdout, " Nb dead partials = %d (< %f RMS threshold or freq <= 0)\n", dead, SMR_thres);
fprintf(stdout, " Optimized = %s\n", sound->optimized != NIL ? "true" : "false");
fprintf(stdout, " Max. frequency = %f\n", sound->frqmax);
fprintf(stdout, " Max. amplitude = %f\n", sound->ampmax);
fprintf(stdout, " Noise ? = %s\n", FILE_HAS_NOISE(header.typ) ? "true" : "false");
fprintf(stdout, " Phase ? = %s\n", FILE_HAS_PHASE(header.typ) ? "true" : "false");
fprintf(stdout, " Nb frames = %d\n", sound->frames);
fprintf(stdout, " Frame size = %d samples\n", sound->frame_size);
fprintf(stdout, " Windows size = %d samples\n", sound->window_size);
/* write header */
fseek(ofile, 0, SEEK_SET);
written += sizeof(ATS_HEADER) * fwrite(&header, sizeof(ATS_HEADER), 1, ofile);
/* write frame data */
for(frm=0; frm<sound->frames; frm++) {
daux = sound->time[0][frm];
written += sizeof(double) * fwrite(&daux, sizeof(double), 1, ofile);
for(i=0; i<sound->partials; i++) {
/* we ouput data in increasing frequency order and we check for dead partials */
if(sound->av[i].frq > 0.0 && sound->av[i].smr >= SMR_thres){
/* get partial number from sound */
par = sound->av[i].track;
/* output data to file */
daux = sound->amp[par][frm];
written += sizeof(double) * fwrite(&daux, sizeof(double), 1, ofile);
daux = sound->frq[par][frm];
written += sizeof(double) * fwrite(&daux, sizeof(double), 1, ofile);
if(FILE_HAS_PHASE(header.typ)){
daux = sound->pha[par][frm];
written += sizeof(double) * fwrite(&daux, sizeof(double), 1, ofile);
}
}
}
/* write noise data */
if(FILE_HAS_NOISE(header.typ)){
for(i=0 ; i<ATSA_CRITICAL_BANDS ; i++){
daux = sound->band_energy[i][frm];
written += sizeof(double) * fwrite(&daux, sizeof(double), 1, ofile);
}
}
}
fclose(ofile);
fprintf(stdout, "... done (%d bytes written).\n", (int)written);
return (int)written;
}
/* Usefull functions taken from atsh/atsh-files.c =========================*/
static int my_filelength(FILE *fpt)
{
int size = 0;
fseek(fpt, 0, SEEK_END);
size=(int)ftell(fpt);
fseek(fpt, 0, SEEK_SET);
return size;
}
static double byte_swap(double *in)
{
int size;
double k;
unsigned char *sw_in, *sw_out;
double *out;
size = sizeof(double);
sw_out = (unsigned char*)malloc(size);
k = (double)in[0];
sw_in = (unsigned char *)&k;
sw_out[0] = sw_in[7];
sw_out[1] = sw_in[6];
sw_out[2] = sw_in[5];
sw_out[3] = sw_in[4];
sw_out[4] = sw_in[3];
sw_out[5] = sw_in[2];
sw_out[6] = sw_in[1];
sw_out[7] = sw_in[0];
out = (double*)sw_out;
return *out;
}
static void byte_swap_header(ATS_HEADER *head, int swapMagic)
{
double tmp;
if (swapMagic)
{ //may be already swapped
tmp=head->mag;
head->mag=byte_swap(&tmp);
}
tmp=head->sr;
head->sr = byte_swap(&tmp);
tmp=head->fs;
head->fs= byte_swap(&tmp);
tmp=head->ws;
head->ws= byte_swap(&tmp);
tmp=head->par;
head->par= byte_swap(&tmp);
tmp=head->fra;
head->fra= byte_swap(&tmp);
tmp=head->ma;
head->ma= byte_swap(&tmp);
tmp=head->mf;
head->mf= byte_swap(&tmp);
tmp=head->dur;
head->dur= byte_swap(&tmp);
tmp=head->typ;
head->typ= byte_swap(&tmp);
}