3 * (C) 2014-12-30 K.Ohta <whatisthis.sowhat@gmail.com>
11 #include "../../fileio.h"
12 #include "../../config.h"
13 #include "agar_logger.h"
20 uint32_t dwFormatSize;
23 uint32_t dwSamplesPerSec;
24 uint32_t dwAvgBytesPerSec;
26 uint16_t wBitsPerSample;
28 uint32_t dwDataLength;
33 int nSndDataLen, nSndDataPos, nSndWritePos;
36 SDL_sem *pSndApplySem;
39 SDL_AudioDeviceID nAudioDevid;
43 void AudioCallbackSDL(void *udata, Uint8 *stream, int len)
50 struct timespec req, remain;
56 sdl_snddata_t *pData = (sdl_snddata_t *)udata;
57 // printf("Called SND: %d %08x len = %d\n", SDL_GetTicks(), pData, len);
58 //if(pData == NULL) return;
61 req.tv_nsec = 500 * 1000; // 0.5ms
65 memset(stream, 0x00, len);
67 SDL_SemWait(*pData->pSndApplySem);
68 if(config.general_sound_level < -32768) config.general_sound_level = -32768;
69 if(config.general_sound_level > 32767) config.general_sound_level = 32767;
70 iTotalVolume = (uint8)(((uint32)(config.general_sound_level + 32768)) >> 9);
72 if(uBufSize <= nSndWritePos) { // Wrap
75 len2 = uBufSize - nSndWritePos;
77 SDL_SemPost(*pData->pSndApplySem);
80 if(len2 >= nSndDataLen) len2 = sndlen; // Okay
81 if((spos + len2) >= (len / sizeof(Sint16))) {
82 len2 = (len / sizeof(Sint16)) - spos;
84 if((nSndWritePos + len2) >= uBufSize ) len2 = uBufSize - nSndWritePos;
86 if((len2 > 0) && (sndlen > 0)){
87 writepos = nSndWritePos;
88 p = (Uint8 *)pSoundBuf;
89 SDL_SemPost(*pData->pSndApplySem);
91 s = &stream[spos * 2];
92 SDL_MixAudio(s, (Uint8 *)p, len2 * 2, iTotalVolume);
93 if(bSoundDebug) AGAR_DebugLog(AGAR_LOG_DEBUG, "SND:Time=%d,Callback,nSndWritePos=%d,spos=%d,len=%d,len2=%d", SDL_GetTicks(),
94 writepos, spos, len, len2);
95 SDL_SemWait(*pData->pSndApplySem);
97 if(nSndDataLen <= 0) nSndDataLen = 0;
99 SDL_SemPost(*pData->pSndApplySem);
102 SDL_SemPost(*pData->pSndApplySem);
103 if(spos >= (len / 2)) return;
104 // while(nSndDataLen <= 0) {
105 #if defined(Q_OS_WIN32)
108 nanosleep(&req, &remain); // Wait 500uS
119 void EMU::initialize_sound()
124 sound_ok = sound_started = now_mute = now_rec_sound = false;
133 // initialize direct sound
137 snddata.pSoundBuf = &pSoundBuf;
138 snddata.uBufSize = &uBufSize;
139 snddata.nSndWritePos = &nSndWritePos;
140 snddata.nSndDataLen = &nSndDataLen;
141 snddata.pSndApplySem = &pSndApplySem;
142 snddata.iTotalVolume = &iTotalVolume;
143 snddata.bSndExit = &bSndExit;
144 snddata.bSoundDebug = &bSoundDebug;
146 SndSpecReq.format = AUDIO_S16SYS;
147 SndSpecReq.channels = 2;
148 SndSpecReq.freq = sound_rate;
149 SndSpecReq.samples = ((sound_rate * 20) / 1000);
150 SndSpecReq.callback = AudioCallbackSDL;
151 SndSpecReq.userdata = (void *)&snddata;
152 for(i = 0; i < SDL_GetNumAudioDevices(0); i++) {
153 devname = SDL_GetAudioDeviceName(i, 0);
154 AGAR_DebugLog(AGAR_LOG_INFO, "Audio Device: %s", devname.c_str());
157 // nAudioDevid = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0,0), 0,
158 // &SndSpecReq, &SndSpecPresented, 0);
159 SDL_OpenAudio(&SndSpecReq, &SndSpecPresented);
163 uBufSize = (100 * SndSpecPresented.freq * SndSpecPresented.channels * 2) / 1000;
164 //uBufSize = sound_samples * 2;
165 pSoundBuf = (Sint16 *)malloc(uBufSize * sizeof(Sint16));
166 if(pSoundBuf == NULL) {
167 SDL_CloseAudioDevice(nAudioDevid);
170 pSndApplySem = SDL_CreateSemaphore(1);
171 if(pSndApplySem == NULL) {
176 AGAR_DebugLog(AGAR_LOG_INFO, "Sound OK: BufSize = %d", uBufSize);
177 ZeroMemory(pSoundBuf, uBufSize * sizeof(Sint16));
178 sound_ok = first_half = true;
179 SDL_PauseAudioDevice(nAudioDevid, 0);
182 void EMU::release_sound()
184 // release direct sound
186 SDL_CloseAudioDevice(nAudioDevid);
187 if(pSndApplySem != NULL) {
188 //SDL_SemWait(pSndApplySem);
189 //SDL_SemPost(pSndApplySem);
190 SDL_DestroySemaphore(pSndApplySem);
192 if(pSoundBuf != NULL) free(pSoundBuf);
197 void EMU::update_sound(int* extra_frames)
208 uint32_t play_c, offset, size1, size2;
212 // check current position
213 play_c = nSndWritePos;
215 if(play_c < (uBufSize / 2)) {
220 if(play_c >= (uBufSize / 2)) {
223 offset = uBufSize / 2;
226 // sound buffer must be updated
227 Sint16* sound_buffer = (Sint16 *)vm->create_sound(extra_frames);
230 if(sound_samples > rec_buffer_ptr) {
231 int samples = sound_samples - rec_buffer_ptr;
232 int length = samples * sizeof(uint16) * 2; // stereo
233 rec->Fwrite(sound_buffer + rec_buffer_ptr * 2, length, 1);
236 // sync video recording
237 static double frames = 0;
238 static int prev_samples = -1;
239 #ifdef SUPPORT_VARIABLE_TIMING
240 static double prev_fps = -1;
241 double fps = vm->frame_rate();
242 if(prev_samples != samples || prev_fps != fps) {
243 prev_samples = samples;
245 frames = fps * (double)samples / (double)sound_rate;
248 if(prev_samples != samples) {
249 prev_samples = samples;
250 frames = FRAMES_PER_SEC * (double)samples / (double)sound_rate;
253 rec_video_frames -= frames;
254 if(rec_video_frames > 2) {
255 rec_video_run_frames -= (rec_video_frames - 2);
256 } else if(rec_video_frames < -2) {
257 rec_video_run_frames -= (rec_video_frames + 2);
259 // rec_video_run_frames -= rec_video_frames;
269 SDL_SemWait(*snddata.pSndApplySem);
270 ssize = sound_samples * SndSpecPresented.channels;
273 ptr1 = &pSoundBuf[pos];
274 //if(nSndDataLen < uBufSize) {
275 if(pos2 >= uBufSize) {
276 size1 = uBufSize - pos;
277 size2 = pos2 - uBufSize;
278 ptr2 = &pSoundBuf[0];
285 memcpy(ptr1, sound_buffer, size1 * sizeof(Sint16));
288 memcpy(ptr2, sound_buffer + size1, size2 * sizeof(Sint16));
290 nSndDataLen = nSndDataLen + ssize;
291 if(nSndDataLen >= uBufSize) nSndDataLen = uBufSize;
292 //printf("samples = %d\n", sound_samples);
293 nSndDataPos = nSndDataPos + ssize;
294 if(nSndDataPos >= uBufSize) nSndDataPos = nSndDataPos - uBufSize;
296 SDL_SemPost(*snddata.pSndApplySem);
298 // SDL_PauseAudioDevice(nAudioDevid, 0);
301 // SDL_PauseAudioDevice(nAudioDevid, 0);
302 first_half = !first_half;
306 void EMU::mute_sound()
308 if(!now_mute && sound_ok) {
309 // check current position
310 uint32_t size1, size2;
317 // if(pSndApplySem) {
318 SDL_SemWait(*snddata.pSndApplySem);
320 // ssize = sound_samples * SndSpecPresented.channels;
321 ssize = uBufSize / 2;
324 ptr1 = &pSoundBuf[pos];
325 if(pos2 >= uBufSize) {
326 size1 = uBufSize - pos;
327 size2 = pos2 - uBufSize;
328 ptr2 = &pSoundBuf[0];
336 ZeroMemory(ptr1, size1 * sizeof(Sint16));
339 ZeroMemory(ptr2, size2 * sizeof(Sint16));
341 nSndDataPos = (nSndDataPos + ssize) % uBufSize;
342 // SDL_UnlockAudio();
343 SDL_SemPost(*snddata.pSndApplySem);
345 // SDL_PauseAudioDevice(nAudioDevid, 0);
350 void EMU::start_rec_sound()
357 //GetLocalTime(&sTime);
360 tnow = std::time(NULL);
361 tm = std::localtime(&tnow);
363 sprintf(sound_file_name, _T("%d-%0.2d-%0.2d_%0.2d-%0.2d-%0.2d.wav"), tm->tm_year + 1900, tm->tm_mon + 1,
364 tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
368 if(rec->Fopen(bios_path(sound_file_name), FILEIO_WRITE_BINARY)) {
369 // write dummy wave header
371 memset(&header, 0, sizeof(wavheader_t));
372 rec->Fwrite(&header, sizeof(wavheader_t), 1);
374 rec_buffer_ptr = vm->sound_buffer_ptr();
375 now_rec_sound = true;
377 // failed to open the wave file
384 void EMU::stop_rec_sound()
390 rec->RemoveFile(sound_file_name);
392 // update wave header
395 header.dwRIFF = EndianToLittle_DWORD(0x46464952);
396 header.dwFileSize = EndianToLittle_DWORD(rec_bytes + sizeof(wavheader_t) - 8);
397 header.dwWAVE = EndianToLittle_DWORD(0x45564157);
398 header.dwfmt_ = EndianToLittle_DWORD(0x20746d66);
399 header.dwFormatSize = EndianToLittle_DWORD(16);
400 header.wFormatTag = EndianToLittle_WORD(1);
401 header.wChannels = EndianToLittle_WORD(2);
402 header.wBitsPerSample = EndianToLittle_WORD(16);
403 header.dwSamplesPerSec = EndianToLittle_DWORD(sound_rate);
404 header.wBlockAlign = EndianToLittle_WORD(header.wChannels * header.wBitsPerSample / 8);
405 header.dwAvgBytesPerSec = EndianToLittle_DWORD(header.dwSamplesPerSec * header.wBlockAlign);
406 header.dwdata = EndianToLittle_DWORD(0x61746164);
407 header.dwDataLength = EndianToLittle_DWORD(rec_bytes);
408 rec->Fseek(0, FILEIO_SEEK_SET);
409 rec->Fwrite(&header, sizeof(wavheader_t), 1);
413 now_rec_sound = false;
418 void EMU::restart_rec_sound()
420 bool tmp = now_rec_sound;
422 if(tmp) start_rec_sound();