OSDN Git Service

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