SoundFile* load_sound_file(const std::string& filename) { if(filename.length() > 6 && filename.compare(filename.length()-6, 6, ".music") == 0) { return load_music_file(filename); } PHYSFS_file* file = PHYSFS_openRead(filename.c_str()); if(!file) { std::stringstream msg; msg << "Couldn't open '" << filename << "': " << PHYSFS_getLastError() << ", using dummy sound file."; throw SoundError(msg.str()); } try { char magic[4]; if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1) throw SoundError("Couldn't read magic, file too short"); PHYSFS_seek(file, 0); if(strncmp(magic, "RIFF", 4) == 0) return new WavSoundFile(file); else if(strncmp(magic, "OggS", 4) == 0) return new OggSoundFile(file, 0, -1); else throw SoundError("Unknown file format"); } catch(std::exception& e) { std::stringstream msg; msg << "Couldn't read '" << filename << "': " << e.what(); throw SoundError(msg.str()); } }
SoundFile* load_music_file(const std::string& filename) { lisp::Parser parser(false); const lisp::Lisp* root = parser.parse(filename); const lisp::Lisp* music = root->get_lisp("supertux-music"); if(music == NULL) throw SoundError("file is not a supertux-music file."); std::string raw_music_file; float loop_begin = 0; float loop_at = -1; music->get("file", raw_music_file); music->get("loop-begin", loop_begin); music->get("loop-at", loop_at); if(loop_begin < 0) { throw SoundError("can't loop from negative value"); } std::string basedir = FileSystem::dirname(filename); raw_music_file = FileSystem::normalize(basedir + raw_music_file); PHYSFS_file* file = PHYSFS_openRead(raw_music_file.c_str()); if(!file) { std::stringstream msg; msg << "Couldn't open '" << raw_music_file << "': " << PHYSFS_getLastError(); throw SoundError(msg.str()); } return new OggSoundFile(file, loop_begin, loop_at); }
size_t WavSoundFile::read(void* buffer, size_t buffer_size) { PHYSFS_sint64 end = datastart + size; PHYSFS_sint64 cur = PHYSFS_tell(file); if(cur >= end) return 0; size_t readsize = std::min(static_cast<size_t> (end - cur), buffer_size); if(PHYSFS_read(file, buffer, readsize, 1) != 1) throw SoundError("read error while reading samples"); #ifdef WORDS_BIGENDIAN if (bits_per_sample != 16) return readsize; char *tmp = (char*)buffer; size_t i; char c; for (i = 0; i < readsize / 2; i++) { c = tmp[2*i]; tmp[2*i] = tmp[2*i+1]; tmp[2*i+1] = c; } buffer = tmp; #endif return readsize; }
static inline uint16_t read16LE(PHYSFS_file* file) { uint16_t result; if(PHYSFS_readULE16(file, &result) == 0) throw SoundError("file too short"); return result; }
WavSoundFile::WavSoundFile(PHYSFS_file* file_) : file(file_), datastart() { assert(file); char magic[4]; if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1) throw SoundError("Couldn't read file magic (not a wave file)"); if(strncmp(magic, "RIFF", 4) != 0) { log_debug << "MAGIC: " << magic << std::endl; throw SoundError("file is not a RIFF wav file"); } uint32_t wavelen = read32LE(file); (void) wavelen; if(PHYSFS_read(file, magic, sizeof(magic), 1) != 1) throw SoundError("Couldn't read chunk header (not a wav file?)"); if(strncmp(magic, "WAVE", 4) != 0) throw SoundError("file is not a valid RIFF/WAVE file"); char chunkmagic[4]; uint32_t chunklen; // search audio data format chunk do { if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1) throw SoundError("EOF while searching format chunk"); chunklen = read32LE(file); if(strncmp(chunkmagic, "fmt ", 4) == 0) break; if(strncmp(chunkmagic, "fact", 4) == 0 || strncmp(chunkmagic, "LIST", 4) == 0) { // skip chunk if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0) throw SoundError("EOF while searching fmt chunk"); } else { throw SoundError("complex WAVE files not supported"); } } while(true); if(chunklen < 16) throw SoundError("Format chunk too short"); // parse format uint16_t encoding = read16LE(file); if(encoding != 1) throw SoundError("only PCM encoding supported"); channels = read16LE(file); rate = read32LE(file); uint32_t byterate = read32LE(file); (void) byterate; uint16_t blockalign = read16LE(file); (void) blockalign; bits_per_sample = read16LE(file); if(chunklen > 16) { if(PHYSFS_seek(file, PHYSFS_tell(file) + (chunklen-16)) == 0) throw SoundError("EOF while reading rest of format chunk"); } // set file offset to DATA chunk data do { if(PHYSFS_read(file, chunkmagic, sizeof(chunkmagic), 1) != 1) throw SoundError("EOF while searching data chunk"); chunklen = read32LE(file); if(strncmp(chunkmagic, "data", 4) == 0) break; // skip chunk if(PHYSFS_seek(file, PHYSFS_tell(file) + chunklen) == 0) throw SoundError("EOF while searching fmt chunk"); } while(true); datastart = PHYSFS_tell(file); size = static_cast<size_t> (chunklen); }
void WavSoundFile::reset() { if(PHYSFS_seek(file, datastart) == 0) throw SoundError("Couldn't seek to data start"); }
void GameEnvironment::initSound() throw(Exception) { triggerMonsterSounds = true; footstepSoundPlayed = false; sprintStepEnable = false; beepCounter = 0; delayBeep = 100; enemyMonsterRandomSoundId = 0; enemyMonsterRandomSoundFlag = 0; enemyMonsterRandomSoundCounter = 0; sound = new FMOD::Sound*[15]; channel = new FMOD::Channel*[15]; if (FMOD::System_Create(&system) != FMOD_OK) throw InitFmodError("Create the main object of fmod failed!\nPlease press OK and restart game."); if (system->init(10, FMOD_INIT_NORMAL, NULL) != FMOD_OK) throw InitFmodError("Initialization fmod failed!\nPlease press OK and restart game."); if (system->createSound("data/audio/monster/attack/alien_attack_ls.wav", FMOD_LOOP_OFF, 0, &sound[0]) != FMOD_OK) // zmiana z &sound throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/attack/alien_attack_ls.wav"); if (system->createSound("data/audio/footstep.wav", FMOD_LOOP_NORMAL, 0, &sound[1]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/footstep.wav"); if (system->createSound("data/audio/footstep_run.wav", FMOD_LOOP_NORMAL, 0, &sound[2]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/footstep_run.wav"); if (system->createSound("data/audio/ambient.wav", FMOD_LOOP_NORMAL, 0, &sound[3]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/ambient.wav"); if (system->createSound("data/audio/monster/attack/alien_attack_hs.wav", FMOD_LOOP_OFF, 0, &sound[4]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/attack/alien_attack_hs.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_1.wav", FMOD_LOOP_OFF, 0, &sound[5]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_1.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_2.wav", FMOD_LOOP_OFF, 0, &sound[6]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_2.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_3.wav", FMOD_LOOP_OFF, 0, &sound[7]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_3.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_4.wav", FMOD_LOOP_OFF, 0, &sound[8]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_4.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_5.wav", FMOD_LOOP_OFF, 0, &sound[9]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_5.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_6.wav", FMOD_LOOP_OFF, 0, &sound[10]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_6.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_7.wav", FMOD_LOOP_OFF, 0, &sound[11]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_7.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_8.wav", FMOD_LOOP_OFF, 0, &sound[12]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_8.wav"); if (system->createSound("data/audio/monster/random/alien_sounds_random_9.wav", FMOD_LOOP_OFF, 0, &sound[13]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/random/alien_sounds_random_9.wav"); if (system->createSound("data/audio/monster/footstep/alien_footstep_echo.wav", FMOD_LOOP_NORMAL, 0, &sound[14]) != FMOD_OK) throw SoundError("Failed to create sound!\nPlease press OK and restart game.\nExpected path of file:\ndata/audio/monster/footstep/alien_footstep_echo.wav"); else system->playSound(sound[3], NULL, false, &channel[3]); }
HRESULT Sound_VBL() { #if SCREENS_PER_SOUND_VBL != 1 static int screens_countdown=SCREENS_PER_SOUND_VBL; screens_countdown--;if (screens_countdown>0) return DD_OK; screens_countdown=SCREENS_PER_SOUND_VBL; cpu_time_of_last_sound_vbl=ABSOLUTE_CPU_TIME; #endif if (sound_internal_speaker){ static double op=0; int abc,chan=-1,max_vol=0,vol; // Find loudest channel for (abc=0;abc<3;abc++){ if ((psg_reg[PSGR_MIXER] & (1 << abc))==0){ // Channel enabled in mixer vol=(psg_reg[PSGR_AMPLITUDE_A+abc] & 15); if (vol>max_vol){ chan=abc; max_vol=vol; } } } if (chan==-1){ //no sound internal_speaker_sound_by_period(0); op=0; }else{ double p=((((int)psg_reg[chan*2+1] & 0xf) << 8) + psg_reg[chan*2]); p*=(1193181.0/125000.0); if (op!=p){ op=p; internal_speaker_sound_by_period((int)p); } } } if (psg_capture_file){ psg_capture_check_boundary(); } // This just clears up some clicks when Sound_VBL is called very soon after Sound_Start if (sound_first_vbl){ sound_first_vbl=0; return DS_OK; } if (sound_mode==SOUND_MODE_MUTE) return DS_OK; if (UseSound==0) return DSERR_GENERIC; // Not initialised if (SoundActive()==0) return DS_OK; // Not started log(""); log("SOUND: Start of Sound_VBL"); void *DatAdr[2]={NULL,NULL}; DWORD LockLength[2]={0,0}; DWORD s_time,write_time_1,write_time_2; HRESULT Ret; int *source_p; DWORD n_samples_per_vbl=(sound_freq*SCREENS_PER_SOUND_VBL)/shifter_freq; log(EasyStr("SOUND: Calculating time; psg_time_of_start_of_buffer=")+psg_time_of_start_of_buffer); s_time=SoundGetTime(); //we have data from time_of_last_vbl+PSG_WRITE_N_SCREENS_AHEAD*n_samples_per_vbl up to //wherever we want write_time_1=psg_time_of_last_vbl_for_writing; //3 screens ahead of where the cursor was // write_time_1=max(write_time_1,min_write_time); //minimum time for new write write_time_2=max(write_time_1+(n_samples_per_vbl+PSG_WRITE_EXTRA), s_time+(n_samples_per_vbl+PSG_WRITE_EXTRA)); if ((write_time_2-write_time_1)>PSG_CHANNEL_BUF_LENGTH){ write_time_2=write_time_1+PSG_CHANNEL_BUF_LENGTH; } // psg_last_write_time=write_time_2; DWORD time_of_next_vbl_to_write=max(s_time+n_samples_per_vbl*psg_write_n_screens_ahead,psg_time_of_next_vbl_for_writing); if (time_of_next_vbl_to_write>s_time+n_samples_per_vbl*(psg_write_n_screens_ahead+2)){ // time_of_next_vbl_to_write=s_time+n_samples_per_vbl*(psg_write_n_screens_ahead+2); // new bit added by Ant 9/1/2001 to stop the sound lagging behind } // get rid of it if it is causing problems log(EasyStr(" writing from ")+write_time_1+" to "+write_time_2+"; current play cursor at "+s_time+" ("+play_cursor+"); minimum write at "+min_write_time+" ("+write_cursor+")"); // log_write(EasyStr("writing ")+(write_time_1-s_time)+" samples ahead of play cursor, "+(write_time_1-min_write_time)+" ahead of min write"); #ifdef SHOW_WAVEFORM temp_waveform_display_counter=write_time_1 MOD_PSG_BUF_LENGTH; temp_waveform_play_counter=play_cursor; #endif log("SOUND: Working out data up to the end of this VBL plus a bit more for all channels"); for (int abc=2;abc>=0;abc--){ psg_write_buffer(abc,time_of_next_vbl_to_write+PSG_WRITE_EXTRA); } if (dma_sound_on_this_screen){ WORD w[2]={dma_sound_channel_buf[dma_sound_channel_buf_last_write_t-2],dma_sound_channel_buf[dma_sound_channel_buf_last_write_t-1]}; for (int i=0;i<PSG_WRITE_EXTRA;i++){ if (dma_sound_channel_buf_last_write_t>=DMA_SOUND_BUFFER_LENGTH) break; dma_sound_channel_buf[dma_sound_channel_buf_last_write_t++]=w[0]; dma_sound_channel_buf[dma_sound_channel_buf_last_write_t++]=w[1]; } }else{ WORD w1,w2; dma_sound_get_last_sample(&w1,&w2); dma_sound_channel_buf[0]=w1; dma_sound_channel_buf[1]=w2; dma_sound_channel_buf_last_write_t=0; } // write_time_1 and 2 are sample variables, convert to bytes DWORD StartByte=(write_time_1 MOD_PSG_BUF_LENGTH)*sound_bytes_per_sample; DWORD NumBytes=((write_time_2-write_time_1)+1)*sound_bytes_per_sample; log(EasyStr("SOUND: Trying to lock from ")+StartByte+", length "+NumBytes); Ret=SoundLockBuffer(StartByte,NumBytes,&DatAdr[0],&LockLength[0],&DatAdr[1],&LockLength[1]); if (Ret!=DSERR_BUFFERLOST){ if (Ret!=DS_OK){ log_write("SOUND: Lock totally failed, disaster!"); return SoundError("Lock for PSG Buffer Failed",Ret); } log(EasyStr("SOUND: Locked lengths ")+LockLength[0]+", "+LockLength[1]); int i=min(max(int(write_time_1-psg_time_of_last_vbl_for_writing),0),PSG_CHANNEL_BUF_LENGTH-10); int v=psg_voltage,dv=psg_dv; //restore from last time log(EasyStr("SOUND: Zeroing channels buffer up to ")+i); for (int j=0;j<i;j++){ psg_channels_buf[j]=VOLTAGE_FP(VOLTAGE_ZERO_LEVEL); //zero the start of the buffer } source_p=psg_channels_buf+i; int samples_left_in_buffer=max(PSG_CHANNEL_BUF_LENGTH-i,0); int countdown_to_storing_values=max((int)(time_of_next_vbl_to_write-write_time_1),0); //this is set when we are counting down to the start time of the next write bool store_values=false,chipmode=bool((sound_mode==SOUND_MODE_EMULATED) ? false:true); if (sound_mode==SOUND_MODE_SHARPSAMPLES) chipmode=(psg_reg[PSGR_MIXER] & b00111111)!=b00111111; if (sound_mode==SOUND_MODE_SHARPCHIP) chipmode=(psg_reg[PSGR_MIXER] & b00111111)==b00111111; if (sound_record){ sound_record_to_wav(countdown_to_storing_values,write_time_1,chipmode,source_p); } #ifdef WRITE_ONLY_SINE_WAVE DWORD t=write_time_1; #endif int val; log("SOUND: Starting to write to buffers"); WORD *lp_dma_sound_channel=dma_sound_channel_buf; WORD *lp_max_dma_sound_channel=dma_sound_channel_buf+dma_sound_channel_buf_last_write_t; BYTE *pb; WORD *pw; for (int n=0;n<2;n++){ if (DatAdr[n]){ pb=(BYTE*)(DatAdr[n]); pw=(WORD*)(DatAdr[n]); int c=min(int(LockLength[n]/sound_bytes_per_sample),samples_left_in_buffer),oc=c; if (c>countdown_to_storing_values){ c=countdown_to_storing_values; oc-=countdown_to_storing_values; store_values=true; } for (;;){ if (sound_num_bits==8){ if (chipmode){ if (sound_low_quality==0){ WRITE_SOUND_LOOP(CALC_V_CHIP,pb,BYTE,DWORD_B_1); }else{ WRITE_SOUND_LOOP(CALC_V_CHIP_25KHZ,pb,BYTE,DWORD_B_1); } }else{ WRITE_SOUND_LOOP(CALC_V_EMU,pb,BYTE,DWORD_B_1); } }else{ if (chipmode){ if (sound_low_quality==0){ WRITE_SOUND_LOOP(CALC_V_CHIP,pw,WORD,MSB_W ^ DWORD_W_0); }else{ WRITE_SOUND_LOOP(CALC_V_CHIP_25KHZ,pw,WORD,MSB_W ^ DWORD_W_0); } }else{ WRITE_SOUND_LOOP(CALC_V_EMU,pw,WORD,MSB_W ^ DWORD_W_0); } } if (store_values){ c=oc; psg_voltage=v; psg_dv=dv; store_values=false; countdown_to_storing_values=0x7fffffff; //don't store the values again. }else{ countdown_to_storing_values-=LockLength[n]/sound_bytes_per_sample; break; } } samples_left_in_buffer-=LockLength[n]/sound_bytes_per_sample; } } SoundUnlock(DatAdr[0],LockLength[0],DatAdr[1],LockLength[1]); while (source_p < (psg_channels_buf+PSG_CHANNEL_BUF_LENGTH)){ *(source_p++)=VOLTAGE_FP(VOLTAGE_ZERO_LEVEL); //zero the rest of the buffer } } psg_buf_pointer[0]=0; psg_buf_pointer[1]=0; psg_buf_pointer[2]=0; psg_time_of_last_vbl_for_writing=time_of_next_vbl_to_write; psg_time_of_next_vbl_for_writing=max(s_time+n_samples_per_vbl*(psg_write_n_screens_ahead+1), time_of_next_vbl_to_write+n_samples_per_vbl); psg_time_of_next_vbl_for_writing=min(psg_time_of_next_vbl_for_writing, s_time+(PSG_BUF_LENGTH/2)); log(EasyStr("SOUND: psg_time_of_next_vbl_for_writing=")+psg_time_of_next_vbl_for_writing); psg_n_samples_this_vbl=psg_time_of_next_vbl_for_writing-psg_time_of_last_vbl_for_writing; log("SOUND: End of Sound_VBL"); log(""); return DS_OK; }