#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/t_lock.h>
#include <AudioDebug.h>
#include <AudioUnixfile.h>
#include <libaudio.h>
#include <audio_hdr.h>
#include <audio/au.h>
AudioUnixfile::
AudioUnixfile(
const char *path,
const FileAccess acc):
AudioStream(path), mode(acc), block(TRUE), fd(-1),
infostring(new char[1]), infolength(1)
{
infostring[0] = '\0';
}
AudioUnixfile::
~AudioUnixfile()
{
if (opened())
(void) Close();
delete infostring;
}
AudioError AudioUnixfile::
OpenPath(
const char *)
{
return (Open());
}
AudioError AudioUnixfile::
decode_filehdr()
{
Boolean saveblock;
AudioHdr hdr_local;
Audio_hdr ohdr;
au_filehdr_t fhdr;
char *ibuf;
int file_type;
int infosize;
int cnt;
struct stat st;
AudioError err;
if (!isfdset() || opened())
return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
if (fstat(getfd(), &st) < 0)
return (RaiseError(AUDIO_UNIXERROR));
saveblock = GetBlocking();
if (!saveblock)
SetBlocking(TRUE);
cnt = read(getfd(), (char *)&fhdr, sizeof (fhdr));
if (cnt != sizeof (fhdr)) {
return (RaiseError(AUDIO_UNIXERROR));
}
err = (AudioError) audio_decode_filehdr(getfd(), (unsigned char *)&fhdr,
&file_type, &ohdr, &infosize);
if (err != AUDIO_SUCCESS)
return (RaiseError(err));
ibuf = new char[infosize];
cnt = read(getfd(), ibuf, infosize);
if (cnt != infosize) {
delete[] ibuf;
return (RaiseError(AUDIO_UNIXERROR));
}
SetBlocking(saveblock);
hdr_local = GetHeader();
hdr_local.sample_rate = ohdr.sample_rate;
hdr_local.samples_per_unit = ohdr.samples_per_unit;
hdr_local.bytes_per_unit = ohdr.bytes_per_unit;
hdr_local.channels = ohdr.channels;
hdr_local.encoding = (AudioEncoding) ohdr.encoding;
hdr_local.endian = BIG_ENDIAN;
err = SetHeader(hdr_local);
if (err != AUDIO_SUCCESS) {
delete[] ibuf;
return (RaiseError(err));
}
SetInfostring(ibuf, infosize);
delete[] ibuf;
if (S_ISREG(st.st_mode)) {
setlength(GetHeader().Bytes_to_Time(
st.st_size - infosize - sizeof (au_filehdr_t)));
if ((ohdr.data_size != AUDIO_UNKNOWN_SIZE) &&
(GetLength() != GetHeader().Bytes_to_Time(ohdr.data_size)))
PrintMsg(_MGET_(
"AudioUnixfile: header/file size mismatch"));
} else {
setlength(AUDIO_UNKNOWN_TIME);
}
filehdrset = TRUE;
return (AUDIO_SUCCESS);
}
AudioError AudioUnixfile::
encode_filehdr()
{
Boolean saveblock;
AudioHdr hdr_local;
Audio_hdr ohdr;
AudioError err;
if (!isfdset() || opened())
return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
hdr_local = GetHeader();
hdr_local.endian = BIG_ENDIAN;
err = SetHeader(hdr_local);
if (err != AUDIO_SUCCESS) {
return (RaiseError(err));
}
ohdr.sample_rate = hdr_local.sample_rate;
ohdr.samples_per_unit = hdr_local.samples_per_unit;
ohdr.bytes_per_unit = hdr_local.bytes_per_unit;
ohdr.channels = hdr_local.channels;
ohdr.encoding = hdr_local.encoding;
if (Undefined(GetLength()))
ohdr.data_size = AUDIO_UNKNOWN_SIZE;
else
ohdr.data_size = (uint_t)GetHeader().Time_to_Bytes(GetLength());
saveblock = GetBlocking();
if (!saveblock)
SetBlocking(TRUE);
err = (AudioError) audio_write_filehdr(getfd(), &ohdr, FILE_AU,
infostring, infolength);
if (err == AUDIO_SUCCESS)
filehdrset = TRUE;
SetBlocking(saveblock);
return (RaiseError(err));
}
void AudioUnixfile::
SetBlocking(
Boolean b)
{
int flag;
if (isfdset()) {
flag = fcntl(getfd(), F_GETFL, 0);
if ((flag < 0) && (errno == EOVERFLOW || errno == EINVAL)) {
RaiseError(AUDIO_UNIXERROR, Fatal,
(char *)"Large File");
} else if (b) {
flag &= ~(O_NDELAY | O_NONBLOCK);
} else {
flag |= O_NONBLOCK;
}
if (fcntl(getfd(), F_SETFL, flag) < 0) {
RaiseError(AUDIO_UNIXERROR, Warning);
}
}
block = b;
}
char *AudioUnixfile::
GetInfostring(
int& len) const
{
len = infolength;
return (infostring);
}
void AudioUnixfile::
SetInfostring(
const char *str,
int len)
{
if (len == -1)
len = strlen(str) + 1;
delete infostring;
infostring = new char[len];
infolength = len;
(void) memcpy(infostring, str, len);
}
AudioError AudioUnixfile::
Close()
{
if (isfdset()) {
if (close(getfd()) < 0)
return (RaiseError(AUDIO_UNIXERROR));
} else {
return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
}
setfd(-1);
filehdrset = FALSE;
(void) SetReadPosition((Double)0., Absolute);
(void) SetWritePosition((Double)0., Absolute);
return (AUDIO_SUCCESS);
}
AudioError AudioUnixfile::
ReadData(
void* buf,
size_t& len,
Double& pos)
{
off_t offset;
off_t cnt;
AudioError err;
cnt = (off_t)len;
len = 0;
if (!opened() || !mode.Readable())
return (RaiseError(AUDIO_ERR_NOEFFECT));
if (Undefined(pos) || (pos < 0.) || (cnt < 0))
return (RaiseError(AUDIO_ERR_BADARG));
err = seekread(pos, offset);
if (err != AUDIO_SUCCESS)
return (err);
if (pos >= GetLength()) {
err = AUDIO_EOF;
err.sys = AUDIO_COPY_INPUT_EOF;
return (err);
}
if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
err = AUDIO_SUCCESS;
err.sys = AUDIO_COPY_ZERO_LIMIT;
return (err);
}
cnt = read(fd, (char *)buf, (int)cnt);
if (cnt < 0) {
if (errno == EOVERFLOW) {
perror("read");
exit(1);
} else if ((errno == EINTR) ||
(((errno == EWOULDBLOCK) || (errno == EAGAIN)) &&
!GetBlocking())) {
err = AUDIO_SUCCESS;
err.sys = AUDIO_COPY_SHORT_INPUT;
return (err);
}
return (RaiseError(AUDIO_UNIXERROR));
}
if ((cnt == 0) && GetBlocking()) {
if (isDevice() || isPipe()) {
AUDIO_DEBUG((1,
"Zero-length blocking device/pipe read?!\n"));
}
err = AUDIO_EOF;
err.sys = AUDIO_COPY_INPUT_EOF;
return (err);
}
err = AUDIO_SUCCESS;
if (cnt == 0) {
err.sys = AUDIO_COPY_SHORT_INPUT;
}
len = (size_t)cnt;
if (GetHeader().Bytes_to_Bytes(cnt) != len) {
AUDIO_DEBUG((1,
"Read returned a partial sample frame?!\n"));
}
pos = GetHeader().Bytes_to_Time(offset + len);
coerceEndian((unsigned char *)buf, len, localByteOrder());
return (err);
}
AudioError AudioUnixfile::
WriteData(
void* buf,
size_t& len,
Double& pos)
{
off_t offset;
off_t cnt;
AudioError err;
cnt = (off_t)len;
len = 0;
if (!opened() || !mode.Writeable())
return (RaiseError(AUDIO_ERR_NOEFFECT));
if (Undefined(pos) || (pos < 0.) || (cnt < 0))
return (RaiseError(AUDIO_ERR_BADARG));
if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
err = AUDIO_SUCCESS;
err.sys = AUDIO_COPY_ZERO_LIMIT;
return (err);
}
err = seekwrite(pos, offset);
if (err != AUDIO_SUCCESS)
return (err);
if (localByteOrder() != GetHeader().endian)
coerceEndian((unsigned char *)buf, (size_t)cnt, SWITCH_ENDIAN);
err = AUDIO_SUCCESS;
cnt = write(fd, (char *)buf, (int)cnt);
if (cnt < 0) {
if (errno == EFBIG) {
perror("write");
exit(1);
} else if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
err.sys = AUDIO_COPY_SHORT_OUTPUT;
return (err);
}
return (RaiseError(AUDIO_UNIXERROR));
}
if (cnt == 0)
err.sys = AUDIO_COPY_SHORT_OUTPUT;
if (localByteOrder() != GetHeader().endian)
coerceEndian((unsigned char *)buf, (size_t)cnt, SWITCH_ENDIAN);
len = (size_t)cnt;
pos = GetHeader().Bytes_to_Time(offset + len);
if (!Undefined(GetLength()) && (pos > GetLength())) {
setlength(pos);
}
return (AUDIO_SUCCESS);
}
AudioError AudioUnixfile::
seekread(
Double pos,
off_t& offset)
{
char *bufp;
size_t bufl;
size_t cnt;
long icnt;
Boolean saveblock;
Double buflen;
AudioError err;
offset = GetHeader().Time_to_Bytes(pos);
pos -= ReadPosition();
if (pos < 0.)
return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
icnt = GetHeader().Time_to_Bytes(pos);
if (icnt == 0)
return (AUDIO_SUCCESS);
buflen = max(pos, 1.);
bufl = (size_t)GetHeader().Time_to_Bytes(buflen);
bufp = new char[bufl];
if (bufp == 0) {
bufl = (size_t)sysconf(_SC_PAGESIZE);
bufp = new char[bufl];
if (bufp == 0)
return (RaiseError(AUDIO_UNIXERROR));
}
saveblock = GetBlocking();
if (!saveblock)
SetBlocking(TRUE);
do {
cnt = (icnt >= (long)bufl) ? bufl : (size_t)icnt;
err = Read(bufp, cnt);
if (err != AUDIO_SUCCESS)
break;
icnt -= (long)cnt;
} while (icnt > 0);
SetBlocking(saveblock);
delete[] bufp;
return (RaiseError(err));
}
AudioError AudioUnixfile::
seekwrite(
Double pos,
off_t& offset)
{
char *bufp;
size_t bufl;
size_t cnt;
long ocnt;
Boolean saveblock;
Double buflen;
AudioError err;
offset = GetHeader().Time_to_Bytes(pos);
pos -= WritePosition();
if (pos < 0.)
return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
ocnt = GetHeader().Time_to_Bytes(pos);
if (ocnt == 0)
return (AUDIO_SUCCESS);
buflen = max(pos, 1.);
bufl = (size_t)GetHeader().Time_to_Bytes(buflen);
bufp = new char[bufl];
if (bufp == 0) {
bufl = (size_t)sysconf(_SC_PAGESIZE);
bufp = new char[bufl];
if (bufp == 0)
return (RaiseError(AUDIO_UNIXERROR));
}
saveblock = GetBlocking();
if (!saveblock)
SetBlocking(TRUE);
do {
cnt = (ocnt >= (long)bufl) ? bufl : (size_t)ocnt;
err = Write(bufp, cnt);
if (err != AUDIO_SUCCESS)
break;
ocnt -= (long)cnt;
} while (ocnt > 0);
SetBlocking(saveblock);
delete[] bufp;
return (RaiseError(err));
}