#define INITGUID
#include <platform.h>
#include <malloc.h>
#include <debug.h>
#include <stdio.h>
#define DWORD_PTR DWORD *
#include <dsound.h>
#include "types.h"
#include "zvuk.h"
#include <math.h>
extern "C" {
#include <memman.h>
  }
#define MAXCHANNELS 32
#define TIMEIDLESTOPCHANNEL 250
#define DEFAULTBUFFERSIZE (256*1024)
#define FADELENGTH 3000
#define MUS_ERRORWAIT 5000
#include "Music.h"


static int cur_device=0;
static int cur_mixfreq=0;
static bool swap_chans=false;

class CheckRes
  {
  public:
   HRESULT operator=(HRESULT other)
     {
     if (other==0) return 0;
     char buff[256];
     sprintf(buff,"DirectSound error HRESULT %08X, code %d",other,other & 0xFFFF);
     int id=MessageBox(NULL,buff,NULL,MB_SYSTEMMODAL|MB_ABORTRETRYIGNORE);
     if (id==IDRETRY) 
       {
       __asm int 3;
       return other;
       }
     if (id==IDIGNORE) return other;
     exit(-1);
     }
   };

     


  
static CheckRes hres;
static HWND hWndDS8=NULL;
static IDirectSound8 *ds8=NULL;
static IDirectSoundBuffer *ds_primary;
static WAVEFORMATEX waveformat;
static int gfx_volume=255;
static int music_volume=127;
static int glob_volume=255;
static int linvoltable[256];
static MusicPlayer GMusicPlayer;
static MusDecoder GMusDecoder;
static WinAmpDecoder GWinAmpPlayer;
static IDecoder *GCurrentDecoder=&GMusDecoder;


class SoundChannelInfo
  {
  public:
  CRITICAL_SECTION sect;
  char *play_pos, *start_loop, *end_loop;
  IDirectSoundBuffer8 *buffer;
  uint32_t chantype;
  uint32_t idsPlayPos;
  uint32_t idsWritePos;
  uint32_t idsBuffSize;
  int32_t volume;    //volume for DS
  int32_t pan;       //pan for DS
  int volleft;    //left volume for skeldal
  int volright;   //right volume for skeldal
  uint32_t preload;
  uint32_t stopTime; //time, when buffer reached end. For calculating idle time
  uint32_t startTime; //time, when buffer started. For calculating idle time, use 0 to sticky buffer
  SoundChannelInfo() 
    {
    buffer=NULL;
    play_pos=start_loop=end_loop=NULL;
    InitializeCriticalSection(&sect);
    pan=DSBPAN_CENTER;
    volume=DSBVOLUME_MAX;
    }
  ~SoundChannelInfo() {if (buffer) buffer->Release();DeleteCriticalSection(&sect);}
  uint32_t CalculateLockSize();
  void ChannelMaintaince();     
  bool IsPlaying() {return play_pos!=NULL;}  
//  bool IsFree(char type) {return play_pos==NULL || (type==chantype && play_pos==end_loop);}
  void Reset();
  void InitChannel(char type, char *ppos, char *bloop,char *eloop, int freq);
  void Lock() {EnterCriticalSection(&sect);}
  void Unlock() {LeaveCriticalSection(&sect);}
  bool TryLock() {return TryEnterCriticalSection(&sect)!=FALSE;}
  void SetVolume(int32_t vol) 
    {
    Lock();
    if (volume!=vol) if (buffer && play_pos ) hres=buffer->SetVolume(vol);
    volume=vol;    
    Unlock();
    }
  void SetPan(int32_t p) 
    {
    Lock();
    if (pan!=p) if (buffer && play_pos ) hres=buffer->SetPan(p);
    pan=p;
    Unlock();
    }
  void Mute() 
    {
    Lock();
    Stop();
    Unlock();
    }
  void Stop()
	{
	if (buffer)
	  buffer->Stop();
	play_pos=NULL;
	}
    
  void BreakLoop() {start_loop=end_loop;}
  void BreakLoopEx(char *end_sample) {start_loop=end_loop=end_sample;}
  };

static bool shutMaintaince=false;
static SoundChannelInfo channels[MAXCHANNELS];
static HANDLE maintainceThread;

ULONG __stdcall MaintainceThread(void *);
  
static void DSStart(int mixfreq)
  {
  DSBUFFERDESC desc; 

  for (int i=1;i<256;i++) 
    {
    double rang=pow(i/255.0,15);
    linvoltable[i]=100*log(rang);
    
    }
  linvoltable[0]=-10000;

  hres=DirectSoundCreate8(&DSDEVID_DefaultPlayback,&ds8,NULL);
  if (hWndDS8) hres=ds8->SetCooperativeLevel(hWndDS8,DSSCL_PRIORITY);
  memset(&desc,0,sizeof(desc));
  desc.dwSize=sizeof(desc);
  desc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRLVOLUME;
  hres=ds8->CreateSoundBuffer(&desc,&ds_primary,NULL); 

  waveformat.cbSize=sizeof(waveformat);
  waveformat.nBlockAlign=4;
  waveformat.nChannels=2;
  waveformat.nSamplesPerSec=mixfreq;
  waveformat.nAvgBytesPerSec=mixfreq*4;
  waveformat.wBitsPerSample=16;
  waveformat.wFormatTag=WAVE_FORMAT_PCM;  

  hres=ds_primary->SetFormat(&waveformat);
  DWORD id;

  shutMaintaince=false;
  maintainceThread=CreateThread(NULL,0,MaintainceThread,NULL,0,&id);
  SetThreadPriority(maintainceThread,THREAD_PRIORITY_HIGHEST);

  GMusicPlayer.InitBuffer(ds8,linvoltable);
  GMusicPlayer.Play();
  } 

void init_winamp_plugins(const char *path)
{
  GWinAmpPlayer.LoadPlugins(path);
}

static void DSStop()
  {  
  GCurrentDecoder->Stop();
  GMusDecoder.Stop();
  GMusicPlayer.Done();
  GWinAmpPlayer.ClearList();
  shutMaintaince=true;
  WaitForSingleObject(maintainceThread,INFINITE);
  CloseHandle(maintainceThread);
  for (int i=0;i<MAXCHANNELS;i++) 
    if (channels[i].buffer) 
      {channels[i].Stop(); channels[i].buffer->Release();channels[i].buffer=NULL;}
  if (ds_primary) ds_primary->Release();
  ds_primary=NULL;
  if (ds8) ds8->Release();   
  ds8=NULL;
  }
  
uint32_t SoundChannelInfo::CalculateLockSize()
  {
  if (buffer==NULL) return 0;
  uint32_t playpos;
  buffer->GetCurrentPosition(NULL,&playpos);
  int32_t diff=(signed)playpos-(signed)idsPlayPos;
  if (diff<0) diff+=idsBuffSize;
  uint32_t wendpos=playpos+preload; 
  if (wendpos>=idsBuffSize) wendpos-=idsBuffSize;
  if (wendpos<idsWritePos && wendpos>playpos) return 0;
  if (wendpos<idsWritePos) wendpos+=idsBuffSize;
  uint32_t sz=(wendpos-idsWritePos+3) & ~3;
  if (sz>idsBuffSize/2) sz=idsBuffSize/2;
  idsPlayPos=playpos;
  return sz;
  }

void SoundChannelInfo::ChannelMaintaince()
  {
  Lock();
  if (play_pos!=NULL)
    {
    if (play_pos!=end_loop) stopTime=GetTickCount();
    uint32_t lockSize=CalculateLockSize();
    if (lockSize)
      {
  //    printf("%8d\r",lockSize);
      void *audioptrs[2];
      uint32_t sizes[2];
      hres=buffer->Lock(idsWritePos,lockSize,audioptrs,sizes,audioptrs+1,sizes+1,0);
      for (int i=0;i<2 && audioptrs[i];i++)
        {
        char *wrt=(char *)audioptrs[i];
        for (uint32_t j=0;j<sizes[i];j++) if (play_pos!=end_loop) 
          {
          *wrt++=*play_pos++;
          if (play_pos==end_loop) play_pos=start_loop;               
          }
        else
          {
          if (chantype & 0x1) *wrt++=0x80; else *wrt++=0;
          }
        }      
      hres=buffer->Unlock(audioptrs[0],sizes[0],audioptrs[1],sizes[1]);      
      idsWritePos+=lockSize;
      if (idsWritePos>=idsBuffSize) idsWritePos-=idsBuffSize;
      }
    if (play_pos==end_loop && GetTickCount()-stopTime>TIMEIDLESTOPCHANNEL)
        {
        Stop();        
        }           
    }
  Unlock();
  }
  
static void MixMaintaince()
  {  
  for (int i=0;i<MAXCHANNELS;i++) channels[i].ChannelMaintaince();   
  }

static ULONG __stdcall MaintainceThread(void *)
  {
  while (!shutMaintaince) {MixMaintaince();Sleep(50);}
  return 0;
  }

void SoundChannelInfo::Reset()
  {
  Lock();
  if (buffer) buffer->Release();
  buffer=NULL;
  Unlock();
  }

void SoundChannelInfo::InitChannel(char type, char *ppos, char *bloop,char *eloop, int freq)
  {
  Lock();
  uint32_t newchantype=type+freq*4;
  if (chantype!=newchantype || buffer==NULL)
    {
    Reset();
    DSBUFFERDESC desc; 
    WAVEFORMATEX wfex;
    wfex.cbSize=sizeof(wfex);
    wfex.wFormatTag=WAVE_FORMAT_PCM;
    wfex.wBitsPerSample=type==1?8:16;
    wfex.nSamplesPerSec=freq;
    wfex.nChannels=1;
    wfex.nBlockAlign=type==1?1:2;
    wfex.nAvgBytesPerSec=wfex.nBlockAlign*wfex.nSamplesPerSec;    

    memset(&desc,0,sizeof(desc));
    desc.dwSize=sizeof(desc);
    desc.dwFlags=DSBCAPS_CTRLFREQUENCY|DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME|DSBCAPS_GETCURRENTPOSITION2;
    desc.dwBufferBytes=DEFAULTBUFFERSIZE;
    desc.lpwfxFormat=&wfex;
    
    preload=wfex.nAvgBytesPerSec/5;

    IDirectSoundBuffer *bufold;
    
    hres=ds8->CreateSoundBuffer(&desc,&bufold,NULL);
    hres=bufold->QueryInterface(IID_IDirectSoundBuffer8,(void **)&buffer);
    bufold->Release();

    idsPlayPos=DEFAULTBUFFERSIZE-cur_mixfreq;
    idsWritePos=0;
    idsBuffSize=DEFAULTBUFFERSIZE;

    play_pos=ppos;
    start_loop=bloop;
    end_loop=eloop;

    chantype=newchantype;

    hres=buffer->SetVolume(volume);
    hres=buffer->SetPan(pan);

    ChannelMaintaince();
    
    hres=buffer->Play(0,0,DSBPLAY_LOOPING);
    stopTime=startTime=GetTickCount();   
    }
  else
    {
    buffer->Stop();
    idsBuffSize=DEFAULTBUFFERSIZE;
    buffer->SetCurrentPosition(0);
    buffer->GetCurrentPosition(&idsPlayPos,&idsWritePos);
    hres=buffer->SetVolume(volume);
    hres=buffer->SetPan(pan);
    play_pos=ppos;
    start_loop=bloop;
    end_loop=eloop;
    ChannelMaintaince();
    buffer->Play(0,0,DSBPLAY_LOOPING);
    stopTime=startTime=GetTickCount();   
    }
  Unlock();
  }

/*  
static int FindFreeChannel(char type)
  {
  int older=0;
  DWORD m_age=0;
  DWORD timval=GetTickCount();
  for (int i=0;i<MAXCHANNELS;i++) if (!channels[i].IsFree(char type)) return i;
    {
    DWORD age==timval-channels[i].startTime;
    if (age>m_age) {older=i;m_age=age;}    
    }
  return older;
  }
  */


extern "C"
  {
int bvolume;      //background volume
void (*konec_skladby)(char **jmeno)=NULL; //pointer to function to notify that end of song has been reached

void DSReportWindowCreation(HWND hWindow)
  {
  hWndDS8=hWindow;
  if (ds8!=NULL) hres=ds8->SetCooperativeLevel(hWndDS8,DSSCL_PRIORITY);
  }


int sound_detect(int *dev,int *port,int *dma, int *irq)
  {
  *dev=DEV_DIRECTSOUND;
  *port=0;
  *dma=0;
  *irq=0;
  return 0;
  }
  
void set_mixing_device(int mix_dev,int mix_freq,...)
  {
  cur_device=mix_dev;
  cur_mixfreq=mix_freq;
  }

char start_mixing()
  {
  if (cur_device!=DEV_DIRECTSOUND) 
    {
    MessageBox(hWndDS8,"Invalid sound device! Check SKELDAL.INI. Only device 9 (DirectSound) can be used.",NULL,MB_SYSTEMMODAL);
    exit(1);
    }
  if (cur_mixfreq==0) return FALSE;
  DSStart(cur_mixfreq);
  return TRUE;
  } 
void stop_mixing()
  {
  DSStop();
  }
 
void play_sample(int channel,void *sample,int32_t size,int32_t lstart,int32_t sfreq,int type)
  {  
  char *start=(char *)sample;
  channels[channel].InitChannel(type,start,start+lstart,start+size,sfreq);
  }

void set_channel_volume(int channel,int left,int right)
  {
  if (left>32767) left=32767;
  if (left<0) left=0;
  if (right>32767) right=32767;
  if (right<0) right=0;
  int volleft=linvoltable[(left>>7)*gfx_volume/255];
  int volright=linvoltable[(right>>7)*gfx_volume/255];
  int volsum=__max(volleft,volright);
  channels[channel].SetVolume(volsum);
  channels[channel].SetPan(swap_chans?(volright-volleft):(volleft-volright));
  channels[channel].volleft=left;
  channels[channel].volright=right;
  }

char get_channel_state(int channel)
  {
  return channels[channel].IsPlaying()==true;
  }

void get_channel_volume(int channel,int *left,int *right)
  {
  if (left) *left=channels[channel].volleft;
  if (right) *right=channels[channel].volright;
  }

void mute_channel(int channel)
  {
  channels[channel].Mute();
  }

void chan_break_loop(int channel)
  {
  channels[channel].BreakLoop();
  }

void chan_break_ext(int channel,void *org_sample,int32_t size_sample) //zrusi loop s moznosti dohrat zvu
  {
  char *end_sample=(char *)org_sample+size_sample;
  channels[channel].BreakLoopEx(end_sample);
  }

char set_snd_effect(int funct,int data)
  {
  switch (funct)
    {
    case SND_PING: return 1;
    case SND_SWAP: swap_chans=(data & 1)!=0;return 1;
    case SND_GFX: gfx_volume=data;return 1;
    case SND_MUSIC: GCurrentDecoder->SetVolume(music_volume=data,get_snd_effect(SND_GVOLUME));return 1;
    case SND_GVOLUME: hres=ds_primary->SetVolume(linvoltable[glob_volume=data]);return 1;
    default: return 0;
    }
  }

int  get_snd_effect(int funct)
  {
  switch (funct)
    {
    case SND_PING: return 1;
    case SND_SWAP: return swap_chans;
    case SND_GFX: return gfx_volume;
    case SND_MUSIC: return music_volume;
    case SND_GVOLUME: return glob_volume;
    default: return 0;
    }
  
  }

char check_snd_effect(int funct)
  {
  switch (funct)
    {
    case SND_PING: 
    case SND_SWAP:
    case SND_MUSIC:
    case SND_GVOLUME: 
    case SND_GFX: return 1;
    default: return 0;
    }
  }




static DWORD Mus_buffSize;
/*
static HANDLE music_file=NULL;
static HANDLE next_music_file=NULL;
static bool fading=false;
static DWORD fadetime;
static DWORD Mus_lastWritePos;
static DWORD Mus_nextBlockSize;
static DWORD Mus_nextBlockRead;
static DWORD Mus_errorWait=0;
static DWORD Mus_silentPlay=0;

#pragma pack (1)

struct MusFile
  {
  short channels;
  int32_t  freq;
  int32_t  ssize;
  int32_t  blocks;
  int32_t  reserved1;
  int32_t  reserved2;
  short ampltable[256];
  };
  
static MusFile curMus;

#pragma pack()




static char OpenMus(HANDLE music_file)
  {
  DWORD bytesread;

  SetFilePointer(music_file,0,NULL,FILE_BEGIN);

  if (ReadFile(music_file,&curMus,sizeof(curMus),&bytesread,NULL)==TRUE && bytesread==sizeof(curMus))    
    {	
    if (ReadFile(music_file,&Mus_nextBlockRead,sizeof(Mus_nextBlockRead),&bytesread,NULL)==FALSE) return 0;
    if (ReadFile(music_file,&Mus_nextBlockSize,sizeof(Mus_nextBlockSize),&bytesread,NULL)==FALSE) return 0;
		
    
    if (ds_music!=NULL) ds_music->Release();
    
    WAVEFORMATEX wfex;
    wfex.cbSize=sizeof(wfex);
    wfex.wBitsPerSample=16;
    wfex.nBlockAlign=2*curMus.channels;
    wfex.nSamplesPerSec=curMus.freq;
    wfex.nAvgBytesPerSec=curMus.freq*wfex.nBlockAlign;
    wfex.wFormatTag=WAVE_FORMAT_PCM;
    wfex.nChannels=curMus.channels;
    
    DSBUFFERDESC desc;
    desc.dwSize=sizeof(desc);
    desc.dwBufferBytes=Mus_buffSize=wfex.nAvgBytesPerSec*4;
    desc.dwReserved=0;
    desc.dwFlags=DSBCAPS_CTRLVOLUME ;
    desc.lpwfxFormat=&wfex;

    IDirectSoundBuffer *dsbf;

    hres=ds8->CreateSoundBuffer(&desc,&dsbf,NULL);
    hres=dsbf->QueryInterface(IID_IDirectSoundBuffer8,(void **)&ds_music);
    dsbf->Release();    
    
    void *ptr;
    DWORD size;

    ds_music->Lock(0,0,&ptr,&size,NULL,NULL,DSBLOCK_ENTIREBUFFER);
    memset(ptr,0,size);
    ds_music->Unlock(ptr,size,NULL,NULL);
    ds_music->SetVolume(linvoltable[music_volume]);
    ds_music->Play(0,0,DSBPLAY_LOOPING);    
    fadetime=0;
    Mus_lastWritePos=wfex.nAvgBytesPerSec;
    Mus_silentPlay=0;

    return 1;
    }
  return 0;
  }

static void PrepareMusFile(const char *filename)
  {
  CloseHandle(next_music_file);
  next_music_file=CreateFile(filename,GENERIC_READ,FILE_SHARE_READ,
                 NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,
                 NULL);
  if (next_music_file==INVALID_HANDLE_VALUE)
    next_music_file=NULL;
  }

static char music_decompres_block()
  {
  DWORD bytesread;
  char *data=(char *)alloca(Mus_nextBlockRead);
  short accum[2]={0,0};
  char curchan=0;
  
  DWORD lockSizes[2];
  void *lockPtrs[2];

  if (ReadFile(music_file,data,Mus_nextBlockRead,&bytesread,NULL)==FALSE) return 0;

  ds_music->Lock(Mus_lastWritePos,Mus_nextBlockSize,lockPtrs,lockSizes,lockPtrs+1,lockSizes+1,0);
  for (int j=0;j<2;j++)    
    {
    short *target=(short *)lockPtrs[j];
    for (DWORD i=0;i<lockSizes[j];i+=2)
      {
      
      short val=accum[curchan]+curMus.ampltable[*data++];
      accum[curchan]=val;
      if (data[-1]==0)  //pridano jako provizorni reseni pro korekci chyby komprimacniho programu
          {
          val-=31767;
          }
      if (fadetime)
        {
        int32_t ftime=FADELENGTH-(GetTickCount()-fadetime);
        if (ftime<0) ftime=0;
        float mul=(float)ftime*(float)ftime/(float)(FADELENGTH*FADELENGTH);
        val=(short)(val*mul);        
        }
      *target++=val;
      curchan++;
      if (curchan>=curMus.channels) curchan=0;
      }
    }
  ds_music->Unlock(lockPtrs[0],lockSizes[0],lockPtrs[1],lockSizes[1]);
  Mus_lastWritePos+=Mus_nextBlockSize;
  if (Mus_lastWritePos>=Mus_buffSize) Mus_lastWritePos-=Mus_buffSize;
  return 1;
  }

static char music_silent_play()
  {
  DWORD lockSizes[2];
  void *lockPtrs[2];

  ds_music->Lock(Mus_lastWritePos,Mus_nextBlockSize,lockPtrs,lockSizes,lockPtrs+1,lockSizes+1,0);
  for (int j=0;j<2;j++)    
    {
    short *target=(short *)lockPtrs[j];
    for (DWORD i=0;i<lockSizes[j];i+=2)
      {
      *target++=0;
      }
    }
  ds_music->Unlock(lockPtrs[0],lockSizes[0],lockPtrs[1],lockSizes[1]);
  Mus_lastWritePos+=Mus_nextBlockSize;
  if (Mus_lastWritePos>=Mus_buffSize) Mus_lastWritePos-=Mus_buffSize;
  return 1;
  }


void fade_music()
  {
  if (fadetime==0) fadetime=music_file?GetTickCount():0;
  }


static int mix_back_sound_worker()
  {
  DWORD bytesread;
  if (music_file==NULL) return -2;
  if (ds_music==NULL) 
    if (OpenMus(music_file)==0) return -2;
  DWORD play;
  ds_music->GetCurrentPosition(&play,NULL);
  while (true)
    {
    if (Mus_lastWritePos<=play) 
      {
      if (Mus_lastWritePos+Mus_nextBlockSize>=play) return 0;
      }
    else
      {
      if (Mus_lastWritePos+Mus_nextBlockSize>play+Mus_buffSize) return 0;
      } 
    if (Mus_silentPlay)
      {
      if (Mus_silentPlay>GetTickCount())
        {
        music_silent_play();
        return 0;
        }
      else
        return -2;
      }

    if (music_decompres_block()==0) return -2;
    curMus.blocks--;
    if (curMus.blocks<0) 
      {
      Mus_silentPlay=GetTickCount()+4000;
      return 0;      
      }
  if (ReadFile(music_file,&Mus_nextBlockRead,sizeof(Mus_nextBlockRead),&bytesread,NULL)==FALSE) return -2;
  if (ReadFile(music_file,&Mus_nextBlockSize,sizeof(Mus_nextBlockSize),&bytesread,NULL)==FALSE) return -2;
    if (fadetime && fadetime+FADELENGTH<GetTickCount())       
      {
      Mus_silentPlay=GetTickCount()+4000;
      return 0;      
      }

    }
  Mus_errorWait=0;
  return 0;
  }

  */
int mix_back_sound(int synchro)
  {
    if (GCurrentDecoder->IsPlaying()) return 0;
    char *next_music;

    konec_skladby(&next_music);
    change_music(next_music);
    return 0;
  }

void change_music(const char *mus_filename)
  {
    if (GCurrentDecoder->NotUsingOutput()) GMusDecoder.Stop();
    GCurrentDecoder->Stop();    
    if (mus_filename==0) 
    {
      mus_filename="?";
      GCurrentDecoder=&GMusDecoder;
    }
    else
    {
      const char *c=strrchr(mus_filename,'.');
      if (c!=0 && stricmp(c,".mus")==0)      
        GCurrentDecoder=&GMusDecoder;
      else
        GCurrentDecoder=&GWinAmpPlayer;
    }
    GCurrentDecoder->AttachOutput(&GMusicPlayer);    
    if (GCurrentDecoder->Play(mus_filename)==false) change_music(0);
    GCurrentDecoder->SetVolume(music_volume,get_snd_effect(SND_GVOLUME));
    if (GCurrentDecoder->NotUsingOutput())
      GMusDecoder.Play("?");
  }

  
int get_timer_value()
  {
  return GetTickCount()/TIMERSPEED
  }
char *device_name(int device)
  {
  if (device!=DEV_DIRECTSOUND) return "Unknown device!";
  else return "DirectSound 8";
  }
void force_music_volume(int volume)
  {
  }
void set_backsnd_freq(int bfreq)
  {
  }

void *PrepareVideoSound(int mixfreq, int buffsize)
  {
    WAVEFORMATEX wfex;
    wfex.cbSize=sizeof(wfex);
    wfex.wBitsPerSample=16;
    wfex.nBlockAlign=4;
    wfex.nSamplesPerSec=mixfreq;
    wfex.nAvgBytesPerSec=mixfreq*wfex.nBlockAlign;
    wfex.wFormatTag=WAVE_FORMAT_PCM;
    wfex.nChannels=2;
    
    DSBUFFERDESC desc;
    desc.dwSize=sizeof(desc);
    desc.dwBufferBytes=Mus_buffSize=buffsize;
    desc.dwReserved=0;
    desc.dwFlags=DSBCAPS_CTRLVOLUME ;
    desc.lpwfxFormat=&wfex;

    IDirectSoundBuffer *dsbf;
	IDirectSoundBuffer8 *ds_music;

    hres=ds8->CreateSoundBuffer(&desc,&dsbf,NULL);
    hres=dsbf->QueryInterface(IID_IDirectSoundBuffer8,(void **)&ds_music);
    dsbf->Release();    
    
    void *ptr;
    DWORD size;

    ds_music->Lock(0,0,&ptr,&size,NULL,NULL,DSBLOCK_ENTIREBUFFER);
    memset(ptr,0,size);
    ds_music->Unlock(ptr,size,NULL,NULL);
    ds_music->SetVolume(0);
    ds_music->Play(0,0,DSBPLAY_LOOPING);    
	return (void *)ds_music;
  }

char LoadNextVideoFrame(void *buffer, char *data, int size, short *xlat,short *accnums, int32_t *writepos)
  {
  IDirectSoundBuffer8 *ds_music=(IDirectSoundBuffer8 *)buffer;
  DSBCAPS caps;
  caps.dwSize=sizeof(caps);
  ds_music->GetCaps(&caps);
  DWORD play;
  ds_music->GetCurrentPosition(&play,NULL);
  int32_t remain=play-*writepos;
  if (remain<0) remain+=caps.dwBufferBytes;
  if (remain<size*2) return 0;
  char curchan=0;

  DWORD lockSizes[2];
  void *lockPtrs[2];

  ds_music->Lock(*writepos,size*2,lockPtrs,lockSizes,lockPtrs+1,lockSizes+1,0);
  for (int j=0;j<2;j++)    
    {
    short *target=(short *)lockPtrs[j];
    for (DWORD i=0;i<lockSizes[j];i+=2)
      {      
      int val=accnums[curchan]+xlat[*data++];
	  if (val>32767) 
		{accnums[curchan]-=val-32768;val=32767;}
	  if (val<-32767) 
		{accnums[curchan]-=val+32768;val=-32767;}
      accnums[curchan]=val;
/*      if (data[-1]==0)  //pridano jako provizorni reseni pro korekci chyby komprimacniho programu
          {
          val-=31767;
          }*/
      *target++=val;
      curchan++;
      if (curchan>=2) curchan=0;
      }
    }
  ds_music->Unlock(lockPtrs[0],lockSizes[0],lockPtrs[1],lockSizes[1]);
  writepos[0]+=size*2;
  if (writepos[0]>caps.dwBufferBytes) writepos[0]-=caps.dwBufferBytes;
  return 1;
  }


void DoneVideoSound(void *buffer)
  {
  IDirectSoundBuffer8 *ds_music=(IDirectSoundBuffer8 *)buffer;
  ds_music->Stop();
  ds_music->Release();
  }

}

