mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-04-11 15:22:46 +02:00
* Removed all CVS tags. * Removed 'const char *name' parameter from all QObject contructors.
1185 lines
26 KiB
C
1185 lines
26 KiB
C
/* wavlib.c
|
|
*
|
|
* A C Library for abstracting WAV files
|
|
*
|
|
* (C) Copyright 1997,1999-2003,2016 Fred Gleason <fredg@paravelsystems.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <sys/soundcard.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <strings.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <math.h>
|
|
#include "wavlib.h"
|
|
|
|
/* #define BUFFER_SIZE 65536 */
|
|
#define BUFFER_SIZE 8192
|
|
#define DEFAULT_WAV_MODE 0644
|
|
|
|
/*
|
|
* Global Variables
|
|
*/
|
|
int iWav=0;
|
|
int dWavPause=0;
|
|
int hGlobalAudio;
|
|
|
|
int IsWav(int hFilename)
|
|
{
|
|
int i;
|
|
char cBuffer[5];
|
|
|
|
/* Is this a riff file? */
|
|
lseek(hFilename,0,SEEK_SET);
|
|
i=read(hFilename,cBuffer,4);
|
|
if(i==4) {
|
|
cBuffer[4]=0;
|
|
if(strcmp("RIFF",cBuffer)!=0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
/* Is this a WAVE file? */
|
|
if(lseek(hFilename,8,SEEK_SET)!=8) {
|
|
return FALSE;
|
|
}
|
|
i=read(hFilename,cBuffer,4);
|
|
if(i==4) {
|
|
cBuffer[4]=0;
|
|
if(strcmp("WAVE",cBuffer)!=0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int OpenWav(char *sFilename,struct wavHeader *wavFilename)
|
|
{
|
|
int hFilename;
|
|
struct wavChunk wavChunk;
|
|
unsigned char cBuffer[16];
|
|
int i;
|
|
off_t oDataChunk;
|
|
|
|
hFilename=open(sFilename,O_RDWR);
|
|
if(hFilename<0) {
|
|
return -1;
|
|
}
|
|
if(!IsWav(hFilename)) {
|
|
return -2;
|
|
}
|
|
|
|
/*
|
|
* Find the format chunk
|
|
*/
|
|
strcpy(wavChunk.sName,"fmt ");
|
|
if(FindChunk(hFilename,&wavChunk)<0) {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Found the format chunk, now get the data
|
|
*/
|
|
lseek(hFilename,8,SEEK_CUR);
|
|
i=read(hFilename,cBuffer,14);
|
|
if(i<14) {
|
|
return -1;
|
|
}
|
|
wavFilename->wFormatTag=cBuffer[0]+256*cBuffer[1];
|
|
wavFilename->wChannels=cBuffer[2]+256*cBuffer[3];
|
|
wavFilename->dwSamplesPerSec=cBuffer[4]+256*cBuffer[5]+
|
|
65536*cBuffer[6]+16777216*cBuffer[7];
|
|
wavFilename->dwAvgBytesPerSec=cBuffer[8]+256*cBuffer[9]+
|
|
65536*cBuffer[10]+16777216*cBuffer[11];
|
|
wavFilename->wBlockAlign=cBuffer[12]+256*cBuffer[13];
|
|
if(wavFilename->wFormatTag==WAVE_FORMAT_PCM) { /*Get bits per sample*/
|
|
i=read(hFilename,cBuffer,2);
|
|
if(i<2) {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
wavFilename->wBitsPerSample=cBuffer[0]+256*cBuffer[1];
|
|
}
|
|
|
|
/*
|
|
* Find the data chunk
|
|
*/
|
|
strcpy(wavChunk.sName,"data");
|
|
if(FindChunk(hFilename,&wavChunk)<0) {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
wavFilename->dwWaveDataSize=wavChunk.oSize;
|
|
oDataChunk=wavChunk.oOffset; /* Save this for restoration later */
|
|
|
|
/*
|
|
* Determine total number of samples (preferably from the fact chunk)
|
|
*/
|
|
strcpy(wavChunk.sName,"fact");
|
|
lseek(hFilename,12,SEEK_SET);
|
|
if(FindChunk(hFilename,&wavChunk)<0) { /* Not present, calculate the size */
|
|
wavFilename->dwFileSize=wavFilename->dwWaveDataSize/
|
|
wavFilename->wBlockAlign;
|
|
}
|
|
else { /* Pull it from the fact chunk */
|
|
lseek(hFilename,8,SEEK_CUR);
|
|
i=read(hFilename,cBuffer,4);
|
|
if(i!=4) {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
wavFilename->dwFileSize=cBuffer[0]+256*cBuffer[1]+65536*cBuffer[2]+
|
|
16777216*cBuffer[3];
|
|
}
|
|
|
|
/*
|
|
* Calculate audio length in seconds
|
|
*/
|
|
wavFilename->tWavLength=wavFilename->dwFileSize/wavFilename->dwSamplesPerSec;
|
|
lseek(hFilename,oDataChunk+8,SEEK_SET);
|
|
return hFilename;
|
|
}
|
|
|
|
|
|
|
|
int CreateWav(char *sFilename,struct wavHeader *wavHeader)
|
|
{
|
|
int hFilename;
|
|
|
|
/*
|
|
* Some sanity checks
|
|
*/
|
|
if(wavHeader->wFormatTag!=WAVE_FORMAT_PCM) {
|
|
return -1;
|
|
}
|
|
/*
|
|
* Don't use O_TRUNC here, it can cause a significant (several second or
|
|
* more) delay on large files. We'll come back and trim the fat after the
|
|
* recording's done.
|
|
*/
|
|
hFilename=open(sFilename,O_RDWR|O_CREAT,DEFAULT_WAV_MODE);
|
|
if(hFilename<0) {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Write the WAVE Header
|
|
*/
|
|
write(hFilename,"RIFF",4);
|
|
WriteDword(hFilename,0);
|
|
write(hFilename,"WAVE",4);
|
|
|
|
/*
|
|
* Write the format chunk
|
|
*/
|
|
write(hFilename,"fmt ",4);
|
|
WriteDword(hFilename,16);
|
|
WriteSword(hFilename,wavHeader->wFormatTag);
|
|
WriteSword(hFilename,wavHeader->wChannels);
|
|
WriteDword(hFilename,wavHeader->dwSamplesPerSec);
|
|
WriteDword(hFilename,wavHeader->dwAvgBytesPerSec);
|
|
WriteSword(hFilename,wavHeader->wBlockAlign);
|
|
WriteSword(hFilename,wavHeader->wBitsPerSample);
|
|
|
|
/*
|
|
* Write the fact chunk
|
|
*/
|
|
write(hFilename,"fact",4);
|
|
WriteDword(hFilename,4);
|
|
WriteDword(hFilename,0);
|
|
|
|
/*
|
|
* Write the data chunk
|
|
*/
|
|
write(hFilename,"data",4);
|
|
WriteDword(hFilename,0);
|
|
|
|
return hFilename;
|
|
}
|
|
|
|
|
|
|
|
int FixWav(int hFilename,unsigned dSamples,unsigned dBytes)
|
|
{
|
|
struct stat statData;
|
|
struct wavChunk wavChunk;
|
|
|
|
/*
|
|
* Get the file size
|
|
*/
|
|
if(fstat(hFilename,&statData)<0) {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Update the file-size counter
|
|
*/
|
|
lseek(hFilename,4,SEEK_SET);
|
|
WriteDword(hFilename,statData.st_size-8);
|
|
|
|
/*
|
|
* Update the fact chunk
|
|
*/
|
|
strcpy(wavChunk.sName,"fact");
|
|
if(FindChunk(hFilename,&wavChunk)>0) {
|
|
lseek(hFilename,8,SEEK_CUR);
|
|
WriteDword(hFilename,dSamples);
|
|
}
|
|
|
|
/*
|
|
* Update the data chunk
|
|
*/
|
|
strcpy(wavChunk.sName,"data");
|
|
FindChunk(hFilename,&wavChunk);
|
|
lseek(hFilename,4,SEEK_CUR);
|
|
WriteDword(hFilename,dBytes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int FindChunk(int hFilename,struct wavChunk *wavChunk)
|
|
{
|
|
int i;
|
|
char sName[5]={0,0,0,0,0};
|
|
unsigned char dBuffer[4];
|
|
off_t oSize;
|
|
|
|
lseek(hFilename,12,SEEK_SET);
|
|
i=read(hFilename,sName,4);
|
|
i=read(hFilename,dBuffer,4);
|
|
oSize=dBuffer[0]+(256*dBuffer[1])+(65536*dBuffer[2])+(16777216*dBuffer[3]);
|
|
while(i==4) {
|
|
if(strcasecmp(wavChunk->sName,sName)==0) {
|
|
wavChunk->oOffset=lseek(hFilename,-8,SEEK_CUR);
|
|
wavChunk->oSize=oSize;
|
|
return 0;
|
|
}
|
|
lseek(hFilename,oSize,SEEK_CUR);
|
|
i=read(hFilename,sName,4);
|
|
i=read(hFilename,dBuffer,4);
|
|
oSize=dBuffer[0]+(256*dBuffer[1])+(65536*dBuffer[2])+(16777216*dBuffer[3]);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
int GetNextChunk(int hFilename,struct wavChunk *wavChunk)
|
|
{
|
|
int i;
|
|
char sName[5]={0,0,0,0,0};
|
|
unsigned char dBuffer[4];
|
|
off_t oSize;
|
|
|
|
i=read(hFilename,sName,4);
|
|
i=read(hFilename,dBuffer,4);
|
|
oSize=dBuffer[0]+(dBuffer[1]*256)+(dBuffer[2]*65536)+(dBuffer[3]*16777216);
|
|
if(i==4) {
|
|
strcpy(wavChunk->sName,sName);
|
|
wavChunk->oOffset=lseek(hFilename,0,SEEK_CUR);
|
|
wavChunk->oSize=oSize;
|
|
lseek(hFilename,oSize,SEEK_CUR);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
int GetListChunk(int hFilename,struct wavList *wavList)
|
|
{
|
|
struct wavChunk wavChunk;
|
|
int i;
|
|
char cBuffer[LIST_SIZE];
|
|
off_t oLimit,oOffset;
|
|
char sName[5]={0,0,0,0,0};
|
|
unsigned dBuffer[4];
|
|
char sBuffer[LIST_SIZE];
|
|
off_t oTemp,oSize;
|
|
int dReturn=1;
|
|
|
|
/*
|
|
* Initialize Data Structures
|
|
*/
|
|
memset(wavList,0,sizeof(struct wavList));
|
|
|
|
/*
|
|
* Find the list chunk
|
|
*/
|
|
strcpy(wavChunk.sName,"LIST");
|
|
if(FindChunk(hFilename,&wavChunk)<0) {
|
|
return -1;
|
|
}
|
|
oLimit=wavChunk.oOffset+wavChunk.oSize;
|
|
|
|
/*
|
|
* Setup to start & do some basic sanity checking
|
|
*/
|
|
oOffset=lseek(hFilename,8,SEEK_CUR);
|
|
i=read(hFilename,cBuffer,4);
|
|
oOffset+=i;
|
|
cBuffer[4]=0;
|
|
if(strcasecmp(cBuffer,"INFO")!=0) { /* I don't understand this format! */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Scan through the chunk
|
|
*/
|
|
while(oOffset<oLimit) {
|
|
oOffset+=read(hFilename,sName,4);
|
|
oOffset+=read(hFilename,dBuffer,4);
|
|
oSize=dBuffer[0]+(dBuffer[1]<<8)+(dBuffer[2]<<16)+(dBuffer[3]<<24);
|
|
oTemp=read(hFilename,sBuffer,dBuffer[0]);
|
|
lseek(hFilename,oSize-oTemp,SEEK_CUR);
|
|
read(hFilename,dBuffer,1);
|
|
if(dBuffer[0]!=0) {
|
|
lseek(hFilename,-1,SEEK_CUR);
|
|
}
|
|
|
|
/*
|
|
* Assign the field value
|
|
*/
|
|
if(strcasecmp(sName,"ICRD")==0) {
|
|
strcpy(wavList->sIcrd,sBuffer);
|
|
dReturn=0;
|
|
}
|
|
if(strcasecmp(sName,"IART")==0) {
|
|
strcpy(wavList->sIart,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"ICMT")==0) {
|
|
strcpy(wavList->sIcmt,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"ICOP")==0) {
|
|
strcpy(wavList->sIcop,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"IENG")==0) {
|
|
strcpy(wavList->sIeng,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"IGNR")==0) {
|
|
strcpy(wavList->sIgnr,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"IKEY")==0) {
|
|
strcpy(wavList->sIkey,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"IMED")==0) {
|
|
strcpy(wavList->sImed,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"INAM")==0) {
|
|
strcpy(wavList->sInam,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"ISFT")==0) {
|
|
strcpy(wavList->sIsft,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"ISRC")==0) {
|
|
strcpy(wavList->sIsrc,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"ITCH")==0) {
|
|
strcpy(wavList->sItch,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"ISBJ")==0) {
|
|
strcpy(wavList->sIsbj,sBuffer);
|
|
}
|
|
if(strcasecmp(sName,"ISRF")==0) {
|
|
strcpy(wavList->sIsrf,sBuffer);
|
|
}
|
|
}
|
|
return dReturn;
|
|
}
|
|
|
|
|
|
|
|
int SetDspDesc(int hAudio,struct wavHeader *wavHeader)
|
|
{
|
|
int dRequest;
|
|
|
|
/*
|
|
* Set sampling format & bits per sample
|
|
*/
|
|
if(wavHeader->wFormatTag==WAVE_FORMAT_PCM) {
|
|
switch(wavHeader->wBitsPerSample) {
|
|
case 8:
|
|
dRequest=AFMT_U8;
|
|
ioctl(hAudio,SNDCTL_DSP_SETFMT,&dRequest);
|
|
if(dRequest!=AFMT_U8) {
|
|
return -1;
|
|
}
|
|
break;
|
|
case 16:
|
|
dRequest=AFMT_S16_LE;
|
|
ioctl(hAudio,SNDCTL_DSP_SETFMT,&dRequest);
|
|
if(dRequest!=AFMT_S16_LE) {
|
|
return -1;
|
|
}
|
|
break;
|
|
default:
|
|
return -1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
/* Set number of channels */
|
|
if(wavHeader->wChannels>0 && wavHeader->wChannels<3) {
|
|
dRequest=wavHeader->wChannels-1;
|
|
ioctl(hAudio,SNDCTL_DSP_STEREO,&dRequest);
|
|
if(dRequest!=wavHeader->wChannels-1) {
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Set sampling rate
|
|
*/
|
|
dRequest=(int)wavHeader->dwSamplesPerSec;
|
|
ioctl(hAudio,SNDCTL_DSP_SPEED,&dRequest);
|
|
if((dRequest<(int)(.99*wavHeader->dwSamplesPerSec)) ||
|
|
(dRequest>(int)(1.01*wavHeader->dwSamplesPerSec))) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
int PlayWavFile(char *sFilename,char *sAudioDev,unsigned dOptions)
|
|
{
|
|
int hAudio;
|
|
int i;
|
|
|
|
if((hAudio=open(sAudioDev,O_WRONLY))<0) {
|
|
return -1;
|
|
}
|
|
i=PlayWavDesc(sFilename,hAudio,dOptions);
|
|
close(hAudio);
|
|
if(i<0) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
int PlayWavDesc(char *sFilename,int hAudio,unsigned dOptions)
|
|
{
|
|
return PlayWavOffsetDesc(sFilename,hAudio,0,dOptions);
|
|
}
|
|
|
|
|
|
|
|
|
|
int PlayWavOffsetDesc(char *sFilename,int hAudio,int dOffset,unsigned dOptions)
|
|
{
|
|
struct wavHeader wavFilename;
|
|
struct wavChunk wavChunk;
|
|
int hFilename;
|
|
int j,k;
|
|
unsigned char cBuffer[BUFFER_SIZE];
|
|
unsigned dWaveOffset=0;
|
|
|
|
/*
|
|
* Pass the device fd to a global so the signal handlers can get it
|
|
*/
|
|
hGlobalAudio=hAudio;
|
|
|
|
/*
|
|
* Signal Handling Stuff
|
|
*/
|
|
if((dOptions&WAVLIB_PAUSEABLE)!=0) {
|
|
(void) signal(SIGTERM,SigStopWav);
|
|
(void) signal(SIGUSR1,SigPauseWav);
|
|
(void) signal(SIGUSR2,SigResumeWav);
|
|
}
|
|
|
|
hFilename=OpenWav(sFilename,&wavFilename);
|
|
if(hFilename<0) {
|
|
return -1;
|
|
}
|
|
|
|
if(SetDspDesc(hAudio,&wavFilename)<0) {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Calculate Wave Data Offset
|
|
*/
|
|
dWaveOffset=dOffset*wavFilename.wBlockAlign;
|
|
|
|
/*
|
|
* OK -- time to play the file!
|
|
*/
|
|
strcpy(wavChunk.sName,"data");
|
|
if(FindChunk(hFilename,&wavChunk)<0) {
|
|
return -1;
|
|
}
|
|
lseek(hFilename,8+dWaveOffset,SEEK_CUR);
|
|
if((dOptions & WAVLIB_TEST)==0) {
|
|
j=(wavFilename.dwWaveDataSize-dWaveOffset)/BUFFER_SIZE;
|
|
k=(wavFilename.dwWaveDataSize-dWaveOffset)-j*BUFFER_SIZE;
|
|
for(iWav=0;iWav<j;iWav++) {
|
|
read(hFilename,cBuffer,BUFFER_SIZE);
|
|
if(dWavPause==1) {
|
|
select(0,NULL,NULL,NULL,NULL); /* Sleep till we get a signal */
|
|
}
|
|
write(hAudio,cBuffer,BUFFER_SIZE);
|
|
}
|
|
memset(cBuffer,0,BUFFER_SIZE);
|
|
read(hFilename,cBuffer,k);
|
|
if(dWavPause==1) {
|
|
select(0,NULL,NULL,NULL,NULL); /* Sleep till we get a signal */
|
|
}
|
|
write(hAudio,cBuffer,k);
|
|
}
|
|
/* Finish up */
|
|
close(hFilename);
|
|
return 0;
|
|
}
|
|
|
|
void SigStopWav(int sig)
|
|
{
|
|
ioctl(hGlobalAudio,SNDCTL_DSP_RESET,NULL);
|
|
exit(0);
|
|
}
|
|
|
|
void SigPauseWav(int sig)
|
|
{
|
|
dWavPause=1;
|
|
ioctl(hGlobalAudio,SNDCTL_DSP_RESET,NULL);
|
|
(void) signal(SIGUSR1,SigPauseWav);
|
|
}
|
|
|
|
void SigResumeWav(int sig)
|
|
{
|
|
dWavPause=0;
|
|
(void) signal(SIGUSR2,SigResumeWav);
|
|
}
|
|
|
|
|
|
int RecWavFile(char *sFilename,char *sAudioDev,unsigned dSeconds,
|
|
unsigned short dChannels,unsigned ldSamplesPerSec,
|
|
unsigned short dBitsPerSample,unsigned dOptions)
|
|
{
|
|
int hAudio;
|
|
int i;
|
|
|
|
hAudio=open(sAudioDev,O_RDONLY);
|
|
if(hAudio<0) {
|
|
return -1;
|
|
}
|
|
i=RecWavDesc(sFilename,hAudio,dSeconds,dChannels,ldSamplesPerSec,
|
|
dBitsPerSample,dOptions);
|
|
close(hAudio);
|
|
if(i<0) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int RecWavDesc(char *sFilename,int hAudio,unsigned dSeconds,
|
|
unsigned short dChannels,unsigned ldSamplesPerSec,
|
|
unsigned short dBitsPerSample,unsigned dOptions)
|
|
{
|
|
int hFilename=0;
|
|
int dRequest;
|
|
char cBuffer[BUFFER_SIZE];
|
|
unsigned dBytesPerSec,dBytesPerSample=0;
|
|
unsigned dBufferWrites;
|
|
unsigned dTotalSampleBytes;
|
|
unsigned dTotalWavSize;
|
|
unsigned i;
|
|
unsigned short dFormatTag=WAVE_FORMAT_PCM;
|
|
unsigned short dBlockAlign;
|
|
|
|
/* Check for format encoding type */
|
|
if(dFormatTag!=WAVE_FORMAT_PCM) {
|
|
return -1;
|
|
}
|
|
/* Set sampling format & bits per sample */
|
|
if(dFormatTag==WAVE_FORMAT_PCM) {
|
|
switch(dBitsPerSample) {
|
|
case 8:
|
|
dRequest=AFMT_U8;
|
|
ioctl(hAudio,SNDCTL_DSP_SETFMT,&dRequest);
|
|
if(dRequest!=AFMT_U8) {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
break;
|
|
case 16:
|
|
dRequest=AFMT_S16_LE;
|
|
ioctl(hAudio,SNDCTL_DSP_SETFMT,&dRequest);
|
|
if(dRequest!=AFMT_S16_LE) {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
break;
|
|
default:
|
|
close(hFilename);
|
|
return -1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
/* Set number of channels */
|
|
if(dChannels>0 && dChannels<3) {
|
|
dRequest=dChannels-1;
|
|
ioctl(hAudio,SNDCTL_DSP_STEREO,&dRequest);
|
|
if(dRequest!=dChannels-1) {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
close(hFilename);
|
|
return -1;
|
|
}
|
|
|
|
/* Set sampling rate */
|
|
dRequest=(int)ldSamplesPerSec;
|
|
ioctl(hAudio,SNDCTL_DSP_SPEED,&dRequest);
|
|
/* if(dRequest<(int).99*ldSamplesPerSec ||
|
|
dRequest>(int)1.01*ldSamplesPerSec) {
|
|
close(hFilename);
|
|
return -1;
|
|
} */
|
|
|
|
/* OK, we know the soundcard can handle it */
|
|
/* If this is just a test, exit */
|
|
if((dOptions & WAVLIB_TEST)!=0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Calculate actual bytes/sec overall */
|
|
if(dBitsPerSample<=8) {
|
|
dBytesPerSample=1;
|
|
}
|
|
if(dBitsPerSample>8 && dBitsPerSample<=16) {
|
|
dBytesPerSample=2;
|
|
}
|
|
if(dBitsPerSample>16) {
|
|
dBytesPerSample=3;
|
|
}
|
|
dBlockAlign=dBytesPerSample*dChannels;
|
|
dBytesPerSec=ldSamplesPerSec*dBytesPerSample*dChannels;
|
|
/* Calculate buffer writes and total sample bytes needed */
|
|
dTotalSampleBytes=dBytesPerSec*dSeconds;
|
|
dBufferWrites=dTotalSampleBytes/BUFFER_SIZE+1;
|
|
dTotalSampleBytes=dBufferWrites*BUFFER_SIZE;
|
|
dTotalWavSize=dTotalSampleBytes+36;
|
|
|
|
/*
|
|
* Open the file and write the header
|
|
*
|
|
* First, open the file, using the appropriate permissions
|
|
*
|
|
* Don't use O_TRUNC here, it can cause a significant (several second or
|
|
* more) delay on large files. We'll come back and trim the fat after the
|
|
* recording's done.
|
|
*/
|
|
hFilename=open(sFilename,O_WRONLY|O_CREAT,DEFAULT_WAV_MODE);
|
|
if(hFilename<0) {
|
|
perror("open");
|
|
return -1;
|
|
}
|
|
|
|
/* The 'RIFF' field */
|
|
strcpy(cBuffer,"RIFF");
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* The size pointer */
|
|
/* wav files are little endian! */
|
|
cBuffer[0]=dTotalWavSize & 0x000000FF;
|
|
cBuffer[1]=(dTotalWavSize & 0x0000FF00) >> 8;
|
|
cBuffer[2]=(dTotalWavSize & 0x00FF0000) >> 16;
|
|
cBuffer[3]=(dTotalWavSize & 0xFF000000) >> 24;
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* The WAVE field */
|
|
strcpy(cBuffer,"WAVE");
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* Now the format chunk */
|
|
/* First the chunk header */
|
|
strcpy(cBuffer,"fmt ");
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* The chunk size field */
|
|
cBuffer[0]=0x10;
|
|
cBuffer[1]=0;
|
|
cBuffer[2]=0;
|
|
cBuffer[3]=0;
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* The Format Category */
|
|
cBuffer[0]=dFormatTag & 0x00FF;
|
|
cBuffer[1]=(dFormatTag & 0xFF00) >> 8;
|
|
write(hFilename,cBuffer,2);
|
|
|
|
/* Number of Channels */
|
|
cBuffer[0]=dChannels & 0x00FF;
|
|
cBuffer[1]=(dChannels & 0xFF00) >> 8;
|
|
write(hFilename,cBuffer,2);
|
|
|
|
/* Samples/second/channel */
|
|
cBuffer[0]=ldSamplesPerSec & 0x000000FF;
|
|
cBuffer[1]=(ldSamplesPerSec & 0x0000FF00) >> 8;
|
|
cBuffer[2]=(ldSamplesPerSec & 0x00FF0000) >> 16;
|
|
cBuffer[3]=(ldSamplesPerSec & 0xFF000000) >> 24;
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* Average bytes per second */
|
|
cBuffer[0]=dBytesPerSec & 0x000000FF;
|
|
cBuffer[1]=(dBytesPerSec & 0x0000FF00) >> 8;
|
|
cBuffer[2]=(dBytesPerSec & 0x00FF0000) >> 16;
|
|
cBuffer[3]=(dBytesPerSec & 0xFF000000) >> 24;
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* Data Block Alignment */
|
|
cBuffer[0]=dBlockAlign & 0x00FF;
|
|
cBuffer[1]=(dBlockAlign & 0xFF00) >> 8;
|
|
write(hFilename,cBuffer,2);
|
|
|
|
/* Bits per Sample */
|
|
/* NOTE: This field pertains only to WAVE_FORMAT_PCM format encoding */
|
|
cBuffer[0]=dBitsPerSample & 0x00FF;
|
|
cBuffer[1]=(dBitsPerSample & 0xFF00) >> 8;
|
|
write(hFilename,cBuffer,2);
|
|
|
|
/* Now, the data chunk */
|
|
/* The 'data' field */
|
|
strcpy(cBuffer,"data");
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* The data size field */
|
|
cBuffer[0]=dTotalSampleBytes & 0x000000FF;
|
|
cBuffer[1]=(dTotalSampleBytes & 0x0000FF00) >> 8;
|
|
cBuffer[2]=(dTotalSampleBytes & 0x00FF0000) >> 16;
|
|
cBuffer[3]=(dTotalSampleBytes & 0xFF000000) >> 24;
|
|
write(hFilename,cBuffer,4);
|
|
|
|
/* Move the audio data */
|
|
for(i=0;i<dBufferWrites;i++) {
|
|
read(hAudio,cBuffer,BUFFER_SIZE);
|
|
write(hFilename,cBuffer,BUFFER_SIZE);
|
|
}
|
|
|
|
/* Finish up */
|
|
ftruncate(hFilename,lseek(hFilename,0,SEEK_CUR));
|
|
close(hFilename);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int RecordWavFile(char *sFilename,char *sAudio,int dSeconds,
|
|
struct wavHeader *wavHeader,struct wavProcess *wavProcess,
|
|
unsigned dOptions)
|
|
{
|
|
int hAudio;
|
|
|
|
hAudio=open(sAudio,O_RDWR);
|
|
if(hAudio<0) {
|
|
return -1;
|
|
}
|
|
return RecordWavDesc(sFilename,hAudio,dSeconds,
|
|
wavHeader,wavProcess,dOptions);
|
|
}
|
|
|
|
|
|
|
|
int RecordWavDesc(char *sFilename,int hAudio,int dSeconds,
|
|
struct wavHeader *wavHeader,struct wavProcess *wavProcess,
|
|
unsigned dOptions)
|
|
{
|
|
int i;
|
|
int hFilename;
|
|
int dBufferWrites,dExcessWrites;
|
|
int dSense=0,dStartWrite=BUFFER_SIZE,dPosThreshold,dNegThreshold;
|
|
unsigned char cBuffer[BUFFER_SIZE];
|
|
short *dBuffer;
|
|
int dBufferCount=0;
|
|
|
|
/*
|
|
* Do we support this format?
|
|
*/
|
|
if(wavHeader->wFormatTag!=WAVE_FORMAT_PCM) {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Flesh out the wavHeader Structure
|
|
*/
|
|
wavHeader->dwAvgBytesPerSec=wavHeader->wChannels*wavHeader->dwSamplesPerSec*
|
|
(wavHeader->wBitsPerSample/8);
|
|
wavHeader->wBlockAlign=wavHeader->wChannels*(wavHeader->wBitsPerSample/8);
|
|
wavHeader->dwWaveDataSize=wavHeader->dwAvgBytesPerSec*dSeconds;
|
|
wavHeader->dwFileSize=wavHeader->dwWaveDataSize/wavHeader->wBlockAlign;
|
|
wavHeader->tWavLength=dSeconds;
|
|
|
|
if(SetDspDesc(hAudio,wavHeader)<0) { /* Format not supported by hardware */
|
|
return -1;
|
|
}
|
|
if((dOptions&WAVLIB_TEST)!=0) { /* Just a test, exit */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Look for silence sense option
|
|
*/
|
|
if(wavProcess!=NULL) {
|
|
if(wavProcess->dSenseTimeout!=0) {
|
|
if(wavHeader->wBitsPerSample==8) {
|
|
dPosThreshold=(int)(128+(128*DbToLinear(-wavProcess->dfThreshold)));
|
|
dNegThreshold=(int)(128-(128*DbToLinear(-wavProcess->dfThreshold)));
|
|
dBufferCount=wavHeader->dwAvgBytesPerSec*wavProcess->dSenseTimeout/
|
|
BUFFER_SIZE;
|
|
while((dSense==0)&&((dBufferCount--)>0)) {
|
|
read(hAudio,cBuffer,BUFFER_SIZE);
|
|
for(i=0;i<BUFFER_SIZE;i++) {
|
|
if((cBuffer[i]>dPosThreshold)||(cBuffer[i]<dNegThreshold)) {
|
|
dSense=1;
|
|
dStartWrite=i;
|
|
i=BUFFER_SIZE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(wavHeader->wBitsPerSample==16) {
|
|
dBuffer=(short *)cBuffer;
|
|
dPosThreshold=(int)(32768*DbToLinear(-wavProcess->dfThreshold));
|
|
dNegThreshold=(int)(-32768*DbToLinear(-wavProcess->dfThreshold));
|
|
dBufferCount=wavHeader->dwAvgBytesPerSec*wavProcess->dSenseTimeout/
|
|
BUFFER_SIZE;
|
|
while((dSense==0)&&((dBufferCount--)>0)) {
|
|
read(hAudio,cBuffer,BUFFER_SIZE);
|
|
for(i=0;i<BUFFER_SIZE/2;i++) {
|
|
if((dBuffer[i]>dPosThreshold)||(dBuffer[i]<dNegThreshold)) {
|
|
dSense=1;
|
|
dStartWrite=i*2;
|
|
i=BUFFER_SIZE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(dBufferCount<=0) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Write the wav file
|
|
*/
|
|
hFilename=CreateWav(sFilename,wavHeader);
|
|
if(hFilename<0) {
|
|
return -1;
|
|
}
|
|
dBufferWrites=(wavHeader->dwWaveDataSize-(BUFFER_SIZE-dStartWrite))/
|
|
BUFFER_SIZE;
|
|
dExcessWrites=(wavHeader->dwWaveDataSize-(BUFFER_SIZE-dStartWrite))-
|
|
(dBufferWrites*BUFFER_SIZE);
|
|
if(dSense==1) {
|
|
write(hFilename,cBuffer+dStartWrite,BUFFER_SIZE-dStartWrite);
|
|
}
|
|
for(i=0;i<dBufferWrites;i++) {
|
|
read(hAudio,cBuffer,BUFFER_SIZE);
|
|
write(hFilename,cBuffer,BUFFER_SIZE);
|
|
}
|
|
read(hAudio,cBuffer,dExcessWrites);
|
|
write(hFilename,cBuffer,dExcessWrites);
|
|
|
|
/*
|
|
* Finish Up
|
|
*/
|
|
ftruncate(hFilename,lseek(hFilename,0,SEEK_CUR));
|
|
FixWav(hFilename,wavHeader->dwFileSize,wavHeader->dwWaveDataSize);
|
|
close(hFilename);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int SoundConfigFile(char *sAudioDev,int dParam,int dRequest)
|
|
|
|
/* This function performs an ioctl call on the audio device
|
|
named by sAudioDev, using the ioctl dParam and the argument
|
|
dRequest. The ioctls are those defined in the OSS API.
|
|
If successful, the function returns the updated value of
|
|
dRequest, otherwise, it returns a -1.
|
|
|
|
It is anticipated that this function will be useful mainly
|
|
as a way for manipulating the soundcard mixer settings */
|
|
|
|
{
|
|
int dAudioDev;
|
|
int i;
|
|
|
|
dAudioDev=open(sAudioDev,O_RDONLY);
|
|
if(dAudioDev<0) {
|
|
return -1;
|
|
}
|
|
i=SoundConfigDesc(dAudioDev,dParam,dRequest);
|
|
close(dAudioDev);
|
|
if(i<0) {
|
|
return -1;
|
|
}
|
|
else {
|
|
return dRequest;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
double DbToLinear(double dfDb)
|
|
{
|
|
return pow(10,dfDb/20);
|
|
}
|
|
|
|
|
|
|
|
double LinearToDb(double dfLinear)
|
|
{
|
|
if(dfLinear==0) {
|
|
return -1;
|
|
}
|
|
return 20*log10(dfLinear);
|
|
}
|
|
|
|
|
|
|
|
int SoundConfigDesc(int dAudioDev,int dParam,int dRequest)
|
|
|
|
/* This function performs an ioctl call on the audio device
|
|
open on hAudioDev, using the ioctl dParam and the argument
|
|
dRequest. The ioctls are those defined in the OSS API.
|
|
If successful, the function returns the updated value of
|
|
dRequest, otherwise, it returns a -1.
|
|
|
|
It is anticipated that this function will be useful mainly
|
|
as a way for manipulating the soundcard mixer settings */
|
|
|
|
{
|
|
int i;
|
|
|
|
i=ioctl(dAudioDev,dParam,&dRequest);
|
|
if(i<0) {
|
|
return -1;
|
|
}
|
|
else {
|
|
return dRequest;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int Pattern(int dFilename,char *sPattern)
|
|
|
|
/* This function finds the looks for the string sPattern
|
|
in the file pointed to by dFilename, starting at the current
|
|
pointer position. If found, it returns the pointer offset and
|
|
with the pointer set to the byte immediately following the pattern.
|
|
If the pattern is not found, it returns -1. */
|
|
|
|
{
|
|
int i,j;
|
|
int dOffset=0; /* file pointer offset */
|
|
unsigned char cBuffer;
|
|
int dPatLength;
|
|
|
|
dPatLength=strlen(sPattern);
|
|
i=read(dFilename,&cBuffer,1);
|
|
if(i<0) {
|
|
perror("read");
|
|
return -1;
|
|
}
|
|
dOffset++;
|
|
while(i==1) {
|
|
j=0;
|
|
while(cBuffer==sPattern[j++] && j<=dPatLength) {
|
|
if(j==dPatLength) { /* it's a match! */
|
|
return dOffset;
|
|
}
|
|
i=read(dFilename,&cBuffer,1);
|
|
dOffset++;
|
|
}
|
|
lseek(dFilename,-(j-1),SEEK_CUR);
|
|
dOffset-=(j-1);
|
|
i=read(dFilename,&cBuffer,1);
|
|
dOffset++;
|
|
}
|
|
lseek(dFilename,-dOffset,SEEK_CUR);
|
|
printf("Offset: %d\n",dOffset);
|
|
return (-1);
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t WriteSword(int hFilename,unsigned dValue)
|
|
{
|
|
unsigned char cBuffer[2];
|
|
|
|
cBuffer[0]=dValue&0xFF;
|
|
cBuffer[1]=(dValue>>8)&0xFF;
|
|
|
|
return write(hFilename,cBuffer,2);
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t WriteDword(int hFilename,unsigned dValue)
|
|
{
|
|
unsigned char cBuffer[4];
|
|
|
|
cBuffer[0]=dValue&0xFF;
|
|
cBuffer[1]=(dValue>>8)&0xFF;
|
|
cBuffer[2]=(dValue>>16)&0xFF;
|
|
cBuffer[3]=(dValue>>24)&0xFF;
|
|
|
|
return write(hFilename,cBuffer,4);
|
|
}
|
|
|
|
|
|
|
|
|
|
int TailTrim(char *sFilename,int dThreshold)
|
|
{
|
|
int i,j;
|
|
int hFilename;
|
|
struct wavHeader wavHeader;
|
|
struct wavChunk wavChunk;
|
|
long dEnd;
|
|
unsigned char cBuffer[BUFFER_SIZE];
|
|
short *dBuffer;
|
|
|
|
/*
|
|
* Initialize
|
|
*/
|
|
dBuffer=(short *)cBuffer;
|
|
|
|
/*
|
|
* Open the file
|
|
*/
|
|
hFilename=OpenWav(sFilename,&wavHeader);
|
|
if(hFilename<0) {
|
|
return hFilename;
|
|
}
|
|
|
|
/*
|
|
* Find the end of the audio data
|
|
*/
|
|
memset(&wavChunk,0,sizeof(struct wavChunk));
|
|
strcpy(wavChunk.sName,"data");
|
|
if(FindChunk(hFilename,&wavChunk)<0) {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
lseek(hFilename,wavChunk.oOffset+wavChunk.oSize-(BUFFER_SIZE*500),SEEK_SET);
|
|
read(hFilename,cBuffer,BUFFER_SIZE);
|
|
for(j=BUFFER_SIZE/2;j>0;j--) {
|
|
printf("VALUE: %d\n",dBuffer[j]);
|
|
}
|
|
exit(0);
|
|
*/
|
|
|
|
/*
|
|
* Scan backwards through the data looking for audio
|
|
*/
|
|
lseek(hFilename,wavChunk.oOffset+wavChunk.oSize+6,SEEK_SET);
|
|
for(i=0;i<wavChunk.oSize/BUFFER_SIZE;i++) {
|
|
lseek(hFilename,-BUFFER_SIZE,SEEK_CUR);
|
|
read(hFilename,cBuffer,BUFFER_SIZE);
|
|
lseek(hFilename,-BUFFER_SIZE,SEEK_CUR);
|
|
for(j=BUFFER_SIZE/2-100;j>=0;j--) {
|
|
/* printf("Threshold: %d Value: %d\n",dThreshold,dBuffer[j]); */
|
|
if(abs(dBuffer[j])>=dThreshold) {
|
|
TruncWav(hFilename,&wavHeader,&wavChunk,lseek(hFilename,0,SEEK_CUR)+j*2);
|
|
close(hFilename);
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
dEnd=wavChunk.oSize-BUFFER_SIZE*(wavChunk.oSize/BUFFER_SIZE);
|
|
lseek(hFilename,-dEnd,SEEK_CUR);
|
|
read(hFilename,cBuffer,dEnd);
|
|
lseek(hFilename,-dEnd,SEEK_CUR);
|
|
for(j=dEnd-1;j>=0;j--) {
|
|
if(abs(dBuffer[j])>=dThreshold) {
|
|
TruncWav(hFilename,&wavHeader,&wavChunk,lseek(hFilename,0,SEEK_CUR)+j);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Finish up
|
|
*/
|
|
close(hFilename);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int TruncWav(int hFilename,struct wavHeader *wavHeader,
|
|
struct wavChunk *wavChunk,unsigned dTruncPoint)
|
|
{
|
|
unsigned dBytes;
|
|
|
|
if(ftruncate(hFilename,dTruncPoint)<0) {
|
|
return -1;
|
|
}
|
|
dBytes=dTruncPoint-wavChunk->oOffset;
|
|
if(FixWav(hFilename,dBytes/(wavHeader->wBitsPerSample/8),dBytes)<0) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|