new, mostly working version of fade-in/fade-out
This commit is contained in:
parent
6a8bd2045d
commit
01745e8374
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue