Source file: /~heha/hsn/zqr.zip/src/save.cpp

#include "zqr.h"
// include file to MP3 encoder API
#include "lame/BladeMP3EncDLL.h"
// The necessary lame_enc.dll should be placed to C:\Windows\System32\ (64 bit)
// and to C:\Windows\SysWOW64\ (32 bit)
// Note that 32-bit file managers will be faked, so use 64-bit file managers
// to place the DLLs to that places.
// Alternatively, this program has a user-defineable path to the lame_enc.dll,
// however, as this conflicts when toggling between 32-bit and 64-bit zqr.exe,
// this is not recommended.
#include <vorbis/vorbisenc.h>
#include <stdio.h>	// fopen (leider ANSI)
#undef WCHAR_MAX	// ein paar Warnings (bei make) weniger
#undef WCHAR_MIN
#include <tchar.h>	// _tcschr
#include <shlwapi.h>	// PathRemoveFileSpec
#include <time.h>	// time(), localtime()

SAVE*save,*saveobj[3];	// aktuelles Speicher-Objekt und 3 vorbereitete Speicher-Objekte

TCHAR currentfile[MAX_PATH];	// Aufgelöster Dateiname der aktuellen Aufnahme
FILETIME starttime;		// Aufnahme-Startzeitpunkt
bool newfile;	// true wenn <currentfile> neu ermittelt wurde, für Notiz-Datei

// <n> wird in der Länge MAX_PATH angenommen.
// TODO: Prüfungen auf Pufferüberläufe sind hier nicht ganz konsequent umgesetzt
// Liefert lstrlen(n)
// TODO: Wenn während der verschiedenen Formatierungen die Zeitangabe springt, kommt Murks heraus!
// Na gut, das ist wirklich unwahrscheinlich und erst mal von untergeordneter Wichtigkeit.
// Dazu müsste man FILETIME<=>time_t konvertieren. Auch wieder unschön.
int sprintDateTime(TCHAR*n,int len,const TCHAR*fmt) {
 TCHAR*start=n;
 TCHAR*end=n+len;
 while (TCHAR c=*fmt++) {
  if (c=='%') {
   const TCHAR*userfmt=0;
   TCHAR cf=*fmt;
// Vorausschau: Nächstes Zeichen 'D' oder 'T' oder '<' für Formatstring?
   if (cf=='<') {	// Idee mit den spitzen Klammern von LabVIEW abgeguckt.
	// Leider mit dem Nachteil, dass man damit kein GetSaveFileName() füttern kann,
    const TCHAR*ef=_tcschr(fmt,'>');
    if (!ef) goto noformat;	// Kein passendes '>' (Schachtelungen oder Escapes sind nicht vorgesehen)
    TCHAR buf[32];
    if (ef-fmt>elemof(buf)) goto noformat;	// Zu lang
    lstrcpyn(buf,fmt+1,int(ef-fmt));
    userfmt=buf;
    fmt=ef+1;
    cf=*fmt;	// Konversionszeichen hinter '>'
   }
   switch (cf) {
    case '%': *n++=cf; break;	// "%%" ergibt "%", "%<abc>%" ergibt ebenso "%"
    case 'D': {
     if (!userfmt) userfmt=T("yyyy-MM-dd");	// Kann leider nicht mit Kalenderwochen umgehen
     n+=GetDateFormat(LOCALE_USER_DEFAULT,0,0,userfmt,n,int(end-n))-1;
     fmt++;
    }break;
    case 'T': {
     if (!userfmt) userfmt=T("HH-mm-ss");
     n+=GetTimeFormat(LOCALE_USER_DEFAULT,0,0,userfmt,n,int(end-n))-1;
     fmt++;
    }break;
    case 't': {	// Unix-Zeitformat von strftime(), allerdings ist %W nicht die Kalenderwoche nach DIN: Blöd
     time_t t;
     time(&t);
     if (!userfmt) userfmt=T("%Y-%m-%d_%H-%M-%S");
     n+=_tcsftime(n,end-n,userfmt,localtime(&t))-1;
     fmt++;
    }break;
    default: goto noformat;
   }
  }else noformat: *n++=c;
 }
 *n=0;
 return int(n-start);
}

// Rekursives Verzeichnis-Erstellen
bool RecurseCreateDir(TCHAR*name) {
 TCHAR p[MAX_PATH];
 lstrcpyn(p,name,elemof(p));
 if (!PathRemoveFileSpec(p)) return false;
 if (CreateDirectory(p,0)) return true;
 switch (GetLastError()) {
  case ERROR_ALREADY_EXISTS: return true;
  case ERROR_PATH_NOT_FOUND: return RecurseCreateDir(p) && CreateDirectory(p,0);
  default: return false;
 }
}

bool save_start() {
 SAVE*s=saveobj[config.z.getfileformat()];
 if (!s->okay()) return false;
 sprintDateTime(currentfile,MAX_PATH,file);
 RecurseCreateDir(currentfile);
 GetSystemTimeAsFileTime(&starttime);
 newfile=true;
 bool ret=s->start();	// "Konstruktor mit Rückgabe"
 if (ret) save=s;	// Jetzt erst Blöcke einschreiben
 return ret;
}

bool save_stop() {
 SAVE*s=save;
 save=0;		// Nicht während des Stopp-Prozesses Blöcke einschreiben
 bool ret=s->stop();
 return ret;
}

class WAVSAVE:public SAVE{
 HMMIO h;
 MMCKINFO ckRiff,ckFmt,ckData;
public:
 WAVSAVE();
 virtual bool okay() const {return true;}
 virtual bool start();
 virtual bool put(const int*,unsigned);
 virtual bool stop();
 virtual ~WAVSAVE() {}
};

// Der MP3-Kompressor
class MP3SAVE:public SAVE{
 HINSTANCE hLame;
// Funktionszeiger in lame_enc.dll, numerisch 1..9
// Einige Funktionen werden nicht aufgerufen, sondern sind hier nur wegen der numerischen Reihenfolge aufgeführt.
// Bekloppt: Dateiname ANSI.
// Unter Windows 10 sollte es mit exotischen Dateinamen erst klappen, wenn man das System auf UTF-8 umstellt.
// Wegen fehlerhafter Deklaration der Funktionszeiger in der Headerdatei (ohne _cdecl) hier noch einmal neu.
// Lame ist open-source und damit fehlerhaft. Leider.
// Diese Namen überschreiben die globalen Definitionen in <BladeMP3EncDLL.h>,
// deren Bezugnahme zu Linker-Fehlern führen würden.
 BE_ERR (_cdecl*beInitStream)	(BE_CONFIG&,unsigned&,unsigned&,HBE_STREAM&); 
 BE_ERR	(_cdecl*beEncodeChunk)	(HBE_STREAM,unsigned,const short*,BYTE*,unsigned&); 
 BE_ERR	(_cdecl*beDeinitStream)	(HBE_STREAM,BYTE*,unsigned&);
 BE_ERR	(_cdecl*beCloseStream)	(HBE_STREAM); 
 void   (_cdecl*beVersion)	(BE_VERSION&);
 BE_ERR	(_cdecl*beWriteVBRHeader)(const char*FileName);
 BE_ERR	(_cdecl*beEncodeChunkFloatS16NI)(HBE_STREAM,DWORD,const float*l,const float*r,BYTE*,unsigned&);
 BE_ERR (_cdecl*beFlushNoGap)	(HBE_STREAM,BYTE*,unsigned&);
 BE_ERR	(_cdecl*beWriteInfoTag)	(HBE_STREAM,const char*FileName); 
 TCHAR*mp3filename;
 FILE*mp3file;
// Needed for LAME encoder:
 unsigned dwSamples,dwMP3Buffer;
 HBE_STREAM hbeStream;
 BYTE*pMP3Buffer;
 short*pWAVBuffer;
 unsigned WavFill;
public:
 MP3SAVE();
 virtual bool okay() const;
 virtual bool start();
 virtual bool put(const int*,unsigned);
 virtual bool stop();
 virtual ~MP3SAVE();
};

class OGGSAVE:public SAVE{
 HMMIO h;
 HINSTANCE hVorbis[2];	// ogg.dll,vorbis.dll
// Auch bei Vorbis habe ich mich für den dynamischen DLL-Aufruf mit Nummern entschieden.
// Auch hier hat man in den Header-Dateien das _cdecl unterschlagen
// und obendrein zwei Low-Aufrufperformance-DLLs (eben mit _cdecl) erzeugt.
// Problem: Es scheint keine 64-Bit-DLLs zu geben,
// und einen sauberen Download von 32-Bit-DLLs scheint es auch nicht zu geben,
// Verdacht: Der No23-Recorder-Programmierer hat diese selbst aus den Vorbis-Quellen erstellt
// Und hat auch vorbisenc.dll uns vorbisfile.dll dazugesetzt, die hier gar nicht benötigt werden.
// Das Interface ist noch viel umständlicher als bei lame_enc.dll,
// aber dafür gibt's kein Problem mit Unicode.
// Erwähnenswert wäre, dass OGG der „Standardcontainer“ für Vorbis ist.
// Man hätte es auch in RIFF stecken können, aber man wollte wohl keinen Mac/Windows-Bezug.
// Sondern lieber, wie leider üblich, das Fahrrad nochmal erfinden.

 int (_cdecl*ogg_page_eos)		(const ogg_page*);			// ogg.dll:0x05
 int (_cdecl*ogg_stream_clear)		(ogg_stream_state*);			// ogg.dll:0x0B
 int (_cdecl*ogg_stream_flush)		(ogg_stream_state*,ogg_page*);		// ogg.dll:0x0E
 int (_cdecl*ogg_stream_init)		(ogg_stream_state*,int serialno);	// ogg.dll:0x0F
 int (_cdecl*ogg_stream_packetin)	(ogg_stream_state*,ogg_packet*);	// ogg.dll:0x10
 int (_cdecl*ogg_stream_pageout)	(ogg_stream_state*,ogg_page*);		// ogg.dll:0x14

 int (_cdecl*vorbis_analysis)		(vorbis_block*,ogg_packet*);		// vorbis.dll:0x05
 int (_cdecl*vorbis_analysis_blockout)	(vorbis_dsp_state*,vorbis_block*);	// vorbis.dll:0x06
 float**(_cdecl*vorbis_analysis_buffer)	(vorbis_dsp_state*,int);		// vorbis.dll:0x07
 int (_cdecl*vorbis_analysis_headerout)	(vorbis_dsp_state*,vorbis_comment*,ogg_packet*,ogg_packet*,ogg_packet*);	// vorbis.dll:0x08
 int (_cdecl*vorbis_analysis_init)	(vorbis_dsp_state*,vorbis_info*);	// vorbis.dll:0x09
 int (_cdecl*vorbis_analysis_wrote)	(vorbis_dsp_state*,int);		// vorbis.dll:0x0A
 int (_cdecl*vorbis_bitrate_addblock)	(vorbis_block*);			// vorbis.dll:0x0B
 int (_cdecl*vorbis_bitrate_flushpacket)(vorbis_dsp_state*,ogg_packet*);	// vorbis.dll:0x0C
 int (_cdecl*vorbis_block_clear)	(vorbis_block*);			// vorbis.dll:0x0D
 int (_cdecl*vorbis_block_init)		(vorbis_dsp_state*,vorbis_block*);	// vorbis.dll:0x0E
 void(_cdecl*vorbis_comment_add_tag)	(vorbis_comment*,const char*,const char*);// vorbis.dll:0x10
 void(_cdecl*vorbis_comment_clear)	(vorbis_comment*);			// vorbis.dll:0x11
 void(_cdecl*vorbis_comment_init)	(vorbis_comment*);			// vorbis.dll:ßx12
 void(_cdecl*vorbis_dsp_clear)		(vorbis_dsp_state*);			// vorbis.dll:0x16
 int (_cdecl*vorbis_encode_init_vbr)	(vorbis_info*,long,long,float);		// vorbis.dll:0x19
 void(_cdecl*vorbis_info_clear)		(vorbis_info*);				// vorbis.dll:0x1F
 void(_cdecl*vorbis_info_init)		(vorbis_info*);				// vorbis.dll:0x20

// Die englischen Kommentare entstammen dem Beispiel-Vorbis-Encoder, es wimmelt darin von Bugs.
 ogg_stream_state os;	// take physical pages, weld into a logical stream of packets -- wieherum auch immer verschweißt!!
 vorbis_info      vi;	// stores all the static vorbis bitstream settings
 vorbis_comment   vc;	// stores all the user comments
 vorbis_dsp_state vd;	// central working state for the packet->PCM decoder -- hä? Hier ist's doch andersherum!!
 vorbis_block     vb;	// working space for packet->PCM decode -- sicherlich andersherum!!
 bool process();	// Kompressionsroutine
public:
 OGGSAVE();			// Konstruktor: Lädt DLLs und stellt die Funktionszeiger her
 virtual bool okay() const;	// <true> wenn der Konstruktor OK verlief (statt Exception-Handling)
 virtual bool start();		// Aufnahme-Start: „currentfile“ enthält die zu erzeugende Datei; Verzeichnis wurde bereits erstellt
 virtual bool put(const int*,unsigned);	// PCM-Samples übergeben, Längenargument in Mono-Samples. Aufruf aus Worker-Thread!
 virtual bool stop();		// Aufnahme-Stopp, <false> wenn beim Schließen der Datei etwas schief ging
 virtual ~OGGSAVE();		// Destruktor: Entlädt DLLs
};

/***********
 * WAVSAVE *
 ***********/

WAVSAVE::WAVSAVE() {
 ckRiff.fccType=mmioFOURCC('W','A','V','E');
 ckFmt.ckid=mmioFOURCC('f','m','t',' ');
 ckData.ckid=mmioFOURCC('d','a','t','a');
}

bool WAVSAVE::start() {
 h=mmioOpen(currentfile,0,MMIO_WRITE|MMIO_DENYNONE|MMIO_ALLOCBUF|MMIO_CREATE);
 if (!h) return false;
 bool ret=true;
 if (mmioCreateChunk(h,&ckRiff,MMIO_CREATERIFF)) ret=false;
 if (mmioCreateChunk(h,&ckFmt,0)) ret=false;
 WAVEFORMATEXTENSIBLE wf;
 InitWaveFormat(wf,config.z.getrate(),config.z.getchan(),config.z.getbits());
 LONG cch=sizeof wf.Format+wf.Format.cbSize;
 if (mmioWrite(h,(const char*)&wf,cch)!=cch) ret=false;
 if (mmioAscend(h,&ckFmt,0)) ret=false;
 if (mmioCreateChunk(h,&ckData,0)) ret=false;
 return ret;
}

// Kopiert <buf> nach <w> unter Reduktion der Bits, <l> = Einzelsamples
void convertWav(const int*buf,unsigned l, char*w) {
 switch (config.z.getbytes()) {
  case 1: {	// 8 Bit
   for (unsigned i=0; i<l; i++) ((BYTE*)w)[i]=(buf[i]>>16)+0x80;
  }break;
  case 2: {	// 16 Bit
   for (unsigned i=0; i<l; i++) ((short*)w)[i]=buf[i]>>8;
  }break;
  case 3: {	// 24 Bit
   for (unsigned i=0; i<l; i++) {
    w[i*3+0]=buf[i];
    w[i*3+1]=buf[i]>>8;
    w[i*3+2]=buf[i]>>16;	// Problem: Endianness?
   }
  }break;
  default: memcpy(w,buf,l<<2);	// DWORDs kopieren
 }
}

bool WAVSAVE::put(const int*buf,unsigned l) {
 l*=config.z.getchan();	// Anzahl Einzelsamples
 if (config.z.getbytes()==4) {		// DWORD?
  l<<=2;				// Bytes
  return (unsigned)mmioWrite(h,(const char*)buf,l)==l;	// <buf> direkt ausschreiben
 }
 unsigned ll=l*config.z.getbytes();
 char*b=new char[ll];	// Hilfspuffer beschaffen (besser wäre _alloca())
 if (!b) return false;
 convertWav(buf,l,b);
 bool ret=(unsigned)mmioWrite(h,b,ll)==ll;
 delete[] b;
 return ret;
}

bool WAVSAVE::stop() {
 bool ret=true;
 if (mmioAscend(h,&ckData,0)) ret=false;
 if (mmioAscend(h,&ckRiff,0)) ret=false;
 if (mmioClose(h,0)) ret=false;
 return ret;
}

/***********
 * MP3SAVE *
 ***********/

MP3SAVE::MP3SAVE() {
 if (!dllLoad(hLame,T("lame_enc.dll"))) return;
 for (int i=1; i<10; i++) {
  ((FARPROC*)&beInitStream)[i-1]=GetProcAddress(hLame,(char*)(LONG_PTR)i);
 }
#ifdef _DEBUG
 BE_VERSION ver;
 beVersion(ver);
 OutputDebugStringA(ver.zHomepage);
#endif
 mp3file=0;
}

bool MP3SAVE::okay() const{
 return hLame && beEncodeChunk;
}

bool MP3SAVE::start() {
 mp3filename=_tcsdup(currentfile);
 BE_CONFIG cfg;
 memset(&cfg,0,sizeof cfg);
 cfg.dwConfig = BE_CONFIG_LAME;	// use the LAME config structure
	// this are the default settings for testcase.wav
 cfg.format.LHV1.dwStructVersion	= 1;
 cfg.format.LHV1.dwStructSize		= sizeof cfg;
 cfg.format.LHV1.dwSampleRate		= config.z.getrate();	// INPUT FREQUENCY
 cfg.format.LHV1.nMode			= config.z.getchan()==2?BE_MP3_MODE_JSTEREO:BE_MP3_MODE_MONO;
 cfg.format.LHV1.dwBitrate		= config.z.mp3min<<3;		// MINIMUM BIT RATE
 cfg.format.LHV1.nPreset		= LQP_R3MIX;		// QUALITY PRESET SETTING
 cfg.format.LHV1.dwMpegVersion		= MPEG1;		// MPEG VERSION (I or II)
 cfg.format.LHV1.bOriginal		= true;			// SET ORIGINAL FLAG
 cfg.format.LHV1.bWriteVBRHeader	= true;			// Write INFO tag
 beInitStream(cfg,dwSamples,dwMP3Buffer,hbeStream);
 pMP3Buffer=new BYTE[dwMP3Buffer];
 pWAVBuffer=new short[dwSamples];
 WavFill=0;
 mp3file=_tfopen(mp3filename,TEXT("wb+"));
 return !!mp3file;
}

bool MP3SAVE::put(const int*buffer,unsigned len) {
 len*=config.z.getchan();	// Samples insgesamt
 bool ret=true;
 while (len) {
  unsigned l=dwSamples-WavFill;	// Platz in pWAVBuffer
  if (l>len) l=len;		// Minimum beider
  for (unsigned i=0; i<l; i++) pWAVBuffer[WavFill+i]=buffer[i]>>8;	// auf 16 Bit verkürzen
  WavFill+=l;
  buffer+=l;
  len-=l;
  if (WavFill==dwSamples) {
   unsigned written;
   if (beEncodeChunk(hbeStream,WavFill,pWAVBuffer,pMP3Buffer,written)) ret=false;
   if (fwrite(pMP3Buffer,1,written,mp3file)!=written) ret=false;
   WavFill=0;
  }
 }
 return ret;
}

bool MP3SAVE::stop() {
 bool ret=true;
 unsigned written;
 if (WavFill) {
  if (beEncodeChunk(hbeStream,WavFill,pWAVBuffer,pMP3Buffer,written)) ret=false;
  if (fwrite(pMP3Buffer,1,written,mp3file)!=written) ret=false;
 }
 if (beDeinitStream(hbeStream,pMP3Buffer,written)) ret=false;
 if (fwrite(pMP3Buffer,1,written,mp3file)!=written) ret=false;
 if (beCloseStream(hbeStream)) ret=false;
 delete[] pWAVBuffer;
 delete[] pMP3Buffer;
 if (fclose(mp3file)) ret=false;
 int nc=lstrlen(currentfile);
#ifdef UNICODE
 int need=WideCharToMultiByte(CP_UTF8,0,mp3filename,-1,0,0,0,0);
 char*n=new char[need];
 WideCharToMultiByte(CP_UTF8,0,mp3filename,-1,n,need,0,0);
 if (beWriteVBRHeader(n)) ret=false;
 delete[] n;
#else
 mp3filename=new char[nc+1];
 memcpy(mp3filename,currentfile,nc+1);
 if (beWriteVBRHeader(mp3filename)) ret=false;
#endif
 free(mp3filename);
 return ret;
}

MP3SAVE::~MP3SAVE() {
 FreeLibrary(hLame);
}

/***********
 * OGGSAVE *
 ***********/

OGGSAVE::OGGSAVE() {
 const TCHAR*p=T("ogg.dll\0vorbis.dll");	//\0vorbisenc.dll\0vorbisfile.dll
 int i;
 for (i=0; i<elemof(hVorbis); i++,p+=lstrlen(p)+1) dllLoad(hVorbis[i],p);
 if (!okay()) return;
 static const BYTE oggentries[]={0x05,0x0B,0x0E,0x0F,0x10,0x14};
 for (i=0; i<elemof(oggentries); i++) {
  ((FARPROC*)&ogg_page_eos)[i]=GetProcAddress(hVorbis[0],(char*)(LONG_PTR)oggentries[i]);
 }
 static const BYTE vorbisentries[]={0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x10,0x11,0x12,0x16,0x19,0x1F,0x20};
 for (i=0; i<elemof(vorbisentries); i++) {
  ((FARPROC*)&vorbis_analysis)[i]=GetProcAddress(hVorbis[1],(char*)(LONG_PTR)vorbisentries[i]);
 }
}

bool OGGSAVE::okay() const{
 for (int i=0; i<elemof(hVorbis); i++) if (!hVorbis[i]) return false;
 return true;
}

bool OGGSAVE::start() {
 vorbis_info_init(&vi);
 h=mmioOpen(currentfile,0,MMIO_WRITE|MMIO_DENYNONE|MMIO_ALLOCBUF|MMIO_CREATE);
 if (!h) return false;
 if (vorbis_encode_init_vbr(&vi,config.z.getchan(),config.z.getrate(),0.1F)) return false;
 vorbis_comment_init(&vc);
 TCHAR s[MAX_PATH];
 GetModuleFileName(0,s,elemof(s));
 TCHAR*p=PathFindFileName(s);
#ifdef UNICODE
 char utf8str[MAX_PATH];
 WideCharToMultiByte(CP_UTF8,0,p,-1,utf8str,sizeof utf8str,0,0);
 vorbis_comment_add_tag(&vc,"ENCODER",utf8str);
#else
 vorbis_comment_add_tag(&vc,"ENCODER",p);
#endif
 vorbis_analysis_init(&vd,&vi);
 vorbis_block_init(&vd,&vb);
/* pick a random serial number; that way we can more likely build chained streams just by concatenation */
 ogg_stream_init(&os,GetTickCount());
 ogg_packet header;
 ogg_packet header_comm;
 ogg_packet header_code;
 vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
 ogg_stream_packetin(&os,&header); /* automatically placed in its own page */
 ogg_stream_packetin(&os,&header_comm);
 ogg_stream_packetin(&os,&header_code);
 ogg_page og; /* one Ogg bitstream page.  Vorbis packets are inside */
 if (!ogg_stream_flush(&os,&og)) return false;
 mmioWrite(h,(char*)og.header,og.header_len);
 mmioWrite(h,(char*)og.body,og.body_len);
 return true;
}

bool OGGSAVE::put(const int*buffer,unsigned len) {
 float **b=vorbis_analysis_buffer(&vd,len);
 unsigned nch=config.z.getchan();
 for(unsigned i=0;i<len;i++){
  for (unsigned j=0;j<nch;j++) {
   b[j][i]=float(*buffer++)/0x800000;	// Matrix stürzen und in float konvertieren
  }
 }
 vorbis_analysis_wrote(&vd,len);	// tell the library how much we actually submitted
 return process();
}

bool OGGSAVE::process() {
 while (vorbis_analysis_blockout(&vd,&vb)==1){		// Get a single block for encoding
  vorbis_analysis(&vb,0);	// analysis, assume we want to use bitrate management
  vorbis_bitrate_addblock(&vb);
  for(ogg_packet op;vorbis_bitrate_flushpacket(&vd,&op);){	// one raw packet of data for decode
   ogg_stream_packetin(&os,&op);			// weld the packet into the bitstream
   for(ogg_page og;ogg_stream_pageout(&os,&og);){	// write out pages
    mmioWrite(h,(char*)og.header,og.header_len);	// one Ogg bitstream page. Vorbis packets are inside
    mmioWrite(h,(char*)og.body,og.body_len);
    if (ogg_page_eos(&og)) break;
   }
  }
 }
 return true;
}

bool OGGSAVE::stop() {
 vorbis_analysis_wrote(&vd,0);	// Null Bytes = End-Of-Stream?
 process();			// ausschreiben
 ogg_stream_clear(&os);
 vorbis_block_clear(&vb);
 vorbis_dsp_clear(&vd);
 vorbis_comment_clear(&vc);
 bool ret=!mmioClose(h,0);
 h=0;
 vorbis_info_clear(&vi);
 return ret;
}

OGGSAVE::~OGGSAVE() {
 for (int i=0; i<elemof(hVorbis); i++) FreeLibrary(hVorbis[i]);
}

#if 0
/*Beispielkode*/
/* takes a stereo 16bit 44.1kHz WAV file from stdin and encodes it into
   a Vorbis bitstream */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#define READ 1024
signed char readbuffer[READ*4+44]; /* out of the data segment, not the stack */

int main(){

  int eos=0,ret;
  int i, founddata;



  /* choose an encoding mode.  A few possibilities commented out, one
     actually used: */

  /*********************************************************************
   Encoding using a VBR quality mode.  The usable range is -.1
   (lowest quality, smallest file) to 1. (highest quality, largest file).
   Example quality mode .4: 44kHz stereo coupled, roughly 128kbps VBR

   ret = vorbis_encode_init_vbr(&vi,2,44100,.4);

   ---------------------------------------------------------------------

   Encoding using an average bitrate mode (ABR).
   example: 44kHz stereo coupled, average 128kbps VBR

   ret = vorbis_encode_init(&vi,2,44100,-1,128000,-1);

   ---------------------------------------------------------------------

   Encode using a quality mode, but select that quality mode by asking for
   an approximate bitrate.  This is not ABR, it is true VBR, but selected
   using the bitrate interface, and then turning bitrate management off:

   ret = ( vorbis_encode_setup_managed(&vi,2,44100,-1,128000,-1) ||
           vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE2_SET,NULL) ||
           vorbis_encode_setup_init(&vi));

   *********************************************************************/

  ret=vorbis_encode_init_vbr(&vi,2,44100,0.1);

  /* do not continue if setup failed; this can happen if we ask for a
     mode that libVorbis does not support (eg, too low a bitrate, etc,
     will return 'OV_EIMPL') */

  if(ret)exit(1);

  /* add a comment */
  vorbis_comment_init(&vc);
  vorbis_comment_add_tag(&vc,"ENCODER","encoder_example.c");

  /* set up the analysis state and auxiliary encoding storage */
  vorbis_analysis_init(&vd,&vi);
  vorbis_block_init(&vd,&vb);

  /* set up our packet->stream encoder */
  /* pick a random serial number; that way we can more likely build
     chained streams just by concatenation */
  srand(time(NULL));
  ogg_stream_init(&os,rand());

  /* Vorbis streams begin with three headers; the initial header (with
     most of the codec setup parameters) which is mandated by the Ogg
     bitstream spec.  The second header holds any comment fields.  The
     third header holds the bitstream codebook.  We merely need to
     make the headers, then pass them to libvorbis one at a time;
     libvorbis handles the additional Ogg bitstream constraints */

  {
    ogg_packet header;
    ogg_packet header_comm;
    ogg_packet header_code;

    vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
    ogg_stream_packetin(&os,&header); /* automatically placed in its own
                                         page */
    ogg_stream_packetin(&os,&header_comm);
    ogg_stream_packetin(&os,&header_code);

    /* This ensures the actual
     * audio data will start on a new page, as per spec
     */
    while(!eos){
      int result=ogg_stream_flush(&os,&og);
      if(result==0)break;
      fwrite(og.header,1,og.header_len,stdout);
      fwrite(og.body,1,og.body_len,stdout);
    }

  }

  while(!eos){
    long i;
    long bytes=fread(readbuffer,1,READ*4,stdin); /* stereo hardwired here */

    if(bytes==0){
      /* end of file.  this can be done implicitly in the mainline,
         but it's easier to see here in non-clever fashion.
         Tell the library we're at end of stream so that it can handle
         the last frame and mark end of stream in the output properly */
      vorbis_analysis_wrote(&vd,0);

    }else{
      /* data to encode */

      /* expose the buffer to submit data */
      float **buffer=vorbis_analysis_buffer(&vd,READ);

      /* uninterleave samples */
      for(i=0;i<bytes/4;i++){
        buffer[0][i]=((readbuffer[i*4+1]<<8)|
                      (0x00ff&(int)readbuffer[i*4]))/32768.f;
        buffer[1][i]=((readbuffer[i*4+3]<<8)|
                      (0x00ff&(int)readbuffer[i*4+2]))/32768.f;
      }

      /* tell the library how much we actually submitted */
      vorbis_analysis_wrote(&vd,i);
    }

    /* vorbis does some data preanalysis, then divvies up blocks for
       more involved (potentially parallel) processing.  Get a single
       block for encoding now */
    while(vorbis_analysis_blockout(&vd,&vb)==1){

      /* analysis, assume we want to use bitrate management */
      vorbis_analysis(&vb,NULL);
      vorbis_bitrate_addblock(&vb);

      for(ogg_packet op;vorbis_bitrate_flushpacket(&vd,&op);){

        /* weld the packet into the bitstream */
        ogg_stream_packetin(&os,&op);

        /* write out pages (if any) */
        while(!eos){
          int result=ogg_stream_pageout(&os,&og);
          if(result==0)break;
          fwrite(og.header,1,og.header_len,stdout);
          fwrite(og.body,1,og.body_len,stdout);

          /* this could be set above, but for illustrative purposes, I do
             it here (to show that vorbis does know where the stream ends) */

          if(ogg_page_eos(&og))eos=1;
        }
      }
    }
  }

  /* clean up and exit.  vorbis_info_clear() must be called last */

  ogg_stream_clear(&os);

  /* ogg_page and ogg_packet structs always point to storage in
     libvorbis.  They're never freed or manipulated directly */

  fprintf(stderr,"Done.\n");
  return(0);
} 

#endif

void SAVE::newall() {
 saveobj[0]=new WAVSAVE;
 saveobj[1]=new MP3SAVE;
 saveobj[2]=new OGGSAVE;
}

void SAVE::deleteall() {
 for(SAVE**i=saveobj; i!=saveobj+elemof(saveobj); i++) {
  delete*i; *i=0;
 }
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded