1 /* -*- C++ -*-
2  *
3  *  PonscripterLabel_sound.cpp - Methods for playing sound
4  *
5  *  Copyright (c) 2001-2008 Ogapee (original ONScripter, of which this
6  *  is a fork).
7  *
8  *  ogapee@aqua.dti2.ne.jp
9  *
10  *  This program is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU General Public License as
12  *  published by the Free Software Foundation; either version 2 of the
13  *  License, or (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307 USA
24  */
25 
26 #include "PonscripterLabel.h"
27 #include "PonscripterUserEvents.h"
28 #ifdef LINUX
29 #include <signal.h>
30 #endif
31 
32 #ifdef USE_AVIFILE
33 #include "AVIWrapper.h"
34 #endif
35 
36 struct WAVE_HEADER {
37     char chunk_riff[4];
38     char riff_length[4];
39     char fmt_id[8];
40     char fmt_size[4];
41     char data_fmt[2];
42     char channels[2];
43     char frequency[4];
44     char byte_size[4];
45     char sample_byte_size[2];
46     char sample_bit_size[2];
47 
48     char chunk_id[4];
49     char data_length[4];
50 } header;
51 
52 typedef struct
53 {
54     SMPEG_Frame *frame;
55     int dirty;
56     SDL_mutex *lock;
57     SDL_Texture *texture;
58 } update_context;
59 
60 extern bool ext_music_play_once_flag;
61 
62 extern "C" {
63     extern void mp3callback(void* userdata, Uint8 * stream, int len);
64 
65     extern void oggcallback(void* userdata, Uint8 * stream, int len);
66 
67 #ifdef MACOSX
68     extern Uint32 SDLCALL midiSDLCallback(Uint32 interval, void* param);
69 
70 #endif
71 }
72 extern void midiCallback(int sig);
73 extern void musicCallback(int sig);
74 
75 #ifdef MACOSX
76 extern SDL_TimerID timer_midi_id;
77 #endif
78 
79 #define TMP_MIDI_FILE "tmp.mid"
80 #define TMP_MUSIC_FILE "tmp.mus"
81 
82 #define SWAP_SHORT_BYTES(sptr){          \
83             Uint8 *bptr = (Uint8 *)sptr; \
84             Uint8 tmpb = *bptr;          \
85             *bptr = *(bptr+1);           \
86             *(bptr+1) = tmpb;            \
87         }
88 
decodeOggVorbis(PonscripterLabel::MusicStruct * music_struct,Uint8 * buf_dst,long len,bool do_rate_conversion)89 extern long decodeOggVorbis(PonscripterLabel::MusicStruct *music_struct, Uint8 *buf_dst, long len, bool do_rate_conversion)
90 {
91     int  current_section;
92     long total_len = 0;
93 
94     OVInfo *ovi = music_struct->ovi;
95     char* buf = (char*) buf_dst;
96     if (do_rate_conversion && ovi->cvt.needed) {
97         len = len * ovi->mult1 / ovi->mult2;
98         if (ovi->cvt_len < len * ovi->cvt.len_mult) {
99             if (ovi->cvt.buf) delete[] ovi->cvt.buf;
100 
101             ovi->cvt.buf = new unsigned char[len * ovi->cvt.len_mult];
102             ovi->cvt_len = len * ovi->cvt.len_mult;
103         }
104 
105         buf = (char*) ovi->cvt.buf;
106     }
107 
108 #ifdef USE_OGG_VORBIS
109     while (1) {
110 #ifdef INTEGER_OGG_VORBIS
111         long src_len = ov_read(&ovi->ovf, buf, len, &current_section);
112 #else
113         long src_len = ov_read(&ovi->ovf, buf, len, 0, 2, 1, &current_section);
114 #endif
115 
116         if (ovi->loop == 1) {
117             ogg_int64_t pcmPos = ov_pcm_tell(&ovi->ovf);
118             if (pcmPos >= ovi->loop_end) {
119                 len -= ((pcmPos - ovi->loop_end) * ovi->channels) * (long)sizeof(Uint16);
120                 ov_pcm_seek(&ovi->ovf, ovi->loop_start);
121             }
122         }
123         if (src_len <= 0) break;
124 
125         int vol = music_struct->is_mute ? 0 : music_struct->volume;
126         long dst_len = src_len;
127         if (do_rate_conversion && ovi->cvt.needed){
128             ovi->cvt.len = src_len;
129             SDL_ConvertAudio(&ovi->cvt);
130             memcpy(buf_dst, ovi->cvt.buf, ovi->cvt.len_cvt);
131             dst_len = ovi->cvt.len_cvt;
132 
133             if (vol != DEFAULT_VOLUME){
134                 // volume change under SOUND_OGG_STREAMING
135                 for (int i=0 ; i<dst_len ; i+=2){
136                     short a = *(short*)(buf_dst+i);
137                     a = a*vol/100;
138                     *(short*)(buf_dst+i) = a;
139                 }
140             }
141             buf_dst += ovi->cvt.len_cvt;
142         }
143         else{
144             if (do_rate_conversion && vol != DEFAULT_VOLUME){
145                 // volume change under SOUND_OGG_STREAMING
146                 for (int i=0 ; i<dst_len ; i+=2){
147 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
148                     SWAP_SHORT_BYTES( ((short*)(buf_dst+i)) )
149 #endif
150                     short a = *(short*)(buf_dst+i);
151                     a = a*vol/100;
152                     *(short*)(buf_dst+i) = a;
153 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
154                     SWAP_SHORT_BYTES( ((short*)(buf_dst+i)) )
155 #endif
156                 }
157             }
158             buf += dst_len;
159             buf_dst += dst_len;
160         }
161 
162         total_len += dst_len;
163         if (src_len == len) break;
164         len -= src_len;
165     }
166 #endif
167 
168     return total_len;
169 }
170 
171 
playSound(const pstring & filename,int format,bool loop_flag,int channel)172 int PonscripterLabel::playSound(const pstring& filename, int format,
173                                 bool loop_flag, int channel)
174 {
175     if ( !audio_open_flag ) return SOUND_NONE;
176     if (filename.length() == 0) return SOUND_NONE;
177 
178     long length = script_h.cBR->getFileLength( filename );
179     if (length == 0) {
180         errorAndCont(filename + " not found");
181         return SOUND_NONE;
182     }
183 
184     //Mion: account for mode_wave_demo setting
185     //(i.e. if not set, then don't play non-bgm wave/ogg during skip mode)
186     if (!mode_wave_demo_flag &&
187         ( skip_flag || ctrl_pressed_status )) {
188         if ((format & (SOUND_OGG | SOUND_WAVE)) &&
189             ((channel < ONS_MIX_CHANNELS) || (channel == MIX_WAVE_CHANNEL) ||
190              (channel == MIX_CLICKVOICE_CHANNEL)))
191             return SOUND_NONE;
192     }
193 
194     unsigned char* buffer;
195 
196     if ((format & (SOUND_MP3 | SOUND_OGG_STREAMING)) &&
197         (length == music_buffer_length) &&
198         music_buffer ){
199         buffer = music_buffer;
200     }
201     else{
202         if (lastRenderEvent < RENDER_EVENT_LOAD_AUDIO) { lastRenderEvent = RENDER_EVENT_LOAD_AUDIO; }
203         buffer = new unsigned char[length];
204         script_h.cBR->getFile( filename, buffer );
205     }
206 
207     if (format & (SOUND_OGG | SOUND_OGG_STREAMING)) {
208         int ret = playOGG(format, buffer, length, loop_flag, channel);
209         if (ret & (SOUND_OGG | SOUND_OGG_STREAMING)) return ret;
210     }
211 
212     if (format & SOUND_WAVE) {
213         Mix_Chunk* chunk = Mix_LoadWAV_RW(SDL_RWFromMem(buffer, length), 1);
214         if (playWave(chunk, format, loop_flag, channel) == 0) {
215             delete[] buffer;
216             return SOUND_WAVE;
217         }
218     }
219 
220     if (format & SOUND_MP3) {
221         if (music_cmd) {
222             FILE* fp = fopen(script_h.save_path + TMP_MUSIC_FILE, "wb");
223             if (fp == NULL) {
224                 fprintf(stderr, "can't open temporary music file %s\n",
225                         TMP_MUSIC_FILE);
226             }
227             else {
228                 fwrite(buffer, 1, length, fp);
229                 fclose(fp);
230                 ext_music_play_once_flag = !loop_flag;
231                 if (playExternalMusic(loop_flag) == 0) {
232                     music_buffer = buffer;
233                     music_buffer_length = length;
234                     return SOUND_MP3;
235                 }
236             }
237         }
238 
239         mp3_sample = SMPEG_new_rwops(SDL_RWFromMem(buffer, length), NULL, 0, 0);
240         if (playMP3() == 0) {
241             music_buffer = buffer;
242             music_buffer_length = length;
243             return SOUND_MP3;
244         }
245     }
246 
247     /* check WMA */
248     if (buffer[0] == 0x30 && buffer[1] == 0x26
249         && buffer[2] == 0xb2 && buffer[3] == 0x75) {
250         delete[] buffer;
251         return SOUND_OTHER;
252     }
253 
254     if (format & SOUND_MIDI) {
255         FILE* fp = fopen(script_h.save_path + TMP_MIDI_FILE, "wb");
256         if (fp == NULL) {
257             fprintf(stderr, "can't open temporary MIDI file %s\n",
258                     TMP_MIDI_FILE);
259         }
260         else {
261             fwrite(buffer, 1, length, fp);
262             fclose(fp);
263             ext_music_play_once_flag = !loop_flag;
264             if (playMIDI(loop_flag) == 0) {
265                 delete[] buffer;
266                 return SOUND_MIDI;
267             }
268         }
269     }
270 
271     delete[] buffer;
272 
273     return SOUND_OTHER;
274 }
275 
276 
playWave(Mix_Chunk * chunk,int format,bool loop_flag,int channel)277 int PonscripterLabel::playWave(Mix_Chunk* chunk, int format, bool loop_flag,
278 			       int channel)
279 {
280     if (!chunk) return -1;
281 
282     Mix_Pause(channel);
283     if (wave_sample[channel]) Mix_FreeChunk(wave_sample[channel]);
284 
285     wave_sample[channel] = chunk;
286 
287     if (channel == 0)
288         Mix_Volume(channel, !volume_on_flag? 0 : voice_volume * 128 / 100);
289     else if (channel == MIX_BGM_CHANNEL)
290         Mix_Volume(channel, !volume_on_flag? 0 : music_volume * 128 / 100);
291     else
292         Mix_Volume(channel, !volume_on_flag? 0 : se_volume * 128 / 100);
293 
294     if (!(format & SOUND_PRELOAD))
295         Mix_PlayChannel(channel, wave_sample[channel], loop_flag ? -1 : 0);
296 
297     return 0;
298 }
299 
300 
playMP3()301 int PonscripterLabel::playMP3()
302 {
303     if (SMPEG_error(mp3_sample)) {
304         //printf(" failed. [%s]\n",SMPEG_error( mp3_sample ));
305         // The line below fails. ?????
306         //SMPEG_delete( mp3_sample );
307         mp3_sample = NULL;
308         return -1;
309     }
310 
311 #ifndef MP3_MAD
312     //Mion - SMPEG doesn't handle different audio spec well, so we might
313     // reset the SDL mixer
314     SDL_AudioSpec wanted;
315     SMPEG_wantedSpec( mp3_sample, &wanted );
316     if ((wanted.format != audio_format.format) ||
317         (wanted.freq != audio_format.freq)) {
318         Mix_CloseAudio();
319         openAudio(wanted.freq, wanted.format, wanted.channels);
320         if (!audio_open_flag) {
321             // didn't work, use the old settings
322             openAudio();
323        }
324     }
325     SMPEG_enableaudio( mp3_sample, 0 );
326     SMPEG_actualSpec( mp3_sample, &audio_format );
327     SMPEG_enableaudio( mp3_sample, 1 );
328 #endif
329     SMPEG_setvolume( mp3_sample, !volume_on_flag? 0 : music_volume );
330     Mix_HookMusic( mp3callback, mp3_sample );
331     SMPEG_play( mp3_sample );
332 
333     return 0;
334 }
335 
336 
playOGG(int format,unsigned char * buffer,long length,bool loop_flag,int channel)337 int PonscripterLabel::playOGG(int format, unsigned char* buffer, long length, bool loop_flag, int channel)
338 {
339     int channels, rate;
340     OVInfo* ovi = openOggVorbis(buffer, length, channels, rate);
341     if (ovi == NULL) return SOUND_OTHER;
342 
343     if (format & SOUND_OGG) {
344         unsigned char* buffer2 = new unsigned char[sizeof(WAVE_HEADER) + ovi->decoded_length];
345 
346         MusicStruct ms;
347         ms.ovi = ovi;
348         ms.voice_sample = NULL;
349         ms.volume = channelvolumes[channel];
350         decodeOggVorbis(&ms, buffer2 + sizeof(WAVE_HEADER), ovi->decoded_length, false);
351         setupWaveHeader(buffer2, channels, rate, 16, ovi->decoded_length);
352         Mix_Chunk* chunk = Mix_LoadWAV_RW(SDL_RWFromMem(buffer2, sizeof(WAVE_HEADER) + ovi->decoded_length), 1);
353         delete[] buffer2;
354         closeOggVorbis(ovi);
355         delete[] buffer;
356 
357         playWave(chunk, format, loop_flag, channel);
358 
359         return SOUND_OGG;
360     }
361 
362     if ((audio_format.format != AUDIO_S16) ||
363         (audio_format.freq != rate)) {
364         Mix_CloseAudio();
365         openAudio(rate, AUDIO_S16, channels);
366         ovi->cvt.needed = 0;
367         if (!audio_open_flag) {
368             // didn't work, use the old settings
369             openAudio();
370             ovi->cvt_len = 0;
371             SDL_BuildAudioCVT(&ovi->cvt,
372                       AUDIO_S16, channels, rate,
373                       audio_format.format, audio_format.channels, audio_format.freq);
374             ovi->mult1 = 10;
375             ovi->mult2 = (int)(ovi->cvt.len_ratio*10.0);
376        }
377     }
378 
379     music_struct.ovi = ovi;
380     music_struct.volume = music_volume;
381     music_struct.is_mute = !volume_on_flag;
382     Mix_HookMusic(oggcallback, &music_struct);
383 
384     music_buffer = buffer;
385     music_buffer_length = length;
386 
387     return SOUND_OGG_STREAMING;
388 }
389 
390 
playExternalMusic(bool loop_flag)391 int PonscripterLabel::playExternalMusic(bool loop_flag)
392 {
393     int music_looping = loop_flag ? -1 : 0;
394 #ifdef LINUX
395     signal(SIGCHLD, musicCallback);
396     if (music_cmd) music_looping = 0;
397 
398 #endif
399 
400     Mix_SetMusicCMD(music_cmd);
401 
402     pstring music_filename = script_h.save_path + TMP_MUSIC_FILE;
403     if ((music_info = Mix_LoadMUS(music_filename)) == NULL) {
404         fprintf(stderr, "can't load Music file %s\n",
405 		(const char*) music_filename);
406         return -1;
407     }
408 
409     // Mix_VolumeMusic( music_volume );
410     Mix_PlayMusic(music_info, music_looping);
411 
412     return 0;
413 }
414 
415 
playMIDI(bool loop_flag)416 int PonscripterLabel::playMIDI(bool loop_flag)
417 {
418     Mix_SetMusicCMD(midi_cmd);
419 
420     pstring midi_filename = script_h.save_path + TMP_MIDI_FILE;
421     if ((midi_info = Mix_LoadMUS(midi_filename)) == NULL) return -1;
422 
423 #ifndef MACOSX
424     int midi_looping = loop_flag ? -1 : 0;
425 #endif
426 
427 #ifdef EXTERNAL_MIDI_PROGRAM
428     FILE* com_file;
429     if (midi_play_loop_flag) {
430         if ((com_file = fopen("play_midi", "wb")) != NULL)
431             fclose(com_file);
432     }
433     else {
434         if ((com_file = fopen("playonce_midi", "wb")) != NULL)
435             fclose(com_file);
436     }
437 
438 #endif
439 
440 #ifdef LINUX
441     signal(SIGCHLD, midiCallback);
442     if (midi_cmd) midi_looping = 0;
443 
444 #endif
445 
446     Mix_VolumeMusic(!volume_on_flag? 0 : music_volume);
447 #ifdef MACOSX
448     // Emulate looping on MacOS ourselves to work around bug in SDL_Mixer
449     Mix_PlayMusic(midi_info, false);
450     timer_midi_id = SDL_AddTimer(1000, midiSDLCallback, NULL);
451 #else
452     Mix_PlayMusic(midi_info, midi_looping);
453 #endif
454 
455     return 0;
456 }
457 
458 
playingMusic()459 int PonscripterLabel::playingMusic()
460 {
461     if ((Mix_GetMusicHookData() != NULL) || (Mix_Playing(MIX_BGM_CHANNEL) == 1)
462         || (Mix_PlayingMusic() == 1))
463         return 1;
464     else
465         return 0;
466 }
467 
setCurMusicVolume(int volume)468 int PonscripterLabel::setCurMusicVolume( int volume )
469 {
470     if (Mix_GetMusicHookData() != NULL) { // for streamed MP3 & OGG
471         if ( mp3_sample ) SMPEG_setvolume( mp3_sample, !volume_on_flag? 0 : volume ); // mp3
472         else music_struct.volume = volume; // ogg
473     } else if (Mix_Playing(MIX_BGM_CHANNEL) == 1) { // wave
474         Mix_Volume( MIX_BGM_CHANNEL, !volume_on_flag? 0 : volume * 128 / 100 );
475     } else if (Mix_PlayingMusic() == 1) { // midi
476         Mix_VolumeMusic( !volume_on_flag? 0 : volume * 128 / 100 );
477     }
478 
479     return 0;
480 }
481 
setVolumeMute(bool do_mute)482 int PonscripterLabel::setVolumeMute( bool do_mute )
483 {
484     if (Mix_GetMusicHookData() != NULL) { // for streamed MP3 & OGG
485         if ( mp3_sample ) SMPEG_setvolume( mp3_sample, do_mute? 0 : music_volume ); // mp3
486         else music_struct.is_mute = do_mute; // ogg
487     } else if (Mix_Playing(MIX_BGM_CHANNEL) == 1) { // wave
488         Mix_Volume( MIX_BGM_CHANNEL, do_mute? 0 : music_volume * 128 / 100 );
489     } else if (Mix_PlayingMusic() == 1) { // midi
490         Mix_VolumeMusic( do_mute? 0 : music_volume * 128 / 100 );
491     }
492     for ( int i=1 ; i<ONS_MIX_CHANNELS ; i++ ) {
493         if ( wave_sample[i] )
494             Mix_Volume( i, do_mute? 0 : channelvolumes[i] * 128 / 100 );
495      }
496     if ( wave_sample[MIX_LOOPBGM_CHANNEL0] )
497         Mix_Volume( MIX_LOOPBGM_CHANNEL0, do_mute? 0 : se_volume * 128 / 100 );
498     if ( wave_sample[MIX_LOOPBGM_CHANNEL1] )
499         Mix_Volume( MIX_LOOPBGM_CHANNEL1, do_mute? 0 : se_volume * 128 / 100 );
500 
501     return 0;
502 }
503 
504 // We assume only one MPEG video will ever play at a time.
505 // This bit is messy, but it seems we cannot use a method here, so we
506 // simply must shift all this stuff into plain C variables.
507 struct SubAndTexture {
508   AnimationInfo *ai;
509   SDL_Texture *tex;
510 };
511 typedef std::vector<SubAndTexture*> olvec;
512 static olvec overlays;
513 /*
514    Each of these is only needed if there are subs.
515    Otherwise we can just pop it straight on the screen
516  */
517 static SDL_Renderer *video_renderer = NULL;
518 
UpdateMPEG(void * data,SMPEG_Frame * frame)519 void UpdateMPEG(void *data, SMPEG_Frame *frame) {
520   update_context *c = (update_context *)data;
521   c->frame = frame;
522   // Let ourselves know we've got a new frame to render
523   c->dirty = 1;
524 }
525 
playMPEG(const pstring & filename,bool click_flag,bool loop_flag,bool mixsound_flag,bool nosound_flag,SubtitleDefs & subtitles)526 int PonscripterLabel::playMPEG(const pstring& filename, bool click_flag, bool loop_flag,
527                                bool mixsound_flag, bool nosound_flag, SubtitleDefs& subtitles)
528 {
529     int ret = 0;
530 #ifndef MP3_MAD
531     bool different_spec = false;
532     pstring mpeg_dat = ScriptHandler::cBR->getFile(filename);
533     SMPEG* mpeg_sample = SMPEG_new_rwops(rwops(mpeg_dat), 0, 0, 0);
534     if (!SMPEG_error(mpeg_sample)) {
535         SMPEG_enableaudio(mpeg_sample, 0);
536 
537         if (audio_open_flag && !nosound_flag) {
538             //Mion - SMPEG doesn't handle different audio spec well, so
539             // let's redo the SDL mixer just for this video playback
540             SDL_AudioSpec wanted;
541             SMPEG_wantedSpec(mpeg_sample, &wanted);
542             if (!mixsound_flag && (wanted.format != audio_format.format ||
543                 wanted.freq != audio_format.freq))
544             {
545                 different_spec = true;
546                 Mix_CloseAudio();
547                 openAudio(wanted.freq, wanted.format, wanted.channels);
548                 if (!audio_open_flag) {
549                   fprintf(stderr, "New format error, using old\n");
550                     openAudio();
551                     different_spec = false;
552                 }
553             }
554             SMPEG_actualSpec(mpeg_sample, &audio_format);
555             SMPEG_enableaudio(mpeg_sample, 1);
556         }
557 
558         SMPEG_enablevideo(mpeg_sample, 1);
559 
560         update_context c = {};
561         c.lock = SDL_CreateMutex();
562 
563         if(subtitles) {
564           /* Pairs of sub-AnimationInfos and sub-textures. The texture is rendered on top of the video_texture */
565           overlays.assign(size_t(subtitles.numdefs()), NULL);
566         }
567 
568         SMPEG_setdisplay(mpeg_sample, UpdateMPEG, &c, c.lock);
569 
570 
571         if (!nosound_flag) {
572             SMPEG_setvolume(mpeg_sample, !volume_on_flag? 0 : music_volume);
573             Mix_HookMusic(mp3callback, mpeg_sample);
574         }
575 
576         if (loop_flag) {
577             SMPEG_loop(mpeg_sample, -1);
578         }
579         SMPEG_play(mpeg_sample);
580 
581         bool done_flag = false;
582         bool interrupted_redraw = false;
583         bool done_click_down = false;
584 
585         while (!done_flag)
586         {
587             if (SMPEG_status(mpeg_sample) != SMPEG_PLAYING) {
588                 if (loop_flag) {
589                     SMPEG_play( mpeg_sample );
590                 } else {
591                     break;
592                 }
593             }
594 
595             SDL_Event event, tmp_event;
596             while (SDL_PollEvent(&event)) {
597                 switch (event.type) {
598                 case SDL_KEYUP: {
599                     int s = ((SDL_KeyboardEvent*) &event)->keysym.sym;
600                     if (s == SDLK_RETURN || s == SDLK_SPACE || s == SDLK_ESCAPE)
601                         done_flag = click_flag;
602                     if (s == SDLK_m) {
603                         volume_on_flag = !volume_on_flag;
604                         SMPEG_setvolume(mpeg_sample, !volume_on_flag? 0 : music_volume);
605                         printf("turned %s volume mute\n", !volume_on_flag?"on":"off");
606                     }
607                     if (s == SDLK_f) {
608                         if (fullscreen_mode) menu_windowCommand("menu_window");
609                         else menu_fullCommand("menu_full");
610                     }
611                     break;
612                 }
613                 case SDL_QUIT:
614                     ret = 1;
615                     done_flag = true;
616                     break;
617                 case SDL_MOUSEBUTTONDOWN:
618                     done_click_down = true;
619                     break;
620                 case SDL_MOUSEMOTION:
621                     done_click_down = false;
622                     break;
623                 case SDL_MOUSEBUTTONUP:
624                     if(done_click_down)
625                         done_flag = click_flag;
626                     break;
627                 case INTERNAL_REDRAW_EVENT:
628                     interrupted_redraw = true;
629                     break;
630                 case SDL_WINDOWEVENT:
631                     switch(event.window.event) {
632                         case SDL_WINDOWEVENT_MAXIMIZED:
633                         case SDL_WINDOWEVENT_RESIZED:
634                             SDL_PumpEvents();
635                             SDL_PeepEvents(&tmp_event, 1, SDL_GETEVENT, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONDOWN);
636                             done_click_down = false;
637                             break;
638                     }
639                 default:
640                     break;
641                 }
642             }
643 
644             if (subtitles) {
645                 SMPEG_Info info;
646                 SMPEG_getinfo(mpeg_sample, &info);
647                 if (info.current_time >= subtitles.next()) {
648                     Subtitle s = subtitles.pop();
649 
650                     if (overlays[s.number]) {
651                       if(overlays[s.number]->tex) {
652                         SDL_DestroyTexture(overlays[s.number]->tex);
653                         overlays[s.number]->tex = NULL;
654                       }
655                       if(overlays[s.number]->ai) {
656                         delete overlays[s.number]->ai;
657                         overlays[s.number]->ai = NULL;
658                       }
659                       delete overlays[s.number];
660                       overlays[s.number] = NULL;
661                     }
662 
663                     AnimationInfo* overlay = 0;
664                     SDL_Texture *overlay_tex = NULL;
665 
666 
667                     if (s.text) {
668                         overlay = new AnimationInfo();
669                         overlay->setImageName(s.text);
670                         overlay->pos.x = screen_width / 2;
671                         overlay->pos.y = subtitles.pos(s.number);
672                         parseTaggedString(overlay);
673                         overlay->color_list[0] = subtitles.colour(s.number);
674                         setupAnimationInfo(overlay);
675                         overlay->trans = subtitles.alpha(s.number);
676 
677                         /* Create the texture that we'll use to render this */
678                         overlay_tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, overlay->pos.w, overlay->pos.h);
679                         SDL_UpdateTexture(overlay_tex, /* whole tex */ NULL, overlay->image_surface->pixels, overlay->image_surface->pitch);
680                         SDL_SetTextureBlendMode(overlay_tex, SDL_BLENDMODE_BLEND);
681                         SDL_SetTextureAlphaMod(overlay_tex, overlay->trans);
682 
683 
684                         overlays[s.number] = new SubAndTexture();
685                         overlays[s.number]->ai = overlay;
686                         overlays[s.number]->tex = overlay_tex;
687                     }
688                 }
689             }
690             if(c.dirty) {
691                 SDL_mutexP(c.lock);
692                 c.dirty = 0; //Flag that we're handling this; if a new frame appears we should deal with it too.
693 
694                 if (!c.texture) {
695                     // Match the texture size to the video size, otherwise you end up with unset data from outside the video bleeding in when upscaling
696                     c.texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, c.frame->image_width, c.frame->image_height);
697                     // YUV textures have no alpha!
698                     SDL_SetTextureBlendMode(c.texture, SDL_BLENDMODE_NONE);
699                 }
700 
701                 SDL_Rect r;
702                 r.x = 0; r.y = 0; r.w = c.frame->image_width; r.h = c.frame->image_height;
703                 SDL_UpdateTexture(c.texture, &r, c.frame->image, c.frame->image_width);
704 
705                 // image_width and image_height are the size of the full image, including padding
706                 // w and h are the size of the actual video
707                 r.w = c.frame->w; r.h = c.frame->h;
708                 SDL_RenderCopy(renderer, c.texture, &r, /* fullscreen */ NULL);
709 
710                 if(subtitles) {
711                     /* render any subs onto the screen */
712                     for(olvec::iterator it = overlays.begin(); it != overlays.end(); ++it) {
713                         if((*it) && (*it)->ai && (*it)->tex) {
714                             SDL_RenderCopy(renderer, (*it)->tex, /* entire src */ NULL, &(*it)->ai->pos);
715                         }
716                     }
717                 }
718 
719                 SDL_mutexV(c.lock);
720                 SDL_RenderPresent(renderer);
721             }
722             SDL_Delay(10);
723         }
724 
725         ctrl_pressed_status = 0;
726 
727         SMPEG_stop(mpeg_sample);
728         if (!nosound_flag) {
729             Mix_HookMusic(NULL, NULL);
730         }
731         SMPEG_delete(mpeg_sample);
732         if (c.texture) {
733             SDL_DestroyTexture(c.texture);
734         }
735 
736         if (different_spec) {
737             //restart mixer with the old audio spec
738             Mix_CloseAudio();
739             openAudio();
740         }
741 
742         if (video_renderer) {
743             SDL_DestroyRenderer(video_renderer);
744             video_renderer = NULL;
745         }
746         for (olvec::iterator it = overlays.begin(); it != overlays.end(); ++it) {
747           if(*it) {
748             if ((*it)->ai) delete ((*it)->ai);
749             if ((*it)->tex) SDL_DestroyTexture((*it)->tex);
750             delete (*it);
751           }
752         }
753         overlays.clear();
754 
755         if(interrupted_redraw) {
756             queueRerender();
757         }
758     }
759 
760 #else
761     fprintf(stderr, "mpegplay command is disabled.\n");
762 #endif
763 
764 
765     return ret;
766 }
767 
768 
playAVI(const pstring & filename,bool click_flag)769 void PonscripterLabel::playAVI(const pstring& filename, bool click_flag)
770 {
771 #ifdef USE_AVIFILE
772     pstring abs_fname = archive_path + filename;
773     replace_ascii(abs_fname, '/', DELIMITER[0]);
774     replace_ascii(abs_fname, '\\', DELIMITER[0]);
775 
776     if (audio_open_flag) Mix_CloseAudio();
777 
778     AVIWrapper* avi = new AVIWrapper();
779     if (avi->init(abs_fname, false) == 0
780         && avi->initAV(accumulation_surface, audio_open_flag) == 0) {
781         if (avi->play(click_flag)) endCommand();
782     }
783 
784     delete avi;
785 
786     if (audio_open_flag) {
787         Mix_CloseAudio();
788         openAudio();
789     }
790 
791 #else
792     fprintf(stderr, "avi command is disabled.\n");
793 #endif
794 }
795 
796 
stopBGM(bool continue_flag)797 void PonscripterLabel::stopBGM(bool continue_flag)
798 {
799 #ifdef EXTERNAL_MIDI_PROGRAM
800     FILE* com_file;
801     if ((com_file = fopen("stop_bgm", "wb")) != NULL)
802         fclose(com_file);
803 
804 #endif
805 
806     if (mp3_sample) {
807         SMPEG_stop(mp3_sample);
808         Mix_HookMusic(NULL, NULL);
809         SMPEG_delete(mp3_sample);
810         mp3_sample = NULL;
811     }
812 
813     if (music_struct.ovi){
814         Mix_HaltMusic();
815         Mix_HookMusic( NULL, NULL );
816         closeOggVorbis(music_struct.ovi);
817         music_struct.ovi = NULL;
818     }
819 
820     if (wave_sample[MIX_BGM_CHANNEL]) {
821         Mix_Pause(MIX_BGM_CHANNEL);
822         Mix_FreeChunk(wave_sample[MIX_BGM_CHANNEL]);
823         wave_sample[MIX_BGM_CHANNEL] = NULL;
824     }
825 
826     if (!continue_flag) {
827         music_file_name = "";
828         music_play_loop_flag = false;
829         if (music_buffer) {
830             delete[] music_buffer;
831             music_buffer = NULL;
832         }
833     }
834 
835     if (midi_info) {
836 #ifdef MACOSX
837         if (timer_midi_id) {
838             SDL_RemoveTimer(timer_midi_id);
839             timer_midi_id = NULL;
840         }
841 
842 #endif
843 
844         ext_music_play_once_flag = true;
845         Mix_HaltMusic();
846         Mix_FreeMusic(midi_info);
847         midi_info = NULL;
848     }
849 
850     if (!continue_flag) {
851         midi_file_name = "";
852         midi_play_loop_flag = false;
853     }
854 
855     if (music_info) {
856         ext_music_play_once_flag = true;
857         Mix_HaltMusic();
858         Mix_FreeMusic(music_info);
859         music_info = NULL;
860     }
861 }
862 
863 
stopAllDWAVE()864 void PonscripterLabel::stopAllDWAVE()
865 {
866     for (int ch = 0; ch < ONS_MIX_CHANNELS; ++ch) {
867         if (wave_sample[ch]) {
868             Mix_Pause(ch);
869             Mix_FreeChunk(wave_sample[ch]);
870             wave_sample[ch] = NULL;
871         }
872     }
873 }
874 
875 
playClickVoice()876 void PonscripterLabel::playClickVoice()
877 {
878     if (clickstr_state == CLICK_NEWPAGE) {
879         if (clickvoice_file_name[CLICKVOICE_NEWPAGE].length() > 0)
880             playSound(clickvoice_file_name[CLICKVOICE_NEWPAGE],
881                       SOUND_WAVE | SOUND_OGG, false, MIX_WAVE_CHANNEL);
882     }
883     else if (clickstr_state == CLICK_WAIT) {
884         if (clickvoice_file_name[CLICKVOICE_NORMAL].length() > 0)
885             playSound(clickvoice_file_name[CLICKVOICE_NORMAL],
886                       SOUND_WAVE | SOUND_OGG, false, MIX_WAVE_CHANNEL);
887     }
888 }
889 
890 
setupWaveHeader(unsigned char * buffer,int channels,int rate,int bits,unsigned long data_length)891 void PonscripterLabel::setupWaveHeader(unsigned char *buffer, int channels,
892                                        int rate, int bits,
893                                        unsigned long data_length )
894 {
895     memcpy(header.chunk_riff, "RIFF", 4);
896     int riff_length = sizeof(WAVE_HEADER) + data_length - 8;
897     header.riff_length[0] = riff_length & 0xff;
898     header.riff_length[1] = (riff_length >> 8) & 0xff;
899     header.riff_length[2] = (riff_length >> 16) & 0xff;
900     header.riff_length[3] = (riff_length >> 24) & 0xff;
901     memcpy(header.fmt_id, "WAVEfmt ", 8);
902     header.fmt_size[0]  = 0x10;
903     header.fmt_size[1]  = header.fmt_size[2] = header.fmt_size[3] = 0;
904     header.data_fmt[0]  = 1; header.data_fmt[1] = 0; // PCM format
905     header.channels[0]  = channels; header.channels[1] = 0;
906     header.frequency[0] = rate & 0xff;
907     header.frequency[1] = (rate >> 8) & 0xff;
908     header.frequency[2] = (rate >> 16) & 0xff;
909     header.frequency[3] = (rate >> 24) & 0xff;
910 
911     int sample_byte_size = channels * bits / 8;
912     int byte_size = sample_byte_size * rate;
913     header.byte_size[0] = byte_size & 0xff;
914     header.byte_size[1] = (byte_size >> 8) & 0xff;
915     header.byte_size[2] = (byte_size >> 16) & 0xff;
916     header.byte_size[3] = (byte_size >> 24) & 0xff;
917     header.sample_byte_size[0] = sample_byte_size;
918     header.sample_byte_size[1] = 0;
919     header.sample_bit_size[0] = bits;
920     header.sample_bit_size[1]  = 0;
921 
922     memcpy(header.chunk_id, "data", 4);
923     header.data_length[0] = (char) (data_length & 0xff);
924     header.data_length[1] = (char) ((data_length >> 8) & 0xff);
925     header.data_length[2] = (char) ((data_length >> 16) & 0xff);
926     header.data_length[3] = (char) ((data_length >> 24) & 0xff);
927 
928     memcpy(buffer, &header, sizeof(header));
929 }
930 
931 
932 #ifdef USE_OGG_VORBIS
oc_read_func(void * ptr,size_t size,size_t nmemb,void * datasource)933 static size_t oc_read_func(void* ptr, size_t size, size_t nmemb,
934 			   void* datasource)
935 {
936     OVInfo* ogg_vorbis_info = (OVInfo*) datasource;
937 
938     ogg_int64_t len = size * nmemb;
939     if (ogg_vorbis_info->pos + len > ogg_vorbis_info->length)
940         len = ogg_vorbis_info->length - ogg_vorbis_info->pos;
941 
942     memcpy(ptr, ogg_vorbis_info->buf + ogg_vorbis_info->pos, len);
943     ogg_vorbis_info->pos += len;
944 
945     return len;
946 }
947 
948 
oc_seek_func(void * datasource,ogg_int64_t offset,int whence)949 static int oc_seek_func(void* datasource, ogg_int64_t offset, int whence)
950 {
951     OVInfo* ogg_vorbis_info = (OVInfo*) datasource;
952 
953     ogg_int64_t pos = 0;
954     if (whence == 0)
955         pos = offset;
956     else if (whence == 1)
957         pos = ogg_vorbis_info->pos + offset;
958     else if (whence == 2)
959         pos = ogg_vorbis_info->length + offset;
960 
961     if (pos < 0 || pos > ogg_vorbis_info->length) return -1;
962 
963     ogg_vorbis_info->pos = pos;
964 
965     return 0;
966 }
967 
968 
oc_close_func(void * datasource)969 static int oc_close_func(void* datasource)
970 {
971     return 0;
972 }
973 
974 
oc_tell_func(void * datasource)975 static long oc_tell_func(void* datasource)
976 {
977     OVInfo* ogg_vorbis_info = (OVInfo*) datasource;
978 
979     return ogg_vorbis_info->pos;
980 }
981 
982 
983 #endif
openOggVorbis(unsigned char * buf,long len,int & channels,int & rate)984 OVInfo* PonscripterLabel::openOggVorbis(unsigned char* buf, long len,
985                                         int &channels, int &rate)
986 {
987     OVInfo* ovi = NULL;
988 
989 #ifdef USE_OGG_VORBIS
990 
991     vorbis_comment *vc;
992     int isLoopLength = 0, i;
993     ogg_int64_t fullLength;
994     ovi = new OVInfo();
995 
996     ovi->buf = buf;
997     ovi->decoded_length = 0;
998     ovi->length = len;
999     ovi->pos = 0;
1000     ovi->loop         = -1;
1001     ovi->loop_start   = -1;
1002     ovi->loop_end     =  0;
1003     ovi->loop_len     =  0;
1004 
1005     ov_callbacks oc;
1006     oc.read_func  = oc_read_func;
1007     oc.seek_func  = oc_seek_func;
1008     oc.close_func = oc_close_func;
1009     oc.tell_func  = oc_tell_func;
1010     if (ov_open_callbacks(ovi, &ovi->ovf, NULL, 0, oc) < 0) {
1011         delete ovi;
1012         return NULL;
1013     }
1014 
1015     vorbis_info* vi = ov_info(&ovi->ovf, -1);
1016     if (vi == NULL) {
1017         ov_clear(&ovi->ovf);
1018         delete ovi;
1019         return NULL;
1020     }
1021 
1022     channels = vi->channels;
1023     ovi->channels = vi->channels;
1024     rate = vi->rate;
1025 
1026     /* vorbis loop start!! */
1027 
1028     vc = ov_comment(&ovi->ovf, -1);
1029     for (i = 0; i < vc->comments; i++) {
1030         int   paramLen = vc->comment_lengths[i] + 1;
1031         char *param = (char *)SDL_malloc((size_t)paramLen);
1032         char *argument  = param;
1033         char *value     = param;
1034         SDL_memset(param, 0, (size_t)paramLen);
1035         SDL_memcpy(param, vc->user_comments[i], (size_t)vc->comment_lengths[i]);
1036         value = SDL_strchr(param, '=');
1037         if (value == NULL) {
1038             value = param + paramLen - 1; /* set null */
1039         } else {
1040             *(value++) = '\0';
1041         }
1042 
1043         #ifdef __USE_ISOC99
1044         #define A_TO_OGG64(x) (ogg_int64_t)atoll(x)
1045         #else
1046         #define A_TO_OGG64(x) (ogg_int64_t)atol(x)
1047         #endif
1048 
1049         if (SDL_strcasecmp(argument, "LOOPSTART") == 0)
1050             ovi->loop_start = A_TO_OGG64(value);
1051         else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) {
1052             ovi->loop_len = A_TO_OGG64(value);
1053             isLoopLength = 1;
1054         }
1055         else if (SDL_strcasecmp(argument, "LOOPEND") == 0) {
1056             isLoopLength = 0;
1057             ovi->loop_end = A_TO_OGG64(value);
1058         }
1059 
1060         #undef A_TO_OGG64
1061         SDL_free(param);
1062     }
1063 
1064     if (isLoopLength == 1)
1065         ovi->loop_end = ovi->loop_start + ovi->loop_len;
1066     else
1067         ovi->loop_len = ovi->loop_end - ovi->loop_start;
1068 
1069     fullLength = ov_pcm_total(&ovi->ovf, -1);
1070     if (((ovi->loop_start >= 0) || (ovi->loop_end > 0)) &&
1071         ((ovi->loop_start < ovi->loop_end) || (ovi->loop_end == 0)) &&
1072          (ovi->loop_start < fullLength) &&
1073          (ovi->loop_end <= fullLength)) {
1074         if (ovi->loop_start < 0) ovi->loop_start = 0;
1075         if (ovi->loop_end == 0)  ovi->loop_end = fullLength;
1076         ovi->loop = 1;
1077     }
1078     /* vorbis loop ends!! */
1079 
1080     ovi->cvt.buf = NULL;
1081     ovi->cvt_len = 0;
1082     SDL_BuildAudioCVT(&ovi->cvt,
1083         AUDIO_S16, channels, rate,
1084         audio_format.format, audio_format.channels, audio_format.freq);
1085     ovi->mult1 = 10;
1086     ovi->mult2 = (int) (ovi->cvt.len_ratio * 10.0);
1087 
1088     ovi->decoded_length = ov_pcm_total(&ovi->ovf, -1) * channels * 2;
1089 #endif
1090 
1091     return ovi;
1092 }
1093 
1094 
closeOggVorbis(OVInfo * ovi)1095 int PonscripterLabel::closeOggVorbis(OVInfo* ovi)
1096 {
1097     if (ovi->buf) {
1098         ovi->buf = NULL;
1099 #ifdef USE_OGG_VORBIS
1100         ovi->length = 0;
1101         ovi->pos = 0;
1102         ov_clear(&ovi->ovf);
1103 #endif
1104     }
1105 
1106     if (ovi->cvt.buf) {
1107         delete[] ovi->cvt.buf;
1108         ovi->cvt.buf = NULL;
1109         ovi->cvt_len = 0;
1110     }
1111 
1112     delete ovi;
1113 
1114     return 0;
1115 }
1116