You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
akode/akode/lib/player.cpp

653 lines
15 KiB

/* aKode: Player
Copyright (C) 2004-2005 Allan Sandfeld Jensen <kde@carewolf.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <cstring>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>
#include "audioframe.h"
#include "audiobuffer.h"
#include "decoder.h"
#include "buffered_decoder.h"
#include "mmapfile.h"
#include "localfile.h"
#include "volumefilter.h"
#include "sink.h"
#include "converter.h"
#include "resampler.h"
#include "magic.h"
#include "player.h"
#ifndef NDEBUG
#include <iostream>
#define AKODE_DEBUG(x) {std::cerr << "akode: " << x << "\n";}
#else
#define AKODE_DEBUG(x) { }
#endif
namespace aKode {
struct Player::private_data
{
private_data() : src(0)
, frame_decoder(0)
, resampler(0)
, converter(0)
, volume_filter(0)
, sink(0)
, manager(0)
, monitor(0)
, decoder_plugin(0)
, resampler_plugin("fast")
, sample_rate(0)
, state(Closed)
, my_file(false)
, my_sink(false)
, start_pos(0)
, halt(false)
, pause(false)
, detached(false)
, running(false)
{};
File *src;
Decoder *frame_decoder;
BufferedDecoder buffered_decoder;
Resampler *resampler;
Converter *converter;
// Volatile because it can be created and destroyed during playback
VolumeFilter* volatile volume_filter;
Sink *sink;
Player::Manager *manager;
Player::Monitor *monitor;
const char* decoder_plugin;
const char* resampler_plugin;
SinkPluginHandler sink_handler;
DecoderPluginHandler decoder_handler;
ResamplerPluginHandler resampler_handler;
unsigned int sample_rate;
State state;
bool my_file;
bool my_sink;
int start_pos;
volatile bool halt;
volatile bool pause;
volatile bool detached;
bool running;
pthread_t player_thread;
sem_t pause_sem;
void setState(Player::State s) {
state = s;
if (manager) manager->stateChangeEvent(s);
}
// Called for detached players
void cleanup() {
buffered_decoder.stop();
buffered_decoder.closeDecoder();
delete frame_decoder;
if (my_file)
delete src;
frame_decoder = 0;
src = 0;
decoder_handler.unload();
delete resampler;
delete converter;
resampler = 0;
converter = 0;
delete this;
}
};
// The player-thread. It is controlled through the two variables
// halt and seek_pos in d
static void* run_player(void* arg) {
Player::private_data *d = (Player::private_data*)arg;
AudioFrame frame;
AudioFrame re_frame;
AudioFrame c_frame;
bool no_error = true;
while(true) {
if (d->pause) {
d->sink->pause();
sem_wait(&d->pause_sem);
d->sink->resume();
}
if (d->halt) break;
no_error = d->buffered_decoder.readFrame(&frame);
if (!no_error) {
if (d->buffered_decoder.eof())
goto eof;
else
if (d->buffered_decoder.error())
goto error;
else
AKODE_DEBUG("Blip?");
}
else {
AudioFrame* out_frame = &frame;
if (d->resampler) {
d->resampler->doFrame(out_frame, &re_frame);
out_frame = &re_frame;
}
if (d->converter) {
d->converter->doFrame(out_frame, &c_frame);
out_frame = &c_frame;
}
if (d->volume_filter)
d->volume_filter->doFrame(out_frame);
no_error = d->sink->writeFrame(out_frame);
if (d->monitor)
d->monitor->writeFrame(out_frame);
if (!no_error) {
// ### Check type of error
goto error;
}
}
}
// Stoped by Player::stop()
// d->buffered_decoder->stop();
assert(!d->detached);
return (void*)0;
error:
if (d->detached) d->cleanup();
else {
d->buffered_decoder.stop();
if (d->manager)
d->manager->errorEvent();
}
return (void*)0;
eof:
if (d->detached) d->cleanup();
else {
d->buffered_decoder.stop();
if (d->manager)
d->manager->eofEvent();
}
return (void*)0;
}
Player::Player() {
d = new private_data;
sem_init(&d->pause_sem,0,0);
}
Player::~Player() {
close();
sem_destroy(&d->pause_sem);
delete d;
}
bool Player::open(const char* sinkname) {
if (state() != Closed)
close();
assert(state() == Closed);
d->sink_handler.load(sinkname);
if (!d->sink_handler.isLoaded()) {
AKODE_DEBUG("Could not load " << sinkname << "-sink");
return false;
}
d->sink = d->sink_handler.openSink();
if (!d->sink) {
AKODE_DEBUG("Could not create " << sinkname << "-sink");
return false;
}
if (!d->sink->open()) {
AKODE_DEBUG("Could not open " << sinkname << "-sink");
delete d->sink;
d->sink = 0;
return false;
}
d->my_sink = true;
setState(Open);
return true;
}
bool Player::open(Sink *sink) {
if (state() != Closed)
close();
assert(state() == Closed);
d->sink = sink;
if (!d->sink->open()) {
AKODE_DEBUG("Could not open sink");
d->sink = 0;
return false;
}
d->my_sink = false;
setState(Open);
return true;
}
void Player::close() {
if (state() == Closed) return;
if (state() != Open)
unload();
assert(state() == Open);
delete d->volume_filter;
d->volume_filter = 0;
if (d->my_sink) delete d->sink;
d->sink = 0;
d->sink_handler.unload();
setState(Closed);
}
bool Player::load(const char* filename) {
if (state() == Closed) return false;
if (state() == Paused || state() == Playing)
stop();
if (state() == Loaded)
unload();
assert(state() == Open);
d->src = new MMapFile(filename);
// Test if the file _can_ be mmaped
if (!d->src->openRO()) {
delete d->src;
d->src = new LocalFile(filename);
if (!d->src->openRO()) {
AKODE_DEBUG("Could not open " << filename);
delete d->src;
d->src = 0;
return false;
}
}
// Some of the later code expects it to be closed
d->src->close();
d->my_file = true;
return load();
}
bool Player::load(File *file) {
if (state() == Closed) return false;
if (state() == Paused || state() == Playing)
stop();
if (state() == Loaded)
unload();
assert(state() == Open);
if (!file->openRO())
return false;
file->close();
d->src = file;
d->my_file = false;
return load();
}
bool Player::load() {
if (d->decoder_plugin) {
if (!d->decoder_handler.load(d->decoder_plugin))
AKODE_DEBUG("Could not load " << d->decoder_plugin << "-decoder");
}
if (!d->decoder_handler.isLoaded()) {
string format = Magic::detectFile(d->src);
if (!format.empty())
AKODE_DEBUG("Guessed format: " << format)
else {
AKODE_DEBUG("Cannot detect mimetype");
delete d->src;
d->src = 0;
return false;
}
if (!d->decoder_handler.load(format))
AKODE_DEBUG("Could not load " << format << "-decoder");
}
if (!d->decoder_handler.isLoaded()) {
delete d->src;
d->src = 0;
return false;
}
d->frame_decoder = d->decoder_handler.openDecoder(d->src);
if (!d->frame_decoder) {
AKODE_DEBUG("Failed to open Decoder");
d->decoder_handler.unload();
delete d->src;
d->src = 0;
return false;
}
AudioFrame first_frame;
if (!d->frame_decoder->readFrame(&first_frame)) {
AKODE_DEBUG("Failed to decode first frame");
delete d->frame_decoder;
d->frame_decoder = 0;
d->decoder_handler.unload();
delete d->src;
d->src = 0;
return false;
}
if (!loadResampler()) {
AKODE_DEBUG("The resampler failed to load");
return false;
}
int state = d->sink->setAudioConfiguration(&first_frame);
if (state < 0) {
AKODE_DEBUG("The sink could not be configured for this format");
delete d->frame_decoder;
d->frame_decoder = 0;
d->decoder_handler.unload();
delete d->src;
d->src = 0;
return false;
}
else
if (state > 0) {
// Configuration not 100% accurate
d->sample_rate = d->sink->audioConfiguration()->sample_rate;
if (d->sample_rate != first_frame.sample_rate) {
AKODE_DEBUG("Resampling to " << d->sample_rate);
d->resampler->setSampleRate(d->sample_rate);
}
int out_channels = d->sink->audioConfiguration()->channels;
int in_channels = first_frame.channels;
if (in_channels != out_channels) {
// ### We don't do mixing yet
AKODE_DEBUG("Sample has wrong number of channels");
delete d->frame_decoder;
d->frame_decoder = 0;
d->decoder_handler.unload();
delete d->src;
d->src = 0;
return false;
}
int out_width = d->sink->audioConfiguration()->sample_width;
int in_width = first_frame.sample_width;
if (in_width != out_width) {
AKODE_DEBUG("Converting to " << out_width << "bits");
if (!d->converter)
d->converter = new Converter(out_width);
else
d->converter->setSampleWidth(out_width);
}
}
else
{
delete d->resampler;
delete d->converter;
d->resampler = 0;
d->converter = 0;
}
// connect the streams to play
d->buffered_decoder.setBlockingRead(true);
d->buffered_decoder.openDecoder(d->frame_decoder);
d->buffered_decoder.buffer()->put(&first_frame);
setState(Loaded);
return true;
}
bool Player::loadResampler() {
if (!d->resampler) {
d->resampler_handler.load(d->resampler_plugin);
d->resampler = d->resampler_handler.openResampler();
}
return d->resampler != 0;
}
void Player::unload() {
if (state() == Closed || state() == Open) return;
if (state() == Paused || state() == Playing)
stop();
assert(state() == Loaded);
d->buffered_decoder.closeDecoder();
delete d->frame_decoder;
if (d->my_file)
delete d->src;
d->frame_decoder = 0;
d->src = 0;
d->decoder_handler.unload();
delete d->resampler;
delete d->converter;
d->resampler = 0;
d->converter = 0;
setState(Open);
}
void Player::play() {
if (state() == Closed || state() == Open) return;
if (state() == Playing) return;
if (state() == Paused) {
return resume();
}
assert(state() == Loaded);
d->frame_decoder->seek(0);
// Start buffering
d->buffered_decoder.start();
d->halt = d->pause = false;
if (pthread_create(&d->player_thread, 0, run_player, d) == 0) {
d->running = true;
setState(Playing);
} else {
d->running = false;
setState(Loaded);
}
}
void Player::detach() {
if (state() == Closed || state() == Open) return;
if (state() == Loaded) return;
if (state() == Paused) resume();
assert(state() == Playing);
if (d->running) {
pthread_detach(d->player_thread);
d->running = false;
}
private_data * d_new = new private_data;
d_new->sink = d->sink;
d_new->volume_filter = d->volume_filter;
d_new->sample_rate = d->sample_rate;
d_new->state = d->state;
d->detached = true;
d = d_new;
setState(Open);
}
void Player::stop() {
if (state() == Closed || state() == Open) return;
if (state() == Loaded) return;
// Needs to set halt first to avoid the paused thread playing a soundbite
d->halt = true;
if (state() == Paused) resume();
assert(state() == Playing);
d->buffered_decoder.stop();
if (d->running) {
pthread_join(d->player_thread, 0);
d->running = false;
}
setState(Loaded);
}
// Much like stop except we don't send a halt signal
void Player::wait() {
if (state() == Closed || state() == Open) return;
if (state() == Loaded) return;
if (state() == Paused) resume();
assert(state() == Playing);
if (d->running) {
pthread_join(d->player_thread, 0);
d->running = false;
}
setState(Loaded);
}
void Player::pause() {
if (state() == Closed || state() == Open || state() == Loaded) return;
if (state() == Paused) return;
assert(state() == Playing);
//d->buffer->pause();
d->pause = true;
setState(Paused);
}
void Player::resume() {
if (state() != Paused) return;
d->pause = false;
sem_post(&d->pause_sem);
setState(Playing);
}
void Player::setVolume(float f) {
if (f < 0.0 || f > 1.0) return;
if (f != 1.0 && !d->volume_filter) {
VolumeFilter *vf = new VolumeFilter();
vf->setVolume(f);
d->volume_filter = vf;
}
else if (f != 1.0) {
d->volume_filter->setVolume(f);
}
else if (d->volume_filter) {
VolumeFilter *f = d->volume_filter;
d->volume_filter = 0;
delete f;
}
}
float Player::volume() const {
if (!d->volume_filter) return 1.0;
else
return d->volume_filter->volume();
}
File* Player::file() const {
return d->src;
}
Sink* Player::sink() const {
return d->sink;
}
Decoder* Player::decoder() const {
return &d->buffered_decoder;
}
Resampler* Player::resampler() const {
return d->resampler;
}
Player::State Player::state() const {
return d->state;
}
void Player::setDecoderPlugin(const char* plugin) {
if (plugin && strncmp(plugin, "auto", 4) == 0) plugin = 0;
d->decoder_plugin = plugin;
}
void Player::setResamplerPlugin(const char* plugin) {
d->resampler_plugin = plugin;
}
void Player::setManager(Manager *manager) {
d->manager = manager;
}
void Player::setMonitor(Monitor *monitor) {
d->monitor = monitor;
}
void Player::setState(Player::State state) {
d->setState(state);
}
} // namespace