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