OSDN Git Service

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