Compare commits

..

1 Commits

Author SHA1 Message Date
ee2971bf6a bench : multi-thread memcpy 2023-11-21 21:57:07 +02:00
35 changed files with 94 additions and 4034 deletions

View File

@ -73,5 +73,3 @@ else()
add_subdirectory(talk-llama)
add_subdirectory(lsp)
endif()
add_subdirectory(wchess)

View File

@ -1,9 +0,0 @@
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(libwchess)
if (EMSCRIPTEN)
add_subdirectory(wchess.wasm)
else()
add_subdirectory(wchess.cmd)
endif()

View File

@ -1,19 +0,0 @@
add_library(libwchess
WChess.cpp
WChess.h
Chessboard.cpp
Chessboard.h
)
target_link_libraries(libwchess
PUBLIC
whisper
common
)
target_include_directories(libwchess
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
)
add_executable(test-chessboard test-chessboard.cpp Chessboard.cpp)

View File

@ -1,714 +0,0 @@
#include "Chessboard.h"
#include <vector>
#include <algorithm>
#include <cstring>
#include <set>
namespace {
// remove std::string_view, c++17 -> c++11
constexpr std::array<const char*, 64> positions = {
"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
"a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
"a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
"a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8",
};
constexpr int INVALID_POS = positions.size();
constexpr int R = 0; // rank index
constexpr int F = 1; // file index
#define POS ((c[F] - '1') * 8 + (c[R] - 'a'))
constexpr int operator ""_P(const char * c, size_t size) {
return size < 2 || POS < 0 || POS > INVALID_POS ? INVALID_POS : POS;
}
#undef POS
struct sview {
const char * ptr = nullptr;
size_t size = 0;
sview() = default;
sview(const char * p, size_t s) : ptr(p), size(s) {}
sview(const std::string& s) : ptr(s.data()), size(s.size()) {}
size_t find(char del, size_t pos) {
while (pos < size && ptr[pos] != del) ++pos;
return pos < size ? pos : std::string::npos;
}
};
std::vector<sview> split(sview str, char del) {
std::vector<sview> res;
size_t cur = 0;
size_t last = 0;
while (cur != std::string::npos) {
if (str.ptr[last] == ' ') {
++last;
continue;
}
cur = str.find(del, last);
size_t len = cur == std::string::npos ? str.size - last : cur - last;
res.emplace_back(str.ptr + last, len);
last = cur + 1;
}
return res;
}
size_t strToPos(sview str) {
return operator ""_P(str.ptr, str.size);
}
constexpr std::array<const char*, 6> pieceNames = {
"pawn", "knight", "bishop", "rook", "queen", "king",
};
int strToType(sview str) {
auto it = std::find_if(pieceNames.begin(), pieceNames.end(), [str] (const char* name) { return strncmp(name, str.ptr, str.size) == 0; });
return it != pieceNames.end() ? int(it - pieceNames.begin()) : pieceNames.size();
}
}
Chessboard::Chessboard()
: blackPieces {{
{Piece::Pawn, Piece::Black, "a7"_P },
{Piece::Pawn, Piece::Black, "b7"_P },
{Piece::Pawn, Piece::Black, "c7"_P },
{Piece::Pawn, Piece::Black, "d7"_P },
{Piece::Pawn, Piece::Black, "e7"_P },
{Piece::Pawn, Piece::Black, "f7"_P },
{Piece::Pawn, Piece::Black, "g7"_P },
{Piece::Pawn, Piece::Black, "h7"_P },
{Piece::Rook, Piece::Black, "a8"_P },
{Piece::Knight, Piece::Black, "b8"_P },
{Piece::Bishop, Piece::Black, "c8"_P },
{Piece::Queen, Piece::Black, "d8"_P },
{Piece::King, Piece::Black, "e8"_P },
{Piece::Bishop, Piece::Black, "f8"_P },
{Piece::Knight, Piece::Black, "g8"_P },
{Piece::Rook, Piece::Black, "h8"_P },
}}
, whitePieces {{
{Piece::Pawn, Piece::White, "a2"_P },
{Piece::Pawn, Piece::White, "b2"_P },
{Piece::Pawn, Piece::White, "c2"_P },
{Piece::Pawn, Piece::White, "d2"_P },
{Piece::Pawn, Piece::White, "e2"_P },
{Piece::Pawn, Piece::White, "f2"_P },
{Piece::Pawn, Piece::White, "g2"_P },
{Piece::Pawn, Piece::White, "h2"_P },
{Piece::Rook, Piece::White, "a1"_P },
{Piece::Knight, Piece::White, "b1"_P },
{Piece::Bishop, Piece::White, "c1"_P },
{Piece::Queen, Piece::White, "d1"_P },
{Piece::King, Piece::White, "e1"_P },
{Piece::Bishop, Piece::White, "f1"_P },
{Piece::Knight, Piece::White, "g1"_P },
{Piece::Rook, Piece::White, "h1"_P },
}}
, board {{
&whitePieces[ 8], &whitePieces[ 9], &whitePieces[10], &whitePieces[11], &whitePieces[12], &whitePieces[13], &whitePieces[14], &whitePieces[15],
&whitePieces[ 0], &whitePieces[ 1], &whitePieces[ 2], &whitePieces[ 3], &whitePieces[ 4], &whitePieces[ 5], &whitePieces[ 6], &whitePieces[ 7],
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
&blackPieces[ 0], &blackPieces[ 1], &blackPieces[ 2], &blackPieces[ 3], &blackPieces[ 4], &blackPieces[ 5], &blackPieces[ 6], &blackPieces[ 7],
&blackPieces[ 8], &blackPieces[ 9], &blackPieces[10], &blackPieces[11], &blackPieces[12], &blackPieces[13], &blackPieces[14], &blackPieces[15],
}}
, whiteMoves {
{"b1"_P, "a3"_P}, {"b1"_P, "c3"_P},
{"g1"_P, "f3"_P}, {"g1"_P, "h3"_P},
{"a2"_P, "a3"_P}, {"a2"_P, "a4"_P},
{"b2"_P, "b3"_P}, {"b2"_P, "b4"_P},
{"c2"_P, "c3"_P}, {"c2"_P, "c4"_P},
{"d2"_P, "d3"_P}, {"d2"_P, "d4"_P},
{"e2"_P, "e3"_P}, {"e2"_P, "e4"_P},
{"f2"_P, "f3"_P}, {"f2"_P, "f4"_P},
{"g2"_P, "g3"_P}, {"g2"_P, "g4"_P},
{"h2"_P, "h3"_P}, {"h2"_P, "h4"_P},
}
, blackMoves {
{"a7"_P, "a5"_P}, {"a7"_P, "a6"_P},
{"b7"_P, "b5"_P}, {"b7"_P, "b6"_P},
{"c7"_P, "c5"_P}, {"c7"_P, "c6"_P},
{"d7"_P, "d5"_P}, {"d7"_P, "d6"_P},
{"e7"_P, "e5"_P}, {"e7"_P, "e6"_P},
{"f7"_P, "f5"_P}, {"f7"_P, "f6"_P},
{"g7"_P, "g5"_P}, {"g7"_P, "g6"_P},
{"h7"_P, "h5"_P}, {"h7"_P, "h6"_P},
{"b8"_P, "a6"_P}, {"b8"_P, "c6"_P},
{"g8"_P, "f6"_P}, {"g8"_P, "h6"_P},
}
{
static_assert(pieceNames.size() == Chessboard::Piece::Taken, "Mismatch between piece names and types");
std::sort(whiteMoves.begin(), whiteMoves.end());
std::sort(blackMoves.begin(), blackMoves.end());
}
std::string Chessboard::getRules(const std::string& prompt) const {
// leading space is very important!
std::string result =
"\n"
"# leading space is very important!\n"
"\n";
if (prompt.empty()) {
result += "move ::= \" \" ((piece | frompos) \" \" \"to \"?)? topos\n";
//result += "move ::= \" \" frompos \" \" \"to \"? topos\n";
}
else {
// result += "move ::= prompt \" \" ((piece | frompos) \" \" \"to \"?)? topos\n"
result += "move ::= prompt \" \" frompos \" \" \"to \"? topos\n"
"\n"
"prompt ::= \" " + prompt + "\"\n";
}
std::set<std::string> pieces;
std::set<std::string> from_pos;
std::set<std::string> to_pos;
auto& allowed_moves = m_moveCounter % 2 ? blackMoves : whiteMoves;
for (auto& m : allowed_moves) {
if (board[m.first]->type != Piece::Taken) pieces.insert(pieceNames[board[m.first]->type]);
from_pos.insert(positions[m.first]);
to_pos.insert(positions[m.second]);
}
if (!pieces.empty()) {
result += "piece ::= (";
for (auto& p : pieces) result += " \"" + p + "\" |";
result.pop_back();
result += ")\n\n";
}
if (!from_pos.empty()) {
result += "frompos ::= (";
for (auto& p : from_pos) result += " \"" + p + "\" |";
result.pop_back();
result += ")\n";
}
if (!to_pos.empty()) {
result += "topos ::= (";
for (auto& p : to_pos) result += " \"" + p + "\" |";
result.pop_back();
result += ")\n";
}
return result;
}
std::string Chessboard::stringifyBoard() {
static constexpr std::array<char, 6> blackShort = {
'p', 'n', 'b', 'r', 'q', 'k',
};
static constexpr std::array<char, 6> whiteShort = {
'P', 'N', 'B', 'R', 'Q', 'K',
};
std::string result;
result.reserve(16 + 2 * 64 + 16);
for (char rank = 'a'; rank <= 'h'; ++rank) {
result.push_back(rank);
result.push_back(' ');
}
result.back() = '\n';
for (int i = 7; i >= 0; --i) {
for (int j = 0; j < 8; ++j) {
auto p = board[i * 8 + j];
if (p) result.push_back(p->color == Piece::White ? whiteShort[p->type] : blackShort[p->type]);
else result.push_back((i + j) % 2 ? '.' : '*');
result.push_back(' ');
}
result.push_back('0' + i + 1);
result.push_back('\n');
}
return result;
}
std::string Chessboard::process(const std::string& command) {
auto color = Piece::Colors(m_moveCounter % 2);
fprintf(stdout, "%s: Command to %s: '%s%.*s%s'\n", __func__, (color ? "Black" : "White"), "\033[1m", int(command.size()), command.data(), "\033[0m");
if (command.empty()) return "";
auto tokens = split(command, ' ');
for (auto& t : tokens) fprintf(stdout, "%s: Token %.*s\n", __func__, int(t.size), t.ptr);
auto pos_from = INVALID_POS;
auto type = Piece::Types::Taken;
auto pos_to = INVALID_POS;
if (tokens.size() == 1) {
type = Piece::Types::Pawn;
pos_to = strToPos(tokens.front());
}
else {
pos_from = strToPos(tokens.front());
if (pos_from == INVALID_POS) type = Piece::Types(strToType(tokens.front()));
pos_to = strToPos(tokens.back());
}
if (pos_to == INVALID_POS) return "";
if (pos_from == INVALID_POS) {
if (type == Piece::Types::Taken) return "";
auto& pieces = color ? blackPieces : whitePieces;
auto pieceIndex = 0u;
for (; pieceIndex < pieces.size(); ++pieceIndex) {
if (pieces[pieceIndex].type == type && validateMove(pieces[pieceIndex], pos_to)) break;
}
if (pieceIndex == pieces.size()) return "";
pos_from = pieces[pieceIndex].pos;
}
if (board[pos_from] == nullptr) return "";
if (board[pos_from]->color != color) return "";
Move m = {pos_from, pos_to};
auto& allowed_moves = color ? blackMoves : whiteMoves;
fprintf(stdout, "%s:allowed size %d :\n", __func__, int(allowed_moves.size()));
for (auto& m : allowed_moves) fprintf(stdout, " %s %s; ", positions[m.first], positions[m.second]);
fprintf(stdout, "\n");
if (!std::binary_search(allowed_moves.begin(), allowed_moves.end(), m)) return "";
move(m);
{
auto it = std::remove_if(allowed_moves.begin(), allowed_moves.end(), [m] (const Move& move) { return move.first == m.first; });
allowed_moves.erase(it, allowed_moves.end());
}
std::vector<Piece*> affected = { board[m.second] };
for (auto& p : whitePieces) {
if (&p == board[m.second]
|| validateMove(p, m.first)
|| validateMove(p, m.second)
|| std::binary_search(whiteMoves.begin(), whiteMoves.end(), Move(p.pos, m.second))
) {
auto it = std::remove_if(whiteMoves.begin(), whiteMoves.end(), [&p] (const Move& m) { return m.first == p.pos; });
whiteMoves.erase(it, whiteMoves.end());
affected.push_back(&p);
}
}
for (auto& p : blackPieces) {
if (&p == board[m.second]
|| validateMove(p, m.first)
|| validateMove(p, m.second)
|| std::binary_search(blackMoves.begin(), blackMoves.end(), Move(p.pos, m.second))
) {
auto it = std::remove_if(blackMoves.begin(), blackMoves.end(), [&p] (const Move& m) { return m.first == p.pos; });
blackMoves.erase(it, blackMoves.end());
affected.push_back(&p);
}
}
for (auto& p : affected) getValidMoves(*p, p->color ? blackMoves : whiteMoves);
std::sort(blackMoves.begin(), blackMoves.end());
std::sort(whiteMoves.begin(), whiteMoves.end());
std::string result = positions[m.first];
result += "-";
result += positions[m.second];
++m_moveCounter;
fprintf(stdout, "%s: Move '%s%s%s'\n", __func__, "\033[1m", result.data(), "\033[0m");
return result;
}
void Chessboard::getValidMoves(const Piece& piece, std::vector<Move>& result) {
std::string cur = positions[piece.pos];
switch (piece.type) {
case Piece::Pawn: {
std::string next = cur;
piece.color ? --next[F] : ++next[F]; // one down / up
std::string left = { char(next[R] - 1), next[F]};
auto pos = strToPos(left);
if (pos != INVALID_POS && board[pos] && board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
std::string right = { char(next[R] + 1), next[F]};
pos = strToPos(right);
if (pos != INVALID_POS && board[pos] && board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
pos = strToPos(next);
if (pos != INVALID_POS && !board[pos]) result.emplace_back(piece.pos, pos);
else break;
if (piece.color ? cur[F] != '7' : cur[F] != '2') break;
piece.color ? --next[F] : ++next[F]; // one down / up
pos = strToPos(next);
if (pos != INVALID_POS && !board[pos]) result.emplace_back(piece.pos, pos);
break;
}
case Piece::Knight: {
std::string next = cur;
--next[F]; --next[F]; --next[R];
auto pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
--next[F]; --next[F]; ++next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[F]; ++next[F]; --next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[F]; ++next[F]; ++next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
--next[F]; --next[R]; --next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[F]; --next[R]; --next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
--next[F]; ++next[R]; ++next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[F]; ++next[R]; ++next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
break;
}
case Piece::Bishop: {
std::string next = cur;
while (true) {
--next[R]; --next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
--next[R]; ++next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[R]; --next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[R]; ++next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
break;
}
case Piece::Rook: {
std::string next = cur;
while (true) {
--next[R];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[R];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
--next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
break;
}
case Piece::Queen: {
std::string next = cur;
while (true) {
--next[R]; --next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
--next[R]; ++next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[R]; --next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[R]; ++next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
--next[R];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[R];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
--next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
next = cur;
while (true) {
++next[F];
auto pos = strToPos(next);
if (pos == INVALID_POS) break;
else if (board[pos]) {
if (board[pos]->color != piece.color) result.emplace_back(piece.pos, pos);
break;
}
result.emplace_back(piece.pos, pos);
}
break;
}
case Piece::King: {
std::string next = cur;
--next[R]; --next[F];
auto pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
--next[R]; ++next[F];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[R]; --next[F];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[R]; ++next[F];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
--next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[R];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
--next[F];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
next = cur;
++next[F];
pos = strToPos(next);
if (pos != INVALID_POS && !(board[pos] && board[pos]->color == piece.color)) result.emplace_back(piece.pos, pos);
break;
}
case Piece::Taken: break;
default: break;
}
}
bool Chessboard::validatePawnMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file) {
int direction = color == Piece::White ? 1 : -1;
bool two_ranks = color == Piece::White ? from_rank == 1 : from_rank == 6;
if (from_file == to_file) {
if (from_rank == to_rank - direction) return board[to_rank * 8 + to_file] == nullptr;
if (two_ranks && from_rank == to_rank - direction * 2) return board[(to_rank - direction) * 8 + to_file] == nullptr && board[to_rank * 8 + to_file] == nullptr;
}
else if (from_file + 1 == to_file || from_file - 1 == to_file) {
if (from_rank == to_rank - direction) return board[to_rank * 8 + to_file] != nullptr && board[to_rank * 8 + to_file]->color != color;
}
return false;
}
bool Chessboard::validateKnightMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file) {
int dr = std::abs(from_rank - to_rank);
int df = std::abs(from_file - to_file);
if ((dr == 2 && df == 1) || (dr == 1 && df == 2)) return board[to_rank * 8 + to_file] == nullptr || board[to_rank * 8 + to_file]->color != color;
return false;
}
bool Chessboard::validateBishopMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file) {
if (from_rank - from_file == to_rank - to_file) {
int direction = from_rank < to_rank ? 1 : -1;
from_rank += direction;
from_file += direction;
while (from_rank != to_rank) {
if (board[from_rank * 8 + from_file]) return false;
from_rank += direction;
from_file += direction;
}
return board[to_rank * 8 + to_file] == nullptr || board[to_rank * 8 + to_file]->color != color;
}
if (from_rank + from_file == to_rank + to_file) {
int direction = from_rank < to_rank ? 1 : -1;
from_rank += direction;
from_file -= direction;
while (from_rank != to_rank) {
if (board[from_rank * 8 + from_file]) return false;
from_rank += direction;
from_file -= direction;
}
return board[to_rank * 8 + to_file] == nullptr || board[to_rank * 8 + to_file]->color != color;
}
return false;
}
bool Chessboard::validateRookMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file) {
if (from_rank == to_rank) {
int direction = from_file < to_file ? 1 : -1;
from_file += direction;
while (from_file != to_file) {
if (board[from_rank * 8 + from_file]) return false;
from_file += direction;
}
return board[to_rank * 8 + to_file] == nullptr || board[to_rank * 8 + to_file]->color != color;
}
if (from_file == to_file) {
int direction = from_rank < to_rank ? 1 : -1;
from_rank += direction;
while (from_rank != to_rank) {
if (board[from_rank * 8 + from_file]) return false;
from_rank += direction;
}
return board[to_rank * 8 + to_file] == nullptr || board[to_rank * 8 + to_file]->color != color;
}
return false;
}
bool Chessboard::validateQueenMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file) {
if (validateBishopMove(color, from_rank, from_file, to_rank, to_file)) return true;
return validateRookMove(color, from_rank, from_file, to_rank, to_file);
}
bool Chessboard::validateKingMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file) {
if (std::abs(from_rank - to_rank) < 2 && std::abs(from_file - to_file) < 2) {
return board[to_rank * 8 + to_file] == nullptr || board[to_rank * 8 + to_file]->color != color;
}
return false;
}
bool Chessboard::validateMove(const Piece& piece, int pos) {
if (piece.type == Piece::Taken) return false;
if (piece.pos == pos) return false;
int i = piece.pos / 8;
int j = piece.pos - i * 8;
int ii = pos / 8;
int jj = pos - ii * 8;
switch (piece.type) {
case Piece::Pawn: return validatePawnMove(piece.color, i, j, ii, jj);
case Piece::Knight: return validateKnightMove(piece.color, i, j, ii, jj);
case Piece::Bishop: return validateBishopMove(piece.color, i, j, ii, jj);
case Piece::Rook: return validateRookMove(piece.color, i, j, ii, jj);
case Piece::Queen: return validateQueenMove(piece.color, i, j, ii, jj);
case Piece::King: return validateKingMove(piece.color, i, j, ii, jj);
default: break;
}
return false;
}
bool Chessboard::move(const Move& m) {
if (!board[m.first] || (board[m.second] && board[m.first]->color == board[m.second]->color)) return false;
if (board[m.second]) board[m.second]->type = Piece::Taken;
board[m.second] = board[m.first];
board[m.first] = nullptr;
board[m.second]->pos = m.second;
return true;
}

View File

@ -1,59 +0,0 @@
#pragma once
#include <string>
#include <array>
#include <vector>
class Chessboard {
public:
Chessboard();
std::string process(const std::string& t);
std::string stringifyBoard();
std::string getRules(const std::string & prompt) const;
using Move = std::pair<int, int>;
private:
bool move(const Move& move);
struct Piece {
enum Types {
Pawn,
Knight,
Bishop,
Rook,
Queen,
King,
Taken,
};
enum Colors {
White,
Black,
};
Types type;
Colors color;
int pos;
};
using PieceSet = std::array<Piece, 16>;
PieceSet blackPieces;
PieceSet whitePieces;
int m_moveCounter = 0;
using Board = std::array<Piece*, 64>;
Board board;
std::vector<Move> whiteMoves;
std::vector<Move> blackMoves;
bool validateMove(const Piece& piece, int pos);
void getValidMoves(const Piece& piece, std::vector<Move>& moves);
// just basic validation
// fixme: missing en passant, castling, promotion, etc.
bool validatePawnMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file);
bool validateKnightMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file);
bool validateBishopMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file);
bool validateRookMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file);
bool validateQueenMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file);
bool validateKingMove(Piece::Colors color, int from_rank, int from_file, int to_rank, int to_file);
};

View File

@ -1,220 +0,0 @@
#include "WChess.h"
#include "Chessboard.h"
#include "grammar-parser.h"
#include "common.h"
#include <thread>
WChess::WChess(whisper_context * ctx,
const whisper_full_params & wparams,
callbacks cb,
settings s)
: m_ctx(ctx)
, m_wparams(wparams)
, m_cb(cb)
, m_settings(s)
, m_board(new Chessboard())
{}
WChess::~WChess() = default;
void WChess::set_status(const std::string& msg) const {
if (m_cb.set_status) (*m_cb.set_status)(msg);
}
void WChess::set_moves(const std::string& moves) const {
if (m_cb.set_moves) (*m_cb.set_moves)(moves);
}
bool WChess::check_running() const {
if (m_cb.check_running) return (*m_cb.check_running)();
return false;
}
void WChess::clear_audio() const {
if (m_cb.clear_audio) (*m_cb.clear_audio)();
}
void WChess::get_audio(int ms, std::vector<float>& pcmf32) const {
if (m_cb.get_audio) (*m_cb.get_audio)(ms, pcmf32);
}
std::string WChess::stringify_board() const {
return m_board->stringifyBoard();
}
void WChess::run() {
set_status("loading data ...");
bool have_prompt = true;
bool ask_prompt = !have_prompt;
float logprob_min0 = 0.0f;
float logprob_min = 0.0f;
float logprob_sum0 = 0.0f;
float logprob_sum = 0.0f;
int n_tokens0 = 0;
int n_tokens = 0;
std::vector<float> pcmf32_cur;
std::vector<float> pcmf32_prompt;
const std::string k_prompt = have_prompt ? "" : "checkmate";
while (check_running()) {
// delay
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (ask_prompt) {
fprintf(stdout, "\n");
fprintf(stdout, "%s: Say the following phrase: '%s%s%s'\n", __func__, "\033[1m", k_prompt.c_str(), "\033[0m");
fprintf(stdout, "\n");
{
char txt[1024];
snprintf(txt, sizeof(txt), "Say the following phrase: '%s'", k_prompt.c_str());
set_status(txt);
}
ask_prompt = false;
}
int64_t t_ms = 0;
{
get_audio(m_settings.vad_ms, pcmf32_cur);
if (!pcmf32_cur.empty()) {
fprintf(stdout, "%s: Processing ...\n", __func__);
set_status("Processing ...");
if (!have_prompt) {
const auto txt = ::trim(transcribe(pcmf32_cur, logprob_min, logprob_sum, n_tokens, t_ms));
fprintf(stdout, "%s: Heard '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", txt.c_str(), "\033[0m", (int) t_ms);
const float sim = similarity(txt, k_prompt);
if (txt.length() < 0.8*k_prompt.length() || txt.length() > 1.2*k_prompt.length() || sim < 0.8f) {
fprintf(stdout, "%s: WARNING: prompt not recognized, try again\n", __func__);
ask_prompt = true;
} else {
fprintf(stdout, "\n");
fprintf(stdout, "%s: The prompt has been recognized!\n", __func__);
fprintf(stdout, "%s: Waiting for voice commands ...\n", __func__);
fprintf(stdout, "\n");
{
char txt[1024];
snprintf(txt, sizeof(txt), "Success! Waiting for voice commands ...");
set_status(txt);
}
// save the audio for the prompt
pcmf32_prompt = pcmf32_cur;
have_prompt = true;
}
} else {
if (!pcmf32_prompt.empty()) pcmf32_cur.insert(pcmf32_cur.begin(), pcmf32_prompt.begin(), pcmf32_prompt.end());
static const size_t MIN_SIZE = 1.2 * WHISPER_SAMPLE_RATE;
if (MIN_SIZE > pcmf32_cur.size()) pcmf32_cur.insert(pcmf32_cur.begin(), MIN_SIZE - pcmf32_cur.size(), 0.0f);
std::string rules = m_board->getRules(k_prompt);
fprintf(stdout, "%s: grammar rules:\n'%s'\n", __func__, rules.c_str());
auto grammar_parsed = grammar_parser::parse(rules.c_str());
auto grammar_rules = grammar_parsed.c_rules();
m_wparams.grammar_rules = grammar_rules.data();
m_wparams.n_grammar_rules = grammar_rules.size();
m_wparams.i_start_rule = grammar_parsed.symbol_ids.at("move");
auto txt = ::trim(transcribe(pcmf32_cur, logprob_min, logprob_sum, n_tokens, t_ms));
const float p = 100.0f * std::exp(logprob_min);
fprintf(stdout, "%s: heard '%s'\n", __func__, txt.c_str());
// find the prompt in the text
float best_sim = 0.0f;
size_t best_len = 0;
for (int n = 0.8*k_prompt.size(); n <= 1.2*k_prompt.size(); ++n) {
const auto prompt = txt.substr(0, n);
const float sim = similarity(prompt, k_prompt);
//fprintf(stderr, "%s: prompt = '%s', sim = %f\n", __func__, prompt.c_str(), sim);
if (sim > best_sim) {
best_sim = sim;
best_len = n;
}
}
fprintf(stdout, "%s: DEBUG: txt = '%s', prob = %.2f%%\n", __func__, txt.c_str(), p);
std::string command = ::trim(txt.substr(best_len));
fprintf(stdout, "%s: Command '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", command.c_str(), "\033[0m", (int) t_ms);
fprintf(stdout, "\n");
{
char txt[1024];
snprintf(txt, sizeof(txt), "Command '%s', (t = %d ms)", command.c_str(), (int) t_ms);
set_status(txt);
}
if (!command.empty()) {
auto move = m_board->process(command);
if (!move.empty()) {
set_moves(std::move(move));
}
}
}
clear_audio();
}
}
}
}
std::string WChess::transcribe(
const std::vector<float> & pcmf32,
float & logprob_min,
float & logprob_sum,
int & n_tokens,
int64_t & t_ms) {
const auto t_start = std::chrono::high_resolution_clock::now();
logprob_min = 0.0f;
logprob_sum = 0.0f;
n_tokens = 0;
t_ms = 0;
if (whisper_full(m_ctx, m_wparams, pcmf32.data(), pcmf32.size()) != 0) {
return {};
}
std::string result;
const int n_segments = whisper_full_n_segments(m_ctx);
for (int i = 0; i < n_segments; ++i) {
const char * text = whisper_full_get_segment_text(m_ctx, i);
result += text;
const int n = whisper_full_n_tokens(m_ctx, i);
for (int j = 0; j < n; ++j) {
const auto token = whisper_full_get_token_data(m_ctx, i, j);
if(token.plog > 0.0f) return {};
logprob_min = std::min(logprob_min, token.plog);
logprob_sum += token.plog;
++n_tokens;
}
}
const auto t_end = std::chrono::high_resolution_clock::now();
t_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t_end - t_start).count();
return result;
}

View File

@ -1,62 +0,0 @@
#pragma once
#include "whisper.h"
#include <string>
#include <vector>
#include <memory>
class Chessboard;
class WChess {
public:
using SetStatusCb = void (*)(const std::string &);
using CheckRunningCb = bool (*)();
using GetAudioCb = void (*)(int, std::vector<float> &);
using SetMovesCb = void (*)(const std::string &);
using ClearAudioCb = void (*)();
struct callbacks {
SetStatusCb set_status = nullptr;
CheckRunningCb check_running = nullptr;
GetAudioCb get_audio = nullptr;
SetMovesCb set_moves = nullptr;
ClearAudioCb clear_audio = nullptr;
};
struct settings {
int32_t vad_ms = 2000;
int32_t prompt_ms = 5000;
int32_t command_ms = 4000;
float vad_thold = 0.2f;
float freq_thold = 100.0f;
bool print_energy = false;
};
WChess(
whisper_context * ctx,
const whisper_full_params & wparams,
callbacks cb,
settings s
);
~WChess();
void run();
std::string stringify_board() const;
private:
void get_audio(int ms, std::vector<float>& pcmf32) const;
void set_status(const std::string& msg) const;
void set_moves(const std::string& moves) const;
bool check_running() const;
void clear_audio() const;
std::string transcribe(
const std::vector<float> & pcmf32,
float & logprob_min,
float & logprob_sum,
int & n_tokens,
int64_t & t_ms);
whisper_context * m_ctx;
whisper_full_params m_wparams;
const callbacks m_cb;
const settings m_settings;
std::unique_ptr<Chessboard> m_board;
};

View File

@ -1,88 +0,0 @@
#include "Chessboard.h"
#define ASSERT(x) \
do { \
if (!(x)) { \
fprintf(stderr, "ASSERT: %s:%d: %s\n", __FILE__, __LINE__, #x); \
fflush(stderr); \
exit(1); \
} \
} while (0)
int main() {
{
// pawns
Chessboard chess;
ASSERT(chess.process("pawn to d4, e5, e3, pawn to d5") == "d2-d4 e7-e5 e2-e3 d7-d5");
ASSERT(chess.process("pawn to d4") == ""); // wrong
ASSERT(chess.process("pawn to c5") == ""); // wrong
ASSERT(chess.process("pawn to d5") == ""); // wrong
ASSERT(chess.process("pawn to d3") == ""); // wrong
ASSERT(chess.process("pawn to f5") == ""); // wrong, white's turn
ASSERT(chess.process("h4") == "h2-h4");
ASSERT(chess.process("d4") == "e5-d4");
ASSERT(chess.process("e4") == "e3-e4");
ASSERT(chess.process("d4") == ""); // wrong
ASSERT(chess.process("e4") == "d5-e4");
}
{
// rook
Chessboard chess;
ASSERT(chess.process("rook to a3") == ""); // wrong
ASSERT(chess.process("a4, h5, rook to a3, rook to h6") == "a2-a4 h7-h5 a1-a3 h8-h6");
ASSERT(chess.process("rook to d3, rook to e6") == "a3-d3 h6-e6");
ASSERT(chess.process("rook to d4, rook to e5") == "d3-d4 e6-e5");
ASSERT(chess.process("rook to a4") == ""); // wrong
ASSERT(chess.process("rook to d8") == ""); // wrong
ASSERT(chess.process("rook to d3") == "d4-d3");
ASSERT(chess.process("rook to e2") == "e5-e2");
}
{
// knight
Chessboard chess;
ASSERT(chess.process("knight to c3, knight to c6") == "b1-c3 b8-c6");
ASSERT(chess.process("knight to c3") == ""); // wrong
ASSERT(chess.process("knight to a2") == ""); // wrong
ASSERT(chess.process("knight to b4") == ""); // wrong, white's turn
ASSERT(chess.process("knight to b5") == "c3-b5");
ASSERT(chess.process("knight to a5") == "c6-a5");
ASSERT(chess.process("knight to c7") == "b5-c7");
}
{
// bishop
Chessboard chess;
ASSERT(chess.process("b3, b6, bishop to b2, bishop to b7") == "b2-b3 b7-b6 c1-b2 c8-b7");
ASSERT(chess.process("bishop to a1") == ""); // wrong
ASSERT(chess.process("bishop to h8") == ""); // wrong
ASSERT(chess.process("bishop to a6") == ""); // wrong, white's turn
ASSERT(chess.process("bishop to g7") == "b2-g7");
}
{
// queen
Chessboard chess;
ASSERT(chess.process("queen to d8") == ""); // wrong
ASSERT(chess.process("queen to f1") == ""); // wrong
ASSERT(chess.process("queen to h5") == ""); // wrong
ASSERT(chess.process("e3, d5, queen to h5, queen to d6") == "e2-e3 d7-d5 d1-h5 d8-d6");
ASSERT(chess.process("queen to c5") == ""); // wrong, white's turn
ASSERT(chess.process("queen to f7") == "h5-f7");
}
{
// king
Chessboard chess;
ASSERT(chess.process("d3, d6, king to d2, king to d7, king to c3, king to c6, king to c4") == "d2-d3 d7-d6 e1-d2 e8-d7 d2-c3 d7-c6 c3-c4");
ASSERT(chess.process("bishop to e6") == "c8-e6");
ASSERT(chess.process("king to b3") == "c4-b3"); // !! check check not implemented
}
}

View File

@ -1,8 +0,0 @@
if (WHISPER_SDL2)
set(TARGET wchess)
add_executable(${TARGET} wchess.cmd.cpp)
include(DefaultTargetOptions)
target_link_libraries(${TARGET} PRIVATE libwchess common-sdl ${CMAKE_THREAD_LIBS_INIT})
endif ()

View File

@ -1,207 +0,0 @@
// Command line voice assisted chess
//
// Speak chess move commands to the microphone.
// The moves will translated to chessboard positions.
//
//
#include "WChess.h"
#include "common-sdl.h"
#include <memory>
#include <thread>
// command-line parameters
struct whisper_params {
int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
int32_t prompt_ms = 5000;
int32_t command_ms = 8000;
int32_t capture_id = -1;
int32_t max_tokens = 32;
int32_t audio_ctx = 0;
float vad_thold = 0.6f;
float freq_thold = 100.0f;
float grammar_penalty = 100.0f;
bool speed_up = false;
bool translate = false;
bool print_special = false;
bool print_energy = false;
bool no_timestamps = true;
bool use_gpu = true;
std::string language = "en";
std::string model = "models/ggml-base.en.bin";
std::string fname_out;
std::string commands;
std::string prompt;
std::string context;
std::string grammar;
};
void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & params) {
fprintf(stderr, "\n");
fprintf(stderr, "usage: %s [options]\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "options:\n");
fprintf(stderr, " -h, --help [default] show this help message and exit\n");
fprintf(stderr, " -t N, --threads N [%-7d] number of threads to use during computation\n", params.n_threads);
fprintf(stderr, " -pms N, --prompt-ms N [%-7d] prompt duration in milliseconds\n", params.prompt_ms);
fprintf(stderr, " -cms N, --command-ms N [%-7d] command duration in milliseconds\n", params.command_ms);
fprintf(stderr, " -c ID, --capture ID [%-7d] capture device ID\n", params.capture_id);
fprintf(stderr, " -mt N, --max-tokens N [%-7d] maximum number of tokens per audio chunk\n", params.max_tokens);
fprintf(stderr, " -ac N, --audio-ctx N [%-7d] audio context size (0 - all)\n", params.audio_ctx);
fprintf(stderr, " -vth N, --vad-thold N [%-7.2f] voice activity detection threshold\n", params.vad_thold);
fprintf(stderr, " -fth N, --freq-thold N [%-7.2f] high-pass frequency cutoff\n", params.freq_thold);
fprintf(stderr, " -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n", params.speed_up ? "true" : "false");
fprintf(stderr, " -tr, --translate [%-7s] translate from source language to english\n", params.translate ? "true" : "false");
fprintf(stderr, " -ps, --print-special [%-7s] print special tokens\n", params.print_special ? "true" : "false");
fprintf(stderr, " -pe, --print-energy [%-7s] print sound energy (for debugging)\n", params.print_energy ? "true" : "false");
fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU\n", params.use_gpu ? "false" : "true");
fprintf(stderr, " -l LANG, --language LANG [%-7s] spoken language\n", params.language.c_str());
fprintf(stderr, " -m FNAME, --model FNAME [%-7s] model path\n", params.model.c_str());
fprintf(stderr, " -f FNAME, --file FNAME [%-7s] text output file name\n", params.fname_out.c_str());
fprintf(stderr, " -cmd FNAME, --commands FNAME [%-7s] text file with allowed commands\n", params.commands.c_str());
fprintf(stderr, " -p, --prompt [%-7s] the required activation prompt\n", params.prompt.c_str());
fprintf(stderr, " -ctx, --context [%-7s] sample text to help the transcription\n", params.context.c_str());
fprintf(stderr, " --grammar-penalty N [%-7.1f] scales down logits of nongrammar tokens\n", params.grammar_penalty);
fprintf(stderr, "\n");
}
bool whisper_params_parse(int argc, char ** argv, whisper_params & params) {
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
if (arg == "-h" || arg == "--help") {
whisper_print_usage(argc, argv, params);
exit(0);
}
else if (arg == "-t" || arg == "--threads") { params.n_threads = std::stoi(argv[++i]); }
else if (arg == "-pms" || arg == "--prompt-ms") { params.prompt_ms = std::stoi(argv[++i]); }
else if (arg == "-cms" || arg == "--command-ms") { params.command_ms = std::stoi(argv[++i]); }
else if (arg == "-c" || arg == "--capture") { params.capture_id = std::stoi(argv[++i]); }
else if (arg == "-mt" || arg == "--max-tokens") { params.max_tokens = std::stoi(argv[++i]); }
else if (arg == "-ac" || arg == "--audio-ctx") { params.audio_ctx = std::stoi(argv[++i]); }
else if (arg == "-vth" || arg == "--vad-thold") { params.vad_thold = std::stof(argv[++i]); }
else if (arg == "-fth" || arg == "--freq-thold") { params.freq_thold = std::stof(argv[++i]); }
else if (arg == "-su" || arg == "--speed-up") { params.speed_up = true; }
else if (arg == "-tr" || arg == "--translate") { params.translate = true; }
else if (arg == "-ps" || arg == "--print-special") { params.print_special = true; }
else if (arg == "-pe" || arg == "--print-energy") { params.print_energy = true; }
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
else if (arg == "-l" || arg == "--language") { params.language = argv[++i]; }
else if (arg == "-m" || arg == "--model") { params.model = argv[++i]; }
else if (arg == "-f" || arg == "--file") { params.fname_out = argv[++i]; }
else if (arg == "-cmd" || arg == "--commands") { params.commands = argv[++i]; }
else if (arg == "-p" || arg == "--prompt") { params.prompt = argv[++i]; }
else if (arg == "-ctx" || arg == "--context") { params.context = argv[++i]; }
else if ( arg == "--grammar-penalty") { params.grammar_penalty = std::stof(argv[++i]); }
else {
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
whisper_print_usage(argc, argv, params);
exit(0);
}
}
return true;
}
std::unique_ptr<WChess> g_wchess;
void set_moves(const std::string & moves) {
if (!moves.empty()) fprintf(stdout, "%s", g_wchess->stringify_board().c_str());
}
audio_async g_audio(30*1000);
void get_audio(int ms, std::vector<float> & pcmf32_cur) {
g_audio.get(ms, pcmf32_cur);
}
void clear_audio() {
g_audio.clear();
}
int main(int argc, char ** argv) {
whisper_params params;
if (whisper_params_parse(argc, argv, params) == false) {
return 1;
}
if (whisper_lang_id(params.language.c_str()) == -1) {
fprintf(stderr, "error: unknown language '%s'\n", params.language.c_str());
whisper_print_usage(argc, argv, params);
exit(0);
}
// whisper init
struct whisper_context_params cparams;
cparams.use_gpu = params.use_gpu;
struct whisper_context * ctx = whisper_init_from_file_with_params(params.model.c_str(), cparams);
// init audio
if (!g_audio.init(params.capture_id, WHISPER_SAMPLE_RATE)) {
fprintf(stderr, "%s: audio.init() failed!\n", __func__);
return 1;
}
whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_BEAM_SEARCH);
wparams.print_progress = false;
wparams.print_special = params.print_special;
wparams.print_realtime = false;
wparams.print_timestamps = !params.no_timestamps;
wparams.translate = params.translate;
wparams.no_context = true;
wparams.no_timestamps = params.no_timestamps;
wparams.single_segment = true;
wparams.max_tokens = params.max_tokens;
wparams.language = params.language.c_str();
wparams.n_threads = params.n_threads;
wparams.audio_ctx = params.audio_ctx;
wparams.speed_up = params.speed_up;
wparams.temperature = 0.4f;
wparams.temperature_inc = 1.0f;
wparams.greedy.best_of = 5;
wparams.beam_search.beam_size = 5;
wparams.initial_prompt = params.context.data();
g_audio.resume();
// wait for 1 second to avoid any buffered noise
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
g_audio.clear();
WChess::callbacks cb;
cb.check_running = sdl_poll_events;
cb.get_audio = get_audio;
cb.set_moves = set_moves;
cb.clear_audio = clear_audio;
WChess::settings s;
s.vad_ms = 2000;
s.prompt_ms = params.prompt_ms;
s.command_ms = params.command_ms;
s.vad_thold = params.vad_thold;
s.freq_thold = params.freq_thold;
s.print_energy = params.print_energy;
g_wchess.reset(new WChess(ctx, wparams, cb, s));
set_moves("start");
g_wchess->run();
g_audio.pause();
whisper_print_timings(ctx);
whisper_free(ctx);
return 0;
}

View File

@ -1,51 +0,0 @@
set(TARGET wchess.wasm)
add_executable(${TARGET}
wchess.wasm.cpp
)
include(DefaultTargetOptions)
target_link_libraries(${TARGET} PRIVATE
common
libwchess
)
unset(EXTRA_FLAGS)
if (WHISPER_WASM_SINGLE_FILE)
set(EXTRA_FLAGS "-s SINGLE_FILE=1")
message(STATUS "Embedding WASM inside chess.js")
add_custom_command(
TARGET ${TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_BINARY_DIR}/bin/${TARGET}.js
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/js/chess.js
)
endif()
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
--bind \
-s USE_PTHREADS=1 \
-s PTHREAD_POOL_SIZE=8 \
-s INITIAL_MEMORY=1024MB \
-s TOTAL_MEMORY=1024MB \
-s FORCE_FILESYSTEM=1 \
-s EXPORTED_RUNTIME_METHODS=\"['print', 'printErr', 'ccall', 'cwrap']\" \
${EXTRA_FLAGS} \
")
add_custom_command(
TARGET ${TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/chessboardjs-1.0.0
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/jquery-3.7.1.min.js
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/js/
)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/examples/helpers.js ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/js/helpers.js @ONLY)

View File

@ -1,32 +0,0 @@
# chessboard.js Change Log
All notable changes to this project will be documented in this file.
## [1.0.0] - 2019-06-11
- Orientation methods now return current orientation. [Issue #64]
- Drop support for IE8
- Do not check for `window.JSON` (Error #1004)
- Rename `ChessBoard` to `Chessboard` (`ChessBoard` is still supported, however)
- id query selectors are now supported as the first argument to `Chessboard()`
- Remove Error #1002
- Format code according to [StandardJS]
- Bump minimum jQuery version to 1.8.3
- Throttle piece drag functions
## [0.3.0] - 2013-08-10
- Added `appearSpeed` animation config property
- Added `onSnapbackEnd` event
- Added `onMoveEnd` event
## [0.2.0] - 2013-08-05
- Added `onMouseoverSquare` and `onMouseoutSquare` events
- Added `onSnapEnd` event
- Added square code as CSS class on the squares
- Added [chess.js] integration examples
## [0.1.0] - 2013-05-21
- Initial release
[chess.js]:https://github.com/jhlywa/chess.js
[Issue #64]:https://github.com/oakmac/chessboardjs/issues/64
[StandardJS]:https://standardjs.com/

View File

@ -1,20 +0,0 @@
Copyright 2019 Chris Oakman
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,82 +0,0 @@
# chessboard.js
chessboard.js is a JavaScript chessboard component. It depends on [jQuery].
Please see [chessboardjs.com] for documentation and examples.
## What is chessboard.js?
chessboard.js is a JavaScript chessboard component with a flexible "just a
board" API that
chessboard.js is a standalone JavaScript Chess Board. It is designed to be "just
a board" and expose a powerful API so that it can be used in different ways.
Here's a non-exhaustive list of things you can do with chessboard.js:
- Use chessboard.js to show game positions alongside your expert commentary.
- Use chessboard.js to have a tactics website where users have to guess the best
move.
- Integrate chessboard.js and [chess.js] with a PGN database and allow people to
search and playback games (see [Example 5000])
- Build a chess server and have users play their games out using the
chessboard.js board.
chessboard.js is flexible enough to handle any of these situations with relative
ease.
## What can chessboard.js **not** do?
The scope of chessboard.js is limited to "just a board." This is intentional and
makes chessboard.js flexible for handling a multitude of chess-related problems.
This is a common source of confusion for new users. [remove?]
Specifically, chessboard.js does not understand anything about how the game of
chess is played: how a knight moves, who's turn is it, is White in check?, etc.
Fortunately, the powerful [chess.js] library deals with exactly this sort of
problem domain and plays nicely with chessboard.js's flexible API. Some examples
of chessboard.js combined with chess.js: 5000, 5001, 5002
Please see the powerful [chess.js] library for an API to deal with these sorts
of questions.
This logic is distinct from the logic of the board. Please see the powerful
[chess.js] library for this aspect of your application.
Here is a list of things that chessboard.js is **not**:
- A chess engine
- A legal move validator
- A PGN parser
chessboard.js is designed to work well with any of those things, but the idea
behind chessboard.js is that the logic that controls the board should be
independent of those other problems.
## Docs and Examples
- Docs - <http://chessboardjs.com/docs>
- Examples - <http://chessboardjs.com/examples>
## Developer Tools
```sh
# create a build in the build/ directory
npm run build
# re-build the website
npm run website
```
## License
[MIT License](LICENSE.md)
[jQuery]:https://jquery.com/
[chessboardjs.com]:http://chessboardjs.com
[chess.js]:https://github.com/jhlywa/chess.js
[Example 5000]:http://chessboardjs.com/examples#5000

View File

@ -1,54 +0,0 @@
/*! chessboard.js v1.0.0 | (c) 2019 Chris Oakman | MIT License chessboardjs.com/license */
.clearfix-7da63 {
clear: both;
}
.board-b72b1 {
border: 2px solid #404040;
box-sizing: content-box;
}
.square-55d63 {
float: left;
position: relative;
/* disable any native browser highlighting */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.white-1e1d7 {
background-color: #f0d9b5;
color: #b58863;
}
.black-3c85d {
background-color: #b58863;
color: #f0d9b5;
}
.highlight1-32417, .highlight2-9c5d2 {
box-shadow: inset 0 0 3px 3px yellow;
}
.notation-322f9 {
cursor: default;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
position: absolute;
}
.alpha-d2270 {
bottom: 1px;
right: 3px;
}
.numeric-fc462 {
top: 2px;
left: 2px;
}

View File

@ -1,2 +0,0 @@
/*! chessboard.js v1.0.0 | (c) 2019 Chris Oakman | MIT License chessboardjs.com/license */
.clearfix-7da63{clear:both}.board-b72b1{border:2px solid #404040;box-sizing:content-box}.square-55d63{float:left;position:relative;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.white-1e1d7{background-color:#f0d9b5;color:#b58863}.black-3c85d{background-color:#b58863;color:#f0d9b5}.highlight1-32417,.highlight2-9c5d2{box-shadow:inset 0 0 3px 3px #ff0}.notation-322f9{cursor:default;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;position:absolute}.alpha-d2270{bottom:1px;right:3px}.numeric-fc462{top:2px;left:2px}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,29 +0,0 @@
{
"author": "Chris Oakman <chris@oakmac.com> (http://chrisoakman.com/)",
"name": "@chrisoakman/chessboardjs",
"description": "JavaScript chessboard widget",
"homepage": "https://chessboardjs.com",
"license": "MIT",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "git://github.com/oakmac/chessboardjs.git"
},
"files": ["dist/"],
"dependencies": {
"jquery": ">=3.4.1"
},
"devDependencies": {
"csso": "3.5.1",
"fs-plus": "3.1.1",
"kidif": "1.1.0",
"mustache": "2.3.0",
"standard": "10.0.2",
"uglify-js": "3.6.0"
},
"scripts": {
"build": "standard lib/chessboard.js && node scripts/build.js",
"standard": "standard --fix lib/*.js website/js/*.js",
"website": "node scripts/website.js"
}
}

View File

@ -1,376 +0,0 @@
<!doctype html>
<html lang="en-us">
<head>
<title>wchess : Voice assistant example using Whisper + WebAssembly</title>
<style>
#output {
width: 100%;
height: 100%;
margin: 0 auto;
margin-top: 10px;
border-left: 0px;
border-right: 0px;
padding-left: 0px;
padding-right: 0px;
display: block;
background-color: black;
color: white;
font-size: 10px;
font-family: 'Lucida Console', Monaco, monospace;
outline: none;
white-space: pre;
overflow-wrap: normal;
overflow-x: scroll;
}
</style>
<link rel="stylesheet" href="css/chessboard-1.0.0.min.css" integrity="sha384-q94+BZtLrkL1/ohfjR8c6L+A6qzNH9R2hBLwyoAfu3i/WCvQjzL2RQJ3uNHDISdU" crossorigin="anonymous">
</head>
<body onload="loadWhisper()">
<div id="main-container">
<b>wchess : Voice assistant example using Whisper + WebAssembly</b>
<br><br>
You can find more about this project on <a href="https://github.com/ggerganov/whisper.cpp/tree/master/examples/command.wasm">GitHub</a>.
<br><br>
<b>More examples:</b>
<a href="https://whisper.ggerganov.com/">main</a> |
<a href="https://whisper.ggerganov.com/bench">bench</a> |
<a href="https://whisper.ggerganov.com/stream">stream</a> |
<a href="https://whisper.ggerganov.com/command">command</a> |
<a href="https://whisper.ggerganov.com/talk">talk</a> |
<br><br>
<hr>
<div id="model-whisper">
Whisper model: <span id="model-whisper-status"></span>
<span id="fetch-whisper-progress"></span>
<button id="clear" onclick="clearCache()">Clear Cache</button>
<!--
<input type="file" id="file" name="file" onchange="loadFile(event, 'whisper.bin')" />
-->
</div>
<br>
<div id="myBoard" style="width: 400px"></div>
<script src="js/jquery-3.7.1.min.js"></script>
<script src="js/chessboard-1.0.0.min.js"></script>
<script>
var board = Chessboard('myBoard', 'start')
</script>
<br>
<div id="input">
<button id="toggler" disabled>Hold</button>
</div>
<br>
<div id="state">
Status: <b><span id="state-status">not started</span></b>
<pre id="state-moves">[The moves will be displayed here]</pre>
</div>
<hr>
Debug output:
<textarea id="output" rows="20"></textarea>
<br>
<b>Troubleshooting</b>
<br><br>
The page does some heavy computations, so make sure:
<ul>
<li>To use a modern web browser (e.g. Chrome, Firefox)</li>
<li>To use a fast desktop or laptop computer (i.e. not a mobile phone)</li>
<li>Your browser supports WASM <a href="https://webassembly.org/roadmap/">Fixed-width SIMD</a></li>
</ul>
<div class="cell-version">
<span>
|
Build time: <span class="nav-link">@GIT_DATE@</span> |
Commit hash: <a class="nav-link" href="https://github.com/ggerganov/whisper.cpp/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
<a class="nav-link" href="https://github.com/ggerganov/whisper.cpp/tree/master/examples/command.wasm">Source Code</a> |
</span>
</div>
</div>
<script type="text/javascript" src="js/helpers.js"></script>
<script type='text/javascript'>
// web audio context
var context = null;
// the command instance
var instance = null;
// model name
var model_whisper = null;
var Module = {
print: printTextarea,
printErr: printTextarea,
setStatus: function(text) {
printTextarea('js: ' + text);
},
monitorRunDependencies: function(left) {
},
preRun: function() {
printTextarea('js: Preparing ...');
},
postRun: function() {
printTextarea('js: Module initialized successfully!');
instance = Module.init('whisper.bin');
if (instance) {
printTextarea("js: whisper initialized, instance: " + instance);
}
else {
printTextarea("js: failed to initialize whisper");
}
}
};
//
// fetch models
//
let dbVersion = 1
let dbName = 'whisper.ggerganov.com';
let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB
function storeFS(fname, buf) {
// write to WASM file using FS_createDataFile
// if the file exists, delete it
try {
Module.FS_unlink(fname);
} catch (e) {
// ignore
}
Module.FS_createDataFile("/", fname, buf, true, true);
printTextarea('storeFS: stored model: ' + fname + ' size: ' + buf.length);
document.getElementById('model-whisper-status').innerHTML = 'loaded "' + model_whisper + '"!';
if (model_whisper != null) {
document.getElementById('toggler').disabled = false;
}
}
function loadWhisper() {
// let urls = {
// 'tiny.en': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en.bin',
// 'base.en': 'https://whisper.ggerganov.com/ggml-model-whisper-base.en.bin',
// 'tiny-en-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en-q5_1.bin',
// 'base-en-q5_1': 'https://whisper.ggerganov.com/ggml-model-whisper-base.en-q5_1.bin',
// };
// let sizes = {
// 'tiny.en': 75,
// 'base.en': 142,
// 'tiny-en-q5_1': 31,
// 'base-en-q5_1': 57,
// };
let url = 'https://whisper.ggerganov.com/ggml-model-whisper-tiny.en-q8_0.bin';
let dst = 'whisper.bin';
let size_mb = 75;
model_whisper = 'tiny.en';
document.getElementById('model-whisper-status').innerHTML = 'loading "' + model_whisper + '" ... ';
cbProgress = function(p) {
let el = document.getElementById('fetch-whisper-progress');
el.innerHTML = Math.round(100*p) + '%';
};
cbCancel = function() {
var el;
el = document.getElementById('model-whisper-status'); if (el) el.innerHTML = '';
};
loadRemote(url, dst, size_mb, cbProgress, storeFS, cbCancel, printTextarea);
}
//
// microphone
//
const kSampleRate = 16000;
const kRestartRecording_s = 120;
const kIntervalAudio_ms = 250; // pass the recorded audio to the C++ instance at this rate
var mediaRecorder = null;
var doRecording = false;
var startTime = 0;
window.AudioContext = window.AudioContext || window.webkitAudioContext;
window.OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
function stopRecording() {
Module.set_status("paused");
mediaRecorder.stop();
}
function startRecording() {
if (!context) {
context = new AudioContext({
sampleRate: kSampleRate,
channelCount: 1,
echoCancellation: false,
autoGainControl: true,
noiseSuppression: true,
});
}
startTime = Date.now();
var chunks = [];
var stream = null;
navigator.mediaDevices.getUserMedia({audio: true, video: false})
.then(function(s) {
stream = s;
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data);
var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
var reader = new FileReader();
reader.onload = function(event) {
var buf = new Uint8Array(reader.result);
if (!context) {
return;
}
context.decodeAudioData(buf.buffer, function(audioBuffer) {
var offlineContext = new OfflineAudioContext(audioBuffer.numberOfChannels, audioBuffer.length, audioBuffer.sampleRate);
var source = offlineContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(offlineContext.destination);
source.start(0);
offlineContext.startRendering().then(function(renderedBuffer) {
let audio = renderedBuffer.getChannelData(0);
if (instance) {
printTextarea('js: number of samples: ' + audio.length);
Module.set_audio(instance, audio);
}
});
mediaRecorder = null;
context = null;
});
}
reader.readAsArrayBuffer(blob);
};
mediaRecorder.onstop = function(e) {
stream.getTracks().forEach(function(track) {
track.stop();
});
};
mediaRecorder.start();
})
.catch(function(err) {
printTextarea('js: error getting audio stream: ' + err);
});
}
//
// main
//
var nLines = 0;
var intervalUpdate = null;
var movesAll = '';
document.body.addEventListener('keydown', function(event) {
if (event.keyCode === 32) {
document.getElementById('toggler').innerText = "Release";
onStart();
}
}, true);
document.body.addEventListener('keyup', function(event) {
if (event.keyCode === 32) {
document.getElementById('toggler').innerText = "Hold";
onStop();
}
}, true);
document.getElementById('toggler').addEventListener('mousedown', function(event) {
this.innerText = "Release";
onStart();
}, true);
document.getElementById('toggler').addEventListener('mouseup', function(event) {
this.innerText = "Hold";
onStop();
}, true);
function onStart() {
if (!instance) {
return;
}
startRecording();
}
function onStop() {
printTextarea('js: stopping recording ...');
stopRecording();
var interval = setInterval(function() {
var moves = Module.get_moves();
if (moves != null && moves.length > 1) {
clearInterval(interval);
for (move of moves.split(' ')) {
board.move(move);
}
movesAll += moves + '<br>';
nLines++;
// if more than 10 lines, remove the first line
if (nLines > 10) {
var i = movesAll.indexOf('<br>');
if (i > 0) {
movesAll = movesAll.substring(i + 4);
nLines--;
}
}
document.getElementById('state-status').innerHTML = Module.get_status();
document.getElementById('state-moves').innerHTML = movesAll;
}
}, 100);
}
</script>
<script type="text/javascript" src="js/chess.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,173 +0,0 @@
#include <WChess.h>
#include <emscripten/bind.h>
#include <atomic>
#include <thread>
constexpr int N_THREAD = 8;
std::vector<struct whisper_context *> g_contexts(4, nullptr);
std::mutex g_mutex;
std::thread g_worker;
std::atomic<bool> g_running(false);
std::string g_status = "";
std::string g_status_forced = "";
std::string g_moves = "";
std::vector<float> g_pcmf32;
void set_status(const std::string & status) {
std::lock_guard<std::mutex> lock(g_mutex);
g_status = status;
}
void set_moves(const std::string & moves) {
std::lock_guard<std::mutex> lock(g_mutex);
g_moves = moves;
}
void get_audio(int /* ms */, std::vector<float> & audio) {
std::lock_guard<std::mutex> lock(g_mutex);
audio = g_pcmf32;
}
bool check_running() {
return g_running;
}
void clear_audio() {
std::lock_guard<std::mutex> lock(g_mutex);
g_pcmf32.clear();
}
void wchess_main(size_t i) {
struct whisper_full_params wparams = whisper_full_default_params(whisper_sampling_strategy::WHISPER_SAMPLING_GREEDY);
wparams.n_threads = std::min(N_THREAD, (int) std::thread::hardware_concurrency());
wparams.offset_ms = 0;
wparams.translate = false;
wparams.no_context = true;
wparams.single_segment = true;
wparams.print_realtime = false;
wparams.print_progress = false;
wparams.print_timestamps = true;
wparams.print_special = false;
wparams.no_timestamps = true;
wparams.max_tokens = 32;
wparams.audio_ctx = 768; // partial encoder context for better performance
wparams.temperature = 0.0f;
wparams.temperature_inc = 2.0f;
wparams.greedy.best_of = 1;
wparams.beam_search.beam_size = 1;
wparams.language = "en";
wparams.grammar_penalty = 100.0;
wparams.initial_prompt = "bishop to c3, rook to d4, knight to e5, d4 d5, knight to c3, c3, queen to d4, king b1, pawn to a1, bishop to b2, knight to c3,";
printf("command: using %d threads\n", wparams.n_threads);
WChess::callbacks cb;
cb.set_status = set_status;
cb.check_running = check_running;
cb.get_audio = get_audio;
cb.set_moves = set_moves;
cb.clear_audio = clear_audio;
WChess(g_contexts[i], wparams, cb, {}).run();
if (i < g_contexts.size()) {
whisper_free(g_contexts[i]);
g_contexts[i] = nullptr;
}
}
EMSCRIPTEN_BINDINGS(command) {
emscripten::function("init", emscripten::optional_override([](const std::string & path_model) {
for (size_t i = 0; i < g_contexts.size(); ++i) {
if (g_contexts[i] == nullptr) {
g_contexts[i] = whisper_init_from_file_with_params(path_model.c_str(), whisper_context_default_params());
if (g_contexts[i] != nullptr) {
g_running = true;
if (g_worker.joinable()) {
g_worker.join();
}
g_worker = std::thread([i]() {
wchess_main(i);
});
return i + 1;
} else {
return (size_t) 0;
}
}
}
return (size_t) 0;
}));
emscripten::function("free", emscripten::optional_override([](size_t /* index */) {
if (g_running) {
g_running = false;
}
}));
emscripten::function("set_audio", emscripten::optional_override([](size_t index, const emscripten::val & audio) {
--index;
if (index >= g_contexts.size()) {
return -1;
}
if (g_contexts[index] == nullptr) {
return -2;
}
{
std::lock_guard<std::mutex> lock(g_mutex);
const int n = audio["length"].as<int>();
emscripten::val heap = emscripten::val::module_property("HEAPU8");
emscripten::val memory = heap["buffer"];
g_pcmf32.resize(n);
emscripten::val memoryView = audio["constructor"].new_(memory, reinterpret_cast<uintptr_t>(g_pcmf32.data()), n);
memoryView.call<void>("set", audio);
}
return 0;
}));
emscripten::function("get_moves", emscripten::optional_override([]() {
std::string moves;
{
std::lock_guard<std::mutex> lock(g_mutex);
moves = std::move(g_moves);
}
return moves;
}));
emscripten::function("get_status", emscripten::optional_override([]() {
std::string status;
{
std::lock_guard<std::mutex> lock(g_mutex);
status = g_status_forced.empty() ? g_status : g_status_forced;
}
return status;
}));
emscripten::function("set_status", emscripten::optional_override([](const std::string & status) {
std::lock_guard<std::mutex> lock(g_mutex);
g_status_forced = status;
}));
}

View File

@ -6064,7 +6064,9 @@ WHISPER_API const char * whisper_bench_memcpy_str(int n_threads) {
// 1GB array
const size_t size = arr*1e6;
// single-thread
double sum = 0.0;
// heat-up
{
char * src = (char *) malloc(size);
char * dst = (char *) malloc(size);
@ -6074,7 +6076,6 @@ WHISPER_API const char * whisper_bench_memcpy_str(int n_threads) {
memcpy(dst, src, size); // heat-up
double tsum = 0.0;
double sum = 0.0;
for (size_t i = 0; i < n; i++) {
const int64_t t0 = ggml_time_us();
@ -6088,21 +6089,108 @@ WHISPER_API const char * whisper_bench_memcpy_str(int n_threads) {
src[rand() % size] = rand() % 256;
}
snprintf(strbuf, sizeof(strbuf), "memcpy: %.2f GB/s (1 thread)\n", (double) (n*size)/(tsum*1e9));
snprintf(strbuf, sizeof(strbuf), "memcpy: %7.2f GB/s (heat-up)\n", (double) (n*size)/(tsum*1e9));
s += strbuf;
// needed to prevent the compiler from optimizing the memcpy away
{
for (size_t i = 0; i < size; i++) sum += dst[i];
snprintf(strbuf, sizeof(strbuf), "sum: %f\n", sum);
s += strbuf;
}
free(src);
free(dst);
}
// single-thread
{
char * src = (char *) malloc(size);
char * dst = (char *) malloc(size);
for (size_t i = 0; i < size; i++) src[i] = i;
memcpy(dst, src, size); // heat-up
double tsum = 0.0;
for (size_t i = 0; i < n; i++) {
const int64_t t0 = ggml_time_us();
memcpy(dst, src, size);
const int64_t t1 = ggml_time_us();
tsum += (t1 - t0)*1e-6;
src[rand() % size] = rand() % 256;
}
snprintf(strbuf, sizeof(strbuf), "memcpy: %7.2f GB/s ( 1 thread)\n", (double) (n*size)/(tsum*1e9));
s += strbuf;
// needed to prevent the compiler from optimizing the memcpy away
{
for (size_t i = 0; i < size; i++) sum += dst[i];
}
free(src);
free(dst);
}
// multi-thread
for (uint32_t n_threads = 1; n_threads <= std::thread::hardware_concurrency(); n_threads++) {
char * src = (char *) malloc(size);
char * dst = (char *) malloc(size);
for (size_t i = 0; i < size; i++) src[i] = i;
memcpy(dst, src, size); // heat-up
double tsum = 0.0;
auto helper = [&](int th) {
const int64_t i0 = (th + 0)*size/n_threads;
const int64_t i1 = (th + 1)*size/n_threads;
for (size_t i = 0; i < n; i++) {
memcpy(dst + i0, src + i0, i1 - i0);
src[i0 + rand() % (i1 - i0)] = rand() % 256;
};
};
const int64_t t0 = ggml_time_us();
std::vector<std::thread> threads(n_threads - 1);
for (uint32_t th = 0; th < n_threads - 1; ++th) {
threads[th] = std::thread(helper, th);
}
helper(n_threads - 1);
for (uint32_t th = 0; th < n_threads - 1; ++th) {
threads[th].join();
}
const int64_t t1 = ggml_time_us();
tsum += (t1 - t0)*1e-6;
snprintf(strbuf, sizeof(strbuf), "memcpy: %7.2f GB/s (%2d thread)\n", (double) (n*size)/(tsum*1e9), n_threads);
s += strbuf;
// needed to prevent the compiler from optimizing the memcpy away
{
for (size_t i = 0; i < size; i++) sum += dst[i];
}
free(src);
free(dst);
}
snprintf(strbuf, sizeof(strbuf), "sum: %f\n", sum);
s += strbuf;
return s.c_str();
}