* Copyright 2008 Stephan Aßmus <superstippi@gmx.de>
* All Rights Reserved. Distributed under the terms of the MIT license.
*/
#include "ProxyAudioSupplier.h"
#include <algorithm>
#include <new>
#include <stdio.h>
#include <string.h>
#include <Autolock.h>
#include <List.h>
#include "AudioTrackSupplier.h"
#include "AudioAdapter.h"
#include "AudioVolumeConverter.h"
#include "PlaybackManager.h"
using std::nothrow;
using std::swap;
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
# define TRACE(x...) printf("ProxyAudioSupplier::"); printf(x)
# define ERROR(x...) fprintf(stderr, "ProxyAudioSupplier::"); fprintf(stderr, x)
#else
# define TRACE(x...)
# define ERROR(x...) fprintf(stderr, "ProxyAudioSupplier::"); fprintf(stderr, x)
#endif
struct PlayingInterval {
PlayingInterval(bigtime_t startTime, bigtime_t endTime)
:
start_time(startTime),
end_time(endTime)
{
}
bigtime_t start_time;
bigtime_t end_time;
bigtime_t x_start_time;
bigtime_t x_end_time;
float speed;
};
ProxyAudioSupplier::ProxyAudioSupplier(PlaybackManager* playbackManager)
:
fSupplierLock("audio supplier lock"),
fPlaybackManager(playbackManager),
fVideoFrameRate(25.0),
fVolume(1.0),
fSupplier(NULL),
fAdapter(NULL),
fVolumeConverter(NULL),
fAudioResampler()
{
TRACE("ProxyAudioSupplier()\n");
}
ProxyAudioSupplier::~ProxyAudioSupplier()
{
TRACE("~ProxyAudioSupplier()\n");
delete fAdapter;
delete fVolumeConverter;
}
bigtime_t
ProxyAudioSupplier::InitialLatency() const
{
BAutolock _(fSupplierLock);
if (fSupplier == NULL)
return 0;
return fSupplier->InitialLatency();
}
status_t
ProxyAudioSupplier::GetFrames(void* buffer, int64 frameCount,
bigtime_t startTime, bigtime_t endTime)
{
TRACE("GetFrames(%p, frameCount: %" B_PRId64 ", time interval: %"
B_PRIdBIGTIME " - %" B_PRIdBIGTIME ")\n",
buffer, frameCount, startTime, endTime);
BList playingIntervals;
status_t error = fPlaybackManager->LockWithTimeout(10000);
if (error == B_OK) {
bigtime_t intervalStartTime = startTime;
while (intervalStartTime < endTime) {
PlayingInterval* interval
= new (nothrow) PlayingInterval(intervalStartTime, endTime);
if (!interval) {
error = B_NO_MEMORY;
break;
}
fPlaybackManager->GetPlaylistTimeInterval(
interval->start_time, interval->end_time,
interval->x_start_time, interval->x_end_time,
interval->speed);
if (intervalStartTime == interval->end_time) {
delete interval;
error = B_ERROR;
ERROR("GetFrames() - zero duration audio interval! start "
"time: %" B_PRIdBIGTIME "\n", intervalStartTime);
break;
}
if (!playingIntervals.AddItem(interval)) {
delete interval;
error = B_NO_MEMORY;
ERROR("GetFrames() - Out of memory\n");
break;
}
intervalStartTime = interval->end_time;
}
fPlaybackManager->SetCurrentAudioTime(endTime);
fPlaybackManager->Unlock();
} else if (error == B_TIMED_OUT) {
TRACE("GetFrames() - LOCKING THE PLAYBACK MANAGER TIMED OUT!!!\n");
}
BAutolock _(fSupplierLock);
if (!fSupplier)
return B_ERROR;
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
int32 intervalIndex = 0;
#endif
int64 framesRead = 0;
while (!playingIntervals.IsEmpty()) {
PlayingInterval* interval
= (PlayingInterval*)playingIntervals.RemoveItem((int32)0);
if (error != B_OK) {
delete interval;
continue;
}
int32 playingDirection = 0;
if (interval->speed > 0)
playingDirection = 1;
else if (interval->speed < 0)
playingDirection = -1;
float absSpeed = interval->speed * playingDirection;
int64 framesToRead = _AudioFrameForTime(interval->end_time)
- _AudioFrameForTime(interval->start_time);
TRACE("GetFrames() - interval (%ld) [%lld, %lld]: [%lld, %lld], "
"frames: %lld\n", intervalIndex,
interval->start_time, interval->end_time,
interval->x_start_time, interval->x_end_time,
framesToRead);
if (absSpeed == 0)
_ReadSilence(buffer, framesToRead);
else {
fAudioResampler.SetInOffset(
_AudioFrameForTime(interval->x_start_time));
fAudioResampler.SetTimeScale(absSpeed);
error = fAudioResampler.Read(buffer, 0, framesToRead);
if (error == B_OK && interval->speed < 0)
_ReverseFrames(buffer, framesToRead);
}
if (error != B_OK) {
_ReadSilence(buffer, framesToRead);
error = B_OK;
}
framesRead += framesToRead;
buffer = _SkipFrames(buffer, framesToRead);
delete interval;
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
intervalIndex++;
#endif
}
if (error != B_OK) {
_ReadSilence(buffer, frameCount);
error = B_OK;
}
TRACE("GetFrames() done\n");
return error;
}
void
ProxyAudioSupplier::SetFormat(const media_format& format)
{
#ifdef TRACE_PROXY_AUDIO_SUPPLIER
char string[256];
string_for_format(format, string, 256);
TRACE("SetFormat(%s)\n", string);
#endif
BAutolock _(fSupplierLock);
fAudioResampler.SetFormat(format);
SetSupplier(fSupplier, fVideoFrameRate);
}
const media_format&
ProxyAudioSupplier::Format() const
{
return fAudioResampler.Format();
}
status_t
ProxyAudioSupplier::InitCheck() const
{
status_t ret = AudioSupplier::InitCheck();
if (ret < B_OK)
return ret;
return B_OK;
}
void
ProxyAudioSupplier::SetSupplier(AudioTrackSupplier* supplier,
float videoFrameRate)
{
TRACE("SetSupplier(%p, %.1f)\n", supplier, videoFrameRate);
BAutolock _(fSupplierLock);
fSupplier = supplier;
fVideoFrameRate = videoFrameRate;
delete fAdapter;
delete fVolumeConverter;
fAdapter = new AudioAdapter(fSupplier, Format());
fVolumeConverter = new AudioVolumeConverter(fAdapter, fVolume);
fAudioResampler.SetSource(fVolumeConverter);
}
void
ProxyAudioSupplier::SetVolume(float volume)
{
BAutolock _(fSupplierLock);
fVolume = volume;
if (fVolumeConverter)
fVolumeConverter->SetVolume(volume);
}
float
ProxyAudioSupplier::Volume()
{
BAutolock _(fSupplierLock);
return fVolume;
}
int64
ProxyAudioSupplier::_AudioFrameForVideoFrame(int64 frame) const
{
if (!fSupplier) {
return (int64)((double)frame * Format().u.raw_audio.frame_rate
/ fVideoFrameRate);
}
const media_format& format = fSupplier->Format();
return (int64)((double)frame * format.u.raw_audio.frame_rate
/ fVideoFrameRate);
}
int64
ProxyAudioSupplier::_VideoFrameForAudioFrame(int64 frame) const
{
if (!fSupplier) {
return (int64)((double)frame * fVideoFrameRate
/ Format().u.raw_audio.frame_rate);
}
const media_format& format = fSupplier->Format();
return (int64)((double)frame * fVideoFrameRate
/ format.u.raw_audio.frame_rate);
}
int64
ProxyAudioSupplier::_AudioFrameForTime(bigtime_t time) const
{
return (int64)((double)time * Format().u.raw_audio.frame_rate
/ 1000000.0);
}
int64
ProxyAudioSupplier::_VideoFrameForTime(bigtime_t time) const
{
return (int64)((double)time * fVideoFrameRate / 1000000.0);
}
void
ProxyAudioSupplier::_ReadSilence(void* buffer, int64 frames) const
{
memset(buffer, 0, (char*)_SkipFrames(buffer, frames) - (char*)buffer);
}
void
ProxyAudioSupplier::_ReverseFrames(void* buffer, int64 frames) const
{
int32 sampleSize = Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 frameSize = sampleSize * Format().u.raw_audio.channel_count;
char* front = (char*)buffer;
char* back = (char*)buffer + (frames - 1) * frameSize;
while (front < back) {
for (int32 i = 0; i < frameSize; i++)
swap(front[i], back[i]);
front += frameSize;
back -= frameSize;
}
}
void*
ProxyAudioSupplier::_SkipFrames(void* buffer, int64 frames) const
{
int32 sampleSize = Format().u.raw_audio.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
int32 frameSize = sampleSize * Format().u.raw_audio.channel_count;
return (char*)buffer + frames * frameSize;
}