OSDN Git Service

[BUILD][Qt][WIN32] Build environment with MINGW / Qt, with GNU/Linux host, but, still...
[csp-qt/common_source_project-fm7.git] / source / src / qt / common / sdl_sound.cpp
1 /*
2  * SDL Sound Handler
3  * (C) 2014-12-30 K.Ohta <whatisthis.sowhat@gmail.com>
4  */
5
6 //#include <SDL/SDL.h>
7 #include <ctime>
8 #include <string>
9 #include "emu.h"
10 #include "vm/vm.h"
11 #include "../../fileio.h"
12 #include "../../config.h"
13 #include "agar_logger.h"
14
15 typedef struct {
16         uint32_t dwRIFF;
17         uint32_t dwFileSize;
18         uint32_t dwWAVE;
19         uint32_t dwfmt_;
20         uint32_t dwFormatSize;
21         uint16_t wFormatTag;
22         uint16_t wChannels;
23         uint32_t dwSamplesPerSec;
24         uint32_t dwAvgBytesPerSec;
25         uint16_t wBlockAlign;
26         uint16_t wBitsPerSample;
27         uint32_t dwdata;
28         uint32_t dwDataLength;
29 } wavheader_t;
30
31 extern "C" {
32         int uBufSize;
33         int nSndDataLen, nSndDataPos, nSndWritePos;
34         bool bSndExit;
35         bool bSoundDebug;
36         SDL_sem *pSndApplySem;
37         Sint16 *pSoundBuf;
38         Uint8 iTotalVolume;
39         SDL_AudioDeviceID nAudioDevid;
40 }
41
42
43 void AudioCallbackSDL(void *udata, Uint8 *stream, int len)
44 {
45         int pos;
46         int blen = len;
47         int len2 = len;
48         int channels = 2;
49         int spos;
50         struct timespec req, remain;
51         Uint8 *p;
52         Uint8 *s;
53         int writepos;
54         int sndlen;
55         
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;
59         
60         req.tv_sec = 0;
61         req.tv_nsec = 500 * 1000; //  0.5ms
62         
63         if(len <= 0) return;
64         spos = 0;
65         memset(stream, 0x00, len);
66         do {
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);
71                 sndlen = nSndDataLen;
72                 if(uBufSize  <= nSndWritePos) { // Wrap
73                         nSndWritePos = 0;
74                 }
75                 len2 = uBufSize - nSndWritePos;
76                 if(bSndExit) {
77                         SDL_SemPost(*pData->pSndApplySem);
78                         return;
79                 }
80                 if(len2 >= nSndDataLen) len2 = sndlen;  // Okay
81                 if((spos + len2) >= (len / sizeof(Sint16))) {
82                         len2 = (len / sizeof(Sint16)) - spos;
83                 }
84                 if((nSndWritePos + len2) >= uBufSize ) len2 = uBufSize - nSndWritePos;
85                 
86                 if((len2 > 0) && (sndlen > 0)){
87                         writepos = nSndWritePos;
88                         p = (Uint8 *)pSoundBuf;
89                         SDL_SemPost(*pData->pSndApplySem);
90                         p = &p[writepos * 2];
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);
96                         nSndDataLen -= len2;
97                         if(nSndDataLen <= 0) nSndDataLen = 0;
98                         nSndWritePos += len2;
99                         SDL_SemPost(*pData->pSndApplySem);
100                 } else {
101                         len2 = 0;
102                         SDL_SemPost(*pData->pSndApplySem);
103                         if(spos >= (len / 2)) return;
104                         //         while(nSndDataLen <= 0) {
105 #if defined(Q_OS_WIN32)
106                         SDL_Delay(1);
107 #else
108                         nanosleep(&req, &remain); // Wait 500uS
109 #endif
110                         if(bSndExit) return;
111                         //         }
112                 }
113                 spos += len2;
114         } while(spos < len); 
115 }
116
117
118
119 void EMU::initialize_sound()
120 {
121         std::string devname;
122         int i;
123
124         sound_ok = sound_started = now_mute = now_rec_sound = false;
125         rec_buffer_ptr = 0;
126         nSndWritePos = 0;
127         nSndDataLen = 0;
128         uBufSize = 0;
129         bSndExit = false;
130         bSoundDebug = false;
131         pSoundBuf = NULL;
132         pSndApplySem = NULL;
133         // initialize direct sound
134
135         iTotalVolume = 127;
136    
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;
145
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());
155         }
156    
157 //        nAudioDevid = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0,0), 0,
158 //                                        &SndSpecReq, &SndSpecPresented, 0);
159         SDL_OpenAudio(&SndSpecReq, &SndSpecPresented);
160         nAudioDevid = 1;
161    
162         // secondary buffer
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);
168                 return;
169         }
170         pSndApplySem = SDL_CreateSemaphore(1);
171         if(pSndApplySem == NULL) {
172                 free(pSoundBuf);
173                 pSoundBuf = NULL;
174                 return;
175         }
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);
180 }
181
182 void EMU::release_sound()
183 {
184         // release direct sound
185         bSndExit = TRUE;
186         SDL_CloseAudioDevice(nAudioDevid);
187         if(pSndApplySem != NULL) {
188            //SDL_SemWait(pSndApplySem);
189            //SDL_SemPost(pSndApplySem);
190            SDL_DestroySemaphore(pSndApplySem);
191         }
192         if(pSoundBuf != NULL) free(pSoundBuf);
193         // stop recording
194         stop_rec_sound();
195 }
196
197 void EMU::update_sound(int* extra_frames)
198 {
199         *extra_frames = 0;
200 #ifdef USE_DEBUGGER
201         if(now_debugging) {
202                 return;
203         }
204 #endif
205         now_mute = false;
206         
207         if(sound_ok) {
208                 uint32_t play_c, offset, size1, size2;
209                 Sint16 *ptr1, *ptr2;
210                 
211                 // start play
212                 // check current position
213                 play_c = nSndWritePos;
214                 if(!first_half) {
215                         if(play_c < (uBufSize / 2)) {
216                                 return;
217                         }
218                         offset = 0;
219                 } else {
220                         if(play_c >= (uBufSize / 2)) {
221                                 return;
222                         }
223                         offset = uBufSize / 2;
224                 }
225                 //SDL_UnlockAudio();
226                 // sound buffer must be updated
227                 Sint16* sound_buffer = (Sint16 *)vm->create_sound(extra_frames);
228                 if(now_rec_sound) {
229                         // record sound
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);
234                                 rec_bytes += length;
235                                 if(now_rec_video) {
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;
244                                                 prev_fps = fps;
245                                                 frames = fps * (double)samples / (double)sound_rate;
246                                         }
247 #else
248                                         if(prev_samples != samples) {
249                                                 prev_samples = samples;
250                                                 frames = FRAMES_PER_SEC * (double)samples / (double)sound_rate;
251                                         }
252 #endif
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);
258                                         }
259 //                                      rec_video_run_frames -= rec_video_frames;
260                                 }
261                         }
262                         rec_buffer_ptr = 0;
263                 }
264                 if(sound_buffer) {
265                         int ssize;
266                         int pos;
267                         int pos2;
268                         if(pSndApplySem) {
269                                 SDL_SemWait(*snddata.pSndApplySem);
270                                 ssize = sound_samples * SndSpecPresented.channels;
271                                 pos = nSndDataPos;
272                                 pos2 = pos + ssize;
273                                 ptr1 = &pSoundBuf[pos];
274                                 //if(nSndDataLen < uBufSize) { 
275                                 if(pos2 >= uBufSize) {
276                                         size1 = uBufSize  - pos;
277                                         size2 = pos2 - uBufSize;
278                                         ptr2 = &pSoundBuf[0];
279                                 } else {
280                                         size1 = ssize;
281                                         size2 = 0;
282                                         ptr2 = NULL;
283                                 }
284                                 if(ptr1) {
285                                         memcpy(ptr1, sound_buffer, size1 * sizeof(Sint16));
286                                 }
287                                 if(ptr2) {
288                                         memcpy(ptr2, sound_buffer + size1, size2 * sizeof(Sint16));
289                                 }
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;
295                                 //}
296                                 SDL_SemPost(*snddata.pSndApplySem);
297                         }
298 //                      SDL_PauseAudioDevice(nAudioDevid, 0);
299                 }
300            
301 //              SDL_PauseAudioDevice(nAudioDevid, 0);
302                 first_half = !first_half;
303         }
304 }
305
306 void EMU::mute_sound()
307 {
308         if(!now_mute && sound_ok) {
309                 // check current position
310                 uint32_t size1, size2;
311                 
312                 Sint16 *ptr1, *ptr2;
313                 // WIP
314                 int ssize;
315                 int pos;
316                 int pos2;
317 //              if(pSndApplySem) { 
318                         SDL_SemWait(*snddata.pSndApplySem);
319 //                      SDL_LockAudio();
320 //                      ssize = sound_samples * SndSpecPresented.channels;
321                         ssize = uBufSize / 2;
322                         pos = nSndDataPos;
323                         pos2 = pos + ssize;
324                         ptr1 = &pSoundBuf[pos];
325                         if(pos2 >= uBufSize) {
326                                 size1 = uBufSize - pos;
327                                 size2 = pos2 - uBufSize;
328                                 ptr2 = &pSoundBuf[0];
329                         } else {
330                                 size1 = ssize;
331                                 size2 = 0;
332                                 ptr2 = NULL;
333                         }
334
335                         if(ptr1) {
336                                 ZeroMemory(ptr1, size1 * sizeof(Sint16));
337                         }
338                         if(ptr2) {
339                                 ZeroMemory(ptr2, size2 * sizeof(Sint16));
340                         }
341                         nSndDataPos = (nSndDataPos + ssize) % uBufSize;
342 //                      SDL_UnlockAudio();
343                         SDL_SemPost(*snddata.pSndApplySem);
344 //              }
345 //              SDL_PauseAudioDevice(nAudioDevid, 0);
346         }
347         now_mute = true;
348 }
349
350 void EMU::start_rec_sound()
351 {
352    
353         if(!now_rec_sound) {
354                 LockVM();
355                 // create file name
356                 //SYSTEMTIME sTime;
357                 //GetLocalTime(&sTime);
358                 std::tm *tm;
359                 std::time_t tnow;
360                 tnow = std::time(NULL);
361                 tm = std::localtime(&tnow);
362                 
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);
365                 
366                 // create wave file
367                 rec = new FILEIO();
368                 if(rec->Fopen(bios_path(sound_file_name), FILEIO_WRITE_BINARY)) {
369                         // write dummy wave header
370                         wavheader_t header;
371                         memset(&header, 0, sizeof(wavheader_t));
372                         rec->Fwrite(&header, sizeof(wavheader_t), 1);
373                         rec_bytes = 0;
374                         rec_buffer_ptr = vm->sound_buffer_ptr();
375                         now_rec_sound = true;
376                 } else {
377                         // failed to open the wave file
378                         delete rec;
379                 }
380                 UnlockVM();
381         }
382 }
383
384 void EMU::stop_rec_sound()
385 {
386         if(now_rec_sound) {
387                 LockVM();
388                 if(rec_bytes == 0) {
389                         rec->Fclose();
390                         rec->RemoveFile(sound_file_name);
391                 } else {
392                         // update wave header
393                         wavheader_t header;
394
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);
410                         rec->Fclose();
411                 }
412                 delete rec;
413                 now_rec_sound = false;
414                 UnlockVM();
415         }
416 }
417
418 void EMU::restart_rec_sound()
419 {
420         bool tmp = now_rec_sound;
421         stop_rec_sound();
422         if(tmp) start_rec_sound();
423 }
424