1 /* 2 audio.directsound (2007-12-26) 3 author: byuu 4 */ 5 6 #include <dsound.h> 7 8 namespace ruby { 9 10 class pAudioDS { 11 public: 12 LPDIRECTSOUND ds; 13 LPDIRECTSOUNDBUFFER dsb_p, dsb_b; 14 DSBUFFERDESC dsbd; 15 WAVEFORMATEX wfx; 16 17 struct { 18 unsigned rings; 19 unsigned latency; 20 21 uint32_t *buffer; 22 unsigned bufferoffset; 23 24 unsigned readring; 25 unsigned writering; 26 int distance; 27 } device; 28 29 struct { 30 HWND handle; 31 bool synchronize; 32 unsigned frequency; 33 unsigned latency; 34 } settings; 35 cap(const string & name)36 bool cap(const string& name) { 37 if(name == Audio::Handle) return true; 38 if(name == Audio::Synchronize) return true; 39 if(name == Audio::Frequency) return true; 40 if(name == Audio::Latency) return true; 41 return false; 42 } 43 get(const string & name)44 any get(const string& name) { 45 if(name == Audio::Handle) return (uintptr_t)settings.handle; 46 if(name == Audio::Synchronize) return settings.synchronize; 47 if(name == Audio::Frequency) return settings.frequency; 48 if(name == Audio::Latency) return settings.latency; 49 return false; 50 } 51 set(const string & name,const any & value)52 bool set(const string& name, const any& value) { 53 if(name == Audio::Handle) { 54 settings.handle = (HWND)any_cast<uintptr_t>(value); 55 return true; 56 } 57 58 if(name == Audio::Synchronize) { 59 settings.synchronize = any_cast<bool>(value); 60 if(ds) clear(); 61 return true; 62 } 63 64 if(name == Audio::Frequency) { 65 settings.frequency = any_cast<unsigned>(value); 66 if(ds) init(); 67 return true; 68 } 69 70 if(name == Audio::Latency) { 71 settings.latency = any_cast<unsigned>(value); 72 if(ds) init(); 73 return true; 74 } 75 76 return false; 77 } 78 sample(uint16_t left,uint16_t right)79 void sample(uint16_t left, uint16_t right) { 80 device.buffer[device.bufferoffset++] = left + (right << 16); 81 if(device.bufferoffset < device.latency) return; 82 device.bufferoffset = 0; 83 84 DWORD pos, size; 85 void *output; 86 87 if(settings.synchronize == true) { 88 //wait until playback buffer has an empty ring to write new audio data to 89 while(device.distance >= device.rings - 1) { 90 dsb_b->GetCurrentPosition(&pos, 0); 91 unsigned activering = pos / (device.latency * 4); 92 if(activering == device.readring) { 93 if(settings.synchronize == false) Sleep(1); 94 continue; 95 } 96 97 //subtract number of played rings from ring distance counter 98 device.distance -= (device.rings + activering - device.readring) % device.rings; 99 device.readring = activering; 100 101 if(device.distance < 2) { 102 //buffer underflow; set max distance to recover quickly 103 device.distance = device.rings - 1; 104 device.writering = (device.rings + device.readring - 1) % device.rings; 105 break; 106 } 107 } 108 } 109 110 device.writering = (device.writering + 1) % device.rings; 111 device.distance = (device.distance + 1) % device.rings; 112 113 if(dsb_b->Lock(device.writering * device.latency * 4, device.latency * 4, &output, &size, 0, 0, 0) == DS_OK) { 114 memcpy(output, device.buffer, device.latency * 4); 115 dsb_b->Unlock(output, size, 0, 0); 116 } 117 } 118 clear()119 void clear() { 120 device.readring = 0; 121 device.writering = device.rings - 1; 122 device.distance = device.rings - 1; 123 124 device.bufferoffset = 0; 125 if(device.buffer) memset(device.buffer, 0, device.latency * device.rings * 4); 126 127 if(!dsb_b) return; 128 dsb_b->Stop(); 129 dsb_b->SetCurrentPosition(0); 130 131 DWORD size; 132 void *output; 133 dsb_b->Lock(0, device.latency * device.rings * 4, &output, &size, 0, 0, 0); 134 memset(output, 0, size); 135 dsb_b->Unlock(output, size, 0, 0); 136 137 dsb_b->Play(0, 0, DSBPLAY_LOOPING); 138 } 139 init()140 bool init() { 141 term(); 142 143 device.rings = 8; 144 device.latency = settings.frequency * settings.latency / device.rings / 1000.0 + 0.5; 145 device.buffer = new uint32_t[device.latency * device.rings]; 146 device.bufferoffset = 0; 147 148 DirectSoundCreate(0, &ds, 0); 149 ds->SetCooperativeLevel((HWND)settings.handle, DSSCL_PRIORITY); 150 151 memset(&dsbd, 0, sizeof(dsbd)); 152 dsbd.dwSize = sizeof(dsbd); 153 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; 154 dsbd.dwBufferBytes = 0; 155 dsbd.lpwfxFormat = 0; 156 ds->CreateSoundBuffer(&dsbd, &dsb_p, 0); 157 158 memset(&wfx, 0, sizeof(wfx)); 159 wfx.wFormatTag = WAVE_FORMAT_PCM; 160 wfx.nChannels = 2; 161 wfx.nSamplesPerSec = settings.frequency; 162 wfx.wBitsPerSample = 16; 163 wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; 164 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; 165 dsb_p->SetFormat(&wfx); 166 167 memset(&dsbd, 0, sizeof(dsbd)); 168 dsbd.dwSize = sizeof(dsbd); 169 dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE; 170 dsbd.dwBufferBytes = device.latency * device.rings * sizeof(uint32_t); 171 dsbd.guid3DAlgorithm = GUID_NULL; 172 dsbd.lpwfxFormat = &wfx; 173 ds->CreateSoundBuffer(&dsbd, &dsb_b, 0); 174 dsb_b->SetFrequency(settings.frequency); 175 dsb_b->SetCurrentPosition(0); 176 177 clear(); 178 return true; 179 } 180 term()181 void term() { 182 if(device.buffer) { 183 delete[] device.buffer; 184 device.buffer = 0; 185 } 186 187 if(dsb_b) { dsb_b->Stop(); dsb_b->Release(); dsb_b = 0; } 188 if(dsb_p) { dsb_p->Stop(); dsb_p->Release(); dsb_p = 0; } 189 if(ds) { ds->Release(); ds = 0; } 190 } 191 pAudioDS()192 pAudioDS() { 193 ds = 0; 194 dsb_p = 0; 195 dsb_b = 0; 196 197 device.buffer = 0; 198 device.bufferoffset = 0; 199 device.readring = 0; 200 device.writering = 0; 201 device.distance = 0; 202 203 settings.handle = GetDesktopWindow(); 204 settings.synchronize = false; 205 settings.frequency = 22050; 206 settings.latency = 120; 207 } 208 }; 209 210 DeclareAudio(DS) 211 212 }; 213