new, mostly working version of fade-in/fade-out

This commit is contained in:
fgerlits 2005-03-04 13:36:32 +00:00
parent 6a8bd2045d
commit 01745e8374
7 changed files with 223 additions and 94 deletions

View File

@ -38,21 +38,21 @@
<playlistElement id="0000000000000102"
relativeOffset="00:00:06" >
<audioClip id="0000000000010002"
playlength="00:00:12"
playlength="00:00:12.200000"
title = "two"
uri="file:var/test10002.mp3" />
<fadeInfo id="0000000000009902"
fadeIn="00:00:05"
fadeOut="0" />
fadeOut="00:00:04" />
</playlistElement>
<playlistElement id="0000000000000103"
relativeOffset="00:00:18" >
relativeOffset="00:00:18.200000" >
<audioClip id="0000000000010003"
playlength="00:00:11.500000"
title = "three"
title = "three"
uri="file:var/test10003.mp3" />
<fadeInfo id="0000000000009903"
fadeIn="0"
fadeOut="00:00:05" />
fadeIn="00:00:06"
fadeOut="00:00:04" />
</playlistElement>
</playlist>

View File

@ -22,7 +22,7 @@
Author : $Author: fgerlits $
Version : $Revision: 1.5 $
Version : $Revision: 1.6 $
Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/playlistExecutor/include/LiveSupport/PlaylistExecutor/AudioPlayerInterface.h,v $
------------------------------------------------------------------------------*/
@ -68,7 +68,7 @@ using namespace LiveSupport::Core;
* A generic interface for playing audio files.
*
* @author $Author: fgerlits $
* @version $Revision: 1.5 $
* @version $Revision: 1.6 $
*/
class AudioPlayerInterface
{
@ -165,12 +165,18 @@ class AudioPlayerInterface
virtual void
stop(void) throw (std::logic_error)
= 0;
/**
* Play a playlist, with simulated fading.
*
* This is a stopgap method, and should be replaced as soon as
* the SMIL animation issues are fixed in the Helix client.
*
* The playlist is assumed to contain a URI field, which points
* to a SMIL file containing the same audio clips, with the same
* offsets, as the playlist. This can be ensured, for example, by
* calling Storage::WebStorageClient::acquirePlaylist().
*
* @param playlist the Playlist object to be played.
* @exception std::invalid_argument playlist is invalid (e.g.,
* does not have a URI field, or there is no valid
@ -180,7 +186,7 @@ class AudioPlayerInterface
* @exception std::runtime_error on errors thrown by the helix player
*/
virtual void
openAndStartPlaylist(Ptr<Playlist>::Ref playlist)
openAndStart(Ptr<Playlist>::Ref playlist)
throw (std::invalid_argument,
std::logic_error,
std::runtime_error)

View File

@ -21,8 +21,8 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Author : $Author: maroy $
Version : $Revision: 1.2 $
Author : $Author: fgerlits $
Version : $Revision: 1.3 $
Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/playlistExecutor/src/Attic/AdviseSink.cxx,v $
------------------------------------------------------------------------------*/
@ -173,6 +173,11 @@ AdviseSink::OnPosLength(UINT32 ulPosition,
UINT32 ulLength) throw ()
{
helixPlayer->setPlaylength(ulLength);
try {
helixPlayer->implementFading(ulPosition);
} catch (std::runtime_error) {
// TODO: mark error; log it somewhere, maybe?
}
return HXR_OK;
}

View File

@ -22,7 +22,7 @@
Author : $Author: fgerlits $
Version : $Revision: 1.14 $
Version : $Revision: 1.15 $
Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/playlistExecutor/src/Attic/HelixPlayer.cxx,v $
------------------------------------------------------------------------------*/
@ -85,14 +85,12 @@ static const std::string clntcoreName = "/clntcore.so";
/**
* Magic number #1: max time to wait for an audio stream, in milliseconds
*/
static const int getAudioStreamTimeOut = 10;
static const int getAudioStreamTimeOut = 5;
/**
* Magic number #2: time to wait after getting crossfade interface,
* and before setting crossfade values, in milliseconds
* Magic number #2: schedule fading this many milliseconds in advance
*/
static const int crossFadeWaitingTime = 50;
static const int lookAheadTime = 2500;
/* =============================================== local function prototypes */
@ -398,10 +396,10 @@ DLLAccessPath* GetDLLAccessPath(void)
/*------------------------------------------------------------------------------
* Play a playlist, with simulated fading.
* Open a playlist, with simulated fading.
*----------------------------------------------------------------------------*/
void
HelixPlayer :: openAndStartPlaylist(Ptr<Playlist>::Ref playlist)
HelixPlayer :: openAndStart(Ptr<Playlist>::Ref playlist)
throw (std::invalid_argument,
std::logic_error,
std::runtime_error)
@ -409,96 +407,177 @@ HelixPlayer :: openAndStartPlaylist(Ptr<Playlist>::Ref playlist)
if (!playlist || !playlist->getUri()) {
throw std::invalid_argument("no playlist SMIL file found");
}
open(*playlist->getUri()); // may throw invalid_argument
bool hasFadeInfo = false;
int numberOfPlaylistElements = 0;
Playlist::const_iterator it = playlist->begin();
while (it != playlist->end()) {
++numberOfPlaylistElements;
if (it->second->getFadeInfo()) {
hasFadeInfo = true;
}
++it;
}
start(); // may throw logic_error
if (!numberOfPlaylistElements || !hasFadeInfo) {
return;
}
IHXAudioPlayer* audioPlayer = 0;
if (player->QueryInterface(IID_IHXAudioPlayer, (void**)&audioPlayer)
!= HXR_OK
if (player->QueryInterface(IID_IHXAudioPlayer,
(void**)&audioPlayer) != HXR_OK
|| !audioPlayer) {
throw std::runtime_error("can't get IHXAudioPlayer interface");
}
IHXAudioCrossFade* crossFade = 0;
if (audioPlayer->QueryInterface(IID_IHXAudioCrossFade, (void**)&crossFade)
!= HXR_OK
|| !crossFade) {
throw std::runtime_error("can't get IHXAudioCrossFade interface");
}
int playlistSize = playlist->size();
IHXAudioStream* audioStream[playlistSize];
unsigned long playlength[playlistSize];
unsigned long relativeOffset[playlistSize];
unsigned long fadeIn[playlistSize];
unsigned long fadeOut[playlistSize];
Ptr<time_duration>::Ref sleepT(new time_duration(microseconds(10)));
IHXAudioStream* audioStream[numberOfPlaylistElements + 1];
for (int i = 0; i < numberOfPlaylistElements; i++) {
int j = 0;
do {
TimeConversion::sleep(sleepT);
audioStream[i] = audioPlayer->GetAudioStream(i);
++j;
if (j > getAudioStreamTimeOut * 100) {
bool hasFadeInfo = false;
Playlist::const_iterator it = playlist->begin();
for (int i = 0; i < playlistSize; ++i) {
audioStream[i] = audioPlayer->GetAudioStream(i);
int counter = 0;
while (!audioStream[i]) {
if (counter > getAudioStreamTimeOut * 100) {
std::stringstream eMsg;
eMsg << "can't get audio stream number " << i;
throw std::runtime_error(eMsg.str());
}
} while (!audioStream[i]);
}
audioStream[numberOfPlaylistElements] = 0; // fade out last clip into 0
it = playlist->begin();
TimeConversion::sleep(sleepT);
sleepT.reset(new time_duration(milliseconds(crossFadeWaitingTime)));
TimeConversion::sleep(sleepT);
for (int i = 0; i < numberOfPlaylistElements; i++) {
Ptr<PlaylistElement>::Ref playlistElement = it->second;
if (!playlistElement->getFadeInfo()) {
++it;
continue;
audioStream[i] = audioPlayer->GetAudioStream(i);
++counter;
}
// we assume i-th fade out is the same as (i+1)-st fade in
unsigned long crossFadeLength = playlistElement
->getFadeInfo()
->getFadeOut()
->total_milliseconds();
unsigned long fadeOutAt = playlistElement->getRelativeOffset()
->total_milliseconds()
+ playlistElement->getPlayable()
->getPlaylength()
->total_milliseconds()
- crossFadeLength;
if (crossFadeLength) {
//std::cerr << "fadeOutAt: " << fadeOutAt << "\n"
// << "crossFadeLength: " << crossFadeLength << "\n";
crossFade->CrossFade(audioStream[i], audioStream[i+1],
fadeOutAt, fadeOutAt, crossFadeLength);
relativeOffset[i] = it->second->getRelativeOffset()
->total_milliseconds();
playlength[i] = it->second->getPlayable()->getPlaylength()
->total_milliseconds();
if (it->second->getFadeInfo()) {
hasFadeInfo = true;
fadeIn[i] = it->second->getFadeInfo()
->getFadeIn()->total_milliseconds();
fadeOut[i] = it->second->getFadeInfo()
->getFadeOut()->total_milliseconds();
} else {
fadeIn[i] = 0;
fadeOut[i] = 0;
}
++it;
}
for (int i = 0; i < numberOfPlaylistElements; i++) {
if (!hasFadeInfo) {
return;
}
fadeIn[0] = 0; // can't do fade-in on the first audio clip, sorry
fadeDataList.reset(new std::list<FadeData>);
FadeData fadeData;
for (int i = 0; i < playlistSize; ++i) {
if (fadeIn[i]) {
fadeData.audioStreamFrom = 0;
fadeData.audioStreamTo = audioStream[i];
fadeData.fadeAt = relativeOffset[i];
fadeData.fadeLength = fadeIn[i];
fadeDataList->push_back(fadeData);
}
if (fadeOut[i]) {
if (i < playlistSize - 1
&& fadeOut[i] == fadeIn[i+1]
&& relativeOffset[i] + playlength[i]
== relativeOffset[i+1] + fadeIn[i+1]) {
fadeData.audioStreamFrom = audioStream[i];
fadeData.audioStreamTo = audioStream[i+1];
fadeData.fadeAt = relativeOffset[i+1];
fadeData.fadeLength = fadeIn[i+1];
fadeDataList->push_back(fadeData);
fadeIn[i+1] = 0;
} else {
fadeData.audioStreamFrom = audioStream[i];
fadeData.audioStreamTo = 0;
fadeData.fadeAt = relativeOffset[i]
+ playlength[i] - fadeOut[i];
fadeData.fadeLength = fadeOut[i];
fadeDataList->push_back(fadeData);
}
}
HX_RELEASE(audioStream[i]);
}
HX_RELEASE(crossFade);
HX_RELEASE(audioPlayer);
//do {
// std::cerr << "\n";
// std::list<FadeData>::const_iterator it = fadeDataList->begin();
// while (it != fadeDataList->end()) {
// std::cerr << it->audioStreamFrom << " -> "
// << it->audioStreamTo << " : at "
// << it->fadeAt << ", for "
// << it->fadeLength << "\n";
// ++it;
// }
// std::cerr << "\n";
//} while (false);
}
/*------------------------------------------------------------------------------
* Activate the crossfading of clips in a playlist.
*----------------------------------------------------------------------------*/
void
HelixPlayer :: implementFading(unsigned long position)
throw (std::runtime_error)
{
if (!fadeDataList) {
return;
}
std::list<FadeData>::iterator it = fadeDataList->begin();
while (it != fadeDataList->end()) {
unsigned long fadeAt = it->fadeAt;
if (fadeAt < position) { // we missed it
it = fadeDataList->erase(it);
continue;
} else if (fadeAt < position + lookAheadTime) { // we are on time
IHXAudioPlayer* audioPlayer = 0;
if (player->QueryInterface(IID_IHXAudioPlayer,
(void**)&audioPlayer) != HXR_OK
|| !audioPlayer) {
throw std::runtime_error("can't get IHXAudioPlayer interface");
}
IHXAudioCrossFade* crossFade = 0;
if (audioPlayer->QueryInterface(IID_IHXAudioCrossFade,
(void**)&crossFade) != HXR_OK
|| !crossFade) {
throw std::runtime_error("can't get IHXAudioCrossFade "
"interface");
}
//std::cerr << "position:" << position << "\n"
// << "fadeAt: " << fadeAt << "\n"
// << "fadeLength: " << it->fadeLength << "\n\n";
crossFade->CrossFade(it->audioStreamFrom, it->audioStreamTo,
fadeAt, fadeAt, it->fadeLength);
HX_RELEASE(crossFade);
HX_RELEASE(audioPlayer);
it = fadeDataList->erase(it);
continue;
} else {
++it;
}
}
}

View File

@ -22,7 +22,7 @@
Author : $Author: fgerlits $
Version : $Revision: 1.12 $
Version : $Revision: 1.13 $
Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/playlistExecutor/src/Attic/HelixPlayer.h,v $
------------------------------------------------------------------------------*/
@ -40,6 +40,7 @@
#include "configure.h"
#endif
#include <list>
#include <boost/enable_shared_from_this.hpp>
#include "LiveSupport/Core/Configurable.h"
@ -54,6 +55,9 @@
#include "ClientContext.h"
#include "LiveSupport/Core/Playlist.h"
#include <hxausvc.h>
namespace LiveSupport {
namespace PlaylistExecutor {
@ -94,7 +98,7 @@ using namespace LiveSupport::Core;
* </pre></code>
*
* @author $Author: fgerlits $
* @version $Revision: 1.12 $
* @version $Revision: 1.13 $
*/
class HelixPlayer : virtual public Configurable,
virtual public AudioPlayerInterface,
@ -135,7 +139,7 @@ class HelixPlayer : virtual public Configurable,
* The Helix player.
*/
IHXPlayer * player;
/**
* The example client context.
*/
@ -171,6 +175,44 @@ class HelixPlayer : virtual public Configurable,
*/
Ptr<Thread>::Ref eventHandlerThread;
/**
* A type to contain the data about a single fade-in or fade-out
* event.
*/
struct FadeData {
IHXAudioStream* audioStreamFrom;
IHXAudioStream* audioStreamTo;
unsigned long fadeAt;
unsigned long fadeLength;
};
/**
* A list of fade-in/fade-out events.
* This is set by the openAndStartPlaylist() method, and the
* actual fading is done by implementFading(), called from
* AdviseSink::OnPosLength().
*/
Ptr<std::list<FadeData> >::Ref fadeDataList;
/**
* Declare AdviseSink::OnPosLength() to be a friend, so it can
* call the private method implementFading().
*/
friend STDMETHODIMP
AdviseSink::OnPosLength(UINT32 ulPosition,
UINT32 ulLength)
throw ();
/**
* Implement the actual fading scheduled by open(Ptr<Playlist>::Ref).
* This method is called from AdviceSink::OnPosLength().
* DO NOT call this method directly.
*
* @param position the clip position
*/
void
implementFading(unsigned long position)
throw(std::runtime_error);
public:
/**
@ -336,9 +378,6 @@ class HelixPlayer : virtual public Configurable,
* This is a stopgap method, and should be replaced as soon as
* the SMIL animation issues are fixed in the Helix client.
*
* Note: the method only reads the fade out value, and assumes
* that the following fade in value is equal to it.
*
* The playlist is assumed to contain a URI field, which points
* to a SMIL file containing the same audio clips, with the same
* offsets, as the playlist. This can be ensured, for example, by
@ -353,7 +392,7 @@ class HelixPlayer : virtual public Configurable,
* @exception std::runtime_error on errors thrown by the helix player
*/
virtual void
openAndStartPlaylist(Ptr<Playlist>::Ref playlist)
openAndStart(Ptr<Playlist>::Ref playlist)
throw (std::invalid_argument,
std::logic_error,
std::runtime_error);

View File

@ -22,7 +22,7 @@
Author : $Author: fgerlits $
Version : $Revision: 1.9 $
Version : $Revision: 1.10 $
Location : $Source: /home/paul/cvs2svn-livesupport/newcvsrepo/livesupport/modules/playlistExecutor/src/Attic/HelixPlayerTest.cxx,v $
------------------------------------------------------------------------------*/
@ -419,7 +419,7 @@ HelixPlayerTest :: animationWorkaroundTest(void)
playlist->setUri(uri);
CPPUNIT_ASSERT_NO_THROW(helixPlayer->initialize());
CPPUNIT_ASSERT_NO_THROW(helixPlayer->openAndStartPlaylist(playlist));
CPPUNIT_ASSERT_NO_THROW(helixPlayer->openAndStart(playlist));
CPPUNIT_ASSERT(helixPlayer->isPlaying());
Ptr<time_duration>::Ref sleepT(new time_duration(microseconds(10)));

View File

@ -4,7 +4,7 @@
<par>
<audio src="file:var/test10001.mp3" begin="0s"/>
<audio src="file:var/test10002.mp3" begin="6s"/>
<audio src="file:var/test10003.mp3" begin="18s"/>
<audio src="file:var/test10003.mp3" begin="18.2s"/>
</par>
</body>
</smil>