diff options
Diffstat (limited to 'Source/Sound/soundtrack.cpp')
-rw-r--r-- | Source/Sound/soundtrack.cpp | 1448 |
1 files changed, 1448 insertions, 0 deletions
diff --git a/Source/Sound/soundtrack.cpp b/Source/Sound/soundtrack.cpp new file mode 100644 index 00000000..add5d819 --- /dev/null +++ b/Source/Sound/soundtrack.cpp @@ -0,0 +1,1448 @@ +//----------------------------------------------------------------------------- +// Name: soundtrack.cpp +// Developer: Wolfire Games LLC +// Description: +// License: Read below +//----------------------------------------------------------------------------- +// +// Copyright 2022 Wolfire Games LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//----------------------------------------------------------------------------- +#include "soundtrack.h" + +#include <Sound/Loader/void_loader.h> +#include <Sound/Loader/ogg_loader.h> + +#include <Logging/logdata.h> +#include <Utility/strings.h> + +#include <algorithm> +#include <cmath> + +static void ToHighResBufferSegment( HighResBufferSegment& out, const BufferSegment& in ) { + out.sample_rate = in.sample_rate; + out.channels = in.channels; + out.data_size = in.FlatSampleCount() * out.SampleSize(); + + for( size_t i = 0; i < in.FlatSampleCount(); i++ ) { + int32_t hbuf; + char* hbuf_c = (char*)&hbuf; + int16_t lbuf; + char* lbuf_c = (char*)&lbuf; + + lbuf_c[0] = in.buf[i*2+0]; + lbuf_c[1] = in.buf[i*2+1]; + + hbuf = lbuf; + + out.buf[i*4+0] = hbuf_c[0]; + out.buf[i*4+1] = hbuf_c[1]; + out.buf[i*4+2] = hbuf_c[2]; + out.buf[i*4+3] = hbuf_c[3]; + } +} + +static void ToBufferSegment( BufferSegment& out, const HighResBufferSegment& in ) { + out.sample_rate = in.sample_rate; + out.channels = in.channels; + out.data_size = in.FlatSampleCount() * out.SampleSize(); + for( size_t i = 0; i < in.FlatSampleCount(); i++ ) { + int32_t hbuf; + char* hbuf_c = (char*)&hbuf; + int16_t lbuf; + char* lbuf_c = (char*)&lbuf; + + hbuf_c[0] = in.buf[i*4+0]; + hbuf_c[1] = in.buf[i*4+1]; + hbuf_c[2] = in.buf[i*4+2]; + hbuf_c[3] = in.buf[i*4+3]; + + lbuf = hbuf; + + out.buf[i*2+0] = lbuf_c[0]; + out.buf[i*2+1] = lbuf_c[1]; + } +} + +PlayedInterface::PlayedInterface( Soundtrack* _owner ) : owner(_owner) { + +} + +PlayedSongInterface::PlayedSongInterface( Soundtrack* _owner ) : PlayedInterface(_owner), song() { + +} + +PlayedSongInterface::PlayedSongInterface(Soundtrack* _owner, const MusicXMLParser::Song& _song) : PlayedInterface(_owner), song(_song) { + +} + +const std::string PlayedSongInterface::GetSongName() const { + return std::string(song.name); +} + +const std::string PlayedSongInterface::GetSongType() const { + return std::string(song.type); +} + +const MusicXMLParser::Song& PlayedSongInterface::GetSong() const { + return song; +} + +Soundtrack::PlayedSegment::PlayedSegment(Soundtrack* _owner ) : +PlayedInterface(_owner), +data(rc_baseLoader(new voidLoader()) ), +overlapped_transition(false), +started(false) { +} + +Soundtrack::PlayedSegment::PlayedSegment( Soundtrack* _owner, const MusicXMLParser::Segment& _segment, bool overlapped_transition ) : +PlayedInterface(_owner), +data(rc_baseLoader(new voidLoader()) ), +segment( _segment ), +overlapped_transition( overlapped_transition ), +started(false) { + if( strlen( segment.path ) > 0 ) { + Path p = FindFilePath( segment.path, kAnyPath ); + if( p.isValid() ) { + data = rc_baseLoader(new oggLoader(p)); + } else { + LOGE << "Segment " << segment << " doesn't refer to a valid resource" << std::endl; + } + } +} + +void Soundtrack::PlayedSegment::update( HighResBufferSegment* out ) { + int result = 1; + + BufferSegment buffer; + + buffer.channels = data->get_channels(); + buffer.sample_rate = data->get_sample_rate(); + + while( result > 0 && buffer.data_size < buffer.BUF_SIZE ) { + result = data->stream_buffer_int16( buffer.buf + buffer.data_size, (int) (buffer.BUF_SIZE - buffer.data_size) ); + + if( result > 0 ) { + buffer.data_size += (size_t)result; + } else if( result < 0 ) { + LOGE << "Got error from underlying stream_buffer_int16 call" << std::endl; + buffer.error_code = result; + } else { + //End of stream. + } + } + + ToHighResBufferSegment(*out, buffer); +} + +const MusicXMLParser::Segment& Soundtrack::PlayedSegment::GetSegment() +{ + return segment; +} + +bool Soundtrack::PlayedSegment::IsAtEnd() +{ + return data->is_at_end(); +} + +void Soundtrack::PlayedSegment::Rewind() +{ + data->rewind(); +} + +int64_t Soundtrack::PlayedSegment::GetPCMPos() +{ + return data->get_pcm_pos(); +} + +void Soundtrack::PlayedSegment::SetPCMPos( int64_t pos ) +{ + data->set_pcm_pos(pos); +} + +int64_t Soundtrack::PlayedSegment::GetPCMCount() +{ + return data->get_sample_count(); +} + +int Soundtrack::PlayedSegment::SampleRate() +{ + return data->get_sample_rate(); +} + +int Soundtrack::PlayedSegment::Channels() +{ + return data->get_channels(); +} + +const std::string Soundtrack::PlayedSegment::GetSegmentName() const +{ + return segment.name; +} + +Soundtrack::PlayedSegmentedSong::PlayedSegmentedSong(Soundtrack* _owner, const MusicXMLParser::Song& _song) : +PlayedSongInterface(_owner, _song), +currentSegment(_owner, _song.GetStartSegment(), false), +mixer(TransitionMixer::Linear,1.0f) +{ + +} + +Soundtrack::PlayedSegmentedSong::~PlayedSegmentedSong( ) { +} + + +void Soundtrack::PlayedSegmentedSong::SetGain(float v) { + //nil +} + +float Soundtrack::PlayedSegmentedSong::GetGain() { + return 1.0f; +} + +void Soundtrack::PlayedSegmentedSong::update( HighResBufferSegment* buffer ) { + //If we _just_ started playing this song, set position of segment to be what it was last this song played. + if( currentSegment.started == false ) { + currentSegment.started = true; + } + + //Aligned transition is a quick transition where the next song is set to the same raw position + //And we mix together the two buffers. + if( segmentQueue.size() > 0 && segmentQueue.front().overlapped_transition ) + { + HighResBufferSegment first; + HighResBufferSegment second; + + if( segmentQueue.front().started == false ) + { + int64_t pos = currentSegment.GetPCMPos(); + segmentQueue.front().SetPCMPos(pos); + segmentQueue.front().started = true; + mixer.Reset(); + } + + currentSegment.update(&first); + segmentQueue.front().update(&second); + + if( currentSegment.IsAtEnd() || segmentQueue.front().IsAtEnd() ) + { + currentSegment.Rewind(); + segmentQueue.front().Rewind(); + } + + //If we're done transitioning, go to next. + if( mixer.Step( buffer, &first, &second ) ) + { + currentSegment = segmentQueue.front(); + segmentQueue.pop_front(); + } + } + else + { + currentSegment.update(buffer); + + if( currentSegment.IsAtEnd() ) + { + if( segmentQueue.size() > 0 ) + { + currentSegment = segmentQueue.front(); + segmentQueue.pop_front(); + } + else + { + currentSegment.Rewind(); + } + } + } + + if( buffer->data_size == 0 ) { + LOGE << "Buffer empty in PlayedSegmentedSong" << std::endl; + } + +} + +bool Soundtrack::PlayedSegmentedSong::QueueSegment( const MusicXMLParser::Segment& nextSeg ) +{ + if( (segmentQueue.empty() && currentSegment.GetSegment() != nextSeg ) || (segmentQueue.size() > 0 && segmentQueue.back().GetSegment() != nextSeg) ) + { + LOGI << "Queueing segment " << nextSeg << std::endl; + + while( segmentQueue.size() > 1 ) + { + segmentQueue.pop_back(); + } + + segmentQueue.push_back(PlayedSegment(owner,nextSeg,false)); + + return true; + } + else + { + return false; + } + return false; +} + + +bool Soundtrack::PlayedSegmentedSong::TransitionIntoSegment( const MusicXMLParser::Segment& newSeg ) +{ + if( (segmentQueue.empty() && currentSegment.GetSegment() != newSeg ) || (segmentQueue.size() > 0 && segmentQueue.back().GetSegment() != newSeg) ) + { + LOGI << "Transitioning into segment " << newSeg << std::endl; + while( segmentQueue.size() > 1 ) + { + segmentQueue.pop_back(); + } + + segmentQueue.push_back(PlayedSegment(owner, newSeg,true)); + + return true; + } + else + { + return false; + } + +} + +bool Soundtrack::PlayedSegmentedSong::SetSegment( const MusicXMLParser::Segment& nextSeg ) { + LOGI << "Setting segment " << nextSeg << std::endl; + segmentQueue.clear(); + currentSegment = PlayedSegment(owner, nextSeg,false); + return true; +} + +const std::string Soundtrack::PlayedSegmentedSong::GetSegmentName() const { + return currentSegment.GetSegmentName(); +} + +int Soundtrack::PlayedSegmentedSong::SampleRate() { + return currentSegment.SampleRate(); +} + +int Soundtrack::PlayedSegmentedSong::Channels() { + return currentSegment.Channels(); +} + +void Soundtrack::PlayedSegmentedSong::Rewind() { + //Nil +} + +bool Soundtrack::PlayedSegmentedSong::IsAtEnd() { + return false; +} + +void Soundtrack::PlayedSegmentedSong::SetPCMPos( int64_t c ) { + currentSegment.SetPCMPos(c); +} + +int64_t Soundtrack::PlayedSegmentedSong::GetPCMPos() { + return currentSegment.GetPCMPos(); +} + +int64_t Soundtrack::PlayedSegmentedSong::GetPCMCount() { + return currentSegment.GetPCMCount(); +} + +Soundtrack::PlayedSingleSong::PlayedSingleSong(Soundtrack* _owner) : +PlayedSongInterface(_owner), +data(rc_baseLoader(new voidLoader()) ), +started(false), +target_gain(1.0f), +current_gain(0.0f), +from_gain(0.0f), +current_volume_step(0), +volume_change_per_second(1.0f) +{ + +} + +Soundtrack::PlayedSingleSong::PlayedSingleSong(Soundtrack* _owner ,const MusicXMLParser::Song& _song ) : +PlayedSongInterface(_owner, _song), +data(rc_baseLoader(new voidLoader())), +started(false) , +target_gain(1.0f), +from_gain(0.0f), +current_gain(0.0f), +current_volume_step(0), +volume_change_per_second(1.0f) +{ + if( strlen(song.file_path) > 0 ) { + Path p = FindFilePath( song.file_path, kAnyPath ); + if( p.isValid() ) { + data = rc_baseLoader(new oggLoader(p)); + } else { + LOGE << "Segment " << song << " doesn't refer to a valid resource" << std::endl; + } + } +} + +void Soundtrack::PlayedSingleSong::update( HighResBufferSegment* buffer ) { + int result = 1; + + BufferSegment songbuf; + songbuf.channels = data->get_channels(); + songbuf.sample_rate = data->get_sample_rate(); + + while( result > 0 && songbuf.data_size < songbuf.BUF_SIZE ) { + result = data->stream_buffer_int16( songbuf.buf + songbuf.data_size, (int)(songbuf.BUF_SIZE - songbuf.data_size) ); + + if( result > 0 ) { + songbuf.data_size += (size_t)result; + } else if( result < 0 ) { + LOGE << "Got error from underlying stream_buffer_int16 call" << std::endl; + songbuf.error_code = result; + } else { + //End of stream. + } + } + + if( songbuf.data_size > 0 ) { + const size_t start = current_volume_step; + //How many bytes for a second of transition times the time of transition. + const size_t end = (size_t) std::abs(((from_gain-target_gain)*volume_change_per_second) * songbuf.sample_rate * songbuf.channels * 2); + + for( size_t i = 0; i < songbuf.data_size/songbuf.channels; i += 2 ) { + float d = 1.0f; + + if( end > 0 ) { + d = (start+i*songbuf.channels)/(float)end; + if( d > 1.0f ) { + d = 1.0f; + } + if( d < 0.0f ) { + d = 0.0f; + } + } + + current_gain = from_gain*(1.0f-d)+target_gain*d; + + union { + int16_t full; + char part[2]; + } cast; + + for( size_t c = 0; c < (size_t)songbuf.channels; c++ ) { + cast.part[0] = songbuf.buf[i*songbuf.channels+(c*2)+0]; + cast.part[1] = songbuf.buf[i*songbuf.channels+(c*2)+1]; + + cast.full = (int16_t) (cast.full * current_gain); + + songbuf.buf[i*songbuf.channels+(c*2)+0] = cast.part[0]; + songbuf.buf[i*songbuf.channels+(c*2)+1] = cast.part[1]; + } + } + + current_volume_step += songbuf.data_size; + if( (size_t)current_volume_step > end ) { + current_volume_step = end; + from_gain = target_gain; + } + } + + if( IsAtEnd() ) { + Rewind(); + } + + ToHighResBufferSegment( *buffer, songbuf ); +} + +bool Soundtrack::PlayedSingleSong::IsAtEnd() { + return data->is_at_end(); +} + +void Soundtrack::PlayedSingleSong::Rewind() { + data->rewind(); +} + +int64_t Soundtrack::PlayedSingleSong::GetPCMPos() { + return data->get_pcm_pos(); +} + +void Soundtrack::PlayedSingleSong::SetPCMPos( int64_t pos ) { + data->set_pcm_pos(pos); +} + +int64_t Soundtrack::PlayedSingleSong::GetPCMCount() { + return data->get_sample_count(); +} + +void Soundtrack::PlayedSingleSong::SetGain( float v ) { + from_gain = current_gain; + target_gain = v; +} + +float Soundtrack::PlayedSingleSong::GetGain() { + return current_gain; +} + +int Soundtrack::PlayedSingleSong::SampleRate() { + return data->get_sample_rate(); +} + +int Soundtrack::PlayedSingleSong::Channels() { + return data->get_channels(); +} + +Soundtrack::PlayedLayeredSong::PlayedLayeredSong( Soundtrack* _owner ) : +PlayedSongInterface(_owner), +pcm_count(0), +pcm_pos(0), +sample_rate(0), +channels(0) { + +} + +Soundtrack::PlayedLayeredSong::PlayedLayeredSong(Soundtrack* _owner, const MusicXMLParser::Song& _song) : +PlayedSongInterface(_owner, _song), +pcm_pos(0), +pcm_count(0), +sample_rate(0), +channels(0) { + bool first_creation = true; + for( unsigned i = 0; i < song.songrefs.size(); i++ ) { + PlayedSongInterface *ps = NULL; + MusicXMLParser::Song ns = owner->GetSong(song.songrefs[i].name); + + LOGI << "Creating a PlayedLayeredSong layer: " << ns.name << std::endl; + + if( strmtch( ns.type, "segmented" ) ) { + LOGE << "No support for segmented songs in layered songs" << std::endl; + } else if( strmtch( ns.type, "single" ) ) { + ps = new PlayedSingleSong( owner, ns ); + } else if( strmtch( ns.type, "layered" ) ) { + LOGE << "No support for layered songs in layered songs" << std::endl; + } else { + LOGE << "Unknown song type in " << ns << std::endl; + } + + if( ps ) { + if( channels == 0 ) { + channels = ps->Channels(); + } else if( channels != ps->Channels() ) { + LOGE << "Mimatching sample rates in layered song " << song << std::endl; + } + + if( sample_rate == 0 ) { + sample_rate = ps->SampleRate(); + } else if( sample_rate != ps->SampleRate() ) { + LOGE << "Misamtching sample rates in layered Song " << song << std::endl; + } + + if( pcm_count < ps->GetPCMCount() ) { + pcm_count = ps->GetPCMCount(); + } + + if( first_creation == false ) { + ps->SetGain(0.0f); + } else { + first_creation = false; + } + + subsongs.push_back(rc_PlayedSongInterface(ps)); + } + } +} + +Soundtrack::PlayedLayeredSong::~PlayedLayeredSong() { + +} + +void Soundtrack::PlayedLayeredSong::SetGain( float v ) { + //nil +} + +float Soundtrack::PlayedLayeredSong::GetGain() { + return 1.0f; +} + +void Soundtrack::PlayedLayeredSong::update( HighResBufferSegment* buffer ) { + HighResBufferSegment& first = *buffer; + HighResBufferSegment second; + + memset(first.buf,0,HighResBufferSegment::BUF_SIZE); + + first.data_size = HighResBufferSegment::BUF_SIZE; + + int64_t max_pcm_pos = 0; + int16_t active_layer_count = 0; + unsigned max_sample_count = 0; + + for( unsigned i = 0; i < subsongs.size(); i++ ) { + second = HighResBufferSegment(); + subsongs[i]->update(&second); + + if( i != 0 ) { + LOG_ASSERT(first.sample_rate == second.sample_rate); + LOG_ASSERT(first.channels == second.channels); + } else { + first.sample_rate = second.sample_rate; + first.channels = second.channels; + } + + unsigned first_sample_count = first.FlatSampleCount(); + unsigned second_sample_count = second.FlatSampleCount(); + + if( second_sample_count > max_sample_count ) { + max_sample_count = second_sample_count; + } + + if( subsongs[i]->GetGain() > 0.0f ) { + active_layer_count++; + + for( unsigned k = 0; k < second_sample_count; k++ ) { + int32_t buf1; + char* buf1a = (char*)&buf1; + + int32_t buf2; + char* buf2a = (char*)&buf2; + + buf1a[0] = first.buf[k*4+0]; + buf1a[1] = first.buf[k*4+1]; + buf1a[2] = first.buf[k*4+2]; + buf1a[3] = first.buf[k*4+3]; + + buf2a[0] = second.buf[k*4+0]; + buf2a[1] = second.buf[k*4+1]; + buf2a[2] = second.buf[k*4+2]; + buf2a[3] = second.buf[k*4+3]; + + buf1 += buf2; + + first.buf[k*4+0] = buf1a[0]; + first.buf[k*4+1] = buf1a[1]; + first.buf[k*4+2] = buf1a[2]; + first.buf[k*4+3] = buf1a[3]; + } + } + + int64_t pcm_pos = subsongs[i]->GetPCMPos(); + if( max_pcm_pos < pcm_pos ) { + max_pcm_pos = pcm_pos; + } + } + + this->pcm_pos = max_pcm_pos; + + first.data_size = max_sample_count*first.SampleSize(); + + if( IsAtEnd() ) { + Rewind(); + } +} + +bool Soundtrack::PlayedLayeredSong::IsAtEnd() { + bool is_at_end = true; + for( unsigned i = 0; i < subsongs.size(); i++ ) { + is_at_end = is_at_end && subsongs[i]->IsAtEnd(); + } + return is_at_end; +} + +void Soundtrack::PlayedLayeredSong::Rewind() { + pcm_pos = 0; + for( unsigned i = 0; i < subsongs.size(); i++ ) { + subsongs[i]->Rewind(); + } +} + +int64_t Soundtrack::PlayedLayeredSong::GetPCMPos() { + return pcm_pos; +} + +void Soundtrack::PlayedLayeredSong::SetPCMPos( int64_t pos ) { + pcm_pos = pos; + for( unsigned i = 0; i < subsongs.size(); i++ ) { + if( pos < subsongs[i]->GetPCMCount() ) { + subsongs[i]->SetPCMPos(pos); + } + } +} + +int64_t Soundtrack::PlayedLayeredSong::GetPCMCount() { + int64_t max = 0; + for( unsigned i = 0; i < subsongs.size(); i++ ) { + if( max < subsongs[i]->GetPCMCount() ) { + max = subsongs[i]->GetPCMCount(); + } + } + return max; +} + +int Soundtrack::PlayedLayeredSong::SampleRate() { + return sample_rate; +} + +int Soundtrack::PlayedLayeredSong::Channels() { + return channels; +} + +bool Soundtrack::PlayedLayeredSong::SetLayerGain( const std::string& name, float gain ) { + if( gain > 1.0f ) { + gain = 1.0f; + } + if( gain < 0.0f ) { + gain = 0.0f; + } + + for( unsigned i = 0; i < subsongs.size(); i++ ) { + if( subsongs[i]->GetSongName() == name ) { + subsongs[i]->SetGain(gain); + return true; + } + } + return false; +} + +float Soundtrack::PlayedLayeredSong::GetLayerGain( const std::string& name ) { + for( unsigned i = 0; i < subsongs.size(); i++ ) { + if( subsongs[i]->GetSongName() == name ) { + return subsongs[i]->GetGain(); + } + } + return 0.0f; +} + +const std::map<std::string,float> Soundtrack::PlayedLayeredSong::GetLayerGains() { + std::map<std::string,float> gains; + for( unsigned i = 0; i < subsongs.size(); i++ ) { + gains[subsongs[i]->GetSongName()] = subsongs[i]->GetGain(); + } + return gains; +} + +std::vector<std::string> Soundtrack::PlayedLayeredSong::GetLayerNames() const { + std::vector<std::string> layers; + for( unsigned i = 0; i < song.songrefs.size(); i++ ) { + layers.push_back(song.songrefs[i].name); + } + return layers; +} + +Soundtrack::TransitionPlayer::TransitionPlayer(Soundtrack* _owner) : +PlayedInterface(_owner), +playing(true), +mixer(TransitionMixer::Sinusoid,2.0f), +currentSong(new PlayedSingleSong(owner)) +{ + +} + +Soundtrack::TransitionPlayer::~TransitionPlayer() { + +} + +void Soundtrack::TransitionPlayer::update( HighResBufferSegment* buffer ) { + //Play normally. + if( songQueue.empty() ) { + currentSong->update(buffer); + + if( buffer->data_size <= 0 ) { + LOGE << "Empty buffer in transition" << std::endl; + } + mixer.Reset(); + } + else //Transition until we have reached end. + { + if( currentSong->SampleRate() == songQueue.front()->SampleRate() ) + { + if( currentSong->Channels() == songQueue.front()->Channels() ) + { + HighResBufferSegment first; + HighResBufferSegment second; + + currentSong->update(&first); + songQueue.front()->update(&second); + + if( mixer.Step(buffer,&first,&second) ) + { + stored_pcm_pos[currentSong->GetSongName()] = currentSong->GetPCMPos(); + + currentSong = songQueue.front(); + songQueue.pop_front(); + mixer.Reset(); + } + } + else + { + LOGE << "Unable to transition as the two segments " + << currentSong->GetSongName() << "(" << currentSong->Channels() << ")" + << " and " + << songQueue.front()->GetSongName() << "(" << songQueue.front()->Channels() << ")" + << " don't have the same number of Channels, skipping transition" << std::endl; + + currentSong = songQueue.front(); + songQueue.pop_front(); + mixer.Reset(); + } + } + else + { + LOGE << "Unable to transition as the two segments " + << currentSong->GetSongName() << "(" << currentSong->SampleRate() << ")" + << " and " + << songQueue.front()->GetSongName() << "(" << songQueue.front()->SampleRate() << ")" + << " don't have the same sample rate, skipping transition" << std::endl; + + currentSong = songQueue.front(); + songQueue.pop_front(); + mixer.Reset(); + } + } +} + +bool Soundtrack::TransitionPlayer::TransitionToSong( const MusicXMLParser::Song& ns ) +{ + if( (songQueue.size() == 0 && currentSong->GetSong() != ns) + || (songQueue.size() > 0 && songQueue.back()->GetSong() != ns ) ) { + + while( songQueue.size() > 1 ) { + songQueue.pop_back(); + } + + PlayedSongInterface *ps = NULL; + + if( strmtch( ns.type, "segmented" ) ) { + ps = new PlayedSegmentedSong( owner, ns ); + } else if( strmtch( ns.type, "single" ) ) { + ps = new PlayedSingleSong( owner, ns ); + } else if( strmtch( ns.type, "layered" ) ) { + ps = new PlayedLayeredSong( owner, ns ); + } else { + LOGE << "Unknown song type " << ns.type << std::endl; + } + + if( ps ) { + std::map<std::string, int64_t>::iterator it = stored_pcm_pos.find(ps->GetSongName()); + if( it != stored_pcm_pos.end() ) { + int64_t size = ps->GetPCMCount(); + if( size > 0 ) { + int64_t v = it->second % size; + ps->SetPCMPos(v); + } + } + + songQueue.push_back(rc_PlayedSongInterface(ps)); + return true; + } else { + return false; + } + } else { + return false; + } +} + +bool Soundtrack::TransitionPlayer::SetSong( const MusicXMLParser::Song& ns ) { + mixer.Reset(); + + LOGI << "Setting song to " << ns << std::endl; + + if( strmtch( ns.type, "segmented" ) ) { + currentSong.Set(new PlayedSegmentedSong(owner,ns)); + } else if( strmtch( ns.type, "single" ) ) { + currentSong.Set(new PlayedSingleSong(owner,ns)); + } else if( strmtch( ns.type, "layered" ) ) { + currentSong.Set(new PlayedLayeredSong(owner,ns)); + } else { + LOGE << "Unknown song type " << ns.type << std::endl; + } + + while( songQueue.size() > 0 ) { + songQueue.pop_front(); + } + return true; +} + +const std::string Soundtrack::TransitionPlayer::GetSegmentName() const { + if( strmtch( GetSongType().c_str(), "segmented" ) ) { + const PlayedSegmentedSong *pss = currentSong.GetConstPtr<PlayedSegmentedSong>(); + return pss->GetSegmentName(); + } else { + return std::string(); + } +} + +const std::string Soundtrack::TransitionPlayer::GetSongName() const +{ + return currentSong.GetConst().GetSongName(); +} + +const std::string Soundtrack::TransitionPlayer::GetSongType() const +{ + return currentSong.GetConst().GetSongType(); +} + +const std::string Soundtrack::TransitionPlayer::GetNextSongName() const +{ + if( songQueue.size() > 0 ) { + return songQueue[0].GetConst().GetSongName(); + } else { + return std::string(); + } +} + +const std::string Soundtrack::TransitionPlayer::GetNextSongType() const +{ + if( songQueue.size() > 0 ) { + return songQueue[0].GetConst().GetSongType(); + } else { + return std::string(); + } +} + +bool Soundtrack::TransitionPlayer::QueueSegment( const std::string& segment_name ) +{ + bool found_match = false; + std::vector<MusicXMLParser::Segment>::const_iterator segmentit; + for( segmentit = currentSong->GetSong().segments.begin(); + segmentit != currentSong->GetSong().segments.end(); + segmentit++ ) + { + if( segmentit->name == segment_name ) { + if( strmtch(currentSong->GetSongType(), "segmented") ) { + PlayedSegmentedSong *pss = currentSong.GetPtr<PlayedSegmentedSong>(); + pss->QueueSegment(*segmentit); + found_match = true; + } + } + } + + std::deque<rc_PlayedSongInterface>::const_iterator songit; + for( songit = songQueue.begin(); + songit != songQueue.end(); + songit++ ) + { + std::vector<MusicXMLParser::Segment>::const_iterator segmentit; + for( segmentit = (*songit).GetConst().GetSong().segments.begin(); + segmentit != (*songit).GetConst().GetSong().segments.end(); + segmentit++ ) { + if( segmentit->name == segment_name ) { + if( strmtch(currentSong->GetSongType(), "segmented") ) { + PlayedSegmentedSong *pss = currentSong.GetPtr<PlayedSegmentedSong>(); + pss->QueueSegment(*segmentit); + found_match = true; + } + } + } + } + if( !found_match ) + LOGE << "Unable to find matching segment " << segment_name << " in current song: " << currentSong->GetSong() << std::endl; + return found_match; +} + +bool Soundtrack::TransitionPlayer::TransitionIntoSegment( const std::string& segment_name ) +{ + bool found_match = false; + std::vector<MusicXMLParser::Segment>::const_iterator segmentit; + for( segmentit = currentSong->GetSong().segments.begin(); + segmentit != currentSong->GetSong().segments.end(); + segmentit++ ) { + if( segmentit->name == segment_name ) { + if( strmtch(currentSong->GetSongType(), "segmented") ) { + PlayedSegmentedSong *pss = currentSong.GetPtr<PlayedSegmentedSong>(); + pss->TransitionIntoSegment(*segmentit); + found_match = true; + } + } + } + + std::deque<rc_PlayedSongInterface>::const_iterator songit; + for( songit = songQueue.begin(); + songit != songQueue.end(); + songit++ ) + { + std::vector<MusicXMLParser::Segment>::const_iterator segmentit; + for( segmentit = (*songit).GetConst().GetSong().segments.begin(); + segmentit != (*songit).GetConst().GetSong().segments.end(); + segmentit++ ) { + if( segmentit->name == segment_name ) { + if( strmtch(currentSong->GetSongType(), "segmented") ) { + PlayedSegmentedSong *pss = currentSong.GetPtr<PlayedSegmentedSong>(); + pss->TransitionIntoSegment(*segmentit); + found_match = true; + } + } + } + } + + if( !found_match ) + LOGE << "Unable to find matching segment " << segment_name << " in current song: " << currentSong->GetSong() << std::endl; + return found_match; +} + +bool Soundtrack::TransitionPlayer::SetSegment( const std::string& segment_name ) +{ + bool found_match = false; + std::vector<MusicXMLParser::Segment>::const_iterator segmentit; + for( segmentit = currentSong->GetSong().segments.begin(); + segmentit != currentSong->GetSong().segments.end(); + segmentit++ ) + { + if( segmentit->name == segment_name ) { + if( strmtch(currentSong->GetSongType(), "segmented") ) { + PlayedSegmentedSong *pss = currentSong.GetPtr<PlayedSegmentedSong>(); + pss->SetSegment(*segmentit); + found_match = true; + } + } + } + + std::deque<rc_PlayedSongInterface>::const_iterator songit; + for( songit = songQueue.begin(); + songit != songQueue.end(); + songit++ ) + { + std::vector<MusicXMLParser::Segment>::const_iterator segmentit; + for( segmentit = (*songit).GetConst().GetSong().segments.begin(); + segmentit != (*songit).GetConst().GetSong().segments.end(); + segmentit++ ) + { + if( segmentit->name == segment_name ) + { + if( strmtch(currentSong->GetSongType(), "segmented") ) { + PlayedSegmentedSong *pss = currentSong.GetPtr<PlayedSegmentedSong>(); + pss->SetSegment(*segmentit); + found_match = true; + } + } + } + } + + if( !found_match ) { + LOGE << "Unable to find matching segment " << segment_name << " in current song: " << currentSong->GetSong() << std::endl; + } + return found_match; +} + +void Soundtrack::TransitionPlayer::SetPCMPos( int64_t c ) { + currentSong->SetPCMPos(c); +} + +int64_t Soundtrack::TransitionPlayer::GetPCMCount() { + return currentSong->GetPCMCount(); +} + +int64_t Soundtrack::TransitionPlayer::GetPCMPos() { + return currentSong->GetPCMPos(); +} + +int Soundtrack::TransitionPlayer::SampleRate() { + return currentSong->SampleRate(); +} + +int Soundtrack::TransitionPlayer::Channels() { + return currentSong->Channels(); +} + +bool Soundtrack::TransitionPlayer::SetLayerGain(const std::string& name, float gain) { + if( songQueue.size() > 0 && strmtch(songQueue[0]->GetSongType(), "layered") ) { + PlayedLayeredSong *pss = songQueue[0].GetPtr<PlayedLayeredSong>(); + return pss->SetLayerGain(name,gain); + } else if( strmtch(currentSong->GetSongType(), "layered") ) { + PlayedLayeredSong *pss = currentSong.GetPtr<PlayedLayeredSong>(); + return pss->SetLayerGain(name,gain); + } else { + return false; + } +} + +float Soundtrack::TransitionPlayer::GetLayerGain(const std::string& name) { + if( songQueue.size() > 0 && strmtch(songQueue[0]->GetSongType(), "layered") ) { + PlayedLayeredSong *pss = songQueue[0].GetPtr<PlayedLayeredSong>(); + return pss->GetLayerGain(name); + } else if( strmtch(currentSong->GetSongType(), "layered") ) { + PlayedLayeredSong *pss = currentSong.GetPtr<PlayedLayeredSong>(); + return pss->GetLayerGain(name); + } + return 0.0f; +} + +std::vector<std::string> Soundtrack::TransitionPlayer::GetLayerNames() const { + if( songQueue.size() > 0 && strmtch(songQueue[0].GetConst().GetSongType(), "layered") ) { + const PlayedLayeredSong *pss = songQueue[0].GetConstPtr<PlayedLayeredSong>(); + return pss->GetLayerNames(); + } else if( strmtch(currentSong.GetConst().GetSongType(), "layered") ) { + const PlayedLayeredSong *pss = currentSong.GetConstPtr<PlayedLayeredSong>(); + return pss->GetLayerNames(); + } + return std::vector<std::string>(); +} + +const std::map<std::string,float> Soundtrack::TransitionPlayer::GetLayerGains() { + if( songQueue.size() > 0 && strmtch(songQueue[0]->GetSongType(), "layered") ) { + PlayedLayeredSong *pss = songQueue[0].GetPtr<PlayedLayeredSong>(); + return pss->GetLayerGains(); + } else if( strmtch(currentSong->GetSongType(), "layered") ) { + PlayedLayeredSong *pss = currentSong.GetPtr<PlayedLayeredSong>(); + return pss->GetLayerGains(); + } + return std::map<std::string,float>(); +} + +bool Soundtrack::QueueSegment( const std::string& name ) +{ + return transitionPlayer.QueueSegment(name); +} + +bool Soundtrack::TransitionIntoSegment( const std::string& name ) +{ + return transitionPlayer.TransitionIntoSegment(name); +} + +bool Soundtrack::SetSegment( const std::string& name ) +{ + return transitionPlayer.SetSegment(name); +} + +const std::string Soundtrack::GetSegment( ) const +{ + return transitionPlayer.GetSegmentName(); +} + +bool Soundtrack::TransitionToSong( const std::string& name ) +{ + std::map<std::string,MusicXMLParser::Music>::iterator musicit; + + if(name == "overgrowth_silence") { + MusicXMLParser::Song song; + strcpy(song.type, "single"); + strcpy(song.file_path, "Data/Music/silence.ogg"); + return transitionPlayer.TransitionToSong(song); + } + + for( musicit = music.begin(); musicit != music.end(); musicit++ ) + { + std::vector<MusicXMLParser::Song>::iterator songit ; + + for( songit = musicit->second.songs.begin(); + songit != musicit->second.songs.end(); + songit++ ) + { + if( songit->name == name ) + { + return transitionPlayer.TransitionToSong(*songit); + } + } + + } + LOGW_ONCE("Did not find song \"" << name << "\""); + return false; +} + +bool Soundtrack::SetSong( const std::string& name ) +{ + std::map<std::string,MusicXMLParser::Music>::iterator musicit; + + for( musicit = music.begin(); musicit != music.end(); musicit++ ) + { + std::vector<MusicXMLParser::Song>::iterator songit ; + + for( songit = musicit->second.songs.begin(); + songit != musicit->second.songs.end(); + songit++ ) + { + if( songit->name == name ) + { + return transitionPlayer.SetSong(*songit); + } + } + + } + return false; +} + +void Soundtrack::SetLayerGain(const std::string& layer, float v) +{ + transitionPlayer.SetLayerGain(layer, v); +} + +const std::string Soundtrack::GetSongName() const +{ + return transitionPlayer.GetSongName(); +} + +const std::string Soundtrack::GetSongType() const +{ + return transitionPlayer.GetSongType(); +} + +const std::string Soundtrack::GetNextSongName() const +{ + return transitionPlayer.GetNextSongName(); +} + +const std::string Soundtrack::GetNextSongType() const +{ + return transitionPlayer.GetNextSongType(); +} + +const MusicXMLParser::Song Soundtrack::GetSong(const std::string& name) { + std::map<std::string,MusicXMLParser::Music>::iterator musicit; + for( musicit = music.begin(); musicit != music.end(); musicit++ ) + { + std::vector<MusicXMLParser::Song>::iterator songit ; + + for( songit = musicit->second.songs.begin(); + songit != musicit->second.songs.end(); + songit++ ) + { + if( songit->name == name ) + { + return *songit; + } + } + + } + LOGW_ONCE("Did not find song \"" << name << "\""); + return MusicXMLParser::Song(); +} + +const std::map<std::string,float> Soundtrack::GetLayerGains() { + return transitionPlayer.GetLayerGains(); +} + +const std::vector<std::string> Soundtrack::GetLayerNames() const { + return transitionPlayer.GetLayerNames(); +} + +void Soundtrack::PostProcessVolume( HighResBufferSegment* buffer ) +{ + const size_t start = current_volume_step; + //How many bytes for a second of transition times the time of transition. + const size_t end = (size_t) std::abs(((volume_start-volume_target)*volume_change_per_second) * buffer->sample_rate); + + for( size_t i = 0; i < buffer->FullSampleCount(); i++ ) + { + float d = 1.0f; + + if( end > 0 ) + { + d = (start+i*buffer->channels)/(float)end; + if( d > 1.0f ) + { + d = 1.0f; + } + + if( d < 0.0f ) + { + d = 0.0f; + } + } + + float current_volume = volume_start*(1.0f-d)+volume_target*d; + + union + { + int32_t full; + char part[4]; + } cast; + + for( size_t c = 0; c < buffer->channels; c++ ) { + cast.part[0] = buffer->buf[i*buffer->channels*4+c*4+0]; + cast.part[1] = buffer->buf[i*buffer->channels*4+c*4+1]; + cast.part[2] = buffer->buf[i*buffer->channels*4+c*4+2]; + cast.part[3] = buffer->buf[i*buffer->channels*4+c*4+3]; + + cast.full = (int32_t) (cast.full * current_volume); + + buffer->buf[i*buffer->channels*4+c*4+0] = cast.part[0]; + buffer->buf[i*buffer->channels*4+c*4+1] = cast.part[1]; + buffer->buf[i*buffer->channels*4+c*4+2] = cast.part[2]; + buffer->buf[i*buffer->channels*4+c*4+3] = cast.part[3]; + } + } + + current_volume_step += buffer->data_size; + if( (size_t)current_volume_step > end ) + { + current_volume_step = end; + volume_start = volume_target; + } +} + +void Soundtrack::Dispose() +{ + +} + +void Soundtrack::update(rc_alAudioBuffer buffer) +{ + HighResBufferSegment hbs; + + transitionPlayer.update(&hbs); + + PostProcessVolume(&hbs); + if( g_sound_enable_layered_soundtrack_limiter ) { + limiter.Step(&hbs); + } + + if( hbs.data_size > 0 && hbs.channels > 0 ) + { + BufferSegment bs; + + ToBufferSegment(bs,hbs); + if( bs.FullSampleCount() == 0 ) { + LOGI << "Feeding a null buffer to soundtrack" << std::endl; + memset(bs.buf,0,BufferSegment::BUF_SIZE); + bs.channels = 2; + bs.sample_rate = 44100; + bs.data_size = BufferSegment::BUF_SIZE; + } + + buffer->BufferData( ( bs.channels == 1)?AL_FORMAT_MONO16:AL_FORMAT_STEREO16, bs.buf, bs.data_size, bs.sample_rate); + } + else + { + LOGE << "Unable to stream data from file" << std::endl; + } +} + +unsigned long Soundtrack::required_buffers() +{ + return 4; +} + +void Soundtrack::Stop() +{ + LOGI << "Stopping playing" << std::endl; + transitionPlayer = TransitionPlayer(this); +} + +Soundtrack::Soundtrack(float volume) : +audioStreamer(), +volume_change_per_second(1.0f), +volume_start(volume), +volume_target(volume), +current_volume_step(0), +transitionPlayer(this) +{ + +} + +Soundtrack::~Soundtrack() +{ + +} + +void Soundtrack::AddMusic( const Path &path ) +{ + LOGI << "Adding music sheet:" << path << std::endl; + MusicXMLParser parser; + + if( music.find( path.GetOriginalPathStr() ) == music.end() ) + { + if( parser.Load( path.GetFullPathStr() ) == MusicXMLParser::kErrorNoError ) + { + std::map<std::string,MusicXMLParser::Music>::iterator musit; + for(musit = music.begin(); musit != music.end(); ++musit) { + bool replace = false; + for(size_t new_i = 0; new_i < parser.music.songs.size(); ++new_i) { + for(size_t old_i = 0; old_i < musit->second.songs.size(); ++old_i) { + if(strmtch(parser.music.songs[new_i].name, musit->second.songs[old_i].name)) { + LOGW << "Track " << parser.music.songs[new_i].name << " already found in " << musit->first << ", old music set will be overwritten" << std::endl; + replace = true; + if(transitionPlayer.GetSongName() == std::string(parser.music.songs[new_i].name)) + Stop(); + break; + } + } + } + + // Loop to print all track collisions, but break here + if(replace) { + music.erase(musit); + break; + } + } + + music[path.GetOriginalPathStr()] = parser.music; + LOGD << "Loaded music sheet " << path << std::endl; + } + else + { + LOGE << "Unable to load music file " << path << std::endl; + } + } + else + { + LOGW << "Music is already loaded, will not reload: " << path << std::endl; + } +} + +void Soundtrack::RemoveMusic( const Path &path ) +{ + std::map<std::string,MusicXMLParser::Music>::iterator musit = music.find( path.GetOriginalPathStr() ); + + if( musit != music.end() ) + { + music.erase( musit ); + } + else + { + LOGW << "Tried to remove not loaded music file: " << path << std::endl; + } +} + +bool Soundtrack::KeepPlaying() +{ + return true; +} + +bool Soundtrack::GetPosition( vec3 &p ) +{ + p = vec3(0.0f); + return false; +} + +void Soundtrack::GetDirection( vec3 &p ) +{ + p = vec3(0.0f); +} + +const vec3& Soundtrack::GetVelocity() +{ + static vec3 v(0.0f); + return v; +} + +const vec3 Soundtrack::GetPosition() +{ + return vec3(0.0f); +} + +const vec3 Soundtrack::GetOcclusionPosition() +{ + return vec3(0.0f); +} + +unsigned char Soundtrack::GetPriority() +{ + return -1; +} + +void Soundtrack::SetVolume(float volume) +{ + if( volume > 1.0f ) + volume = 1.0f; + if( volume < 0.0f ) + volume = 0.0f; + current_volume_step = 0; + volume_target = volume; +} + +bool Soundtrack::IsTransient() +{ + return false; +} |