Merge branch 'CC-2301'

This commit is contained in:
Naomi 2013-05-03 16:15:39 -04:00
commit 6bdc2caaec
40 changed files with 4204 additions and 140 deletions

View File

@ -52,6 +52,24 @@ class LibraryController extends Zend_Controller_Action
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/spl.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/playlist/smart_blockbuilder.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/observer/observer.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/config.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/curves.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/fades.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/local_storage.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/controls.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/playout.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/track_render.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/track.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/time_scale.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/waveformplaylist/playlist.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
//arbitrary attributes need to be allowed to set an id for the templates.
$this->view->headScript()->setAllowArbitraryAttributes(true);
//$this->view->headScript()->appendScript(file_get_contents(APPLICATION_PATH.'/../public/js/waveformplaylist/templates/bottombar.tpl'),
// 'text/template', array('id' => 'tpl_playlist_cues', 'noescape' => true));
$this->view->headLink()->appendStylesheet($baseUrl.'css/playlist_builder.css?'.$CC_CONFIG['airtime_version']);
try {

View File

@ -10,6 +10,7 @@ class PlaylistController extends Zend_Controller_Action
->addActionContext('move-items', 'json')
->addActionContext('delete-items', 'json')
->addActionContext('set-fade', 'json')
->addActionContext('set-crossfade', 'json')
->addActionContext('set-cue', 'json')
->addActionContext('new', 'json')
->addActionContext('edit', 'json')
@ -417,6 +418,33 @@ class PlaylistController extends Zend_Controller_Action
$this->playlistUnknownError($e);
}
}
public function setCrossfadeAction()
{
$id1 = $this->_getParam('id1');
$id2 = $this->_getParam('id2');
$type = $this->_getParam('type');
$fadeIn = $this->_getParam('fadeIn', 0);
$fadeOut = $this->_getParam('fadeOut', 0);
$offset = $this->_getParam('offset', 0);
try {
$obj = $this->getPlaylist($type);
$response = $obj->createCrossfade($id1, $fadeOut, $id2, $fadeIn, $offset);
if (!isset($response["error"])) {
$this->createUpdateResponse($obj);
} else {
$this->view->error = $response["error"];
}
} catch (PlaylistOutDatedException $e) {
$this->playlistOutdated($e);
} catch (PlaylistNotFoundException $e) {
$this->playlistNotFound($type);
} catch (Exception $e) {
$this->playlistUnknownError($e);
}
}
public function getPlaylistFadesAction()
{

View File

@ -43,7 +43,9 @@ class PreferenceController extends Zend_Controller_Action
if ($form->isValid($values)) {
Application_Model_Preference::SetHeadTitle($values["stationName"], $this->view);
Application_Model_Preference::SetDefaultFade($values["stationDefaultFade"]);
Application_Model_Preference::SetDefaultCrossfadeDuration($values["stationDefaultCrossfadeDuration"]);
Application_Model_Preference::SetDefaultFadeIn($values["stationDefaultFadeIn"]);
Application_Model_Preference::SetDefaultFadeOut($values["stationDefaultFadeOut"]);
Application_Model_Preference::SetAllow3rdPartyApi($values["thirdPartyApi"]);
Application_Model_Preference::SetDefaultLocale($values["locale"]);
Application_Model_Preference::SetDefaultTimezone($values["timezone"]);

View File

@ -12,11 +12,9 @@ class Application_Form_GeneralPreferences extends Zend_Form_SubForm
array('ViewScript', array('viewScript' => 'form/preferences_general.phtml'))
));
$defaultFade = Application_Model_Preference::GetDefaultFade();
if ($defaultFade == "") {
$defaultFade = '0.5';
}
$defaultFadeIn = Application_Model_Preference::GetDefaultFadeIn();
$defaultFadeOut = Application_Model_Preference::GetDefaultFadeOut();
//Station name
$this->addElement('text', 'stationName', array(
'class' => 'input_text',
@ -28,11 +26,30 @@ class Application_Form_GeneralPreferences extends Zend_Form_SubForm
'ViewHelper'
)
));
//Default station fade in
$this->addElement('text', 'stationDefaultCrossfadeDuration', array(
'class' => 'input_text',
'label' => _('Default Crossfade Duration (s):'),
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
array(
$rangeValidator,
$notEmptyValidator,
'regex', false, array('/^[0-9]{1,2}(\.\d{1})?$/', 'messages' => _('enter a time in seconds 0{.0}'))
)
),
'value' => Application_Model_Preference::GetDefaultCrossfadeDuration(),
'decorators' => array(
'ViewHelper'
)
));
//Default station fade
$this->addElement('text', 'stationDefaultFade', array(
//Default station fade in
$this->addElement('text', 'stationDefaultFadeIn', array(
'class' => 'input_text',
'label' => _('Default Fade (s):'),
'label' => _('Default Fade In (s):'),
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
@ -42,11 +59,30 @@ class Application_Form_GeneralPreferences extends Zend_Form_SubForm
'regex', false, array('/^[0-9]{1,2}(\.\d{1})?$/', 'messages' => _('enter a time in seconds 0{.0}'))
)
),
'value' => $defaultFade,
'value' => $defaultFadeIn,
'decorators' => array(
'ViewHelper'
)
));
//Default station fade out
$this->addElement('text', 'stationDefaultFadeOut', array(
'class' => 'input_text',
'label' => _('Default Fade Out (s):'),
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
array(
$rangeValidator,
$notEmptyValidator,
'regex', false, array('/^[0-9]{1,2}(\.\d{1})?$/', 'messages' => _('enter a time in seconds 0{.0}'))
)
),
'value' => $defaultFadeOut,
'decorators' => array(
'ViewHelper'
)
));
$third_party_api = new Zend_Form_Element_Radio('thirdPartyApi');
$third_party_api->setLabel(

View File

@ -33,5 +33,46 @@
</div>
<div class="wrapper" id="content"><?php echo $this->layout()->content ?></div>
<script id="tmpl-pl-cues" type="text/template">
<div class="waveform-cues">
<div class="playlist-tracks"></div>
<div class="playlist-controls">
<span class="btn_play ui-state-default">Play</span>
<span class="btn_stop ui-state-default">Stop</span>
</div>
<div>
<input type="text" class="audio_start">
<input type="button" class="set-cue-in" value="Set Cue In">
<input type="text" class="audio_end">
<input type="button" class="set-cue-out" value="Set Cue Out">
<input type="text" class="audio_pos">
</div>
<div>
<label for="editor-cue-in">Cue In</label>
<input type="text" id="editor-cue-in" class="editor-cue-in">
<span style="display:none" class="cue-in-error"></span>
<label for="editor-cue-out">Cue Out</label>
<input type="text" id="editor-cue-out" class="editor-cue-out">
<span style="display:none" class="cue-out-error"></span>
</div>
</div>
</script>
<script id="tmpl-pl-fades" type="text/template">
<div class="waveform-fades">
<div class="playlist-tracks"></div>
<div>
<span class="btn_play ui-state-default">Play</span>
<span class="btn_stop ui-state-default">Stop</span>
</div>
<div>
<span class="btn_select ui-state-default" data-state="cursor">Cursor</span>
<span class="btn_fadein ui-state-default" data-state="fadein">Fade In</span>
<span class="btn_fadeout ui-state-default" data-state="fadeout">Fade Out</span>
<span class="btn_shift ui-state-default" data-state="shift">Shift</span>
</div>
</div>
</script>
</body>
</html>

View File

@ -99,13 +99,8 @@ class Application_Model_Block implements Application_Model_LibraryEditable
$this->block->save();
}
$defaultFade = Application_Model_Preference::GetDefaultFade();
if ($defaultFade !== "") {
//fade is in format SS.uuuuuu
$this->blockItem["fadein"] = $defaultFade;
$this->blockItem["fadeout"] = $defaultFade;
}
$this->blockItem["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
$this->blockItem["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
$this->con = isset($con) ? $con : Propel::getConnection(CcBlockPeer::DATABASE_NAME);
$this->id = $this->block->getDbId();
@ -200,6 +195,7 @@ SELECT pc.id AS id,
pc.cueout,
pc.fadein,
pc.fadeout,
pc.trackoffset,
bl.type,
f.LENGTH AS orig_length,
f.id AS item_id,
@ -234,7 +230,15 @@ SQL;
foreach ($rows as &$row) {
$clipSec = Application_Common_DateHelper::playlistTimeToSeconds($row['length']);
$row['trackSec'] = $clipSec;
$row['cueInSec'] = Application_Common_DateHelper::playlistTimeToSeconds($row['cuein']);
$row['cueOutSec'] = Application_Common_DateHelper::playlistTimeToSeconds($row['cueout']);
$trackoffset = $row['trackoffset'];
$offset += $clipSec;
$offset -= $trackoffset;
$offset_cliplength = Application_Common_DateHelper::secondsToPlaylistTime($offset);
//format the length for UI.
@ -668,6 +672,29 @@ SQL;
return array($fadeIn, $fadeOut);
}
/*
* create a crossfade from item in cc_playlist_contents with $id1 to item $id2.
*
* $fadeOut length of fade out in seconds if $id1
* $fadeIn length of fade in in seconds of $id2
* $offset time in seconds from end of $id1 that $id2 will begin to play.
*/
public function createCrossfade($id1, $fadeOut, $id2, $fadeIn, $offset)
{
$this->con->beginTransaction();
try {
$this->changeFadeInfo($id1, null, $fadeOut);
$this->changeFadeInfo($id2, $fadeIn, null, $offset);
$this->con->commit();
} catch (Exception $e) {
$this->con->rollback();
throw $e;
}
}
/**
* Change fadeIn and fadeOut values for block Element
@ -680,7 +707,7 @@ SQL;
* new value in ss.ssssss or extent format
* @return boolean
*/
public function changeFadeInfo($id, $fadeIn, $fadeOut)
public function changeFadeInfo($id, $fadeIn, $fadeOut, $offset=null)
{
//See issue CC-2065, pad the fadeIn and fadeOut so that it is TIME compatable with the DB schema
//For the top level PlayList either fadeIn or fadeOut will sometimes be Null so need a gaurd against
@ -714,6 +741,12 @@ SQL;
}
$row->setDbFadein($fadeIn);
if (!is_null($offset)) {
$row->setDbTrackOffset($offset);
Logging::info("Setting offset {$offset} on item {$id}");
$row->save($this->con);
}
}
if (!is_null($fadeOut)) {

View File

@ -36,6 +36,7 @@ class Application_Model_Playlist implements Application_Model_LibraryEditable
"cueout" => "00:00:00",
"fadein" => "0.0",
"fadeout" => "0.0",
"crossfadeDuration" => 0
);
//using propel's phpNames.
@ -60,13 +61,9 @@ class Application_Model_Playlist implements Application_Model_LibraryEditable
$this->pl->save();
}
$defaultFade = Application_Model_Preference::GetDefaultFade();
if ($defaultFade !== "") {
//fade is in format SS.uuuuuu
$this->plItem["fadein"] = $defaultFade;
$this->plItem["fadeout"] = $defaultFade;
}
$this->plItem["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
$this->plItem["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
$this->plItem["crossfadeDuration"] = Application_Model_Preference::GetDefaultCrossfadeDuration();
$this->con = isset($con) ? $con : Propel::getConnection(CcPlaylistPeer::DATABASE_NAME);
$this->id = $this->pl->getDbId();
@ -166,6 +163,7 @@ class Application_Model_Playlist implements Application_Model_LibraryEditable
pc.cueout,
pc.fadein,
pc.fadeout,
pc.trackoffset,
f.id AS item_id,
f.track_title,
f.artist_name AS creator,
@ -193,6 +191,7 @@ SQL;
pc.cueout,
pc.fadein,
pc.fadeout,
pc.trackoffset,
ws.id AS item_id,
(ws.name || ': ' || ws.url) AS title,
sub.login AS creator,
@ -213,6 +212,7 @@ SQL;
pc.cueout,
pc.fadein,
pc.fadeout,
pc.trackoffset,
bl.id AS item_id,
bl.name AS title,
sbj.login AS creator,
@ -227,6 +227,7 @@ SQL;
AND pc.TYPE = 2)) AS temp
ORDER BY temp.position;
SQL;
//Logging::info($sql);
$params = array(
':playlist_id1'=>$this->id, ':playlist_id2'=>$this->id, ':playlist_id3'=>$this->id);
@ -238,8 +239,18 @@ SQL;
$offset = 0;
foreach ($rows as &$row) {
//Logging::info($row);
$clipSec = Application_Common_DateHelper::playlistTimeToSeconds($row['length']);
$row['trackSec'] = $clipSec;
$row['cueInSec'] = Application_Common_DateHelper::playlistTimeToSeconds($row['cuein']);
$row['cueOutSec'] = Application_Common_DateHelper::playlistTimeToSeconds($row['cueout']);
$trackoffset = $row['trackoffset'];
$offset += $clipSec;
$offset -= $trackoffset;
$offset_cliplength = Application_Common_DateHelper::secondsToPlaylistTime($offset);
//format the length for UI.
@ -356,6 +367,7 @@ SQL;
$row->setDbFadeout(Application_Common_DateHelper::secondsToPlaylistTime($info["fadeout"]));
if ($info["ftype"] == "audioclip") {
$row->setDbFileId($info["id"]);
$row->setDbTrackOffset($info["crossfadeDuration"]);
$type = 0;
} elseif ($info["ftype"] == "stream") {
$row->setDbStreamId($info["id"]);
@ -645,19 +657,42 @@ SQL;
return array($fadeIn, $fadeOut);
}
/*
* create a crossfade from item in cc_playlist_contents with $id1 to item $id2.
*
* $fadeOut length of fade out in seconds if $id1
* $fadeIn length of fade in in seconds of $id2
* $offset time in seconds from end of $id1 that $id2 will begin to play.
*/
public function createCrossfade($id1, $fadeOut, $id2, $fadeIn, $offset)
{
$this->con->beginTransaction();
try {
$this->changeFadeInfo($id1, null, $fadeOut);
$this->changeFadeInfo($id2, $fadeIn, null, $offset);
$this->con->commit();
} catch (Exception $e) {
$this->con->rollback();
throw $e;
}
}
/**
* Change fadeIn and fadeOut values for playlist Element
*
* @param int $pos
* position of audioclip in playlist
* @param int $id
* id of audioclip in playlist contents table.
* @param string $fadeIn
* new value in ss.ssssss or extent format
* @param string $fadeOut
* new value in ss.ssssss or extent format
* @return boolean
*/
public function changeFadeInfo($id, $fadeIn, $fadeOut)
public function changeFadeInfo($id, $fadeIn, $fadeOut, $offset=null)
{
//See issue CC-2065, pad the fadeIn and fadeOut so that it is TIME compatable with the DB schema
//For the top level PlayList either fadeIn or fadeOut will sometimes be Null so need a gaurd against
@ -683,6 +718,12 @@ SQL;
$fadeIn = $clipLength;
}
$row->setDbFadein($fadeIn);
if (!is_null($offset)) {
$row->setDbTrackOffset($offset);
Logging::info("Setting offset {$offset} on item {$id}");
$row->save($this->con);
}
}
if (!is_null($fadeOut)) {

View File

@ -194,6 +194,57 @@ class Application_Model_Preference
return new DateTime($date, new DateTimeZone("UTC"));
}
}
public static function SetDefaultCrossfadeDuration($duration)
{
self::setValue("default_crossfade_duration", $duration);
}
public static function GetDefaultCrossfadeDuration()
{
$duration = self::getValue("default_crossfade_duration");
if ($duration === "") {
// the default value of the fade is 00.5
return "0";
}
return $duration;
}
public static function SetDefaultFadeIn($fade)
{
self::setValue("default_fade_in", $fade);
}
public static function GetDefaultFadeIn()
{
$fade = self::getValue("default_fade_in");
if ($fade === "") {
// the default value of the fade is 00.5
return "00.5";
}
return $fade;
}
public static function SetDefaultFadeOut($fade)
{
self::setValue("default_fade_out", $fade);
}
public static function GetDefaultFadeOut()
{
$fade = self::getValue("default_fade_out");
if ($fade === "") {
// the default value of the fade is 00.5
return "00.5";
}
return $fade;
}
public static function SetDefaultFade($fade)
{

View File

@ -17,6 +17,8 @@ class Application_Model_Scheduler
private $epochNow;
private $nowDT;
private $user;
private $crossfadeDuration;
private $checkUserPermissions = true;
@ -38,6 +40,8 @@ class Application_Model_Scheduler
}
$this->user = Application_Model_User::getCurrentUser();
$this->crossfadeDuration = Application_Model_Preference::GetDefaultCrossfadeDuration();
}
public function setCheckUserPermissions($value)
@ -201,12 +205,9 @@ class Application_Model_Scheduler
$data["cuein"] = $file->getDbCuein();
$data["cueout"] = $file->getDbCueout();
$defaultFade = Application_Model_Preference::GetDefaultFade();
if (isset($defaultFade)) {
//fade is in format SS.uuuuuu
$data["fadein"] = $defaultFade;
$data["fadeout"] = $defaultFade;
}
//fade is in format SS.uuuuuu
$data["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
$data["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
$files[] = $data;
}
@ -260,12 +261,11 @@ class Application_Model_Scheduler
$cuein = Application_Common_DateHelper::calculateLengthInSeconds($data["cuein"]);
$cueout = Application_Common_DateHelper::calculateLengthInSeconds($data["cueout"]);
$data["cliplength"] = Application_Common_DateHelper::secondsToPlaylistTime($cueout - $cuein);
$defaultFade = Application_Model_Preference::GetDefaultFade();
if (isset($defaultFade)) {
//fade is in format SS.uuuuuu
$data["fadein"] = $defaultFade;
$data["fadeout"] = $defaultFade;
}
//fade is in format SS.uuuuuu
$data["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
$data["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
$data["type"] = 0;
$files[] = $data;
}
@ -286,12 +286,9 @@ class Application_Model_Scheduler
$data["cueout"] = $stream->getDbLength();
$data["type"] = 1;
$defaultFade = Application_Model_Preference::GetDefaultFade();
if (isset($defaultFade)) {
//fade is in format SS.uuuuuu
$data["fadein"] = $defaultFade;
$data["fadeout"] = $defaultFade;
}
//fade is in format SS.uuuuuu
$data["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
$data["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
$files[] = $data;
}
@ -321,12 +318,11 @@ class Application_Model_Scheduler
$cuein = Application_Common_DateHelper::calculateLengthInSeconds($data["cuein"]);
$cueout = Application_Common_DateHelper::calculateLengthInSeconds($data["cueout"]);
$data["cliplength"] = Application_Common_DateHelper::secondsToPlaylistTime($cueout - $cuein);
$defaultFade = Application_Model_Preference::GetDefaultFade();
if (isset($defaultFade)) {
//fade is in format SS.uuuuuu
$data["fadein"] = $defaultFade;
$data["fadeout"] = $defaultFade;
}
//fade is in format SS.uuuuuu
$data["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
$data["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
$data["type"] = 0;
$files[] = $data;
}
@ -336,6 +332,31 @@ class Application_Model_Scheduler
return $files;
}
/*
* @param DateTime startDT in UTC
* @param string duration
* in format H:i:s.u (could be more that 24 hours)
*
* @return DateTime endDT in UTC
*/
private function findTimeDifference($p_startDT, $p_seconds)
{
$startEpoch = $p_startDT->format("U.u");
//add two float numbers to 6 subsecond precision
//DateTime::createFromFormat("U.u") will have a problem if there is no decimal in the resulting number.
$newEpoch = bcsub($startEpoch , (string) $p_seconds, 6);
$dt = DateTime::createFromFormat("U.u", $newEpoch, new DateTimeZone("UTC"));
if ($dt === false) {
//PHP 5.3.2 problem
$dt = DateTime::createFromFormat("U", intval($newEpoch), new DateTimeZone("UTC"));
}
return $dt;
}
/*
* @param DateTime startDT in UTC
@ -393,6 +414,43 @@ class Application_Model_Scheduler
return $nextDT;
}
/*
* @param int $showInstance
* This function recalculates the start/end times of items in a gapless show to
* account for crossfade durations.
*/
private function calculateCrossfades($showInstance)
{
Logging::info("adjusting start, end times of scheduled items to account for crossfades show instance #".$showInstance);
$instance = CcShowInstancesQuery::create()->findPK($showInstance, $this->con);
if (is_null($instance)) {
throw new OutDatedScheduleException(_("The schedule you're viewing is out of date!"));
}
$itemStartDT = $instance->getDbStarts(null);
$itemEndDT = null;
$schedule = CcScheduleQuery::create()
->filterByDbInstanceId($showInstance)
->orderByDbStarts()
->find($this->con);
foreach ($schedule as $item) {
$itemEndDT = $item->getDbEnds(null);
$item
->setDbStarts($itemStartDT)
->setDbEnds($itemEndDT);
$itemStartDT = $this->findTimeDifference($itemEndDT, $this->crossfadeDuration);
$itemEndDT = $this->findEndTime($itemStartDT, $item->getDbClipLength());
}
$schedule->save($this->con);
}
/*
* @param int $showInstance
@ -648,6 +706,8 @@ class Application_Model_Scheduler
$pend = microtime(true);
Logging::debug("adjusting all following items.");
Logging::debug(floatval($pend) - floatval($pstart));
$this->calculateCrossfades($instance->getDbId());
}
}//for each instance
@ -929,6 +989,7 @@ class Application_Model_Scheduler
foreach ($showInstances as $instance) {
$this->removeGaps($instance);
$this->calculateCrossfades($instance);
}
}

View File

@ -42,6 +42,7 @@ class CcBlockcontentsTableMap extends TableMap {
$this->addForeignKey('BLOCK_ID', 'DbBlockId', 'INTEGER', 'cc_block', 'ID', false, null, null);
$this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null);
$this->addColumn('POSITION', 'DbPosition', 'INTEGER', false, null, null);
$this->addColumn('TRACKOFFSET', 'DbTrackOffset', 'REAL', true, null, 0);
$this->addColumn('CLIPLENGTH', 'DbCliplength', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('CUEIN', 'DbCuein', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('CUEOUT', 'DbCueout', 'VARCHAR', false, null, '00:00:00');

View File

@ -45,6 +45,7 @@ class CcPlaylistcontentsTableMap extends TableMap {
$this->addColumn('STREAM_ID', 'DbStreamId', 'INTEGER', false, null, null);
$this->addColumn('TYPE', 'DbType', 'SMALLINT', true, null, 0);
$this->addColumn('POSITION', 'DbPosition', 'INTEGER', false, null, null);
$this->addColumn('TRACKOFFSET', 'DbTrackOffset', 'REAL', true, null, 0);
$this->addColumn('CLIPLENGTH', 'DbCliplength', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('CUEIN', 'DbCuein', 'VARCHAR', false, null, '00:00:00');
$this->addColumn('CUEOUT', 'DbCueout', 'VARCHAR', false, null, '00:00:00');

View File

@ -48,6 +48,13 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
*/
protected $position;
/**
* The value for the trackoffset field.
* Note: this column has a database default value of: 0
* @var double
*/
protected $trackoffset;
/**
* The value for the cliplength field.
* Note: this column has a database default value of: '00:00:00'
@ -118,6 +125,7 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
*/
public function applyDefaultValues()
{
$this->trackoffset = 0;
$this->cliplength = '00:00:00';
$this->cuein = '00:00:00';
$this->cueout = '00:00:00';
@ -175,6 +183,16 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
return $this->position;
}
/**
* Get the [trackoffset] column value.
*
* @return double
*/
public function getDbTrackOffset()
{
return $this->trackoffset;
}
/**
* Get the [cliplength] column value.
*
@ -359,6 +377,26 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
return $this;
} // setDbPosition()
/**
* Set the value of [trackoffset] column.
*
* @param double $v new value
* @return CcBlockcontents The current object (for fluent API support)
*/
public function setDbTrackOffset($v)
{
if ($v !== null) {
$v = (double) $v;
}
if ($this->trackoffset !== $v || $this->isNew()) {
$this->trackoffset = $v;
$this->modifiedColumns[] = CcBlockcontentsPeer::TRACKOFFSET;
}
return $this;
} // setDbTrackOffset()
/**
* Set the value of [cliplength] column.
*
@ -529,6 +567,10 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
*/
public function hasOnlyDefaultValues()
{
if ($this->trackoffset !== 0) {
return false;
}
if ($this->cliplength !== '00:00:00') {
return false;
}
@ -575,11 +617,12 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
$this->block_id = ($row[$startcol + 1] !== null) ? (int) $row[$startcol + 1] : null;
$this->file_id = ($row[$startcol + 2] !== null) ? (int) $row[$startcol + 2] : null;
$this->position = ($row[$startcol + 3] !== null) ? (int) $row[$startcol + 3] : null;
$this->cliplength = ($row[$startcol + 4] !== null) ? (string) $row[$startcol + 4] : null;
$this->cuein = ($row[$startcol + 5] !== null) ? (string) $row[$startcol + 5] : null;
$this->cueout = ($row[$startcol + 6] !== null) ? (string) $row[$startcol + 6] : null;
$this->fadein = ($row[$startcol + 7] !== null) ? (string) $row[$startcol + 7] : null;
$this->fadeout = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null;
$this->trackoffset = ($row[$startcol + 4] !== null) ? (double) $row[$startcol + 4] : null;
$this->cliplength = ($row[$startcol + 5] !== null) ? (string) $row[$startcol + 5] : null;
$this->cuein = ($row[$startcol + 6] !== null) ? (string) $row[$startcol + 6] : null;
$this->cueout = ($row[$startcol + 7] !== null) ? (string) $row[$startcol + 7] : null;
$this->fadein = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null;
$this->fadeout = ($row[$startcol + 9] !== null) ? (string) $row[$startcol + 9] : null;
$this->resetModified();
$this->setNew(false);
@ -588,7 +631,7 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
$this->ensureConsistency();
}
return $startcol + 9; // 9 = CcBlockcontentsPeer::NUM_COLUMNS - CcBlockcontentsPeer::NUM_LAZY_LOAD_COLUMNS).
return $startcol + 10; // 10 = CcBlockcontentsPeer::NUM_COLUMNS - CcBlockcontentsPeer::NUM_LAZY_LOAD_COLUMNS).
} catch (Exception $e) {
throw new PropelException("Error populating CcBlockcontents object", $e);
@ -947,18 +990,21 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
return $this->getDbPosition();
break;
case 4:
return $this->getDbCliplength();
return $this->getDbTrackOffset();
break;
case 5:
return $this->getDbCuein();
return $this->getDbCliplength();
break;
case 6:
return $this->getDbCueout();
return $this->getDbCuein();
break;
case 7:
return $this->getDbFadein();
return $this->getDbCueout();
break;
case 8:
return $this->getDbFadein();
break;
case 9:
return $this->getDbFadeout();
break;
default:
@ -989,11 +1035,12 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
$keys[1] => $this->getDbBlockId(),
$keys[2] => $this->getDbFileId(),
$keys[3] => $this->getDbPosition(),
$keys[4] => $this->getDbCliplength(),
$keys[5] => $this->getDbCuein(),
$keys[6] => $this->getDbCueout(),
$keys[7] => $this->getDbFadein(),
$keys[8] => $this->getDbFadeout(),
$keys[4] => $this->getDbTrackOffset(),
$keys[5] => $this->getDbCliplength(),
$keys[6] => $this->getDbCuein(),
$keys[7] => $this->getDbCueout(),
$keys[8] => $this->getDbFadein(),
$keys[9] => $this->getDbFadeout(),
);
if ($includeForeignObjects) {
if (null !== $this->aCcFiles) {
@ -1046,18 +1093,21 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
$this->setDbPosition($value);
break;
case 4:
$this->setDbCliplength($value);
$this->setDbTrackOffset($value);
break;
case 5:
$this->setDbCuein($value);
$this->setDbCliplength($value);
break;
case 6:
$this->setDbCueout($value);
$this->setDbCuein($value);
break;
case 7:
$this->setDbFadein($value);
$this->setDbCueout($value);
break;
case 8:
$this->setDbFadein($value);
break;
case 9:
$this->setDbFadeout($value);
break;
} // switch()
@ -1088,11 +1138,12 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
if (array_key_exists($keys[1], $arr)) $this->setDbBlockId($arr[$keys[1]]);
if (array_key_exists($keys[2], $arr)) $this->setDbFileId($arr[$keys[2]]);
if (array_key_exists($keys[3], $arr)) $this->setDbPosition($arr[$keys[3]]);
if (array_key_exists($keys[4], $arr)) $this->setDbCliplength($arr[$keys[4]]);
if (array_key_exists($keys[5], $arr)) $this->setDbCuein($arr[$keys[5]]);
if (array_key_exists($keys[6], $arr)) $this->setDbCueout($arr[$keys[6]]);
if (array_key_exists($keys[7], $arr)) $this->setDbFadein($arr[$keys[7]]);
if (array_key_exists($keys[8], $arr)) $this->setDbFadeout($arr[$keys[8]]);
if (array_key_exists($keys[4], $arr)) $this->setDbTrackOffset($arr[$keys[4]]);
if (array_key_exists($keys[5], $arr)) $this->setDbCliplength($arr[$keys[5]]);
if (array_key_exists($keys[6], $arr)) $this->setDbCuein($arr[$keys[6]]);
if (array_key_exists($keys[7], $arr)) $this->setDbCueout($arr[$keys[7]]);
if (array_key_exists($keys[8], $arr)) $this->setDbFadein($arr[$keys[8]]);
if (array_key_exists($keys[9], $arr)) $this->setDbFadeout($arr[$keys[9]]);
}
/**
@ -1108,6 +1159,7 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
if ($this->isColumnModified(CcBlockcontentsPeer::BLOCK_ID)) $criteria->add(CcBlockcontentsPeer::BLOCK_ID, $this->block_id);
if ($this->isColumnModified(CcBlockcontentsPeer::FILE_ID)) $criteria->add(CcBlockcontentsPeer::FILE_ID, $this->file_id);
if ($this->isColumnModified(CcBlockcontentsPeer::POSITION)) $criteria->add(CcBlockcontentsPeer::POSITION, $this->position);
if ($this->isColumnModified(CcBlockcontentsPeer::TRACKOFFSET)) $criteria->add(CcBlockcontentsPeer::TRACKOFFSET, $this->trackoffset);
if ($this->isColumnModified(CcBlockcontentsPeer::CLIPLENGTH)) $criteria->add(CcBlockcontentsPeer::CLIPLENGTH, $this->cliplength);
if ($this->isColumnModified(CcBlockcontentsPeer::CUEIN)) $criteria->add(CcBlockcontentsPeer::CUEIN, $this->cuein);
if ($this->isColumnModified(CcBlockcontentsPeer::CUEOUT)) $criteria->add(CcBlockcontentsPeer::CUEOUT, $this->cueout);
@ -1177,6 +1229,7 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
$copyObj->setDbBlockId($this->block_id);
$copyObj->setDbFileId($this->file_id);
$copyObj->setDbPosition($this->position);
$copyObj->setDbTrackOffset($this->trackoffset);
$copyObj->setDbCliplength($this->cliplength);
$copyObj->setDbCuein($this->cuein);
$copyObj->setDbCueout($this->cueout);
@ -1336,6 +1389,7 @@ abstract class BaseCcBlockcontents extends BaseObject implements Persistent
$this->block_id = null;
$this->file_id = null;
$this->position = null;
$this->trackoffset = null;
$this->cliplength = null;
$this->cuein = null;
$this->cueout = null;

View File

@ -26,7 +26,7 @@ abstract class BaseCcBlockcontentsPeer {
const TM_CLASS = 'CcBlockcontentsTableMap';
/** The total number of columns. */
const NUM_COLUMNS = 9;
const NUM_COLUMNS = 10;
/** The number of lazy-loaded columns. */
const NUM_LAZY_LOAD_COLUMNS = 0;
@ -43,6 +43,9 @@ abstract class BaseCcBlockcontentsPeer {
/** the column name for the POSITION field */
const POSITION = 'cc_blockcontents.POSITION';
/** the column name for the TRACKOFFSET field */
const TRACKOFFSET = 'cc_blockcontents.TRACKOFFSET';
/** the column name for the CLIPLENGTH field */
const CLIPLENGTH = 'cc_blockcontents.CLIPLENGTH';
@ -74,12 +77,12 @@ abstract class BaseCcBlockcontentsPeer {
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
*/
private static $fieldNames = array (
BasePeer::TYPE_PHPNAME => array ('DbId', 'DbBlockId', 'DbFileId', 'DbPosition', 'DbCliplength', 'DbCuein', 'DbCueout', 'DbFadein', 'DbFadeout', ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbBlockId', 'dbFileId', 'dbPosition', 'dbCliplength', 'dbCuein', 'dbCueout', 'dbFadein', 'dbFadeout', ),
BasePeer::TYPE_COLNAME => array (self::ID, self::BLOCK_ID, self::FILE_ID, self::POSITION, self::CLIPLENGTH, self::CUEIN, self::CUEOUT, self::FADEIN, self::FADEOUT, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID', 'BLOCK_ID', 'FILE_ID', 'POSITION', 'CLIPLENGTH', 'CUEIN', 'CUEOUT', 'FADEIN', 'FADEOUT', ),
BasePeer::TYPE_FIELDNAME => array ('id', 'block_id', 'file_id', 'position', 'cliplength', 'cuein', 'cueout', 'fadein', 'fadeout', ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, )
BasePeer::TYPE_PHPNAME => array ('DbId', 'DbBlockId', 'DbFileId', 'DbPosition', 'DbTrackOffset', 'DbCliplength', 'DbCuein', 'DbCueout', 'DbFadein', 'DbFadeout', ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbBlockId', 'dbFileId', 'dbPosition', 'dbTrackOffset', 'dbCliplength', 'dbCuein', 'dbCueout', 'dbFadein', 'dbFadeout', ),
BasePeer::TYPE_COLNAME => array (self::ID, self::BLOCK_ID, self::FILE_ID, self::POSITION, self::TRACKOFFSET, self::CLIPLENGTH, self::CUEIN, self::CUEOUT, self::FADEIN, self::FADEOUT, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID', 'BLOCK_ID', 'FILE_ID', 'POSITION', 'TRACKOFFSET', 'CLIPLENGTH', 'CUEIN', 'CUEOUT', 'FADEIN', 'FADEOUT', ),
BasePeer::TYPE_FIELDNAME => array ('id', 'block_id', 'file_id', 'position', 'trackoffset', 'cliplength', 'cuein', 'cueout', 'fadein', 'fadeout', ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, )
);
/**
@ -89,12 +92,12 @@ abstract class BaseCcBlockcontentsPeer {
* e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0
*/
private static $fieldKeys = array (
BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbBlockId' => 1, 'DbFileId' => 2, 'DbPosition' => 3, 'DbCliplength' => 4, 'DbCuein' => 5, 'DbCueout' => 6, 'DbFadein' => 7, 'DbFadeout' => 8, ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbBlockId' => 1, 'dbFileId' => 2, 'dbPosition' => 3, 'dbCliplength' => 4, 'dbCuein' => 5, 'dbCueout' => 6, 'dbFadein' => 7, 'dbFadeout' => 8, ),
BasePeer::TYPE_COLNAME => array (self::ID => 0, self::BLOCK_ID => 1, self::FILE_ID => 2, self::POSITION => 3, self::CLIPLENGTH => 4, self::CUEIN => 5, self::CUEOUT => 6, self::FADEIN => 7, self::FADEOUT => 8, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'BLOCK_ID' => 1, 'FILE_ID' => 2, 'POSITION' => 3, 'CLIPLENGTH' => 4, 'CUEIN' => 5, 'CUEOUT' => 6, 'FADEIN' => 7, 'FADEOUT' => 8, ),
BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'block_id' => 1, 'file_id' => 2, 'position' => 3, 'cliplength' => 4, 'cuein' => 5, 'cueout' => 6, 'fadein' => 7, 'fadeout' => 8, ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, )
BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbBlockId' => 1, 'DbFileId' => 2, 'DbPosition' => 3, 'DbTrackOffset' => 4, 'DbCliplength' => 5, 'DbCuein' => 6, 'DbCueout' => 7, 'DbFadein' => 8, 'DbFadeout' => 9, ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbBlockId' => 1, 'dbFileId' => 2, 'dbPosition' => 3, 'dbTrackOffset' => 4, 'dbCliplength' => 5, 'dbCuein' => 6, 'dbCueout' => 7, 'dbFadein' => 8, 'dbFadeout' => 9, ),
BasePeer::TYPE_COLNAME => array (self::ID => 0, self::BLOCK_ID => 1, self::FILE_ID => 2, self::POSITION => 3, self::TRACKOFFSET => 4, self::CLIPLENGTH => 5, self::CUEIN => 6, self::CUEOUT => 7, self::FADEIN => 8, self::FADEOUT => 9, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'BLOCK_ID' => 1, 'FILE_ID' => 2, 'POSITION' => 3, 'TRACKOFFSET' => 4, 'CLIPLENGTH' => 5, 'CUEIN' => 6, 'CUEOUT' => 7, 'FADEIN' => 8, 'FADEOUT' => 9, ),
BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'block_id' => 1, 'file_id' => 2, 'position' => 3, 'trackoffset' => 4, 'cliplength' => 5, 'cuein' => 6, 'cueout' => 7, 'fadein' => 8, 'fadeout' => 9, ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, )
);
/**
@ -170,6 +173,7 @@ abstract class BaseCcBlockcontentsPeer {
$criteria->addSelectColumn(CcBlockcontentsPeer::BLOCK_ID);
$criteria->addSelectColumn(CcBlockcontentsPeer::FILE_ID);
$criteria->addSelectColumn(CcBlockcontentsPeer::POSITION);
$criteria->addSelectColumn(CcBlockcontentsPeer::TRACKOFFSET);
$criteria->addSelectColumn(CcBlockcontentsPeer::CLIPLENGTH);
$criteria->addSelectColumn(CcBlockcontentsPeer::CUEIN);
$criteria->addSelectColumn(CcBlockcontentsPeer::CUEOUT);
@ -180,6 +184,7 @@ abstract class BaseCcBlockcontentsPeer {
$criteria->addSelectColumn($alias . '.BLOCK_ID');
$criteria->addSelectColumn($alias . '.FILE_ID');
$criteria->addSelectColumn($alias . '.POSITION');
$criteria->addSelectColumn($alias . '.TRACKOFFSET');
$criteria->addSelectColumn($alias . '.CLIPLENGTH');
$criteria->addSelectColumn($alias . '.CUEIN');
$criteria->addSelectColumn($alias . '.CUEOUT');

View File

@ -10,6 +10,7 @@
* @method CcBlockcontentsQuery orderByDbBlockId($order = Criteria::ASC) Order by the block_id column
* @method CcBlockcontentsQuery orderByDbFileId($order = Criteria::ASC) Order by the file_id column
* @method CcBlockcontentsQuery orderByDbPosition($order = Criteria::ASC) Order by the position column
* @method CcBlockcontentsQuery orderByDbTrackOffset($order = Criteria::ASC) Order by the trackoffset column
* @method CcBlockcontentsQuery orderByDbCliplength($order = Criteria::ASC) Order by the cliplength column
* @method CcBlockcontentsQuery orderByDbCuein($order = Criteria::ASC) Order by the cuein column
* @method CcBlockcontentsQuery orderByDbCueout($order = Criteria::ASC) Order by the cueout column
@ -20,6 +21,7 @@
* @method CcBlockcontentsQuery groupByDbBlockId() Group by the block_id column
* @method CcBlockcontentsQuery groupByDbFileId() Group by the file_id column
* @method CcBlockcontentsQuery groupByDbPosition() Group by the position column
* @method CcBlockcontentsQuery groupByDbTrackOffset() Group by the trackoffset column
* @method CcBlockcontentsQuery groupByDbCliplength() Group by the cliplength column
* @method CcBlockcontentsQuery groupByDbCuein() Group by the cuein column
* @method CcBlockcontentsQuery groupByDbCueout() Group by the cueout column
@ -45,6 +47,7 @@
* @method CcBlockcontents findOneByDbBlockId(int $block_id) Return the first CcBlockcontents filtered by the block_id column
* @method CcBlockcontents findOneByDbFileId(int $file_id) Return the first CcBlockcontents filtered by the file_id column
* @method CcBlockcontents findOneByDbPosition(int $position) Return the first CcBlockcontents filtered by the position column
* @method CcBlockcontents findOneByDbTrackOffset(double $trackoffset) Return the first CcBlockcontents filtered by the trackoffset column
* @method CcBlockcontents findOneByDbCliplength(string $cliplength) Return the first CcBlockcontents filtered by the cliplength column
* @method CcBlockcontents findOneByDbCuein(string $cuein) Return the first CcBlockcontents filtered by the cuein column
* @method CcBlockcontents findOneByDbCueout(string $cueout) Return the first CcBlockcontents filtered by the cueout column
@ -55,6 +58,7 @@
* @method array findByDbBlockId(int $block_id) Return CcBlockcontents objects filtered by the block_id column
* @method array findByDbFileId(int $file_id) Return CcBlockcontents objects filtered by the file_id column
* @method array findByDbPosition(int $position) Return CcBlockcontents objects filtered by the position column
* @method array findByDbTrackOffset(double $trackoffset) Return CcBlockcontents objects filtered by the trackoffset column
* @method array findByDbCliplength(string $cliplength) Return CcBlockcontents objects filtered by the cliplength column
* @method array findByDbCuein(string $cuein) Return CcBlockcontents objects filtered by the cuein column
* @method array findByDbCueout(string $cueout) Return CcBlockcontents objects filtered by the cueout column
@ -279,6 +283,37 @@ abstract class BaseCcBlockcontentsQuery extends ModelCriteria
return $this->addUsingAlias(CcBlockcontentsPeer::POSITION, $dbPosition, $comparison);
}
/**
* Filter the query on the trackoffset column
*
* @param double|array $dbTrackOffset The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
*
* @return CcBlockcontentsQuery The current query, for fluid interface
*/
public function filterByDbTrackOffset($dbTrackOffset = null, $comparison = null)
{
if (is_array($dbTrackOffset)) {
$useMinMax = false;
if (isset($dbTrackOffset['min'])) {
$this->addUsingAlias(CcBlockcontentsPeer::TRACKOFFSET, $dbTrackOffset['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbTrackOffset['max'])) {
$this->addUsingAlias(CcBlockcontentsPeer::TRACKOFFSET, $dbTrackOffset['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN;
}
}
return $this->addUsingAlias(CcBlockcontentsPeer::TRACKOFFSET, $dbTrackOffset, $comparison);
}
/**
* Filter the query on the cliplength column
*

View File

@ -67,6 +67,13 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
*/
protected $position;
/**
* The value for the trackoffset field.
* Note: this column has a database default value of: 0
* @var double
*/
protected $trackoffset;
/**
* The value for the cliplength field.
* Note: this column has a database default value of: '00:00:00'
@ -143,6 +150,7 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
public function applyDefaultValues()
{
$this->type = 0;
$this->trackoffset = 0;
$this->cliplength = '00:00:00';
$this->cuein = '00:00:00';
$this->cueout = '00:00:00';
@ -230,6 +238,16 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
return $this->position;
}
/**
* Get the [trackoffset] column value.
*
* @return double
*/
public function getDbTrackOffset()
{
return $this->trackoffset;
}
/**
* Get the [cliplength] column value.
*
@ -478,6 +496,26 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
return $this;
} // setDbPosition()
/**
* Set the value of [trackoffset] column.
*
* @param double $v new value
* @return CcPlaylistcontents The current object (for fluent API support)
*/
public function setDbTrackOffset($v)
{
if ($v !== null) {
$v = (double) $v;
}
if ($this->trackoffset !== $v || $this->isNew()) {
$this->trackoffset = $v;
$this->modifiedColumns[] = CcPlaylistcontentsPeer::TRACKOFFSET;
}
return $this;
} // setDbTrackOffset()
/**
* Set the value of [cliplength] column.
*
@ -652,6 +690,10 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
return false;
}
if ($this->trackoffset !== 0) {
return false;
}
if ($this->cliplength !== '00:00:00') {
return false;
}
@ -701,11 +743,12 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
$this->stream_id = ($row[$startcol + 4] !== null) ? (int) $row[$startcol + 4] : null;
$this->type = ($row[$startcol + 5] !== null) ? (int) $row[$startcol + 5] : null;
$this->position = ($row[$startcol + 6] !== null) ? (int) $row[$startcol + 6] : null;
$this->cliplength = ($row[$startcol + 7] !== null) ? (string) $row[$startcol + 7] : null;
$this->cuein = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null;
$this->cueout = ($row[$startcol + 9] !== null) ? (string) $row[$startcol + 9] : null;
$this->fadein = ($row[$startcol + 10] !== null) ? (string) $row[$startcol + 10] : null;
$this->fadeout = ($row[$startcol + 11] !== null) ? (string) $row[$startcol + 11] : null;
$this->trackoffset = ($row[$startcol + 7] !== null) ? (double) $row[$startcol + 7] : null;
$this->cliplength = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null;
$this->cuein = ($row[$startcol + 9] !== null) ? (string) $row[$startcol + 9] : null;
$this->cueout = ($row[$startcol + 10] !== null) ? (string) $row[$startcol + 10] : null;
$this->fadein = ($row[$startcol + 11] !== null) ? (string) $row[$startcol + 11] : null;
$this->fadeout = ($row[$startcol + 12] !== null) ? (string) $row[$startcol + 12] : null;
$this->resetModified();
$this->setNew(false);
@ -714,7 +757,7 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
$this->ensureConsistency();
}
return $startcol + 12; // 12 = CcPlaylistcontentsPeer::NUM_COLUMNS - CcPlaylistcontentsPeer::NUM_LAZY_LOAD_COLUMNS).
return $startcol + 13; // 13 = CcPlaylistcontentsPeer::NUM_COLUMNS - CcPlaylistcontentsPeer::NUM_LAZY_LOAD_COLUMNS).
} catch (Exception $e) {
throw new PropelException("Error populating CcPlaylistcontents object", $e);
@ -1099,18 +1142,21 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
return $this->getDbPosition();
break;
case 7:
return $this->getDbCliplength();
return $this->getDbTrackOffset();
break;
case 8:
return $this->getDbCuein();
return $this->getDbCliplength();
break;
case 9:
return $this->getDbCueout();
return $this->getDbCuein();
break;
case 10:
return $this->getDbFadein();
return $this->getDbCueout();
break;
case 11:
return $this->getDbFadein();
break;
case 12:
return $this->getDbFadeout();
break;
default:
@ -1144,11 +1190,12 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
$keys[4] => $this->getDbStreamId(),
$keys[5] => $this->getDbType(),
$keys[6] => $this->getDbPosition(),
$keys[7] => $this->getDbCliplength(),
$keys[8] => $this->getDbCuein(),
$keys[9] => $this->getDbCueout(),
$keys[10] => $this->getDbFadein(),
$keys[11] => $this->getDbFadeout(),
$keys[7] => $this->getDbTrackOffset(),
$keys[8] => $this->getDbCliplength(),
$keys[9] => $this->getDbCuein(),
$keys[10] => $this->getDbCueout(),
$keys[11] => $this->getDbFadein(),
$keys[12] => $this->getDbFadeout(),
);
if ($includeForeignObjects) {
if (null !== $this->aCcFiles) {
@ -1213,18 +1260,21 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
$this->setDbPosition($value);
break;
case 7:
$this->setDbCliplength($value);
$this->setDbTrackOffset($value);
break;
case 8:
$this->setDbCuein($value);
$this->setDbCliplength($value);
break;
case 9:
$this->setDbCueout($value);
$this->setDbCuein($value);
break;
case 10:
$this->setDbFadein($value);
$this->setDbCueout($value);
break;
case 11:
$this->setDbFadein($value);
break;
case 12:
$this->setDbFadeout($value);
break;
} // switch()
@ -1258,11 +1308,12 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
if (array_key_exists($keys[4], $arr)) $this->setDbStreamId($arr[$keys[4]]);
if (array_key_exists($keys[5], $arr)) $this->setDbType($arr[$keys[5]]);
if (array_key_exists($keys[6], $arr)) $this->setDbPosition($arr[$keys[6]]);
if (array_key_exists($keys[7], $arr)) $this->setDbCliplength($arr[$keys[7]]);
if (array_key_exists($keys[8], $arr)) $this->setDbCuein($arr[$keys[8]]);
if (array_key_exists($keys[9], $arr)) $this->setDbCueout($arr[$keys[9]]);
if (array_key_exists($keys[10], $arr)) $this->setDbFadein($arr[$keys[10]]);
if (array_key_exists($keys[11], $arr)) $this->setDbFadeout($arr[$keys[11]]);
if (array_key_exists($keys[7], $arr)) $this->setDbTrackOffset($arr[$keys[7]]);
if (array_key_exists($keys[8], $arr)) $this->setDbCliplength($arr[$keys[8]]);
if (array_key_exists($keys[9], $arr)) $this->setDbCuein($arr[$keys[9]]);
if (array_key_exists($keys[10], $arr)) $this->setDbCueout($arr[$keys[10]]);
if (array_key_exists($keys[11], $arr)) $this->setDbFadein($arr[$keys[11]]);
if (array_key_exists($keys[12], $arr)) $this->setDbFadeout($arr[$keys[12]]);
}
/**
@ -1281,6 +1332,7 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
if ($this->isColumnModified(CcPlaylistcontentsPeer::STREAM_ID)) $criteria->add(CcPlaylistcontentsPeer::STREAM_ID, $this->stream_id);
if ($this->isColumnModified(CcPlaylistcontentsPeer::TYPE)) $criteria->add(CcPlaylistcontentsPeer::TYPE, $this->type);
if ($this->isColumnModified(CcPlaylistcontentsPeer::POSITION)) $criteria->add(CcPlaylistcontentsPeer::POSITION, $this->position);
if ($this->isColumnModified(CcPlaylistcontentsPeer::TRACKOFFSET)) $criteria->add(CcPlaylistcontentsPeer::TRACKOFFSET, $this->trackoffset);
if ($this->isColumnModified(CcPlaylistcontentsPeer::CLIPLENGTH)) $criteria->add(CcPlaylistcontentsPeer::CLIPLENGTH, $this->cliplength);
if ($this->isColumnModified(CcPlaylistcontentsPeer::CUEIN)) $criteria->add(CcPlaylistcontentsPeer::CUEIN, $this->cuein);
if ($this->isColumnModified(CcPlaylistcontentsPeer::CUEOUT)) $criteria->add(CcPlaylistcontentsPeer::CUEOUT, $this->cueout);
@ -1353,6 +1405,7 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
$copyObj->setDbStreamId($this->stream_id);
$copyObj->setDbType($this->type);
$copyObj->setDbPosition($this->position);
$copyObj->setDbTrackOffset($this->trackoffset);
$copyObj->setDbCliplength($this->cliplength);
$copyObj->setDbCuein($this->cuein);
$copyObj->setDbCueout($this->cueout);
@ -1564,6 +1617,7 @@ abstract class BaseCcPlaylistcontents extends BaseObject implements Persistent
$this->stream_id = null;
$this->type = null;
$this->position = null;
$this->trackoffset = null;
$this->cliplength = null;
$this->cuein = null;
$this->cueout = null;

View File

@ -26,7 +26,7 @@ abstract class BaseCcPlaylistcontentsPeer {
const TM_CLASS = 'CcPlaylistcontentsTableMap';
/** The total number of columns. */
const NUM_COLUMNS = 12;
const NUM_COLUMNS = 13;
/** The number of lazy-loaded columns. */
const NUM_LAZY_LOAD_COLUMNS = 0;
@ -52,6 +52,9 @@ abstract class BaseCcPlaylistcontentsPeer {
/** the column name for the POSITION field */
const POSITION = 'cc_playlistcontents.POSITION';
/** the column name for the TRACKOFFSET field */
const TRACKOFFSET = 'cc_playlistcontents.TRACKOFFSET';
/** the column name for the CLIPLENGTH field */
const CLIPLENGTH = 'cc_playlistcontents.CLIPLENGTH';
@ -83,12 +86,12 @@ abstract class BaseCcPlaylistcontentsPeer {
* e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
*/
private static $fieldNames = array (
BasePeer::TYPE_PHPNAME => array ('DbId', 'DbPlaylistId', 'DbFileId', 'DbBlockId', 'DbStreamId', 'DbType', 'DbPosition', 'DbCliplength', 'DbCuein', 'DbCueout', 'DbFadein', 'DbFadeout', ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbPlaylistId', 'dbFileId', 'dbBlockId', 'dbStreamId', 'dbType', 'dbPosition', 'dbCliplength', 'dbCuein', 'dbCueout', 'dbFadein', 'dbFadeout', ),
BasePeer::TYPE_COLNAME => array (self::ID, self::PLAYLIST_ID, self::FILE_ID, self::BLOCK_ID, self::STREAM_ID, self::TYPE, self::POSITION, self::CLIPLENGTH, self::CUEIN, self::CUEOUT, self::FADEIN, self::FADEOUT, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID', 'PLAYLIST_ID', 'FILE_ID', 'BLOCK_ID', 'STREAM_ID', 'TYPE', 'POSITION', 'CLIPLENGTH', 'CUEIN', 'CUEOUT', 'FADEIN', 'FADEOUT', ),
BasePeer::TYPE_FIELDNAME => array ('id', 'playlist_id', 'file_id', 'block_id', 'stream_id', 'type', 'position', 'cliplength', 'cuein', 'cueout', 'fadein', 'fadeout', ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, )
BasePeer::TYPE_PHPNAME => array ('DbId', 'DbPlaylistId', 'DbFileId', 'DbBlockId', 'DbStreamId', 'DbType', 'DbPosition', 'DbTrackOffset', 'DbCliplength', 'DbCuein', 'DbCueout', 'DbFadein', 'DbFadeout', ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbPlaylistId', 'dbFileId', 'dbBlockId', 'dbStreamId', 'dbType', 'dbPosition', 'dbTrackOffset', 'dbCliplength', 'dbCuein', 'dbCueout', 'dbFadein', 'dbFadeout', ),
BasePeer::TYPE_COLNAME => array (self::ID, self::PLAYLIST_ID, self::FILE_ID, self::BLOCK_ID, self::STREAM_ID, self::TYPE, self::POSITION, self::TRACKOFFSET, self::CLIPLENGTH, self::CUEIN, self::CUEOUT, self::FADEIN, self::FADEOUT, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID', 'PLAYLIST_ID', 'FILE_ID', 'BLOCK_ID', 'STREAM_ID', 'TYPE', 'POSITION', 'TRACKOFFSET', 'CLIPLENGTH', 'CUEIN', 'CUEOUT', 'FADEIN', 'FADEOUT', ),
BasePeer::TYPE_FIELDNAME => array ('id', 'playlist_id', 'file_id', 'block_id', 'stream_id', 'type', 'position', 'trackoffset', 'cliplength', 'cuein', 'cueout', 'fadein', 'fadeout', ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, )
);
/**
@ -98,12 +101,12 @@ abstract class BaseCcPlaylistcontentsPeer {
* e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0
*/
private static $fieldKeys = array (
BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbPlaylistId' => 1, 'DbFileId' => 2, 'DbBlockId' => 3, 'DbStreamId' => 4, 'DbType' => 5, 'DbPosition' => 6, 'DbCliplength' => 7, 'DbCuein' => 8, 'DbCueout' => 9, 'DbFadein' => 10, 'DbFadeout' => 11, ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbPlaylistId' => 1, 'dbFileId' => 2, 'dbBlockId' => 3, 'dbStreamId' => 4, 'dbType' => 5, 'dbPosition' => 6, 'dbCliplength' => 7, 'dbCuein' => 8, 'dbCueout' => 9, 'dbFadein' => 10, 'dbFadeout' => 11, ),
BasePeer::TYPE_COLNAME => array (self::ID => 0, self::PLAYLIST_ID => 1, self::FILE_ID => 2, self::BLOCK_ID => 3, self::STREAM_ID => 4, self::TYPE => 5, self::POSITION => 6, self::CLIPLENGTH => 7, self::CUEIN => 8, self::CUEOUT => 9, self::FADEIN => 10, self::FADEOUT => 11, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'PLAYLIST_ID' => 1, 'FILE_ID' => 2, 'BLOCK_ID' => 3, 'STREAM_ID' => 4, 'TYPE' => 5, 'POSITION' => 6, 'CLIPLENGTH' => 7, 'CUEIN' => 8, 'CUEOUT' => 9, 'FADEIN' => 10, 'FADEOUT' => 11, ),
BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'playlist_id' => 1, 'file_id' => 2, 'block_id' => 3, 'stream_id' => 4, 'type' => 5, 'position' => 6, 'cliplength' => 7, 'cuein' => 8, 'cueout' => 9, 'fadein' => 10, 'fadeout' => 11, ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, )
BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbPlaylistId' => 1, 'DbFileId' => 2, 'DbBlockId' => 3, 'DbStreamId' => 4, 'DbType' => 5, 'DbPosition' => 6, 'DbTrackOffset' => 7, 'DbCliplength' => 8, 'DbCuein' => 9, 'DbCueout' => 10, 'DbFadein' => 11, 'DbFadeout' => 12, ),
BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbPlaylistId' => 1, 'dbFileId' => 2, 'dbBlockId' => 3, 'dbStreamId' => 4, 'dbType' => 5, 'dbPosition' => 6, 'dbTrackOffset' => 7, 'dbCliplength' => 8, 'dbCuein' => 9, 'dbCueout' => 10, 'dbFadein' => 11, 'dbFadeout' => 12, ),
BasePeer::TYPE_COLNAME => array (self::ID => 0, self::PLAYLIST_ID => 1, self::FILE_ID => 2, self::BLOCK_ID => 3, self::STREAM_ID => 4, self::TYPE => 5, self::POSITION => 6, self::TRACKOFFSET => 7, self::CLIPLENGTH => 8, self::CUEIN => 9, self::CUEOUT => 10, self::FADEIN => 11, self::FADEOUT => 12, ),
BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'PLAYLIST_ID' => 1, 'FILE_ID' => 2, 'BLOCK_ID' => 3, 'STREAM_ID' => 4, 'TYPE' => 5, 'POSITION' => 6, 'TRACKOFFSET' => 7, 'CLIPLENGTH' => 8, 'CUEIN' => 9, 'CUEOUT' => 10, 'FADEIN' => 11, 'FADEOUT' => 12, ),
BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'playlist_id' => 1, 'file_id' => 2, 'block_id' => 3, 'stream_id' => 4, 'type' => 5, 'position' => 6, 'trackoffset' => 7, 'cliplength' => 8, 'cuein' => 9, 'cueout' => 10, 'fadein' => 11, 'fadeout' => 12, ),
BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, )
);
/**
@ -182,6 +185,7 @@ abstract class BaseCcPlaylistcontentsPeer {
$criteria->addSelectColumn(CcPlaylistcontentsPeer::STREAM_ID);
$criteria->addSelectColumn(CcPlaylistcontentsPeer::TYPE);
$criteria->addSelectColumn(CcPlaylistcontentsPeer::POSITION);
$criteria->addSelectColumn(CcPlaylistcontentsPeer::TRACKOFFSET);
$criteria->addSelectColumn(CcPlaylistcontentsPeer::CLIPLENGTH);
$criteria->addSelectColumn(CcPlaylistcontentsPeer::CUEIN);
$criteria->addSelectColumn(CcPlaylistcontentsPeer::CUEOUT);
@ -195,6 +199,7 @@ abstract class BaseCcPlaylistcontentsPeer {
$criteria->addSelectColumn($alias . '.STREAM_ID');
$criteria->addSelectColumn($alias . '.TYPE');
$criteria->addSelectColumn($alias . '.POSITION');
$criteria->addSelectColumn($alias . '.TRACKOFFSET');
$criteria->addSelectColumn($alias . '.CLIPLENGTH');
$criteria->addSelectColumn($alias . '.CUEIN');
$criteria->addSelectColumn($alias . '.CUEOUT');

View File

@ -13,6 +13,7 @@
* @method CcPlaylistcontentsQuery orderByDbStreamId($order = Criteria::ASC) Order by the stream_id column
* @method CcPlaylistcontentsQuery orderByDbType($order = Criteria::ASC) Order by the type column
* @method CcPlaylistcontentsQuery orderByDbPosition($order = Criteria::ASC) Order by the position column
* @method CcPlaylistcontentsQuery orderByDbTrackOffset($order = Criteria::ASC) Order by the trackoffset column
* @method CcPlaylistcontentsQuery orderByDbCliplength($order = Criteria::ASC) Order by the cliplength column
* @method CcPlaylistcontentsQuery orderByDbCuein($order = Criteria::ASC) Order by the cuein column
* @method CcPlaylistcontentsQuery orderByDbCueout($order = Criteria::ASC) Order by the cueout column
@ -26,6 +27,7 @@
* @method CcPlaylistcontentsQuery groupByDbStreamId() Group by the stream_id column
* @method CcPlaylistcontentsQuery groupByDbType() Group by the type column
* @method CcPlaylistcontentsQuery groupByDbPosition() Group by the position column
* @method CcPlaylistcontentsQuery groupByDbTrackOffset() Group by the trackoffset column
* @method CcPlaylistcontentsQuery groupByDbCliplength() Group by the cliplength column
* @method CcPlaylistcontentsQuery groupByDbCuein() Group by the cuein column
* @method CcPlaylistcontentsQuery groupByDbCueout() Group by the cueout column
@ -58,6 +60,7 @@
* @method CcPlaylistcontents findOneByDbStreamId(int $stream_id) Return the first CcPlaylistcontents filtered by the stream_id column
* @method CcPlaylistcontents findOneByDbType(int $type) Return the first CcPlaylistcontents filtered by the type column
* @method CcPlaylistcontents findOneByDbPosition(int $position) Return the first CcPlaylistcontents filtered by the position column
* @method CcPlaylistcontents findOneByDbTrackOffset(double $trackoffset) Return the first CcPlaylistcontents filtered by the trackoffset column
* @method CcPlaylistcontents findOneByDbCliplength(string $cliplength) Return the first CcPlaylistcontents filtered by the cliplength column
* @method CcPlaylistcontents findOneByDbCuein(string $cuein) Return the first CcPlaylistcontents filtered by the cuein column
* @method CcPlaylistcontents findOneByDbCueout(string $cueout) Return the first CcPlaylistcontents filtered by the cueout column
@ -71,6 +74,7 @@
* @method array findByDbStreamId(int $stream_id) Return CcPlaylistcontents objects filtered by the stream_id column
* @method array findByDbType(int $type) Return CcPlaylistcontents objects filtered by the type column
* @method array findByDbPosition(int $position) Return CcPlaylistcontents objects filtered by the position column
* @method array findByDbTrackOffset(double $trackoffset) Return CcPlaylistcontents objects filtered by the trackoffset column
* @method array findByDbCliplength(string $cliplength) Return CcPlaylistcontents objects filtered by the cliplength column
* @method array findByDbCuein(string $cuein) Return CcPlaylistcontents objects filtered by the cuein column
* @method array findByDbCueout(string $cueout) Return CcPlaylistcontents objects filtered by the cueout column
@ -388,6 +392,37 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
return $this->addUsingAlias(CcPlaylistcontentsPeer::POSITION, $dbPosition, $comparison);
}
/**
* Filter the query on the trackoffset column
*
* @param double|array $dbTrackOffset The value to use as filter.
* Accepts an associative array('min' => $minValue, 'max' => $maxValue)
* @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
*
* @return CcPlaylistcontentsQuery The current query, for fluid interface
*/
public function filterByDbTrackOffset($dbTrackOffset = null, $comparison = null)
{
if (is_array($dbTrackOffset)) {
$useMinMax = false;
if (isset($dbTrackOffset['min'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::TRACKOFFSET, $dbTrackOffset['min'], Criteria::GREATER_EQUAL);
$useMinMax = true;
}
if (isset($dbTrackOffset['max'])) {
$this->addUsingAlias(CcPlaylistcontentsPeer::TRACKOFFSET, $dbTrackOffset['max'], Criteria::LESS_EQUAL);
$useMinMax = true;
}
if ($useMinMax) {
return $this;
}
if (null === $comparison) {
$comparison = Criteria::IN;
}
}
return $this->addUsingAlias(CcPlaylistcontentsPeer::TRACKOFFSET, $dbTrackOffset, $comparison);
}
/**
* Filter the query on the cliplength column
*

View File

@ -14,14 +14,40 @@
</ul>
<?php endif; ?>
</dd>
<dt id="stationDefaultFade-label" class="block-display">
<label class="optional" for="stationDefaultFade"><?php echo $this->element->getElement('stationDefaultFade')->getLabel() ?></label>
<dt id="stationDefaultFadeIn-label" class="block-display">
<label class="optional" for="stationDefaultFadeIn"><?php echo $this->element->getElement('stationDefaultFadeIn')->getLabel() ?></label>
</dt>
<dd id="stationDefaultFade-element" class="block-display">
<?php echo $this->element->getElement('stationDefaultFade') ?>
<?php if($this->element->getElement('stationDefaultFade')->hasErrors()) : ?>
<dd id="stationDefaultFadeIn-element" class="block-display">
<?php echo $this->element->getElement('stationDefaultFadeIn') ?>
<?php if($this->element->getElement('stationDefaultFadeIn')->hasErrors()) : ?>
<ul class='errors'>
<?php foreach($this->element->getElement('stationDefaultFade')->getMessages() as $error): ?>
<?php foreach($this->element->getElement('stationDefaultFadeIn')->getMessages() as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</dd>
<dt id="stationDefaultFadeOut-label" class="block-display">
<label class="optional" for="stationDefaultFadeOut"><?php echo $this->element->getElement('stationDefaultFadeOut')->getLabel() ?></label>
</dt>
<dd id="stationDefaultFadeOut-element" class="block-display">
<?php echo $this->element->getElement('stationDefaultFadeOut') ?>
<?php if($this->element->getElement('stationDefaultFadeOut')->hasErrors()) : ?>
<ul class='errors'>
<?php foreach($this->element->getElement('stationDefaultFadeOut')->getMessages() as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</dd>
<dt id="stationDefaultCrossfadeDuration-label" class="block-display">
<label class="optional" for="stationDefaultCrossfadeDuration"><?php echo $this->element->getElement('stationDefaultCrossfadeDuration')->getLabel() ?></label>
</dt>
<dd id="stationDefaultCrossfadeDuration-element" class="block-display">
<?php echo $this->element->getElement('stationDefaultCrossfadeDuration') ?>
<?php if($this->element->getElement('stationDefaultCrossfadeDuration')->hasErrors()) : ?>
<ul class='errors'>
<?php foreach($this->element->getElement('stationDefaultCrossfadeDuration')->getMessages() as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>

View File

@ -1,11 +1,14 @@
<dl id="spl_cue_editor" class="inline-list">
<dd data-uri="<?php echo $this->uri; ?>">
<input type="button" class="pl-waveform-cues-btn" value="Show Waveform"></input>
</dd>
<dt><? echo _("Cue In: "); ?><span class='spl_cue_hint'><? echo _("(hh:mm:ss.t)")?></span></dt>
<dd id="spl_cue_in_<?php echo $this->id; ?>" class="spl_cue_in">
<dd id="spl_cue_in_<?php echo $this->id; ?>" class="spl_cue_in" data-cue-in="<?php echo $this->cueIn; ?>">
<span contenteditable="true" class="spl_text_input"><?php echo $this->cueIn; ?></span>
</dd>
<dd class="edit-error"></dd>
<dt><? echo _("Cue Out: "); ?><span class='spl_cue_hint'><? echo _("(hh:mm:ss.t)")?></span></dt>
<dd id="spl_cue_out_<?php echo $this->id; ?>" class="spl_cue_out">
<dd id="spl_cue_out_<?php echo $this->id; ?>" class="spl_cue_out" data-cue-out="<?php echo $this->cueOut; ?>">
<span contenteditable="true" class="spl_text_input"><?php echo $this->cueOut; ?></span>
</dd>
<dd class="edit-error"></dd>

View File

@ -1,16 +1,23 @@
<dl id="spl_editor" class="inline-list">
<dd>
<input type="button" class="pl-waveform-fades-btn" value="Show Waveform"></input>
</dd>
<?php if ($this->item1Type == 0) {?>
<dt><? echo _("Fade out: "); ?><span class='spl_cue_hint'><? echo _("(ss.t)")?></span></dt>
<dd id="spl_fade_out_<?php echo $this->item1; ?>" class="spl_fade_out">
<dd id="spl_fade_out_<?php echo $this->item1; ?>" class="spl_fade_out" data-fadeout="<?php echo $this->item1Url; ?>"
data-cuein="<?php echo $this->cueIn1; ?>" data-cueout="<?php echo $this->cueOut1; ?>" data-length="<?php echo $this->fadeOut; ?>"
data-type="logarithmic" data-item="<?php echo $this->item1; ?>">
<span contenteditable="true" class="spl_text_input"><?php echo $this->fadeOut; ?></span>
</dd>
<dd class="edit-error"></dd>
<?php }
if ($this->item2Type == 0) {?>
<dt><? echo _("Fade in: "); ?><span class='spl_cue_hint'><? echo _("(ss.t)")?></span></dt>
<dd id="spl_fade_in_<?php echo $this->item2; ?>" class="spl_fade_in">
<dd id="spl_fade_in_<?php echo $this->item2; ?>" class="spl_fade_in" data-fadein="<?php echo $this->item2Url; ?>" data-offset="<?php if ($this->item1Type == 0) { echo $this->offset; } else { echo 0; } ?>"
data-cuein="<?php echo $this->cueIn2; ?>" data-cueout="<?php echo $this->cueOut2; ?>" data-length="<?php echo $this->fadeIn; ?>"
data-type="logarithmic" data-item="<?php echo $this->item2; ?>">
<span contenteditable="true" class="spl_text_input"><?php echo $this->fadeIn; ?></span>
</dd>
<dd class="edit-error"></dd>
<dd class="edit-error"></dd>
<?php }?>
</dl>

View File

@ -8,6 +8,15 @@ if ($item['type'] == 2) {
$bl= new Application_Model_Block($item['item_id']);
$staticBlock = $bl->isStatic();
}
else if ($item['type'] == 0) {
$audiofile = Application_Model_StoredFile::RecallById($item['item_id']);
$fileUrl = $audiofile->getFileUrl();
}
if (($i < count($items) -1) && ($items[$i+1]['type'] == 0)) {
$nextAudiofile = Application_Model_StoredFile::RecallById($items[$i+1]['item_id']);
$nextFileUrl = $nextAudiofile->getFileUrl();
}
?>
<li class="ui-state-default" id="spl_<?php echo $item["id"] ?>" unqid="<?php echo $item["id"]; ?>">
<div class="list-item-container">
@ -65,6 +74,7 @@ if ($item['type'] == 2) {
'id' => $item["id"],
'cueIn' => $item['cuein'],
'cueOut' => $item['cueout'],
'uri' => $fileUrl,
'origLength' => $item['orig_length'])); ?>
</div>
<?php }?>
@ -80,8 +90,16 @@ if ($item['type'] == 2) {
'item2' => $items[$i+1]['id'],
'item1Type' => $items[$i]['type'],
'item2Type' => $items[$i+1]['type'],
'item1Url' => $fileUrl,
'item2Url' => $nextFileUrl,
'fadeOut' => $items[$i]['fadeout'],
'fadeIn' => $items[$i+1]['fadein'])); ?>
'fadeIn' => $items[$i+1]['fadein'],
'offset' => $items[$i]['trackSec'] - $items[$i+1]['trackoffset'],
'cueIn1' => $items[$i]['cueInSec'],
'cueOut1' => $items[$i]['cueOutSec'],
'cueIn2' => $items[$i+1]['cueInSec'],
'cueOut2' => $items[$i+1]['cueOutSec'])
); ?>
</div>
<?php endif; ?>
<?php if ($item['type'] == 2) {?>

View File

@ -231,6 +231,7 @@
-->
<column name="type" phpName="DbType" type="SMALLINT" required="true" default="0"/>
<column name="position" phpName="DbPosition" type="INTEGER" required="false"/>
<column name="trackoffset" phpName="DbTrackOffset" type="REAL" required="true" default="0"/>
<column name="cliplength" phpName="DbCliplength" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="cuein" phpName="DbCuein" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="cueout" phpName="DbCueout" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
@ -269,6 +270,7 @@
<column name="block_id" phpName="DbBlockId" type="INTEGER" required="false"/>
<column name="file_id" phpName="DbFileId" type="INTEGER" required="false"/>
<column name="position" phpName="DbPosition" type="INTEGER" required="false"/>
<column name="trackoffset" phpName="DbTrackOffset" type="REAL" required="true" default="0"/>
<column name="cliplength" phpName="DbCliplength" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="cuein" phpName="DbCuein" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
<column name="cueout" phpName="DbCueout" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>

View File

@ -298,6 +298,7 @@ CREATE TABLE "cc_playlistcontents"
"stream_id" INTEGER,
"type" INT2 default 0 NOT NULL,
"position" INTEGER,
"trackoffset" FLOAT default 0 NOT NULL,
"cliplength" interval default '00:00:00',
"cuein" interval default '00:00:00',
"cueout" interval default '00:00:00',
@ -347,6 +348,7 @@ CREATE TABLE "cc_blockcontents"
"block_id" INTEGER,
"file_id" INTEGER,
"position" INTEGER,
"trackoffset" FLOAT default 0 NOT NULL,
"cliplength" interval default '00:00:00',
"cuein" interval default '00:00:00',
"cueout" interval default '00:00:00',

View File

@ -28,7 +28,7 @@
height: 28px;
margin: 0 7px 20px 0;
}*/
#side_playlist input,#side_playlist textarea {
#side_playlist textarea {
width: 200px;
}
@ -585,3 +585,28 @@ li.spl_empty {
.expand-block-separate {
border-top: 1px solid #5B5B5B;
}
.channel-wrapper {
position: relative;
}
.channel {
position: absolute;
margin: 0;
padding: 0;
}
.state-select {
cursor: text;
}
.playlist-tracks {
overflow-x: auto;
overflow-y: hidden;
}
.playlist-fade {
position: absolute;
background-color: rgba(0,0,0,0.1);
z-index: 1000;
}

View File

@ -137,6 +137,84 @@ var AIRTIME = (function(AIRTIME){
highlightActive(li.find('.spl_cue'));
});
}
/* used from waveform pop-up */
function changeCues($el, id, cueIn, cueOut) {
var url = baseUrl+"Playlist/set-cue",
lastMod = getModified(),
type = $('#obj_type').val(),
li;
if (!isTimeValid(cueIn)){
$el.find('.cue-in-error').val($.i18n._("please put in a time '00:00:00 (.0)'")).show();
return;
}
else {
$el.find('.cue-in-error').hide();
}
if (!isTimeValid(cueOut)){
$el.find('.cue-out-error').val($.i18n._("please put in a time '00:00:00 (.0)'")).show();
return;
}
else {
$el.find('.cue-out-error').hide();
}
$.post(url,
{format: "json", cueIn: cueIn, cueOut: cueOut, id: id, modified: lastMod, type: type},
function(json){
$el.dialog('destroy');
$el.remove();
if (json.error !== undefined){
playlistError(json);
return;
}
if (json.cue_error !== undefined) {
showError(span, json.cue_error);
return;
}
setPlaylistContent(json);
li = $('#side_playlist li[unqid='+id+']');
li.find(".cue-edit").toggle();
highlightActive(li);
highlightActive(li.find('.spl_cue'));
});
}
/* used from waveform pop-up */
function changeCrossfade($el, id1, id2, fadeIn, fadeOut, offset) {
var url = baseUrl+"Playlist/set-crossfade",
lastMod = getModified(),
type = $('#obj_type').val(),
li, id;
$.post(url,
{format: "json", fadeIn: fadeIn, fadeOut: fadeOut, id1: id1, id2: id2, offset: offset, modified: lastMod, type: type},
function(json){
$el.dialog('destroy');
$el.remove();
if (json.error !== undefined){
playlistError(json);
return;
}
setPlaylistContent(json);
id = id1 === undefined ? id2 : id1;
li = $('#side_playlist li[unqid='+id+']');
li.find('.crossfade').toggle();
highlightActive(li.find('.spl_fade_control'));
});
}
function changeFadeIn(event) {
event.preventDefault();
@ -292,6 +370,8 @@ var AIRTIME = (function(AIRTIME){
}
function setPlaylistContent(json) {
var $html = $(json.html);
$('#spl_name > a')
.empty()
.append(json.name);
@ -305,7 +385,7 @@ var AIRTIME = (function(AIRTIME){
$('#spl_sortable').off('focusout keydown');
$('#spl_sortable')
.empty()
.append(json.html);
.append($html);
setCueEvents();
setFadeEvents();
setModified(json.modified);
@ -515,6 +595,11 @@ var AIRTIME = (function(AIRTIME){
temp.on("focusout", ".spl_cue_out span", changeCueOut);
temp.on("keydown", ".spl_cue_out span", submitOnEnter);
//remove show waveform buttons since web audio api is not supported.
if (!(window.AudioContext || window.webkitAudioContext)) {
temp.find('.pl-waveform-cues-btn').parent().remove();
}
}
//sets events dynamically for the fade editor.
@ -525,6 +610,11 @@ var AIRTIME = (function(AIRTIME){
temp.on("focusout", ".spl_fade_out span", changeFadeOut);
temp.on("keydown", ".spl_fade_out span", submitOnEnter);
//remove show waveform buttons since web audio api is not supported.
if (!(window.AudioContext || window.webkitAudioContext)) {
temp.find('.pl-waveform-fades-btn').parent().remove();
}
}
function initialEvents() {
@ -1061,6 +1151,206 @@ var AIRTIME = (function(AIRTIME){
playlistRequest(sUrl, oData);
};
mod.showFadesWaveform = function(e) {
var $el = $(e.target),
$parent = $el.parents("dl"),
$fadeOut = $parent.find(".spl_fade_out"),
$fadeIn = $parent.find(".spl_fade_in"),
$html = $($("#tmpl-pl-fades").html()),
tracks = [],
dim = AIRTIME.utilities.findViewportDimensions(),
playlistEditor,
id1, id2;
function removeDialog() {
playlistEditor.stop();
$html.dialog("destroy");
$html.remove();
}
if ($fadeOut.length > 0) {
tracks.push({
src: $fadeOut.data("fadeout"),
cuein: $fadeOut.data("cuein"),
cueout: $fadeOut.data("cueout"),
fades: [{
shape: $fadeOut.data("type"),
type: "FadeOut",
end: $fadeOut.data("cueout") - $fadeOut.data("cuein"),
start: $fadeOut.data("cueout") - $fadeOut.data("cuein") - $fadeOut.data("length")
}],
states: {
'fadein': false,
'shift': false
}
});
id1 = $fadeOut.data("item");
}
if ($fadeIn.length > 0) {
tracks.push({
src: $fadeIn.data("fadein"),
start: $fadeIn.data("offset"),
cuein: $fadeIn.data("cuein"),
cueout: $fadeIn.data("cueout"),
fades: [{
shape: $fadeIn.data("type"),
type: "FadeIn",
end: $fadeIn.data("length"),
start: 0
}],
states: {
'fadeout': false,
'shift': false
}
});
id2 = $fadeIn.data("item");
}
//set the first track to not be moveable (might only be one track depending on what follows)
//tracks[0].states["shift"] = false;
$html.dialog({
modal: true,
title: "Fade Editor",
show: 'clip',
hide: 'clip',
width: dim.width - 100,
height: dim.height - 100,
buttons: [
{text: "Cancel", click: removeDialog},
{text: "Save", click: function() {
var json = playlistEditor.getJson(),
offset,
fadeIn, fadeOut,
fade;
playlistEditor.stop();
if (json.length === 1) {
fade = json[0]["fades"][0];
if (fade["type"] === "FadeOut") {
fadeOut = fade["end"] - fade["start"];
}
else {
fadeIn = fade["end"] - fade["start"];
}
}
else {
offset = json[0]["end"] - json[1]["start"];
fade = json[0]["fades"][0];
fadeOut = fade["end"] - fade["start"];
fade = json[1]["fades"][0];
fadeIn = fade["end"] - fade["start"];
}
changeCrossfade($html, id1, id2, fadeIn.toFixed(1), fadeOut.toFixed(1), offset);
}}
],
open: function (event, ui) {
var config = new Config({
resolution: 15000,
state: "shift",
mono: true,
waveHeight: 80,
container: $html[0],
UITheme: "jQueryUI",
timeFormat: 'hh:mm:ss.u'
});
playlistEditor = new PlaylistEditor();
playlistEditor.setConfig(config);
playlistEditor.init(tracks);
},
close: removeDialog
});
};
mod.showCuesWaveform = function(e) {
var $el = $(e.target),
$li = $el.parents("li"),
id = $li.attr("unqid"),
$parent = $el.parent(),
uri = $parent.data("uri"),
$html = $($("#tmpl-pl-cues").html()),
tracks = [{
src: uri
}],
cueIn = $li.find('.spl_cue_in').data("cueIn"),
cueOut = $li.find('.spl_cue_out').data("cueOut"),
dim = AIRTIME.utilities.findViewportDimensions(),
playlistEditor;
function removeDialog() {
playlistEditor.stop();
$html.dialog("destroy");
$html.remove();
}
$html.find('.editor-cue-in').val(cueIn);
$html.find('.editor-cue-out').val(cueOut);
$html.on("click", ".set-cue-in", function(e) {
var cueIn = $html.find('.audio_start').val();
$html.find('.editor-cue-in').val(cueIn);
});
$html.on("click", ".set-cue-out", function(e) {
var cueOut = $html.find('.audio_end').val();
$html.find('.editor-cue-out').val(cueOut);
});
$html.dialog({
modal: true,
title: "Cue Editor",
show: 'clip',
hide: 'clip',
width: dim.width - 100,
height: dim.height - 100,
buttons: [
{text: "Cancel", click: removeDialog},
{text: "Save", click: function() {
var cueIn = $html.find('.editor-cue-in').val(),
cueOut = $html.find('.editor-cue-out').val();
playlistEditor.stop();
changeCues($html, id, cueIn, cueOut);
}}
],
open: function (event, ui) {
var config = new Config({
resolution: 15000,
mono: true,
waveHeight: 80,
container: $html[0],
UITheme: "jQueryUI",
timeFormat: 'hh:mm:ss.u'
});
playlistEditor = new PlaylistEditor();
playlistEditor.setConfig(config);
playlistEditor.init(tracks);
},
close: removeDialog
});
};
mod.init = function() {
/*
$.contextMenu({
@ -1089,6 +1379,14 @@ var AIRTIME = (function(AIRTIME){
AIRTIME.playlist.fnWsDelete();
}});
$pl.delegate(".pl-waveform-cues-btn", {"click": function(ev){
AIRTIME.playlist.showCuesWaveform(ev);
}});
$pl.delegate(".pl-waveform-fades-btn", {"click": function(ev){
AIRTIME.playlist.showFadesWaveform(ev);
}});
setPlaylistEntryEvents();
setCueEvents();
setFadeEvents();

View File

@ -0,0 +1,171 @@
/*
Stores configuration settings for the playlist builder.
A container object (ex a div) must be passed in, the playlist will be built on this element.
*/
var Config = function(params) {
var that = this,
defaultParams;
defaultParams = {
ac: new (window.AudioContext || window.webkitAudioContext),
resolution: 4096, //resolution - samples per pixel to draw.
timeFormat: 'hh:mm:ss.uuu',
mono: true, //whether to draw multiple channels or combine them.
fadeType: 'logarithmic',
timescale: false, //whether or not to include the time measure.
UITheme: "default", // bootstrap || jQueryUI || default
waveColor: 'grey',
progressColor: 'orange',
loadingColor: 'purple',
cursorColor: 'green',
markerColor: 'green',
selectBorderColor: 'red',
selectBackgroundColor: 'rgba(0,0,0,0.1)',
timeColor: 'grey',
fontColor: 'black',
fadeColor: 'black',
waveHeight: 128, //height of each canvas element a waveform is on.
trackscroll: {
left: 0,
top: 0
},
state: 'select',
cursorPos: 0 //value is kept in seconds.
};
params = Object.create(params);
Object.keys(defaultParams).forEach(function(key) {
if (!(key in params)) {
params[key] = defaultParams[key];
}
});
/*
Start of all getter methods for config.
*/
that.getContainer = function getContainer() {
return params.container;
};
that.isTimeScaleEnabled = function isTimeScaleEnabled() {
return params.timescale;
};
that.getFadeType = function getFadeType() {
return params.fadeType;
};
that.isDisplayMono = function isDisplayMono() {
return params.mono;
};
that.getUITheme = function getUITheme() {
return params.UITheme;
};
that.getCursorPos = function getCursorPos() {
return params.cursorPos;
};
that.getState = function getState() {
return params.state;
};
that.getAudioContext = function getAudioContext() {
return params.ac;
};
that.getSampleRate = function getSampleRate() {
return params.ac.sampleRate;
};
that.getCurrentTime = function getCurrentTime() {
return params.ac.currentTime;
};
that.getTimeFormat = function getTimeFormat() {
return params.timeFormat;
};
that.getResolution = function getResolution() {
return params.resolution;
};
that.getWaveHeight = function getWaveHeight() {
return params.waveHeight;
};
that.getColorScheme = function getColorScheme() {
return {
waveColor: params.waveColor,
progressColor: params.progressColor,
loadingColor: params.loadingColor,
cursorColor: params.cursorColor,
markerColor: params.markerColor,
timeColor: params.timeColor,
fontColor: params.fontColor,
fadeColor: params.fadeColor,
selectBorderColor: params.selectBorderColor,
selectBackgroundColor: params.selectBackgroundColor,
};
};
that.getTrackScroll = function getTrackScroll() {
var scroll = params.trackscroll;
return {
left: scroll.left,
top: scroll.top
};
};
/*
Start of all setter methods for config.
*/
that.setResolution = function setResolution(resolution) {
params.resolution = resolution;
};
that.setTimeFormat = function setTimeFormat(format) {
params.timeFormat = format;
};
that.setFadeType = function setFadeType(type) {
params.fadeType = type;
};
that.setDisplayMono = function setDisplayMono(bool) {
params.mono = bool;
};
that.setCursorPos = function setCursorPos(pos) {
params.cursorPos = pos;
};
that.setState = function setState(state) {
params.state = state;
};
that.setTrackScroll = function setTrackScroll(left, top) {
var scroll = params.trackscroll;
scroll.left = left;
scroll.top = top;
};
};

View File

@ -0,0 +1,562 @@
'use strict';
var AudioControls = function() {
};
AudioControls.prototype.groups = {
"audio-select": ["btns_audio_tools", "btns_fade"]
};
AudioControls.prototype.classes = {
"btn-state-active": "btn btn-mini active",
"btn-state-default": "btn btn-mini",
"disabled": "disabled",
"active": "active"
};
AudioControls.prototype.events = {
"btn_rewind": {
click: "rewindAudio"
},
"btn_play": {
click: "playAudio"
},
"btn_stop": {
click: "stopAudio"
},
"btn_cursor": {
click: "changeState"
},
"btn_select": {
click: "changeState"
},
"btn_shift": {
click: "changeState"
},
"btn_fadein": {
click: "changeState"
},
"btn_fadeout": {
click: "changeState"
},
"btns_fade": {
click: "createFade"
},
"btn_save": {
click: "save"
},
"btn_open": {
click: "open"
},
"btn_trim_audio": {
click: "trimAudio"
},
"time_format": {
change: "changeTimeFormat"
},
"audio_start": {
blur: "validateCueIn"
},
"audio_end": {
blur: "validateCueOut"
},
"audio_pos": {
},
"audio_resolution": {
change: "changeResolution"
}
};
AudioControls.prototype.validateCue = function(value) {
var validators,
regex,
result;
validators = {
"seconds": /^\d+$/,
"thousandths": /^\d+\.\d{3}$/,
"hh:mm:ss": /^[0-9]{2,}:[0-5][0-9]:[0-5][0-9]$/,
"hh:mm:ss.u": /^[0-9]{2,}:[0-5][0-9]:[0-5][0-9]\.\d{1}$/,
"hh:mm:ss.uu": /^[0-9]{2,}:[0-5][0-9]:[0-5][0-9]\.\d{2}$/,
"hh:mm:ss.uuu": /^[0-9]{2,}:[0-5][0-9]:[0-5][0-9]\.\d{3}$/
};
regex = validators[this.timeFormat];
result = regex.test(value);
return result;
};
AudioControls.prototype.cueToSeconds = function(value) {
var converter,
func,
seconds;
function clockConverter(value) {
var data = value.split(":"),
hours = parseInt(data[0], 10) * 3600,
mins = parseInt(data[1], 10) * 60,
secs = parseFloat(data[2]),
seconds;
seconds = hours + mins + secs;
return seconds;
}
converter = {
"seconds": function(value) {
return parseInt(value, 10);
},
"thousandths": function(value) {
return parseFloat(value);
},
"hh:mm:ss": function(value) {
return clockConverter(value);
},
"hh:mm:ss.u": function(value) {
return clockConverter(value);
},
"hh:mm:ss.uu": function(value) {
return clockConverter(value);
},
"hh:mm:ss.uuu": function(value) {
return clockConverter(value);
}
};
func = converter[this.timeFormat];
seconds = func(value);
return seconds;
};
AudioControls.prototype.cueFormatters = function(format) {
function clockFormat(seconds, decimals) {
var hours,
minutes,
secs,
result;
hours = parseInt(seconds / 3600, 10) % 24;
minutes = parseInt(seconds / 60, 10) % 60;
secs = seconds % 60;
secs = secs.toFixed(decimals);
result = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (secs < 10 ? "0" + secs : secs);
return result;
}
var formats = {
"seconds": function (seconds) {
return seconds.toFixed(0);
},
"thousandths": function (seconds) {
return seconds.toFixed(3);
},
"hh:mm:ss": function (seconds) {
return clockFormat(seconds, 0);
},
"hh:mm:ss.u": function (seconds) {
return clockFormat(seconds, 1);
},
"hh:mm:ss.uu": function (seconds) {
return clockFormat(seconds, 2);
},
"hh:mm:ss.uuu": function (seconds) {
return clockFormat(seconds, 3);
}
};
return formats[format];
};
AudioControls.prototype.init = function(config) {
var that = this,
className,
event,
events = this.events,
tmpEl,
func,
state,
container,
tmpBtn;
makePublisher(this);
this.ctrls = {};
this.config = config;
container = this.config.getContainer();
state = this.config.getState();
tmpBtn = document.getElementsByClassName("btn_"+state)[0];
if (tmpBtn) {
this.activateButton(tmpBtn);
}
for (className in events) {
tmpEl = container.getElementsByClassName(className)[0];
this.ctrls[className] = tmpEl;
for (event in events[className]) {
if (tmpEl) {
func = that[events[className][event]].bind(that);
tmpEl.addEventListener(event, func);
}
}
}
if (this.ctrls["time_format"]) {
this.ctrls["time_format"].value = this.config.getTimeFormat();
}
if (this.ctrls["audio_resolution"]) {
this.ctrls["audio_resolution"].value = this.config.getResolution();
}
this.timeFormat = this.config.getTimeFormat();
//Kept in seconds so time format change can update fields easily.
this.currentSelectionValues = undefined;
this.onCursorSelection({
start: 0,
end: 0
});
};
AudioControls.prototype.changeTimeFormat = function(e) {
var format = e.target.value,
func, start, end;
format = (this.cueFormatters(format) !== undefined) ? format : "hh:mm:ss";
this.config.setTimeFormat(format);
this.timeFormat = format;
if (this.currentSelectionValues !== undefined) {
func = this.cueFormatters(format);
start = this.currentSelectionValues.start;
end = this.currentSelectionValues.end;
if (this.ctrls["audio_start"]) {
this.ctrls["audio_start"].value = func(start);
}
if (this.ctrls["audio_end"]) {
this.ctrls["audio_end"].value = func(end);
}
}
};
AudioControls.prototype.changeResolution = function(e) {
var res = parseInt(e.target.value, 10);
this.config.setResolution(res);
this.fire("changeresolution", res);
};
AudioControls.prototype.validateCueIn = function(e) {
var value = e.target.value,
end,
startSecs;
if (this.validateCue(value)) {
end = this.currentSelectionValues.end;
startSecs = this.cueToSeconds(value);
if (startSecs <= end) {
this.notifySelectionUpdate(startSecs, end);
this.currentSelectionValues.start = startSecs;
return;
}
}
//time entered was otherwise invalid.
e.target.value = this.cueFormatters(this.timeFormat)(this.currentSelectionValues.start);
};
AudioControls.prototype.validateCueOut = function(e) {
var value = e.target.value,
start,
endSecs;
if (this.validateCue(value)) {
start = this.currentSelectionValues.start;
endSecs = this.cueToSeconds(value);
if (endSecs >= start) {
this.notifySelectionUpdate(start, endSecs);
this.currentSelectionValues.end = endSecs;
return;
}
}
//time entered was otherwise invalid.
e.target.value = this.cueFormatters(this.timeFormat)(this.currentSelectionValues.end);
};
AudioControls.prototype.activateButtonGroup = function(id) {
var el = document.getElementById(id),
btns,
classes = this.classes,
i, len;
if (el === null) {
return;
}
btns = el.getElementsByTagName("a");
for (i = 0, len = btns.length; i < len; i++) {
btns[i].classList.remove(classes["disabled"]);
}
};
AudioControls.prototype.deactivateButtonGroup = function(id) {
var el = document.getElementById(id),
btns,
classes = this.classes,
i, len;
if (el === null) {
return;
}
btns = el.getElementsByTagName("a");
for (i = 0, len = btns.length; i < len; i++) {
btns[i].classList.add(classes["disabled"]);
}
};
AudioControls.prototype.activateAudioSelection = function() {
var ids = this.groups["audio-select"],
i, len;
for (i = 0, len = ids.length; i < len; i++) {
this.activateButtonGroup(ids[i]);
}
};
AudioControls.prototype.deactivateAudioSelection = function() {
var ids = this.groups["audio-select"],
i, len;
for (i = 0, len = ids.length; i < len; i++) {
this.deactivateButtonGroup(ids[i]);
}
};
AudioControls.prototype.save = function() {
this.fire('playlistsave', this);
};
AudioControls.prototype.open = function() {
this.fire('playlistrestore', this);
};
AudioControls.prototype.rewindAudio = function() {
this.fire('rewindaudio', this);
};
AudioControls.prototype.playAudio = function() {
this.fire('playaudio', this);
};
AudioControls.prototype.stopAudio = function() {
this.fire('stopaudio', this);
};
AudioControls.prototype.activateButton = function(el) {
if (el) {
el.classList.add(this.classes["active"]);
}
};
AudioControls.prototype.deactivateButton = function(el) {
if (el) {
el.classList.remove(this.classes["active"]);
}
};
AudioControls.prototype.enableButton = function(el) {
if (el) {
el.classList.remove(this.classes["disabled"]);
}
};
AudioControls.prototype.disableButton = function(el) {
if (el) {
el.classList.add(this.classes["disabled"]);
}
};
AudioControls.prototype.changeState = function(e) {
var el = e.currentTarget,
prevEl = el.parentElement.getElementsByClassName('active')[0],
state = el.dataset.state;
this.deactivateButton(prevEl);
this.activateButton(el);
this.config.setState(state);
this.fire('changestate', this);
};
AudioControls.prototype.zeroCrossing = function(e) {
var el = e.target,
disabled,
classes = this.classes;
disabled = el.classList.contains(classes["disabled"]);
if (!disabled) {
this.fire('trackedit', {
type: "zeroCrossing"
});
}
};
AudioControls.prototype.trimAudio = function(e) {
var el = e.target,
disabled,
classes = this.classes;
disabled = el.classList.contains(classes["disabled"]);
if (!disabled) {
this.fire('trackedit', {
type: "trimAudio"
});
}
};
AudioControls.prototype.removeAudio = function(e) {
var el = e.target,
disabled,
classes = this.classes;
disabled = el.classList.contains(classes["disabled"]);
if (!disabled) {
this.fire('trackedit', {
type: "removeAudio"
});
}
};
AudioControls.prototype.createFade = function(e) {
var el = e.target,
shape = el.dataset.shape,
type = el.dataset.type,
disabled,
classes = this.classes;
disabled = el.classList.contains(classes["disabled"]);
if (!disabled) {
this.fire('trackedit', {
type: "createFade",
args: {
type: type,
shape: shape
}
});
}
};
AudioControls.prototype.onAudioSelection = function() {
this.activateAudioSelection();
};
AudioControls.prototype.onAudioDeselection = function() {
this.deactivateAudioSelection();
};
/*
start, end in seconds
*/
AudioControls.prototype.notifySelectionUpdate = function(start, end) {
this.fire('changeselection', {
start: start,
end: end
});
};
/*
start, end in seconds
*/
AudioControls.prototype.onCursorSelection = function(args) {
var startFormat = this.cueFormatters(this.timeFormat)(args.start),
endFormat = this.cueFormatters(this.timeFormat)(args.end),
start = this.cueToSeconds(startFormat),
end = this.cueToSeconds(endFormat);
this.currentSelectionValues = {
start: start,
end:end
};
if (this.ctrls["audio_start"]) {
this.ctrls["audio_start"].value = startFormat;
}
if (this.ctrls["audio_end"]) {
this.ctrls["audio_end"].value = endFormat;
}
};
/*
args {seconds, pixels}
*/
AudioControls.prototype.onAudioUpdate = function(args) {
if (this.ctrls["audio_pos"]) {
this.ctrls["audio_pos"].value = this.cueFormatters(this.timeFormat)(args.seconds);
}
};

View File

@ -0,0 +1,72 @@
var Curves = {};
Curves.createLinearBuffer = function createLinearBuffer(length, rotation) {
var curve = new Float32Array(length),
i, x,
scale = length - 1;
for (i = 0; i < length; i++) {
x = i / scale;
if (rotation > 0) {
curve[i] = x;
}
else {
curve[i] = 1 - x;
}
}
return curve;
};
Curves.createExponentialBuffer = function createExponentialBuffer(length, rotation) {
var curve = new Float32Array(length),
i, x,
scale = length - 1,
index;
for (i = 0; i < length; i++) {
x = i / scale;
index = rotation > 0 ? i : length - 1 - i;
curve[index] = Math.exp(2 * x - 1) / Math.exp(1);
}
return curve;
};
//creating a curve to simulate an S-curve with setValueCurveAtTime.
Curves.createSCurveBuffer = function createSCurveBuffer(length, phase) {
var curve = new Float32Array(length),
i;
for (i = 0; i < length; ++i) {
curve[i] = (Math.sin((Math.PI * i / length) - phase)) /2 + 0.5;
}
return curve;
};
//creating a curve to simulate a logarithmic curve with setValueCurveAtTime.
Curves.createLogarithmicBuffer = function createLogarithmicBuffer(length, base, rotation) {
var curve = new Float32Array(length),
index,
key = ""+length+base+rotation,
store = [],
x = 0,
i;
if (store[key]) {
return store[key];
}
for (i = 0; i < length; i++) {
//index for the curve array.
index = rotation > 0 ? i : length - 1 - i;
x = i / length;
curve[index] = Math.log(1 + base*x) / Math.log(1 + base);
}
store[key] = curve;
return curve;
};

View File

@ -0,0 +1,140 @@
var Fades = function() {};
Fades.prototype.init = function init(sampleRate) {
this.sampleRate = sampleRate;
}
/*
The setValueCurveAtTime method
Sets an array of arbitrary parameter values starting at the given time for the given duration. The number of values will be scaled to fit into the desired duration.
The values parameter is a Float32Array representing a parameter value curve. These values will apply starting at the given time and lasting for the given duration.
The startTime parameter is the time in the same time coordinate system as AudioContext.currentTime.
The duration parameter is the amount of time in seconds (after the time parameter) where values will be calculated according to the values parameter..
During the time interval: startTime <= t < startTime + duration, values will be calculated:
v(t) = values[N * (t - startTime) / duration], where N is the length of the values array.
After the end of the curve time interval (t >= startTime + duration), the value will remain constant at the final curve value, until there is another automation event (if any).
*/
Fades.prototype.sCurveFadeIn = function sCurveFadeIn(gain, start, duration, options) {
var curve;
curve = Curves.createSCurveBuffer(this.sampleRate, (Math.PI/2));
gain.setValueCurveAtTime(curve, start, duration);
};
Fades.prototype.sCurveFadeOut = function sCurveFadeOut(gain, start, duration, options) {
var curve;
curve = Curves.createSCurveBuffer(this.sampleRate, -(Math.PI/2));
gain.setValueCurveAtTime(curve, start, duration);
};
/*
The linearRampToValueAtTime method
Schedules a linear continuous change in parameter value from the previous scheduled parameter value to the given value.
The value parameter is the value the parameter will linearly ramp to at the given time.
The endTime parameter is the time in the same time coordinate system as AudioContext.currentTime.
The value during the time interval T0 <= t < T1 (where T0 is the time of the previous event and T1 is the endTime parameter passed into this method) will be calculated as:
v(t) = V0 + (V1 - V0) * ((t - T0) / (T1 - T0))
Where V0 is the value at the time T0 and V1 is the value parameter passed into this method.
If there are no more events after this LinearRampToValue event then for t >= T1, v(t) = V1
*/
Fades.prototype.linearFadeIn = function linearFadeIn(gain, start, duration, options) {
gain.linearRampToValueAtTime(0, start);
gain.linearRampToValueAtTime(1, start + duration);
};
Fades.prototype.linearFadeOut = function linearFadeOut(gain, start, duration, options) {
gain.linearRampToValueAtTime(1, start);
gain.linearRampToValueAtTime(0, start + duration);
};
/*
DOES NOT WORK PROPERLY USING 0
The exponentialRampToValueAtTime method
Schedules an exponential continuous change in parameter value from the previous scheduled parameter value to the given value. Parameters representing filter frequencies and playback rate are best changed exponentially because of the way humans perceive sound.
The value parameter is the value the parameter will exponentially ramp to at the given time. An exception will be thrown if this value is less than or equal to 0, or if the value at the time of the previous event is less than or equal to 0.
The endTime parameter is the time in the same time coordinate system as AudioContext.currentTime.
The value during the time interval T0 <= t < T1 (where T0 is the time of the previous event and T1 is the endTime parameter passed into this method) will be calculated as:
v(t) = V0 * (V1 / V0) ^ ((t - T0) / (T1 - T0))
Where V0 is the value at the time T0 and V1 is the value parameter passed into this method.
If there are no more events after this ExponentialRampToValue event then for t >= T1, v(t) = V1
*/
Fades.prototype.exponentialFadeIn = function exponentialFadeIn(gain, start, duration, options) {
gain.exponentialRampToValueAtTime(0.01, start);
gain.exponentialRampToValueAtTime(1, start + duration);
};
Fades.prototype.exponentialFadeOut = function exponentialFadeOut(gain, start, duration, options) {
gain.exponentialRampToValueAtTime(1, start);
gain.exponentialRampToValueAtTime(0.01, start + duration);
};
Fades.prototype.logarithmicFadeIn = function logarithmicFadeIn(gain, start, duration, options) {
var curve,
base = options.base;
base = typeof base !== 'undefined' ? base : 10;
curve = Curves.createLogarithmicBuffer(this.sampleRate, base, 1);
gain.setValueCurveAtTime(curve, start, duration);
};
Fades.prototype.logarithmicFadeOut = function logarithmicFadeOut(gain, start, duration, options) {
var curve,
base = options.base;
base = typeof base !== 'undefined' ? base : 10;
curve = Curves.createLogarithmicBuffer(this.sampleRate, base, -1);
gain.setValueCurveAtTime(curve, start, duration);
};
/**
Calls the appropriate fade type with options
options {
start,
duration,
base (for logarithmic)
}
*/
Fades.prototype.createFadeIn = function createFadeIn(gain, type, options) {
var method = type + "FadeIn",
fn = this[method];
fn.call(this, gain, options.start, options.duration, options);
};
Fades.prototype.createFadeOut = function createFadeOut(gain, type, options) {
var method = type + "FadeOut",
fn = this[method];
fn.call(this, gain, options.start, options.duration, options);
};

View File

@ -0,0 +1,86 @@
'use strict';
var BufferLoader = function() {
}
BufferLoader.prototype.init = function(params) {
var loader = this;
loader.context = params.context;
loader.bufferList = [];
loader.loadCount = 0;
loader.defaultParams = {
};
loader.params = Object.create(params);
Object.keys(loader.defaultParams).forEach(function (key) {
if (!(key in params)) { params[key] = loader.defaultParams[key]; }
});
}
BufferLoader.prototype.requestBuffer = function(url, name) {
var loader = this,
request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = function() {
loader.context.decodeAudioData(request.response, function(buffer) {
if (!buffer) {
alert('error decoding file data: '+url);
return;
}
loader.loadCount++;
loader.onAudioFileLoad(name, buffer);
if (loader.loadCount === loader.urlList.length) {
loader.onAudioFilesDone(loader.bufferList);
}
},
function(error) {
console.error('decodeAudioData error',error);
});
}
request.onerror = function(){
alert('BufferLoader: XHR error');
};
request.send();
};
BufferLoader.prototype.loadAudio = function(aUrls, callback) {
var names=[];
var paths=[];
for (var name in aUrls) {
var path = aUrls[name];
names.push(name);
paths.push(path);
}
this.urlList = paths;
var i,
length;
for (i = 0, length = paths.length; i < length; i++) {
this.requestBuffer(paths[i], names[i]);
}
}
BufferLoader.prototype.onAudioFileLoad = function(name, buffer) {
this.bufferList[name] = buffer;
}
BufferLoader.prototype.onAudioFilesDone = function(bufferList) {
var fn = this.params.onComplete;
fn(bufferList);
}

View File

@ -0,0 +1,16 @@
var Storage = function() {};
Storage.prototype.save = function save(name, playlist) {
var json = JSON.stringify(playlist);
localStorage.setItem(name, json);
};
Storage.prototype.restore = function restore(name) {
var JSONstring = localStorage.getItem(name),
data;
data = JSON.parse(JSONstring);
return data;
};

View File

@ -0,0 +1,57 @@
/*
Code taken from http://www.jspatterns.com/book/7/observer-game.html
Pub/Sub
*/
var publisher = {
subscribers: {
any: []
},
on: function (type, fn, context) {
type = type || 'any';
fn = typeof fn === "function" ? fn : context[fn];
if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push({fn: fn, context: context || this});
},
remove: function (type, fn, context) {
this.visitSubscribers('unsubscribe', type, fn, context);
},
fire: function (type, publication) {
this.visitSubscribers('publish', type, publication);
},
reset: function (type) {
this.subscribers[type] = undefined;
},
visitSubscribers: function (action, type, arg, context) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max = subscribers ? subscribers.length : 0;
for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i].fn.call(subscribers[i].context, arg);
}
else {
if (subscribers[i].fn === arg && subscribers[i].context === context) {
subscribers.splice(i, 1);
}
}
}
}
};
function makePublisher(o) {
var i;
for (i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] === "function") {
o[i] = publisher[i];
}
}
o.subscribers = {any: []};
}

View File

@ -0,0 +1,57 @@
/*
Code taken from http://www.jspatterns.com/book/7/observer-game.html
Pub/Sub
*/
var publisher = {
subscribers: {
any: []
},
on: function (type, fn, context) {
type = type || 'any';
fn = typeof fn === "function" ? fn : context[fn];
if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push({fn: fn, context: context || this});
},
remove: function (type, fn, context) {
this.visitSubscribers('unsubscribe', type, fn, context);
},
fire: function (type, publication) {
this.visitSubscribers('publish', type, publication);
},
reset: function (type) {
},
visitSubscribers: function (action, type, arg, context) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max = subscribers ? subscribers.length : 0;
for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i].fn.call(subscribers[i].context, arg);
}
else {
if (subscribers[i].fn === arg && subscribers[i].context === context) {
subscribers.splice(i, 1);
}
}
}
}
};
function makePublisher(o) {
var i;
for (i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] === "function") {
o[i] = publisher[i];
}
}
o.subscribers = {any: []};
}

View File

@ -0,0 +1,349 @@
'use strict';
var PlaylistEditor = function() {
};
PlaylistEditor.prototype.setConfig = function(config) {
this.config = config;
};
PlaylistEditor.prototype.init = function(tracks) {
var that = this,
i,
len,
container = this.config.getContainer(),
div = container.getElementsByClassName("playlist-tracks")[0],
fragment = document.createDocumentFragment(),
trackEditor,
trackElem,
timeScale,
audioControls;
makePublisher(this);
this.storage = new Storage();
this.trackContainer = div;
this.trackEditors = [];
audioControls = new AudioControls();
audioControls.init(this.config);
if (this.config.isTimeScaleEnabled()) {
timeScale = new TimeScale();
timeScale.init(this.config);
audioControls.on("changeresolution", "onResolutionChange", timeScale);
this.on("trackscroll", "onTrackScroll", timeScale);
}
this.timeScale = timeScale;
for (i = 0, len = tracks.length; i < len; i++) {
trackEditor = new TrackEditor();
trackEditor.setConfig(this.config);
trackElem = trackEditor.loadTrack(tracks[i]);
this.trackEditors.push(trackEditor);
fragment.appendChild(trackElem);
audioControls.on("trackedit", "onTrackEdit", trackEditor);
audioControls.on("changeresolution", "onResolutionChange", trackEditor);
trackEditor.on("activateSelection", "onAudioSelection", audioControls);
trackEditor.on("deactivateSelection", "onAudioDeselection", audioControls);
trackEditor.on("changecursor", "onCursorSelection", audioControls);
trackEditor.on("changecursor", "onSelectUpdate", this);
}
div.innerHTML = '';
div.appendChild(fragment);
div.onscroll = this.onTrackScroll.bind(that);
this.sampleRate = this.config.getSampleRate();
this.scrollTimeout = false;
//for setInterval that's toggled during play/stop.
this.interval;
this.on("playbackcursor", "onAudioUpdate", audioControls);
audioControls.on("playlistsave", "save", this);
audioControls.on("playlistrestore", "restore", this);
audioControls.on("rewindaudio", "rewind", this);
audioControls.on("playaudio", "play", this);
audioControls.on("stopaudio", "stop", this);
audioControls.on("trimaudio", "onTrimAudio", this);
audioControls.on("removeaudio", "onRemoveAudio", this);
audioControls.on("changestate", "onStateChange", this);
audioControls.on("changeselection", "onSelectionChange", this);
};
PlaylistEditor.prototype.onTrimAudio = function() {
var track = this.activeTrack,
selected = track.getSelectedArea(),
start, end;
if (selected === undefined) {
return;
}
track.trim(selected.start, selected.end);
};
PlaylistEditor.prototype.onRemoveAudio = function() {
var track = this.activeTrack,
selected = track.getSelectedArea(),
start, end;
if (selected === undefined) {
return;
}
track.removeAudio(selected.start, selected.end);
};
PlaylistEditor.prototype.onSelectionChange = function(args) {
if (this.activeTrack === undefined) {
return;
}
var res = this.config.getResolution(),
start = ~~(args.start * this.sampleRate / res),
end = ~~(args.end * this.sampleRate / res);
this.config.setCursorPos(args.start);
this.activeTrack.setSelectedArea(start, end);
this.activeTrack.updateEditor(-1, undefined, undefined, true);
};
PlaylistEditor.prototype.onStateChange = function() {
var that = this,
editors = this.trackEditors,
i,
len,
editor,
state = this.config.getState();
for(i = 0, len = editors.length; i < len; i++) {
editors[i].deactivate();
editors[i].setState(state);
}
};
PlaylistEditor.prototype.onTrackScroll = function(e) {
var that = this,
el = e.srcElement;
if (that.scrollTimeout) return;
//limit the scroll firing to every 25ms.
that.scrollTimeout = setTimeout(function() {
that.config.setTrackScroll(el.scrollLeft, el.scrollTop);
that.fire('trackscroll', e);
that.scrollTimeout = false;
}, 25);
};
PlaylistEditor.prototype.activateTrack = function(trackEditor) {
var that = this,
editors = this.trackEditors,
i,
len,
editor;
for (i = 0, len = editors.length; i < len; i++) {
editor = editors[i];
if (editor === trackEditor) {
editor.activate();
this.activeTrack = trackEditor;
}
else {
editor.deactivate();
}
}
};
PlaylistEditor.prototype.onSelectUpdate = function(event) {
this.activateTrack(event.editor);
};
PlaylistEditor.prototype.resetCursor = function() {
this.config.setCursorPos(0);
this.notifySelectUpdate(0, 0);
};
PlaylistEditor.prototype.onCursorSelection = function(args) {
this.activateTrack(args.editor);
};
PlaylistEditor.prototype.rewind = function() {
if (this.activeTrack !== undefined) {
this.activeTrack.resetCursor();
}
else {
this.resetCursor();
}
this.stop();
};
/*
returns selected time in global (playlist relative) seconds.
*/
PlaylistEditor.prototype.getSelected = function() {
var selected,
start,
end;
if (this.activeTrack) {
selected = this.activeTrack.selectedArea;
if (selected !== undefined && (selected.end > selected.start)) {
return this.activeTrack.getSelectedPlayTime();
}
}
};
PlaylistEditor.prototype.isPlaying = function() {
var that = this,
editors = this.trackEditors,
i,
len,
isPlaying = false;
for (i = 0, len = editors.length; i < len; i++) {
isPlaying = isPlaying || editors[i].isPlaying();
}
return isPlaying;
};
PlaylistEditor.prototype.play = function() {
var that = this,
editors = this.trackEditors,
i,
len,
currentTime = this.config.getCurrentTime(),
delay = 0.2,
startTime = this.config.getCursorPos(),
endTime,
selected = this.getSelected();
if (selected !== undefined) {
startTime = selected.startTime;
endTime = selected.endTime;
}
for (i = 0, len = editors.length; i < len; i++) {
editors[i].schedulePlay(currentTime, delay, startTime, endTime);
}
this.lastPlay = currentTime + delay;
this.interval = setInterval(that.updateEditor.bind(that), 25);
};
PlaylistEditor.prototype.stop = function() {
var editors = this.trackEditors,
i,
len,
currentTime = this.config.getCurrentTime();
clearInterval(this.interval);
for (i = 0, len = editors.length; i < len; i++) {
editors[i].scheduleStop(currentTime);
editors[i].updateEditor(-1, undefined, undefined, true);
}
};
PlaylistEditor.prototype.updateEditor = function() {
var editors = this.trackEditors,
i,
len,
currentTime = this.config.getCurrentTime(),
elapsed = currentTime - this.lastPlay,
res = this.config.getResolution(),
cursorPos = this.config.getCursorPos(),
cursorPixel,
playbackSec,
selected = this.getSelected(),
start, end,
highlighted = false;
if (selected !== undefined) {
start = ~~(selected.startTime * this.sampleRate / res);
end = Math.ceil(selected.endTime * this.sampleRate / res);
highlighted = true;
}
if (this.isPlaying()) {
if (elapsed) {
playbackSec = cursorPos + elapsed;
cursorPixel = Math.ceil(playbackSec * this.sampleRate / res);
for (i = 0, len = editors.length; i < len; i++) {
editors[i].updateEditor(cursorPixel, start, end, highlighted);
}
this.fire("playbackcursor", {
"seconds": playbackSec,
"pixels": cursorPixel
});
}
}
else {
clearInterval(this.interval);
for (i = 0, len = editors.length; i < len; i++) {
editors[i].updateEditor(-1, undefined, undefined, true);
}
}
};
PlaylistEditor.prototype.getJson = function() {
var editors = this.trackEditors,
i,
len,
info = [],
json;
for (i = 0, len = editors.length; i < len; i++) {
info.push(editors[i].getTrackDetails());
}
json = JSON.stringify(info);
return info;
};
PlaylistEditor.prototype.save = function() {
var editors = this.trackEditors,
i,
len,
info = [];
for (i = 0, len = editors.length; i < len; i++) {
info.push(editors[i].getTrackDetails());
}
this.storage.save("test", info);
};
PlaylistEditor.prototype.restore = function() {
var state;
state = this.storage.restore("test");
this.trackContainer.innerHTML='';
this.init(state);
};

View File

@ -0,0 +1,161 @@
'use strict';
var AudioPlayout = function() {
};
AudioPlayout.prototype.init = function(config) {
makePublisher(this);
this.config = config;
this.ac = this.config.getAudioContext();
this.fadeMaker = new Fades();
this.fadeMaker.init(this.ac.sampleRate);
this.gainNode = undefined;
this.destination = this.ac.destination;
this.analyser = this.ac.createAnalyser();
this.analyser.connect(this.destination);
};
AudioPlayout.prototype.getBuffer = function() {
return this.buffer;
};
AudioPlayout.prototype.setBuffer = function(buffer) {
this.buffer = buffer;
};
/*
param relPos: cursor position in seconds relative to this track.
can be negative if the cursor is placed before the start of this track etc.
*/
AudioPlayout.prototype.applyFades = function(fades, relPos, now, delay) {
var id,
fade,
fn,
options,
startTime,
duration;
this.gainNode && this.gainNode.disconnect();
this.gainNode = this.ac.createGainNode();
for (id in fades) {
fade = fades[id];
if (relPos <= fade.start) {
startTime = now + (fade.start - relPos) + delay;
duration = fade.end - fade.start;
}
else if (relPos > fade.start && relPos < fade.end) {
startTime = now - (relPos - fade.start) + delay;
duration = fade.end - fade.start;
}
options = {
start: startTime,
duration: duration
};
if (fades.hasOwnProperty(id)) {
fn = this.fadeMaker["create"+fade.type];
fn.call(this.fadeMaker, this.gainNode.gain, fade.shape, options);
}
}
};
/**
* Loads audiobuffer.
*
* @param {AudioBuffer} audioData Audio data.
*/
AudioPlayout.prototype.loadData = function (audioData, cb) {
var that = this;
this.ac.decodeAudioData(
audioData,
function (buffer) {
that.buffer = buffer;
cb(buffer);
},
Error
);
};
AudioPlayout.prototype.isUnScheduled = function() {
return this.source && (this.source.playbackState === this.source.UNSCHEDULED_STATE);
};
AudioPlayout.prototype.isScheduled = function() {
return this.source && (this.source.playbackState === this.source.SCHEDULED_STATE);
};
AudioPlayout.prototype.isPlaying = function() {
return this.source && (this.source.playbackState === this.source.PLAYING_STATE);
};
AudioPlayout.prototype.isFinished = function() {
return this.source && (this.source.playbackState === this.source.FINISHED_STATE);
};
AudioPlayout.prototype.getDuration = function() {
return this.buffer.duration;
};
AudioPlayout.prototype.getPlayOffset = function() {
var offset = 0;
//TODO needs a fix for when the buffer naturally plays out. But also have to mind the entire playlist.
if (this.playing) {
offset = this.secondsOffset + (this.ac.currentTime - this.playTime);
}
else {
offset = this.secondsOffset;
}
return offset;
};
AudioPlayout.prototype.setPlayedPercents = function(percent) {
this.secondsOffset = this.getDuration() * percent;
};
AudioPlayout.prototype.getPlayedPercents = function() {
return this.getPlayOffset() / this.getDuration();
};
AudioPlayout.prototype.setSource = function(source) {
this.source && this.source.disconnect();
this.source = source;
this.source.buffer = this.buffer;
this.source.connect(this.gainNode);
this.gainNode.connect(this.analyser);
};
/*
source.start is picky when passing the end time.
If rounding error causes a number to make the source think
it is playing slightly more samples than it has it won't play at all.
Unfortunately it doesn't seem to work if you just give it a start time.
*/
AudioPlayout.prototype.play = function(when, start, duration) {
if (!this.buffer) {
console.error("no buffer to play");
return;
}
this.setSource(this.ac.createBufferSource());
this.source.start(when || 0, start, duration);
};
AudioPlayout.prototype.stop = function(when) {
this.source && this.source.stop(when || 0);
};

View File

@ -0,0 +1,25 @@
<form class="form-inline">
<select id="time_format">
<option value="seconds">seconds</option>
<option value="thousandths">thousandths</option>
<option value="hh:mm:ss">hh:mm:ss</option>
<option value="hh:mm:ss.uu">hh:mm:ss + hundredths</option>
<option value="hh:mm:ss.uuu">hh:mm:ss + milliseconds</option>
</select>
<input id="audio_start" type="text" class="input-small">
<input id="audio_end" type="text" class="input-small">
<input id="audio_pos" type="text" class="input-small">
<select id="audio_resolution">
<option>4000</option>
<option>5000</option>
<option>6000</option>
<option>7000</option>
<option>8000</option>
<option>9000</option>
<option>10000</option>
<option>11000</option>
<option>12000</option>
<option>15000</option>
<option>20000</option>
</select>
</form>

View File

@ -0,0 +1,25 @@
<form class="form-inline">
<select id="time_format">
<option value="seconds">seconds</option>
<option value="thousandths">thousandths</option>
<option value="hh:mm:ss">hh:mm:ss</option>
<option value="hh:mm:ss.uu">hh:mm:ss + hundredths</option>
<option value="hh:mm:ss.uuu">hh:mm:ss + milliseconds</option>
</select>
<input id="audio_start" type="text" class="input-small">
<input id="audio_end" type="text" class="input-small">
<input id="audio_pos" type="text" class="input-small">
<select id="audio_resolution">
<option>4000</option>
<option>5000</option>
<option>6000</option>
<option>7000</option>
<option>8000</option>
<option>9000</option>
<option>10000</option>
<option>11000</option>
<option>12000</option>
<option>15000</option>
<option>20000</option>
</select>
</form>

View File

@ -0,0 +1,151 @@
'use strict';
var TimeScale = function() {
};
TimeScale.prototype.init = function(config) {
var that = this,
canv,
div;
makePublisher(this);
div = document.getElementsByClassName("playlist-time-scale")[0];
if (div === undefined) {
return;
}
canv = document.createElement("canvas");
this.canv = canv;
this.context = canv.getContext('2d');
this.config = config;
this.container = div; //container for the main time scale.
//TODO check for window resizes to set these.
this.width = this.container.clientWidth;
this.height = this.container.clientHeight;
canv.setAttribute('width', this.width);
canv.setAttribute('height', this.height);
//array of divs displaying time every 30 seconds. (TODO should make this depend on resolution)
this.times = [];
this.prevScrollPos = 0; //checking the horizontal scroll (must update timeline above in case of change)
this.drawScale();
};
/*
Return time in format mm:ss
*/
TimeScale.prototype.formatTime = function(seconds) {
var out, m, s;
s = seconds % 60;
m = (seconds - s) / 60;
if (s < 10) {
s = "0"+s;
}
out = m + ":" + s;
return out;
};
TimeScale.prototype.clear = function() {
this.container.innerHTML = "";
this.context.clearRect(0, 0, this.width, this.height);
};
TimeScale.prototype.drawScale = function(offset) {
var cc = this.context,
canv = this.canv,
colors = this.config.getColorScheme(),
pix,
res = this.config.getResolution(),
SR = this.config.getSampleRate(),
pixPerSec = SR/res,
pixOffset = offset || 0, //caused by scrolling horizontally
i,
end,
counter = 0,
pixIndex,
container = this.container,
width = this.width,
height = this.height,
div,
time,
sTime,
fragment = document.createDocumentFragment(),
scaleY,
scaleHeight;
this.clear();
fragment.appendChild(canv);
cc.fillStyle = colors.timeColor;
end = width + pixOffset;
for (i = 0; i < end; i = i + pixPerSec) {
pixIndex = ~~(i);
pix = pixIndex - pixOffset;
if (pixIndex >= pixOffset) {
//put a timestamp every 30 seconds.
if (counter % 30 === 0) {
sTime = this.formatTime(counter);
time = document.createTextNode(sTime);
div = document.createElement("div");
div.style.left = pix+"px";
div.appendChild(time);
fragment.appendChild(div);
scaleHeight = 10;
scaleY = height - scaleHeight;
}
else if (counter % 5 === 0) {
scaleHeight = 5;
scaleY = height - scaleHeight;
}
else {
scaleHeight = 2;
scaleY = height - scaleHeight;
}
cc.fillRect(pix, scaleY, 1, scaleHeight);
}
counter++;
}
container.appendChild(fragment);
};
TimeScale.prototype.onTrackScroll = function() {
var scroll = this.config.getTrackScroll(),
scrollX = scroll.left;
if (scrollX !== this.prevScrollPos) {
this.prevScrollPos = scrollX;
this.drawScale(scrollX);
}
};
TimeScale.prototype.onResolutionChange = function() {
var scroll = this.config.getTrackScroll(),
scrollX = scroll.left;
this.drawScale(scrollX);
};

View File

@ -0,0 +1,860 @@
'use strict';
var TrackEditor = function() {
};
TrackEditor.prototype.classes = {
"cursor": [
"state-select"
],
"select": [
"state-select"
],
"fadein": [
"state-select"
],
"fadeout": [
"state-select"
],
"shift": [
"state-shift"
],
"active": [
"active"
],
"disabled": [
"disabled"
]
};
TrackEditor.prototype.events = {
"cursor": {
"mousedown": "selectCursorPos"
},
"select": {
"mousedown": "selectStart"
},
"fadein": {
"mousedown": "selectFadeIn"
},
"fadeout": {
"mousedown": "selectFadeOut"
},
"shift": {
"mousedown": "timeShift"
}
};
TrackEditor.prototype.setConfig = function(config) {
this.config = config;
};
TrackEditor.prototype.setWidth = function(width) {
this.width = width;
};
TrackEditor.prototype.init = function(src, start, end, fades, cues, stateConfig) {
var statesEnabled = {
'cursor': true,
'fadein': true,
'fadeout': true,
'select': true,
'shift': true
};
//extend enabled states config.
Object.keys(statesEnabled).forEach(function (key) {
statesEnabled[key] = (key in stateConfig) ? stateConfig[key] : statesEnabled[key];
});
this.enabledStates = statesEnabled;
makePublisher(this);
this.container = document.createElement("div");
this.drawer = new WaveformDrawer();
this.drawer.init(this.container, this.config);
this.playout = new AudioPlayout();
this.playout.init(this.config);
this.sampleRate = this.config.getSampleRate();
this.resolution = this.config.getResolution();
//value is a float in seconds
this.startTime = start || 0;
//value is a float in seconds
this.endTime = end || 0; //set properly in onTrackLoad.
this.leftOffset = this.secondsToSamples(this.startTime); //value is measured in samples.
this.prevStateEvents = {};
this.setState(this.config.getState());
this.fades = {};
if (fades !== undefined && fades.length > 0) {
for (var i = 0; i < fades.length; i++) {
this.fades[this.getFadeId()] = fades[i];
}
}
if (cues.cuein !== undefined) {
this.setCuePoints(this.secondsToSamples(cues.cuein), this.secondsToSamples(cues.cueout));
}
this.selectedArea = undefined; //selected area of track stored as inclusive buffer indices to the audio buffer.
this.active = false;
this.container.classList.add("channel-wrapper");
this.container.style.left = this.leftOffset;
this.drawer.drawLoading();
return this.container;
};
TrackEditor.prototype.getFadeId = function() {
var id = ""+Math.random();
return id.replace(".", "");
};
TrackEditor.prototype.getBuffer = function() {
return this.playout.getBuffer();
};
TrackEditor.prototype.setBuffer = function(buffer) {
this.playout.setBuffer(buffer);
};
TrackEditor.prototype.loadTrack = function(track) {
var el;
el = this.init(
track.src,
track.start,
track.end,
track.fades,
{
cuein: track.cuein,
cueout: track.cueout
},
track.states || {}
);
this.loadBuffer(track.src);
return el;
};
/**
* Loads an audio file via XHR.
*/
TrackEditor.prototype.loadBuffer = function(src) {
var that = this,
xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.addEventListener('progress', function(e) {
var percentComplete;
if (e.lengthComputable) {
percentComplete = e.loaded / e.total * 100;
that.drawer.updateLoader(percentComplete);
}
}, false);
xhr.addEventListener('load', function(e) {
that.src = src;
that.drawer.setLoaderState("decoding");
that.playout.loadData(
e.target.response,
that.onTrackLoad.bind(that)
);
}, false);
xhr.open('GET', src, true);
xhr.send();
};
TrackEditor.prototype.drawTrack = function(buffer) {
this.drawer.drawBuffer(buffer, this.getPixelOffset(this.leftOffset), this.cues);
this.drawer.drawFades(this.fades);
};
TrackEditor.prototype.onTrackLoad = function(buffer) {
var res;
if (this.cues === undefined) {
this.setCuePoints(0, buffer.length - 1);
}
//adjust if the length was inaccurate and cueout is set to a higher sample than we actually have.
else if (this.cues.cueout > (buffer.length - 1)) {
this.cues.cueout = buffer.length - 1;
}
if (this.width !== undefined) {
res = Math.ceil(buffer.length / this.width);
this.config.setResolution(res);
this.resolution = res;
}
this.drawTrack(buffer);
};
TrackEditor.prototype.samplesToSeconds = function(samples) {
return samples / this.sampleRate;
};
TrackEditor.prototype.secondsToSamples = function(seconds) {
return Math.ceil(seconds * this.sampleRate);
};
TrackEditor.prototype.samplesToPixels = function(samples) {
return ~~(samples / this.resolution);
};
TrackEditor.prototype.pixelsToSamples = function(pixels) {
return ~~(pixels * this.resolution);
};
TrackEditor.prototype.pixelsToSeconds = function(pixels) {
return pixels * this.resolution / this.sampleRate;
};
TrackEditor.prototype.secondsToPixels = function(seconds) {
return ~~(seconds * this.sampleRate / this.resolution);
};
TrackEditor.prototype.getPixelOffset = function() {
return this.leftOffset / this.resolution;
};
TrackEditor.prototype.activate = function() {
this.active = true;
this.container.classList.add("active");
};
TrackEditor.prototype.deactivate = function() {
this.active = false;
this.selectedArea = undefined;
this.container.classList.remove("active");
this.updateEditor(-1, undefined, undefined, true);
};
/* start of state methods */
TrackEditor.prototype.timeShift = function(e) {
var el = e.currentTarget, //want the events placed on the channel wrapper.
startX = e.pageX,
diffX = 0,
origX = 0,
updatedX = 0,
editor = this,
res = editor.resolution,
scroll = this.config.getTrackScroll(),
scrollX = scroll.left;
origX = editor.leftOffset / res;
//dynamically put an event on the element.
el.onmousemove = function(e) {
var endX = e.pageX;
diffX = endX - startX;
updatedX = origX + diffX;
editor.drawer.setTimeShift(updatedX);
editor.leftOffset = editor.pixelsToSamples(updatedX);
};
el.onmouseup = function() {
var delta;
el.onmousemove = el.onmouseup = null;
editor.leftOffset = editor.pixelsToSamples(updatedX);
delta = editor.pixelsToSeconds(diffX);
//update track's start and end time relative to the playlist.
editor.startTime = editor.startTime + delta;
editor.endTime = editor.endTime + delta;
};
};
/*
startTime, endTime in seconds.
*/
TrackEditor.prototype.notifySelectUpdate = function(startTime, endTime) {
this.fire('changecursor', {
start: startTime,
end: endTime,
editor: this
});
};
TrackEditor.prototype.getSelectedPlayTime = function() {
var selected = this.selectedArea,
offset = this.leftOffset,
start = this.samplesToSeconds(offset + selected.start),
end = this.samplesToSeconds(offset + selected.end);
return {
startTime: start,
endTime: end
}
};
TrackEditor.prototype.getSelectedArea = function() {
return this.selectedArea;
};
/*
start, end in samples. (relative to cuein/cueout)
*/
TrackEditor.prototype.adjustSelectedArea = function(start, end) {
var buffer = this.getBuffer(),
cues = this.cues;
if (start === undefined || start < 0) {
start = 0;
}
if (end === undefined) {
end = cues.cueout - cues.cuein;
}
if (end > buffer.length - 1) {
end = buffer.length - 1;
}
return {
start: start,
end: end
};
};
/*
start, end in pixels
*/
TrackEditor.prototype.setSelectedArea = function(start, end, shiftKey) {
var left,
right,
currentStart,
currentEnd,
sampLeft,
sampRight,
buffer = this.getBuffer();
//extending selected area since shift is pressed.
if (shiftKey && (end - start === 0) && (this.prevSelectedArea !== undefined)) {
currentStart = this.samplesToPixels(this.prevSelectedArea.start);
currentEnd = this.samplesToPixels(this.prevSelectedArea.end);
if (start < currentStart) {
left = start;
right = currentEnd;
}
else if (end > currentEnd) {
left = currentStart;
right = end;
}
//it's ambigous otherwise, cut off the smaller duration.
else {
if ((start - currentStart) < (currentEnd - start)) {
left = start;
right = currentEnd;
}
else {
left = currentStart;
right = end;
}
}
}
else {
left = start;
right = end;
}
sampLeft = left === undefined ? undefined : this.pixelsToSamples(left);
sampRight = right === undefined ? undefined : this.pixelsToSamples(right);
this.prevSelectedArea = this.selectedArea;
this.selectedArea = this.adjustSelectedArea(sampLeft, sampRight);
};
TrackEditor.prototype.activateAudioSelection = function() {
this.fire("activateSelection");
};
TrackEditor.prototype.deactivateAudioSelection = function() {
this.fire("deactivateSelection");
};
TrackEditor.prototype.findLayerOffset = function(e) {
var layerOffset = 0,
parent;
if (e.target.tagName !== "CANVAS") {
layerOffset = -1;
}
else {
//have to check if a fade canvas was selected. (Must add left offset)
parent = e.target.parentNode;
if (parent.classList.contains('playlist-fade')) {
layerOffset = parent.offsetLeft;
}
}
return layerOffset;
};
TrackEditor.prototype.selectStart = function(e) {
var el = e.currentTarget, //want the events placed on the channel wrapper.
editor = this,
startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
prevX = e.layerX || e.offsetX,
offset = this.leftOffset,
startTime,
layerOffset;
layerOffset = this.findLayerOffset(e);
if (layerOffset < 0) {
return;
}
startX = startX + layerOffset;
prevX = prevX + layerOffset;
editor.setSelectedArea(startX, startX);
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
editor.updateEditor(-1, undefined, undefined, true);
editor.notifySelectUpdate(startTime, startTime);
//dynamically put an event on the element.
el.onmousemove = function(e) {
var currentX = layerOffset + (e.layerX || e.offsetX),
delta = currentX - prevX,
minX = Math.min(prevX, currentX, startX),
maxX = Math.max(prevX, currentX, startX),
selectStart,
selectEnd,
startTime, endTime;
if (currentX > startX) {
selectStart = startX;
selectEnd = currentX;
}
else {
selectStart = currentX;
selectEnd = startX;
}
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
endTime = editor.samplesToSeconds(offset + editor.selectedArea.end);
editor.setSelectedArea(selectStart, selectEnd);
editor.updateEditor(-1, undefined, undefined, true);
editor.notifySelectUpdate(startTime, endTime);
prevX = currentX;
};
el.onmouseup = function(e) {
var endX = layerOffset + (e.layerX || e.offsetX),
minX, maxX,
startTime, endTime;
minX = Math.min(startX, endX);
maxX = Math.max(startX, endX);
editor.setSelectedArea(minX, maxX, e.shiftKey);
minX = editor.samplesToPixels(offset + editor.selectedArea.start);
maxX = editor.samplesToPixels(offset + editor.selectedArea.end);
el.onmousemove = el.onmouseup = null;
//if more than one pixel is selected, listen to possible fade events.
if (Math.abs(minX - maxX)) {
editor.activateAudioSelection();
}
else {
editor.deactivateAudioSelection();
}
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
endTime = editor.samplesToSeconds(offset + editor.selectedArea.end);
editor.updateEditor(-1, undefined, undefined, true);
editor.config.setCursorPos(startTime);
editor.notifySelectUpdate(startTime, endTime);
};
};
TrackEditor.prototype.selectCursorPos = function(e) {
var editor = this,
startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
offset = this.leftOffset,
startTime,
endTime,
layerOffset;
layerOffset = this.findLayerOffset(e);
if (layerOffset < 0) {
return;
}
startX = startX + layerOffset;
editor.setSelectedArea(startX, startX);
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
endTime = editor.samplesToSeconds(offset + editor.selectedArea.end);
editor.updateEditor(-1, undefined, undefined, true);
editor.config.setCursorPos(startTime);
editor.notifySelectUpdate(startTime, endTime);
editor.deactivateAudioSelection();
};
TrackEditor.prototype.selectFadeIn = function(e) {
var startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
layerOffset,
FADETYPE = "FadeIn",
shape = this.config.getFadeType();
layerOffset = this.findLayerOffset(e);
if (layerOffset < 0) {
return;
}
startX = startX + layerOffset;
this.setSelectedArea(undefined, startX);
this.removeFadeType(FADETYPE);
this.createFade(FADETYPE, shape);
};
TrackEditor.prototype.selectFadeOut = function(e) {
var startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
layerOffset,
FADETYPE = "FadeOut",
shape = this.config.getFadeType();
layerOffset = this.findLayerOffset(e);
if (layerOffset < 0) {
return;
}
startX = startX + layerOffset;
this.setSelectedArea(startX, undefined);
this.removeFadeType(FADETYPE);
this.createFade(FADETYPE, shape);
};
/* end of state methods */
TrackEditor.prototype.saveFade = function(id, type, shape, start, end) {
this.fades[id] = {
type: type,
shape: shape,
start: start,
end: end
};
return id;
};
TrackEditor.prototype.removeFade = function(id) {
delete this.fades[id];
this.drawer.removeFade(id);
};
TrackEditor.prototype.removeFadeType = function(type) {
var id,
fades = this.fades,
fade;
for (id in fades) {
fade = fades[id];
if (fade.type === type) {
this.removeFade(id);
}
}
};
/*
Cue points are stored internally in the editor as sample indices for highest precision.
sample at index cueout is not included.
*/
TrackEditor.prototype.setCuePoints = function(cuein, cueout) {
var offset = this.cues ? this.cues.cuein : 0;
this.cues = {
cuein: offset + cuein,
cueout: offset + cueout
};
this.duration = (cueout - cuein) / this.sampleRate;
this.endTime = this.duration + this.startTime;
};
/*
Will remove all audio samples from the track's buffer except for the currently selected area.
Used to set cuein / cueout points in the audio.
start, end are indices into the audio buffer and are inclusive.
*/
TrackEditor.prototype.trim = function(start, end) {
this.setCuePoints(start, end+1);
this.resetCursor();
this.drawTrack(this.getBuffer());
};
/*
Will remove all audio samples from the track's buffer in the currently selected area.
start, end are indices into the audio buffer and are inclusive.
*/
TrackEditor.prototype.removeAudio = function(start, end) {
};
TrackEditor.prototype.onTrackEdit = function(event) {
var type = event.type,
method = "on" + type.charAt(0).toUpperCase() + type.slice(1);
if (this.active === true) {
this[method].call(this, event.args);
}
};
TrackEditor.prototype.createFade = function(type, shape) {
var selected = this.selectedArea,
start = this.samplesToPixels(selected.start),
end = this.samplesToPixels(selected.end),
startTime = this.samplesToSeconds(selected.start),
endTime = this.samplesToSeconds(selected.end),
id = this.getFadeId();
this.resetCursor();
this.saveFade(id, type, shape, startTime, endTime);
this.updateEditor(-1, undefined, undefined, true);
this.drawer.drawFade(id, type, shape, start, end);
};
TrackEditor.prototype.onCreateFade = function(args) {
this.createFade(args.type, args.shape);
this.deactivateAudioSelection();
};
TrackEditor.prototype.onZeroCrossing = function() {
var selected = this.getSelectedArea(),
startTime,
endTime,
offset = this.leftOffset;
this.selectedArea = this.findNearestZeroCrossing(selected.start, selected.end);
startTime = this.samplesToSeconds(offset + this.selectedArea.start);
endTime = this.samplesToSeconds(offset + this.selectedArea.end);
this.notifySelectUpdate(startTime, endTime);
this.updateEditor(-1, undefined, undefined, true);
};
TrackEditor.prototype.onTrimAudio = function() {
var selected = this.getSelectedArea();
this.trim(selected.start, selected.end);
this.deactivateAudioSelection();
};
TrackEditor.prototype.onRemoveAudio = function() {
var selected = this.getSelectedArea();
this.removeAudio(selected.start, selected.end);
this.deactivateAudioSelection();
};
TrackEditor.prototype.setState = function(state) {
var that = this,
stateEvents = this.events[state],
stateClasses = this.classes[state],
disabledClasses = this.classes['disabled'],
enabledStates = this.enabledStates,
container = this.container,
prevState = this.currentState,
prevStateClasses,
prevStateEvents = this.prevStateEvents,
func, event, cl,
i, len;
if (prevState) {
prevStateClasses = this.classes[prevState];
if (enabledStates[prevState] === true) {
for (event in prevStateEvents) {
container.removeEventListener(event, prevStateEvents[event]);
}
this.prevStateEvents = {};
for (i = 0, len = prevStateClasses.length; i < len; i++) {
container.classList.remove(prevStateClasses[i]);
}
}
else {
for (i = 0, len = disabledClasses.length; i < len; i++) {
container.classList.remove(disabledClasses[i]);
}
}
}
if (enabledStates[state] === true) {
for (event in stateEvents) {
func = that[stateEvents[event]].bind(that);
//need to keep track of the added events for later removal since a new function is returned after using "bind"
this.prevStateEvents[event] = func;
container.addEventListener(event, func);
}
for (i = 0, len = stateClasses.length; i < len; i++) {
container.classList.add(stateClasses[i]);
}
}
else {
for (i = 0, len = disabledClasses.length; i < len; i++) {
container.classList.add(disabledClasses[i]);
}
}
this.currentState = state;
};
TrackEditor.prototype.onResolutionChange = function(res) {
var selected = this.selectedArea;
this.resolution = res;
this.drawTrack(this.getBuffer());
if (this.active === true && this.selectedArea !== undefined) {
this.updateEditor(-1, this.samplesToPixels(selected.start), this.samplesToPixels(selected.end), true);
}
};
TrackEditor.prototype.isPlaying = function() {
return this.playout.isScheduled() || this.playout.isPlaying();
};
/*
startTime, endTime in seconds (float).
*/
TrackEditor.prototype.schedulePlay = function(now, delay, startTime, endTime) {
var start,
duration,
relPos,
when = now + delay,
window = (endTime) ? (endTime - startTime) : undefined,
cueOffset = this.cues.cuein / this.sampleRate;
//track has no content to play.
if (this.endTime <= startTime) return;
//track does not start in this selection.
if (window && (startTime + window) < this.startTime) return;
//track should have something to play if it gets here.
//the track starts in the future of the cursor position
if (this.startTime >= startTime) {
start = 0;
when = when + this.startTime - startTime; //schedule additional delay for this audio node.
window = window - (this.startTime - startTime);
duration = (endTime) ? Math.min(window, this.duration) : this.duration;
}
else {
start = startTime - this.startTime;
duration = (endTime) ? Math.min(window, this.duration - start) : this.duration - start;
}
start = start + cueOffset;
relPos = startTime - this.startTime;
this.playout.applyFades(this.fades, relPos, now, delay);
this.playout.play(when, start, duration);
};
TrackEditor.prototype.scheduleStop = function(when) {
this.playout.stop(when);
};
TrackEditor.prototype.resetCursor = function() {
this.selectedArea = undefined;
this.config.setCursorPos(0);
this.notifySelectUpdate(0, 0);
};
TrackEditor.prototype.updateEditor = function(cursorPos, start, end, highlighted) {
var pixelOffset = this.getPixelOffset(),
selected;
if (this.selectedArea) {
//must pass selected area in pixels.
selected = {
start: this.samplesToPixels(this.selectedArea.start),
end: this.samplesToPixels(this.selectedArea.end)
};
}
this.drawer.updateEditor(cursorPos, pixelOffset, start, end, highlighted, selected);
};
TrackEditor.prototype.getTrackDetails = function() {
var d,
cues = this.cues,
fades = [],
id;
for (id in this.fades) {
fades.push(this.fades[id]);
}
d = {
start: this.startTime,
end: this.endTime,
fades: fades,
src: this.src,
cuein: this.samplesToSeconds(cues.cuein),
cueout: this.samplesToSeconds(cues.cueout)
};
return d;
};

View File

@ -0,0 +1,450 @@
'use strict';
var WaveformDrawer = function() {
};
WaveformDrawer.prototype.init = function(container, config) {
makePublisher(this);
this.config = config;
this.container = container;
this.channels = []; //array of canvases, contexts, 1 for each channel displayed.
var theme = this.config.getUITheme();
if (this.loaderStates[theme] !== undefined) {
this.loaderStates = this.loaderStates[theme];
}
else {
this.loaderStates = this.loaderStates["default"];
}
};
WaveformDrawer.prototype.loaderStates = {
"bootstrap": {
"downloading": "progress progress-warning",
"decoding": "progress progress-success progress-striped active",
"loader": "bar"
},
"jQueryUI": {
"downloading": "ui-progressbar ui-widget ui-widget-content ui-corner-all",
"decoding": "ui-progressbar ui-widget ui-widget-content ui-corner-all",
"loader": "ui-progressbar-value ui-widget-header ui-corner-left"
},
"default": {
"downloading": "progress",
"decoding": "decoding",
"loader": "bar"
}
};
WaveformDrawer.prototype.getPeaks = function(buffer, cues) {
// Frames per pixel
var res = this.config.getResolution(),
peaks = [],
i, c, p, l,
chanLength = cues.cueout - cues.cuein,
pixels = Math.ceil(chanLength / res),
numChan = buffer.numberOfChannels,
weight = 1 / (numChan),
makeMono = this.config.isDisplayMono(),
chan,
start,
end,
vals,
max,
min,
maxPeak = -Infinity; //used to scale the waveform on the canvas.
for (i = 0; i < pixels; i++) {
peaks[i] = [];
for (c = 0; c < numChan; c++) {
chan = buffer.getChannelData(c);
chan = chan.subarray(cues.cuein, cues.cueout);
start = i * res;
end = (i + 1) * res > chanLength ? chanLength : (i + 1) * res;
vals = chan.subarray(start, end);
max = -Infinity;
min = Infinity;
for (p = 0, l = vals.length; p < l; p++) {
if (vals[p] > max){
max = vals[p];
}
if (vals[p] < min){
min = vals[p];
}
}
peaks[i].push({max:max, min:min});
maxPeak = Math.max.apply(Math, [maxPeak, Math.abs(max), Math.abs(min)]);
}
if (makeMono) {
max = min = 0;
for (c = 0 ; c < numChan; c++) {
max = max + weight * peaks[i][c].max;
min = min + weight * peaks[i][c].min;
}
peaks[i] = []; //need to clear out old stuff (maybe we should keep it for toggling views?).
peaks[i].push({max:max, min:min});
}
}
this.maxPeak = maxPeak;
this.peaks = peaks;
};
WaveformDrawer.prototype.setTimeShift = function(pixels) {
var i, len;
for (i = 0, len = this.channels.length; i < len; i++) {
this.channels[i].div.style.left = pixels+"px";
}
};
WaveformDrawer.prototype.updateLoader = function(percent) {
this.loader.style.width = percent+"%";
};
WaveformDrawer.prototype.setLoaderState = function(state) {
this.progressDiv.className = this.loaderStates[state];
};
WaveformDrawer.prototype.drawLoading = function() {
var div,
loader;
this.height = this.config.getWaveHeight();
div = document.createElement("div");
div.style.height = this.height+"px";
loader = document.createElement("div");
loader.style.height = "10px";
loader.className = this.loaderStates["loader"];
div.appendChild(loader);
this.progressDiv = div;
this.loader = loader;
this.setLoaderState("downloading");
this.updateLoader(0);
this.container.appendChild(div);
};
WaveformDrawer.prototype.drawBuffer = function(buffer, pixelOffset, cues) {
var canv,
div,
i,
top = 0,
left = 0,
makeMono = this.config.isDisplayMono(),
res = this.config.getResolution(),
numChan = makeMono? 1 : buffer.numberOfChannels,
numSamples = cues.cueout - cues.cuein + 1,
fragment = document.createDocumentFragment(),
wrapperHeight;
this.container.innerHTML = "";
this.channels = [];
//width and height is per waveform canvas.
this.width = Math.ceil(numSamples / res);
this.height = this.config.getWaveHeight();
for (i = 0; i < numChan; i++) {
div = document.createElement("div");
div.classList.add("channel");
div.classList.add("channel-"+i);
div.style.width = this.width+"px";
div.style.height = this.height+"px";
div.style.top = top+"px";
div.style.left = left+"px";
canv = document.createElement("canvas");
canv.setAttribute('width', this.width);
canv.setAttribute('height', this.height);
this.channels.push({
canvas: canv,
context: canv.getContext('2d'),
div: div
});
div.appendChild(canv);
fragment.appendChild(div);
top = top + this.height;
}
wrapperHeight = numChan * this.height;
this.container.style.height = wrapperHeight+"px";
this.container.appendChild(fragment);
this.getPeaks(buffer, cues);
this.updateEditor();
this.setTimeShift(pixelOffset);
};
WaveformDrawer.prototype.drawFrame = function(chanNum, index, peaks, maxPeak, cursorPos, pixelOffset) {
var x, y, w, h, max, min,
h2 = this.height / 2,
cc = this.channels[chanNum].context,
colors = this.config.getColorScheme();
max = (peaks.max / maxPeak) * h2;
min = (peaks.min / maxPeak) * h2;
w = 1;
x = index * w;
y = Math.round(h2 - max);
h = Math.ceil(max - min);
//to prevent blank space when there is basically silence in the track.
h = h === 0 ? 1 : h;
if (cursorPos >= (x + pixelOffset)) {
cc.fillStyle = colors.progressColor;
}
else {
cc.fillStyle = colors.waveColor;
}
cc.fillRect(x, y, w, h);
};
/*
start, end are optional parameters to only redraw part of the canvas.
*/
WaveformDrawer.prototype.draw = function(cursorPos, pixelOffset, start, end) {
var that = this,
peaks = this.peaks,
i = (start) ? start - pixelOffset : 0,
len = (end) ? end - pixelOffset + 1 : peaks.length;
if (i < 0 && len < 0) {
return;
}
if (i < 0) {
i = 0;
}
if (len > peaks.length) {
len = peaks.length;
}
this.clear(i, len);
for (; i < len; i++) {
peaks[i].forEach(function(peak, chanNum) {
that.drawFrame(chanNum, i, peak, that.maxPeak, cursorPos, pixelOffset);
});
}
};
/*
If start/end are set clear only part of the canvas.
*/
WaveformDrawer.prototype.clear = function(start, end) {
var i, len,
width = end - start;
for (i = 0, len = this.channels.length; i < len; i++) {
this.channels[i].context.clearRect(start, 0, width, this.height);
}
};
WaveformDrawer.prototype.updateEditor = function(cursorPos, pixelOffset, start, end, highlighted, selected) {
var i, len,
fragment = document.createDocumentFragment();
this.container.innerHTML = "";
this.draw(cursorPos, pixelOffset, start, end);
if (highlighted === true && selected !== undefined) {
var border = (selected.end - selected.start === 0) ? true : false;
this.drawHighlight(selected.start, selected.end, border);
}
for (i = 0, len = this.channels.length; i < len; i++) {
fragment.appendChild(this.channels[i].div);
}
this.container.appendChild(fragment);
};
/*
start, end in pixels.
*/
WaveformDrawer.prototype.drawHighlight = function(start, end, isBorder) {
var i, len,
colors = this.config.getColorScheme(),
fillStyle,
ctx,
width = end - start + 1;
fillStyle = (isBorder) ? colors.selectBorderColor : colors.selectBackgroundColor;
for (i = 0, len = this.channels.length; i < len; i++) {
ctx = this.channels[i].context;
ctx.fillStyle = fillStyle;
ctx.fillRect(start, 0, width, this.height);
}
};
WaveformDrawer.prototype.sCurveFadeIn = function sCurveFadeIn(ctx, width) {
return Curves.createSCurveBuffer(width, (Math.PI/2));
};
WaveformDrawer.prototype.sCurveFadeOut = function sCurveFadeOut(ctx, width) {
return Curves.createSCurveBuffer(width, -(Math.PI/2));
};
WaveformDrawer.prototype.logarithmicFadeIn = function logarithmicFadeIn(ctx, width) {
return Curves.createLogarithmicBuffer(width, 10, 1);
};
WaveformDrawer.prototype.logarithmicFadeOut = function logarithmicFadeOut(ctx, width) {
return Curves.createLogarithmicBuffer(width, 10, -1);
};
WaveformDrawer.prototype.exponentialFadeIn = function exponentialFadeIn(ctx, width) {
return Curves.createExponentialBuffer(width, 1);
};
WaveformDrawer.prototype.exponentialFadeOut = function exponentialFadeOut(ctx, width) {
return Curves.createExponentialBuffer(width, -1);
};
WaveformDrawer.prototype.linearFadeIn = function linearFadeIn(ctx, width) {
return Curves.createLinearBuffer(width, 1);
};
WaveformDrawer.prototype.linearFadeOut = function linearFadeOut(ctx, width) {
return Curves.createLinearBuffer(width, -1);
};
WaveformDrawer.prototype.drawFadeCurve = function(ctx, shape, type, width) {
var method = shape+type,
fn = this[method],
colors = this.config.getColorScheme(),
curve,
i, len,
cHeight = this.height,
y;
ctx.strokeStyle = colors.fadeColor;
curve = fn.call(this, ctx, width);
y = cHeight - curve[0] * cHeight;
ctx.beginPath();
ctx.moveTo(0, y);
for (i = 1, len = curve.length; i < len; i++) {
y = cHeight - curve[i] * cHeight;
ctx.lineTo(i, y);
}
ctx.stroke();
};
WaveformDrawer.prototype.removeFade = function(id) {
var fadeClass = "playlist-fade-"+id,
el, els,
i,len;
els = this.container.getElementsByClassName(fadeClass);
len = els.length;
//DOM NodeList is live, use a decrementing counter.
if (len > 0) {
for (i = len-1; i >= 0; i--) {
el = els[i];
el.parentNode.removeChild(el);
}
}
};
WaveformDrawer.prototype.drawFade = function(id, type, shape, start, end) {
var div,
canv,
width,
left,
fragment = document.createDocumentFragment(),
i, len,
dup,
ctx,
tmpCtx;
if ((end - start) === 0) {
return;
}
width = ~~(end - start + 1);
left = start;
div = document.createElement("div");
div.classList.add("playlist-fade");
div.classList.add("playlist-fade-"+id);
div.style.width = width+"px";
div.style.height = this.height+"px";
div.style.top = 0;
div.style.left = left+"px";
canv = document.createElement("canvas");
canv.setAttribute('width', width);
canv.setAttribute('height', this.height);
ctx = canv.getContext('2d');
this.drawFadeCurve(ctx, shape, type, width);
div.appendChild(canv);
fragment.appendChild(div);
for (i = 0, len = this.channels.length; i < len; i++) {
dup = fragment.cloneNode(true);
tmpCtx = dup.querySelector('canvas').getContext('2d');
tmpCtx.drawImage(canv, 0, 0);
this.channels[i].div.appendChild(dup);
}
};
WaveformDrawer.prototype.drawFades = function(fades) {
var id,
fade,
startPix,
endPix,
SR = this.config.getSampleRate(),
res = this.config.getResolution();
for (id in fades) {
fade = fades[id];
if (fades.hasOwnProperty(id)) {
startPix = fade.start * SR / res;
endPix = fade.end * SR / res;
this.drawFade(id, fade.type, fade.shape, startPix, endPix);
}
}
};