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, ¤t_section);
112 #else
113 long src_len = ov_read(&ovi->ovf, buf, len, 0, 2, 1, ¤t_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