Merge branch 'player' of Macaw/radio into master

What do i write to myself?

Now system of player supposed to work.
Lorgar request parts from Corax, decodes with emscripten ported libmad and plays with AudioContext.
Queue is virtual - you can travel with prev and next buttons but can't see it yet.
Seeking, volume, album cover, Artist Album Title - everything seems to work for now
This commit is contained in:
Blue 2019-02-01 22:32:51 +00:00 committed by Gitea
commit 05b7a33a2a
91 changed files with 5047 additions and 272 deletions

View File

@ -23,6 +23,7 @@ set(SOURCES
)
add_executable(corax ${HEADERS} ${SOURCES})
add_subdirectory(models)
target_link_libraries(corax Qt5::Core)
target_link_libraries(corax Qt5::Network)
@ -31,10 +32,10 @@ target_link_libraries(corax wSocket)
target_link_libraries(corax wDispatcher)
target_link_libraries(corax utils)
target_link_libraries(corax wModel)
target_link_libraries(corax wController)
target_link_libraries(corax wServerUtils)
target_link_libraries(corax wDatabase)
target_link_libraries(corax tag)
target_link_libraries(corax tools)
target_link_libraries(corax coraxModels)
target_link_libraries(corax wServerUtils)
install(TARGETS corax RUNTIME DESTINATION bin)

View File

@ -9,6 +9,7 @@ Corax* Corax::corax = 0;
Corax::Corax(QObject *parent):
QObject(parent),
started(false),
server(new W::Server(W::String(u"Corax"), this)),
logger(new W::Logger()),
parentReporter(new W::ParentReporter()),
@ -17,7 +18,8 @@ Corax::Corax(QObject *parent):
connector(0),
dispatcher(new W::Dispatcher()),
caches(),
parsers()
parsers(),
players()
{
if (corax != 0)
{
@ -41,7 +43,8 @@ Corax::Corax(QObject *parent):
attributes->addAttribute(W::String(u"connectionsCount"), new M::String(W::String(u"0"), W::Address({u"attributes", u"connectionCount"})));
attributes->addAttribute(W::String(u"name"), new M::String(W::String(u"Corax"), W::Address({u"attributes", u"name"})));
attributes->addAttribute(W::String(u"version"), new M::String(W::String(u"0.0.2"), W::Address({u"attributes", u"version"})));
attributes->addAttribute(W::String(u"version"), new M::String(W::String(u"0.0.3"), W::Address({u"attributes", u"version"})));
attributes->addAttribute(W::String(u"players"), new M::String(W::String(u"0"), W::Address({u"attributes", u"players"})));
createCaches();
createHandlers();
@ -49,20 +52,27 @@ Corax::Corax(QObject *parent):
Corax::~Corax()
{
std::map<W::String, Parser*>::iterator pbeg = parsers.begin();
std::map<W::String, Parser*>::iterator pend = parsers.end();
std::map<W::String, Parser*>::const_iterator pbeg = parsers.begin();
std::map<W::String, Parser*>::const_iterator pend = parsers.end();
for (; pbeg != pend; ++pbeg) {
delete pbeg->second;
}
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
std::map<W::String, ResourceCache*>::const_iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::const_iterator end = caches.end();
for (; beg != end; ++beg) {
delete beg->second;
}
std::map<W::Address, M::Player*>::const_iterator plit = players.begin();
std::map<W::Address, M::Player*>::const_iterator plend = players.end();
for (; plit != plend; ++plit) {
delete plit->second;
}
delete connector;
dispatcher->unregisterDefaultHandler(logger);
@ -83,6 +93,9 @@ void Corax::onConnectionCountChanged(uint64_t count)
void Corax::start()
{
if (started) {
throw 3;
}
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
@ -90,11 +103,18 @@ void Corax::start()
server->listen(8080);
cout << "Registering models..." << endl;
attributes->registerModel(dispatcher, server);
commands->registerModel(dispatcher, server);
attributes->getRegistered(connector);
commands->getRegistered(connector);
for (; beg != end; ++beg) {
beg->second->registerModel(dispatcher, server);
beg->second->getRegistered(connector);
}
std::map<W::Address, M::Player*>::const_iterator plit = players.begin();
std::map<W::Address, M::Player*>::const_iterator plend = players.end();
for (; plit != plend; ++plit) {
plit->second->getRegistered(connector);;
}
cout << "Opening caches..." << endl;
@ -105,24 +125,40 @@ void Corax::start()
}
commands->enableCommand(W::String(u"clearCache"), true);
commands->enableCommand(W::String(u"givePlayer"), true);
started = true;
cout << "Corax is ready" << endl;
}
void Corax::stop()
{
if (!started) {
throw 3;
}
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
cout << "Stopping corax..." << endl;
commands->unregisterModel();
attributes->unregisterModel();
commands->getUnregistered();
attributes->getUnregistered();
for (; beg != end; ++beg) {
beg->second->unregisterModel();
beg->second->getUnregistered();
}
std::map<W::Address, M::Player*>::const_iterator plit = players.begin();
std::map<W::Address, M::Player*>::const_iterator plend = players.end();
for (; plit != plend; ++plit) {
plit->second->getUnregistered();
}
server->stop();
started = false;
cout << "Corax is stopped" << endl;
}
void Corax::onModelServiceMessage(const QString& msg)
@ -169,10 +205,10 @@ void Corax::h_parseDirectory(const W::Event& ev)
if (itr != parsers.end()) {
cout << "directory " << path.toString() << " is already being parsed" << endl;
} else {
const W::Socket& socket = connector->getNodeSocket(W::String(u"Perturabo"));
const W::Socket* socket = connector->getNodeSocket(W::String(u"Perturabo"));
ResourceCache* music = caches.at(W::String(u"music"));
ResourceCache* images = caches.at(W::String(u"images"));
Parser* parser = new Parser(&socket, dispatcher, music, images);
Parser* parser = new Parser(socket, dispatcher, music, images);
parsers.insert(std::make_pair(path, parser));
connect(parser, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
@ -202,6 +238,9 @@ void Corax::createHandlers()
W::Vocabulary parseArgs;
parseArgs.insert(u"path", W::Uint64(W::Object::string));
commands->addCommand(W::String(u"parseDirectory"), parseDirectory, parseArgs);
W::Handler* givePlayer = W::Handler::create(W::Address({u"management", u"givePlayer"}), this, &Corax::_h_givePlayer);
commands->addCommand(W::String(u"givePlayer"), givePlayer, W::Vocabulary());
}
void Corax::onParserDone(const W::String& path)
@ -234,3 +273,28 @@ void Corax::onNodeDisconnected(const W::String& name)
commands->enableCommand(W::String(u"parseDirectory"), false);
}
}
void Corax::h_givePlayer(const W::Event& ev)
{
W::String num(std::to_string(players.size()));
W::Address addr{u"players", num};
M::Player* pl = new M::Player(addr);
connect(pl, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
players.insert(std::make_pair(addr, pl));
attributes->setAttribute(W::String(u"players"), W::String(std::to_string(players.size())));
pl->getRegistered(connector); //it's a handler, so I assume corax is started here;
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"address", addr);
const W::Vocabulary& svc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Address& source = static_cast<const W::Address&>(svc.at(u"source"));
uint64_t id = ev.getSenderId();
vc->insert(u"source", W::Address{u"management"}); //TODO think about it, may be Corax should be a model?
W::Event res(source + W::Address{u"getPlayer"}, vc);
res.setSenderId(id);
connector->getConnection(id)->send(res);
}

View File

@ -30,6 +30,7 @@
#include <wDatabase/resourcecache.h>
#include "tools/parser.h"
#include "models/player.h"
class Corax: public QObject
{
@ -42,6 +43,7 @@ public:
static Corax* corax;
private:
bool started;
W::Server *server;
W::Logger *logger;
W::ParentReporter* parentReporter;
@ -53,9 +55,11 @@ private:
std::map<W::String, ResourceCache*> caches;
std::map<W::String, Parser*> parsers;
std::map<W::Address, M::Player*> players;
handler(clearCache);
handler(parseDirectory);
handler(givePlayer);
public slots:
void start();

View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 2.8.12)
project(coraxModels)
find_package(Qt5Core REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
player.h
proxysong.h
)
set(SOURCES
player.cpp
proxysong.cpp
)
add_library(coraxModels STATIC ${HEADERS} ${SOURCES})
target_link_libraries(coraxModels wModel)

313
corax/models/player.cpp Normal file
View File

@ -0,0 +1,313 @@
#include "player.h"
M::Player::Player(const W::Address& address, QObject* parent):
M::Model(address, parent),
controls(),
views(),
playPauseBtn(new M::Button(address + W::Address{u"play"})),
nextBtn(new M::Button(address + W::Address{u"next"})),
prevBtn(new M::Button(address + W::Address{u"prev"})),
_queueView(new M::List(address + W::Address{u"queueView"})),
_queue(),
current(0),
counter(0),
currentIndex(0),
mode(playBack),
playing(false),
scheduledToplay(false)
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::Player::_h_get);
W::Handler* hqueue = W::Handler::create(address + W::Address({u"queue"}), this, &M::Player::_h_queue);
W::Handler* hplay = W::Handler::create(address + W::Address({u"play"}), this, &M::Player::_h_play);
addHandler(get);
addHandler(hqueue);
addHandler(hplay);
playPauseBtn->setLabel(W::String(u"Play"));
playPauseBtn->setEnabled(false);
connect(playPauseBtn, SIGNAL(activated()), this, SLOT(onPlayPauseBtn()));
nextBtn->setLabel(W::String(u"Next"));
nextBtn->setEnabled(false);
connect(nextBtn, SIGNAL(activated()), this, SLOT(onNextBtn()));
prevBtn->setLabel(W::String(u"Prev"));
prevBtn->setEnabled(false);
connect(prevBtn, SIGNAL(activated()), this, SLOT(onPrevBtn()));
addModel(playPauseBtn);
addModel(nextBtn);
addModel(prevBtn);
addModel(_queueView);
controls.insert(std::make_pair(playPause, playPauseBtn->getAddress()));
controls.insert(std::make_pair(next, nextBtn->getAddress()));
controls.insert(std::make_pair(prev, prevBtn->getAddress()));
views.insert(std::make_pair(queue, _queueView->getAddress()));
}
M::Player::~Player()
{
}
void M::Player::set(const W::Object& value)
{
throw 14; //what do you expect here? not implemented, and not sure it ever would be
}
void M::Player::set(W::Object* value)
{
set(*value);
}
M::Model::ModelType M::Player::getType() const
{
return M::Model::player;
}
void M::Player::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_get(ev);
}
void M::Player::h_get(const W::Event& ev)
{
W::Vector* ctrls = new W::Vector();
ItemMap::const_iterator citr = controls.begin();
ItemMap::const_iterator cend = controls.end();
for (; citr != cend; ++citr) {
W::Vocabulary* cvc = new W::Vocabulary();
cvc->insert(u"type", new W::Uint64(citr->first));
cvc->insert(u"address", citr->second);
ctrls->push(cvc);
}
W::Vector* vws = new W::Vector();
ItemMap::const_iterator vitr = views.begin();
ItemMap::const_iterator vend = views.end();
for (; vitr != vend; ++vitr) {
W::Vocabulary* vvc = new W::Vocabulary();
vvc->insert(u"type", new W::Uint64(vitr->first));
vvc->insert(u"address", vitr->second);
vws->push(vvc);
}
W::Vocabulary* res = new W::Vocabulary();
res->insert(u"controls", ctrls);
res->insert(u"views", vws);
res->insert(u"mode", new W::Uint64(mode));
response(res, W::Address({u"get"}), ev);
}
void M::Player::onPlayPauseBtn()
{
if (playing) {
pause();
} else {
play();
}
}
void M::Player::h_queue(const W::Event& ev)
{
const W::Vocabulary& data = static_cast<const W::Vocabulary&>(ev.getData());
const W::Uint64& id = static_cast<const W::Uint64&>(data.at(u"id"));
ProxySong* song = new ProxySong(id, address + W::Address{W::String(W::Uint64(counter++).toString())});
addModel(song);
_queue.push_back(song);
_queueView->push(song->getAddress());
if (current == 0) {
setActive(song);
}
if (currentIndex + 1 < _queue.size()) {
nextBtn->setEnabled(true);
}
}
void M::Player::play()
{
if (!playing) {
playPauseBtn->setLabel(W::String(u"Pause"));
playing = true;
switch (mode) {
case playBack:
if (current == 0) {
scheduledToplay = true;
} else {
if (current->isReady()) {
scheduledToplay = false;
broadcast(new W::Vocabulary(), W::Address{u"play"});
break;
} else {
scheduledToplay = true;
}
}
}
}
}
void M::Player::pause()
{
if (playing) {
playPauseBtn->setLabel(W::String(u"Play"));
playing = false;
switch (mode) {
case playBack:
scheduledToplay = false;
broadcast(new W::Vocabulary(), W::Address{u"pause"});
break;
}
}
}
void M::Player::onSongReady()
{
emit serviceMessage("Song is ready");
playPauseBtn->setEnabled(true);
if (scheduledToplay) {
scheduledToplay = false;
if (playing) {
scheduledToplay = false;
broadcast(new W::Vocabulary(), W::Address{u"play"});
} else {
play();
}
}
}
void M::Player::onSongNotReady()
{
playPauseBtn->setEnabled(false);
emit serviceMessage("Something happend to the current song, not sure yet what to do");
}
void M::Player::onNextBtn()
{
if (currentIndex + 1 < _queue.size()) {
if (playing) {
pause();
scheduledToplay = true;
}
setActive(currentIndex + 1);
}
}
void M::Player::onPrevBtn()
{
if (currentIndex > 0) {
if (playing) {
pause();
scheduledToplay = true;
}
setActive(currentIndex - 1);
}
}
void M::Player::setActive(ProxySong* song)
{
if (current == song) {
return;
}
bool found = false;
int index;
for (index = 0; index < _queue.size(); ++index) {
if (_queue.at(index) == song) {
found = true;
break;
}
}
if (found) {
setActive(index);
} else {
emit serviceMessage("An attempt to set active a song which is no in the queue, not supposed to happen");
return;
}
}
void M::Player::setActive(uint64_t index)
{
if (index >= _queue.size()) {
emit serviceMessage("An attempt to set active a song which is no in the queue, not supposed to happen");
return;
}
ProxySong* song = _queue.at(index);
currentIndex = index;
if (currentIndex + 1 < _queue.size()) {
nextBtn->setEnabled(true);
} else {
nextBtn->setEnabled(false);
}
if (currentIndex > 0) {
prevBtn->setEnabled(true);
} else {
prevBtn->setEnabled(false);
}
W::Vocabulary* res = new W::Vocabulary();
W::Vector* add = new W::Vector();
W::Vector* remove = new W::Vector();
if (current != 0) {
disconnect(current, SIGNAL(ready()), this, SLOT(onSongReady()));
disconnect(current, SIGNAL(notReady()), this, SLOT(onSongNotReady()));
remove->push(new W::Uint64(currentPlayback));
}
current = song;
connect(song, SIGNAL(ready()), this, SLOT(onSongReady()));
connect(song, SIGNAL(notReady()), this, SLOT(onSongNotReady()));
views.insert(std::make_pair(currentPlayback, song->getAddress()));
W::Vocabulary* avc = new W::Vocabulary();
avc->insert(u"type", new W::Uint64(currentPlayback));
avc->insert(u"address", song->getAddress());
add->push(avc);
res->insert(u"add", add);
res->insert(u"remove", remove);
broadcast(res, W::Address{u"viewsChange"});
if (song->isReady()) {
playPauseBtn->setEnabled(true);
if (scheduledToplay) {
play();
}
} else {
playPauseBtn->setEnabled(false);
}
}
void M::Player::h_play(const W::Event& ev)
{
const W::Vocabulary& data = static_cast<const W::Vocabulary&>(ev.getData());
const W::Uint64& id = static_cast<const W::Uint64&>(data.at(u"id"));
ProxySong* song = new ProxySong(id, address + W::Address{W::String(W::Uint64(counter++).toString())});
addModel(song);
_queue.push_back(song);
_queueView->push(song->getAddress());
scheduledToplay = true;
setActive(song);
if (currentIndex + 1 < _queue.size()) {
nextBtn->setEnabled(true);
}
}

85
corax/models/player.h Normal file
View File

@ -0,0 +1,85 @@
#ifndef PLAYER_H
#define PLAYER_H
#include <map>
#include <deque>
#include <wModel/model.h>
#include <wModel/button.h>
#include <wModel/modelstring.h>
#include <wModel/list.h>
#include <wType/vocabulary.h>
#include <wType/vector.h>
#include <wType/address.h>
#include "proxysong.h"
/**
* @todo write docs
*/
namespace M {
class Player : public M::Model {
Q_OBJECT
public:
Player(const W::Address& address, QObject* parent = 0);
~Player();
void set(const W::Object & value) override;
void set(W::Object * value) override;
M::Model::ModelType getType() const override;
enum ItemType {
playPause,
currentPlayback,
queue,
picture,
prev,
next
};
enum Mode {
playBack
};
void play();
void pause();
protected:
void h_subscribe(const W::Event & ev) override;
handler(get);
handler(queue);
handler(play);
private:
typedef std::map<ItemType, W::Address> ItemMap;
typedef std::deque<ProxySong*> Queue;
ItemMap controls;
ItemMap views;
M::Button* playPauseBtn;
M::Button* nextBtn;
M::Button* prevBtn;
M::List* _queueView;
Queue _queue;
ProxySong* current;
uint64_t counter;
uint64_t currentIndex;
Mode mode;
bool playing;
bool scheduledToplay;
void setActive(ProxySong* song);
void setActive(uint64_t index);
private slots:
void onPlayPauseBtn();
void onNextBtn();
void onPrevBtn();
void onSongReady();
void onSongNotReady();
};
}
#endif // PLAYER_H

138
corax/models/proxysong.cpp Normal file
View File

@ -0,0 +1,138 @@
#include "proxysong.h"
ProxySong::ProxySong(const W::Uint64& p_id, const W::Address& p_address, QObject* parent):
M::Vocabulary(p_address, parent),
songCtrl(new C::Vocabulary(W::Address{u"songs", W::String(p_id.toString())})),
albumCtrl(0),
artistCtrl(0),
fileId(0),
_ready(false)
{
addController(songCtrl, W::String(u"Perturabo"));
connect(songCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), SLOT(onSongNewElement(const W::String&, const W::Object&)));
connect(songCtrl, SIGNAL(removeElement(const W::String&)), SLOT(onSongRemoveElement(const W::String&)));
insert(W::String(u"id"), p_id);
insert(W::String(u"artist"), new W::String(u"undefined"));
insert(W::String(u"album"), new W::String(u"undefined"));
insert(W::String(u"song"), new W::String(u"undefined"));
insert(W::String(u"image"), new W::Uint64(0));
insert(W::String(u"audio"), new W::Uint64(0));
}
bool ProxySong::isReady() const
{
return _ready;
}
ProxySong::~ProxySong()
{
}
void ProxySong::onSongNewElement(const W::String& key, const W::Object& element)
{
if (key == u"name") {
insert(W::String(u"song"), element);
} else if (key == u"audio") {
if (_ready) {
_ready = false;
emit notReady();
}
fileId = static_cast<const W::Uint64&>(element);
insert(key, element);
_ready = true;
emit ready();
} else if (key == u"artist") {
if (artistCtrl != 0) {
removeController(artistCtrl);
disconnect(artistCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onArtistNewElement(const W::String&, const W::Object&)));
disconnect(artistCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAtristRemoveElement(const W::String&)));
artistCtrl->deleteLater();
}
const W::Uint64& aid = static_cast<const W::Uint64&>(element);
artistCtrl = new C::Vocabulary(W::Address{u"artists", W::String(aid.toString())});
addController(artistCtrl, W::String(u"Perturabo"));
connect(artistCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), SLOT(onArtistNewElement(const W::String&, const W::Object&)));
connect(artistCtrl, SIGNAL(removeElement(const W::String&)), SLOT(onAtristRemoveElement(const W::String&)));
} else if (key == u"album") {
if (albumCtrl != 0) {
removeController(albumCtrl);
disconnect(albumCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onAlbumNewElement(const W::String&, const W::Object&)));
disconnect(albumCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAlbumRemoveElement(const W::String&)));
albumCtrl->deleteLater();
}
const W::Uint64& aid = static_cast<const W::Uint64&>(element);
albumCtrl = new C::Vocabulary(W::Address{u"albums", W::String(aid.toString())});
addController(albumCtrl, W::String(u"Perturabo"));
connect(albumCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), SLOT(onAlbumNewElement(const W::String&, const W::Object&)));
connect(albumCtrl, SIGNAL(removeElement(const W::String&)), SLOT(onAlbumRemoveElement(const W::String&)));
}
}
void ProxySong::onSongRemoveElement(const W::String& key)
{
if (key == u"name") {
insert(key, new W::String(u"undefined"));
} else if (key == u"audio") {
insert(key, new W::Uint64(0));
if (_ready) {
_ready = false;
fileId = W::Uint64(0);
emit notReady();
}
} else if (key == u"artist") {
if (artistCtrl != 0) {
removeController(artistCtrl);
disconnect(artistCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onArtistNewElement(const W::String&, const W::Object&)));
disconnect(artistCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAtristRemoveElement(const W::String&)));
artistCtrl->deleteLater();
artistCtrl = 0;
}
} else if (key == u"album") {
if (albumCtrl != 0) {
removeController(albumCtrl);
disconnect(albumCtrl, SIGNAL(newElement(const W::String&, const W::Object&)), this, SLOT(onAlbumNewElement(const W::String&, const W::Object&)));
disconnect(albumCtrl, SIGNAL(removeElement(const W::String&)), this, SLOT(onAlbumRemoveElement(const W::String&)));
albumCtrl->deleteLater();
albumCtrl = 0;
}
}
}
void ProxySong::onAlbumNewElement(const W::String& key, const W::Object& element)
{
if (key == u"name") {
insert(W::String(u"album"), element);
} else if (key == u"image") {
insert(key, element);
}
}
void ProxySong::onAlbumRemoveElement(const W::String& key)
{
if (key == u"name") {
insert(W::String(u"album"), new W::String(u"undefined"));
} else if (key == u"image") {
insert(key, new W::Uint64(0));
}
}
void ProxySong::onArtistNewElement(const W::String& key, const W::Object& element)
{
if (key == u"name") {
insert(W::String(u"artist"), element);
}
}
void ProxySong::onArtistRemoveElement(const W::String& key)
{
if (key == u"name") {
insert(W::String(u"artist"), new W::String(u"undefined"));
}
}
const W::Uint64 & ProxySong::getFileId() const
{
return fileId;
}

47
corax/models/proxysong.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef PROXYSONG_H
#define PROXYSONG_H
/**
* @todo write docs
*/
#include <wType/uint64.h>
#include <wType/address.h>
#include <wModel/vocabulary.h>
#include <wController/vocabulary.h>
class ProxySong : public M::Vocabulary {
Q_OBJECT
public:
ProxySong(const W::Uint64& p_id, const W::Address& p_address, QObject* parent = 0);
~ProxySong();
const W::Uint64& getFileId() const;
bool isReady() const;
signals:
void ready();
void notReady();
private:
C::Vocabulary* songCtrl;
C::Vocabulary* albumCtrl;
C::Vocabulary* artistCtrl;
W::Uint64 fileId;
bool _ready;
private slots:
void onSongNewElement(const W::String& key, const W::Object& element);
void onSongRemoveElement(const W::String& key);
void onAlbumNewElement(const W::String& key, const W::Object& element);
void onAlbumRemoveElement(const W::String& key);
void onArtistNewElement(const W::String& key, const W::Object& element);
void onArtistRemoveElement(const W::String& key);
};
#endif // PROXYSONG_H

View File

@ -276,3 +276,8 @@ bool C::Controller::isSubscribed()
{
return subscribed;
}
const W::Address & C::Controller::getAddress() const
{
return address;
}

View File

@ -40,6 +40,7 @@ namespace C {
void subscribe();
void unsubscribe();
bool isSubscribed();
const W::Address& getAddress() const;
void removeHandler(W::Handler* handler);
void removeController(C::Controller* ctrl);

View File

@ -175,6 +175,8 @@ void Database::clear()
lmdb::txn transaction = lmdb::txn::begin(environment);
dbi.drop(transaction);
transaction.commit();
elements.clear();
}
void Database::addModel(M::Model* model)

View File

@ -215,7 +215,7 @@ void ResourceCache::h_subscribeMember(const W::Event& ev)
M::File* modelRecord = M::File::create(readFile(*record), address + lastHops >> 1);
delete record;
addModel(modelRecord);
passToHandler(ev);
passToLocalHandler(ev);
} catch (int err) {
if (err == 3) {
emit serviceMessage(QString("An attempt to create and subscribe record model in resourcecache, but it is not found. Event: ") + ev.toString().c_str());

View File

@ -14,7 +14,9 @@ set(HEADERS
attributes.h
icatalogue.h
catalogue.h
button.h
file/file.h
file/audio.h
)
set(SOURCES
@ -25,12 +27,15 @@ set(SOURCES
attributes.cpp
icatalogue.cpp
catalogue.cpp
button.cpp
file/file.cpp
file/audio.cpp
)
add_library(wModel STATIC ${HEADERS} ${SOURCES})
target_link_libraries(wModel Qt5::Core)
target_link_libraries(wModel wSocket)
target_link_libraries(wModel wDispatcher)
target_link_libraries(wModel wServerUtils)
target_link_libraries(wModel wType)
target_link_libraries(wModel wController)
target_link_libraries(wModel mad)

121
lib/wModel/button.cpp Normal file
View File

@ -0,0 +1,121 @@
#include "button.h"
M::Button::Button(const W::Address& address, QObject* parent):
M::Model(address, parent),
enabled(true),
hasImage(false),
hasLabel(false),
imageName(0),
label(0)
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::Button::_h_get);
W::Handler* activate = W::Handler::create(address + W::Address({u"activate"}), this, &M::Button::_h_activate);
addHandler(get);
addHandler(activate);
}
M::Button::~Button()
{
if (hasImage) {
delete imageName;
}
}
void M::Button::setImage(const W::String& p_image)
{
if (hasImage) {
if (*imageName != p_image) {
imageName = static_cast<W::String*>(p_image.copy());
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"image", p_image);
broadcast(vc, W::Address{u"changeImage"});
}
}
} else {
imageName = static_cast<W::String*>(p_image.copy());
hasImage = true;
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"image", p_image);
broadcast(vc, W::Address{u"setImage"});
}
}
hasImage = true;
}
void M::Button::setEnabled(bool p_enabled)
{
if (enabled != p_enabled) {
enabled = p_enabled;
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"enabled", new W::Boolean(enabled));
broadcast(vc, W::Address{u"setEnabled"});
}
}
}
void M::Button::setLabel(const W::String& p_label)
{
if (hasLabel) {
label->set(p_label);
} else {
label = new M::String(p_label, address + W::Address{u"label"});
addModel(label);
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"hasLabel", new W::Boolean(true));
vc->insert(u"label", label->getAddress());
broadcast(vc, W::Address{u"setLabel"});
}
hasLabel = true;
}
}
void M::Button::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_get(ev);
}
void M::Button::h_get(const W::Event& ev)
{
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"hasImage", new W::Boolean(hasImage));
if (hasImage) {
vc->insert(u"image", imageName->copy());
}
vc->insert(u"hasLabel", new W::Boolean(hasLabel));
if (hasLabel) {
vc->insert(u"label", label->getAddress());
}
vc->insert(u"enabled", new W::Boolean(enabled));
response(vc, W::Address({u"get"}), ev);
}
void M::Button::h_activate(const W::Event& ev)
{
if (enabled) {
emit activated();
}
}
M::Model::ModelType M::Button::getType() const
{
return M::Model::button;
}
void M::Button::set(const W::Object& value)
{
throw 14; //what do you expect here? not implemented, and not sure it ever would be
}
void M::Button::set(W::Object* value)
{
set(*value);
}

52
lib/wModel/button.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef BUTTON_H
#define BUTTON_H
#include <wModel/model.h>
#include <wModel/modelstring.h>
#include <wDispatcher/handler.h>
#include <wType/address.h>
#include <wType/event.h>
#include <wType/vocabulary.h>
#include <wType/boolean.h>
/**
* @todo write docs
*/
namespace M {
class Button : public Model {
Q_OBJECT
public:
Button(const W::Address& address, QObject* parent = 0);
~Button();
void setImage(const W::String& p_image);
void setLabel(const W::String& p_label);
void setEnabled(bool p_enabled);
M::Model::ModelType getType() const override;
void set(const W::Object & value) override;
void set(W::Object * value) override;
signals:
void activated() const;
protected:
void h_subscribe(const W::Event & ev) override;
handler(get);
handler(activate);
protected:
bool enabled;
bool hasImage;
bool hasLabel;
W::String* imageName;
M::String* label;
};
}
#endif // BUTTON_H

45
lib/wModel/file/audio.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "audio.h"
#include <mad.h>
M::Audio::Audio(W::Blob* p_file, const W::Address& addr, QObject* parent):
File(p_file, addr, parent)
{
}
M::Audio::~Audio()
{}
M::Model::ModelType M::Audio::getType() const
{
return type;
}
void M::Audio::initAdditional(const W::String& p_mime)
{
File::initAdditional(p_mime); //TODO handle other than mp3 formats
mad_stream stream;
mad_header header;
mad_stream_init(&stream);
mad_header_init(&header);
mad_stream_buffer(&stream, file->uchar(), file->size());
uint64_t length = 0;
uint64_t tBits = 0;
uint64_t amount = 0;
while(stream.error != MAD_ERROR_BUFLEN) { //TODO handle other errors;
int success = mad_header_decode(&header, &stream);
if (success == 0) {
amount++;
length += header.duration.seconds * MAD_TIMER_RESOLUTION + header.duration.fraction;
tBits += header.bitrate;
}
}
additional.insert(u"duration", new W::Uint64(length * 1000 / MAD_TIMER_RESOLUTION));
additional.insert(u"bitrate", new W::Uint64(tBits / amount));
}

31
lib/wModel/file/audio.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef M_AUDIO_H
#define M_AUDIO_H
#include <wModel/file/file.h>
#include <wType/blob.h>
#include <deque>
namespace M {
/**
* @todo write docs
*/
class Audio : public File {
friend class File;
protected:
Audio(W::Blob* p_file, const W::Address& addr, QObject* parent = 0);
public:
~Audio();
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = audio;
protected:
void initAdditional(const W::String& p_mime) override;
};
}
#endif // M_AUDIO_H

View File

@ -1,7 +1,13 @@
#include "file.h"
#include <iostream>
#include "audio.h"
QMimeDatabase M::File::mimeDB;
const std::map<QString, M::Model::ModelType> M::File::mimeMap = {
{"image/jpeg", M::Model::file},
{"audio/mpeg", M::Model::audio}
};
M::File::File(W::Blob* p_file, const W::Address& addr, QObject* parent):
M::Model(addr, parent),
@ -10,9 +16,11 @@ M::File::File(W::Blob* p_file, const W::Address& addr, QObject* parent):
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::File::_h_get);
W::Handler* getAdditional = W::Handler::create(address + W::Address({u"getAdditional"}), this, &M::File::_h_getAdditional);
W::Handler* getSlice = W::Handler::create(address + W::Address({u"getSlice"}), this, &M::File::_h_getSlice);
addHandler(get);
addHandler(getAdditional);
addHandler(getSlice);
}
M::File::~File()
@ -29,7 +37,7 @@ void M::File::initAdditional(const W::String& p_mime)
{
additional.clear();
additional.insert(u"size", new W::Uint64(file->size()));
additional.insert(u"size", new W::Uint64(file->length()));
additional.insert(u"mimeType", p_mime);
}
@ -79,8 +87,42 @@ M::File * M::File::create(W::Blob* blob, const W::Address& addr, QObject* parent
M::File* out;
QMimeType mt = mimeDB.mimeTypeForData(blob->byteArray());
out = new File(blob, addr, parent);
out->initAdditional(W::String(mt.name().toStdString()));
const QString& mime = mt.name();
std::map<QString, M::Model::ModelType>::const_iterator itr = mimeMap.find(mime);
M::Model::ModelType modelType = M::Model::file;
if (itr != mimeMap.end()) {
modelType = itr->second;
}
switch (modelType) {
case Model::audio:
out = new Audio(blob, addr, parent);
break;
default:
out = new File(blob, addr, parent);
break;
}
out->initAdditional(W::String(mime.toStdString()));
return out;
}
void M::File::h_getSlice(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Uint64& begin = static_cast<const W::Uint64&>(vc.at(u"begin"));
const W::Uint64& size = static_cast<const W::Uint64&>(vc.at(u"size"));
W::Vocabulary* evc = new W::Vocabulary();
if (begin > file->length() || begin + size > file->length()) {
evc->insert(u"result", new W::Uint64(1));
} else {
evc->insert(u"result", new W::Uint64(0));
evc->insert(u"slice", file->slice(begin, size));
}
response(evc, W::Address{u"getSlice"}, ev);
}

View File

@ -32,12 +32,14 @@ namespace M {
handler(get);
handler(getAdditional);
handler(getSlice);
protected:
W::Vocabulary additional;
W::Blob* file;
static QMimeDatabase mimeDB;
static const std::map<QString, M::Model::ModelType> mimeMap;
};
}

View File

@ -44,6 +44,15 @@ void M::ICatalogue::clear()
broadcast(new W::Vocabulary(), W::Address{u"clear"});
}
std::map<uint64_t, M::Vocabulary*>::iterator aItr = activeChildren.begin();
std::map<uint64_t, M::Vocabulary*>::iterator aEnd = activeChildren.end();
for (; aItr != aEnd; ++aItr) {
removeModel(aItr->second);
aItr->second->deleteLater();
}
activeChildren.clear();
emit countChange(0);
}

View File

@ -5,12 +5,12 @@ M::Model::Model(const W::Address p_address, QObject* parent):
address(p_address),
registered(false),
subscribers(new Map()),
dispatcher(0),
server(0),
connector(0),
subscribersCount(0),
handlers(new HList()),
properties(new W::Vector()),
models(new MList())
models(new MList()),
controllers(new Controllers())
{
W::Handler* subscribe = W::Handler::create(address + W::Address({u"subscribe"}), this, &M::Model::_h_subscribe);
W::Handler* unsubscribe = W::Handler::create(address + W::Address({u"unsubscribe"}), this, &M::Model::_h_unsubscribe);
@ -21,7 +21,7 @@ M::Model::Model(const W::Address p_address, QObject* parent):
M::Model::~Model()
{
if (registered) {
unregisterModel();
getUnregistered();
}
MList::iterator itr = models->begin();
@ -37,11 +37,19 @@ M::Model::~Model()
for (; hItr != hEnd; ++hItr) {
delete *hItr;
}
Controllers::iterator cItr = controllers->begin();
Controllers::iterator cEnd = controllers->end();
for (; cItr != cEnd; ++cItr) {
delete cItr->first;
}
delete subscribers;
delete properties;
delete handlers;
delete models;
delete controllers;
}
void M::Model::addModel(M::Model* model)
@ -49,7 +57,7 @@ void M::Model::addModel(M::Model* model)
models->push_back(model);
connect(model, SIGNAL(serviceMessage(const QString&)), SIGNAL(serviceMessage(const QString&)));
if (registered) {
model->registerModel(dispatcher, server);
model->getRegistered(connector);
}
}
@ -57,7 +65,7 @@ void M::Model::addHandler(W::Handler* handler)
{
handlers->push_back(handler);
if (registered) {
dispatcher->registerHandler(handler);
connector->registerHandler(handler);
}
}
@ -83,21 +91,20 @@ W::Address M::Model::getAddress() const
}
void M::Model::registerModel(W::Dispatcher* dp, W::Server* srv)
void M::Model::getRegistered(U::Connector* cn)
{
if (registered) {
emit serviceMessage(QString("Model ") + address.toString().c_str() + " is already registered");
throw 1;
} else {
dispatcher = dp;
server = srv;
connector = cn;
MList::iterator itr = models->begin();
MList::iterator end = models->end();
for (; itr != end; ++itr) {
M::Model* model = *itr;
model->registerModel(dispatcher, server);
model->getRegistered(connector);
}
HList::iterator hItr = handlers->begin();
@ -105,14 +112,21 @@ void M::Model::registerModel(W::Dispatcher* dp, W::Server* srv)
for (; hItr != hEnd; ++hItr) {
W::Handler* handler = *hItr;
dispatcher->registerHandler(handler);
connector->registerHandler(handler);
}
Controllers::iterator cItr = controllers->begin();
Controllers::iterator cEnd = controllers->end();
for (; cItr != cEnd; ++cItr) {
connector->registerController(cItr->first, cItr->second);
}
registered = true;
}
}
void M::Model::unregisterModel()
void M::Model::getUnregistered()
{
if (!registered) {
emit serviceMessage(QString("Model ") + address.toString().c_str() + " is not registered");
@ -123,7 +137,7 @@ void M::Model::unregisterModel()
for (; itr != end; ++itr) {
Model* model = *itr;
model->unregisterModel();
model->getUnregistered();
}
HList::iterator hItr = handlers->begin();
@ -131,21 +145,27 @@ void M::Model::unregisterModel()
for (; hItr != hEnd; ++hItr) {
W::Handler* handler = *hItr;
dispatcher->unregisterHandler(handler);
connector->unregisterHandler(handler);
}
Map::iterator sItr = subscribers->begin();
Map::iterator sEnd = subscribers->end();
for (; sItr != sEnd; ++sItr) {
const W::Socket& socket = server->getConnection(sItr->first);
disconnect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
const W::Socket* socket = connector->getConnection(sItr->first);
disconnect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
}
subscribers->clear();
subscribersCount = 0;
dispatcher = 0;
server = 0;
Controllers::iterator cItr = controllers->begin();
Controllers::iterator cEnd = controllers->end();
for (; cItr != cEnd; ++cItr) {
connector->unregisterController(cItr->first, cItr->second);
}
connector = 0;
registered = false;
}
@ -162,7 +182,6 @@ void M::Model::h_subscribe(const W::Event& ev)
params = static_cast<const W::Vocabulary&>(vc.at(u"params"));
}
Map::iterator sItr = subscribers->find(id);
if (sItr == subscribers->end()) {
@ -171,8 +190,8 @@ void M::Model::h_subscribe(const W::Event& ev)
emit serviceMessage(QString("Model ") + address.toString().c_str() + ": something completely wrong happened");
throw 3;
}
const W::Socket& socket = server->getConnection(id);
connect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
const W::Socket* socket = connector->getConnection(id);
connect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
sItr = pair.first;
}
SMap::const_iterator oItr = sItr->second.find(source);
@ -238,8 +257,8 @@ void M::Model::h_unsubscribe(const W::Event& ev)
smap.erase(sItr);
if (smap.size() == 0) {
const W::Socket& socket = server->getConnection(itr->first);
disconnect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
const W::Socket* socket = connector->getConnection(itr->first);
disconnect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
subscribers->erase(itr);
}
--subscribersCount;
@ -256,7 +275,7 @@ void M::Model::send(W::Vocabulary* vc, const W::Address& destination, uint64_t c
}
W::Event ev(destination, vc);
ev.setSenderId(connectionId);
server->getConnection(connectionId).send(ev);
connector->getConnection(connectionId)->send(ev);
}
void M::Model::response(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Event& src)
@ -272,7 +291,7 @@ void M::Model::response(W::Vocabulary* vc, const W::Address& handlerAddress, con
W::Event ev(source + handlerAddress, vc);
ev.setSenderId(id);
server->getConnection(id).send(ev);
connector->getConnection(id)->send(ev);
}
void M::Model::fakeResponse(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Address& sourceAddress, const W::Event& src)
@ -288,7 +307,7 @@ void M::Model::fakeResponse(W::Vocabulary* vc, const W::Address& handlerAddress,
W::Event ev(source + handlerAddress, vc);
ev.setSenderId(id);
server->getConnection(id).send(ev);
connector->getConnection(id)->send(ev);
}
void M::Model::broadcast(W::Vocabulary* vc, const W::Address& handlerAddress)
@ -307,7 +326,7 @@ void M::Model::broadcast(W::Vocabulary* vc, const W::Address& handlerAddress)
for (;oItr != oEnd; ++oItr) {
W::Event ev(oItr->first + handlerAddress, vc->copy());
ev.setSenderId(itr->first);
server->getConnection(itr->first).send(ev);
connector->getConnection(itr->first)->send(ev);
}
}
delete vc;
@ -317,23 +336,54 @@ void M::Model::removeHandler(W::Handler* handler)
{
handlers->erase(handler);
if (registered) {
dispatcher->unregisterHandler(handler);
connector->unregisterHandler(handler);
}
}
void M::Model::removeModel(M::Model* model)
{
models->erase(model);
disconnect(model, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
if (registered) {
model->unregisterModel();
model->getUnregistered();
}
}
void M::Model::passToHandler(const W::Event& event) const
void M::Model::passToLocalHandler(const W::Event& event) const
{
if (registered) {
dispatcher->pass(event);
connector->passThroughDispatcher(event);
} else {
emit serviceMessage(QString("An attempt to pass event to dispatcher from unregistered model\nModel address ") + address.toString().c_str());
}
}
void M::Model::addController(C::Controller* ctrl, const W::String& nodeName)
{
Controllers::const_iterator itr = controllers->find(ctrl);
if (itr != controllers->end()) {
emit serviceMessage(QString("An attempt to add controller ") + ctrl->getAddress().toString().c_str() + QString(" for the second time in model ") + address.toString().c_str());
throw 9;
}
controllers->insert(std::make_pair(ctrl, nodeName));
connect(ctrl, SIGNAL(serviceMessage(const QString&)), SIGNAL(serviceMessage(const QString&)));
if (registered) {
connector->registerController(ctrl, nodeName);;
}
}
void M::Model::removeController(C::Controller* ctrl)
{
Controllers::const_iterator itr = controllers->find(ctrl);
if (itr == controllers->end()) {
emit serviceMessage(QString("An attempt to remove absent controller ") + ctrl->getAddress().toString().c_str() + QString(" from the model ") + address.toString().c_str());
throw 10;
}
disconnect(ctrl, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
if (registered) {
connector->unregisterController(itr->first, itr->second);
}
controllers->erase(itr);
}

View File

@ -13,10 +13,10 @@
#include <wType/event.h>
#include <wType/string.h>
#include <wSocket/socket.h>
#include <wSocket/server.h>
#include <wDispatcher/dispatcher.h>
#include <wDispatcher/handler.h>
#include <wContainer/order.h>
#include <wServerUtils/connector.h>
#include <wController/controller.h>
namespace M {
@ -29,10 +29,16 @@ namespace M {
list,
vocabulary,
catalogue,
image,
button,
model,
attributes = 50,
file,
resourceCache
resourceCache,
audio,
player = 107
};
Model(const W::Address p_address, QObject* parent = 0);
@ -46,13 +52,15 @@ namespace M {
void addModel(M::Model* model);
void addHandler(W::Handler* handler);
void addProperty(const W::String& value, const W::String& name);
void addController(C::Controller* ctrl, const W::String& nodeName);
W::Address getAddress() const;
void registerModel(W::Dispatcher* dp, W::Server* srv);
void unregisterModel();
void getRegistered(U::Connector* connector);
void getUnregistered();
void removeHandler(W::Handler* handler);
void removeModel(M::Model* model);
void passToHandler(const W::Event& event) const;
void removeController(C::Controller* ctrl);
void passToLocalHandler(const W::Event& event) const;
signals:
void serviceMessage(const QString& msg) const;
@ -76,13 +84,14 @@ namespace M {
private:
typedef W::Order<W::Handler*> HList;
typedef W::Order<M::Model*> MList;
typedef std::map<C::Controller*, W::String> Controllers;
W::Dispatcher* dispatcher;
W::Server* server;
U::Connector* connector;
uint64_t subscribersCount;
HList* handlers;
W::Vector* properties;
MList* models;
Controllers* controllers;
private slots:
void onSocketDisconnected();

View File

@ -12,8 +12,7 @@
namespace M {
class ICatalogue;
class Vocabulary : public M::Model
{
class Vocabulary : public M::Model {
friend class ICatalogue;
public:
Vocabulary(const W::Address p_address, QObject* parent = 0);

View File

@ -1,4 +1,5 @@
#include "connector.h"
#include "commands.h"
U::Connector::Connector(W::Dispatcher* dp, W::Server* srv, U::Commands* cmds, QObject* parent):
QObject(parent),
@ -6,7 +7,8 @@ U::Connector::Connector(W::Dispatcher* dp, W::Server* srv, U::Commands* cmds, QO
server(srv),
commands(cmds),
nodes(),
ignoredNodes()
ignoredNodes(),
controllers()
{
connect(server, SIGNAL(newConnection(const W::Socket&)), SLOT(onNewConnection(const W::Socket&)));
connect(server, SIGNAL(closedConnection(const W::Socket&)), SLOT(onClosedConnection(const W::Socket&)));
@ -30,6 +32,17 @@ U::Connector::~Connector()
for (; itr != end; ++itr) {
commands->removeCommand(dc + itr->first);
}
std::multimap<W::String, C::Controller*>::const_iterator cbeg = controllers.begin();
std::multimap<W::String, C::Controller*>::const_iterator cend = controllers.end();
for (; cbeg != cend; ++cbeg) {
C::Controller* ctrl = cbeg->second;
if (ctrl->isSubscribed()) {
ctrl->unsubscribe();
ctrl->unregisterController();
}
}
}
void U::Connector::addIgnoredNode(const W::String& name)
@ -43,7 +56,7 @@ void U::Connector::sendTo(const W::String& name, const W::Event& event)
if (itr != nodes.end()) {
throw new NodeAccessError(name);
} else {
server->getConnection(itr->second).send(event);
server->getConnection(itr->second)->send(event);
}
}
@ -69,6 +82,14 @@ void U::Connector::onNewConnection(const W::Socket& socket)
emit serviceMessage(QString("New connection, id: ") + socket.getId().toString().c_str());
connect(&socket, SIGNAL(message(const W::Event&)), dispatcher, SLOT(pass(const W::Event&)));
std::multimap<W::String, C::Controller*>::const_iterator beg = controllers.lower_bound(name);
std::multimap<W::String, C::Controller*>::const_iterator end = controllers.upper_bound(name);
for (; beg != end; ++beg) {
beg->second->registerController(dispatcher, &socket);
beg->second->subscribe();
}
emit nodeConnected(name);
}
} else {
@ -90,6 +111,14 @@ void U::Connector::onClosedConnection(const W::Socket& socket)
if (ign == ignoredNodes.end()) {
Map::const_iterator itr = nodes.find(name);
if (itr != nodes.end()) {
std::multimap<W::String, C::Controller*>::const_iterator beg = controllers.lower_bound(name);
std::multimap<W::String, C::Controller*>::const_iterator end = controllers.upper_bound(name);
for (; beg != end; ++beg) {
beg->second->unsubscribe();
beg->second->unregisterController();
}
emit nodeDisconnected(name);
commands->removeCommand(W::String(u"disconnect") + name);
nodes.erase(itr);
@ -114,7 +143,7 @@ void U::Connector::h_disconnect(const W::Event& ev)
server->closeConnection(itr->second);
}
const W::Socket& U::Connector::getNodeSocket(const W::String& name)
const W::Socket* U::Connector::getNodeSocket(const W::String& name)
{
Map::const_iterator itr = nodes.find(name);
if (itr == nodes.end()) {
@ -122,3 +151,62 @@ const W::Socket& U::Connector::getNodeSocket(const W::String& name)
}
return server->getConnection(itr->second);
}
void U::Connector::registerHandler(W::Handler* handler)
{
dispatcher->registerHandler(handler);
}
void U::Connector::unregisterHandler(W::Handler* handler)
{
dispatcher->unregisterHandler(handler);
}
const W::Socket * U::Connector::getConnection(uint64_t p_id) const
{
return server->getConnection(p_id);
}
void U::Connector::passThroughDispatcher(const W::Event& ev) const
{
dispatcher->pass(ev);
}
void U::Connector::registerController(C::Controller* ctrl, const W::String& node)
{
std::set<W::String>::const_iterator iitr = ignoredNodes.find(node);
if (iitr != ignoredNodes.end()) {
throw 3; //this means you're trying to receive something from one of ignored nodes, which never going to be handled in connector, most probably it's a mistake
}
controllers.insert(std::make_pair(node, ctrl));
Map::const_iterator itr = nodes.find(node);
if (itr != nodes.end()) {
ctrl->registerController(dispatcher, server->getConnection(itr->second));
ctrl->subscribe(); //let's say I always need them subscribed, for now at least
}
}
void U::Connector::unregisterController(C::Controller* ctrl, const W::String& node)
{
bool found = false;
std::multimap<W::String, C::Controller*>::const_iterator beg = controllers.lower_bound(node);
std::multimap<W::String, C::Controller*>::const_iterator end = controllers.upper_bound(node);
for (; beg != end; ++beg) {
if (beg->second == ctrl) {
found = true; //TODO make a proper way to store 'em
break;
}
}
if (!found) {
throw 4;
}
if (ctrl->isSubscribed()) {
ctrl->unsubscribe();
ctrl->unregisterController();
}
controllers.erase(beg);
}

View File

@ -1,11 +1,14 @@
#ifndef CONNECTOR_H
#define CONNECTOR_H
#include <utils/defines.h>
#include <QtCore/QObject>
#include <map>
#include <set>
#include <wDispatcher/dispatcher.h>
#include <wDispatcher/handler.h>
#include <wSocket/socket.h>
#include <wSocket/server.h>
@ -16,9 +19,11 @@
#include <utils/exception.h>
#include "commands.h"
#include <wController/controller.h>
namespace U {
class Commands;
class Connector : public QObject
{
Q_OBJECT
@ -29,7 +34,13 @@ namespace U {
void addIgnoredNode(const W::String& name);
void sendTo(const W::String& name, const W::Event& event);
const W::Socket& getNodeSocket(const W::String& name);
const W::Socket* getNodeSocket(const W::String& name);
void registerHandler(W::Handler* handler);
void unregisterHandler(W::Handler* handler);
const W::Socket* getConnection(uint64_t p_id) const;
void passThroughDispatcher(const W::Event& ev) const;
void registerController(C::Controller* ctrl, const W::String& node);
void unregisterController(C::Controller* ctrl, const W::String& node);
signals:
void serviceMessage(const QString& msg);
@ -42,6 +53,7 @@ namespace U {
U::Commands* commands;
Map nodes;
std::set<W::String> ignoredNodes;
std::multimap<W::String, C::Controller*> controllers;
protected:
handler(connect);

View File

@ -44,14 +44,14 @@ void W::Server::stop()
}
}
const W::Socket& W::Server::getConnection(uint64_t p_id) const
const W::Socket* W::Server::getConnection(uint64_t p_id) const
{
std::map<uint64_t, Socket*>::const_iterator itr = connections.find(p_id);
if (itr == connections.end()) {
throw new SocketAccessError();
}
return *(itr->second);
return itr->second;
}
uint64_t W::Server::getConnectionsCount() const

View File

@ -27,7 +27,7 @@ namespace W
void listen(uint16_t port);
void stop();
const Socket& getConnection(uint64_t p_id) const;
const Socket* getConnection(uint64_t p_id) const;
uint64_t getConnectionsCount() const;
void closeConnection(uint64_t p_id);
void openConnection(const String& addr, const Uint64& port);

View File

@ -20,4 +20,4 @@ add_library(wSsh ${HEADERS} ${SOURCES})
target_link_libraries(wSsh Qt5::Core)
target_link_libraries(wSsh ssh)
target_link_libraries(wSsh ssh_threads)
#target_link_libraries(wSsh ssh_threads)

View File

@ -144,3 +144,17 @@ const QByteArray & W::Blob::byteArray() const
return qDataView;
}
const unsigned char * W::Blob::uchar() const
{
return (unsigned char*) data;
}
W::Blob* W::Blob::slice(uint64_t start, uint64_t length) const
{
char* n_data = new char[length];
for (int i = 0; i < length; ++i) {
n_data[i] = data[start + i];
}
return new W::Blob(length, n_data);
}

View File

@ -22,6 +22,7 @@ namespace W
size_type size() const override;
objectType getType() const override;
Blob* slice(uint64_t start, uint64_t length = 0) const;
bool operator==(const W::Object & other) const override;
@ -33,6 +34,7 @@ namespace W
static const objectType type = blob;
const QByteArray& byteArray() const;
const unsigned char* uchar() const;
protected:
bool hasData;

View File

@ -102,5 +102,7 @@ W::Object::StdStr W::Object::getTypeName(W::Object::objectType type)
case blob:
return "Blob";
}
throw 5;
}

View File

@ -2,4 +2,6 @@ cmake_minimum_required(VERSION 2.8.12)
configure_file(class.js class.js)
configure_file(subscribable.js subscribable.js)
configure_file(globalMethods.js globalMethods.js)
configure_file(globalMethods.js globalMethods.js)
configure_file(enum.js enum.js)
configure_file(stateMachine.js stateMachine)

69
libjs/utils/enum.js Normal file
View File

@ -0,0 +1,69 @@
"use strict";
var Class = require("./class");
var Enum = Class.inherit({
className: "Enum",
constructor: function(name, additional) {
if (typeof name !== "string" || name.length === 0) {
throw new Error("An attempt to register enum with wrong or empty name");
}
if (storage[name]) {
throw new Error("An attempt to register enum " + name + " for the second time");
}
Class.fn.constructor.call(this);
this.name = name;
this.straight = Object.create(null);
this.reversed = Object.create(null);
this.additional = Object.create(null);
this._additionals = additional || [];
this._lastId = -1;
storage[name] = this;
},
add: function(name, additional, id) {
if (typeof name !== "string" || name.length === 0) {
throw new Error("An attempt to add an entry with invalid name to enum " + name);
}
if (!id) {
id = this._lastId + 1;
}
if (this.straight[id] !== undefined) {
throw new Error("Id duplication in enum " + this.name + " during an attempt to add entry " + name);
}
if (this.reversed[name] !== undefined) {
throw new Error("Name duplication in enum " + this.name + " during an attempt to add entry " + name);
}
this.straight[name] = id;
this.reversed[id] = name;
this.additional[id] = Object.create(null);
for (var i = 0; i < this._additionals.length; ++i) {
var key = this._additionals[i];
var aVal = additional[key];
if (aVal === undefined) {
throw new Error("An attempt to add an entry " + name + " into enum " + this.name + " without providing additional value " + key);
}
this.additional[id][key] = aVal;
}
this._lastId = id;
},
hasAdditional: function(name) {
return this._additionals.indexOf(name) !== -1;
}
});
var storage = Object.create(null);
Enum.storage = storage;
module.exports = Enum;

View File

@ -65,5 +65,40 @@ global.W = {
// Return the modified object
return lTarget;
},
touchToMouse: function (e) {
e.preventDefault();
if (e.touches.length > 1 || (e.type == "touchend" && e.touches.length > 0))
return;
var type = null;
var touch = null;
var src = e.currentTarget;
switch (e.type) {
case "touchstart":
type = "mousedown";
touch = e.changedTouches[0];
break;
case "touchmove":
type = "mousemove";
touch = e.changedTouches[0];
src = window;
break;
case "touchend":
type = "mouseup";
touch = e.changedTouches[0];
src = window;
break;
}
var event = new MouseEvent(type, {
button: 0,
screenX: touch.screenX,
screenY: touch.screenY,
clientX: touch.clientX,
clientY: touch.clientY
});
src.dispatchEvent(event);
}
};

View File

@ -0,0 +1,33 @@
"use strict";
var Subscribable = require("./subscribable");
var StateMachine = Subscribable.inherit({
className: "StateMachine",
constructor: function (initialState, graph) {
Subscribable.fn.constructor.call(this);
this._state = initialState;
this._graph = graph;
},
manipulation: function (name) {
var newState = this._graph[this._state][name];
if (newState) {
var oldState = this._state;
this._state = newState;
this.trigger("stateChanged", {
newState: newState,
manipulation: name,
oldState: oldState
});
} else {
this.trigger("stateMissed");
}
},
state: function () {
return this._state;
}
});
module.exports = StateMachine;

View File

@ -17,3 +17,6 @@ configure_file(localModel.js localModel.js)
configure_file(catalogue.js catalogue.js)
configure_file(imagePane.js imagePane.js)
configure_file(file/file.js file/file.js)
configure_file(file/audio.js file/audio.js)
configure_file(player.js player.js)
configure_file(imageById.js imageById.js)

View File

@ -0,0 +1,71 @@
"use strict";
var Controller = require("./controller");
var String = require("./string");
var Vocabulary = require("../wType/vocabulary");
var Button = Controller.inherit({
"className": "Button",
"constructor": function(addr) {
Controller.fn.constructor.call(this, addr);
this.enabled = false;
this.hasLabel = false;
this.addHandler("get");
this.addHandler("setLabel");
this.addHandler("setImage");
this.addHandler("setEnabled");
this.addHandler("changeImage");
},
"destructor": function() {
Controller.fn.destructor.call(this);
},
"activate": function() {
if (this.enabled) {
this.send(new Vocabulary, "activate");
}
},
"_h_changeImage": function(ev) {
},
"_h_get": function(ev) {
this._h_setLabel(ev);
this._h_setImage(ev);
this._h_setEnabled(ev);
this.initialized = true;
},
"_h_setEnabled": function(ev) {
var data = ev.getData();
var enabled = data.at("enabled").valueOf();
if (this.enabled !== enabled) {
this.enabled = enabled;
this.trigger("setEnabled", this.enabled);
}
},
"_h_setLabel": function(ev) {
var data = ev.getData();
var hasLabel = data.at("hasLabel").valueOf();
if (hasLabel !== this.hasLabel) {
this.hasLabel = hasLabel;
if (hasLabel) {
this.label = new String(data.at("label").clone());
this.addController(this.label);
this.trigger("setLabel", true, this.label);
} else {
this.trigger("setLabel", false);
this.removeController(this.label);
this.label.destructor();
}
}
},
"_h_setImage": function(ev) {
}
});
module.exports = Button;

View File

@ -188,7 +188,7 @@ var Controller = Subscribable.inherit({
}
if (this._registered) {
global.registerForeignController(pair.n, pair.c);
global.unregisterForeignController(pair.n, pair.c);
}
pair.c.off("serviceMessage", this._onControllerServiceMessage, this);
@ -328,8 +328,10 @@ Controller.ModelType = {
String: 0,
List: 1,
Vocabulary: 2,
Image: 3,
Controller: 4,
Catalogue: 3,
Image: 4,
Button: 5,
Controller: 6,
Attributes: 50,
@ -339,15 +341,18 @@ Controller.ModelType = {
PageStorage: 103,
PanesList: 104,
Theme: 105,
ThemeStorage: 106
ThemeStorage: 106,
Player: 107
};
Controller.ReversedModelType = {
"0": "String",
"1": "List",
"2": "Vocabulary",
"3": "Image",
"4": "Controller",
"3": "Catalogue",
"4": "Image",
"5": "Button",
"6": "Controller",
"50": "Attributes",
@ -357,7 +362,8 @@ Controller.ReversedModelType = {
"103": "PageStorage",
"104": "PanesList",
"105": "Theme",
"106": "ThemeStorage"
"106": "ThemeStorage",
"107": "Player"
};
Controller.ModelTypesPaths = {
@ -372,7 +378,10 @@ Controller.ModelTypesPaths = {
PanesList: "./panesList", //resolve as dependency
Theme: "./theme", //resolve as dependency
ThemeStorage: "./themeStorage", //resolve as dependency
Image: "./image" //resolve as dependency
Image: "./image", //resolve as dependency
Button: "./button", //resolve as dependency
Catalogue: "./catalogue", //resolve as dependency
Player: "./player" //resolve as dependency
};
Controller.constructors = {

View File

@ -0,0 +1,24 @@
"use strict";
var File = require("./file");
var Vocabulary = require("../../wType/vocabulary");
var Vector = require("../../wType/vector");
var Uint64 = require("../../wType/uint64");
var Audio = File.inherit({
className: "Audio",
constructor: function Audio(addr) {
File.fn.constructor.call(this, addr);
},
hasMore: function() {
return this.getSize() > this.data.length();
},
getBitrate: function() {
return this._additional.at("bitrate").valueOf();
},
getDuration: function() {
return this._additional.at("duration").valueOf() / 1000;
}
});
module.exports = Audio;

View File

@ -2,6 +2,8 @@
var Controller = require("../controller");
var WVocabulary = require("../../wType/vocabulary");
var Uint64 = require("../../wType/uint64");
var Blob = require("../../wType/blob");
var File = Controller.inherit({
"className": "File",
@ -10,17 +12,17 @@ var File = Controller.inherit({
this._hasData = false;
this._hasAdditional = false;
this.data = null;
this.data = new Blob();
this._additional = null;
this._waitingForSlice = false;
this._need = 0;
this.addHandler("get");
this.addHandler("getAdditional");
this.addHandler("getSlice");
},
"destructor": function() {
if (this._hasData) {
this.data.destructor();
}
this.data.destructor();
if (this._hasAdditional) {
this._additional.destructor();
}
@ -46,6 +48,9 @@ var File = Controller.inherit({
"getMimeType": function() {
return this._additional.at("mimeType").toString();
},
"getSize": function() {
return this._additional.at("size").valueOf();
},
"_h_get": function(ev) {
var dt = ev.getData();
@ -55,7 +60,9 @@ var File = Controller.inherit({
}
this._hasData = true;
var oldData = this.data;
this.data = dt.at("data").clone();
oldData.destructor();
this.trigger("data");
},
"_h_getAdditional": function(ev) {
@ -63,6 +70,29 @@ var File = Controller.inherit({
if (ac) {
this.trigger("additionalChange");
}
if (!this.initialized) {
this.initialized = true;
this.trigger("ready");
}
},
"_h_getSlice": function(ev) {
if (this._waitingForSlice) {
this._waitingForSlice = false;
var vc = ev.getData();
if (vc.at("result").valueOf() == 0) {
var slice = vc.at("slice");
this.data["+="](slice);
this.trigger("slice", slice);
if (this.getSize() === this.data.length()) {
this._hasData = true;
this.trigger("data");
}
} else {
this.trigger("serviceMessage", "Error receiving slice from " + this._pairAddress.toString(), 1);
}
}
},
"needData": function() {
if (this._need === 0) {
@ -72,6 +102,30 @@ var File = Controller.inherit({
}
++this._need;
},
"requestSlice": function(size) {
if (this._hasAdditional) {
if (this._waitingForSlice) {
throw new Error("An attempt to request a slice of data from " + this._pairAddress.toString() + " while another request is in progress");
}
var begin = this.data.length();
var newSize = Math.min(size, this.getSize() - begin);
if (newSize !== size) {
//TODO may be inform the developer about that?
}
if (newSize === 0) {
return; //TODO may be inform the developer about that?
}
var vc = new WVocabulary();
vc.insert("begin", new Uint64(begin));
vc.insert("size", new Uint64(newSize));
this.send(vc, "getSlice");
this._waitingForSlice = true;
} else {
throw new Error("An attempt to request a slice of data from " + this._pairAddress.toString() + " before controller got initialized");
}
},
"subscribe": function() {
Controller.fn.subscribe.call(this);

View File

@ -0,0 +1,46 @@
"use strict";
var LocalModel = require("./localModel");
var File = require("./file/file");
var Address = require("../wType/address");
var ImageById = LocalModel.inherit({
className: "ImageById",
constructor: function(properties, id) {
LocalModel.fn.constructor.call(this, properties);
this._imageId = id;
this._fileCtrl = new File(new Address(["images", this._imageId.toString()]));
this._need = 0;
this._fileCtrl.on("data", this._onControllerData, this);
this.addForeignController("Corax", this._fileCtrl);
},
dontNeedData: function() {
--this._need;
if (this._need === 0) {
this._fileCtrl.dontNeedData();
}
},
getMimeType: function () {
return this._fileCtrl.getMimeType();
},
hasData: function() {
return this._fileCtrl.hasData();
},
needData: function() {
if (this._need === 0) {
this._fileCtrl.needData();
}
++this._need;
},
_onControllerData: function() {
this.data = this._fileCtrl.data;
this.trigger("data");
}
});
module.exports = ImageById;

View File

@ -4,26 +4,97 @@ var Address = require("../wType/address");
var Vocabulary = require("./vocabulary");
var File = require("./file/file");
var LM = require("./localModel");
var ImagePane = Vocabulary.inherit({
"className": "ImagePane",
"constructor": function(addr) {
Vocabulary.fn.constructor.call(this, addr);
this._actions = [];
this._hasPageLink = false;
this._hasImage = false;
this.image = null;
},
"addElement": function(key, element) {
if (key === "image" && !this._hasImage) {
this._hasImage = true;
this.image = new File(new Address(["images", element.toString()]));
this.addForeignController("Corax", this.image);
"_actionActivate": function(name) {
var actObj = standardActions[name];
actObj.handler(this);
},
"addAction": function(action) {
var type = action.at("type").valueOf();
var obj = Object.create(null);
var supported = true;
obj.type = type;
switch (type) {
case 0:
var act = action.at("action").toString();
var actObj = standardActions[act];
if (actObj === undefined) {
this.trigger("seviceMessage", "Action " + act + " is not supported in ImagePanel, skipping");
supported = false;
}
var model = new LM(actionProps, "");
model.enabled = true;
model.hasLabel = true;
model.label = new LM(actionLabelProps, actObj.name);
model.addController(model.label);
model.activate = this._actionActivate.bind(this, act);
obj.model = model;
obj.action = act;
break;
}
if (supported) {
this._actions.push(obj);
this.trigger("addAction", obj.model);
}
},
"addElement": function(key, element) {
switch (key) {
case "image":
if (!this._hasImage) {
this._hasImage = true;
this.image = new File(new Address(["images", element.toString()]));
this.addForeignController("Corax", this.image);
}
break;
case "hasPageLink":
this._hasPageLink = element.valueOf();
break;
case "actions":
var size = element.length();
for (var i = 0; i < size; ++i) {
this.addAction(element.at(i));
}
}
Vocabulary.fn.addElement.call(this, key, element);
},
"getActions": function() {
var models = [];
for (var i = 0; i < this._actions.length; ++i) {
models.push(this._actions[i].model);
}
return models;
},
"getPageLink": function() {
if (this._hasPageLink) {
return this.data.at("pageLink").clone();
} else {
throw new Error("An attempt to request a page link from pane which doesn't have it");
}
},
"hasImage": function() {
return this._hasImage;
},
"hasPageLink": function() {
return this._hasPageLink;
},
"removeElement": function(key) {
Vocabulary.fn.removeElement.call(this, key);
@ -37,4 +108,32 @@ var ImagePane = Vocabulary.inherit({
}
});
var standardActions = {
"play": {
handler: function (obj) {
var id = obj._pairAddress.back(); //todo it's a kind of crutch, need to do something about it in the future
window.play(id);
id.destructor();
},
name: "Play"
},
"scheduledToPlay": {
handler: function(obj) {
var id = obj._pairAddress.back(); //todo it's a kind of crutch, need to do something about it in the future
window.scheduleToPlay(id);
id.destructor();
},
name: "Schedule"
}
};
var actionProps = {
"backgroundColor": "primaryColor"
};
var actionLabelProps = {
"fontFamily": "casualFont",
"fontSize": "casualFontSize",
"color": "primaryFontColor"
}
module.exports = ImagePane;

View File

@ -10,16 +10,12 @@ var Link = Controller.inherit({
"constructor": function(addr) {
Controller.fn.constructor.call(this, addr);
var hop = new Address(["label"]);
this.targetAddress = new Address([]);
this.label = new String(addr['+'](hop));
this.addController(this.label);
this.addHandler("get");
hop.destructor();
},
"destructor": function() {
this.targetAddress.destructor();
@ -35,4 +31,6 @@ var Link = Controller.inherit({
}
});
var hop = new Address(["label"]);
module.exports = Link;

View File

@ -1,14 +1,16 @@
"use strict";
var counter = 0;
var Subscribable = require("../utils/subscribable");
var Controller = require("./controller");
var LocalModel = Subscribable.inherit({
"className": "LocalModel",
"constructor": function(properties) {
"constructor": function(properties, data) {
Subscribable.fn.constructor.call(this);
this.properties = [];
this._controllers = [];
this._foreignControllers = [];
if (properties) {
for (var key in properties) {
@ -18,9 +20,67 @@ var LocalModel = Subscribable.inherit({
}
}
}
if (data !== undefined) {
this.data = data;
this.initialized = true;
}
},
"destructor": function() {
for (i = 0; i < this._foreignControllers.length; ++i) {
var pair = this._foreignControllers[i];
global.unsubscribeForeignController(pair.n, pair.c);
global.unregisterForeignController(pair.n, pair.c);
pair.c.destructor();
}
for (var i = 0; i < this._controllers.length; ++i) {
this._controllers[i].destructor();
}
Subscribable.fn.destructor.call(this);
},
"addController": function(ctrl) {
this._controllers.push(ctrl);
},
"addForeignController": function(nodeName, ctrl) {
if (!(ctrl instanceof Controller)) {
throw new Error("An attempt to add not a controller into " + this.className);
}
this._foreignControllers.push({n: nodeName, c: ctrl});
ctrl.on("serviceMessage", this._onControllerServiceMessage, this);
global.registerForeignController(nodeName, ctrl);
global.subscribeForeignController(nodeName, ctrl);
},
"_onControllerServiceMessage": Controller.fn._onControllerServiceMessage,
"removeForeignController": function(ctrl) {
if (!(ctrl instanceof Controller)) {
throw new Error("An attempt to remove not a controller from " + this.className);
}
for (var i = 0; i < this._foreignControllers.length; ++i) {
if (this._foreignControllers[i].c === ctrl) {
break;
}
}
if (i === this._foreignControllers.length) {
throw new Error("An attempt to remove not not existing controller from " + this.className);
} else {
var pair = this._foreignControllers[i];
global.unsubscribeForeignController(pair.n, pair.c);
global.registerForeignController(pair.n, pair.c);
pair.c.off("serviceMessage", this._onControllerServiceMessage, this);
this._foreignControllers.splice(i, 1);
}
},
"setData": function(data) {
this.data = data;
this.initialized = true;
this.trigger("data");
}
});

View File

@ -9,15 +9,28 @@ var PanesList = List.inherit({
"constructor": function PanesListModel(addr) {
List.fn.constructor.call(this, addr);
this.actions = null;
this._subscriptionStart = 0;
this._subscriptionEnd = Infinity;
},
"destructor": function() {
if (this.initialized) {
this.actions.destructor();
}
List.fn.destructor.call(this);
},
"addElement": function(element) {
var size = this.data.length();
List.fn.addElement.call(this, element);
if (size >= this._subscriptionStart && size < this._subscriptionEnd) {
var controller = new ImagePane(this._pairAddress["+"](new Address([element.toString()])));
var size = this.actions.length();
for (var i = 0; i < size; ++i) {
controller.addAction(this.actions.at(i));
}
this.addController(controller);
}
},
@ -25,6 +38,24 @@ var PanesList = List.inherit({
List.fn.clear.call(this);
this.clearChildren();
},
"_h_get": function(ev) {
this.clear();
var root = ev.getData();
var data = root.at("data");
if (this.initialized) {
this.actions.destructor();
}
this.actions = root.at("actions").clone();
var size = data.length();
for (var i = 0; i < size; ++i) {
this.addElement(data.at(i).clone());
}
this.initialized = true;
this.trigger("data");
},
"setSubscriptionRange": function(s, e) {
var needStart = s !== this._subscriptionStart;
var needEnd = e !== this._subscriptionEnd;
@ -33,7 +64,8 @@ var PanesList = List.inherit({
var oe = this._subscriptionEnd;
this._subscriptionStart = s;
this._subscriptionEnd = e;
if (this._subscribed) {
if (this._subscribed && this.initialized) {
var size = this.actions.length();
this.trigger("rangeStart");
if (needStart) {
if (s > os) {
@ -47,6 +79,9 @@ var PanesList = List.inherit({
var limit = Math.min(os, e) - s;
for (var i = 0; i < limit; ++i) {
var ctrl = new ImagePane(this._pairAddress["+"](new Address([this.data.at(i + s).toString()])));
for (var a = 0; a < size; ++a) {
ctrl.addAction(this.actions.at(a));
}
this.addController(ctrl, i);
}
}
@ -59,6 +94,9 @@ var PanesList = List.inherit({
var amount = ce - start; //it can be negative, it's fine
for (var i = 0; i < amount; ++i) {
var ctrl = new ImagePane(this._pairAddress["+"](new Address([this.data.at(start + i).toString()])));
for (var a = 0; a < size; ++a) {
ctrl.addAction(this.actions.at(a));
}
this.addController(ctrl);
}
} else if (ce < coe) {

603
libjs/wController/player.js Normal file
View File

@ -0,0 +1,603 @@
"use strict";
var Uint64 = require("../wType/uint64");
var Address = require("../wType/address");
var Controller = require("./controller");
var Button = require("./button");
var ImageById = require("./imageById");
var Vocabulary = require("./vocabulary");
var Audio = require("./file/audio");
var Model = require("./localModel");
var Enum = require("../utils/enum");
var StateMachine = require("../utils/stateMachine");
var Player = Controller.inherit({
className: "Player",
constructor: function(addr) {
Controller.fn.constructor.call(this, addr);
this.controls = Object.create(null);
this.views = Object.create(null);
this.mode = PlayerMode.straight.playback;
this.progress = new ProgressModel();
this.volume = new Slider();
this.volume.setValue(1);
this.progress.on("seekingStart", this._onSeekingStart, this);
this.progress.on("seekingEnd", this._onSeekingEnd, this);
this.progress.enable(false);
this.volume.on("value", this._onVolume, this);
this._audio = null;
this._createStateMachine();
this._createPlayingInfrastructure();
this.addHandler("get");
this.addHandler("viewsChange");
this.addHandler("play");
this.addHandler("pause");
this._playbackInterval = setInterval(this._onInterval.bind(this), intervalPrecision);
},
destructor: function() {
this._clearInterval(this._playbackInterval);
this._destroyPlayingInfrastructure();
this._fsm.destructor();
this.progress.destructor();
Controller.fn.destructor.call(this);
},
_addControl: function(type, address) {
var t = type.valueOf();
if (this.controls[t] !== undefined) {
throw new Error("An attempt to add multiple instances of " + ItemType.reversed[t] + " into Player");
}
if (ItemType.reversed[t] !== undefined) {
switch (t) {
case ItemType.straight.playPause:
case ItemType.straight.prev:
case ItemType.straight.next:
var btn = new Button(address.clone());
btn.itemType = t;
this.controls[t] = btn;
this.addController(btn);
this.trigger("newElement", btn, t);
break;
default:
this.trigger("serviceMessage", "An attempt to add ItemType " + ItemType.reversed[t] + " to controls of the Player, but it's not qualified to be a control", 1);
}
} else {
this.trigger("serviceMessage", "An unrecgnized item ItemType in Player: " + t, 1);
}
},
_addView: function(type, address) {
var t = type.valueOf();
var ctrl;
var supported = false;
if (this.views[t] !== undefined) {
throw new Error("An attempt to add multiple instances of " + ItemType.reversed[t] + " into Player");
}
if (ItemType.reversed[t] !== undefined) {
switch (t) {
case ItemType.straight.queue:
this.trigger("serviceMessage", "Queue is not supported yet in Player", 1);
break;
case ItemType.straight.currentPlayback:
ctrl = new Vocabulary(address.clone());
ctrl.on("newElement", this._onNewPlaybackElement, this);
ctrl.on("removeElement", this._onRemovePlaybackElement, this);
supported = true;
break;
case ItemType.straight.picture:
ctrl = new ImageById(null, address.back());
ctrl.ItemType = t;
this.views[t] = ctrl;
this.trigger("newElement", ctrl, t);
supported = false; //just to avoid adding with addController, since ImageById is not a controller
break;
default:
this.trigger("serviceMessage", "An attempt to add ItemType " + ItemType.reversed[t] + " to views of the Player, but it's not qualified to be a view", 1);
}
} else {
this.trigger("serviceMessage", "An unrecognized item ItemType in Player: " + t, 1);
}
if (supported) {
ctrl.ItemType = t;
this.views[t] = ctrl;
this.addController(ctrl);
this.trigger("newElement", ctrl, t);
}
},
_checkIfEnough: function() {
var diff = this._currentTime - this._seekingTime - this._ctx.currentTime;
if (diff > threshold) {
this._fsm.manipulation("enough");
} else {
this._fsm.manipulation("notEnough");
}
},
_createPlayingInfrastructure: function() {
this._ctx = new AudioContext();
this._decoder = new Mp3Decoder();
this._gainNode = this._ctx.createGain();
this._gainNode.connect(this._ctx.destination);
this._currentTime = 0;
this._seekingTime = 0;
this._buffers = [];
this._sources = [];
this._ctx.suspend();
},
_createStateMachine: function() {
this._fsm = new StateMachine("initial", graphs[this.mode]);
this._fsm.on("stateChanged", this._onStateChanged, this);
},
_destroyPlayingInfrastructure: function() {
this._ctx.close();
this._decoder.delete();
},
getCurrentPlaybackTime: function() {
return this._ctx.currentTime + this._seekingTime;
},
_h_get: function(ev) {
var data = ev.getData();
var controls = data.at("controls");
var views = data.at("views");
var mode = data.at("mode").valueOf();
var size, i, vc;
size = controls.length();
for (i = 0; i < size; ++i) {
vc = controls.at(i);
this._addControl(vc.at("type"), vc.at("address"));
}
size = views.length();
for (i = 0; i < size; ++i) {
vc = views.at(i);
this._addView(vc.at("type"), vc.at("address"));
}
if (this.mode !== mode) {
if (PlayerMode.reversed[mode] === undefined) {
throw new Error("Unsupported mode of player: " + mode);
}
this.mode = mode;
}
this.initialized = true;
this.trigger("data");
},
_h_pause: function(ev) {
this._fsm.manipulation("pause");
},
_h_play: function(ev) {
this._fsm.manipulation("play");
},
_h_viewsChange: function(ev) {
var data = ev.getData();
var add = data.at("add");
var remove = data.at("remove");
var size, i, vc;
size = remove.length();
for (i = 0; i < size; ++i) {
this._removeView(remove.at(i).valueOf());
}
size = add.length();
for (i = 0; i < size; ++i) {
vc = add.at(i);
this._addView(vc.at("type"), vc.at("address"));
}
},
_onAudioNewSlice: function(frames) {
var arr = new Uint8Array(frames.valueOf());
this._decoder.addFragment(arr);
while (this._decoder.hasMore()) {
var sb = this._decoder.decode(9999);
if (sb === undefined) {
break;
} else {
this._buffers.push(sb);
var startTime = this._currentTime - this._seekingTime;
if (startTime < this._ctx.currentTime) {
var offset = startTime - this._ctx.currentTime + sb.duration;
if (offset > 0) {
var src = this._ctx.createBufferSource();
src.buffer = sb;
src.connect(this._gainNode);
src.start(0, Math.abs(startTime - this._ctx.currentTime));
this._sources.push(src);
}
} else {
var src = this._ctx.createBufferSource();
src.buffer = sb;
src.connect(this._gainNode);
src.start(startTime);
this._sources.push(src);
}
this._currentTime += sb.duration;
}
}
this.progress.setLoad(this._currentTime / this._audio.getDuration());
this._fsm.manipulation("newFrames");
if (this._audio.hasMore()) {
this._audio.requestSlice(audioPortion);
} else {
this._fsm.manipulation("noMoreFrames");
}
},
_onControllerReady: function() {
this._fsm.manipulation("controllerReady");
},
_onInterval: function() {
if (this._audio && this._audio.initialized && seekingStates.indexOf(this._fsm.state()) === -1) {
var duration = this._audio.getDuration();
this.progress.setValue(this.getCurrentPlaybackTime() / duration);
this._checkIfEnough();
if (this.progress.value >= 0.9999) {
var next = this.controls[ItemType.straight.next];
if (next && next.enabled) {
next.activate();
} else {
this._fsm.manipulation("pause");
this._onSeekingStart();
this._onSeekingEnd(0);
this.controls[ItemType.straight.playPause].activate();
}
}
}
},
_onNewPlaybackElement: function(key, element) {
switch (key) {
case "image":
var address = new Address(["images", element.toString()]);
this._addView(new Uint64(ItemType.straight.picture), address);
address.destructor();
break;
case "audio":
if (this.mode === PlayerMode.straight.playback) {
this._audio = new Audio(new Address(["music", element.toString()]));
this.addForeignController("Corax", this._audio);
this._audio.on("slice", this._onAudioNewSlice, this);
this._audio.on("ready", this._onControllerReady, this);
this._fsm.manipulation("controller");
}
break;
}
},
_onRemovePlaybackElement: function(key) {
switch (key) {
case "image":
this._removeView(ItemType.straight.picture);
break;
case "audio":
this.removeForeignController(this._audio);
this._audio.destructor();
this._audio = null;
}
},
_onSeekingStart: function() {
this._fsm.manipulation("startSeeking");
},
_onSeekingEnd: function(progress) {
if (seekingStates.indexOf(this._fsm.state()) !== -1) {
for (var i = 0; i < this._sources.length; ++i) {
this._sources[i].stop();
}
this._sources = [];
var ct = this.getCurrentPlaybackTime();
var duration = this._audio.getDuration();
var targetTime = duration * progress;
this._seekingTime += targetTime - ct;
var nc = 0;
for (var i = 0; i < this._buffers.length; ++i) {
var buffer = this._buffers[i];
var startTime = nc - targetTime;
if (startTime < 0) {
var offset = startTime + buffer.duration;
if (offset > 0) {
var src = this._ctx.createBufferSource();
src.buffer = buffer;
src.connect(this._gainNode);
src.start(0, Math.abs(startTime));
this._sources.push(src);
}
} else {
var src = this._ctx.createBufferSource();
src.buffer = buffer;
src.connect(this._gainNode);
src.start(this._ctx.currentTime + startTime);
this._sources.push(src);
}
nc += buffer.duration;
}
}
this._fsm.manipulation("stopSeeking");
},
_onStateChanged: function(e) {
switch (e.newState) {
case "initial":
if (e.manipulation === "noController") {
this.progress.enable(false);
this.removeForeignController(this._audio);
this._audio.destructor();
this._audio = null;
this._destroyPlayingInfrastructure();
this._createPlayingInfrastructure();
}
break;
case "initialPlaying":
if (e.manipulation === "noController") {
this.progress.enable(false);
this._ctx.suspend();
this.removeForeignController(this._audio);
this._audio.destructor();
this._audio = null;
this._destroyPlayingInfrastructure();
this._createPlayingInfrastructure();
}
break;
case "controllerNotReady":
break
case "controllerNotReadyPlaying":
break
case "hasController":
break;
case "hasControllerPlaying":
if (this._audio.hasMore()) {
this._audio.requestSlice(audioPortion);
} else {
this._fsm.manipulation("noMoreFrames");
}
break;
case "buffering":
if (e.oldState === "hasController") {
this.progress.enable(true);
}
break;
case "bufferingPlaying":
if (e.oldState === "playing") {
this._ctx.suspend();
} else if (e.oldState === "hasControllerPlaying") {
this.progress.enable(true);
}
break;
case "seeking":
break;
case "seekingPlaying":
if (e.oldState === "playing") {
this._ctx.suspend();
}
break;
case "seekingAllLoaded":
break;
case "seekingPlayingAllLoaded":
if (e.oldState === "playingAllLoaded") {
this._ctx.suspend();
}
break;
case "paused":
switch (e.oldState) {
case "playing":
this._ctx.suspend();
break;
}
break;
case "pausedAllLoaded":
switch (e.oldState) {
case "playingAllLoaded":
this._ctx.suspend();
break;
}
break;
case "playing":
this._ctx.resume();
break;
case "playingAllLoaded":
switch (e.oldState) {
case "pausedAllLoaded":
case "bufferingPlaying":
case "seekingPlayingAllLoaded":
this._ctx.resume();
break;
}
break;
}
},
_onVolume: function(volume) {
this._gainNode.gain.cancelScheduledValues(this._ctx.currentTime);
this._gainNode.gain.exponentialRampToValueAtTime(volume, this._ctx.currentTime + 0.01);
},
_removeControl: function(type) {
var ctrl = this.controls[type];
if (ctrl !== undefined) {
this.trigger("removeElement", type);
this.removeController(ctrl);
ctrl.destructor();
}
},
_removeView: function(type) {
var view = this.views[type];
if (view !== undefined) {
this.trigger("removeElement", type);
if (type !== ItemType.straight.picture) {
this.removeController(view);
}
if (type === ItemType.straight.currentPlayback) {
if (this.views[ItemType.straight.picture]) {
this._removeView(ItemType.straight.picture);
}
this._fsm.manipulation("noController");
}
delete this.views[type];
view.destructor();
}
}
});
var ItemType = new Enum("ItemType");
ItemType.add("playPause");
ItemType.add("currentPlayback");
ItemType.add("queue");
ItemType.add("picture");
ItemType.add("prev");
ItemType.add("next");
var PlayerMode = new Enum("PlayerMode");
PlayerMode.add("playback");
Player.ItemType = ItemType;
var graphs = Object.create(null);
graphs[PlayerMode.straight.playback] = {
"initial": {
controller: "controllerNotReady",
play: "initialPlaying"
},
"initialPlaying": {
pause: "initial",
controller: "controllerNotReadyPlaying"
},
"controllerNotReady": {
play: "controllerNotReadyPlaying",
controllerReady: "hasController"
},
"controllerNotReadyPlaying": {
pause: "controllerNotReady",
controllerReady: "hasControllerPlaying"
},
"hasController": {
newFrames: "bufferingPlaying",
play: "hasControllerPlaying",
noController: "initial"
},
"hasControllerPlaying": {
newFrames: "bufferingPlaying",
pause: "hasController",
noController: "initialPlaying"
},
"buffering": {
play: "bufferingPlaying",
enough: "paused",
noMoreFrames: "pausedAllLoaded",
startSeeking: "seeking",
noController: "initial"
},
"bufferingPlaying": {
pause: "buffering",
enough: "playing",
noMoreFrames: "playingAllLoaded",
startSeeking: "seekingPlaying",
noController: "initialPlaying"
},
"seeking": {
stopSeeking: "buffering",
noMoreFrames: "seekingAllLoaded",
noController: "initial"
},
"seekingPlaying": {
stopSeeking: "bufferingPlaying",
noMoreFrames: "playingAllLoaded",
noController: "initialPlaying"
},
"seekingAllLoaded": {
stopSeeking: "pausedAllLoaded",
noController: "initial"
},
"seekingPlayingAllLoaded": {
stopSeeking: "playingAllLoaded",
noController: "initialPlaying"
},
"paused": {
play: "playing",
notEnough: "buffering",
noController: "initial",
noMoreFrames: "pausedAllLoaded",
startSeeking: "seeking"
},
"pausedAllLoaded": {
play: "playingAllLoaded",
noController: "initial",
startSeeking: "seekingAllLoaded"
},
"playing": {
pause: "paused",
notEnough: "bufferingPlaying",
noMoreFrames: "playingAllLoaded",
noController: "initialPlaying",
startSeeking: "seekingPlaying"
},
"playingAllLoaded": {
pause: "pausedAllLoaded",
noController: "initialPlaying",
startSeeking: "seekingPlayingAllLoaded"
}
}
var seekingStates = ["seeking", "seekingPlaying", "seekingAllLoaded", "seekingPlayingAllLoaded"]
var audioPortion = 1024 * 50; //bytes to download for each portion
var threshold = 2; //seconds to buffer before playing
var intervalPrecision = 100; //millisecond of how often to check the playback
var Slider = Model.inherit({
className: "Slider",
constructor: function(properties) {
Model.fn.constructor.call(this, properties);
this.enabled = true;
this.value = 0;
this.initialized = true;
},
enable: function(en) {
if (en !== this.enabled) {
this.enabled = en;
this.trigger("enabled", en);
}
},
setValue: function(p) {
if (p !== this.value) {
this.value = p;
this.trigger("value", p);
}
}
});
var ProgressModel = Slider.inherit({
className: "ProgressModel",
constructor: function(properties) {
Slider.fn.constructor.call(this, properties);
this.value = 0;
},
setLoad: function(l) {
if (l !== this.load) {
this.load = l;
this.trigger("load", l);
}
}
});
module.exports = Player;

View File

@ -45,7 +45,7 @@ var Vocabulary = Controller.inherit({
var keys = insert.getKeys();
for (var j = 0; j < keys.length; ++j) {
key = keys[i];
key = keys[j];
this.addElement(key, insert.at(key).clone());
}
this.trigger("change", data.clone());

View File

@ -13,5 +13,6 @@ configure_file(themeStorage.js themeStorage.js)
configure_file(vocabulary.js vocabulary.js)
configure_file(attributes.js attributes.js)
configure_file(image.js image.js)
configure_file(button.js button.js)
add_subdirectory(proxy)

90
libjs/wModel/button.js Normal file
View File

@ -0,0 +1,90 @@
"use strict";
var Model = require("./model");
var ModelString = require("./string");
var Vocabulary = require("../wType/vocabulary");
var Boolean = require("../wType/boolean");
var Address = require("../wType/address");
var String = require("../wType/string");
var Button = Model.inherit({
"className": "Button",
"constructor": function(address) {
Model.fn.constructor.call(this, address);
this._enabled = true;
this._hasImage = false;
this._hasLabel =false;
this._imageName = undefined;
this._label = undefined;
this.addHandler("get");
this.addHandler("activate");
},
"setImage": function(name) {
if (this._hasImage) {
if (this._imageName !== name) {
this._image = name;
var vc = new Vocabulary();
vc.insert("image", new String(this._imageName));
this.broadcast(vc, "changeImage");
}
} else {
this._image = name;
this._hasImage = true;
var vc = new Vocabulary();
vc.insert("image", new String(this._imageName));
this.broadcast(vc, "setImage");
}
},
"setEnabled": function(enabled) {
if (enabled !== this._enabled) {
this._enabled = enabled;
var vc = new Vocabulary();
vc.insert("enabled", new Boolean(this._enabled));
this.broadcast(vc, "setEnabled");
}
},
"setLabel": function(text) {
if (this._hasLabel) {
this._label.set(text);
} else {
this._label = new ModelString(this._address["+"](labelHop), text);
this.addModel(this._label);
var vc = new Vocabulary();
vc.insert("hasLabel", new Boolean(true));
vc.insert("label", this._label.getAddress());
this.broadcast(vc, "setLabel");
this._hasLabel = true;
}
},
"_h_subscribe": function(ev) {
Model.fn._h_subscribe.call(this, ev);
this._h_get(ev);
},
"_h_get": function(ev) {
var vc = new Vocabulary();
vc.insert("hasImage", new Boolean(this._hasImage));
if (this._hasImage) {
vc.insert("image", new String(this._imageName));
}
vc.insert("hasLabel", new Boolean(this._hasLabel));
if (this._hasLabel) {
vc.insert("label", this._label.getAddress());
}
vc.insert("enabled", new Boolean(this._enabled));
this.response(vc, "get", ev);
},
"_h_activate": function() {
if (this._enabled) {
this.trigger("activated");
}
}
});
var labelHop = new Address(["label"]);
module.exports = Button;

View File

@ -299,8 +299,10 @@ Model.ModelType = {
String: 0,
List: 1,
Vocabulary: 2,
Image: 3,
Model: 4,
//Catalogue: 3,
Image: 4,
Button: 5,
Model: 6,
Attributes: 50,
@ -310,7 +312,8 @@ Model.ModelType = {
PageStorage: 103,
PanesList: 104,
Theme: 105,
ThemeStorage: 106
ThemeStorage: 106,
Player: 107
};
module.exports = Model;

View File

@ -17,6 +17,36 @@ var Blob = Object.inherit({
return this.size() == other.size(); //TODO let's pretend one shall never wish to compare blobs)
},
"+=": function(other) {
if (this.getType() !== other.getType()) {
throw new Error("An attempt to add and assign an " + other.className + " to " + this.className);
}
var newData = new ArrayBuffer(this._data.byteLength + other._data.byteLength);
var newView = new Uint8Array(newData);
var thisView = new Uint8Array(this._data);
var otherView = new Uint8Array(other._data);
newView.set(thisView, 0);
newView.set(otherView, this._data.byteLength);
this._data = newData;
},
"+": function(other) {
if (this.getType() !== other.getType()) {
throw new Error("An attempt to add an " + other.className + " to " + this.className);
}
var newData = new ArrayBuffer(this._data.byteLength + other._data.byteLength);
var newView = new Uint8Array(newData);
var thisView = new Uint8Array(this._data);
var otherView = new Uint8Array(other._data);
newView.set(thisView, 0);
newView.set(otherView, this._data.byteLength);
return new Blob(newData);
},
"base64": function() {
var arr = new Uint8Array(this._data);
var bin = "";

View File

@ -4,6 +4,7 @@
var defineArray = [];
defineArray.push("lib/utils/class");
defineArray.push("lib/utils/enum");
defineArray.push("lib/wSocket/socket");
defineArray.push("lib/wDispatcher/dispatcher");
defineArray.push("lib/wDispatcher/handler");
@ -18,6 +19,7 @@
defineArray.push("lib/wController/pageStorage");
defineArray.push("lib/wController/page");
defineArray.push("lib/wController/localModel");
defineArray.push("lib/wController/player");
defineArray.push("views/view");
defineArray.push("views/layout");
@ -27,6 +29,7 @@
define(moduleName, defineArray, function lorgar_module() {
var Class = require("lib/utils/class");
var Enum = require("lib/utils/enum");
var Socket = require("lib/wSocket/socket");
var Dispatcher = require("lib/wDispatcher/dispatcher");
var Handler = require("lib/wDispatcher/handler");
@ -41,6 +44,7 @@
var PageStorage = require("lib/wController/pageStorage");
var PageController = require("lib/wController/page");
var LocalModel = require("lib/wController/localModel");
var PlayerModel = require("lib/wController/player");
var View = require("views/view");
var Layout = require("views/layout");
@ -53,16 +57,18 @@
"constructor": function() {
Class.fn.constructor.call(this);
this._playerCtl = undefined;
this._currentPageCtl = undefined;
this._nodes = Object.create(null);
this._initDispatcher();
this._initModels();
this._initViews();
this._prepareNode("Magnus", "localhost", 8081);
this._prepareNode("Corax", "localhost", 8080);
this._initModels();
this._initViews();
this._registerModels();
this.connectNode("Magnus");
this.connectNode("Corax");
@ -70,6 +76,8 @@
},
"destructor": function() {
window.onpopstate = undefined;
this._unregisterModels();
if (this._currentPageCtl) {
this._currentPage.destructor();
this._currentPageCtl.destructor();
@ -77,16 +85,17 @@
this._gc.destructor();
this._ps.destructor();
this._mainColorHelper.destructor();
this._emptyHelper.destructor();
this._body.destructor();
this.coraxSocket.close();
this.dispatcher.unregisterHandler(this._playerResponseHandler);
this.dispatcher.unregisterDefaultHandler(this._logger);
this._logger.destructor();
this.dispatcher.destructor();
this._playerResponseHandler.destructor();
//this.magnusSocket.destructor();
//this.coraxSocket.destructor();
@ -106,31 +115,37 @@
}
node.socket.open(node.address, node.port);
node.state.setData(SocketState.straight.connecting);
},
"_initCoraxSocket": function() {
this.coraxSocket = new Socket("Lorgar");
this.coraxSocket.on("connected", this._coraxSocketConnected, this);
this.coraxSocket.on("disconnected", this._coraxSocketDisconnected, this);
this.coraxSocket.on("error", this._coraxSocketError, this);
this.coraxSocket.on("message", this.dispatcher.pass, this.dispatcher);
_registerModels: function () {
this._gc.register(this.dispatcher, this._nodes.Magnus.socket);
this._ps.register(this.dispatcher, this._nodes.Magnus.socket);
},
_unregisterModels: function() {
if (this._currentPageCtl) {
this._currentPageCtl.unregister();
}
this._gc.unregister();
this._ps.unregister();
},
"_initDispatcher": function() {
this.dispatcher = new Dispatcher();
this._logger = new Logger();
this._playerResponseHandler = new Handler(new Address(["getPlayer"]), this, this._responsePlayer);
this.dispatcher.registerDefaultHandler(this._logger);
this.dispatcher.registerHandler(this._playerResponseHandler);
},
"_initModels": function() {
this._gc = new GlobalControls(new Address(["magnus", "gc"]));
this._ps = new PageStorage(new Address(["magnus", "ps"]));
this._mainColorHelper = new LocalModel({backgroundColor: "mainColor"});
this._emptyHelper = new LocalModel();
this._gc.on("serviceMessage", this._onServiceMessage, this);
this._ps.on("serviceMessage", this._onServiceMessage, this);
this._gc.on("themeSelected", this.setTheme, this);
this._gc.register(this.dispatcher, this._nodes.Magnus.socket);
this._ps.register(this.dispatcher, this._nodes.Magnus.socket);
this._ps.on("pageName", this._onPageName, this);
},
"_initPageController": function(addr) {
@ -150,18 +165,10 @@
document.body.innerHTML = "";
document.body.appendChild(this._body._e);
window.addEventListener("resize",this._onWindowResize.bind(this) ,false);
window.addEventListener("resize", this._onWindowResize.bind(this), false);
this._body.setSize(document.body.offsetWidth, document.body.offsetHeight);
this._body.append(this._mainLayout);
var spacerL = new View(this._mainColorHelper, {
maxWidth: 50
});
var spacerR = new View(this._mainColorHelper, {
maxWidth: 50
});
this._mainLayout.append(spacerL, 1, 0, 1, 1);
this._mainLayout.append(spacerR, 1, 2, 1, 1);
},
"_onHistoryPopState": function(e) {
this._initPageController(new Address(e.state.address));
@ -171,10 +178,29 @@
address: this._currentPageCtl.getPairAddress().toArray()
}, "", name);
},
"_onServiceMessage": function(text, severity) {
var fn;
switch (severity) {
case 2:
fn = console.error;
break;
case 1:
fn = console.warn;
break;
case 0:
default:
fn = console.info;
break;
}
fn(text);
},
"_onSocketConnected": function(name) {
console.log(name + " socket connected");
var node = this._nodes[name];
node.connected = true;
node.state.setData(SocketState.straight.connected);
for (var id in node.foreigns) {
if (node.foreigns[id].subscribed) {
@ -182,13 +208,18 @@
}
}
if (name === "Magnus") {
this._gc.subscribe();
switch (name) {
case "Magnus":
this._gc.subscribe();
if (!this._currentPageCtl) {
this._ps.getPageAddress(location.pathname);
this._ps.one("pageAddress", this._initPageController, this);
}
if (!this._currentPageCtl) {
this._ps.getPageAddress(location.pathname);
this._ps.one("pageAddress", this._initPageController, this);
}
break;
case "Corax":
this._requestPlayer();
break;
}
},
"_onSocketDisconnected": function(name) {
@ -196,19 +227,44 @@
var node = this._nodes[name];
node.connected = false;
switch (name) {
case "Corax":
if (this._playerCtl) {
this._mainLayout.removePlayer();
this._playerCtl._onSocketDisconnected();
this._playerCtl.unregister();
this._playerCtl.destructor();
this._playerCtl = undefined;
}
}
for (var id in node.foreigns) {
if (node.foreigns[id].subscribed) {
node.foreigns[id].controller._onSocketDisconnected;
node.foreigns[id].controller._onSocketDisconnected();
}
}
node.state.setData(SocketState.straight.disconnected);
},
"_onSocketError": function(name) {
"_onSocketError": function(name, e) {
console.log(name + " socket error: ");
console.log(e);
},
"_onWindowResize": function() {
this._body.setSize(document.body.offsetWidth, document.body.offsetHeight);
},
"play": function(id) {
if (this._nodes.Corax && this._nodes.Corax.connected) {
var vc = new Vocabulary();
vc.insert("id", id.clone());
var ev = new Event(this._playerCtl.getPairAddress()["+="](play), vc);
var socket = this._nodes.Corax.socket;
ev.setSenderId(socket.getId().clone());
socket.send(ev);
ev.destructor();
}
},
"_prepareNode": function(name, address, port) {
if (this._nodes[name]) {
throw new Error("An attempt to prepeare node " + name + " for the second time");
@ -220,22 +276,27 @@
obj.socket = new Socket("Lorgar");
obj.connected = false;
obj.foreigns = Object.create(null);
obj.state = new LocalModel({fontFamily: "casualFont"});
obj.state.enum = SocketState;
obj.state.setData(SocketState.straight.disconnected);
obj.socket.on("connected", this._onSocketConnected.bind(this, name));
obj.socket.on("disconnected", this._onSocketDisconnected.bind(this, name));
obj.socket.on("error", this._onSocketError.bind(this, name));
obj.socket.on("message", this.dispatcher.pass, this.dispatcher);
this._mainLayout.addState(name, obj.state);
this._nodes[name] = obj;
},
"registerForeignController": function(node, controller) {
var node = this._nodes[node];
"registerForeignController": function(nodeName, controller) {
var node = this._nodes[nodeName];
if (node === undefined) {
throw new Error("An attempt to register controller to an unknown node " + node);
throw new Error("An attempt to register controller to an unknown node " + nodeName);
}
if (node.foreigns[controller.id] !== undefined) {
throw new Error("An attempt to register a controller under node " + node + " for a second time");
throw new Error("An attempt to register a controller under node " + nodeName + " for a second time");
}
var obj = Object.create(null);
obj.controller = controller;
@ -243,47 +304,89 @@
node.foreigns[controller.id] = obj;
controller.register(this.dispatcher, node.socket);
},
"_requestPlayer": function() {
var vc = new Vocabulary();
vc.insert("source", new Address([]));
var ev = new Event(new Address(["management", "givePlayer"]), vc);
var socket = this._nodes.Corax.socket;
ev.setSenderId(socket.getId().clone());
socket.send(ev);
ev.destructor();
},
"_responsePlayer": function(ev) {
var data = ev.getData();
this._playerCtl = new PlayerModel(data.at("address").clone());
this._playerCtl.register(this.dispatcher, this._nodes["Corax"].socket);
this._playerCtl.subscribe();
this._mainLayout.appendPlayer(this._playerCtl);
this._playerCtl.on("serviceMessage", this._onServiceMessage, this);
},
"scheduleToPlay": function(id) {
if (this._nodes.Corax && this._nodes.Corax.connected) {
var vc = new Vocabulary();
vc.insert("id", id.clone());
var ev = new Event(this._playerCtl.getPairAddress()["+="](queue), vc);
var socket = this._nodes.Corax.socket;
ev.setSenderId(socket.getId().clone());
socket.send(ev);
ev.destructor();
}
},
"setTheme": function(theme) {
View.setTheme(theme);
},
"subscribeForeignController": function(node, controller) {
var node = this._nodes[node];
"subscribeForeignController": function(nodeName, controller) {
var node = this._nodes[nodeName];
if (node === undefined) {
throw new Error("An attempt to subscribe a controller to an unknown node " + node);
throw new Error("An attempt to subscribe a controller to an unknown node " + nodeName);
}
if (node.foreigns[controller.id] === undefined) {
throw new Error("An attempt to subscribe not registered controller to node " + node);
throw new Error("An attempt to subscribe not registered controller to node " + nodeName);
}
node.foreigns[controller.id].subscribed = true;
controller.subscribe();
},
"unregisterForeignController": function(node, controller) {
var node = this._nodes[node];
"unregisterForeignController": function(nodeName, controller) {
var node = this._nodes[nodeName];
if (node === undefined) {
throw new Error("An attempt to unregister a controller from an unknown node " + node);
throw new Error("An attempt to unregister a controller from an unknown node " + nodeName);
}
if (node.foreigns[controller.id] === undefined) {
throw new Error("An attempt to unregister not registered controller from node " + node);
throw new Error("An attempt to unregister not registered controller from node " + nodeName);
}
delete node.foreigns[controller.id];
controller.unregister();
},
"unsubscribeForeignController": function(node, controller) {
var node = this._nodes[node];
"unsubscribeForeignController": function(nodeName, controller) {
var node = this._nodes[nodeName];
if (node === undefined) {
throw new Error("An attempt to unsubscribe a controller from an unknown node " + node);
throw new Error("An attempt to unsubscribe a controller from an unknown node " + nodeName);
}
if (node.foreigns[controller.id] === undefined) {
throw new Error("An attempt to unsubscribe not registered controller from node " + node);
throw new Error("An attempt to unsubscribe not registered controller from node " + nodeName);
}
node.foreigns[controller.id].subscribed = false;
controller.unsubscribe();
}
});
var SocketState = new Enum("SocketState", ["description"]);
SocketState.add("disconnected", {description: "Socket is disconnected"});
SocketState.add("connecting", {description: "Socket is connecting to remote host"});
SocketState.add("connected", {description: "Socket is connected"});
var queue = new Address(["queue"]);
var play = new Address(["play"]);
return Lorgar;
});
})();

View File

@ -45,3 +45,15 @@ div.dragging .draggable {
cursor: -moz-grabbing;
cursor: grabbing;
}
.disabled {
opacity: 0.5;
}
.button {
cursor: pointer
}
.button.disabled {
cursor: not-allowed
}

View File

@ -10,4 +10,4 @@
<body>
<p>I am</p>
</body>
</html>
</html>

View File

@ -10,3 +10,4 @@ add_subdirectory(wDispatcher)
add_subdirectory(wTest)
add_subdirectory(wController)
add_subdirectory(fonts)
add_subdirectory(em)

View File

@ -1,3 +1,3 @@
cmake_minimum_required(VERSION 2.8.12)
configure_file(index.js index.js)
configure_file(index.js index.js)

View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 2.8.12)
execute_process(COMMAND
emcc --bind
${CMAKE_CURRENT_SOURCE_DIR}/libmad.bc
${CMAKE_CURRENT_SOURCE_DIR}/decoder.cpp
-o ${CMAKE_CURRENT_BINARY_DIR}/wrapper.js
-s FILESYSTEM=0
-s ENVIRONMENT=web
-O3
--llvm-lto 1
)

347
lorgar/lib/em/decoder.cpp Normal file
View File

@ -0,0 +1,347 @@
#include "decoder.h"
#include <iostream>
Decoder::Decoder():
state(empty),
sampleRate(0),
channels(0),
cachedLength(0),
samplesPerFrame(0),
glue(new uint8_t[GLUE_LENGTH]),
cachedNext(NULL),
cachedThis(NULL),
cachedError(MAD_ERROR_NONE),
cached(false),
synth(new mad_synth()),
stream(new mad_stream()),
frame(new mad_frame()),
context(0),
pending()
{
for (int i = 0; i < GLUE_LENGTH; ++i) {
glue[i] = 0;
}
mad_frame_init(frame);
mad_stream_init(stream);
mad_synth_init(synth);
emscripten::val AudioContext = emscripten::val::global("AudioContext");
if (!AudioContext.as<bool>()) {
AudioContext = emscripten::val::global("webkitAudioContext");
}
context = AudioContext.new_();
}
Decoder::~Decoder()
{
context.call<void>("close");
mad_synth_finish(synth);
mad_stream_finish(stream);
mad_frame_finish(frame);
delete synth;
delete stream;
delete frame;
delete[] glue;
}
void Decoder::addFragment(const emscripten::val& array)
{
uint32_t length = array["length"].as<uint32_t>();
if (length < GLUE_LENGTH / 2) {
std::cout << "Error: an attempt to add fragment smaller then half of the glue buffer, ignoring";
return;
}
uint8_t* buffer = new uint8_t[length];
for (int i = 0; i < length; ++i) {
buffer[i] = array[std::to_string(i)].as<uint8_t>();
}
RawBuffer rb = {buffer, length};
pending.push_back(rb);
switch (state) {
case empty:
mad_stream_buffer(stream, buffer, length);
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = buffer[length - GLUE_LENGTH/2 + i];
}
state = onBufferHalf;
prepareNextBuffer();
break;
case onBufferHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}
state = onBufferFull;
break;
case onBufferFull:
break;
case onGlueHalf:
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = buffer[i];
}
state = onGlueFull;
cached = false;
prepareNextBuffer();
break;
case onGlueFull:
break;
}
}
emscripten::val Decoder::decode(uint32_t count)
{
emscripten::val ret = emscripten::val::undefined();
int available = framesLeft(count);
int success = 0;
if (available > 0) {
ret = context.call<emscripten::val>("createBuffer", channels, available * samplesPerFrame, sampleRate);
std::vector<emscripten::val> chans(channels, emscripten::val::undefined());
for (int i = 0; i < channels; ++i) {
chans[i] = ret.call<emscripten::val>("getChannelData", i);
}
for (int i = 0; success < available; ++i) {
int res = mad_frame_decode(frame, stream);
if (res != 0) {
if (MAD_RECOVERABLE(stream->error)) {
std::cout << "Unexpected error during the decoding process: " << mad_stream_errorstr(stream) << std::endl;
continue;
} else {
break;
}
}
mad_synth_frame(synth, frame);
for (int j = 0; j < samplesPerFrame; ++j) {
for (int k = 0; k < channels; ++k) {
float value = mad_f_todouble(synth->pcm.samples[k][j]);
chans[k].set(std::to_string(success * samplesPerFrame + j), emscripten::val(value));
}
}
++success;
}
cachedLength -= available;
#if DEBUGGING
std::cout << "Processed " << available << " frames, " << success << " successfully, last error " << mad_stream_errorstr(stream) << std::endl;
#endif
if (cachedLength == 0) {
cached = false;
prepareNextBuffer();
}
}
return ret;
}
bool Decoder::hasMore() const
{
if (pending.size() == 1) {
return stream->error != MAD_ERROR_BUFLEN;
} else {
return true;
}
}
uint32_t Decoder::framesLeft(uint32_t max)
{
if (state == empty || state == onGlueHalf) {
return 0;
}
if (cached == false) {
mad_stream probe;
mad_header ph;
initializeProbe(probe);
mad_header_init(&ph);
sampleRate = 0;
while (cachedLength < max) {
if (mad_header_decode(&ph, &probe) == 0) {
if (sampleRate == 0) {
sampleRate = ph.samplerate;
channels = MAD_NCHANNELS(&ph);
samplesPerFrame = MAD_NSBSAMPLES(&ph) * 32; //not sure why 32, it's in libmad source
} else {
if (sampleRate != ph.samplerate || channels != MAD_NCHANNELS(&ph) || samplesPerFrame != MAD_NSBSAMPLES(&ph) * 32) {
if (cachedLength > 0) {
#if DEBUGGING
std::cout << "sample rate " << sampleRate << " -> " << ph.samplerate << std::endl;
std::cout << "channels " << channels << " -> " << MAD_NCHANNELS(&ph) << std::endl;
std::cout << "samples per frame " << samplesPerFrame << " -> " << MAD_NSBSAMPLES(&ph) * 32 << std::endl;
#endif
probe.next_frame = probe.this_frame;
break;
}
}
}
if (probe.next_frame > probe.this_frame) {
++cachedLength;
}
} else {
#if DEBUGGING
std::cout << "framesLeft error: " << mad_stream_errorstr(&probe) << std::endl;
#endif
if (!MAD_RECOVERABLE(probe.error)) {
break;
}
}
}
cachedNext = probe.next_frame;
cachedThis = probe.this_frame;
cachedError = probe.error;
mad_header_finish(&ph);
mad_stream_finish(&probe);
#if DEBUGGING
std::cout << cachedLength << " frames are available for decoding" << std::endl;
#endif
cached = true;
}
return std::min(cachedLength, max);
}
void Decoder::pullBuffer()
{
if (cached == false) {
std::cout << "Error in pullBuffer method!" << std::endl;
}
stream->this_frame = cachedThis;
stream->next_frame = cachedNext;
stream->error = cachedError;
}
void Decoder::changeBuffer()
{
switch (state) {
case empty:
std::cout << "Wrong state on switchBuffer method - empty, aborting" << std::endl;
case onBufferHalf:
switchToGlue();
state = onGlueHalf;
break;
case onBufferFull:
switchToGlue();
state = onGlueFull;
break;
case onGlueHalf:
std::cout << "Wrong state on switchBuffer method - onGlueHalf, aborting" << std::endl;
break;
case onGlueFull:
#if DEBUGGING
std::cout << "Having another fragment " << pending[0].length << " bytes long" << std::endl;
#endif
switchBuffer(pending[0].ptr, pending[0].length);
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[i] = pending[0].ptr[pending[0].length - GLUE_LENGTH/2 + i];
}
state = onBufferHalf;
if (pending.size() > 1) {
for (int i = 0; i < GLUE_LENGTH/2; ++i) {
glue[GLUE_LENGTH/2 + i] = pending[1].ptr[i];
}
state = onBufferFull;
}
}
cached = false;
}
void Decoder::prepareNextBuffer()
{
bool shift;
do {
shift = false;
framesLeft();
if (cachedLength == 0 && state != empty && state != onGlueHalf) {
pullBuffer();
changeBuffer();
shift = true;
}
} while (shift);
}
void Decoder::initializeProbe(mad_stream& probe)
{
mad_stream_init(&probe);
probe.buffer = stream->buffer;
probe.bufend = stream->bufend;
probe.skiplen = stream->skiplen;
//probe.sync = stream->sync;
//probe.freerate = stream->freerate;
//probe.this_frame = stream->this_frame;
probe.next_frame = stream->next_frame;
//probe.ptr.byte = stream->ptr.byte;
//probe.ptr.cache = stream->ptr.cache;
//probe.ptr.cache = stream->ptr.cache;
//probe.anc_ptr.byte = stream->anc_ptr.byte;
//probe.anc_ptr.cache = stream->anc_ptr.cache;
//probe.anc_ptr.cache = stream->anc_ptr.cache;
//probe.anc_bitlen = stream->anc_bitlen;
//probe.main_data = stream.main_data;
//probe.md_len = stream.md_len;
//probe.options = stream->options;
//probe.error = stream->error;
}
void Decoder::switchToGlue()
{
#if DEBUGGING
std::cout << "Switching to glue" << std::endl;
#endif
switchBuffer(glue, GLUE_LENGTH);
#if DEBUGGING
std::cout << "Freeing the drained fragment" << std::endl;
#endif
delete[] pending[0].ptr;
pending.pop_front();
}
void Decoder::switchBuffer(uint8_t* bufferPtr, uint32_t length)
{
uint32_t left;
if (stream->error != MAD_ERROR_BUFLEN) {
std::cout << "WARNING: Switching buffers while the previous one is not drained, last error: " << mad_stream_errorstr(stream) << std::endl;
}
if (stream->next_frame != NULL) {
left = stream->bufend - stream->next_frame;
} else {
std::cout << "WARNING: not supposed to happen" << std::endl;
}
if (left > GLUE_LENGTH / 2) {
std::cout << "Error: bytes to read in the buffer are more then glue buffer can fit (" << left << ")" << std::endl;
throw 1;
}
mad_stream_buffer(stream, bufferPtr + GLUE_LENGTH / 2 - left, length - (GLUE_LENGTH / 2 - left));
stream->error = MAD_ERROR_NONE;
while (mad_header_decode(&frame->header, stream) != 0 && stream->error != MAD_ERROR_BUFLEN) {}
}

79
lorgar/lib/em/decoder.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef DECODER_H
#define DECODER_H
/**
* @todo write docs
*/
#include <deque>
#include <emscripten/val.h>
#include <emscripten/bind.h>
#include "mad.h"
#define GLUE_LENGTH 6000
#define DEBUGGING false
class Decoder {
public:
Decoder();
~Decoder();
void addFragment(const emscripten::val& array);
emscripten::val decode(uint32_t count = UINT32_MAX);
bool hasMore() const;
uint32_t framesLeft(uint32_t max = UINT32_MAX);
private:
enum State {
empty,
onBufferHalf,
onBufferFull,
onGlueHalf,
onGlueFull
};
struct RawBuffer {
uint8_t* ptr;
uint32_t length;
};
State state;
uint32_t sampleRate;
uint8_t channels;
uint32_t cachedLength;
uint16_t samplesPerFrame;
uint8_t* glue;
uint8_t const* cachedNext;
uint8_t const* cachedThis;
mad_error cachedError;
bool cached;
mad_synth* synth;
mad_stream* stream;
mad_frame* frame;
emscripten::val context;
std::deque<RawBuffer> pending;
private:
void pullBuffer();
void changeBuffer();
void prepareNextBuffer();
void initializeProbe(mad_stream& probe);
void switchToGlue();
void switchBuffer(uint8_t* bufferPtr, uint32_t length);
};
EMSCRIPTEN_BINDINGS(jsmad) {
emscripten::class_<Decoder>("Decoder")
.constructor<>()
.function("addFragment", &Decoder::addFragment)
.function("hasMore", &Decoder::hasMore)
.function("framesLeft", &Decoder::framesLeft)
.function("decode", &Decoder::decode);
}
#endif // DECODER_H

BIN
lorgar/lib/em/libmad.bc Normal file

Binary file not shown.

964
lorgar/lib/em/mad.h Normal file
View File

@ -0,0 +1,964 @@
/*
* libmad - MPEG audio decoder library
* Copyright (C) 2000-2004 Underbit Technologies, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* If you would like to negotiate alternate licensing terms, you may do
* so by contacting: Underbit Technologies, Inc. <info@underbit.com>
*/
# ifdef __cplusplus
extern "C" {
# endif
# define FPM_64BIT
# define SIZEOF_INT 4
# define SIZEOF_LONG 4
# define SIZEOF_LONG_LONG 8
/* Id: version.h,v 1.26 2004/01/23 09:41:33 rob Exp */
# ifndef LIBMAD_VERSION_H
# define LIBMAD_VERSION_H
# define MAD_VERSION_MAJOR 0
# define MAD_VERSION_MINOR 15
# define MAD_VERSION_PATCH 1
# define MAD_VERSION_EXTRA " (beta)"
# define MAD_VERSION_STRINGIZE(str) #str
# define MAD_VERSION_STRING(num) MAD_VERSION_STRINGIZE(num)
# define MAD_VERSION MAD_VERSION_STRING(MAD_VERSION_MAJOR) "." \
MAD_VERSION_STRING(MAD_VERSION_MINOR) "." \
MAD_VERSION_STRING(MAD_VERSION_PATCH) \
MAD_VERSION_EXTRA
# define MAD_PUBLISHYEAR "2000-2004"
# define MAD_AUTHOR "Underbit Technologies, Inc."
# define MAD_EMAIL "info@underbit.com"
extern char const mad_version[];
extern char const mad_copyright[];
extern char const mad_author[];
extern char const mad_build[];
# endif
/* Id: fixed.h,v 1.38 2004/02/17 02:02:03 rob Exp */
# ifndef LIBMAD_FIXED_H
# define LIBMAD_FIXED_H
# if SIZEOF_INT >= 4
typedef signed int mad_fixed_t;
typedef signed int mad_fixed64hi_t;
typedef unsigned int mad_fixed64lo_t;
# else
typedef signed long mad_fixed_t;
typedef signed long mad_fixed64hi_t;
typedef unsigned long mad_fixed64lo_t;
# endif
# if defined(_MSC_VER)
# define mad_fixed64_t signed __int64
# elif 1 || defined(__GNUC__)
# define mad_fixed64_t signed long long
# endif
# if defined(FPM_FLOAT)
typedef double mad_sample_t;
# else
typedef mad_fixed_t mad_sample_t;
# endif
/*
* Fixed-point format: 0xABBBBBBB
* A == whole part (sign + 3 bits)
* B == fractional part (28 bits)
*
* Values are signed two's complement, so the effective range is:
* 0x80000000 to 0x7fffffff
* -8.0 to +7.9999999962747097015380859375
*
* The smallest representable value is:
* 0x00000001 == 0.0000000037252902984619140625 (i.e. about 3.725e-9)
*
* 28 bits of fractional accuracy represent about
* 8.6 digits of decimal accuracy.
*
* Fixed-point numbers can be added or subtracted as normal
* integers, but multiplication requires shifting the 64-bit result
* from 56 fractional bits back to 28 (and rounding.)
*
* Changing the definition of MAD_F_FRACBITS is only partially
* supported, and must be done with care.
*/
# define MAD_F_FRACBITS 28
# if MAD_F_FRACBITS == 28
# define MAD_F(x) ((mad_fixed_t) (x##L))
# else
# if MAD_F_FRACBITS < 28
# warning "MAD_F_FRACBITS < 28"
# define MAD_F(x) ((mad_fixed_t) \
(((x##L) + \
(1L << (28 - MAD_F_FRACBITS - 1))) >> \
(28 - MAD_F_FRACBITS)))
# elif MAD_F_FRACBITS > 28
# error "MAD_F_FRACBITS > 28 not currently supported"
# define MAD_F(x) ((mad_fixed_t) \
((x##L) << (MAD_F_FRACBITS - 28)))
# endif
# endif
# define MAD_F_MIN ((mad_fixed_t) -0x80000000L)
# define MAD_F_MAX ((mad_fixed_t) +0x7fffffffL)
# define MAD_F_ONE MAD_F(0x10000000)
# define mad_f_tofixed(x) ((mad_fixed_t) \
((x) * (double) (1L << MAD_F_FRACBITS) + 0.5))
# define mad_f_todouble(x) ((double) \
((x) / (double) (1L << MAD_F_FRACBITS)))
# define mad_f_intpart(x) ((x) >> MAD_F_FRACBITS)
# define mad_f_fracpart(x) ((x) & ((1L << MAD_F_FRACBITS) - 1))
/* (x should be positive) */
# define mad_f_fromint(x) ((x) << MAD_F_FRACBITS)
# define mad_f_add(x, y) ((x) + (y))
# define mad_f_sub(x, y) ((x) - (y))
# if defined(FPM_FLOAT)
# error "FPM_FLOAT not yet supported"
# undef MAD_F
# define MAD_F(x) mad_f_todouble(x)
# define mad_f_mul(x, y) ((x) * (y))
# define mad_f_scale64
# undef ASO_ZEROCHECK
# elif defined(FPM_64BIT)
/*
* This version should be the most accurate if 64-bit types are supported by
* the compiler, although it may not be the most efficient.
*/
# if defined(OPT_ACCURACY)
# define mad_f_mul(x, y) \
((mad_fixed_t) \
((((mad_fixed64_t) (x) * (y)) + \
(1L << (MAD_F_SCALEBITS - 1))) >> MAD_F_SCALEBITS))
# else
# define mad_f_mul(x, y) \
((mad_fixed_t) (((mad_fixed64_t) (x) * (y)) >> MAD_F_SCALEBITS))
# endif
# define MAD_F_SCALEBITS MAD_F_FRACBITS
/* --- Intel --------------------------------------------------------------- */
# elif defined(FPM_INTEL)
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4035) /* no return value */
static __forceinline
mad_fixed_t mad_f_mul_inline(mad_fixed_t x, mad_fixed_t y)
{
enum {
fracbits = MAD_F_FRACBITS
};
__asm {
mov eax, x
imul y
shrd eax, edx, fracbits
}
/* implicit return of eax */
}
# pragma warning(pop)
# define mad_f_mul mad_f_mul_inline
# define mad_f_scale64
# else
/*
* This Intel version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
asm ("imull %3" \
: "=a" (lo), "=d" (hi) \
: "%a" (x), "rm" (y) \
: "cc")
# if defined(OPT_ACCURACY)
/*
* This gives best accuracy but is not very fast.
*/
# define MAD_F_MLA(hi, lo, x, y) \
({ mad_fixed64hi_t __hi; \
mad_fixed64lo_t __lo; \
MAD_F_MLX(__hi, __lo, (x), (y)); \
asm ("addl %2,%0\n\t" \
"adcl %3,%1" \
: "=rm" (lo), "=rm" (hi) \
: "r" (__lo), "r" (__hi), "0" (lo), "1" (hi) \
: "cc"); \
})
# endif /* OPT_ACCURACY */
# if defined(OPT_ACCURACY)
/*
* Surprisingly, this is faster than SHRD followed by ADC.
*/
# define mad_f_scale64(hi, lo) \
({ mad_fixed64hi_t __hi_; \
mad_fixed64lo_t __lo_; \
mad_fixed_t __result; \
asm ("addl %4,%2\n\t" \
"adcl %5,%3" \
: "=rm" (__lo_), "=rm" (__hi_) \
: "0" (lo), "1" (hi), \
"ir" (1L << (MAD_F_SCALEBITS - 1)), "ir" (0) \
: "cc"); \
asm ("shrdl %3,%2,%1" \
: "=rm" (__result) \
: "0" (__lo_), "r" (__hi_), "I" (MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# elif defined(OPT_INTEL)
/*
* Alternate Intel scaling that may or may not perform better.
*/
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("shrl %3,%1\n\t" \
"shll %4,%2\n\t" \
"orl %2,%1" \
: "=rm" (__result) \
: "0" (lo), "r" (hi), \
"I" (MAD_F_SCALEBITS), "I" (32 - MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# else
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("shrdl %3,%2,%1" \
: "=rm" (__result) \
: "0" (lo), "r" (hi), "I" (MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# endif /* OPT_ACCURACY */
# define MAD_F_SCALEBITS MAD_F_FRACBITS
# endif
/* --- ARM ----------------------------------------------------------------- */
# elif defined(FPM_ARM)
/*
* This ARM V4 version is as accurate as FPM_64BIT but much faster. The
* least significant bit is properly rounded at no CPU cycle cost!
*/
# if 1
/*
* This is faster than the default implementation via MAD_F_MLX() and
* mad_f_scale64().
*/
# define mad_f_mul(x, y) \
({ mad_fixed64hi_t __hi; \
mad_fixed64lo_t __lo; \
mad_fixed_t __result; \
asm ("smull %0, %1, %3, %4\n\t" \
"movs %0, %0, lsr %5\n\t" \
"adc %2, %0, %1, lsl %6" \
: "=&r" (__lo), "=&r" (__hi), "=r" (__result) \
: "%r" (x), "r" (y), \
"M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# endif
# define MAD_F_MLX(hi, lo, x, y) \
asm ("smull %0, %1, %2, %3" \
: "=&r" (lo), "=&r" (hi) \
: "%r" (x), "r" (y))
# define MAD_F_MLA(hi, lo, x, y) \
asm ("smlal %0, %1, %2, %3" \
: "+r" (lo), "+r" (hi) \
: "%r" (x), "r" (y))
# define MAD_F_MLN(hi, lo) \
asm ("rsbs %0, %2, #0\n\t" \
"rsc %1, %3, #0" \
: "=r" (lo), "=r" (hi) \
: "0" (lo), "1" (hi) \
: "cc")
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("movs %0, %1, lsr %3\n\t" \
"adc %0, %0, %2, lsl %4" \
: "=&r" (__result) \
: "r" (lo), "r" (hi), \
"M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# define MAD_F_SCALEBITS MAD_F_FRACBITS
/* --- MIPS ---------------------------------------------------------------- */
# elif defined(FPM_MIPS)
/*
* This MIPS version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
asm ("mult %2,%3" \
: "=l" (lo), "=h" (hi) \
: "%r" (x), "r" (y))
# if defined(HAVE_MADD_ASM)
# define MAD_F_MLA(hi, lo, x, y) \
asm ("madd %2,%3" \
: "+l" (lo), "+h" (hi) \
: "%r" (x), "r" (y))
# elif defined(HAVE_MADD16_ASM)
/*
* This loses significant accuracy due to the 16-bit integer limit in the
* multiply/accumulate instruction.
*/
# define MAD_F_ML0(hi, lo, x, y) \
asm ("mult %2,%3" \
: "=l" (lo), "=h" (hi) \
: "%r" ((x) >> 12), "r" ((y) >> 16))
# define MAD_F_MLA(hi, lo, x, y) \
asm ("madd16 %2,%3" \
: "+l" (lo), "+h" (hi) \
: "%r" ((x) >> 12), "r" ((y) >> 16))
# define MAD_F_MLZ(hi, lo) ((mad_fixed_t) (lo))
# endif
# if defined(OPT_SPEED)
# define mad_f_scale64(hi, lo) \
((mad_fixed_t) ((hi) << (32 - MAD_F_SCALEBITS)))
# define MAD_F_SCALEBITS MAD_F_FRACBITS
# endif
/* --- SPARC --------------------------------------------------------------- */
# elif defined(FPM_SPARC)
/*
* This SPARC V8 version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
asm ("smul %2, %3, %0\n\t" \
"rd %%y, %1" \
: "=r" (lo), "=r" (hi) \
: "%r" (x), "rI" (y))
/* --- PowerPC ------------------------------------------------------------- */
# elif defined(FPM_PPC)
/*
* This PowerPC version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
do { \
asm ("mullw %0,%1,%2" \
: "=r" (lo) \
: "%r" (x), "r" (y)); \
asm ("mulhw %0,%1,%2" \
: "=r" (hi) \
: "%r" (x), "r" (y)); \
} \
while (0)
# if defined(OPT_ACCURACY)
/*
* This gives best accuracy but is not very fast.
*/
# define MAD_F_MLA(hi, lo, x, y) \
({ mad_fixed64hi_t __hi; \
mad_fixed64lo_t __lo; \
MAD_F_MLX(__hi, __lo, (x), (y)); \
asm ("addc %0,%2,%3\n\t" \
"adde %1,%4,%5" \
: "=r" (lo), "=r" (hi) \
: "%r" (lo), "r" (__lo), \
"%r" (hi), "r" (__hi) \
: "xer"); \
})
# endif
# if defined(OPT_ACCURACY)
/*
* This is slower than the truncating version below it.
*/
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result, __round; \
asm ("rotrwi %0,%1,%2" \
: "=r" (__result) \
: "r" (lo), "i" (MAD_F_SCALEBITS)); \
asm ("extrwi %0,%1,1,0" \
: "=r" (__round) \
: "r" (__result)); \
asm ("insrwi %0,%1,%2,0" \
: "+r" (__result) \
: "r" (hi), "i" (MAD_F_SCALEBITS)); \
asm ("add %0,%1,%2" \
: "=r" (__result) \
: "%r" (__result), "r" (__round)); \
__result; \
})
# else
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("rotrwi %0,%1,%2" \
: "=r" (__result) \
: "r" (lo), "i" (MAD_F_SCALEBITS)); \
asm ("insrwi %0,%1,%2,0" \
: "+r" (__result) \
: "r" (hi), "i" (MAD_F_SCALEBITS)); \
__result; \
})
# endif
# define MAD_F_SCALEBITS MAD_F_FRACBITS
/* --- Default ------------------------------------------------------------- */
# elif defined(FPM_DEFAULT)
/*
* This version is the most portable but it loses significant accuracy.
* Furthermore, accuracy is biased against the second argument, so care
* should be taken when ordering operands.
*
* The scale factors are constant as this is not used with SSO.
*
* Pre-rounding is required to stay within the limits of compliance.
*/
# if defined(OPT_SPEED)
# define mad_f_mul(x, y) (((x) >> 12) * ((y) >> 16))
# else
# define mad_f_mul(x, y) ((((x) + (1L << 11)) >> 12) * \
(((y) + (1L << 15)) >> 16))
# endif
/* ------------------------------------------------------------------------- */
# else
# error "no FPM selected"
# endif
/* default implementations */
# if !defined(mad_f_mul)
# define mad_f_mul(x, y) \
({ register mad_fixed64hi_t __hi; \
register mad_fixed64lo_t __lo; \
MAD_F_MLX(__hi, __lo, (x), (y)); \
mad_f_scale64(__hi, __lo); \
})
# endif
# if !defined(MAD_F_MLA)
# define MAD_F_ML0(hi, lo, x, y) ((lo) = mad_f_mul((x), (y)))
# define MAD_F_MLA(hi, lo, x, y) ((lo) += mad_f_mul((x), (y)))
# define MAD_F_MLN(hi, lo) ((lo) = -(lo))
# define MAD_F_MLZ(hi, lo) ((void) (hi), (mad_fixed_t) (lo))
# endif
# if !defined(MAD_F_ML0)
# define MAD_F_ML0(hi, lo, x, y) MAD_F_MLX((hi), (lo), (x), (y))
# endif
# if !defined(MAD_F_MLN)
# define MAD_F_MLN(hi, lo) ((hi) = ((lo) = -(lo)) ? ~(hi) : -(hi))
# endif
# if !defined(MAD_F_MLZ)
# define MAD_F_MLZ(hi, lo) mad_f_scale64((hi), (lo))
# endif
# if !defined(mad_f_scale64)
# if defined(OPT_ACCURACY)
# define mad_f_scale64(hi, lo) \
((((mad_fixed_t) \
(((hi) << (32 - (MAD_F_SCALEBITS - 1))) | \
((lo) >> (MAD_F_SCALEBITS - 1)))) + 1) >> 1)
# else
# define mad_f_scale64(hi, lo) \
((mad_fixed_t) \
(((hi) << (32 - MAD_F_SCALEBITS)) | \
((lo) >> MAD_F_SCALEBITS)))
# endif
# define MAD_F_SCALEBITS MAD_F_FRACBITS
# endif
/* C routines */
mad_fixed_t mad_f_abs(mad_fixed_t);
mad_fixed_t mad_f_div(mad_fixed_t, mad_fixed_t);
# endif
/* Id: bit.h,v 1.12 2004/01/23 09:41:32 rob Exp */
# ifndef LIBMAD_BIT_H
# define LIBMAD_BIT_H
struct mad_bitptr {
unsigned char const *byte;
unsigned short cache;
unsigned short left;
};
void mad_bit_init(struct mad_bitptr *, unsigned char const *);
# define mad_bit_finish(bitptr) /* nothing */
unsigned int mad_bit_length(struct mad_bitptr const *,
struct mad_bitptr const *);
# define mad_bit_bitsleft(bitptr) ((bitptr)->left)
unsigned char const *mad_bit_nextbyte(struct mad_bitptr const *);
void mad_bit_skip(struct mad_bitptr *, unsigned int);
unsigned long mad_bit_read(struct mad_bitptr *, unsigned int);
void mad_bit_write(struct mad_bitptr *, unsigned int, unsigned long);
unsigned short mad_bit_crc(struct mad_bitptr, unsigned int, unsigned short);
# endif
/* Id: timer.h,v 1.16 2004/01/23 09:41:33 rob Exp */
# ifndef LIBMAD_TIMER_H
# define LIBMAD_TIMER_H
typedef struct {
signed long seconds; /* whole seconds */
unsigned long fraction; /* 1/MAD_TIMER_RESOLUTION seconds */
} mad_timer_t;
extern mad_timer_t const mad_timer_zero;
# define MAD_TIMER_RESOLUTION 352800000UL
enum mad_units {
MAD_UNITS_HOURS = -2,
MAD_UNITS_MINUTES = -1,
MAD_UNITS_SECONDS = 0,
/* metric units */
MAD_UNITS_DECISECONDS = 10,
MAD_UNITS_CENTISECONDS = 100,
MAD_UNITS_MILLISECONDS = 1000,
/* audio sample units */
MAD_UNITS_8000_HZ = 8000,
MAD_UNITS_11025_HZ = 11025,
MAD_UNITS_12000_HZ = 12000,
MAD_UNITS_16000_HZ = 16000,
MAD_UNITS_22050_HZ = 22050,
MAD_UNITS_24000_HZ = 24000,
MAD_UNITS_32000_HZ = 32000,
MAD_UNITS_44100_HZ = 44100,
MAD_UNITS_48000_HZ = 48000,
/* video frame/field units */
MAD_UNITS_24_FPS = 24,
MAD_UNITS_25_FPS = 25,
MAD_UNITS_30_FPS = 30,
MAD_UNITS_48_FPS = 48,
MAD_UNITS_50_FPS = 50,
MAD_UNITS_60_FPS = 60,
/* CD audio frames */
MAD_UNITS_75_FPS = 75,
/* video drop-frame units */
MAD_UNITS_23_976_FPS = -24,
MAD_UNITS_24_975_FPS = -25,
MAD_UNITS_29_97_FPS = -30,
MAD_UNITS_47_952_FPS = -48,
MAD_UNITS_49_95_FPS = -50,
MAD_UNITS_59_94_FPS = -60
};
# define mad_timer_reset(timer) ((void) (*(timer) = mad_timer_zero))
int mad_timer_compare(mad_timer_t, mad_timer_t);
# define mad_timer_sign(timer) mad_timer_compare((timer), mad_timer_zero)
void mad_timer_negate(mad_timer_t *);
mad_timer_t mad_timer_abs(mad_timer_t);
void mad_timer_set(mad_timer_t *, unsigned long, unsigned long, unsigned long);
void mad_timer_add(mad_timer_t *, mad_timer_t);
void mad_timer_multiply(mad_timer_t *, signed long);
signed long mad_timer_count(mad_timer_t, enum mad_units);
unsigned long mad_timer_fraction(mad_timer_t, unsigned long);
void mad_timer_string(mad_timer_t, char *, char const *,
enum mad_units, enum mad_units, unsigned long);
# endif
/* Id: stream.h,v 1.20 2004/02/05 09:02:39 rob Exp */
# ifndef LIBMAD_STREAM_H
# define LIBMAD_STREAM_H
# define MAD_BUFFER_GUARD 8
# define MAD_BUFFER_MDLEN (511 + 2048 + MAD_BUFFER_GUARD)
enum mad_error {
MAD_ERROR_NONE = 0x0000, /* no error */
MAD_ERROR_BUFLEN = 0x0001, /* input buffer too small (or EOF) */
MAD_ERROR_BUFPTR = 0x0002, /* invalid (null) buffer pointer */
MAD_ERROR_NOMEM = 0x0031, /* not enough memory */
MAD_ERROR_LOSTSYNC = 0x0101, /* lost synchronization */
MAD_ERROR_BADLAYER = 0x0102, /* reserved header layer value */
MAD_ERROR_BADBITRATE = 0x0103, /* forbidden bitrate value */
MAD_ERROR_BADSAMPLERATE = 0x0104, /* reserved sample frequency value */
MAD_ERROR_BADEMPHASIS = 0x0105, /* reserved emphasis value */
MAD_ERROR_BADCRC = 0x0201, /* CRC check failed */
MAD_ERROR_BADBITALLOC = 0x0211, /* forbidden bit allocation value */
MAD_ERROR_BADSCALEFACTOR = 0x0221, /* bad scalefactor index */
MAD_ERROR_BADMODE = 0x0222, /* bad bitrate/mode combination */
MAD_ERROR_BADFRAMELEN = 0x0231, /* bad frame length */
MAD_ERROR_BADBIGVALUES = 0x0232, /* bad big_values count */
MAD_ERROR_BADBLOCKTYPE = 0x0233, /* reserved block_type */
MAD_ERROR_BADSCFSI = 0x0234, /* bad scalefactor selection info */
MAD_ERROR_BADDATAPTR = 0x0235, /* bad main_data_begin pointer */
MAD_ERROR_BADPART3LEN = 0x0236, /* bad audio data length */
MAD_ERROR_BADHUFFTABLE = 0x0237, /* bad Huffman table select */
MAD_ERROR_BADHUFFDATA = 0x0238, /* Huffman data overrun */
MAD_ERROR_BADSTEREO = 0x0239 /* incompatible block_type for JS */
};
# define MAD_RECOVERABLE(error) ((error) & 0xff00)
struct mad_stream {
unsigned char const *buffer; /* input bitstream buffer */
unsigned char const *bufend; /* end of buffer */
unsigned long skiplen; /* bytes to skip before next frame */
int sync; /* stream sync found */
unsigned long freerate; /* free bitrate (fixed) */
unsigned char const *this_frame; /* start of current frame */
unsigned char const *next_frame; /* start of next frame */
struct mad_bitptr ptr; /* current processing bit pointer */
struct mad_bitptr anc_ptr; /* ancillary bits pointer */
unsigned int anc_bitlen; /* number of ancillary bits */
unsigned char (*main_data)[MAD_BUFFER_MDLEN];
/* Layer III main_data() */
unsigned int md_len; /* bytes in main_data */
int options; /* decoding options (see below) */
enum mad_error error; /* error code (see above) */
};
enum {
MAD_OPTION_IGNORECRC = 0x0001, /* ignore CRC errors */
MAD_OPTION_HALFSAMPLERATE = 0x0002 /* generate PCM at 1/2 sample rate */
# if 0 /* not yet implemented */
MAD_OPTION_LEFTCHANNEL = 0x0010, /* decode left channel only */
MAD_OPTION_RIGHTCHANNEL = 0x0020, /* decode right channel only */
MAD_OPTION_SINGLECHANNEL = 0x0030 /* combine channels */
# endif
};
void mad_stream_init(struct mad_stream *);
void mad_stream_finish(struct mad_stream *);
# define mad_stream_options(stream, opts) \
((void) ((stream)->options = (opts)))
void mad_stream_buffer(struct mad_stream *,
unsigned char const *, unsigned long);
void mad_stream_skip(struct mad_stream *, unsigned long);
int mad_stream_sync(struct mad_stream *);
char const *mad_stream_errorstr(struct mad_stream const *);
# endif
/* Id: frame.h,v 1.20 2004/01/23 09:41:32 rob Exp */
# ifndef LIBMAD_FRAME_H
# define LIBMAD_FRAME_H
enum mad_layer {
MAD_LAYER_I = 1, /* Layer I */
MAD_LAYER_II = 2, /* Layer II */
MAD_LAYER_III = 3 /* Layer III */
};
enum mad_mode {
MAD_MODE_SINGLE_CHANNEL = 0, /* single channel */
MAD_MODE_DUAL_CHANNEL = 1, /* dual channel */
MAD_MODE_JOINT_STEREO = 2, /* joint (MS/intensity) stereo */
MAD_MODE_STEREO = 3 /* normal LR stereo */
};
enum mad_emphasis {
MAD_EMPHASIS_NONE = 0, /* no emphasis */
MAD_EMPHASIS_50_15_US = 1, /* 50/15 microseconds emphasis */
MAD_EMPHASIS_CCITT_J_17 = 3, /* CCITT J.17 emphasis */
MAD_EMPHASIS_RESERVED = 2 /* unknown emphasis */
};
struct mad_header {
enum mad_layer layer; /* audio layer (1, 2, or 3) */
enum mad_mode mode; /* channel mode (see above) */
int mode_extension; /* additional mode info */
enum mad_emphasis emphasis; /* de-emphasis to use (see above) */
unsigned long bitrate; /* stream bitrate (bps) */
unsigned int samplerate; /* sampling frequency (Hz) */
unsigned short crc_check; /* frame CRC accumulator */
unsigned short crc_target; /* final target CRC checksum */
int flags; /* flags (see below) */
int private_bits; /* private bits (see below) */
mad_timer_t duration; /* audio playing time of frame */
};
struct mad_frame {
struct mad_header header; /* MPEG audio header */
int options; /* decoding options (from stream) */
mad_fixed_t sbsample[2][36][32]; /* synthesis subband filter samples */
mad_fixed_t (*overlap)[2][32][18]; /* Layer III block overlap data */
};
# define MAD_NCHANNELS(header) ((header)->mode ? 2 : 1)
# define MAD_NSBSAMPLES(header) \
((header)->layer == MAD_LAYER_I ? 12 : \
(((header)->layer == MAD_LAYER_III && \
((header)->flags & MAD_FLAG_LSF_EXT)) ? 18 : 36))
enum {
MAD_FLAG_NPRIVATE_III = 0x0007, /* number of Layer III private bits */
MAD_FLAG_INCOMPLETE = 0x0008, /* header but not data is decoded */
MAD_FLAG_PROTECTION = 0x0010, /* frame has CRC protection */
MAD_FLAG_COPYRIGHT = 0x0020, /* frame is copyright */
MAD_FLAG_ORIGINAL = 0x0040, /* frame is original (else copy) */
MAD_FLAG_PADDING = 0x0080, /* frame has additional slot */
MAD_FLAG_I_STEREO = 0x0100, /* uses intensity joint stereo */
MAD_FLAG_MS_STEREO = 0x0200, /* uses middle/side joint stereo */
MAD_FLAG_FREEFORMAT = 0x0400, /* uses free format bitrate */
MAD_FLAG_LSF_EXT = 0x1000, /* lower sampling freq. extension */
MAD_FLAG_MC_EXT = 0x2000, /* multichannel audio extension */
MAD_FLAG_MPEG_2_5_EXT = 0x4000 /* MPEG 2.5 (unofficial) extension */
};
enum {
MAD_PRIVATE_HEADER = 0x0100, /* header private bit */
MAD_PRIVATE_III = 0x001f /* Layer III private bits (up to 5) */
};
void mad_header_init(struct mad_header *);
# define mad_header_finish(header) /* nothing */
int mad_header_decode(struct mad_header *, struct mad_stream *);
void mad_frame_init(struct mad_frame *);
void mad_frame_finish(struct mad_frame *);
int mad_frame_decode(struct mad_frame *, struct mad_stream *);
void mad_frame_mute(struct mad_frame *);
# endif
/* Id: synth.h,v 1.15 2004/01/23 09:41:33 rob Exp */
# ifndef LIBMAD_SYNTH_H
# define LIBMAD_SYNTH_H
struct mad_pcm {
unsigned int samplerate; /* sampling frequency (Hz) */
unsigned short channels; /* number of channels */
unsigned short length; /* number of samples per channel */
mad_fixed_t samples[2][1152]; /* PCM output samples [ch][sample] */
};
struct mad_synth {
mad_fixed_t filter[2][2][2][16][8]; /* polyphase filterbank outputs */
/* [ch][eo][peo][s][v] */
unsigned int phase; /* current processing phase */
struct mad_pcm pcm; /* PCM output */
};
/* single channel PCM selector */
enum {
MAD_PCM_CHANNEL_SINGLE = 0
};
/* dual channel PCM selector */
enum {
MAD_PCM_CHANNEL_DUAL_1 = 0,
MAD_PCM_CHANNEL_DUAL_2 = 1
};
/* stereo PCM selector */
enum {
MAD_PCM_CHANNEL_STEREO_LEFT = 0,
MAD_PCM_CHANNEL_STEREO_RIGHT = 1
};
void mad_synth_init(struct mad_synth *);
# define mad_synth_finish(synth) /* nothing */
void mad_synth_mute(struct mad_synth *);
void mad_synth_frame(struct mad_synth *, struct mad_frame const *);
# endif
/* Id: decoder.h,v 1.17 2004/01/23 09:41:32 rob Exp */
# ifndef LIBMAD_DECODER_H
# define LIBMAD_DECODER_H
enum mad_decoder_mode {
MAD_DECODER_MODE_SYNC = 0,
MAD_DECODER_MODE_ASYNC
};
enum mad_flow {
MAD_FLOW_CONTINUE = 0x0000, /* continue normally */
MAD_FLOW_STOP = 0x0010, /* stop decoding normally */
MAD_FLOW_BREAK = 0x0011, /* stop decoding and signal an error */
MAD_FLOW_IGNORE = 0x0020 /* ignore the current frame */
};
struct mad_decoder {
enum mad_decoder_mode mode;
int options;
struct {
long pid;
int in;
int out;
} async;
struct {
struct mad_stream stream;
struct mad_frame frame;
struct mad_synth synth;
} *sync;
void *cb_data;
enum mad_flow (*input_func)(void *, struct mad_stream *);
enum mad_flow (*header_func)(void *, struct mad_header const *);
enum mad_flow (*filter_func)(void *,
struct mad_stream const *, struct mad_frame *);
enum mad_flow (*output_func)(void *,
struct mad_header const *, struct mad_pcm *);
enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
enum mad_flow (*message_func)(void *, void *, unsigned int *);
};
void mad_decoder_init(struct mad_decoder *, void *,
enum mad_flow (*)(void *, struct mad_stream *),
enum mad_flow (*)(void *, struct mad_header const *),
enum mad_flow (*)(void *,
struct mad_stream const *,
struct mad_frame *),
enum mad_flow (*)(void *,
struct mad_header const *,
struct mad_pcm *),
enum mad_flow (*)(void *,
struct mad_stream *,
struct mad_frame *),
enum mad_flow (*)(void *, void *, unsigned int *));
int mad_decoder_finish(struct mad_decoder *);
# define mad_decoder_options(decoder, opts) \
((void) ((decoder)->options = (opts)))
int mad_decoder_run(struct mad_decoder *, enum mad_decoder_mode);
int mad_decoder_message(struct mad_decoder *, void *, unsigned int *);
# endif
# ifdef __cplusplus
}
# endif

View File

@ -2,4 +2,6 @@ cmake_minimum_required(VERSION 2.8.12)
add_jslib(utils/class.js lib/utils/class ${LORGAR_DIR} browser)
add_jslib(utils/subscribable.js lib/utils/subscribable ${LORGAR_DIR} browser)
add_jslib(utils/globalMethods.js lib/utils/globalMethods ${LORGAR_DIR} browser)
add_jslib(utils/globalMethods.js lib/utils/globalMethods ${LORGAR_DIR} browser)
add_jslib(utils/enum.js lib/utils/enum ${LORGAR_DIR} browser)
add_jslib(utils/stateMachine.js lib/utils/stateMachine ${LORGAR_DIR} browser)

View File

@ -16,4 +16,8 @@ add_jslib(wController/attributes.js lib/wController/attributes ${LORGAR_DIR} bro
add_jslib(wController/localModel.js lib/wController/localModel ${LORGAR_DIR} browser)
add_jslib(wController/imagePane.js lib/wController/imagePane ${LORGAR_DIR} browser)
add_jslib(wController/file/file.js lib/wController/file/file ${LORGAR_DIR} browser)
add_jslib(wController/file/audio.js lib/wController/file/audio ${LORGAR_DIR} browser)
add_jslib(wController/image.js lib/wController/image ${LORGAR_DIR} browser)
add_jslib(wController/button.js lib/wController/button ${LORGAR_DIR} browser)
add_jslib(wController/player.js lib/wController/player ${LORGAR_DIR} browser)
add_jslib(wController/imageById.js lib/wController/imageById ${LORGAR_DIR} browser)

View File

@ -1,5 +1,8 @@
"use strict";
(function main_js() {
requirejs.config({
"baseUrl": "/"
});
requirejs.onError = function(e) {
throw e;
}
@ -8,6 +11,8 @@
defineArray.push("test/test");
defineArray.push("core/lorgar");
defineArray.push("lib/utils/globalMethods");
defineArray.push("lib/em/wrapper");
require(defineArray, function main_module() {
require("lib/utils/globalMethods");
@ -17,6 +22,8 @@
var Controller = require("lib/wController/controller");
var View = require("views/view");
window.Mp3Decoder = Module.Decoder;
var waiter = {
views: false,
controllers: false,
@ -32,12 +39,14 @@
window.unregisterForeignController = window.lorgar.unregisterForeignController.bind(window.lorgar);
window.subscribeForeignController = window.lorgar.subscribeForeignController.bind(window.lorgar);
window.unsubscribeForeignController = window.lorgar.unsubscribeForeignController.bind(window.lorgar);
window.play = window.lorgar.play.bind(window.lorgar);
window.scheduleToPlay = window.lorgar.scheduleToPlay.bind(window.lorgar);
}
}
}
Controller.initialize(["String", "List", "Vocabulary", "Page", "PanesList", "Link", "Image"], waiter.check.bind(waiter, "controllers"));
View.initialize(["Label", "Page", "PanesList", "Nav", "Image"], waiter.check.bind(waiter, "views"));
Controller.initialize(["String", "List", "Vocabulary", "Page", "PanesList", "Link", "Image", "Button"], waiter.check.bind(waiter, "controllers"));
View.initialize(["Label", "Page", "PanesList", "Nav", "Image", "Button", "Enumeration"], waiter.check.bind(waiter, "views"));
var test = new Test();
test.run();

View File

@ -11,5 +11,10 @@ configure_file(mainLayout.js mainLayout.js)
configure_file(page.js page.js)
configure_file(pane.js pane.js)
configure_file(image.js image.js)
configure_file(button.js button.js)
configure_file(enumeration.js enumeration.js)
configure_file(player.js player.js)
configure_file(slider.js slider.js)
configure_file(songProgress.js songProgress.js)
add_subdirectory(helpers)

98
lorgar/views/button.js Normal file
View File

@ -0,0 +1,98 @@
"use strict";
(function() {
var moduleName = "views/button";
var deps = [];
deps.push("views/layout");
deps.push("views/label");
define(moduleName, deps, function() {
var Layout = require("views/layout");
var Label = require("views/label");
var Button = Layout.inherit({
"className": "Button",
"constructor": function(controller, options) {
var base = {
padding: 5
};
W.extend(base, options)
Layout.fn.constructor.call(this, controller, base);
this.addClass("hoverable");
this.addClass("button");
this._enabled = true;
this._hasLabel = false;
this._e.addEventListener("click", this._onClick.bind(this), false);
controller.on("setEnabled", this._onSetEnabled, this);
controller.on("setLabel", this._onSetLabel, this);
this._onSetEnabled(controller.enabled);
this._onSetLabel(controller.hasLabel, controller.label);
},
"destructor": function() {
this._f.off("setEnabled", this._onSetEnabled, this);
this._f.off("setLabel", this._onSetLabel, this);
Layout.fn.destructor.call(this);
},
"append": function(child, aligment, index) {
this._updateLimits();
Layout.fn.append.call(this, child, aligment, index);
},
"_onChildChangeLimits": function(child) {
this._updateLimits();
Layout.fn._onChildChangeLimits.call(this, child);
},
"_onClick": function(e) {
if (this._enabled) {
this._f.activate();
}
e.stopPropagation();
},
"_onSetEnabled": function(enabled) {
if (this._enabled !== enabled) {
this._enabled = enabled;
if (this._enabled) {
this.removeClass("disabled");
} else {
this.addClass("disabled");
}
}
},
"_onSetLabel": function(hasLabel, label) {
if (this._hasLabel !== hasLabel) {
this._hasLabel = hasLabel;
if (this._hasLabel) {
this._label = new Label(label);
this.append(this._label, Layout.Aligment.CenterCenter);
} else {
this._label.destructor();
delete this._label();
}
}
},
"_updateLimits": function() {
var minWidth = this._o.padding * 2;
var maxWidth = this._o.padding * 2;
var minHeight = this._o.padding * 2;
var maxHeight = this._o.padding * 2;
if (this._hasLabel) {
minWidth += this._label._o.minWidth;
minHeight += this._label._o.minHeight;
maxWidth += this._label._o.maxWidth;
maxHeight += this._label._o.maxHeight;
}
this._setLimits(minWidth, minHeight, maxWidth, maxHeight);
}
});
return Button;
})
})();

View File

@ -0,0 +1,53 @@
"use strict";
(function view_enumeration_js() {
var moduleName = "views/enumeration";
var deps = [];
deps.push("views/gridLayout");
deps.push("views/label");
deps.push("lib/wController/localModel");
define(moduleName, deps, function view_enumeration_module() {
var GridLayout = require("views/gridLayout");
var Label = require("views/label");
var LocalModel = require("lib/wController/localModel");
var Enumeration = GridLayout.inherit({
className: "Enumeration",
constructor: function(controller, options) {
var base = {};
W.extend(base, options)
this._lm = new LocalModel();
GridLayout.fn.constructor.call(this, controller, base);
this._lv = new Label(this._lm);
this.append(this._lv, 0, 0, 1, 1, GridLayout.Aligment.CenterCenter);
this._uncyclic.push(this._lm.destructor.bind(this._lm));
},
_onData: function() {
if (this._f.initialized) {
var e = this._f.enum;
var value = this._f.data;
var title;
if (e.hasAdditional("title")) {
title = e.additional[value].title;
} else {
title = e.reversed[value];
}
var desc = "";
if (e.hasAdditional("description")) {
desc = e.additional[value].description;
}
this._lm.setData(title);
this._e.setAttribute("title", desc);
}
}
});
return Enumeration;
})
})();

View File

@ -250,20 +250,52 @@
var target = pos + span;
var minSize = 0;
var maxSize = 0;
var flexibleColls = [];
for (j = pos; j < target; ++j) {
minSize += this._cols[j].min;
maxSize += this._cols[j].max;
}
if (e.child._o.minWidth > minSize) {
var portion = (e.child._o.minWidth - minSize) / span;
for (j = pos; j < target; ++j) {
this._cols[j].min += portion;
if (this._cols[j].min < this._cols[j].max) {
flexibleColls.push(this._cols[j]);
}
}
if (e.child._o.maxWidth < maxSize) {
var portion = (maxSize - e.child._o.maxWidth) / span;
for (j = pos; j < target; ++j) {
this._cols[j].max -= portion;
var leftMin = e.child._o.minWidth - minSize;
if (leftMin > 0) {
while (leftMin > 0 && flexibleColls.length > 0) {
var portion = leftMin / flexibleColls.length;
for (j = 0; j < flexibleColls.length; ++j) {
var lPortion = Math.min(flexibleColls[j].max, portion);
flexibleColls[j].min += lPortion;
leftMin -= lPortion;
if (flexibleColls[j].min === flexibleColls[j].max) {
flexibleColls.splice(j, 1);
break;
}
}
if (leftMin < 1) {
leftMin = 0;
}
}
}
var leftMax = maxSize - e.child._o.maxWidth
if (leftMax > 0) {
while (leftMax > 0 && flexibleColls.length > 0) {
var portion = leftMax / flexibleColls.length;
for (j = 0; j < flexibleColls.length; ++j) {
var lPortion = Math.max(flexibleColls[j].min, portion);
flexibleColls[j].max -= lPortion;
leftMax -= lPortion;
if (flexibleColls[j].min === flexibleColls[j].max) {
flexibleColls.splice(j, 1);
break;
}
}
if (leftMax < 1) {
leftMax = 0;
}
}
}
}
@ -275,20 +307,52 @@
var target = pos + span;
var minSize = 0;
var maxSize = 0;
var flexibleRows = [];
for (j = pos; j < target; ++j) {
minSize += this._rows[j].min;
maxSize += this._rows[j].max;
}
if (e.child._o.minHeight > minSize) {
var portion = (e.child._o.minHeight - minSize) / span;
for (j = pos; j < target; ++j) {
this._rows[j].min += portion;
if (this._rows[j].min < this._rows[j].max) {
flexibleRows.push(this._rows[j]);
}
}
if (e.child._o.maxHeight < maxSize) {
var portion = (maxSize - e.child._o.maxHeight) / span;
for (j = pos; j < target; ++j) {
this._rows[j].max -= portion;
var leftMin = e.child._o.minHeigh - minSize;
if (leftMin > 0) {
while (leftMin > 0 && flexibleRows.length > 0) {
var portion = leftMin / flexibleRows.length;
for (j = 0; j < flexibleRows.length; ++j) {
var lPortion = Math.min(flexibleRows[j].max, portion);
flexibleRows[j].min += lPortion;
leftMin -= lPortion;
if (flexibleRows[j].min === flexibleRows[j].max) {
flexibleRows.splice(j, 1);
break;
}
}
if (leftMin < 1) {
leftMin = 0;
}
}
}
var leftMax = maxSize - e.child._o.maxHeigh
if (leftMax > 0) {
while (leftMax > 0 && flexibleRows.length > 0) {
var portion = leftMax / flexibleRows.length;
for (j = 0; j < flexibleRows.length; ++j) {
var lPortion = Math.max(flexibleRows[j].min, portion);
flexibleRows[j].max -= lPortion;
leftMax -= lPortion;
if (flexibleRows[j].min === flexibleRows[j].max) {
flexibleRows.splice(j, 1);
break;
}
}
if (leftMax < 1) {
leftMax = 0;
}
}
}
}

View File

@ -30,9 +30,9 @@
this._y = 0;
this._e.addEventListener("mousedown", this._proxy.onMouseDown, false);
this._e.addEventListener("touchstart", this._touch, false);
this._e.addEventListener("touchmove", this._touch, false);
this._e.addEventListener("touchend", this._touch, false);
this._e.addEventListener("touchstart", W.touchToMouse, false);
this._e.addEventListener("touchmove", W.touchToMouse, false);
this._e.addEventListener("touchend", W.touchToMouse, false);
},
"destructor": function () {
if (this._dragging) {
@ -45,9 +45,9 @@
}
this._e.removeEventListener("mousedown", this._proxy.onMouseDown);
this._e.removeEventListener("touchstart", this._touch);
this._e.removeEventListener("touchmove", this._touch);
this._e.removeEventListener("touchend", this._touch);
this._e.removeEventListener("touchstart", W.touchToMouse);
this._e.removeEventListener("touchmove", W.touchToMouse);
this._e.removeEventListener("touchend", W.touchToMouse);
Subscribable.fn.destructor.call(this);
},
@ -105,41 +105,6 @@
this._v.trigger("dragEnd");
return false;
}
},
"_touch": function (e) {
e.preventDefault();
if (e.touches.length > 1 || (e.type == "touchend" && e.touches.length > 0))
return;
var type = null;
var touch = null;
var src = e.currentTarget;
switch (e.type) {
case "touchstart":
type = "mousedown";
touch = e.changedTouches[0];
break;
case "touchmove":
type = "mousemove";
touch = e.changedTouches[0];
src = window;
break;
case "touchend":
type = "mouseup";
touch = e.changedTouches[0];
src = window;
break;
}
var event = new MouseEvent(type, {
button: 0,
screenX: touch.screenX,
screenY: touch.screenY,
clientX: touch.clientX,
clientY: touch.clientY
});
src.dispatchEvent(event);
}
});

View File

@ -76,8 +76,12 @@
}
}
if (this._w !== undefined && this._h !== undefined) {
child.setSize(this._w, this._h);
index = index || this._c.length - 1;
var c = this._c[index];
this._positionElement(c);
}
},
"clear": function() {

View File

@ -5,19 +5,46 @@
var defineArray = [];
defineArray.push("views/gridLayout");
defineArray.push("views/label");
defineArray.push("views/view");
defineArray.push("views/navigationPanel");
defineArray.push("views/layout");
defineArray.push("views/enumeration");
defineArray.push("views/player");
defineArray.push("lib/wController/localModel");
define(moduleName, defineArray, function mainLayout_module() {
var GridLayout = require("views/gridLayout");
var ViewLabel = require("views/label");
var View = require("views/view");
var ViewNavigationPanel = require("views/navigationPanel");
var Layout = require("views/layout");
var Enumeration = require("views/enumeration");
var Player = require("views/player");
var LocalModel = require("lib/wController/localModel");
var MainLayout = GridLayout.inherit({
"className": "MainLayout",
"constructor": function(controller, options) {
GridLayout.fn.constructor.call(this, controller, options);
this._statusBarPosition = 2;
this._player = undefined;
this._mainColorHelper = new LocalModel({backgroundColor: "mainColor"});
this._statusBarModel = new LocalModel({backgroundColor: "secondaryColor"});
this._uncyclic.push(this._statusBarModel.destructor.bind(this._statusBarModel));
this._uncyclic.push(this._mainColorHelper.destructor.bind(this._mainColorHelper));
var spacerL = new View(this._mainColorHelper, {maxWidth: 50});
var spacerR = new View(this._mainColorHelper, {maxWidth: 50});
this.append(spacerL, 1, 0, 1, 1);
this.append(spacerR, 1, 2, 1, 1);
this._statusBar = new GridLayout(this._statusBarModel);
this._statusBar.append(new View(this._statusBarModel), 0, 1, 1, 1);
this.append(this._statusBar, 3, 0, 1, 3);
},
"_onNewController": function(controller) {
GridLayout.fn._onNewController.call(this, controller);
@ -25,13 +52,8 @@
switch (controller.name) {
case "version":
var lm = new LocalModel({
backgroundColor: "secondaryColor"
});
var lay = new Layout(lm, {maxHeight: 15})
view = new ViewLabel(controller);
lay.append(view, Layout.Aligment.RightCenter);
this.append(lay, 2, 0, 1, 3);
this._statusBar.append(view, 0, 0, 1, 1, Layout.Aligment.LeftCenter);
break;
case "navigationPanel":
view = new ViewNavigationPanel(controller);
@ -43,6 +65,31 @@
//this.trigger("serviceMessage", "Unsupported view: " + name + " (" + type + ")", 1);
break;
}
},
addState: function(name, state) {
var lm = new LocalModel({fontFamily: "casualFont"});
lm.setData(name + ": ");
var lv = new ViewLabel(lm);
var e = new Enumeration(state);
this._statusBar.append(lv, 0, this._statusBarPosition++, 1, 1, Layout.Aligment.LeftCenter);
this._statusBar.append(e, 0, this._statusBarPosition++, 1, 1, Layout.Aligment.LeftCenter);
this._uncyclic.push(lm.destructor.bind(lm));
},
appendPlayer: function(playerModel) {
if (this._player !== undefined) {
throw new Error("An attempt to add player to main layout for the second time");
}
this._player = new Player(playerModel);
this.append(this._player, 2, 0, 1, 3);
},
removePlayer: function() {
if (this._player === undefined) {
throw new Error("An attempt to remove non existing player from mainLayout");
}
this.removeChild(this._player);
this._player.destructor();
this._player = undefined;
}
});

View File

@ -8,6 +8,7 @@
defineArray.push("views/layout");
defineArray.push("views/label");
defineArray.push("views/image");
defineArray.push("views/button");
defineArray.push("lib/wController/localModel");
define(moduleName, defineArray, function() {
@ -15,6 +16,7 @@
var Layout = require("views/layout");
var Label = require("views/label");
var Image = require("views/image");
var Button = require("views/button");
var LM = require("lib/wController/localModel");
var Pane = Layout.inherit({
@ -25,6 +27,7 @@
W.extend(base, options);
Layout.fn.constructor.call(this, controller, options);
this._aCount = 0;
this._initProxy();
this.addClass("hoverable");
this._e.addEventListener("click", this._proxy.onClick, false);
@ -46,6 +49,12 @@
this._f.on("newElement", this._onNewElement, this);
this._f.on("removeElement", this._onRemoveElement, this);
this._f.on("addAction", this._onAddAction, this);
var acts = this._f.getActions();
for (var i = 0; i < acts.length; ++i) {
this._onAddAction(acts[i]);
}
this._uncyclic.push(function() {
lm.destructor();
@ -56,6 +65,31 @@
Layout.fn.destructor.call(this);
},
"_onAddAction": function(model) {
var alignment;
switch (this._aCount) {
case 0:
alignment = Layout.Aligment.LeftTop;
break;
case 1:
alignment = Layout.Aligment.RightTop;
break;
case 2:
alignment = Layout.Aligment.RightBottom;
break;
case 3:
alignment = Layout.Aligment.LeftBottom;
break;
default:
console.warn("Pane can't place more then 4 action, ignoring");
break
}
if (alignment !== undefined) {
var view = new Button(model);
this.append(view, alignment);
this._aCount++;
}
},
"_applyProperties": function() {
this._onAddProperty("secondaryColor", "background");
@ -67,8 +101,8 @@
};
},
"_onClick": function() {
if (this._f.data.at("hasPageLink").valueOf() === true) {
this.trigger("activate", this._f.data.at("pageLink").clone());
if (this._f.hasPageLink()) {
this.trigger("activate", this._f.getPageLink());
}
},
"_onLabelChangeLimits": function(label) {

View File

@ -50,15 +50,13 @@
this._f.setSubscriptionRange(0, 0);
},
"append": function(child, index) {
var model = new LM();
var nest = new Layout(model, {
var nest = new Layout(helper, {
minHeight: this._o.nestHeight,
maxHeight: this._o.nestHeight,
minWidth: this._o.nestWidth,
minWidth: this._o.nestWidth,
scrollable: Layout.Scroll.None
});
nest._uncyclic.push(function() {model.destructor()});
nest.append(child);
child.on("activate", this._onChildActivate, this); //todo need to remove handler on deletion
this._addChild(nest, 0, index);
@ -184,6 +182,8 @@
}
});
var helper = new LM();
return PanesList;
});
})();

163
lorgar/views/player.js Normal file
View File

@ -0,0 +1,163 @@
"use strict";
(function() {
var moduleName = "views/player"
var deps = [];
deps.push("views/gridLayout");
deps.push("views/button");
deps.push("views/label");
deps.push("views/view");
deps.push("views/image");
deps.push("views/songProgress");
deps.push("views/slider");
deps.push("lib/wController/localModel");
deps.push("lib/utils/enum");
define(moduleName, deps, function() {
var GridLayout = require("views/gridLayout");
var Button = require("views/button");
var Label = require("views/label");
var View = require("views/view");
var Image = require("views/image");
var SongProgress = require("views/songProgress");
var Slider = require("views/slider");
var Model = require("lib/wController/localModel");
var Enum = require("lib/utils/enum");
var Player = GridLayout.inherit({
className: "Player",
constructor: function(ctrl, options) {
GridLayout.fn.constructor.call(this, ctrl, options);
this._playPause = null;
this._prev = null;
this._next = null;
this._picture = null;
this._cpbCtrl = null;
this._infoModels = {
artist: new Model(null, "artist: unknown"),
album: new Model(null, "album: unknown"),
song: new Model(null, "song: unknown")
}
ctrl.on("newElement", this._onNewElement, this);
ctrl.on("removeElement", this._onRemoveElement, this);
var artist = new Label(this._infoModels.artist);
var album = new Label(this._infoModels.album);
var song = new Label(this._infoModels.song);
var spacer = new View(helper, {maxWidth: 10});
var progress = new SongProgress(ctrl.progress);
var volume = new Slider(ctrl.volume, {maxWidth: 100});
this.append(artist, 0, 4, 1, 1, GridLayout.Aligment.LeftCenter);
this.append(song, 1, 4, 1, 1, GridLayout.Aligment.LeftCenter);
this.append(album, 2, 4, 1, 1, GridLayout.Aligment.LeftCenter);
this.append(spacer, 0, 6, 3, 1, GridLayout.Aligment.LeftCenter);
this.append(progress, 1, 5, 1, 1, GridLayout.Aligment.CenterCenter);
this.append(volume, 1, 7, 1, 1, GridLayout.Aligment.CenterCenter);
this._uncyclic.push(this._infoModels.artist.destructor.bind(this._infoModels.artist));
this._uncyclic.push(this._infoModels.song.destructor.bind(this._infoModels.song));
this._uncyclic.push(this._infoModels.album.destructor.bind(this._infoModels.album));
},
destructor: function() {
this._f.off("newElement", this._onNewElement, this);
this._f.off("removeElement", this._onRemoveElement, this);
this._clearCpbCtrl();
GridLayout.fn.destructor.call(this);
},
_clearCpbCtrl: function() {
if (this._cpbCtrl !== null) {
this._cpbCtrl.off("newElement", this._onCpbNewElement, this);
this._cpbCtrl.off("removeElement", this._onCpbRemoveElement, this);
this._cpbCtrl = null;
}
},
_onNewElement: function(ctrl, type) {
var ItemType = Enum.storage["ItemType"];
switch (type) {
case ItemType.straight.playPause:
this._playPause = new Button(ctrl);
this.append(this._playPause, 0, 2, 3, 1);
break;
case ItemType.straight.prev:
this._prev = new Button(ctrl);
this.append(this._prev, 0, 1, 3, 1);
break;
case ItemType.straight.next:
this._next = new Button(ctrl);
this.append(this._next, 0, 3, 3, 1);
break;
case ItemType.straight.queue:
break;
case ItemType.straight.currentPlayback:
this._clearCpbCtrl();
this._cpbCtrl = ctrl;
this._cpbCtrl.on("newElement", this._onCpbNewElement, this);
this._cpbCtrl.on("removeElement", this._onCpbRemoveElement, this);
break;
case ItemType.straight.picture:
this._picture = new Image(ctrl, {
maxWidth: 100,
maxHeight: 100
});
this.append(this._picture, 0, 0, 3, 1);
break;
}
},
_onRemoveElement: function(type) {
var ItemType = Enum.storage["ItemType"];
switch (type) {
case ItemType.straight.playPause:
this._playPause.destructor();
this._playPause = null;
break;
case ItemType.straight.prev:
this._prev.destructor();
this._prev = null;
break;
case ItemType.straight.next:
this._next.destructor();
this._next = null;
break;
case ItemType.straight.queue:
break;
case ItemType.straight.currentPlayback:
this._clearCpbCtrl();
break;
case ItemType.straight.picture:
this._picture.destructor();
this._picture = null;
break;
}
},
_onCpbNewElement: function(key, value) {
var model = this._infoModels[key];
if (model) {
model.setData(key + ": " + value.toString());
}
},
_onCpbRemoveElement: function(key) {
var model = this._infoModels[key];
if (model) {
model.setData(key + ": unknown");
}
}
});
var helper = new Model();
return Player;
});
})()

142
lorgar/views/slider.js Normal file
View File

@ -0,0 +1,142 @@
"use strict";
(function() {
var moduleName = "views/slider";
var deps = [];
deps.push("views/view");
define(moduleName, deps, function() {
var View = require("views/view");
var Slider = View.inherit({
className: "Slider",
constructor: function(controller, options) {
var base = {
minHeight: 10,
maxHeight: 10
};
W.extend(base, options)
var el = document.createElement("div");
this._createBars();
View.fn.constructor.call(this, controller, base, el);
this._seeking = false;
this._createProxy();
this._f.on("value", this._onValue, this);
this._f.on("enabled", this._onEnabled, this);
this._e.style.backgroundColor = "#eeeeee";
this._e.appendChild(this._value);
if (this._f.enabled) {
this._e.addEventListener("mousedown", this._proxy.onMouseDown, false);
this._e.addEventListener("touchstart", W.touchToMouse, false);
this._e.addEventListener("touchmove", W.touchToMouse, false);
this._e.addEventListener("touchend", W.touchToMouse, false);
}
},
destructor: function() {
if (this._seeking) {
window.removeEventListener("mouseup", this._proxy.onMouseUp);
window.removeEventListener("mousemove", this._proxy.onMouseMove);
lorgar._body.removeClass("dragging");
lorgar._body.removeClass("non-selectable");
}
if (this._f.enabled) {
this._e.removeEventListener("mousedown", this._proxy.onMouseDown);
this._e.removeEventListener("touchstart", W.touchToMouse);
this._e.removeEventListener("touchmove", W.touchToMouse);
this._e.removeEventListener("touchend", W.touchToMouse);
}
this._f.off("value", this._value, this);
this._f.off("enabled", this._onEnabled, this);
View.fn.destructor.call(this);
},
_createBars: function() {
this._value = document.createElement("div");
this._value.style.backgroundColor = View.theme.primaryColor || "#ff0000";
this._value.style.height = "100%";
this._value.style.width = "0";
this._value.style.position = "absolute";
this._value.style.top = "0";
this._value.style.left = "0";
},
_createProxy: function () {
this._proxy = {
onMouseDown: this._onMouseDown.bind(this),
onMouseUp: this._onMouseUp.bind(this),
onMouseMove: this._onMouseMove.bind(this)
}
},
_onData: function() {
this._onValue(this._f.value);
},
_onEnabled: function(enabled) {
if (enabled) {
this._e.addEventListener("mousedown", this._proxy.onMouseDown, false);
this._e.addEventListener("touchstart", W.touchToMouse, false);
this._e.addEventListener("touchmove", W.touchToMouse, false);
this._e.addEventListener("touchend", W.touchToMouse, false);
} else {
if (this._seeking) {
this._onMouseUp();
}
this._e.removeEventListener("mousedown", this._proxy.onMouseDown);
this._e.removeEventListener("touchstart", W.touchToMouse);
this._e.removeEventListener("touchmove", W.touchToMouse);
this._e.removeEventListener("touchend", W.touchToMouse);
}
},
_onMouseDown: function(e) {
if (e.which === 1) {
window.addEventListener("mouseup", this._proxy.onMouseUp);
window.addEventListener("mousemove", this._proxy.onMouseMove);
lorgar._body.addClass("dragging");
lorgar._body.addClass("non-selectable");
this._seeking = true;
this._ap = this.getAbsolutePosition();
var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0);
var nSeek = seek / this._w;
if (this._seek !== nSeek) {
this._seek = nSeek;
this._f.setValue(this._seek);
}
}
},
_onMouseMove: function(e) {
var seek = Math.max(Math.min(e.pageX - this._ap.x, this._w), 0);
var nSeek = seek / this._w;
if (this._seek !== nSeek) {
this._seek = nSeek;
this._f.setValue(this._seek);
}
},
_onMouseUp: function() {
delete this._ap;
delete this._seek;
this._seeking = false;
window.removeEventListener("mouseup", this._proxy.onMouseUp);
window.removeEventListener("mousemove", this._proxy.onMouseMove);
lorgar._body.removeClass("dragging");
},
_onValue: function(pb) {
this._value.style.width = pb * 100 + "%";
},
_resetTheme: function() {
View.fn._resetTheme.call(this);
this._value.style.backgroundColor = View.theme.primaryColor || "#ff0000";
}
});
return Slider;
})
})();

View File

@ -0,0 +1,70 @@
"use strict";
(function() {
var moduleName = "views/songProgress";
var deps = [];
deps.push("views/slider");
define(moduleName, deps, function() {
var Slider = require("views/slider");
var SongProgress = Slider.inherit({
className: "SongProgress",
constructor: function(controller, options) {
var base = {
minHeight: 10,
maxHeight: 10
};
W.extend(base, options)
Slider.fn.constructor.call(this, controller, base);
this._f.on("load", this._onLoad, this);
this._e.insertBefore(this._loadProgress, this._value);
},
destructor: function() {
this._f.off("load", this._onLoad, this);
Slider.fn.destructor.call(this);
},
_createBars: function() {
Slider.fn._createBars.call(this);
this._loadProgress = document.createElement("div");
this._loadProgress.style.backgroundColor = Slider.theme.secondaryColor || "#ffff00";
this._loadProgress.style.height = "100%";
this._loadProgress.style.width = "0";
this._loadProgress.style.position = "absolute";
this._loadProgress.style.top = "0";
this._loadProgress.style.left = "0";
},
_onData: function() {
Slider.fn._onData.call(this);
this._onLoad(this._f.load);
},
_onLoad: function(load) {
this._loadProgress.style.width = load * 100 + "%";
},
_onMouseDown: function(e) {
if (e.which === 1) {
this._f.trigger("seekingStart");
}
Slider.fn._onMouseDown.call(this, e);
},
_onMouseUp: function(e) {
Slider.fn._onMouseUp.call(this, e);
this._f.trigger("seekingEnd", this._f.value);
},
_resetTheme: function() {
Slider.fn._resetTheme.call(this);
this._loadProgress.style.backgroundColor = Slider.theme.secondaryColor || "#ffff00"
}
});
return SongProgress;
})
})();

View File

@ -54,7 +54,9 @@
for (var i = 0; i < this._f._controllers.length; ++i) {
this._onNewController(this._f._controllers[i]);
}
this._onData(this._f);
//if (this._f.initialized) {
this._onData(this._f);
//}
View.collection[this._id] = this;
this._applyProperties();
@ -76,11 +78,7 @@
Subscribable.fn.destructor.call(this);
},
"addClass": function(className) {
var arr = this._e.className.split(" ");
if (arr.indexOf(className) === -1) {
arr.push(className);
this._e.className = arr.join(" ");
}
this._e.classList.add(className);
},
"_applyProperties": function() {
for (var i = 0; i < this._f.properties.length; ++i) {
@ -100,6 +98,21 @@
return w;
},
"getAbsolutePosition": function() {
var pp;
if (this._p) {
pp = this._p.getAbsolutePosition();
} else {
pp = Object.create(null);
pp.x = 0;
pp.y = 0;
}
pp.x += this._x;
pp.y += this._y;
return pp;
},
"_initDraggable": function() {
this._dg = new Draggable(this, {
snapDistance: this._o.snapDistance
@ -136,20 +149,11 @@
"remove": function() {
if (this._p) {
this._p.removeChild(this);
delete this._p; //just to make sure
}
},
"removeClass": function(className) {
var arr = this._e.className.split(" ");
var index = arr.indexOf(className)
var toJoin = false;
while (index !== -1) {
arr.splice(index, 1);
index = arr.indexOf(className)
toJoin = true;
}
if (toJoin) {
this._e.className = arr.join(" ");
}
this._e.classList.remove(className);
},
"_resetTheme": function() {
this._onClearProperties();
@ -191,9 +195,12 @@
}
if (needToTell) {
this.trigger("changeLimits", this);
if (this._w !== undefined && this._h !== undefined) {
this.setSize(this._w, this._h);
}
}
return needToTell && this._events.changeLimits && this._events.changeLimits.length; //to see if someone actually going to listen that event
return needToTell && this._events.changeLimits && this._events.changeLimits.length; //to see if someone actually going to listen that event, if not - return result
},
"setMaxSize": function(w, h) {
this._o.maxWidth = w;
@ -284,22 +291,29 @@
View.ViewType = {
Label: 0,
Image: 3,
View: 4,
Image: 4,
Button: 5,
View: 6,
Enumeration: 7,
Page: 102,
PanesList: 104
PanesList: 104,
Player: 107
};
View.ReversedViewType = {
"0": "Label",
"3": "Image",
"4": "View",
"4": "Image",
"5": "Button",
"6": "View",
"7": "Enumeration",
"101": "Nav",
"102": "Page",
"104": "PanesList"
"104": "PanesList",
"107": "Player"
};
View.ViewTypesPaths = {
@ -307,7 +321,10 @@
Nav: "views/nav",
Page: "views/page",
PanesList: "views/panesList",
Image: "views/image"
Image: "views/image",
Button: "views/button",
Player: "views/player",
Enumeration: "views/enumeration"
};
View.constructors = {

View File

@ -3,6 +3,8 @@ var morgan = require("morgan");
var favicon = require("serve-favicon");
var Magnus = require("./core/magnus");
express.static.mime.types.wasm = 'application/wasm';
require("./lib/utils/globalMethods");
var config = require("./config");

View File

@ -5,7 +5,7 @@ var ENV = config.get('build');
function getLogger(module) {
var path = module.filename.split('/').slice(-2).join('/');
return new Winston.Logger({
return Winston.createLogger({
transports: [
new Winston.transports.Console({
colorize: true,

View File

@ -13,5 +13,6 @@ add_jslib(wModel/themeStorage.js lib/wModel/themeStorage ${MAGNUS_DIR} node)
add_jslib(wModel/vocabulary.js lib/wModel/vocabulary ${MAGNUS_DIR} node)
add_jslib(wModel/attributes.js lib/wModel/attributes ${MAGNUS_DIR} node)
add_jslib(wModel/image.js lib/wModel/image ${MAGNUS_DIR} node)
add_jslib(wModel/button.js lib/wModel/button ${MAGNUS_DIR} node)
add_subdirectory(proxy)

View File

@ -6,3 +6,4 @@ add_jslib(wModel/proxy/vocabulary.js lib/wModel/proxy/vocabulary ${MAGNUS_DIR} n
add_jslib(wModel/proxy/catalogue.js lib/wModel/proxy/catalogue ${MAGNUS_DIR} node)
configure_file(pane.js pane.js)
configure_file(panesList.js panesList.js)

View File

@ -4,12 +4,18 @@ var MVocabulary = require("./vocabulary");
var Address = require("../../wType/address");
var Boolean = require("../../wType/boolean");
var Uint64 = require("../../wType/uint64");
var String = require("../../wType/string");
var Vocabulary = require("../../wType/vocabulary");
var Vector = require("../../wType/vector");
var Pane = MVocabulary.inherit({
"className": "Pane",
"constructor": function(address, controllerAddress, socket) {
"constructor": function(address, controllerAddress, socket, actions) {
MVocabulary.fn.constructor.call(this, address, controllerAddress, socket);
this._actions = new Vector();
this._createActions(actions || []);
if (this.constructor.pageAddress) {
this.hasPageLink = true;
@ -17,14 +23,45 @@ var Pane = MVocabulary.inherit({
this._pageLink = this.constructor.pageAddress["+"](new Address([id.toString()]));
}
},
"destructor": function() {
this._actions.destructor();
if (this.hasPageLink) {
this._pageLink.destructor();
}
MVocabulary.fn.destructor.call(this);
},
"_createActions": function(actions) {
for (var i = 0; i < actions.length; ++i) {
var action = actions[i];
var actionVC = new Vocabulary();
actionVC.insert("type", new Uint64(action.type));
var supported = false;
switch (action.type) {
case 0: //this type of actions has only action itself, no additional arguments, what to do is desctibed in the view
actionVC.insert("action", new String(action.action));
supported = true;
break;
}
if (supported) {
this._actions.push(actionVC);
} else {
actionVC.destructor();
}
}
},
"_getAllData": function() {
var vc = this.controller.data.clone();
vc.insert("hasPageLink", new Boolean(this.hasPageLink));
vc.insert("actions", this._actions.clone());
if (this.hasPageLink) {
vc.insert("pageLink", this._pageLink.clone());
}
return vc;
}
});

View File

@ -0,0 +1,66 @@
"use strict";
var Vocabulary = require("../../wType/vocabulary");
var Uint64 = require("../../wType/uint64");
var String = require("../../wType/string");
var Vector = require("../../wType/vector");
var Catalogue = require("./catalogue");
var PanesList = Catalogue.inherit({
"className": "PanesList",
"constructor": function(address, ctrlAddr, ctrlOpts, socket, actions) {
Catalogue.fn.constructor.call(this, address, ctrlAddr, ctrlOpts, socket);
this._actions = new Vector();
this._createActions(actions || []);
},
"destructor": function() {
this._actions.destructor();
Catalogue.fn.destructor.call(this);
},
"_createActions": function(actions) {
for (var i = 0; i < actions.length; ++i) {
var action = actions[i];
var actionVC = new Vocabulary();
actionVC.insert("type", new Uint64(action.type));
var supported = false;
switch (action.type) {
case 0: //this type of actions has only action itself, no additional arguments, what to do is desctibed in the view
actionVC.insert("action", new String(action.action));
supported = true;
break;
}
if (supported) {
this._actions.push(actionVC);
} else {
actionVC.destructor();
}
}
},
"_h_get": function(ev) {
if (this.ready) {
var vc = new Vocabulary();
vc.insert("data", this._getAllData());
vc.insert("actions", this._actions.clone());
this.response(vc, "get", ev);
} else {
this._waitingEvents.push(ev.clone());
}
},
"_onRemoteData": function() {
this.setReady(true);
var vc = new Vocabulary();
vc.insert("data", this._getAllData());
vc.insert("actions", this._actions.clone());
this.broadcast(vc, "get")
}
});
module.exports = PanesList;

View File

@ -2,7 +2,7 @@
var TempPage = require("./tempPage");
var String = require("../lib/wModel/string");
var ProxyCatModel = require("../lib/wModel/proxy/catalogue");
var PanesList = require("../lib/wModel/proxy/panesList");
var PaneModel = require("../lib/wModel/proxy/pane");
var Image = require("../lib/wModel/image");
var Model = require("../lib/wModel/model");
@ -41,16 +41,23 @@ var AlbumPage = TempPage.inherit({
var spacer = new Model(this._address["+"](new Address(["spacer"])));
this.addItem(spacer, 0, 2, 2, 1);
this._songs = new ProxyCatModel(
this._songs = new PanesList(
this._address["+"](new Address(["songs"])),
new Address(["songs"]),
{
sorting: {ascending: true, field: "name"},
filter: {album: id.clone()}
},
proxySocket
proxySocket,
[{
type: 0,
action: "play"
},
{
type: 0,
action: "scheduledToPlay"
}]
);
this._songs.className = "PanesList";
var PaneClass = PaneModel.Songs;
this._songs.setChildrenClass(PaneClass);
this.addItem(this._songs, 2, 0, 1, 3, TempPage.Aligment.CenterTop);

View File

@ -2,7 +2,7 @@
var TempPage = require("./tempPage");
var String = require("../lib/wModel/string");
var ProxyCatModel = require("../lib/wModel/proxy/catalogue");
var PanesList = require("../lib/wModel/proxy/panesList");
var PaneModel = require("../lib/wModel/proxy/pane");
var VCController = require("../lib/wController/vocabulary");
@ -22,7 +22,7 @@ var ArtistPage = TempPage.inherit({
header.addProperty("fontFamily", "casualFont");
this.addItem(header, 0, 0, 1, 1, TempPage.Aligment.CenterTop);
this._albums = new ProxyCatModel(
this._albums = new PanesList(
this._address["+"](new Address(["albums"])),
new Address(["albums"]),
{
@ -36,16 +36,23 @@ var ArtistPage = TempPage.inherit({
this._albums.setChildrenClass(PaneClass);
this.addItem(this._albums, 1, 0, 1, 1, TempPage.Aligment.CenterTop);
this._songs = new ProxyCatModel(
this._songs = new PanesList(
this._address["+"](new Address(["songs"])),
new Address(["songs"]),
{
sorting: {ascending: true, field: "name"},
filter: {artist: id.clone()}
},
proxySocket
proxySocket,
[{
type: 0,
action: "play"
},
{
type: 0,
action: "scheduledToPlay"
}]
);
this._songs.className = "PanesList";
var PaneClass = PaneModel.Songs;
this._songs.setChildrenClass(PaneClass);
this.addItem(this._songs, 2, 0, 1, 1, TempPage.Aligment.CenterTop);

View File

@ -2,42 +2,45 @@
var Page = require("../lib/wModel/page");
var String = require("../lib/wModel/string");
var ProxyCatModel = require("../lib/wModel/proxy/catalogue");
var PanesList = require("../lib/wModel/proxy/panesList");
var PaneModel = require("../lib/wModel/proxy/pane");
var Address = require("../lib/wType/address");
var Uint64 = require("../lib/wType/uint64");
var Vocabulary = require("../lib/wType/vocabulary");
var Handler = require("../lib/wDispatcher/handler");
var List = Page.inherit({
"className": "ListPage",
"constructor": function(address, name, remoteAddress, socket, ChildClass) {
"constructor": function(address, name, remoteAddress, socket, ChildClass, listOptions, paneActions) {
Page.fn.constructor.call(this, address, name);
this._proxySocket = socket;
this._ChildClass = ChildClass;
this._listOptions = listOptions || new Vocabulary()
var header = new String(this._address["+"](new Address(["header"])), name);
header.addProperty("fontFamily", "casualFont");
this.addItem(header, 0, 0, 1, 1, Page.Aligment.CenterTop);
this._list = new ProxyCatModel(
this._list = new PanesList(
this._address["+"](new Address(["list"])),
remoteAddress,
{
sorting: {ascending: true, field: "name"}
},
socket
socket,
paneActions
);
this._list.className = "PanesList";
var PaneClass = PaneModel[name];
if (!PaneClass) {
PaneClass = PaneModel.inherit({});
PaneClass.pageAddress = address;
PaneModel[name] = PaneClass;
}
this._list.setChildrenClass(PaneClass);
this.addItem(this._list, 1, 0, 1, 1, Page.Aligment.CenterTop);
this.addItem(this._list, 1, 0, 1, 1, Page.Aligment.CenterTop, new Uint64(List.getModelTypeId(this._list)), listOptions);
this._reporterHandler = new Handler(this._address["+"](new Address(["subscribeMember"])), this, this._h_subscribeMember);
},

View File

@ -7,6 +7,7 @@ var PanesList = require("../lib/wModel/panesList");
var Address = require("../lib/wType/address");
var Vocabulary = require("../lib/wType/vocabulary");
var Boolean = require("../lib/wType/boolean");
var Uint64 = require("../lib/wType/uint64");
var Link = require("../lib/wModel/link");
@ -135,12 +136,23 @@ var MusicPage = Page.inherit({
this._albumsLink.label.addProperty("color", "primaryFontColor");
this._albumsLink.addProperty("backgroundColor", "primaryColor");
var lOpts = new Vocabulary();
lOpts.insert("nestWidth", new Uint64(300));
this._songs = new List(
this._addresses.songs.local.clone(),
"Songs",
this._addresses.songs.remote.clone(),
socket,
Song
Song,
lOpts,
[{
type: 0,
action: "play"
},
{
type: 0,
action: "scheduledToPlay"
}]
);
this._songsLink = new Link(this._address["+"](new Address(["songsLink"])), "Songs", this._addresses.songs.local.clone());
this._songsLink.label.addProperty("fontSize", "largeFontSize");

View File

@ -2,6 +2,7 @@
var Page = require("../lib/wModel/page");
var String = require("../lib/wModel/string");
var Button = require("../lib/wModel/button");
var Address = require("../lib/wType/address");
@ -13,6 +14,16 @@ var TestPage = Page.inherit({
var header = new String(this._address["+"](new Address(["message"])), "This is a test page");
header.addProperty("fontFamily", "casualFont");
this.addItem(header, 0, 0, 1, 1, Page.Aligment.CenterTop);
this._button = new Button(this._address["+"](new Address(["testButton"])));
this._button.setLabel("Push me");
this._button.on("activated", this._onActivate, this);
this.addItem(this._button, 1, 0, 1, 1, Page.Aligment.CenterTop);
},
"_onActivate": function() {
this.trigger("serviceMessage", "Button works!");
this._button.setEnabled(false);
setTimeout(this._button.setEnabled.bind(this._button, true), 3000);
}
});

View File

@ -25,7 +25,7 @@ target_link_libraries(perturabo wSocket)
target_link_libraries(perturabo wDispatcher)
target_link_libraries(perturabo utils)
target_link_libraries(perturabo wModel)
target_link_libraries(perturabo wServerUtils)
target_link_libraries(perturabo wDatabase)
target_link_libraries(perturabo wServerUtils)
install(TARGETS perturabo RUNTIME DESTINATION bin)

View File

@ -86,11 +86,11 @@ void Perturabo::start()
server->listen(8082);
cout << "Registering models..." << endl;
attributes->registerModel(dispatcher, server);
commands->registerModel(dispatcher, server);
attributes->getRegistered(connector);
commands->getRegistered(connector);
for (; beg != end; ++beg) {
beg->second->registerModel(dispatcher, server);
beg->second->getRegistered(connector);
}
cout << "Opening and indexing databases..." << endl;
@ -115,11 +115,11 @@ void Perturabo::stop()
commands->enableCommand(W::String(u"clearDatabase"), false);
for (; beg != end; ++beg) {
beg->second->unregisterModel();
beg->second->getUnregistered();
}
commands->unregisterModel();
attributes->unregisterModel();
commands->getUnregistered();
attributes->getUnregistered();
server->stop();
}

View File

@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.8.12)
project(test)
find_package(CxxTest)
cmake_policy(SET CMP0071 NEW)
if(CXXTEST_FOUND)
include_directories(${CXXTEST_INCLUDE_DIR})
enable_testing()