Aegisub/aegisub/src/audio_controller.h
Thomas Goyne aaf7285a6e Remove UI-related responsibilities from AudioController
Keeping track of audio markers and labels to be shown in the audio
display is not particularly related to AudioController's primary
responsiblity of controlling the playback of audio.

Originally committed to SVN as r6427.
2012-02-01 23:59:12 +00:00

303 lines
11 KiB
C++

// Copyright (c) 2009-2010, Niels Martin Hansen
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
//
// $Id$
/// @file audio_controller.h
/// @see audio_controller.cpp
/// @ingroup audio_ui
#pragma once
#ifndef AGI_PRE
#include <memory>
#include <vector>
#include <set>
#include <stdint.h>
#include <assert.h>
#include <wx/event.h>
#include <wx/string.h>
#include <wx/timer.h>
#include <wx/pen.h>
#include <wx/power.h>
#endif
#include <libaegisub/exception.h>
#include <libaegisub/scoped_ptr.h>
#include <libaegisub/signal.h>
class AudioPlayer;
class AudioProvider;
class AudioTimingController;
namespace agi { struct Context; }
class TimeRange;
/// @class AudioController
/// @brief Manage an open audio stream
///
/// Creates and destroys audio providers and players. This behaviour should at
/// some point be moved to a separate class, as it adds too many
/// responsibilities to this class, but at the time of writing, it would extend
/// the scope of reworking components too much.
///
/// There is not supposed to be a way to get direct access to the audio
/// providers or players owned by a controller. If some operation that isn't
/// possible in the existing design is needed, the controller should be
/// extended in some way to allow it.
class AudioController : public wxEvtHandler {
/// Project context this controller belongs to
agi::Context *context;
/// Slot for subtitles save signal
agi::signal::Connection subtitle_save_slot;
/// A new audio stream was opened (and any previously open was closed)
agi::signal::Signal<AudioProvider*> AnnounceAudioOpen;
/// The current audio stream was closed
agi::signal::Signal<> AnnounceAudioClose;
/// Playback is in progress and the current position was updated
agi::signal::Signal<int> AnnouncePlaybackPosition;
/// Playback has stopped
agi::signal::Signal<> AnnouncePlaybackStop;
/// The timing controller was replaced
agi::signal::Signal<> AnnounceTimingControllerChanged;
/// The audio output object
AudioPlayer *player;
/// The audio provider
AudioProvider *provider;
/// The current timing mode, if any; owned by the audio controller
agi::scoped_ptr<AudioTimingController> timing_controller;
/// The URL of the currently open audio, if any
wxString audio_url;
enum PlaybackMode {
PM_NotPlaying,
PM_Range,
PM_PrimaryRange,
PM_ToEnd
};
/// The current playback mode
PlaybackMode playback_mode;
/// Timer used for playback position updates
wxTimer playback_timer;
/// Event handler for the playback timer
void OnPlaybackTimer(wxTimerEvent &event);
/// @brief Timing controller signals primary playback range changed
void OnTimingControllerUpdatedPrimaryRange();
/// @brief Timing controller signals that the rendering style ranges have changed
void OnTimingControllerUpdatedStyleRanges();
/// Subtitles save slot which adds the audio uri to the subtitles
void OnSubtitlesSave();
/// Handler for the current audio player changing
void OnAudioPlayerChanged();
/// Handler for the current audio provider changing
void OnAudioProviderChanged();
#ifdef wxHAS_POWER_EVENTS
/// Handle computer going into suspend mode by stopping audio and closing device
void OnComputerSuspending(wxPowerEvent &event);
/// Handle computer resuming from suspend by re-opening the audio device
void OnComputerResuming(wxPowerEvent &event);
#endif
/// @brief Convert a count of audio samples to a time in milliseconds
/// @param samples Sample count to convert
/// @return The number of milliseconds equivalent to the sample-count, rounded down
int64_t MillisecondsFromSamples(int64_t samples) const;
/// @brief Convert a time in milliseconds to a count of audio samples
/// @param ms Time in milliseconds to convert
/// @return The index of the first sample that is wholly inside the millisecond
int64_t SamplesFromMilliseconds(int64_t ms) const;
public:
/// @brief Constructor
AudioController(agi::Context *context);
/// @brief Destructor
~AudioController();
/// @brief Open an audio stream
/// @param url URL of the stream to open
///
/// The URL can either be a plain filename (with no qualifiers) or one
/// recognised by various providers.
void OpenAudio(const wxString &url);
/// @brief Closes the current audio stream
void CloseAudio();
/// @brief Determine whether audio is currently open
/// @return True if an audio stream is open and can be played back
bool IsAudioOpen() const;
/// @brief Get the URL for the current open audio stream
/// @return The URL for the audio stream
///
/// The returned URL can be passed into OpenAudio() later to open the same
/// stream again.
wxString GetAudioURL() const;
/// @brief Start or restart audio playback, playing a range
/// @param range The range of audio to play back
///
/// The end of the played back range may be requested changed, but is not
/// changed automatically from any other operations.
void PlayRange(const TimeRange &range);
/// @brief Start or restart audio playback, playing the primary playback range
///
/// If the primary playback range is updated during playback, the end of
/// the active playback range will be updated to match the new selection.
/// The playback end can not be changed in any other way.
void PlayPrimaryRange();
/// @brief Start or restart audio playback, playing from a point to the end of of the primary playback range
/// @param start_ms Time in milliseconds to start playback at
///
/// This behaves like PlayPrimaryRange, but the start point can differ from
/// the beginning of the primary range.
void PlayToEndOfPrimary(int start_ms);
/// @brief Start or restart audio playback, playing from a point to the end of stream
/// @param start_ms Time in milliseconds to start playback at
///
/// Playback to end cannot be converted to a range playback like range
/// playback can, it will continue until the end is reached, it is stopped,
/// or restarted.
void PlayToEnd(int start_ms);
/// @brief Stop all audio playback
void Stop();
/// @brief Determine whether playback is ongoing
/// @return True if audio is being played back
bool IsPlaying();
/// @brief Get the current playback position
/// @return Approximate current time in milliseconds being heard by the user
///
/// Returns 0 if playback is stopped. The return value is only approximate.
int GetPlaybackPosition();
/// Get the duration of the currently open audio in milliseconds, or 0 if none
/// @return Duration in milliseconds
int GetDuration() const;
/// @brief If playing, restart playback from the specified position
/// @param new_position Time to restart playback from
///
/// This function can be used to re-synchronise audio playback to another
/// source that might not be able to keep up with the full speed, such as
/// video playback in high resolution or with complex subtitles.
///
/// This function only does something if audio is already playing.
void ResyncPlaybackPosition(int new_position);
/// @brief Get the primary playback range
/// @return An immutable TimeRange object
TimeRange GetPrimaryPlaybackRange() const;
/// @brief Get the playback audio volume
/// @return The amplification factor for the audio
double GetVolume() const;
/// @brief Set the playback audio volume
/// @param volume The new amplification factor for the audio
void SetVolume(double volume);
/// @brief Return the current timing controller
/// @return The current timing controller or 0
AudioTimingController * GetTimingController() const { return timing_controller.get(); }
/// @brief Change the current timing controller
/// @param new_mode The new timing controller or 0. This may be the same
/// object as the current timing controller, to signal that the timing
/// controller has changed the object being timed, e.g. changed to a new
/// dialogue line.
void SetTimingController(AudioTimingController *new_controller);
/// @brief Save a portion of the decoded loaded audio to a wav file
/// @param filename File to save to
/// @param range Time range to save
void SaveClip(wxString const& filename, TimeRange const& range) const;
DEFINE_SIGNAL_ADDERS(AnnounceAudioOpen, AddAudioOpenListener)
DEFINE_SIGNAL_ADDERS(AnnounceAudioClose, AddAudioCloseListener)
DEFINE_SIGNAL_ADDERS(AnnouncePlaybackPosition, AddPlaybackPositionListener)
DEFINE_SIGNAL_ADDERS(AnnouncePlaybackStop, AddPlaybackStopListener)
DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener)
};
namespace agi {
/// Base class for all audio-related errors
DEFINE_BASE_EXCEPTION(AudioError, Exception)
/// Opening the audio failed for any reason
DEFINE_SIMPLE_EXCEPTION(AudioOpenError, AudioError, "audio/open")
/// There are no audio providers available to open audio files
DEFINE_SIMPLE_EXCEPTION(NoAudioProvidersError, AudioOpenError, "audio/open/no_providers")
/// The file exists, but no providers could find any audio tracks in it
DEFINE_SIMPLE_EXCEPTION(AudioDataNotFoundError, AudioOpenError, "audio/open/no_tracks")
/// There are audio tracks, but no provider could actually read them
DEFINE_SIMPLE_EXCEPTION(AudioProviderOpenError, AudioOpenError, "audio/open/provider")
/// The audio cache failed to initialize
DEFINE_SIMPLE_EXCEPTION(AudioCacheOpenError, AudioOpenError, "audio/open/cache")
/// There are no audio players available
DEFINE_SIMPLE_EXCEPTION(NoAudioPlayersError, AudioOpenError, "audio/open/no_players")
/// The audio player failed to initialize
DEFINE_SIMPLE_EXCEPTION(AudioPlayerOpenError, AudioOpenError, "audio/open/player")
}