576 lines
15 KiB
C++
576 lines
15 KiB
C++
|
/*************************************************************************/
|
||
|
/* */
|
||
|
/* Centre for Speech Technology Research */
|
||
|
/* University of Edinburgh, UK */
|
||
|
/* Copyright (c) 1995,1996 */
|
||
|
/* All Rights Reserved. */
|
||
|
/* */
|
||
|
/* Permission is hereby granted, free of charge, to use and distribute */
|
||
|
/* this software and its documentation without restriction, including */
|
||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||
|
/* distribute, sublicense, and/or sell copies of this work, and to */
|
||
|
/* permit persons to whom this work is furnished to do so, subject to */
|
||
|
/* the following conditions: */
|
||
|
/* 1. The code must retain the above copyright notice, this list of */
|
||
|
/* conditions and the following disclaimer. */
|
||
|
/* 2. Any modifications must be clearly marked as such. */
|
||
|
/* 3. Original authors' names are not deleted. */
|
||
|
/* 4. The authors' names are not used to endorse or promote products */
|
||
|
/* derived from this software without specific prior written */
|
||
|
/* permission. */
|
||
|
/* */
|
||
|
/* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */
|
||
|
/* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
|
||
|
/* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
|
||
|
/* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */
|
||
|
/* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
|
||
|
/* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
|
||
|
/* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
|
||
|
/* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
|
||
|
/* THIS SOFTWARE. */
|
||
|
/* */
|
||
|
/*************************************************************************/
|
||
|
/* */
|
||
|
/* Author : Richard Caley <rjc@cstr.ed.ac.uk> */
|
||
|
/* ------------------------------------------------------------------- */
|
||
|
/* Wave file input and output. */
|
||
|
/* */
|
||
|
/*************************************************************************/
|
||
|
#include <cstdlib>
|
||
|
#include "EST_Wave.h"
|
||
|
#include "EST_WaveFile.h"
|
||
|
#include "waveP.h"
|
||
|
#include "EST_cutils.h"
|
||
|
#include "EST_Option.h"
|
||
|
#include "EST_io_aux.h"
|
||
|
#include "stdio.h"
|
||
|
#include "math.h"
|
||
|
|
||
|
void extract(EST_Wave &sig, EST_Option &al);
|
||
|
|
||
|
typedef
|
||
|
EST_read_status (*standard_load_fn_fp)(EST_TokenStream &ts,
|
||
|
short **data, int *nsamp, int *nchan,
|
||
|
int *wsize,
|
||
|
int *srate,
|
||
|
EST_sample_type_t *stype, int *bo,
|
||
|
int offset, int length);
|
||
|
|
||
|
typedef
|
||
|
EST_write_status (*standard_save_fn_fp)(FILE *fp,
|
||
|
const short *data,
|
||
|
int offset, int nsamp,
|
||
|
int nchan, int srate,
|
||
|
EST_sample_type_t stype, int bo);
|
||
|
|
||
|
|
||
|
static
|
||
|
EST_read_status load_using(standard_load_fn_fp fn,
|
||
|
EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
short *data;
|
||
|
int nsamp;
|
||
|
int wsize;
|
||
|
int srate = rate;
|
||
|
|
||
|
EST_read_status status = (*fn)(ts,
|
||
|
&data, &nsamp, &nchan,
|
||
|
&wsize,
|
||
|
&srate, &stype, &bo,
|
||
|
offset, length);
|
||
|
|
||
|
if (status == read_ok)
|
||
|
{
|
||
|
wv.values().set_memory(data, 0, nsamp, nchan, TRUE);
|
||
|
wv.set_sample_rate(srate);
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
EST_write_status save_using(standard_save_fn_fp fn,
|
||
|
FILE *fp, const EST_Wave wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
EST_write_status status = (*fn)(fp,
|
||
|
wv.values().memory(),
|
||
|
0, wv.num_samples(), wv.num_channels(),
|
||
|
wv.sample_rate(),
|
||
|
stype, bo);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_nist(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
return load_using(load_wave_nist,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_nist(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
return save_using(save_wave_nist, fp, wv, stype, bo);
|
||
|
}
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_est(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
(void) ts;
|
||
|
return load_using(load_wave_est,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_est(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
return save_using(save_wave_est,
|
||
|
fp, wv,
|
||
|
stype, bo);
|
||
|
|
||
|
}
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_aiff(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
return load_using(load_wave_aiff,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_aiff(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
return save_using(save_wave_aiff, fp, wv, stype, bo);
|
||
|
}
|
||
|
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_riff(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
return load_using(load_wave_riff,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_riff(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
return save_using(save_wave_riff, fp, wv, stype, bo);
|
||
|
}
|
||
|
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_esps(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
return load_using(load_wave_sd,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_esps(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
return save_using(save_wave_sd,
|
||
|
fp, wv,
|
||
|
stype, bo);
|
||
|
}
|
||
|
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_audlab(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
return load_using(load_wave_audlab,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_audlab(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
return save_using(save_wave_audlab, fp, wv, stype, bo);
|
||
|
}
|
||
|
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_snd(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
return load_using(load_wave_snd,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_snd(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
return save_using(save_wave_snd, fp, wv, stype, bo);
|
||
|
}
|
||
|
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_raw(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
short *data;
|
||
|
int nsamp;
|
||
|
int wsize;
|
||
|
int srate = rate;
|
||
|
|
||
|
EST_read_status status = load_wave_raw(ts,
|
||
|
&data, &nsamp, &nchan,
|
||
|
&wsize,
|
||
|
&srate, &stype, &bo,
|
||
|
offset, length,
|
||
|
rate, stype, bo, nchan);
|
||
|
|
||
|
if (status == read_ok)
|
||
|
{
|
||
|
wv.values().set_memory(data, 0, nsamp, nchan, TRUE);
|
||
|
wv.set_sample_rate(srate);
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_raw(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
EST_write_status status = save_wave_raw(fp,
|
||
|
(short *)wv.values().memory(),
|
||
|
0, wv.num_samples(), wv.num_channels(),
|
||
|
wv.sample_rate(),
|
||
|
stype, bo);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
EST_read_status EST_WaveFile::load_ulaw(EST_TokenStream &ts,
|
||
|
EST_Wave &wv,
|
||
|
int rate,
|
||
|
EST_sample_type_t stype, int bo, int nchan,
|
||
|
int offset, int length)
|
||
|
{
|
||
|
return load_using(load_wave_ulaw,
|
||
|
ts, wv, rate,
|
||
|
stype, bo, nchan,
|
||
|
offset, length);
|
||
|
}
|
||
|
|
||
|
EST_write_status EST_WaveFile::save_ulaw(FILE *fp,
|
||
|
const EST_Wave &wv,
|
||
|
EST_sample_type_t stype, int bo)
|
||
|
{
|
||
|
EST_Wave localwv = wv;
|
||
|
localwv.resample(8000);
|
||
|
return save_using(save_wave_ulaw, fp, localwv, stype, bo);
|
||
|
}
|
||
|
|
||
|
static int parse_esps_r_option(EST_String arg, int &offset, int &length)
|
||
|
{
|
||
|
EST_String s, e;
|
||
|
|
||
|
if (arg.contains("-"))
|
||
|
{
|
||
|
s = arg.before("-");
|
||
|
e = arg.after("-");
|
||
|
}
|
||
|
else if (arg.contains(":"))
|
||
|
{
|
||
|
s = arg.before(":");
|
||
|
e = arg.after(":");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cerr << "Argument to -r is illformed " << arg << endl;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!s.matches(RXint))
|
||
|
{
|
||
|
cerr << "First argument to -r must be an integer " << arg << endl;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
offset = atoi(s);
|
||
|
if (e.contains("+"))
|
||
|
{
|
||
|
e = e.after("+");
|
||
|
length = atoi(e);
|
||
|
}
|
||
|
else
|
||
|
length = atoi(e) - offset;
|
||
|
|
||
|
if (length <= 0)
|
||
|
{
|
||
|
cerr << "length is negative or zero " << arg << endl;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
EST_read_status read_wave(EST_Wave &sig, const EST_String &in_file,
|
||
|
EST_Option &al)
|
||
|
{
|
||
|
char *sr;
|
||
|
EST_String fname, file_type, sample_type;
|
||
|
int sample_rate;
|
||
|
EST_read_status rval;
|
||
|
int num_channels;
|
||
|
int offset=0, length=0;
|
||
|
int bo;
|
||
|
|
||
|
if (in_file == "-")
|
||
|
fname = stdin_to_file();
|
||
|
else
|
||
|
fname = in_file;
|
||
|
|
||
|
if (al.present("-n"))
|
||
|
num_channels = al.ival("-n", 0);
|
||
|
else
|
||
|
num_channels = 1;
|
||
|
|
||
|
if (al.present("-ulaw"))
|
||
|
{
|
||
|
al.add_item("-itype","ulaw");
|
||
|
al.add_item("-f","8000");
|
||
|
}
|
||
|
if (al.present("-iswap"))
|
||
|
al.add_item("-ibo","other");
|
||
|
|
||
|
if (al.present("-istype"))
|
||
|
sample_type = al.val("-istype");
|
||
|
else
|
||
|
sample_type = sig.sample_type(); // else default type;
|
||
|
|
||
|
if (al.present("-itype"))
|
||
|
file_type = al.val("-itype"); // else default type;
|
||
|
else
|
||
|
file_type = "undef";
|
||
|
|
||
|
|
||
|
if (al.present("-f"))
|
||
|
sample_rate = al.ival("-f", 0);
|
||
|
else if ((sr = getenv("NA_PLAY_SAMPLE_RATE")) != NULL)
|
||
|
{
|
||
|
sample_rate = atoi(sr);
|
||
|
cerr << "Warning: no sample rate specified, " <<
|
||
|
" using NA_PLAY_SAMPLE_RATE environment variable\n";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sample_rate = EST_Wave::default_sample_rate;
|
||
|
if (file_type == "raw")
|
||
|
cerr << "Warning: no sample rate specified - using default " <<
|
||
|
sample_rate << endl;
|
||
|
}
|
||
|
|
||
|
if (file_type == "ulaw")
|
||
|
{
|
||
|
sample_rate = 8000;
|
||
|
sample_type = "mulaw";
|
||
|
}
|
||
|
|
||
|
if (al.present("-r")) // only load in part of waveform
|
||
|
{
|
||
|
if (parse_esps_r_option(al.val("-r"), offset, length) != 0)
|
||
|
return read_error;
|
||
|
}
|
||
|
else
|
||
|
offset = length = 0;
|
||
|
|
||
|
if (al.present("-iswap"))
|
||
|
bo = str_to_bo("swap");
|
||
|
else
|
||
|
bo = str_to_bo("native");
|
||
|
if (al.present("-ibo")) // can override -iswap
|
||
|
bo = str_to_bo(al.val("-ibo"));
|
||
|
|
||
|
if (file_type == "" ||file_type == "undef")
|
||
|
rval = sig.load(fname, offset, length, sample_rate);
|
||
|
else
|
||
|
rval = sig.load_file(fname,file_type, sample_rate,
|
||
|
sample_type, bo, num_channels, offset, length);
|
||
|
|
||
|
if ((rval == wrong_format) && (al.present("-basic")))
|
||
|
{
|
||
|
// For HTML audio/basic, it seems to mean headered or ulaw 8k
|
||
|
// so try to load it again as ulaw 8k.
|
||
|
rval = sig.load_file(fname, "raw", 8000,
|
||
|
"mulaw", bo, 1, offset, length);
|
||
|
}
|
||
|
if (rval != format_ok)
|
||
|
{
|
||
|
if (in_file == "-") unlink(fname);
|
||
|
cerr << "Cannot recognize file format or cannot access file: \"" << in_file << "\"\n";
|
||
|
return read_error;
|
||
|
}
|
||
|
|
||
|
if (al.present("-start") || al.present("-end")
|
||
|
|| al.present("-to") || al.present("-from"))
|
||
|
extract(sig, al);
|
||
|
|
||
|
if (in_file == "-") unlink(fname);
|
||
|
return read_ok;
|
||
|
}
|
||
|
|
||
|
EST_write_status write_wave(EST_Wave &sig, const EST_String &out_file,
|
||
|
EST_Option &al)
|
||
|
{
|
||
|
EST_String file_type, sample_type;
|
||
|
int bo;
|
||
|
|
||
|
if (al.present("-otype"))
|
||
|
file_type = al.val("-otype");
|
||
|
else
|
||
|
file_type = sig.file_type();
|
||
|
|
||
|
if (al.present("-ostype"))
|
||
|
sample_type = al.val("-ostype");
|
||
|
else
|
||
|
sample_type = "undef";
|
||
|
|
||
|
if (al.present("-oswap"))
|
||
|
bo = str_to_bo("swap");
|
||
|
else
|
||
|
bo = str_to_bo("native");
|
||
|
|
||
|
if (al.present("-obo")) // can over ride -oswap
|
||
|
bo = str_to_bo(al.val("-obo"));
|
||
|
|
||
|
if (sample_type == "undef" || sample_type == "")
|
||
|
sample_type = "short";
|
||
|
|
||
|
if (sig.save_file(out_file, file_type,
|
||
|
sample_type, bo) != write_ok)
|
||
|
{
|
||
|
cerr << "Cannot write file: \"" << out_file << "\"\n";
|
||
|
return write_error;
|
||
|
}
|
||
|
|
||
|
return write_ok;
|
||
|
}
|
||
|
|
||
|
EST_String EST_WaveFile::options_short(void)
|
||
|
{
|
||
|
EST_String s("");
|
||
|
|
||
|
for(int n=0; n< EST_WaveFile::map.n() ; n++)
|
||
|
{
|
||
|
const char *nm = EST_WaveFile::map.name(EST_WaveFile::map.token(n));
|
||
|
|
||
|
if (s != "")
|
||
|
s += ", ";
|
||
|
|
||
|
s += nm;
|
||
|
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
EST_String EST_WaveFile::options_supported(void)
|
||
|
{
|
||
|
EST_String s("Available wave file formats:\n");
|
||
|
|
||
|
for(int n=0; n< EST_WaveFile::map.n() ; n++)
|
||
|
{
|
||
|
const char *nm = EST_WaveFile::map.name(EST_WaveFile::map.token(n));
|
||
|
const char *d = EST_WaveFile::map.info(EST_WaveFile::map.token(n)).description;
|
||
|
|
||
|
s += EST_String::cat(" ", nm, EST_String(" ")*(12-strlen(nm)), d, "\n");
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
typedef struct TInfo {
|
||
|
bool recognise;
|
||
|
const char *description;
|
||
|
} TInfo;
|
||
|
|
||
|
// note the order here defines the order in which loads are tried.
|
||
|
|
||
|
|
||
|
static
|
||
|
EST_TValuedEnumDefinition<EST_WaveFileType, const char *, EST_WaveFile::Info> wavefile_names[] =
|
||
|
{
|
||
|
{ wff_none, { NULL },
|
||
|
{ FALSE, NULL, NULL, "unknown track file type"} },
|
||
|
{ wff_nist, { "nist", "timit" },
|
||
|
{ TRUE, EST_WaveFile::load_nist, EST_WaveFile::save_nist, "nist/timit" } },
|
||
|
{ wff_est, { "est"},
|
||
|
{ TRUE, EST_WaveFile::load_est, EST_WaveFile::save_est, "est" } },
|
||
|
{ wff_esps, { "esps", "sd"},
|
||
|
{ TRUE, EST_WaveFile::load_esps, EST_WaveFile::save_esps, "esps SD waveform" } },
|
||
|
{ wff_audlab, { "audlab", "vox"},
|
||
|
{ TRUE, EST_WaveFile::load_audlab, EST_WaveFile::save_audlab, "audlab waveform" } },
|
||
|
{ wff_snd, { "snd", "au"},
|
||
|
{ TRUE, EST_WaveFile::load_snd, EST_WaveFile::save_snd, "Sun snd file" } },
|
||
|
{ wff_aiff, { "aiff" },
|
||
|
{ TRUE, EST_WaveFile::load_aiff, EST_WaveFile::save_aiff, "Apple aiff file" } },
|
||
|
{ wff_riff, { "riff", "wav" },
|
||
|
{ TRUE, EST_WaveFile::load_riff, EST_WaveFile::save_riff, "Microsoft wav/riff file" } },
|
||
|
{ wff_raw, { "raw" },
|
||
|
{ FALSE, EST_WaveFile::load_raw, EST_WaveFile::save_raw, "Headerless File" } },
|
||
|
{ wff_ulaw, { "ulaw", "basic" },
|
||
|
{ FALSE, EST_WaveFile::load_ulaw, EST_WaveFile::save_ulaw, "Headerless 8K ulaw File" } },
|
||
|
{ wff_none, {NULL} }
|
||
|
};
|
||
|
|
||
|
EST_TNamedEnumI<EST_WaveFileType, EST_WaveFile::Info> EST_WaveFile::map(wavefile_names);
|
||
|
|
||
|
#if defined(INSTANTIATE_TEMPLATES)
|
||
|
|
||
|
#include "../base_class/EST_TNamedEnum.cc"
|
||
|
template class EST_TNamedEnumI<EST_WaveFileType, EST_WaveFile::Info>;
|
||
|
template class EST_TValuedEnumI<EST_WaveFileType, const char *,
|
||
|
EST_WaveFile::Info>;
|
||
|
|
||
|
#endif
|