diff options
Diffstat (limited to 'akode/lib/player.cpp')
-rw-r--r-- | akode/lib/player.cpp | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/akode/lib/player.cpp b/akode/lib/player.cpp new file mode 100644 index 0000000..c785569 --- /dev/null +++ b/akode/lib/player.cpp @@ -0,0 +1,650 @@ +/* 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 <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 |