1 /* -*- C++ -*-
2 *
3 * ONScripter_sound.cpp - Methods for playing sound
4 *
5 * Copyright (c) 2001-2020 Ogapee. All rights reserved.
6 *
7 * ogapee@aqua.dti2.ne.jp
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "ONScripter.h"
25 #include <new>
26 #if defined(LINUX)
27 #include <signal.h>
28 #endif
29
30 #ifdef ANDROID
31 extern "C" void playVideoAndroid(const char *filename);
32 #endif
33
34 #if defined(IOS)
35 extern "C" void playVideoIOS(const char *filename, bool click_flag, bool loop_flag);
36 #endif
37
38 #if defined(USE_AVIFILE)
39 #include "AVIWrapper.h"
40 #endif
41
42 #if defined(USE_SMPEG)
mp3callback(void * userdata,Uint8 * stream,int len)43 extern "C" void mp3callback( void *userdata, Uint8 *stream, int len )
44 {
45 SMPEG_playAudio( (SMPEG*)userdata, stream, len );
46 }
47 #endif
48
49 extern bool ext_music_play_once_flag;
50
51 extern "C"{
52 extern void musicFinishCallback();
53 extern Uint32 SDLCALL cdaudioCallback( Uint32 interval, void *param );
54 }
55 extern void midiCallback( int sig );
56 extern SDL_TimerID timer_cdaudio_id;
57
58 extern SDL_TimerID timer_bgmfade_id;
59 extern "C" Uint32 SDLCALL bgmfadeCallback( Uint32 interval, void *param );
60
61 #define TMP_MUSIC_FILE "tmp.mus"
62
playSound(const char * filename,int format,bool loop_flag,int channel)63 int ONScripter::playSound(const char *filename, int format, bool loop_flag, int channel)
64 {
65 if ( !audio_open_flag ) return SOUND_NONE;
66
67 long length = script_h.cBR->getFileLength( filename );
68 if (length == 0) return SOUND_NONE;
69
70 unsigned char *buffer;
71
72 if (format & SOUND_MUSIC &&
73 length == music_buffer_length &&
74 music_buffer ){
75 buffer = music_buffer;
76 }
77 else{
78 buffer = new(std::nothrow) unsigned char[length];
79 if (buffer == NULL){
80 fprintf( stderr, "failed to load [%s] because file size [%lu] is too large.\n", filename, length);
81 return SOUND_NONE;
82 }
83 script_h.cBR->getFile( filename, buffer );
84 }
85
86 if (format & SOUND_MUSIC){
87 music_info = Mix_LoadMUS_RW( SDL_RWFromMem( buffer, length ) );
88 Mix_VolumeMusic( music_volume );
89 Mix_HookMusicFinished( musicFinishCallback );
90 if ( Mix_PlayMusic( music_info, (music_play_loop_flag&&music_loopback_offset==0.0)?-1:0 ) == 0 ){
91 music_buffer = buffer;
92 music_buffer_length = length;
93 return SOUND_MUSIC;
94 }
95 }
96
97 if (format & SOUND_CHUNK){
98 Mix_Chunk *chunk = Mix_LoadWAV_RW(SDL_RWFromMem(buffer, length), 1);
99 if (playWave(chunk, format, loop_flag, channel) == 0){
100 delete[] buffer;
101 return SOUND_CHUNK;
102 }
103 }
104
105 /* check WMA */
106 if ( buffer[0] == 0x30 && buffer[1] == 0x26 &&
107 buffer[2] == 0xb2 && buffer[3] == 0x75 ){
108 delete[] buffer;
109 return SOUND_OTHER;
110 }
111
112 if (format & SOUND_MIDI){
113 FILE *fp;
114 if ( (fp = fopen(TMP_MUSIC_FILE, "wb", true)) == NULL){
115 fprintf(stderr, "can't open temporaly MIDI file %s\n", TMP_MUSIC_FILE);
116 }
117 else{
118 fwrite(buffer, 1, length, fp);
119 fclose( fp );
120 ext_music_play_once_flag = !loop_flag;
121 if (playMIDI(loop_flag) == 0){
122 delete[] buffer;
123 return SOUND_MIDI;
124 }
125 }
126 }
127
128 delete[] buffer;
129
130 return SOUND_OTHER;
131 }
132
playCDAudio()133 void ONScripter::playCDAudio()
134 {
135 if ( cdaudio_flag ){
136 #ifdef USE_CDROM
137 if ( cdrom_info ){
138 int length = cdrom_info->track[current_cd_track - 1].length / 75;
139 SDL_CDPlayTracks( cdrom_info, current_cd_track - 1, 0, 1, 0 );
140 timer_cdaudio_id = SDL_AddTimer( length * 1000, cdaudioCallback, NULL );
141 }
142 #endif
143 }
144 else{
145 char filename[256];
146 sprintf( filename, "cd\\track%2.2d.mp3", current_cd_track );
147 int ret = playSound( filename, SOUND_MUSIC, cd_play_loop_flag );
148 if (ret == SOUND_MUSIC) return;
149
150 sprintf( filename, "cd\\track%2.2d.ogg", current_cd_track );
151 ret = playSound( filename, SOUND_MUSIC, cd_play_loop_flag );
152 if (ret == SOUND_MUSIC) return;
153
154 sprintf( filename, "cd\\track%2.2d.wav", current_cd_track );
155 ret = playSound( filename, SOUND_MUSIC|SOUND_CHUNK, cd_play_loop_flag, MIX_BGM_CHANNEL );
156 }
157 }
158
playWave(Mix_Chunk * chunk,int format,bool loop_flag,int channel)159 int ONScripter::playWave(Mix_Chunk *chunk, int format, bool loop_flag, int channel)
160 {
161 if (!chunk) return -1;
162
163 Mix_Pause( channel );
164 if ( wave_sample[channel] ) Mix_FreeChunk( wave_sample[channel] );
165 wave_sample[channel] = chunk;
166
167 if (channel == 0) Mix_Volume( channel, voice_volume * MIX_MAX_VOLUME / 100 );
168 else if (channel == MIX_BGM_CHANNEL) Mix_Volume( channel, music_volume * MIX_MAX_VOLUME / 100 );
169 else Mix_Volume( channel, se_volume * MIX_MAX_VOLUME / 100 );
170
171 if ( !(format & SOUND_PRELOAD) )
172 Mix_PlayChannel( channel, wave_sample[channel], loop_flag?-1:0 );
173
174 return 0;
175 }
176
playMIDI(bool loop_flag)177 int ONScripter::playMIDI(bool loop_flag)
178 {
179 Mix_SetMusicCMD(midi_cmd);
180
181 char midi_filename[256];
182 sprintf(midi_filename, "%s%s", save_dir?save_dir:archive_path, TMP_MUSIC_FILE);
183 if ((midi_info = Mix_LoadMUS(midi_filename)) == NULL) return -1;
184
185 int midi_looping = loop_flag ? -1 : 0;
186
187 #if defined(LINUX)
188 signal(SIGCHLD, midiCallback);
189 if (midi_cmd) midi_looping = 0;
190 #endif
191
192 Mix_VolumeMusic(music_volume);
193 Mix_PlayMusic(midi_info, midi_looping);
194 current_cd_track = -2;
195
196 return 0;
197 }
198
199 #if defined(USE_SMPEG)
200 #if defined(USE_SDL_RENDERER)
201 struct OverlayInfo{
202 SDL_Overlay overlay;
203 SDL_mutex *mutex;
204 };
smpeg_filter_callback(SDL_Overlay * dst,SDL_Overlay * src,SDL_Rect * region,SMPEG_FilterInfo * filter_info,void * data)205 static void smpeg_filter_callback(SDL_Overlay * dst, SDL_Overlay * src, SDL_Rect * region, SMPEG_FilterInfo * filter_info, void * data)
206 {
207 if (dst){
208 dst->w = 0;
209 dst->h = 0;
210 }
211
212 OverlayInfo *oi = (OverlayInfo*)data;
213
214 SDL_mutexP(oi->mutex);
215 memcpy(oi->overlay.pixels[0], src->pixels[0],
216 oi->overlay.w*oi->overlay.h + (oi->overlay.w/2)*(oi->overlay.h/2)*2);
217 SDL_mutexV(oi->mutex);
218 }
smpeg_filter_destroy(struct SMPEG_Filter * filter)219 static void smpeg_filter_destroy(struct SMPEG_Filter * filter)
220 {
221 }
222 #elif defined(ANDROID)
smpeg_filter_callback(SDL_Overlay * dst,SDL_Overlay * src,SDL_Rect * region,SMPEG_FilterInfo * filter_info,void * data)223 static void smpeg_filter_callback(SDL_Overlay * dst, SDL_Overlay * src, SDL_Rect * region, SMPEG_FilterInfo * filter_info, void * data)
224 {
225 if (dst){
226 dst->w = 0;
227 dst->h = 0;
228 }
229
230 ONScripter *ons = (ONScripter*)data;
231 AnimationInfo *ai = ons->getSMPEGInfo();
232 ai->convertFromYUV(src);
233 }
smpeg_filter_destroy(struct SMPEG_Filter * filter)234 static void smpeg_filter_destroy(struct SMPEG_Filter * filter)
235 {
236 }
237 #endif
238 #endif
239
playMPEG(const char * filename,bool click_flag,bool loop_flag,bool nosound_flag)240 int ONScripter::playMPEG(const char *filename, bool click_flag, bool loop_flag, bool nosound_flag)
241 {
242 unsigned long length = script_h.cBR->getFileLength( filename );
243 if (length == 0){
244 fprintf( stderr, " *** can't find file [%s] ***\n", filename );
245 return 0;
246 }
247
248 #ifdef IOS
249 char *absolute_filename = new char[ strlen(archive_path) + strlen(filename) + 1 ];
250 sprintf( absolute_filename, "%s%s", archive_path, filename );
251 playVideoIOS(absolute_filename, click_flag, loop_flag);
252 delete[] absolute_filename;
253 #endif
254
255 int ret = 0;
256 #if defined(USE_SMPEG)
257 stopSMPEG();
258 layer_smpeg_buffer = new unsigned char[length];
259 script_h.cBR->getFile( filename, layer_smpeg_buffer );
260 SMPEG_Info info;
261 layer_smpeg_sample = SMPEG_new_rwops( SDL_RWFromMem( layer_smpeg_buffer, length ), &info, 0 );
262 unsigned char packet_code[4] = {0x00, 0x00, 0x01, 0xba};
263 if (SMPEG_error( layer_smpeg_sample ) ||
264 layer_smpeg_buffer[0] != packet_code[0] ||
265 layer_smpeg_buffer[1] != packet_code[1] ||
266 layer_smpeg_buffer[2] != packet_code[2] ||
267 layer_smpeg_buffer[3] != packet_code[3] ||
268 (layer_smpeg_buffer[4] & 0xf0) != 0x20){
269 stopSMPEG();
270 #ifdef ANDROID
271 playVideoAndroid(filename);
272 #endif
273 return ret;
274 }
275
276 SMPEG_enableaudio( layer_smpeg_sample, 0 );
277 if (audio_open_flag && !nosound_flag){
278 int mpegversion, frequency, layer, bitrate;
279 char mode[10];
280 sscanf(info.audio_string,
281 "MPEG-%d Layer %d %dkbit/s %dHz %s",
282 &mpegversion, &layer, &bitrate, &frequency, mode);
283 printf("MPEG-%d Layer %d %dkbit/s %dHz %s\n",
284 mpegversion, layer, bitrate, frequency, mode);
285
286 openAudio(frequency);
287
288 SMPEG_actualSpec( layer_smpeg_sample, &audio_format );
289 SMPEG_enableaudio( layer_smpeg_sample, 1 );
290 }
291 SMPEG_enablevideo( layer_smpeg_sample, 1 );
292
293 #if defined(USE_SDL_RENDERER)
294 SMPEG_setdisplay(layer_smpeg_sample, accumulation_surface, NULL, NULL);
295
296 OverlayInfo oi;
297 Uint16 pitches[3];
298 Uint8 *pixels[3];
299 oi.overlay.format = SDL_YV12_OVERLAY;
300 oi.overlay.w = info.width;
301 oi.overlay.h = info.height;
302 oi.overlay.planes = 3;
303 pitches[0] = info.width;
304 pitches[1] = info.width/2;
305 pitches[2] = info.width/2;
306 oi.overlay.pitches = pitches;
307 Uint8 *pixel_buf = new Uint8[info.width*info.height + (info.width/2)*(info.height/2)*2];
308 pixels[0] = pixel_buf;
309 pixels[1] = pixel_buf + info.width*info.height;
310 pixels[2] = pixel_buf + info.width*info.height + (info.width/2)*(info.height/2);
311 oi.overlay.pixels = pixels;
312 oi.mutex = SDL_CreateMutex();
313
314 texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_TARGET, info.width, info.height);
315
316 layer_smpeg_filter.data = &oi;
317 layer_smpeg_filter.callback = smpeg_filter_callback;
318 layer_smpeg_filter.destroy = smpeg_filter_destroy;
319 SMPEG_filter(layer_smpeg_sample, &layer_smpeg_filter);
320 #elif defined(ANDROID)
321 SMPEG_setdisplay(layer_smpeg_sample, screen_surface, NULL, NULL);
322 AnimationInfo *smpeg_info_back = smpeg_info;
323 smpeg_info = new AnimationInfo();
324 smpeg_info->image_surface = accumulation_surface;
325 layer_smpeg_filter.data = this;
326 layer_smpeg_filter.callback = smpeg_filter_callback;
327 layer_smpeg_filter.destroy = smpeg_filter_destroy;
328 SMPEG_filter(layer_smpeg_sample, &layer_smpeg_filter);
329 #else
330 SMPEG_setdisplay(layer_smpeg_sample, screen_surface, NULL, NULL);
331 #endif
332 if (!nosound_flag){
333 SMPEG_setvolume(layer_smpeg_sample, music_volume);
334 if (info.has_audio) Mix_HookMusic(mp3callback, layer_smpeg_sample);
335 }
336
337 SMPEG_loop(layer_smpeg_sample, loop_flag?1:0);
338 SMPEG_play(layer_smpeg_sample);
339
340 bool done_flag = false;
341 while(!(done_flag & click_flag) && SMPEG_status(layer_smpeg_sample) == SMPEG_PLAYING){
342 SDL_Event event;
343
344 while( SDL_PollEvent( &event ) ){
345 switch (event.type){
346 case SDL_KEYUP:
347 if ( ((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_RETURN ||
348 ((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_SPACE ||
349 ((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_ESCAPE )
350 done_flag = true;
351 if ( ((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_RCTRL)
352 ctrl_pressed_status &= ~0x01;
353
354 if ( ((SDL_KeyboardEvent *)&event)->keysym.sym == SDLK_LCTRL)
355 ctrl_pressed_status &= ~0x02;
356 break;
357 case SDL_QUIT:
358 ret = 1;
359 case SDL_MOUSEBUTTONUP:
360 done_flag = true;
361 break;
362 default:
363 break;
364 }
365 }
366
367 #if defined(USE_SDL_RENDERER)
368 SDL_mutexP(oi.mutex);
369 flushDirectYUV(&oi.overlay);
370 SDL_mutexV(oi.mutex);
371 #elif defined(ANDROID)
372 SDL_mutexP(smpeg_info->mutex);
373 flushDirect(screen_rect, REFRESH_NONE_MODE);
374 SDL_mutexV(smpeg_info->mutex);
375 #endif
376 SDL_Delay( 1 );
377 }
378
379 if (!nosound_flag)
380 Mix_HookMusic( NULL, NULL );
381 stopSMPEG();
382 if (!nosound_flag)
383 openAudio();
384 #if defined(USE_SDL_RENDERER)
385 delete[] pixel_buf;
386 SDL_DestroyMutex(oi.mutex);
387 texture = SDL_CreateTextureFromSurface(renderer, accumulation_surface);
388 #elif defined(ANDROID)
389 smpeg_info->image_surface = NULL;
390 delete smpeg_info;
391 smpeg_info = smpeg_info_back;
392 #endif
393 #elif !defined(IOS)
394 fprintf( stderr, "mpegplay command is disabled.\n" );
395 #endif
396
397 return ret;
398 }
399
playAVI(const char * filename,bool click_flag)400 int ONScripter::playAVI( const char *filename, bool click_flag )
401 {
402 unsigned long length = script_h.cBR->getFileLength( filename );
403 if (length == 0){
404 fprintf( stderr, " *** can't find file [%s] ***\n", filename );
405 return 0;
406 }
407
408 #ifdef ANDROID
409 playVideoAndroid(filename);
410 return 0;
411 #endif
412
413 #if defined(USE_AVIFILE) && !defined(USE_SDL_RENDERER)
414 char *absolute_filename = new char[ strlen(archive_path) + strlen(filename) + 1 ];
415 sprintf( absolute_filename, "%s%s", archive_path, filename );
416 for ( unsigned int i=0 ; i<strlen( absolute_filename ) ; i++ )
417 if ( absolute_filename[i] == '/' ||
418 absolute_filename[i] == '\\' )
419 absolute_filename[i] = DELIMITER;
420
421 if ( audio_open_flag ) Mix_CloseAudio();
422
423 AVIWrapper *avi = new AVIWrapper();
424 if ( avi->init( absolute_filename, false ) == 0 &&
425 avi->initAV( screen_surface, audio_open_flag ) == 0 ){
426 if (avi->play( click_flag )) return 1;
427 }
428 delete avi;
429 delete[] absolute_filename;
430
431 if ( audio_open_flag ){
432 Mix_CloseAudio();
433 openAudio();
434 }
435 #else
436 fprintf( stderr, "avi command is disabled.\n" );
437 #endif
438
439 return 0;
440 }
441
stopBGM(bool continue_flag)442 void ONScripter::stopBGM( bool continue_flag )
443 {
444 removeBGMFadeEvent();
445 if (timer_bgmfade_id) SDL_RemoveTimer( timer_bgmfade_id );
446 timer_bgmfade_id = NULL;
447 mp3fadeout_duration_internal = 0;
448
449 #ifdef USE_CDROM
450 if ( cdaudio_flag && cdrom_info ){
451 extern SDL_TimerID timer_cdaudio_id;
452
453 if ( timer_cdaudio_id ){
454 SDL_RemoveTimer( timer_cdaudio_id );
455 timer_cdaudio_id = NULL;
456 }
457 if (SDL_CDStatus( cdrom_info ) >= CD_PLAYING )
458 SDL_CDStop( cdrom_info );
459 }
460 #endif
461
462 if ( wave_sample[MIX_BGM_CHANNEL] ){
463 Mix_Pause( MIX_BGM_CHANNEL );
464 Mix_FreeChunk( wave_sample[MIX_BGM_CHANNEL] );
465 wave_sample[MIX_BGM_CHANNEL] = NULL;
466 }
467
468 if ( music_info ){
469 ext_music_play_once_flag = true;
470 Mix_HaltMusic();
471 Mix_FreeMusic( music_info );
472 music_info = NULL;
473 }
474
475 if ( midi_info ){
476 ext_music_play_once_flag = true;
477 Mix_HaltMusic();
478 Mix_FreeMusic( midi_info );
479 midi_info = NULL;
480 }
481
482 if ( !continue_flag ){
483 setStr( &music_file_name, NULL );
484 music_play_loop_flag = false;
485 if (music_buffer){
486 delete[] music_buffer;
487 music_buffer = NULL;
488 }
489
490 setStr( &midi_file_name, NULL );
491 midi_play_loop_flag = false;
492
493 current_cd_track = -1;
494 }
495 }
496
stopAllDWAVE()497 void ONScripter::stopAllDWAVE()
498 {
499 for (int ch=0; ch<ONS_MIX_CHANNELS ; ch++)
500 if ( wave_sample[ch] ){
501 Mix_Pause( ch );
502 Mix_FreeChunk( wave_sample[ch] );
503 wave_sample[ch] = NULL;
504 }
505 }
506
playClickVoice()507 void ONScripter::playClickVoice()
508 {
509 if ( clickstr_state == CLICK_NEWPAGE ){
510 if ( clickvoice_file_name[CLICKVOICE_NEWPAGE] )
511 playSound(clickvoice_file_name[CLICKVOICE_NEWPAGE],
512 SOUND_CHUNK, false, MIX_WAVE_CHANNEL);
513 }
514 else if ( clickstr_state == CLICK_WAIT ){
515 if ( clickvoice_file_name[CLICKVOICE_NORMAL] )
516 playSound(clickvoice_file_name[CLICKVOICE_NORMAL],
517 SOUND_CHUNK, false, MIX_WAVE_CHANNEL);
518 }
519 }
520