OSDN Git Service

[VM][Qt][OSD][CMake] Fix FTBFSs at least for FM-7 series with GCC/*nix/Qt.
[csp-qt/common_source_project-fm7.git] / source / src / qt / osd_sound.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : K.Ohta <whatisthis.sowhat _at_ gmail.com>
5         Date   : 2015.11.30-
6
7         [Qt/SDL sound ]
8 */
9
10 #include "../emu.h"
11 #include "../fileio.h"
12 #include <SDL.h>
13 #include "qt_main.h"
14 #include "agar_logger.h"
15
16 #include <QString>
17 #include <QDateTime>
18
19 extern "C" {
20         static int uBufSize;
21         static int nSndDataLen, nSndDataPos, nSndWritePos;
22         static bool bSndExit;
23         static bool bSoundDebug;
24         static SDL_sem *pSndApplySem;
25         static Sint16 *pSoundBuf;
26         Uint8 iTotalVolume;
27 #if defined(USE_SDL2)   
28         static SDL_AudioDeviceID nAudioDevid;
29 #else
30         static int nAudioDevid;
31 #endif
32         static SDL_AudioSpec SndSpecReq, SndSpecPresented;
33 }
34
35 void AudioCallbackSDL(void *udata, Uint8 *stream, int len)
36 {
37         int pos;
38         int blen = len;
39         int len2 = len;
40         int channels = 2;
41         int spos;
42         struct timespec req, remain;
43         Uint8 *p;
44         Uint8 *s;
45         int writepos;
46         int sndlen;
47         
48         sdl_snddata_t *pData = (sdl_snddata_t *)udata;
49         //   printf("Called SND: %d %08x len = %d\n", SDL_GetTicks(), pData, len);
50         //if(pData == NULL) return;
51         
52         req.tv_sec = 0;
53         req.tv_nsec = 500 * 1000; //  0.5ms
54         
55         if(len <= 0) return;
56         spos = 0;
57         memset(stream, 0x00, len);
58         do {
59                 SDL_SemWait(*pData->pSndApplySem);
60                 if(config.general_sound_level < -32768) config.general_sound_level = -32768;
61                 if(config.general_sound_level > 32767)  config.general_sound_level = 32767;
62                 iTotalVolume = (uint8)(((uint32)(config.general_sound_level + 32768)) >> 9);
63                 sndlen = nSndDataLen;
64                 if(uBufSize  <= nSndWritePos) { // Wrap
65                         nSndWritePos = 0;
66                 }
67                 len2 = uBufSize - nSndWritePos;
68                 if(bSndExit) {
69                         SDL_SemPost(*pData->pSndApplySem);
70                         return;
71                 }
72                 if(len2 >= sndlen) len2 = sndlen;  // Okay
73                 if((spos + len2) >= (len / sizeof(Sint16))) {
74                         len2 = (len / sizeof(Sint16)) - spos;
75                 }
76                 if((nSndWritePos + len2) >= uBufSize ) len2 = uBufSize - nSndWritePos;
77                 
78                 if((len2 > 0) && (sndlen > 0)){
79                         writepos = nSndWritePos;
80                         p = (Uint8 *)pSoundBuf;
81                         SDL_SemPost(*pData->pSndApplySem);
82                         p = &p[writepos * 2];
83                         s = &stream[spos * 2];
84                         SDL_MixAudio(s, (Uint8 *)p, len2 * 2, iTotalVolume);
85                         if(bSoundDebug) AGAR_DebugLog(AGAR_LOG_DEBUG, "SND:Time=%d,Callback,nSndWritePos=%d,spos=%d,len=%d,len2=%d", SDL_GetTicks(), 
86                                   writepos, spos, len, len2);
87                         SDL_SemWait(*pData->pSndApplySem);
88                         nSndDataLen -= len2;
89                         if(nSndDataLen <= 0) nSndDataLen = 0;
90                         nSndWritePos += len2;
91                         SDL_SemPost(*pData->pSndApplySem);
92                 } else {
93                         len2 = 0;
94                         SDL_SemPost(*pData->pSndApplySem);
95                         if(spos >= (len / 2)) return;
96                         while(nSndDataLen <= 0) {
97 #if defined(Q_OS_WIN32)
98                                 SDL_Delay(1);
99 #else
100                                 nanosleep(&req, &remain); // Wait 500uS
101 #endif
102                                 if(bSndExit) return;
103                         }
104                 }
105                 spos += len2;
106         } while(spos < len); 
107 }
108
109 void OSD::initialize_sound(int rate, int samples)
110 {
111         std::string devname;
112         int i;
113
114         sound_rate = rate;
115         sound_samples = samples;
116         sound_ok = sound_started = now_mute = now_rec_sound = false;
117         rec_sound_buffer_ptr = 0;
118         sound_ok = sound_started = now_mute = now_rec_sound = false;
119         nSndWritePos = 0;
120         nSndDataLen = 0;
121         uBufSize = 0;
122         bSndExit = false;
123         bSoundDebug = false;
124         pSoundBuf = NULL;
125         pSndApplySem = NULL;
126         // initialize direct sound
127
128         iTotalVolume = 127;
129    
130         snddata.pSoundBuf = &pSoundBuf;
131         snddata.uBufSize = &uBufSize;
132         snddata.nSndWritePos = &nSndWritePos;
133         snddata.nSndDataLen = &nSndDataLen;
134         snddata.pSndApplySem = &pSndApplySem;
135         snddata.iTotalVolume = &iTotalVolume;
136         snddata.bSndExit = &bSndExit;
137         snddata.bSoundDebug = &bSoundDebug;
138
139         SndSpecReq.format = AUDIO_S16SYS;
140         SndSpecReq.channels = 2;
141         SndSpecReq.freq = sound_rate;
142         SndSpecReq.samples = ((sound_rate * 20) / 1000);
143         SndSpecReq.callback = AudioCallbackSDL;
144         SndSpecReq.userdata = (void *)&snddata;
145 #if defined(USE_SDL2)      
146         for(i = 0; i < SDL_GetNumAudioDevices(0); i++) {
147                 devname = SDL_GetAudioDeviceName(i, 0);
148                 AGAR_DebugLog(AGAR_LOG_INFO, "Audio Device: %s", devname.c_str());
149         }
150 #endif   
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 #if defined(USE_SDL2)              
160                 SDL_CloseAudioDevice(nAudioDevid);
161 #else      
162                 SDL_CloseAudio();
163 #endif     
164                 return;
165         }
166         pSndApplySem = SDL_CreateSemaphore(1);
167         if(pSndApplySem == NULL) {
168                 free(pSoundBuf);
169                 pSoundBuf = NULL;
170                 return;
171         }
172         AGAR_DebugLog(AGAR_LOG_INFO, "Sound OK: BufSize = %d", uBufSize);
173         ZeroMemory(pSoundBuf, uBufSize * sizeof(Sint16));
174 #if defined(USE_SDL2)   
175         SDL_PauseAudioDevice(nAudioDevid, 0);
176 #else   
177         SDL_PauseAudio(0);
178 #endif   
179         
180         sound_ok = sound_first_half = true;
181 }
182
183 void OSD::release_sound()
184 {
185         // release SDL sound
186         bSndExit = TRUE;
187 #if defined(USE_SDL2)   
188         SDL_CloseAudioDevice(nAudioDevid);
189 #else   
190         SDL_CloseAudio();
191 #endif   
192         if(pSndApplySem != NULL) {
193                 SDL_DestroySemaphore(pSndApplySem);
194         }
195         if(pSoundBuf != NULL) free(pSoundBuf);
196         // stop recording
197         stop_rec_sound();
198 }
199
200 void OSD::update_sound(int* extra_frames)
201 {
202         *extra_frames = 0;
203 #ifdef USE_DEBUGGER
204 //      if(now_debugging) {
205 //              return;
206 //      }
207 #endif
208         now_mute = false;
209         if(sound_ok) {
210                 uint32_t play_c, offset, size1, size2;
211                 Sint16 *ptr1, *ptr2;
212                 
213                 // start play
214                 // check current position
215                 play_c = nSndWritePos;
216                 if(!sound_first_half) {
217                         if(play_c < (uBufSize / 2)) {
218                                 return;
219                         }
220                         offset = 0;
221                 } else {
222                         if(play_c >= (uBufSize / 2)) {
223                                 return;
224                         }
225                         offset = uBufSize / 2;
226                 }
227                 //SDL_UnlockAudio();
228                 // sound buffer must be updated
229                 Sint16* sound_buffer = (Sint16 *)vm->create_sound(extra_frames);
230                 if(now_rec_sound) {
231                         // record sound
232                         if(sound_samples > rec_sound_buffer_ptr) {
233                                 int samples = sound_samples - rec_sound_buffer_ptr;
234                                 int length = samples * sizeof(uint16) * 2; // stereo
235                                 rec_sound_fio->Fwrite(sound_buffer + rec_sound_buffer_ptr * 2, length, 1);
236                                 rec_sound_bytes += length;
237                                 if(now_rec_video) {
238                                         // sync video recording
239                                         static double frames = 0;
240                                         static int prev_samples = -1;
241 #ifdef SUPPORT_VARIABLE_TIMING
242                                         static double prev_fps = -1;
243                                         double fps = vm->frame_rate();
244                                         if(prev_samples != samples || prev_fps != fps) {
245                                                 prev_samples = samples;
246                                                 prev_fps = fps;
247                                                 frames = fps * (double)samples / (double)sound_rate;
248                                         }
249 #else
250                                         if(prev_samples != samples) {
251                                                 prev_samples = samples;
252                                                 frames = FRAMES_PER_SEC * (double)samples / (double)sound_rate;
253                                         }
254 #endif
255                                         rec_video_frames -= frames;
256                                         if(rec_video_frames > 2) {
257                                                 rec_video_run_frames -= (rec_video_frames - 2);
258                                         } else if(rec_video_frames < -2) {
259                                                 rec_video_run_frames -= (rec_video_frames + 2);
260                                         }
261 //                                      rec_video_run_frames -= rec_video_frames;
262                                 }
263                         }
264                         rec_sound_buffer_ptr = 0;
265                 }
266                 if(sound_buffer) {
267                         int ssize;
268                         int pos;
269                         int pos2;
270                         if(pSndApplySem) {
271                                 SDL_SemWait(*snddata.pSndApplySem);
272                                 ssize = sound_samples * SndSpecPresented.channels;
273                                 pos = nSndDataPos;
274                                 pos2 = pos + ssize;
275                                 ptr1 = &pSoundBuf[pos];
276                                 if(pos2 >= uBufSize) {
277                                         size1 = uBufSize  - pos;
278                                         size2 = pos2 - uBufSize;
279                                         ptr2 = &pSoundBuf[0];
280                                 } else {
281                                         size1 = ssize;
282                                         size2 = 0;
283                                         ptr2 = NULL;
284                                 }
285                                 if(ptr1) {
286                                         memcpy(ptr1, sound_buffer, size1 * sizeof(Sint16));
287                                 }
288                                 if(ptr2) {
289                                         memcpy(ptr2, sound_buffer + size1, size2 * sizeof(Sint16));
290                                 }
291                                 nSndDataLen = nSndDataLen + ssize;
292                                 if(nSndDataLen >= uBufSize) nSndDataLen = uBufSize;
293                                 nSndDataPos = nSndDataPos + ssize;
294                                 if(nSndDataPos >= uBufSize) nSndDataPos = nSndDataPos - uBufSize;
295                                 SDL_SemPost(*snddata.pSndApplySem);
296                         }
297 //                      SDL_PauseAudioDevice(nAudioDevid, 0);
298                 }
299            
300 //              SDL_PauseAudioDevice(nAudioDevid, 0);
301                 sound_first_half = !sound_first_half;
302         }
303 }
304
305 void OSD::mute_sound()
306 {
307         if(!now_mute && sound_ok) {
308                 // check current position
309                 uint32_t size1, size2;
310             
311                 Sint16 *ptr1, *ptr2;
312                 // WIP
313                 int ssize;
314                 int pos;
315                 int pos2;
316                 SDL_SemWait(*snddata.pSndApplySem);
317                 ssize = uBufSize / 2;
318                 pos = nSndDataPos;
319                 pos2 = pos + ssize;
320                 ptr1 = &pSoundBuf[pos];
321                 if(pos2 >= uBufSize) {
322                         size1 = uBufSize - pos;
323                         size2 = pos2 - uBufSize;
324                         ptr2 = &pSoundBuf[0];
325                 } else {
326                         size1 = ssize;
327                         size2 = 0;
328                         ptr2 = NULL;
329                 }
330                 
331                 if(ptr1) {
332                         ZeroMemory(ptr1, size1 * sizeof(Sint16));
333                 }
334                 if(ptr2) {
335                         ZeroMemory(ptr2, size2 * sizeof(Sint16));
336                 }
337                 nSndDataPos = (nSndDataPos + ssize) % uBufSize;
338                 SDL_SemPost(*snddata.pSndApplySem);
339         }
340         now_mute = true;
341 }
342
343 void OSD::stop_sound()
344 {
345         if(sound_ok && sound_started) {
346 #if defined(USE_SDL2)   
347                 SDL_PauseAudioDevice(nAudioDevid, 1);
348 #else   
349                 SDL_PauseAudio(1);
350 #endif   
351                 sound_started = false;
352         }
353 }
354
355 void OSD::start_rec_sound()
356 {
357    
358         if(!now_rec_sound) {
359                 //LockVM();
360                 
361                 QDateTime nowTime = QDateTime::currentDateTime();
362                 QString tmps = QString::fromUtf8("Screen_Save_emu");
363                 tmps = tmps + QString::fromUtf8(CONFIG_NAME);
364                 tmps = tmps + QString::fromUtf8("_");
365                 tmps = tmps + nowTime.toString(QString::fromUtf8("yyyy-MM-dd_hh-mm-ss.zzz"));
366                 tmps = tmps + QString::fromUtf8(".wav");
367                 strncpy(sound_file_name, tmps.toUtf8().constData(), sizeof(sound_file_name));
368                 // create wave file
369                 rec_sound_fio = new FILEIO();
370                 if(rec_sound_fio->Fopen(bios_path(sound_file_name), FILEIO_WRITE_BINARY)) {
371                         // write dummy wave header
372                         wav_header_t header;
373                         memset(&header, 0, sizeof(wav_header_t));
374                         rec_sound_fio->Fwrite(&header, sizeof(wav_header_t), 1);
375                         rec_sound_bytes = 0;
376                         rec_sound_buffer_ptr = vm->sound_buffer_ptr();
377                         now_rec_sound = true;
378                 } else {
379                         // failed to open the wave file
380                         delete rec_sound_fio;
381                 }
382                 //UnlockVM();
383         }
384 }
385
386 void OSD::stop_rec_sound()
387 {
388                 if(now_rec_sound) {
389                         //LockVM();
390                 if(rec_sound_bytes == 0) {
391                         rec_sound_fio->Fclose();
392                         rec_sound_fio->RemoveFile(sound_file_name);
393                 } else {
394                         // update wave header
395                         wav_header_t wav_header;
396
397                         memcpy(wav_header.riff_chunk.id, "RIFF", 4);
398                         wav_header.riff_chunk.size = EndianToLittle_DWORD(rec_sound_bytes + sizeof(wav_header_t) - 8);
399                         memcpy(wav_header.wave, "WAVE", 4);
400                         memcpy(wav_header.fmt_chunk.id, "fmt ", 4);
401                         wav_header.fmt_chunk.size = EndianToLittle_DWORD(16);
402                         wav_header.format_id = EndianToLittle_WORD(1);
403                         wav_header.channels =  EndianToLittle_WORD(2);
404                         wav_header.sample_bits = EndianToLittle_WORD(16);
405                         wav_header.sample_rate = EndianToLittle_DWORD(sound_rate);
406                         wav_header.block_size = EndianToLittle_WORD(wav_header.channels * wav_header.sample_bits / 8);
407                         wav_header.data_speed = EndianToLittle_DWORD(wav_header.sample_rate * wav_header.block_size);
408                         
409                         memcpy(wav_header.fmt_chunk.id, "data", 4);
410                         wav_header.fmt_chunk.size = EndianToLittle_DWORD(rec_sound_bytes);
411
412                         rec_sound_fio->Fseek(0, FILEIO_SEEK_SET);
413                         rec_sound_fio->Fwrite(&wav_header, sizeof(wav_header_t), 1);
414                         rec_sound_fio->Fclose();
415                 }
416                 delete rec_sound_fio;
417                 now_rec_sound = false;
418                 //UnlockVM();
419         }
420 }
421
422 void OSD::restart_rec_sound()
423 {
424         bool tmp = now_rec_sound;
425         stop_rec_sound();
426         if(tmp) {
427                 start_rec_sound();
428         }
429 }
430