View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -36,6 +36,7 @@
#include "gui/lua/widgets.hpp"
#include "gui/lua/audio.hpp"
#include "gui/app/app.hpp"
#include "gui/api/service.hpp"
#include "conversation/conversation_service.hpp"
namespace fire
@@ -45,10 +46,6 @@ namespace fire
namespace lua
{
using script_ptr = std::shared_ptr<SLB::Script>;
using widget_map = std::unordered_map<int, QWidget*>;
using image_map = std::unordered_map<int, QImage_ptr>;
using layout_map = std::unordered_map<int, QGridLayout*>;
using timer_map = std::unordered_map<int, QTimer*>;
using callback_map = std::unordered_map<std::string, std::string>;
struct error_info
@@ -58,34 +55,41 @@ namespace fire
};
using error_infos = std::vector<error_info>;
class lua_api : public QObject
class lua_api : public api::backend
{
Q_OBJECT
public:
lua_api(
app::app_ptr a,
messages::sender_ptr sender,
conversation::conversation_ptr conversation,
conversation::conversation_service_ptr conversation_service,
QWidget* can,
QGridLayout* lay,
list* out = nullptr);
api::frontend*);
~lua_api();
public:
virtual void button_clicked(api::ref_id);
virtual void edit_edited(api::ref_id id);
virtual void edit_finished(api::ref_id id);
virtual void text_edit_edited(api::ref_id id);
virtual void timer_triggered(api::ref_id id);
virtual void got_sound(api::ref_id id, const util::bytes&);
virtual void draw_mouse_pressed(api::ref_id, int button, int x, int y);
virtual void draw_mouse_released(api::ref_id, int button, int x, int y);
virtual void draw_mouse_dragged(api::ref_id, int button, int x, int y);
virtual void draw_mouse_moved(api::ref_id, int x, int y);
public:
//misc
app::app_ptr app;
store_ref_ptr local_data;
store_ref_ptr data;
error_infos errors;
list* output;
QWidget* canvas;
QGridLayout* layout;
SLB::Manager manager;
script_ptr state;
std::string who_started_id;
std::mutex mutex;
api::frontend* front;
//message
conversation::conversation_ptr conversation;
@@ -118,15 +122,9 @@ namespace fire
microphone_ref_map mic_refs;
speaker_ref_map speaker_refs;
//all widgets referenced are stored here
layout_map layouts;
widget_map widgets;
image_map images;
timer_map timers;
//id functions
int ids = 0;
int new_id();
api::ref_id ids = 0;
api::ref_id new_id();
//observable functions
observable_ref_name_map observable_names;
@@ -149,15 +147,15 @@ namespace fire
text_edit_ref make_text_edit(const std::string& text);
list_ref make_list();
draw_ref make_draw(int width, int height);
QPen make_pen(const std::string& color, int width);
pen_ref make_pen(const std::string& color, int width);
timer_ref make_timer(int msec, const std::string& callback);
image_ref make_image(const bin_data& data);
//multimedia
microphone_ref make_mic(const std::string& callback, const std::string& codec);
speaker_ref make_speaker(const std::string& codec);
opus_encoder make_audio_encoder();
opus_decoder make_audio_decoder();
opus_encoder_wrapper make_audio_encoder();
opus_decoder_wrapper make_audio_decoder();
vclock_wrapper make_vclock();
//grid
@@ -199,20 +197,12 @@ namespace fire
bool launched_local() const;
//file
file_data open_file();
bin_file_data open_bin_file();
file_data_wrapper open_file();
bin_file_data_wrapper open_bin_file();
bool save_file(const std::string& suggested_name, const std::string& data);
bool save_bin_file(const std::string& suggested_name, const bin_data& data);
void connect_sound(int id, QAudioInput*, QIODevice*);
public slots:
void button_clicked(int id);
void edit_edited(int id);
void edit_finished(int id);
void text_edit_edited(int id);
void timer_triggered(int id);
void got_sound(int id);
private:
bool visible() const;
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -33,6 +33,7 @@
#define FIRESTR_APP_LUA_AUDIO_H
#include "gui/lua/base.hpp"
#include "util/audio.hpp"
#include <QAudioFormat>
#include <QAudioInput>
@@ -48,108 +49,26 @@ namespace fire
{
namespace lua
{
class opus_encoder
{
public:
opus_encoder();
~opus_encoder();
public:
bin_data encode(const bin_data&);
private:
OpusEncoder* _opus = nullptr;
};
using opus_encoder_ptr = std::shared_ptr<opus_encoder>;
class opus_decoder
{
public:
opus_decoder();
~opus_decoder();
public:
bin_data decode(const bin_data&);
private:
OpusDecoder* _opus = nullptr;
};
using opus_decoder_ptr = std::shared_ptr<opus_decoder>;
enum codec_type { pcm, opus};
class microphone
{
public:
microphone(lua_api*, int id, const std::string& codec = "pcm");
void stop();
void start();
bool recording() const;
codec_type codec() const;
util::bytes encode(const util::bytes&);
util::bytes read_data();
private:
QAudioFormat _f;
QAudioDeviceInfo _inf;
QAudioInput* _i;
codec_type _t;
int _id;
QIODevice* _d = nullptr;
bool _recording = false;
lua_api* _api;
//opus specific members
opus_encoder_ptr _opus;
util::bytes _buffer;
size_t _skip = 0;
size_t _channels = 0;
};
using microphone_ptr = std::shared_ptr<microphone>;
struct microphone_ref : public basic_ref
{
std::string callback;
void set_callback(const std::string&);
void stop();
void start();
microphone_ptr mic;
};
using microphone_ref_map = std::unordered_map<int, microphone_ref>;
struct speaker
{
public:
speaker(lua_api*, const std::string& code = "pcm");
void mute();
void unmute();
void play(const bin_data&);
codec_type codec() const;
util::bytes decode(const util::bytes&);
private:
bool _mute = false;
QAudioFormat _f;
codec_type _t;
QAudioOutput* _o;
QIODevice* _d = nullptr;
lua_api* _api;
//opus specific members
opus_decoder_ptr _opus;
size_t _rep = 0;
size_t _channels = 0;
};
using speaker_ptr = std::shared_ptr<speaker>;
struct speaker_ref : public basic_ref
{
void mute();
void unmute();
void play(const bin_data&);
speaker_ptr spkr;
};
using speaker_ref_map = std::unordered_map<int, speaker_ref>;
extern const size_t MAX_SAMPLE_BYTES;
}
}
}
View
@@ -29,17 +29,12 @@
* also delete it here.
*/
#include <QtWidgets>
#include "gui/lua/base.hpp"
#include "gui/lua/api.hpp"
#include "gui/util.hpp"
#include "util/dbc.hpp"
#include "util/log.hpp"
#include <QTimer>
#include <QSignalMapper>
#include <functional>
namespace m = fire::message;
@@ -57,29 +52,10 @@ namespace fire
namespace
{
const std::string ARRAY_K = "__a";
const size_t FRAMES = 480; //40ms of PCM frames. Opus can handles 2.5, 5, 10, 20, 40 or 60ms of audio per frame.
const size_t MAX_FRAMES = 2*FRAMES;
const size_t MAX_OPUS_DECODE_SIZE = MAX_FRAMES * sizeof(opus_int16);
const size_t SAMPLE_RATE = 12000;
const size_t SAMPLE_SIZE = 16;
const size_t CHANNELS = 1;
const std::string Q_CODEC = "audio/pcm";
const size_t MIN_BUF_SIZE = FRAMES * sizeof(opus_int16);
}
const size_t MAX_SAMPLE_BYTES = SAMPLE_SIZE * FRAMES;
void set_enabled(int id, widget_map& map, bool enabled)
{
auto w = get_widget<QWidget>(id, map);
if(!w) return;
w->setEnabled(enabled);
}
void observable_ref::set_name(const std::string& n)
{
std::lock_guard<std::mutex> lock(api->mutex);
INVARIANT(api);
auto obs = api->get_observable(id);
@@ -92,33 +68,31 @@ namespace fire
std::string observable_ref::get_name() const
{
std::lock_guard<std::mutex> lock(api->mutex);
return _name;
}
bool widget_ref::enabled()
{
INVARIANT(api);
std::lock_guard<std::mutex> lock(api->mutex);
INVARIANT(api->front);
auto w = get_widget<QWidget>(id, api->widgets);
return w ? w->isEnabled() : false;
return api->front->is_widget_enabled(id);
}
void widget_ref::enable()
{
INVARIANT(api);
std::lock_guard<std::mutex> lock(api->mutex);
INVARIANT(api->front);
set_enabled(id, api->widgets, true);
api->front->widget_enable(id, true);
}
void widget_ref::disable()
{
INVARIANT(api);
std::lock_guard<std::mutex> lock(api->mutex);
INVARIANT(api->front);
set_enabled(id, api->widgets, false);
api->front->widget_enable(id, true);
}
std::string app_ref::get_id() const
@@ -162,7 +136,6 @@ namespace fire
if(is_self) return true;
std::lock_guard<std::mutex> lock(api->mutex);
return api->conversation->user_service()->contact_available(user_id);
}
@@ -198,44 +171,44 @@ namespace fire
data.insert(data.end(), n.data.begin(), n.data.end());
}
std::string bin_file_data::get_name() const
std::string bin_file_data_wrapper::get_name() const
{
return name;
return file.name;
}
size_t bin_file_data::get_size() const
size_t bin_file_data_wrapper::get_size() const
{
return data.data.size();
return file.data.size();
}
bin_data bin_file_data::get_data() const
bin_data bin_file_data_wrapper::get_data() const
{
return data;
return bin_data{file.data};
}
bool bin_file_data::is_good() const
bool bin_file_data_wrapper::is_good() const
{
return good;
return file.good;
}
std::string file_data::get_name() const
std::string file_data_wrapper::get_name() const
{
return name;
return file.name;
}
size_t file_data::get_size() const
size_t file_data_wrapper::get_size() const
{
return data.size();
return file.data.size();
}
std::string file_data::get_data() const
std::string file_data_wrapper::get_data() const
{
return data;
return file.data;
}
bool file_data::is_good() const
bool file_data_wrapper::is_good() const
{
return good;
return file.good;
}
const std::string EVENT_MESSAGE = "#e";
@@ -374,7 +347,6 @@ namespace fire
{
INVARIANT(_api);
INVARIANT(_api->conversation);
std::lock_guard<std::mutex> lock(_api->mutex);
auto c = _api->conversation->contacts().by_id(_from_id);
if(!c || is_local()) return empty_contact_ref(*_api);
View
@@ -32,23 +32,17 @@
#ifndef FIRESTR_APP_LUA_BASE_H
#define FIRESTR_APP_LUA_BASE_H
#include "gui/api/service.hpp"
#include "gui/list.hpp"
#include "gui/message.hpp"
#include "conversation/conversation.hpp"
#include "message/mailbox.hpp"
#include "messages/sender.hpp"
#include "util/audio.hpp"
#include "util/vclock.hpp"
#include "util/disk_store.hpp"
#include <QObject>
#include <QLabel>
#include <QTextEdit>
#include <QPushButton>
#include <QComboBox>
#include <QSignalMapper>
#include <QGraphicsView>
#include "slb/SLB.hpp"
#include <string>
@@ -121,22 +115,18 @@ namespace fire
std::string to_str() const;
};
struct bin_file_data
struct bin_file_data_wrapper
{
std::string name;
bin_data data;
bool good = false;
api::bin_file_data file;
std::string get_name() const;
size_t get_size() const;
bin_data get_data() const;
bool is_good() const;
};
struct file_data
struct file_data_wrapper
{
std::string name;
std::string data;
bool good = false;
api::file_data file;
std::string get_name() const;
size_t get_size() const;
std::string get_data() const;
@@ -225,6 +215,22 @@ namespace fire
util::disk_store& _d;
};
class opus_encoder_wrapper
{
public:
bin_data encode(const bin_data& d) { return bin_data{_e.encode(d.data)};}
private:
util::opus_encoder _e;
};
class opus_decoder_wrapper
{
public:
bin_data decode(const bin_data& d) { return bin_data{_e.decode(d.data)};}
private:
util::opus_decoder _e;
};
class vclock_wrapper
{
public:
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -34,15 +34,19 @@
#include "gui/lua/base.hpp"
#include <QImage>
namespace fire
{
namespace gui
{
namespace lua
{
struct pen_ref : public widget_ref
{
void set_width(int);
};
struct image_ref;
struct button_ref : public widget_ref
{
std::string callback;
@@ -106,12 +110,6 @@ namespace fire
void remove(const widget_ref& r);
size_t size() const;
void clear();
private:
using list_widget = std::pair<list*, QWidget*>;
list* get_list() const;
list_widget get_both(const widget_ref& r);
};
using list_ref_map = std::unordered_map<int, list_ref>;
@@ -145,8 +143,8 @@ namespace fire
void set_mouse_pressed_callback(const std::string&);
void set_mouse_moved_callback(const std::string&);
void set_mouse_dragged_callback(const std::string&);
void set_pen(QPen);
QPen get_pen() { return pen;}
void set_pen(pen_ref);
pen_ref get_pen() { return pen;}
void mouse_pressed(int button, int x, int y);
void mouse_released(int button, int x, int y);
@@ -156,26 +154,10 @@ namespace fire
void handle(const std::string& t, const util::value& event);
draw_view* get_view();
QPen pen;
pen_ref pen;
};
using draw_ref_map = std::unordered_map<int, draw_ref>;
class draw_view : public QGraphicsView
{
Q_OBJECT
public:
draw_view(draw_ref, int width, int height, QWidget* parent = nullptr);
protected:
void mousePressEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
private:
draw_ref _ref;
int _button;
};
struct timer_ref : public basic_ref
{
bool running();
@@ -198,7 +180,6 @@ namespace fire
bool g;
};
using QImage_ptr = std::shared_ptr<QImage>;
using image_ref_map = std::unordered_map<int, image_ref>;
}
View
@@ -0,0 +1,349 @@
/*
* Copyright (C) 2014 Maxim Noah Khailo
*
* 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 vedit_refsion 3 of the License, or
* (at your option) any later vedit_refsion.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* Botan library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU General Public License in all respects for
* all of the code used other than Botan. If you modify file(s) with
* this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version. If you delete
* this exception statement from all source files in the program, then
* also delete it here.
*/
#include <QtWidgets>
#include "gui/qtw/audio.hpp"
#include "gui/qtw/frontend.hpp"
#include "gui/util.hpp"
#include "util/dbc.hpp"
#include "util/log.hpp"
#include <QAudioDeviceInfo>
namespace u = fire::util;
namespace fire
{
namespace gui
{
namespace qtw
{
namespace
{
const size_t FRAMES = u::FRAMES; //40ms of PCM frames. Opus can handles 2.5, 5, 10, 20, 40 or 60ms of audio per frame.
const size_t SAMPLE_RATE = u::SAMPLE_RATE;
const size_t SAMPLE_SIZE = 16;
const size_t MAX_SAMPLE_BYTES = SAMPLE_SIZE * FRAMES;
const size_t CHANNELS = u::CHANNELS;
const std::string Q_CODEC = "audio/pcm";
const size_t MIN_BUF_SIZE = u::MIN_BUF_SIZE;
}
codec_type parse_codec(const std::string& codec)
{
if(codec == "pcm") return codec_type::pcm;
else if(codec == "opus") return codec_type::opus;
return codec_type::pcm;
}
microphone::microphone(api::backend* back, qt_frontend* front, api::ref_id id, const std::string& codec) :
_id{id}, _back{back}, _front{front}
{
REQUIRE(back);
REQUIRE(front);
INVARIANT(_back);
INVARIANT(_front);
_f.setSampleRate(SAMPLE_RATE);
_f.setChannelCount(CHANNELS);
_f.setSampleSize(SAMPLE_SIZE);
_f.setSampleType(QAudioFormat::SignedInt);
_f.setByteOrder(QAudioFormat::LittleEndian);
_f.setCodec(Q_CODEC.c_str());
_t = parse_codec(codec);
_inf = QAudioDeviceInfo::defaultInputDevice();
if (!_inf.isFormatSupported(_f))
{
_f = _inf.nearestFormat(_f);
LOG << "format not supported, using nearest." << std::endl;
LOG << "sample rate: " << _f.sampleRate() << std::endl;
LOG << "sample size: " << _f.sampleSize() << std::endl;
LOG << "channels: " << _f.channelCount() << std::endl;
LOG << "codec: " << convert(_f.codec()) << std::endl;
}
LOG << "using mic device: " << convert(_inf.deviceName()) << std::endl;
_i = new QAudioInput{_inf, _f, _front->canvas};
CHECK_GREATER_EQUAL(static_cast<size_t>(_f.sampleRate()), SAMPLE_RATE);
_skip = _f.sampleRate() / SAMPLE_RATE;
_channels = _f.channelCount();
if(_t == codec_type::opus) _opus = std::make_shared<u::opus_encoder>();
}
//simple low pass filter
void reduce_noise(u::bytes& s, size_t len)
{
REQUIRE_EQUAL(s.size() % 2, 0);
auto ss = reinterpret_cast<short*>(s.data());
len /= 2;
for(size_t i = 1; i < len; i++)
ss[i] = (0.333 * ss[i]) + ((1 - 0.333) * ss[i-1]) + 0.5;
}
//decimate sound to SAMPLE_RATE, using averaging
void decimate(const u::bytes& s, u::bytes& d, size_t channels, size_t skip)
{
REQUIRE_FALSE(s.empty());
REQUIRE_EQUAL(s.size() % 2, 0);
//get sizes
auto dz = (s.size() / skip);
auto nz = d.size() + dz;
//add padding
if(nz % 2 == 1) nz += 1;
CHECK_EQUAL(nz % 2, 0);
//resize dest
const auto odz = d.size();
d.resize(nz);
//cast to short arrays
auto ss = reinterpret_cast<const short*>(s.data());
const auto sz = s.size() / 2;
auto sd = reinterpret_cast<short*>(d.data());
const auto sdz = nz / 2;
int accum = 0;
size_t c = 1;
size_t si = 0;
auto di = odz / 2;
for(;si < sz; si+=channels)
{
accum += static_cast<int>(ss[si]);
if(c == skip)
{
accum /= c;
sd[di] = accum;
di++;
accum = 0;
c = 1;
continue;
}
c++;
}
//repeat last value if we have padding
si = sz-1;
while(di < sdz)
{
sd[di] = ss[si];
di++;
}
CHECK_EQUAL(di, sdz);
}
u::bytes microphone::encode(const u::bytes& b)
{
REQUIRE(_opus);
REQUIRE_FALSE(b.empty());
return _opus->encode(b);
}
u::bytes microphone::read_data()
{
REQUIRE(_d);
if(!_d) return {};
INVARIANT(_i);
auto len = _i->bytesReady();
if(len <= 0) return {};
if(static_cast<size_t>(len) > MAX_SAMPLE_BYTES) len = MAX_SAMPLE_BYTES;
u::bytes data;
data.resize(len);
auto l = _d->read(data.data(), len);
if(l <= 0) return {};
data.resize(l);
//decimate and add to buffer
decimate(data, _buffer, _channels, _skip);
if(_buffer.size() < MIN_BUF_SIZE) return {};
//once we have enough data, do noise reduction
reduce_noise(_buffer, MIN_BUF_SIZE);
//copy buffer to result
u::bytes r(_buffer.begin(), _buffer.begin() + MIN_BUF_SIZE);
//copy extra to front and resize. Probably a better idea to use a circular buf here.
auto final_buf_size = _buffer.size() - MIN_BUF_SIZE;
std::copy(_buffer.begin() + MIN_BUF_SIZE, _buffer.end(), _buffer.begin());
_buffer.resize(final_buf_size);
return r;
}
codec_type microphone::codec() const
{
return _t;
}
bool microphone::recording() const
{
return _recording;
}
void microphone::stop()
{
INVARIANT(_i);
_recording = false;
}
void microphone::start()
{
INVARIANT(_i);
INVARIANT(_back);
if(!_d)
{
_d = _i->start();
if(_d) _front->connect_sound(_id, _i, _d);
}
_recording = true;
}
void inflate(const u::bytes& s, u::bytes& d, size_t channels, size_t rep)
{
REQUIRE_EQUAL(s.size() % 2, 0);
rep*=channels;
d.resize(s.size() * rep);
auto ss = reinterpret_cast<const short*>(s.data());
auto sz = s.size() / 2;
auto sd = reinterpret_cast<short*>(d.data());
size_t di = 0;
for(size_t si = 0; si < sz; si++)
for(size_t p = 0; p < rep; p++, di++)
sd[di] = ss[si];
}
speaker::speaker(api::backend* back, qt_frontend* front, const std::string& codec) : _back{back}, _front{front}
{
REQUIRE(back);
REQUIRE(front);
INVARIANT(_back);
INVARIANT(_front);
_f.setSampleRate(SAMPLE_RATE);
_f.setChannelCount(CHANNELS);
_f.setSampleSize(SAMPLE_SIZE);
_f.setSampleType(QAudioFormat::SignedInt);
_f.setByteOrder(QAudioFormat::LittleEndian);
_f.setCodec(Q_CODEC.c_str());
_t = parse_codec(codec);
QAudioDeviceInfo i{QAudioDeviceInfo::defaultOutputDevice()};
if (!i.isFormatSupported(_f)) _f = i.nearestFormat(_f);
LOG << "using speaker device: " << convert(i.deviceName()) << std::endl;
_o = new QAudioOutput{i, _f, _front};
CHECK_GREATER_EQUAL(static_cast<size_t>(_f.sampleRate()), SAMPLE_RATE);
_rep = _f.sampleRate() / SAMPLE_RATE;
_channels = _f.channelCount();
if(_t == codec_type::opus) _opus = std::make_shared<u::opus_decoder>();
}
u::bytes speaker::decode(const u::bytes& b)
{
REQUIRE(_opus);
return _opus->decode(b);
}
codec_type speaker::codec() const
{
return _t;
}
void speaker::mute()
{
INVARIANT(_o);
_mute = true;
}
void speaker::unmute()
{
INVARIANT(_o);
_mute = false;
}
void speaker::play(const u::bytes& d)
{
INVARIANT(_o);
if(_mute) return;
if(d.empty()) return;
const u::bytes* data = &d;
u::bytes dec;
if(_t == codec_type::opus)
{
dec = decode(d);
data = &dec;
}
CHECK(data);
//repeat to match speaker sample rate
u::bytes r;
inflate(*data, r, _channels, _rep);
if(_d)
{
_d->write(r.data(), r.size());
if(_o->state() == QAudio::SuspendedState)
{
_o->reset();
_o->resume();
}
}
else
{
_d = _o->start();
_d->write(r.data(), r.size());
}
}
}
}
}
View
@@ -0,0 +1,113 @@
/*
* Copyright (C) 2014 Maxim Noah Khailo
*
* 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 vedit_refsion 3 of the License, or
* (at your option) any later vedit_refsion.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* Botan library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU General Public License in all respects for
* all of the code used other than Botan. If you modify file(s) with
* this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version. If you delete
* this exception statement from all source files in the program, then
* also delete it here.
*/
#ifndef FIRESTR_GUI_QTW_AUDIO_H
#define FIRESTR_GUI_QTW_AUDIO_H
#include "gui/api/service.hpp"
#include "util/audio.hpp"
#include <QAudioFormat>
#include <QAudioInput>
#include <QAudioOutput>
#include <string>
#include <unordered_map>
#include <opus/opus.h>
namespace fire
{
namespace gui
{
namespace qtw
{
class qt_frontend;
enum codec_type { pcm, opus};
class microphone
{
public:
microphone(api::backend*, qt_frontend*, api::ref_id id, const std::string& codec = "pcm");
void stop();
void start();
bool recording() const;
codec_type codec() const;
util::bytes encode(const util::bytes&);
util::bytes read_data();
private:
QAudioFormat _f;
QAudioDeviceInfo _inf;
QAudioInput* _i;
codec_type _t;
api::ref_id _id;
QIODevice* _d = nullptr;
bool _recording = false;
api::backend* _back;
qt_frontend* _front;
//opus specific members
util::opus_encoder_ptr _opus;
util::bytes _buffer;
size_t _skip = 0;
size_t _channels = 0;
};
using microphone_ptr = std::shared_ptr<microphone>;
struct speaker
{
public:
speaker(api::backend*, qt_frontend*, const std::string& code = "pcm");
void mute();
void unmute();
void play(const util::bytes&);
codec_type codec() const;
util::bytes decode(const util::bytes&);
private:
bool _mute = false;
QAudioFormat _f;
codec_type _t;
QAudioOutput* _o;
QIODevice* _d = nullptr;
api::backend* _back;
qt_frontend* _front;
//opus specific members
util::opus_decoder_ptr _opus;
size_t _rep = 0;
size_t _channels = 0;
};
using speaker_ptr = std::shared_ptr<speaker>;
}
}
}
#endif
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,240 @@
/*
* Copyright (C) 2014 Maxim Noah Khailo
*
* 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 vedit_refsion 3 of the License, or
* (at your option) any later vedit_refsion.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* Botan library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU General Public License in all respects for
* all of the code used other than Botan. If you modify file(s) with
* this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version. If you delete
* this exception statement from all source files in the program, then
* also delete it here.
*/
#ifndef FIRESTR_GUI_QTW_H
#define FIRESTR_GUI_QTW_H
#include "gui/app/app.hpp"
#include "gui/api/service.hpp"
#include "gui/qtw/audio.hpp"
#include "gui/list.hpp"
#include "conversation/conversation_service.hpp"
#include <QImage>
#include <QGraphicsView>
namespace fire
{
namespace gui
{
namespace qtw
{
using QImage_ptr = std::shared_ptr<QImage>;
using widget_map = std::unordered_map<api::ref_id, QWidget*>;
using image_map = std::unordered_map<api::ref_id, QImage_ptr>;
using layout_map = std::unordered_map<api::ref_id, QGridLayout*>;
using timer_map = std::unordered_map<api::ref_id, QTimer*>;
using callback_map = std::unordered_map<std::string, std::string>;
using pen_map = std::unordered_map<api::ref_id, QPen>;
using mic_map = std::unordered_map<api::ref_id, microphone_ptr>;
using spk_map = std::unordered_map<api::ref_id, speaker_ptr>;
class qt_frontend;
class draw_view : public QGraphicsView
{
Q_OBJECT
public:
draw_view(qt_frontend*,
api::backend*,
api::ref_id,
int width,
int height,
QWidget* parent = nullptr);
protected:
void mousePressEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
private:
api::ref_id _id;
qt_frontend* _front;
api::backend* _back;
int _button;
};
class qt_frontend : public QObject, public api::frontend
{
Q_OBJECT
public:
qt_frontend(QWidget* c, QGridLayout* cl, list* output);
public:
void set_backend(api::backend*);
public:
//all widgets
virtual void place(api::ref_id, int r, int c);
virtual void place_across(api::ref_id id, int r, int c, int row_span, int col_span);
virtual void widget_enable(api::ref_id, bool);
virtual bool is_widget_enabled(api::ref_id);
//grid
virtual void add_grid(api::ref_id);
virtual void grid_place(api::ref_id grid_id, api::ref_id widget_id, int r, int c);
virtual void grid_place_across(api::ref_id grid_id, api::ref_id widget_id, int r, int c, int row_span, int col_span);
//button
virtual void add_button(api::ref_id, const std::string&);
virtual std::string button_get_text(api::ref_id);
virtual void button_set_text(api::ref_id, const std::string&);
virtual void button_set_image(api::ref_id id, api::ref_id image_id);
//label
virtual void add_label(api::ref_id, const std::string& text);
virtual std::string label_get_text(api::ref_id);
virtual void label_set_text(api::ref_id, const std::string& text);
//edit
virtual void add_edit(api::ref_id id, const std::string& text);
virtual std::string edit_get_text(api::ref_id);
virtual void edit_set_text(api::ref_id, const std::string& text);
//text edit
virtual void add_text_edit(api::ref_id id, const std::string& text);
virtual std::string text_edit_get_text(api::ref_id);
virtual void text_edit_set_text(api::ref_id, const std::string& text);
//list
virtual void add_list(api::ref_id id);
virtual void list_add(api::ref_id list_id, api::ref_id widget_id);
virtual void list_remove(api::ref_id list_id, api::ref_id widget_id);
virtual size_t list_size(api::ref_id);
virtual void list_clear(api::ref_id);
//pen
virtual void add_pen(api::ref_id id, const std::string& color, int width);
virtual void pen_set_width(api::ref_id id, int width);
//draw
virtual void add_draw(api::ref_id id, int width, int height);
virtual void draw_line(api::ref_id id, api::ref_id pen_id, double x1, double y1, double x2, double y2);
virtual void draw_circle(api::ref_id id, api::ref_id pen_id, double x, double y, double r);
virtual void draw_image(api::ref_id id, api::ref_id image_id, double x, double y, double w, double h);
virtual void draw_clear(api::ref_id id);
//timer
virtual void add_timer(api::ref_id id, int msec);
virtual bool timer_running(api::ref_id id);
virtual void timer_stop(api::ref_id id);
virtual void timer_start(api::ref_id id);
virtual void timer_set_interval(api::ref_id, int msec);
//image
virtual bool add_image(api::ref_id, const util::bytes& d);
virtual int image_width(api::ref_id);
virtual int image_height(api::ref_id);
//mic
virtual void add_mic(api::ref_id id, const std::string& codec);
virtual void mic_start(api::ref_id);
virtual void mic_stop(api::ref_id);
//speaker
virtual void add_speaker(api::ref_id, const std::string& codec);
virtual void speaker_mute(api::ref_id);
virtual void speaker_unmute(api::ref_id);
virtual void speaker_play(api::ref_id, const util::bytes&);
//file
virtual api::file_data open_file();
virtual api::bin_file_data open_bin_file();
virtual bool save_file(const std::string&, const std::string&);
virtual bool save_bin_file(const std::string&, const util::bytes&);
//debug
virtual void print(const std::string&);
//overall gui
virtual void height(int h);
virtual void grow();
virtual bool visible();
//errors
virtual void report_error(const std::string& e);
virtual void reset();
public slots:
void button_clicked(int id);
void edit_edited(int id);
void edit_finished(int id);
void text_edit_edited(int id);
void timer_triggered(int id);
void got_sound(int id);
public:
void connect_sound(api::ref_id id, QAudioInput* i, QIODevice* d);
private:
friend class microphone;
friend class speaker;
private:
//all widgets referenced are stored here
layout_map layouts;
widget_map widgets;
image_map images;
timer_map timers;
pen_map pens;
mic_map mics;
spk_map spkrs;
list* output = nullptr;
QWidget* canvas = nullptr;
QGridLayout* layout = nullptr;
api::backend* back = nullptr;
};
using qt_frontend_ptr = std::shared_ptr<qt_frontend>;
template<class W, class M>
W* get_widget(int id, M& map)
{
auto wp = map.find(id);
return wp != map.end() ? dynamic_cast<W*>(wp->second) : nullptr;
}
template<class W, class M>
W get_ptr_from_map(int id, M& map)
{
auto wp = map.find(id);
return wp != map.end() ? wp->second : nullptr;
}
}
}
}
#endif
View
@@ -53,71 +53,56 @@ namespace fire
out_pop_count = 0;
}
mailbox::mailbox() :
_address{}, _in{}, _out{}
{
}
mailbox::mailbox() : _m{} { }
mailbox::mailbox(const std::string& a) :
_address{a}, _in{}, _out{}
{
}
mailbox::mailbox(const std::string& a) : _m{a} { }
mailbox::~mailbox()
{
done();
}
void mailbox::done()
{
_in.done();
_out.done();
}
void mailbox::done() { _m.done(); }
const std::string& mailbox::address() const
{
return _address;
return _m.address();
}
void mailbox::address(const std::string& a)
{
_address = a;
_m.address(a);
}
void mailbox::push_inbox(const message& m)
{
if(_stats.on) _stats.in_push_count++;
_in.push(m);
_m.push_inbox(m);
}
bool mailbox::pop_inbox(message& m, bool wait)
{
const bool p = _in.pop(m, wait);
const bool p = _m.pop_inbox(m, wait);
if(_stats.on && p) _stats.in_pop_count++;
return p;
}
void mailbox::push_outbox(const message& m)
{
if(_stats.on) _stats.out_push_count++;
_out.push(m);
_m.push_outbox(m);
}
bool mailbox::pop_outbox(message& m, bool wait)
{
bool p = _out.pop(m, wait);
const bool p = _m.pop_outbox(m, wait);
if(_stats.on && p) _stats.out_pop_count++;
return p;
}
size_t mailbox::in_size() const
{
return _in.size();
return _m.in_size();
}
size_t mailbox::out_size() const
{
return _out.size();
return _m.out_size();
}
const mailbox_stats& mailbox::stats() const
View
@@ -35,12 +35,13 @@
#include <memory>
#include "message/message.hpp"
#include "util/queue.hpp"
#include "util/mailbox.hpp"
namespace fire
{
namespace message
{
using queue = util::queue<message>;
struct mailbox_stats
@@ -61,7 +62,6 @@ namespace fire
public:
mailbox();
mailbox(const std::string&);
~mailbox();
public:
const std::string& address() const;
@@ -88,10 +88,7 @@ namespace fire
void done();
private:
std::string _address;
queue _in;
queue _out;
util::mailbox<message> _m;
mailbox_stats _stats;
};
View
@@ -0,0 +1,160 @@
/*
* Copyright (C) 2014 Maxim Noah Khailo
*
* 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 vedit_refsion 3 of the License, or
* (at your option) any later vedit_refsion.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* Botan library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU General Public License in all respects for
* all of the code used other than Botan. If you modify file(s) with
* this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version. If you delete
* this exception statement from all source files in the program, then
* also delete it here.
*/
#ifndef FIRESTR_UTIL_AUDIO_H
#define FIRESTR_UTIL_AUDIO_H
#include "util/audio.hpp"
#include "util/dbc.hpp"
#include "util/log.hpp"
namespace u = fire::util;
namespace fire
{
namespace util
{
const size_t FRAMES = 480; //40ms of PCM frames. Opus can handles 2.5, 5, 10, 20, 40 or 60ms of audio per frame.
const size_t MAX_FRAMES = 2*FRAMES;
const size_t MAX_OPUS_DECODE_SIZE = MAX_FRAMES * sizeof(opus_int16);
const size_t SAMPLE_RATE = 12000;
const size_t CHANNELS = 1;
const size_t MIN_BUF_SIZE = FRAMES * sizeof(opus_int16);
void log_opus_error(int e)
{
switch(e)
{
case OPUS_ALLOC_FAIL: LOG << "BAD ALLOC" << std::endl; break;
case OPUS_BAD_ARG: LOG << "BAD ARG" << std::endl; break;
case OPUS_BUFFER_TOO_SMALL: LOG << "TOO SMALL" << std::endl; break;
case OPUS_INTERNAL_ERROR: LOG << "INTERNAL ERR" << std::endl; break;
case OPUS_INVALID_PACKET: LOG << "INVALID PACKET" << std::endl; break;
case OPUS_INVALID_STATE: LOG << "INVALID STATE" << std::endl; break;
case OPUS_OK: LOG << "OK" << std::endl; break;
default: LOG << e << std::endl;
}
}
opus_encoder::opus_encoder()
{
int err;
_opus = opus_encoder_create(SAMPLE_RATE,CHANNELS, OPUS_APPLICATION_VOIP, &err);
if(err != OPUS_OK)
{
LOG << "opus encoder create error: ";
log_opus_error(err);
}
opus_encoder_ctl(_opus, OPUS_SET_BITRATE(OPUS_AUTO));
opus_encoder_ctl(_opus, OPUS_SET_VBR(1));
opus_encoder_ctl(_opus, OPUS_SET_FORCE_CHANNELS(1)); //force mono
opus_encoder_ctl(_opus, OPUS_SET_PACKET_LOSS_PERC(2));
ENSURE(_opus);
}
opus_encoder::~opus_encoder()
{
REQUIRE(_opus);
opus_encoder_destroy(_opus);
}
bytes opus_encoder::encode(const bytes& b)
{
REQUIRE(_opus);
REQUIRE_FALSE(b.empty());
if(b.size() != MIN_BUF_SIZE) return {};
u::bytes r;
r.resize(MIN_BUF_SIZE);
auto size = opus_encode(_opus,
reinterpret_cast<const opus_int16*>(b.data()),
FRAMES,
reinterpret_cast<unsigned char*>(r.data()),
r.size());
if(size < 0)
{
LOG << "opus encode error: "; log_opus_error(size);
return {};
}
r.resize(size);
return r;
}
opus_decoder::opus_decoder()
{
int err;
_opus = opus_decoder_create(SAMPLE_RATE, CHANNELS, &err);
if(err != OPUS_OK)
{
LOG << "opus decoder create error: "; log_opus_error(err);
}
ENSURE(_opus);
}
opus_decoder::~opus_decoder()
{
REQUIRE(_opus);
opus_decoder_destroy(_opus);
}
bytes opus_decoder::decode(const bytes& b)
{
REQUIRE(_opus);
u::bytes t;
t.resize(MAX_OPUS_DECODE_SIZE);
auto frames = opus_decode(
_opus,
reinterpret_cast<const unsigned char*>(b.data()),
b.size(),
reinterpret_cast<opus_int16*>(t.data()),
MAX_FRAMES,
0);
if(frames < 0)
{
LOG << "opus error decoding: "; log_opus_error(frames);
return {};
}
auto size = frames * sizeof(opus_int16);
t.resize(size);
return t;
}
}
}
#endif
View
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2014 Maxim Noah Khailo
*
* 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 vedit_refsion 3 of the License, or
* (at your option) any later vedit_refsion.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* Botan library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU General Public License in all respects for
* all of the code used other than Botan. If you modify file(s) with
* this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version. If you delete
* this exception statement from all source files in the program, then
* also delete it here.
*/
#ifndef FIRESTR_APP_QTW_AUDIO_H
#define FIRESTR_APP_QTW_AUDIO_H
#include "util/bytes.hpp"
#include <string>
#include <unordered_map>
#include <opus/opus.h>
namespace fire
{
namespace util
{
class opus_encoder
{
public:
opus_encoder();
~opus_encoder();
public:
bytes encode(const bytes&);
private:
OpusEncoder* _opus = nullptr;
};
using opus_encoder_ptr = std::shared_ptr<opus_encoder>;
class opus_decoder
{
public:
opus_decoder();
~opus_decoder();
public:
bytes decode(const bytes&);
private:
OpusDecoder* _opus = nullptr;
};
using opus_decoder_ptr = std::shared_ptr<opus_decoder>;
extern const size_t FRAMES; //40ms of PCM frames. Opus can handles 2.5, 5, 10, 20, 40 or 60ms of audio per frame.
extern const size_t MAX_FRAMES;
extern const size_t MAX_OPUS_DECODE_SIZE;
extern const size_t SAMPLE_RATE;
extern const size_t CHANNELS;
extern const size_t MIN_BUF_SIZE;
}
}
#endif