OSDN Git Service

[General][Qt] Use AGAR_LoggerFoo() to log messages.
[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 "agar_logger.h"
13
14 typedef struct {
15         DWORD dwRIFF;
16         DWORD dwFileSize;
17         DWORD dwWAVE;
18         DWORD dwfmt_;
19         DWORD dwFormatSize;
20         WORD wFormatTag;
21         WORD wChannels;
22         DWORD dwSamplesPerSec;
23         DWORD dwAvgBytesPerSec;
24         WORD wBlockAlign;
25         WORD wBitsPerSample;
26         DWORD dwdata;
27         DWORD dwDataLength;
28 } wavheader_t;
29
30 extern "C" {
31         int uBufSize;
32         int nSndDataLen, nSndDataPos, nSndWritePos;
33         bool bSndExit;
34         bool bSoundDebug;
35         SDL_sem *pSndApplySem;
36         Sint16 *pSoundBuf;
37         Uint8 iTotalVolume;
38         SDL_AudioDeviceID nAudioDevid;
39 }
40
41
42 void AudioCallbackSDL(void *udata, Uint8 *stream, int len)
43 {
44         int pos;
45         int blen = len;
46         int len2 = len;
47         int channels = 2;
48         int spos;
49         struct timespec req, remain;
50         Uint8 *p;
51         Uint8 *s;
52         int writepos;
53         int sndlen;
54         
55         sdl_snddata_t *pData = (sdl_snddata_t *)udata;
56         //   printf("Called SND: %d %08x len = %d\n", SDL_GetTicks(), pData, len);
57         //if(pData == NULL) return;
58         
59         req.tv_sec = 0;
60         req.tv_nsec = 500 * 1000; //  0.5ms
61         
62         if(len <= 0) return;
63         spos = 0;
64         memset(stream, 0x00, len);
65         do {
66                 SDL_SemWait(*pData->pSndApplySem);
67                 sndlen = nSndDataLen;
68                 if(uBufSize  <= nSndWritePos) { // Wrap
69                         nSndWritePos = 0;
70                 }
71                 len2 = uBufSize - nSndWritePos;
72                 if(bSndExit) {
73                         SDL_SemPost(*pData->pSndApplySem);
74                         return;
75                 }
76                 if(len2 >= nSndDataLen) len2 = sndlen;  // Okay
77                 if((spos + len2) >= (len / sizeof(Sint16))) {
78                         len2 = (len / sizeof(Sint16)) - spos;
79                 }
80                 if((nSndWritePos + len2) >= uBufSize ) len2 = uBufSize - nSndWritePos;
81                 
82                 if((len2 > 0) && (sndlen > 0)){
83                         writepos = nSndWritePos;
84                         p = (Uint8 *)pSoundBuf;
85                         SDL_SemPost(*pData->pSndApplySem);
86                         p = &p[writepos * 2];
87                         s = &stream[spos * 2];
88                         SDL_MixAudio(s, (Uint8 *)p, len2 * 2, iTotalVolume);
89                         if(bSoundDebug) AGAR_DebugLog(AGAR_LOG_DEBUG, "SND:Time=%d,Callback,nSndWritePos=%d,spos=%d,len=%d,len2=%d", SDL_GetTicks(), 
90                                   writepos, spos, len, len2);
91                         SDL_SemWait(*pData->pSndApplySem);
92                         nSndDataLen -= len2;
93                         if(nSndDataLen <= 0) nSndDataLen = 0;
94                         nSndWritePos += len2;
95                         SDL_SemPost(*pData->pSndApplySem);
96                 } else {
97                         len2 = 0;
98                         SDL_SemPost(*pData->pSndApplySem);
99                         if(spos >= (len / 2)) return;
100                         //         while(nSndDataLen <= 0) {
101                         nanosleep(&req, &remain); // Wait 500uS
102                         if(bSndExit) return;
103                         //         }
104                 }
105                 spos += len2;
106         } while(spos < len); 
107 }
108
109
110
111 void EMU::initialize_sound()
112 {
113         std::string devname;
114         int i;
115
116         sound_ok = sound_started = now_mute = now_rec_sound = false;
117         rec_buffer_ptr = 0;
118         nSndWritePos = 0;
119         nSndDataLen = 0;
120         uBufSize = 0;
121         bSndExit = false;
122         bSoundDebug = false;
123         pSoundBuf = NULL;
124         pSndApplySem = NULL;
125         // initialize direct sound
126
127         iTotalVolume = 127;
128    
129         snddata.pSoundBuf = &pSoundBuf;
130         snddata.uBufSize = &uBufSize;
131         snddata.nSndWritePos = &nSndWritePos;
132         snddata.nSndDataLen = &nSndDataLen;
133         snddata.pSndApplySem = &pSndApplySem;
134         snddata.iTotalVolume = &iTotalVolume;
135         snddata.bSndExit = &bSndExit;
136         snddata.bSoundDebug = &bSoundDebug;
137
138         SndSpecReq.format = AUDIO_S16SYS;
139         SndSpecReq.channels = 2;
140         SndSpecReq.freq = sound_rate;
141         SndSpecReq.samples = ((sound_rate * 20) / 1000);
142         SndSpecReq.callback = AudioCallbackSDL;
143         SndSpecReq.userdata = (void *)&snddata;
144         for(i = 0; i < SDL_GetNumAudioDevices(0); i++) {
145                 devname = SDL_GetAudioDeviceName(i, 0);
146                 AGAR_DebugLog(AGAR_LOG_INFO, "Audio Device: %s", devname.c_str());
147         }
148    
149 //        nAudioDevid = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0,0), 0,
150 //                                        &SndSpecReq, &SndSpecPresented, 0);
151         SDL_OpenAudio(&SndSpecReq, &SndSpecPresented);
152         nAudioDevid = 1;
153    
154         // secondary buffer
155         uBufSize = (100 * SndSpecPresented.freq * SndSpecPresented.channels * 2) / 1000;
156         //uBufSize = sound_samples * 2;
157         pSoundBuf = (Sint16 *)malloc(uBufSize * sizeof(Sint16)); 
158         if(pSoundBuf == NULL) {
159                 SDL_CloseAudioDevice(nAudioDevid);
160                 return;
161         }
162         pSndApplySem = SDL_CreateSemaphore(1);
163         if(pSndApplySem == NULL) {
164                 free(pSoundBuf);
165                 pSoundBuf = NULL;
166                 return;
167         }
168         AGAR_DebugLog(AGAR_LOG_INFO, "Sound OK: BufSize = %d", uBufSize);
169         ZeroMemory(pSoundBuf, uBufSize * sizeof(Sint16));
170         sound_ok = first_half = true;
171         SDL_PauseAudioDevice(nAudioDevid, 0);
172 }
173
174 void EMU::release_sound()
175 {
176         // release direct sound
177         bSndExit = TRUE;
178         SDL_CloseAudioDevice(nAudioDevid);
179         if(pSndApplySem != NULL) {
180            //SDL_SemWait(pSndApplySem);
181            //SDL_SemPost(pSndApplySem);
182            SDL_DestroySemaphore(pSndApplySem);
183         }
184         if(pSoundBuf != NULL) free(pSoundBuf);
185         // stop recording
186         stop_rec_sound();
187 }
188
189 void EMU::update_sound(int* extra_frames)
190 {
191         *extra_frames = 0;
192 #ifdef USE_DEBUGGER
193 //      if(now_debugging) {
194 //              return;
195 //      }
196 #endif
197         now_mute = false;
198         
199         if(sound_ok) {
200                 DWORD play_c, offset, size1, size2;
201                 Sint16 *ptr1, *ptr2;
202                 
203                 // start play
204                 // check current position
205                 play_c = nSndWritePos;
206                 if(!first_half) {
207                         if(play_c < (uBufSize / 2)) {
208                                 return;
209                         }
210                         offset = 0;
211                 } else {
212                         if(play_c >= (uBufSize / 2)) {
213                                 return;
214                         }
215                         offset = uBufSize / 2;
216                 }
217                 //SDL_UnlockAudio();
218                 // sound buffer must be updated
219                 Sint16* sound_buffer = (Sint16 *)vm->create_sound(extra_frames);
220                 if(now_rec_sound) {
221                         // record sound
222                         if(sound_samples > rec_buffer_ptr) {
223                                 int samples = sound_samples - rec_buffer_ptr;
224                                 int length = samples * sizeof(uint16) * 2; // stereo
225                                 rec->Fwrite(sound_buffer + rec_buffer_ptr * 2, length, 1);
226                                 rec_bytes += length;
227                                 if(now_rec_video) {
228                                         // sync video recording
229                                         static double frames = 0;
230                                         static int prev_samples = -1;
231 #ifdef SUPPORT_VARIABLE_TIMING
232                                         static double prev_fps = -1;
233                                         double fps = vm->frame_rate();
234                                         if(prev_samples != samples || prev_fps != fps) {
235                                                 prev_samples = samples;
236                                                 prev_fps = fps;
237                                                 frames = fps * (double)samples / (double)sound_rate;
238                                         }
239 #else
240                                         if(prev_samples != samples) {
241                                                 prev_samples = samples;
242                                                 frames = FRAMES_PER_SEC * (double)samples / (double)sound_rate;
243                                         }
244 #endif
245                                         rec_video_frames -= frames;
246                                         if(rec_video_frames > 2) {
247                                                 rec_video_run_frames -= (rec_video_frames - 2);
248                                         } else if(rec_video_frames < -2) {
249                                                 rec_video_run_frames -= (rec_video_frames + 2);
250                                         }
251 //                                      rec_video_run_frames -= rec_video_frames;
252                                 }
253                         }
254                         rec_buffer_ptr = 0;
255                 }
256                 if(sound_buffer) {
257                         int ssize;
258                         int pos;
259                         int pos2;
260                         if(pSndApplySem) {
261                                 SDL_SemWait(*snddata.pSndApplySem);
262                                 ssize = sound_samples * SndSpecPresented.channels;
263                                 pos = nSndDataPos;
264                                 pos2 = pos + ssize;
265                                 ptr1 = &pSoundBuf[pos];
266                                 //if(nSndDataLen < uBufSize) { 
267                                 if(pos2 >= uBufSize) {
268                                         size1 = uBufSize  - pos;
269                                         size2 = pos2 - uBufSize;
270                                         ptr2 = &pSoundBuf[0];
271                                 } else {
272                                         size1 = ssize;
273                                         size2 = 0;
274                                         ptr2 = NULL;
275                                 }
276                                 if(ptr1) {
277                                         memcpy(ptr1, sound_buffer, size1 * sizeof(Sint16));
278                                 }
279                                 if(ptr2) {
280                                         memcpy(ptr2, sound_buffer + size1, size2 * sizeof(Sint16));
281                                 }
282                                 nSndDataLen = nSndDataLen + ssize;
283                                 if(nSndDataLen >= uBufSize) nSndDataLen = uBufSize;
284                                    //printf("samples = %d\n", sound_samples);
285                                 nSndDataPos = nSndDataPos + ssize;
286                                 if(nSndDataPos >= uBufSize) nSndDataPos = nSndDataPos - uBufSize;
287                                 //}
288                                 SDL_SemPost(*snddata.pSndApplySem);
289                         }
290 //                      SDL_PauseAudioDevice(nAudioDevid, 0);
291                 }
292            
293                 SDL_PauseAudioDevice(nAudioDevid, 0);
294                 first_half = !first_half;
295         }
296 }
297
298 void EMU::mute_sound()
299 {
300         if(!now_mute && sound_ok) {
301                 // check current position
302                 DWORD size1, size2;
303                 
304                 Sint16 *ptr1, *ptr2;
305                 // WIP
306                 int ssize;
307                 int pos;
308                 int pos2;
309 //              if(pSndApplySem) { 
310                         SDL_SemWait(*snddata.pSndApplySem);
311 //                      SDL_LockAudio();
312 //                      ssize = sound_samples * SndSpecPresented.channels;
313                         ssize = uBufSize / 2;
314                         pos = nSndDataPos;
315                         pos2 = pos + ssize;
316                         ptr1 = &pSoundBuf[pos];
317                         if(pos2 >= uBufSize) {
318                                 size1 = uBufSize - pos;
319                                 size2 = pos2 - uBufSize;
320                                 ptr2 = &pSoundBuf[0];
321                         } else {
322                                 size1 = ssize;
323                                 size2 = 0;
324                                 ptr2 = NULL;
325                         }
326
327                         if(ptr1) {
328                                 ZeroMemory(ptr1, size1 * sizeof(Sint16));
329                         }
330                         if(ptr2) {
331                                 ZeroMemory(ptr2, size2 * sizeof(Sint16));
332                         }
333                         nSndDataPos = (nSndDataPos + ssize) % uBufSize;
334 //                      SDL_UnlockAudio();
335                         SDL_SemPost(*snddata.pSndApplySem);
336 //              }
337                 SDL_PauseAudioDevice(nAudioDevid, 0);
338         }
339         now_mute = true;
340 }
341
342 void EMU::start_rec_sound()
343 {
344    
345         LockVM();
346         if(!now_rec_sound) {
347                 // create file name
348                 //SYSTEMTIME sTime;
349                 //GetLocalTime(&sTime);
350                 std::tm *tm;
351                 std::time_t tnow;
352                 tnow = std::time(NULL);
353                 tm = std::localtime(&tnow);
354                 
355                 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, 
356                         tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
357                 
358                 // create wave file
359                 rec = new FILEIO();
360                 if(rec->Fopen(bios_path(sound_file_name), FILEIO_WRITE_BINARY)) {
361                         // write dummy wave header
362                         wavheader_t header;
363                         memset(&header, 0, sizeof(wavheader_t));
364                         rec->Fwrite(&header, sizeof(wavheader_t), 1);
365                         rec_bytes = 0;
366                         rec_buffer_ptr = vm->sound_buffer_ptr();
367                         now_rec_sound = true;
368                 } else {
369                         // failed to open the wave file
370                         delete rec;
371                 }
372         }
373         UnlockVM();
374 }
375
376 void EMU::stop_rec_sound()
377 {
378         LockVM();
379         if(now_rec_sound) {
380                 if(rec_bytes == 0) {
381                         rec->Fclose();
382                         rec->Remove(sound_file_name);
383                 } else {
384                         // update wave header
385                         wavheader_t header;
386
387                         header.dwRIFF = EndianToLittle_DWORD(0x46464952);
388                         header.dwFileSize = EndianToLittle_DWORD(rec_bytes + sizeof(wavheader_t) - 8);
389                         header.dwWAVE = EndianToLittle_DWORD(0x45564157);
390                         header.dwfmt_ = EndianToLittle_DWORD(0x20746d66);
391                         header.dwFormatSize = EndianToLittle_DWORD(16);
392                         header.wFormatTag = EndianToLittle_WORD(1);
393                         header.wChannels =  EndianToLittle_WORD(2);
394                         header.wBitsPerSample = EndianToLittle_WORD(16);
395                         header.dwSamplesPerSec = EndianToLittle_DWORD(sound_rate);
396                         header.wBlockAlign = EndianToLittle_WORD(header.wChannels * header.wBitsPerSample / 8);
397                         header.dwAvgBytesPerSec = EndianToLittle_DWORD(header.dwSamplesPerSec * header.wBlockAlign);
398                         header.dwdata = EndianToLittle_DWORD(0x61746164);
399                         header.dwDataLength = EndianToLittle_DWORD(rec_bytes);
400                         rec->Fseek(0, FILEIO_SEEK_SET);
401                         rec->Fwrite(&header, sizeof(wavheader_t), 1);
402                         rec->Fclose();
403                 }
404                 delete rec;
405                 now_rec_sound = false;
406         }
407         UnlockVM();
408 }
409
410 void EMU::restart_rec_sound()
411 {
412         bool tmp = now_rec_sound;
413         stop_rec_sound();
414         if(tmp) start_rec_sound();
415 }
416