A simpler way to determine the winner in tic-tac-toe? - alloy

I model the tic-tac-toe game board this way:
one sig gameBoard {
cells: Row -> Col -> Mark -> Time
}
Mark is either X or O:
enum Mark { X, O }
Row and Col are simply sets:
sig Row {}{ #Row = 3}
sig Col {}{ #Col = 3}
There is a winner when:
there is a row with all X's or all O's, or
there is a col with all X's or all O's, or
there is a left-to-right diagonal with all X's or all O's, or
there is a right-to-left diagonal with all X's or all O's.
I express that with the following complex predicate. Is there a simpler way to express the winner?
pred winner [t: Time] {
some m: Mark |
some r: Row | all c: Col | board[r, c, t] = m
or
some c: Col| all r: Row | board[r, c, t] = m
or
board[first, first, t] = m and
board[first.next, first.next, t] = m and
board[first.next.next, first.next.next, t] = m
or
board[last,last, t] = m and
board[last.prev, last.prev, t] = m and
board[last.prev.prev,last.prev.prev, t] = m
}
Here is my complete tic-tac-toe model:
open util/ordering[Time]
open util/ordering[Row]
open util/ordering[Col]
/*
Structure:
1. The game is played on a 3x3 board.
2. There are two players, Player1 and Player2.
3. Players mark the game board with either X or O.
4. The game is played over a series of time steps.
*/
// 4. The game is played over a series of time steps.
sig Time {}
// 3. Players mark the game board with either X or O.
enum Mark { X, O }
// 2. There are two players, Player1 and Player2.
enum Player { Player1, Player2 }
// 1. The game is played on a ... board.
one sig gameBoard {
cells: Row -> Col -> Mark -> Time
}
// 1. ... on a 3x3 board.
sig Row {}{ #Row = 3}
sig Col {}{ #Col = 3}
/*
Constraints:
1. Each cell has at most one mark (X or O) at each time.
2. A win stops further marking.
3. When all cells are marked, there can be no additional marking.
4. Players alternate moves.
5. There is no interrupt in the play: If cells are empty at time t-1,
and there is no winner at time t-1, then there will be one
fewer empty cells at time t. If there is a winner at time t-1,
then there will be no change to the number of empty cells at
time t (per invariant 2).
6. Player1 marks cells O and Player2 marks cells X.
7. When there is a winner or when all cells are marked,
then the recording of "last player to move" is blank.
*/
// 1. Each cell has at most one mark (X or O) at each time.
pred Each_cell_has_at_most_one_mark {
no r: Row, c: Col, t: Time, disj m, m': Mark |
((r -> c -> m) in gameBoard.cells.t) and
((r -> c -> m') in gameBoard.cells.t)
}
// 2. A win stops further marking.
pred gameBoard_remains_unchanged_after_win {
all t: Time - first |
winner[t.prev] => gameBoard.cells.t = gameBoard.cells.(t.prev)
}
// 3. When all cells are marked, there can be no additional marking.
pred gameBoard_remains_unchanged_after_every_cell_is_marked {
all t: Time - first |
every_cell_is_marked[t.prev] => gameBoard.cells.t = gameBoard.cells.(t.prev)
}
// 4. Players alternate moves.
pred Players_alternately_move {
no t: Time - last, t': t.next |
(some LastPlayerToMove.person.t) and
(some LastPlayerToMove.person.t') and
(LastPlayerToMove.person.t = LastPlayerToMove.person.t')
}
// 5. There is no interrupt in the play: If cells are empty at time t-1,
// and there is no winner at time t-1, then there will be one
// fewer empty cells at time t. If there is a winner at time t-1,
// then there will be no change to the number of empty cells at
// time t (per invariant 2).
pred Progressively_fewer_empty_cells {
all t: Time - first |
not every_cell_is_marked[t.prev] and not winner[t.prev] =>
#empty_cells[t] < #empty_cells[t.prev]
}
// 6. Player1 marks cells O and Player2 marks cells X.
pred Players_mark_cells_appropriately {
all t: Time - first |
not every_cell_is_marked[t.prev] and not winner[t.prev] =>
let c = gameBoard.cells.t - gameBoard.cells.(t.prev) |
c[Row][Col] = X =>
(LastPlayerToMove.person.t = Player2)
else
(LastPlayerToMove.person.t = Player1)
}
// 7. When there is a winner or when all cells are marked,
// then the recording of "last player to move" is blank.
pred LastPlayerToMove_remains_unchanged_after_win_or_all_cells_marked {
all t: Time - first |
((every_cell_is_marked[t.prev]) or (winner[t.prev])) =>
no LastPlayerToMove.person.t
}
// This provides one place that you can call to
// have all the constraints enforced.
pred game_is_constrained_by_these_constraints {
Each_cell_has_at_most_one_mark
gameBoard_remains_unchanged_after_win
gameBoard_remains_unchanged_after_every_cell_is_marked
Players_alternately_move
Progressively_fewer_empty_cells
Players_mark_cells_appropriately
LastPlayerToMove_remains_unchanged_after_win_or_all_cells_marked
}
// Return the set of empty cells at time t.
// This is implemented using set subtraction.
// (Row -> Col) is the set of all possible combinations
// of row and col. Subtract from that the set
// of (row, col) pairs containing a mark at time t.
fun empty_cells[t: Time]: Row -> Col {
(Row -> Col) - gameBoard.cells.t.Mark
}
// Once the game board is completely marked,
// there won't be a "last player." Ditto for when
// there is a winner. That's why there "may" be
// a last player at time t. That is, there isn’t
// necessarily a player involved at every time step,
// i.e., there isn’t necessarily a (Player, Time) pair
// for every value of Time.
one sig LastPlayerToMove {
person: Player lone -> Time
}
// Return the mark (X or O) on board[r][c] at time t,
// or none if there is no mark.
fun board [r: Row, c: Col, t: Time]: lone Mark {
gameBoard.cells[r][c].t
}
// There is a winner when (a) there is a row
// with all X's or all O's, or (b) there is a col
// with all X's or all O's, or (c) there is a left-to-right
// diagonal with all X's or all O's, or (d) there is a
// right-to-left diagonal with all X's or all O's.
pred winner [t: Time] {
some m: Mark |
some r: Row | all c: Col | board[r, c, t] = m
or
some c: Col| all r: Row | board[r, c, t] = m
or
board[first, first, t] = m and
board[first.next, first.next, t] = m and
board[first.next.next, first.next.next, t] = m
or
board[last,last, t] = m and
board[last.prev, last.prev, t] = m and
board[last.prev.prev,last.prev.prev, t] = m
}
// Every call of the game board is marked when
// the set of cells with marks equals all combinations
// of (row, col)
pred every_cell_is_marked[t: Time] {
gameBoard.cells.t.Mark = (Row -> Col)
}
// Initially the game board has no cells.
// One of the players is first to play.
// The game is constrained by the invariants.
pred init [t: Time] {
no gameBoard.cells.t
one p: Player | LastPlayerToMove.person.t = p
game_is_constrained_by_these_constraints
}
pred doNothing [t: Time] {
gameBoard.cells.t = gameBoard.cells.(t.prev)
}
pred Play {
init[first]
all t: Time - first |
X.marked_on_gameboard_at_time[t]
or O.marked_on_gameboard_at_time[t]
or doNothing[t]
}
pred marked_on_gameboard_at_time [m: Mark, t: Time] {
some r: Row, c: Col {
gameBoard.cells.t = gameBoard.cells.(t.prev) +
{r': Row, c': Col, m': Mark | r' = r and c' = c and m' = m}
}
}
run Play for 3 but 12 Time

comment: You can run this whole markdown text in Alloy 5 Beta
TIC-TAC-TOE
We design this game around a Board. The Board is the state and we will use game rules encoded in predicates to
constrain the transitions to the next board.
Setup
Setup the game by defining the board size, the board and the players. The Board has a relative large number
of fields because that makes the trace output nice to read.
```alloy
open util/ordering[Board]
let N = 0+1+2
let highest = max[N]
sig Board {
cells : N -> N -> Cell,
move: Cell,
to : N->N,
won : Cell
}
enum Cell { _, X, O }
```
Winning
The game is won when there is a row, a colum, or a diagonal that holds the same player. We can encode this as follows:
```alloy
let rightdiag = { r,c : N | r = c }
let leftdiag = { r,c : N | c = highest.minus[r] }
pred won[b,b':Board ] {
some token : X + O {
let positions = b.cells.token {
some row : N | N = row.positions
or some column : N | N = positions.column
or rightdiag in positions
or leftdiag in positions
b'.won = token
}
}
}
```
Finished
The game is finished when a player won or there are no more free places.
```alloy
pred finished[ b,b' : Board ] {
not won[b,b'] implies {
b'.won = _
_ not in b'.cells[N][N]
}
b'.cells = b.cells
b'.move = _
b'.to = none -> none
}
```
Play
Plays should alternate between the players. That is why we keep the player in the previous board's move field
and check it is not us. We then constrain the board to have an empty position replaced with the player's
token.
```alloy
pred play[b, b' : Board ] {
b'.won=_
some token : X+O {
b.move != token
b'.move = token
some row,col : N {
b.cells[row][col] = _
b'.cells = b.cells - row->col->_ + row->col->token
b'.to = row->col
}
}
}
```
Trace
Then the only thing remaining is to setup the first board and ensure the game (the trace of Boards) is
played according to the rules.
```alloy
fact trace {
first.move = _
first.won = _
first.cells = N->N->_
all b' : Board - first, b : b'.prev {
not finished[b,b'] => play[b,b']
}
}
```
Run
With the run we can look for certain types of solutions. In this example we try to find a game where O
wins with a righ diagonal ...
```alloy
run { some b : Board | rightdiag in b.cells.(O) } for 11 but 3 int
```
This provides the following output in Alloy 5 Table view (table is reorded from beta 5):
┌──────────┬──────┬────┬───┬───┐
│this/Board│cells │move│to │won│
├──────────┼─┬─┬──┼────┼───┼───┤
│Board⁰ │0│0│_⁰│_⁰ │ │_⁰ │
│ │ ├─┼──┼────┤ ├───┤
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board¹ │0│0│_⁰│X⁰ │2│1│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board² │0│0│_⁰│O⁰ │1│2│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board³ │0│0│_⁰│X⁰ │0│1│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁴ │0│0│O⁰│O⁰ │0│0│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁵ │0│0│O⁰│X⁰ │2│0│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁶ │0│0│O⁰│O⁰ │1│1│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│_⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│O⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁷ │0│0│O⁰│X⁰ │1│0│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│O⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
├──────────┼─┼─┼──┼────┼─┬─┼───┤
│Board⁸ │0│0│O⁰│O⁰ │2│2│_⁰ │
│ │ ├─┼──┼────┼─┴─┼───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│O⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
├──────────┼─┼─┼──┼────┼───┼───┤
│Board⁹ │0│0│O⁰│_⁰ │ │O⁰ │
│ │ ├─┼──┼────┤ ├───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│O⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
├──────────┼─┼─┼──┼────┼───┼───┤
│Board¹⁰ │0│0│O⁰│_⁰ │ │O⁰ │
│ │ ├─┼──┼────┤ ├───┤
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│_⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │1│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│O⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
│ ├─┼─┼──┤ │ │ │
│ │2│0│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │1│X⁰│ │ │ │
│ │ ├─┼──┤ │ │ │
│ │ │2│O⁰│ │ │ │
└──────────┴─┴─┴──┴────┴───┴───┘

I had some fun implementing this example in Lightning. You might find my results interesting:
Lightning (lightning-workbench) is a language workbench based on Alloy allowing you to define a concrete syntax for any of your Alloy specifications. The concrete syntax is defined through the use of a dedicated model transformation language called f-alloy ( an Alloy variant having the property of being interpretable rather than analyzable ), so you might need a bit of time getting used to it.
The tool's available as an eclipse plugin (update site).
Here's an archive file containing the source of the TicTacToe project illustrated in the above picture, so that you can play around the example by yourself.

Related

Google Cloud Storage NodeJS multiple read requests loading too slow

how do you do?
I'm trying to figure out why some requests to my Images API (usually the last ones) are taking over than 1 minute to load. The first ones are basically instantaneous. Search all over the internet, but had no apropriate answer yet. I am using Google Cloud to storage the images and NodeJS at the server, who is providing the images as a bufferized writing head to the browser.
You can check what am I saying accessing the website (18 year old content):
https://divinasacompanhantes.com/
As you can see, some images just don't load properly. I'm worried because this website is expected to have thousands more profiles, all over the world.
I am using PM2 to handle the services at the server side (2GB available memory). Here's the table:
┌─────┬─────────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├─────┼─────────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 7 │ ServiceAfiliado │ default │ 1.0.0 │ fork │ 31312 │ 20m │ 3 │ online │ 0% │ 55.9mb │ root │ disabled │
│ 0 │ ServiceAvaliacao │ default │ 1.0.0 │ fork │ 31249 │ 20m │ 3 │ online │ 0% │ 55.2mb │ root │ disabled │
│ 8 │ ServiceBlog │ default │ 1.0.0 │ fork │ 31330 │ 20m │ 3 │ online │ 0% │ 61.1mb │ root │ disabled │
│ 1 │ ServiceChat │ default │ 1.0.0 │ fork │ 31256 │ 20m │ 3 │ online │ 0% │ 57.3mb │ root │ disabled │
│ 9 │ ServiceConfig │ default │ 1.0.0 │ fork │ 31337 │ 20m │ 3 │ online │ 0% │ 56.2mb │ root │ disabled │
│ 10 │ ServiceImage │ default │ 1.0.0 │ fork │ 31904 │ 0s │ 13 │ online │ 0% │ 19.1mb │ root │ disabled │
│ 2 │ ServiceLead │ default │ 1.0.0 │ fork │ 31269 │ 20m │ 3 │ online │ 0% │ 54.8mb │ root │ disabled │
│ 3 │ ServiceMail │ default │ 1.0.0 │ fork │ 31276 │ 20m │ 3 │ online │ 0% │ 43.3mb │ root │ disabled │
│ 4 │ ServicePagamento │ default │ 1.0.0 │ fork │ 31289 │ 20m │ 3 │ online │ 0% │ 42.5mb │ root │ disabled │
│ 5 │ ServiceParceiro │ default │ 1.0.0 │ fork │ 31296 │ 20m │ 3 │ online │ 0% │ 60.1mb │ root │ disabled │
│ 6 │ ServicePerfil │ default │ 1.0.0 │ fork │ 31309 │ 20m │ 3 │ online │ 0% │ 69.7mb │ root │ disabled │
└─────┴─────────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
The route handling this specific request:
router.get('/image/:imageId', async function (req, res) {
try {
let imageId = req.param('imageId')
let returnImage = await cloudController.getImageFromBucket('fotos_perfil', imageId)
res.writeHead(200, {'Content-Type': 'image/jpg'});
returnImage.on('data', (data) => {
res.write(data)
})
returnImage.on('error', (error) => {
res.status(400).send('Erro lendo a imagem')
console.error(error)
})
returnImage.on('end', () => {
res.end()
})
} catch (err) {
res.status(500).send('Internal Server Error')
}
})
And the controller associated:
async function getImageFromBucket(bucket, imageId) {
return new Promise((resolve, reject) => {
try {
let imageInfo = storage.bucket(bucket).file(imageId).createReadStream()
resolve(imageInfo)
} catch (e) {
reject(e)
}
})
}
Can anyone provide me some ideas to solve this? I've read the official Google documentation and the tip is to use fast-crc32c, but only. No clues on how to configure...

Create a spectrogram in Node.js

How can I extract the time/frequency/amplitude information from a WAV (or AIFF)? I want a spectrogram as a an array, e.g.
[
[ // 1st sample, time
[frequency1_t1, amplitude_f1t1], [frequency2_t1, amplitude_f2t1], ...
],
[ // 2nd sample, time
[frequency1_t2, amplitude_f1t2], [frequency2_t2, amplitude_f2t2], ...
]
]
(or similar, the shape is not relevant)
At the moment I don't understand:
I would expect an array with values for each frequency in the spectrum. E.g. 0.2 for 40Hz, 0.3 for 41Hz and so on... but I get an array with the half of the sample length.
Code:
require('dotenv').config()
const fs = require('fs');
const ft = require('fourier-transform');
const db = require('decibels');
const sine = require('audio-oscillator/sin');
const WaveFile = require('wavefile');
const {unpackArray} = require('byte-data');
const generateLength = 1024// 11289600; // 1024
/**
* Return the sample at a given index.
* #param {number} startIndex The sample start index.
* #param {number} stopIndex The sample stop index.
* #return {number} The sample.
* #throws {Error} If the sample index is off range.
*/
const getSamples = (wav, startIndex, stopIndex) => {
startIndex = startIndex * (wav.dataType.bits / 8);
stopIndex = stopIndex * (wav.dataType.bits / 8);
if (stopIndex + wav.dataType.bits / 8 > wav.data.samples.length) {
throw new Error('Range error');
}
return unpackArray(
wav.data.samples.slice(startIndex, stopIndex),
wav.dataType
);
};
/**
* Generate a sine wave
*/
const generateWav = () => {
// generate sine wave 440 Hz
const waveform = sine(generateLength, 440); // samples, frequency
return waveform
};
/**
* Read a wav file from disk
*/
const readWav = (callback) => {
// return callback(null, generateWav())
if (process.env.GENERATE === 'true') {
return callback(null, generateWav())
}
let wav;
// read the wav file
const filePath = process.env.AUDIO_IN_FOLDER + process.env.AUDIO_FILE;
fs.readFile(filePath, (err, buffer) => {
if (err) {
return callback(err);
}
wav = new WaveFile(buffer);
const samples = getSamples(wav, 0, 1024);
return callback(null, samples);
})
};
/**
* Process the wav
*/
readWav((err, samples) => {
if (err) {
throw new Error(err);
}
// in case I create the sine wave, I get an array of length 1024, with values from -1 to +1
// the same applies for the wav
console.log('samples', samples, samples.length);
// get normalized magnitudes for frequencies from 0 to 22050 with interval 44100/1024 ≈ 43Hz
// I get an array of length 512 (probably 1024/ 2 channels) with value from 0 - 0.7 (max prob. 1)
const spectrum = ft(samples);
console.table(spectrum);
// //convert to decibels
// const decibels = spectrum.map((value) => db.fromGain(value));
// console.table(decibels);
});
I would like to retrieve the amplitudes at the diff. frequencies.
PS:
I managed to do this in the browser with the web audio API but the only package I found for node.js so far is:
https://github.com/audiojs/web-audio-api which is abandoned.
EDIT: I found another library which delivers more plausible results:
const fft = require('fft-js').fft;
const fftUtil = require('fft-js').util;
/**
* Process the wav
*/
const phasors = fft(generateWav());
const frequencies = fftUtil.fftFreq(phasors, 44100), // Sample rate and coef is just used for length, and frequency step
magnitudes = fftUtil.fftMag(phasors);
const both = frequencies.map(function (f, ix) {
return {frequency: f, magnitude: magnitudes[ix]};
});
console.table(both);
Output:
┌─────────┬────────────────┬────────────────────┐
│ (index) │ frequency │ magnitude │
├─────────┼────────────────┼────────────────────┤
│ 0 │ 0 │ 12.152954611168916 │
│ 1 │ 43.06640625 │ 12.371840124428138 │
│ 2 │ 86.1328125 │ 13.049660038632785 │
│ 3 │ 129.19921875 │ 14.257748464539002 │
│ 4 │ 172.265625 │ 16.145493015641954 │
│ 5 │ 215.33203125 │ 19.006782054318574 │
│ 6 │ 258.3984375 │ 23.444013431023404 │
│ 7 │ 301.46484375 │ 30.839530345061696 │
│ 8 │ 344.53125 │ 45.104565448543774 │
│ 9 │ 387.59765625 │ 83.05529774944604 │
│ 10 │ 430.6640625 │ 471.99329683509916 │
│ 11 │ 473.73046875 │ 132.38812248080427 │
│ 12 │ 516.796875 │ 58.94894310683054 │
│ 13 │ 559.86328125 │ 38.29097615220791 │
│ 14 │ 602.9296875 │ 28.55339797808452 │
│ 15 │ 645.99609375 │ 22.8834459943328 │
│ 16 │ 689.0625 │ 19.169092636055744 │
│ 17 │ 732.12890625 │ 16.544346462620222 │
│ 18 │ 775.1953125 │ 14.588639163496254 │
│ 19 │ 818.26171875 │ 13.073165838642582 │
│ 20 │ 861.328125 │ 11.862817675507356 │
│ 21 │ 904.39453125 │ 10.872696852206447 │
│ 22 │ 947.4609375 │ 10.04678419250667 │
│ 23 │ 990.52734375 │ 9.346626225654326 │
│ 24 │ 1033.59375 │ 8.744951406922706 │
│ 25 │ 1076.66015625 │ 8.221880329631402 │
│ 26 │ 1119.7265625 │ 7.762578689775234 │
│ 27 │ 1162.79296875 │ 7.355750727231507 │
│ 28 │ 1205.859375 │ 6.992641946837161 │
│ 29 │ 1248.92578125 │ 6.666361076689498 │
│ 30 │ 1291.9921875 │ 6.371408134738898 │
│ 31 │ 1335.05859375 │ 6.103339064749246 │
│ 32 │ 1378.125 │ 5.858522971508816 │
│ 33 │ 1421.19140625 │ 5.633963445745503 │
│ 34 │ 1464.2578125 │ 5.427165073795851 │
│ 35 │ 1507.32421875 │ 5.236032340013399 │
│ 36 │ 1550.390625 │ 5.058792106400648 │
│ 37 │ 1593.45703125 │ 4.893933492369614 │
│ 38 │ 1636.5234375 │ 4.74016075996692 │
│ 39 │ 1679.58984375 │ 4.596356034064684 │
│ 40 │ 1722.65625 │ 4.461549540593798 │
│ 41 │ 1765.72265625 │ 4.334895649436749 │
│ 42 │ 1808.7890625 │ 4.21565344088737 │
│ 43 │ 1851.85546875 │ 4.10317082794807 │
│ 44 │ 1894.921875 │ 3.9968714964351753 │
│ 45 │ 1937.98828125 │ 3.896244094995214 │
│ 46 │ 1981.0546875 │ 3.8008332343806863 │
│ 47 │ 2024.12109375 │ 3.710231951372898 │
│ 48 │ 2067.1875 │ 3.624075365850696 │
│ 49 │ 2110.25390625 │ 3.5420353156097173 │
│ 50 │ 2153.3203125 │ 3.463815796922526 │
│ 51 │ 2196.38671875 │ 3.3891490726204667 │
│ 52 │ 2239.453125 │ 3.317792335974296 │
│ 53 │ 2282.51953125 │ 3.2495248395633305 │
│ 54 │ 2325.5859375 │ 3.184145414927604 │
│ 55 │ 2368.65234375 │ 3.121470322059914 │
│ 56 │ 2411.71875 │ 3.0613313784448493 │
│ 57 │ 2454.78515625 │ 3.0035743259530703 │
│ 58 │ 2497.8515625 │ 2.9480574008737856 │
│ 59 │ 2540.91796875 │ 2.8946500780591413 │
│ 60 │ 2583.984375 │ 2.843231964813001 │
│ 61 │ 2627.05078125 │ 2.7936918239895956 │
│ 62 │ 2670.1171875 │ 2.7459267089345327 │
│ 63 │ 2713.18359375 │ 2.6998411955269557 │
│ 64 │ 2756.25 │ 2.65534669876918 │
│ 65 │ 2799.31640625 │ 2.6123608631974977 │
│ 66 │ 2842.3828125 │ 2.570807017921727 │
│ 67 │ 2885.44921875 │ 2.5306136883907895 │
│ 68 │ 2928.515625 │ 2.491714158071842 │
│ 69 │ 2971.58203125 │ 2.4540460741530628 │
│ 70 │ 3014.6484375 │ 2.4175510921649352 │
│ 71 │ 3057.71484375 │ 2.3821745550828988 │
│ 72 │ 3100.78125 │ 2.3478652030460068 │
│ 73 │ 3143.84765625 │ 2.3145749103153315 │
│ 74 │ 3186.9140625 │ 2.282258446516979 │
│ 75 │ 3229.98046875 │ 2.2508732595769505 │
│ 76 │ 3273.046875 │ 2.220379278067911 │
│ 77 │ 3316.11328125 │ 2.1907387309594126 │
│ 78 │ 3359.1796875 │ 2.1619159829977477 │
│ 79 │ 3402.24609375 │ 2.1338773841468113 │
│ 80 │ 3445.3125 │ 2.1065911316990023 │
│ 81 │ 3488.37890625 │ 2.0800271438214692 │
│ 82 │ 3531.4453125 │ 2.054156943439062 │
│ 83 │ 3574.51171875 │ 2.0289535514749724 │
│ 84 │ 3617.578125 │ 2.0043913885751485 │
│ 85 │ 3660.64453125 │ 1.9804461845350614 │
│ 86 │ 3703.7109375 │ 1.9570948947290032 │
│ 87 │ 3746.77734375 │ 1.9343156229142768 │
│ 88 │ 3789.84375 │ 1.9120875498459815 │
│ 89 │ 3832.91015625 │ 1.8903908671957874 │
│ 90 │ 3875.9765625 │ 1.8692067163169377 │
│ 91 │ 3919.04296875 │ 1.8485171314433517 │
│ 92 │ 3962.109375 │ 1.8283049869505235 │
│ 93 │ 4005.17578125 │ 1.8085539483406927 │
│ 94 │ 4048.2421875 │ 1.789248426647436 │
│ 95 │ 4091.30859375 │ 1.770373535982654 │
│ 96 │ 4134.375 │ 1.7519150539745088 │
│ 97 │ 4177.44140625 │ 1.733859384867805 │
│ 98 │ 4220.5078125 │ 1.7161935250787304 │
│ 99 │ 4263.57421875 │ 1.6989050310145166 │
│ 100 │ 4306.640625 │ 1.6819819889848078 │
│ 101 │ 4349.70703125 │ 1.6654129870471555 │
│ 102 │ 4392.7734375 │ 1.649187088641961 │
│ 103 │ 4435.83984375 │ 1.6332938078849353 │
│ 104 │ 4478.90625 │ 1.6177230863958667 │
│ 105 │ 4521.97265625 │ 1.6024652715528576 │
│ 106 │ 4565.0390625 │ 1.5875110960700973 │
│ 107 │ 4608.10546875 │ 1.5728516588054446 │
│ 108 │ 4651.171875 │ 1.5584784067120494 │
│ 109 │ 4694.23828125 │ 1.5443831178544063 │
│ 110 │ 4737.3046875 │ 1.5305578854161885 │
│ 111 │ 4780.37109375 │ 1.5169951026323973 │
│ 112 │ 4823.4375 │ 1.503687448583734 │
│ 113 │ 4866.50390625 │ 1.4906278747959294 │
│ 114 │ 4909.5703125 │ 1.4778095925909394 │
│ 115 │ 4952.63671875 │ 1.465226061140952 │
│ 116 │ 4995.703125 │ 1.452870976179976 │
│ 117 │ 5038.76953125 │ 1.4407382593305655 │
│ 118 │ 5081.8359375 │ 1.4288220480072211 │
│ 119 │ 5124.90234375 │ 1.4171166858597923 │
│ 120 │ 5167.96875 │ 1.4056167137234505 │
│ 121 │ 5211.03515625 │ 1.3943168610441796 │
│ 122 │ 5254.1015625 │ 1.383212037750449 │
│ 123 │ 5297.16796875 │ 1.3722973265443272 │
│ 124 │ 5340.234375 │ 1.3615679755866048 │
│ 125 │ 5383.30078125 │ 1.3510193915528388 │
│ 126 │ 5426.3671875 │ 1.3406471330380145 │
│ 127 │ 5469.43359375 │ 1.3304469042899936 │
│ 128 │ 5512.5 │ 1.3204145492521742 │
│ 129 │ 5555.56640625 │ 1.310546045897987 │
│ 130 │ 5598.6328125 │ 1.3008375008404036 │
│ 131 │ 5641.69921875 │ 1.291285144201002 │
│ 132 │ 5684.765625 │ 1.2818853247240856 │
│ 133 │ 5727.83203125 │ 1.2726345051221066 │
│ 134 │ 5770.8984375 │ 1.2635292576398605 │
│ 135 │ 5813.96484375 │ 1.2545662598252671 │
│ 136 │ 5857.03125 │ 1.2457422904957558 │
│ 137 │ 5900.09765625 │ 1.2370542258895871 │
│ 138 │ 5943.1640625 │ 1.2284990359922503 │
│ 139 │ 5986.23046875 │ 1.2200737810288795 │
│ 140 │ 6029.296875 │ 1.2117756081135687 │
│ 141 │ 6072.36328125 │ 1.2036017480478065 │
│ 142 │ 6115.4296875 │ 1.1955495122601592 │
│ 143 │ 6158.49609375 │ 1.1876162898798384 │
│ 144 │ 6201.5625 │ 1.1797995449375729 │
│ 145 │ 6244.62890625 │ 1.1720968136870935 │
│ 146 │ 6287.6953125 │ 1.164505702041419 │
│ 147 │ 6330.76171875 │ 1.1570238831180508 │
│ 148 │ 6373.828125 │ 1.1496490948877365 │
│ 149 │ 6416.89453125 │ 1.1423791379217956 │
│ 150 │ 6459.9609375 │ 1.1352118732330423 │
│ 151 │ 6503.02734375 │ 1.1281452202060451 │
│ 152 │ 6546.09375 │ 1.121177154611966 │
│ 153 │ 6589.16015625 │ 1.1143057067045552 │
│ 154 │ 6632.2265625 │ 1.1075289593929885 │
│ 155 │ 6675.29296875 │ 1.1008450464880004 │
│ 156 │ 6718.359375 │ 1.0942521510181538 │
│ 157 │ 6761.42578125 │ 1.0877485036126562 │
│ 158 │ 6804.4921875 │ 1.0813323809478814 │
│ 159 │ 6847.55859375 │ 1.0750021042545954 │
│ 160 │ 6890.625 │ 1.0687560378830854 │
│ 161 │ 6933.69140625 │ 1.0625925879237053 │
│ 162 │ 6976.7578125 │ 1.0565102008801939 │
│ 163 │ 7019.82421875 │ 1.0505073623935723 │
│ 164 │ 7062.890625 │ 1.0445825960142023 │
│ 165 │ 7105.95703125 │ 1.038734462020084 │
│ 166 │ 7149.0234375 │ 1.0329615562791732 │
│ 167 │ 7192.08984375 │ 1.0272625091539958 │
│ 168 │ 7235.15625 │ 1.0216359844465652 │
│ 169 │ 7278.22265625 │ 1.0160806783819898 │
│ 170 │ 7321.2890625 │ 1.0105953186290697 │
│ 171 │ 7364.35546875 │ 1.0051786633563107 │
│ 172 │ 7407.421875 │ 0.999829500321966 │
│ 173 │ 7450.48828125 │ 0.994546645996437 │
│ 174 │ 7493.5546875 │ 0.9893289447159996 │
│ 175 │ 7536.62109375 │ 0.9841752678663415 │
│ 176 │ 7579.6875 │ 0.979084513094743 │
│ 177 │ 7622.75390625 │ 0.9740556035497749 │
│ 178 │ 7665.8203125 │ 0.9690874871473424 │
│ 179 │ 7708.88671875 │ 0.9641791358620374 │
│ 180 │ 7751.953125 │ 0.9593295450427901 │
│ 181 │ 7795.01953125 │ 0.9545377327518263 │
│ 182 │ 7838.0859375 │ 0.9498027391260084 │
│ 183 │ 7881.15234375 │ 0.9451236257597078 │
│ 184 │ 7924.21875 │ 0.9404994751083671 │
│ 185 │ 7967.28515625 │ 0.9359293899118848 │
│ 186 │ 8010.3515625 │ 0.9314124926370905 │
│ 187 │ 8053.41796875 │ 0.9269479249386134 │
│ 188 │ 8096.484375 │ 0.922534847137409 │
│ 189 │ 8139.55078125 │ 0.9181724377161812 │
│ 190 │ 8182.6171875 │ 0.9138598928313305 │
│ 191 │ 8225.68359375 │ 0.9095964258403147 │
│ 192 │ 8268.75 │ 0.9053812668445473 │
│ 193 │ 8311.81640625 │ 0.9012136622464734 │
│ 194 │ 8354.8828125 │ 0.8970928743209342 │
│ 195 │ 8397.94921875 │ 0.8930181807999668 │
│ 196 │ 8441.015625 │ 0.8889888744705485 │
│ 197 │ 8484.08203125 │ 0.8850042627850117 │
│ 198 │ 8527.1484375 │ 0.8810636674834446 │
│ 199 │ 8570.21484375 │ 0.8771664242277897 │
│ 200 │ 8613.28125 │ 0.8733118822471652 │
│ 201 │ 8656.34765625 │ 0.8694994039939781 │
│ 202 │ 8699.4140625 │ 0.8657283648106044 │
│ 203 │ 8742.48046875 │ 0.8619981526059125 │
│ 204 │ 8785.546875 │ 0.8583081675418567 │
│ 205 │ 8828.61328125 │ 0.8546578217290682 │
│ 206 │ 8871.6796875 │ 0.8510465389317811 │
│ 207 │ 8914.74609375 │ 0.8474737542813985 │
│ 208 │ 8957.8125 │ 0.8439389139984876 │
│ 209 │ 9000.87890625 │ 0.8404414751229206 │
│ 210 │ 9043.9453125 │ 0.8369809052519736 │
│ 211 │ 9087.01171875 │ 0.8335566822859806 │
│ 212 │ 9130.078125 │ 0.8301682941811723 │
│ 213 │ 9173.14453125 │ 0.8268152387099789 │
│ 214 │ 9216.2109375 │ 0.8234970232278401 │
│ 215 │ 9259.27734375 │ 0.820213164446897 │
│ 216 │ 9302.34375 │ 0.8169631882159036 │
│ 217 │ 9345.41015625 │ 0.813746629306537 │
│ 218 │ 9388.4765625 │ 0.8105630312055566 │
│ 219 │ 9431.54296875 │ 0.8074119459128286 │
│ 220 │ 9474.609375 │ 0.8042929337449944 │
│ 221 │ 9517.67578125 │ 0.8012055631444901 │
│ 222 │ 9560.7421875 │ 0.7981494104939528 │
│ 223 │ 9603.80859375 │ 0.7951240599355628 │
│ 224 │ 9646.875 │ 0.7921291031954435 │
│ 225 │ 9689.94140625 │ 0.7891641394127583 │
│ 226 │ 9733.0078125 │ 0.7862287749734875 │
│ 227 │ 9776.07421875 │ 0.783322623348609 │
│ 228 │ 9819.140625 │ 0.7804453049366743 │
│ 229 │ 9862.20703125 │ 0.7775964469105909 │
│ 230 │ 9905.2734375 │ 0.7747756830683817 │
│ 231 │ 9948.33984375 │ 0.7719826536880043 │
│ 232 │ 9991.40625 │ 0.7692170053859664 │
│ 233 │ 10034.47265625 │ 0.7664783909795868 │
│ 234 │ 10077.5390625 │ 0.7637664693528738 │
│ 235 │ 10120.60546875 │ 0.7610809053259505 │
│ 236 │ 10163.671875 │ 0.7584213695278121 │
│ 237 │ 10206.73828125 │ 0.7557875382722999 │
│ 238 │ 10249.8046875 │ 0.7531790934373814 │
│ 239 │ 10292.87109375 │ 0.7505957223474149 │
│ 240 │ 10335.9375 │ 0.7480371176584374 │
│ 241 │ 10379.00390625 │ 0.745502977246369 │
│ 242 │ 10422.0703125 │ 0.7429930040980557 │
│ 243 │ 10465.13671875 │ 0.7405069062049577 │
│ 244 │ 10508.203125 │ 0.738044396459589 │
│ 245 │ 10551.26953125 │ 0.7356051925545116 │
│ 246 │ 10594.3359375 │ 0.7331890168837784 │
│ 247 │ 10637.40234375 │ 0.7307955964470021 │
│ 248 │ 10680.46875 │ 0.7284246627553796 │
│ 249 │ 10723.53515625 │ 0.7260759517404931 │
│ 250 │ 10766.6015625 │ 0.72374920366505 │
│ 251 │ 10809.66796875 │ 0.7214441630358492 │
│ 252 │ 10852.734375 │ 0.7191605785188439 │
│ 253 │ 10895.80078125 │ 0.716898202856309 │
│ 254 │ 10938.8671875 │ 0.7146567927859353 │
│ 255 │ 10981.93359375 │ 0.712436108961887 │
│ 256 │ 11025 │ 0.7102359158777101 │
│ 257 │ 11068.06640625 │ 0.7080559817911036 │
│ 258 │ 11111.1328125 │ 0.7058960786504748 │
│ 259 │ 11154.19921875 │ 0.7037559820231458 │
│ 260 │ 11197.265625 │ 0.7016354710253743 │
│ 261 │ 11240.33203125 │ 0.6995343282538382 │
│ 262 │ 11283.3984375 │ 0.6974523397188923 │
│ 263 │ 11326.46484375 │ 0.6953892947792317 │
│ 264 │ 11369.53125 │ 0.6933449860781569 │
│ 265 │ 11412.59765625 │ 0.6913192094812238 │
│ 266 │ 11455.6640625 │ 0.6893117640154295 │
│ 267 │ 11498.73046875 │ 0.6873224518095441 │
│ 268 │ 11541.796875 │ 0.6853510780362898 │
│ 269 │ 11584.86328125 │ 0.6833974508552063 │
│ 270 │ 11627.9296875 │ 0.6814613813572552 │
│ 271 │ 11670.99609375 │ 0.6795426835105075 │
│ 272 │ 11714.0625 │ 0.677641174107065 │
│ 273 │ 11757.12890625 │ 0.6757566727111779 │
│ 274 │ 11800.1953125 │ 0.6738890016084861 │
│ 275 │ 11843.26171875 │ 0.6720379857564167 │
│ 276 │ 11886.328125 │ 0.6702034527356622 │
│ 277 │ 11929.39453125 │ 0.6683852327027254 │
│ 278 │ 11972.4609375 │ 0.6665831583435046 │
│ 279 │ 12015.52734375 │ 0.6647970648279368 │
│ 280 │ 12058.59375 │ 0.663026789765516 │
│ 281 │ 12101.66015625 │ 0.661272173161877 │
│ 282 │ 12144.7265625 │ 0.6595330573763096 │
│ 283 │ 12187.79296875 │ 0.6578092870801286 │
│ 284 │ 12230.859375 │ 0.6561007092160064 │
│ 285 │ 12273.92578125 │ 0.6544071729580885 │
│ 286 │ 12316.9921875 │ 0.6527285296730775 │
│ 287 │ 12360.05859375 │ 0.6510646328820283 │
│ 288 │ 12403.125 │ 0.6494153382230178 │
│ 289 │ 12446.19140625 │ 0.6477805034145884 │
│ 290 │ 12489.2578125 │ 0.646159988219951 │
│ 291 │ 12532.32421875 │ 0.6445536544119334 │
│ 292 │ 12575.390625 │ 0.6429613657386952 │
│ 293 │ 12618.45703125 │ 0.6413829878901197 │
│ 294 │ 12661.5234375 │ 0.6398183884648961 │
│ 295 │ 12704.58984375 │ 0.6382674369383002 │
│ 296 │ 12747.65625 │ 0.6367300046307028 │
│ 297 │ 12790.72265625 │ 0.6352059646765789 │
│ 298 │ 12833.7890625 │ 0.6336951919942735 │
│ 299 │ 12876.85546875 │ 0.6321975632563025 │
│ 300 │ 12919.921875 │ 0.6307129568604022 │
│ 301 │ 12962.98828125 │ 0.6292412529009501 │
│ 302 │ 13006.0546875 │ 0.6277823331411194 │
│ 303 │ 13049.12109375 │ 0.626336080985555 │
│ 304 │ 13092.1875 │ 0.6249023814535887 │
│ 305 │ 13135.25390625 │ 0.6234811211530132 │
│ 306 │ 13178.3203125 │ 0.6220721882543192 │
│ 307 │ 13221.38671875 │ 0.6206754724655292 │
│ 308 │ 13264.453125 │ 0.6192908650074535 │
│ 309 │ 13307.51953125 │ 0.6179182585895316 │
│ 310 │ 13350.5859375 │ 0.6165575473859886 │
│ 311 │ 13393.65234375 │ 0.6152086270126826 │
│ 312 │ 13436.71875 │ 0.6138713945041759 │
│ 313 │ 13479.78515625 │ 0.6125457482914228 │
│ 314 │ 13522.8515625 │ 0.6112315881798147 │
│ 315 │ 13565.91796875 │ 0.6099288153276872 │
│ 316 │ 13608.984375 │ 0.6086373322251584 │
│ 317 │ 13652.05078125 │ 0.6073570426735272 │
│ 318 │ 13695.1171875 │ 0.6060878517649428 │
│ 319 │ 13738.18359375 │ 0.6048296658624938 │
│ 320 │ 13781.25 │ 0.6035823925807031 │
│ 321 │ 13824.31640625 │ 0.6023459407663738 │
│ 322 │ 13867.3828125 │ 0.6011202204798132 │
│ 323 │ 13910.44921875 │ 0.5999051429763976 │
│ 324 │ 13953.515625 │ 0.5987006206885245 │
│ 325 │ 13996.58203125 │ 0.5975065672078168 │
│ 326 │ 14039.6484375 │ 0.5963228972677794 │
│ 327 │ 14082.71484375 │ 0.5951495267266942 │
│ 328 │ 14125.78125 │ 0.5939863725508676 │
│ 329 │ 14168.84765625 │ 0.592833352798187 │
│ 330 │ 14211.9140625 │ 0.5916903866019808 │
│ 331 │ 14254.98046875 │ 0.5905573941551976 │
│ 332 │ 14298.046875 │ 0.5894342966948477 │
│ 511 │ 22006.93359375 │ 0.5015789716443024 │
└─────────┴────────────────┴────────────────────┘
At the moment I don't understand:
I would expect an array with values for each frequency in the spectrum. E.g. 0.2 for 40Hz, 0.3 for 41Hz and so on... but I get an array with the half of the sample length.
The maximum frequency that can be represented without distortion is not equal the sampling frequency. It's half of it:
https://en.wikipedia.org/wiki/Nyquist_frequency

How to setup Vue and webpack in my existing Express multi page project?

guys
I need a help regarding using Vue and webpack with my existing project.
I have built my website using nodejs express server among with ejs as templates.
actually, I want to change the front end of my website totally. And I wanna use Vuetify as the primary frontend framework. But I have never implemented web pack in an existing project. can anyone please help me to do it?
My project file structure is following:
├───includs
│ func.js
│ mailer.js
│ middlewares.js
│
├───models
│ BruteForceSchema.js
│ coupon.js
│ invoice.js
│ payment.js
│ product.js
│ services.js
│ statement.js
│ ticket.js
│ user.js
│
├───routs
│ │ auth.js
│ │ footer.js
│ │ index.js
│ │ products.js
│ │ profile.js
│ │
│ ├───admin
│ │ coupon.js
│ │ index.js
│ │ plans.js
│ │ recharge.js
│ │ services.js
│ │ tickets.js
│ │ users.js
│ │
│ └───clientarea
│ hostings.js
│ index.js
│ recharge.js
│ statement.js
│ tickets.js
│
└───views
│ 404.ejs
│ index.ejs
│
├───admin
│ │ activeService.ejs
│ │ editplan.ejs
│ │ editservice.ejs
│ │ index.ejs
│ │ newplan.ejs
│ │ plans.ejs
│ │ recharge.ejs
│ │ services.ejs
│ │ users.ejs
│ │ viewplan.ejs
│ │ viewservice.ejs
│ │
│ ├───coupon
│ │ addnew.ejs
│ │ edit.ejs
│ │ index.ejs
│ │ view.ejs
│ │
│ ├───tickets
│ │ index.ejs
│ │ view.ejs
│ │
│ └───users
│ editUser.ejs
│ userlist.ejs
│ viewUser.ejs
│
├───auth
│ login.ejs
│ newpassword.ejs
│ resetpwd.ejs
│ signup.ejs
│
├───clientarea
│ │ head.ejs
│ │ hostings.ejs
│ │ index.ejs
│ │ recharge.ejs
│ │ recharge_old.ejs
│ │ statement.ejs
│ │ viewhosting.ejs
│ │
│ └───payments
│ proceed.ejs
│
├───email
│ │ resetdone.ejs
│ │ welcome.ejs
│ │
│ └───searvice
│ renewDone.ejs
│ renewFailed.ejs
│
├───footer
│ aboutus.ejs
│ contactus.ejs
│ development.ejs
│ privacy.ejs
│ tos.ejs
│
├───inc
│ admincpsider.ejs
│ footer.ejs
│ func.ejs
│ header.ejs
│ usercpsider.ejs
│
├───products
│ buy.ejs
│ filehosting.ejs
│ index.ejs
│ info.ejs
│ shared-hosting.ejs
│ wordpress.ejs
│
├───profile
│ edit.ejs
│ index.ejs
│
└───tickets
index.ejs
newTicket.ejs
view.ejs

rimraf. recursively remove files and folders except specific folder

├───app
│ ├───modules
│ │ ├───asd
│ │ │ ├───angular
│ │ │ │ ├───src
│ │ │ │ └───test
│ │ │ ├───app
│ │ │ │ └───src
│ │ │ ├───base
│ │ │ │ └───src
│ │ │ │ └───client
│ │ │ ├───common
│ │ │ │ ├───json
│ │ │ │ │ ├───src
│ │ │ │ │ └───test
│ │ │ │ ├───src
│ │ │ │ └───test
│ │ │ └───test
│ │ ├───core
│ │ │ ├───base
│ │ │ │ ├───docs
│ │ │ │ └───src
│ │ │ ├───ui
│ │ │ │ ├───directives
│ │ │ │ │ └───src
│ │ │ │ │ └───bmbDropdownMenu
│ │ │ │ │ └───css
│ │ │ │ ├───directives.CategorizedList
│ │ │ │ │ └───docs
│ │ │ │ ├───directives.noResults
│ │ │ │ │ └───lang
│ │ │ │ └───directives.popover
│ │ │ │ └───docs
Is it possible to remove all files and directories inside "app/modules" exception folder "core" via rimraf or another module?
I'm trying like this (doesn't work):
rimraf('./app/modules/(!(core)|**)/*.*', callback);
I spent a lot of time on this, only to figure out that rimraf doesn't support globbing.
I went with del instead, which supports the same format as gulp (an array of paths, including negated ones).
var globby = require('globby');
var rimraf = require('rimraf');
globby(['*', '!app/modules/core.js'])
.then(function then(paths) {
paths.map(function map(item) {
rimraf.sync(item);
});
});
For more info, see the globby documentation
Credit: vladimir-starkov

How to easily verify correct npm dependencies installed?

How can I know when to prompt user to run npm install if there are any unmet package.json dependencies?
I would like to do this, because if any require() fails, the user gets a poor error message:
module.js:340
throw err;
^
Error: Cannot find module 'nopt'
I've previously tried to just check for the existence of a node_modules directory, but this only works effectively for fresh git clones. I've also tried just requiring npm and running npm install as part of load, but that is very heavy weight.
I'm hoping there is a lighter weight library out there that just parses package.json and makes sure node_modules contents satisfy the requirements.
One idea was to use process.on('uncaughtException') to catch only module import errors, but looking to see if there is a "standard" solution first.
You can use yarn and do yarn check --verify-tree (you can continue using npm for everything else)
Found this today. Not sure if your still need this.
https://www.npmjs.com/package/check-dependencies
npm install check-dependencies --save-dev
Install this package and save to your package.json.
require('check-dependencies')(config, callback);
config is the following object, which is then passed to the callback.
{
status: number, // 0 if successful, 1 otherwise
depsWereOk: boolean, // true if dependencies were already satisfied
log: array, // array of logged messages
error: array, // array of logged errors
}
npm ls will report missing packages when run from the project folder.
npm-ls documentation
This might have issues if you're using git dependencies, though. (Thanks #gman).
Another solution
function dependenciesNeedUpdating() {
const childProcess = require('child_process');
const result = JSON.parse(childProcess.execSync('npm install --dry-run --json').toString());
return result.added.length > 0 || result.updated.length > 0 || result.removed > 0;
}
Call it like this
if (dependenciesNeedUpdating()) {
console.error('dependencies need updating. Please run `npm install`');
process.exit(1);
}
If you want to install this as a dependency 🤣 its 5 lines are in an npm package here. It adds a ld-check-dependencies command (the 4 usage lines above) so you can use it directly in your npm scripts. Example:
"scripts": {
"build": "ld-check-dependencies && rollup ..."
},
Rationale:
I'm learning that dependencies are a huge liability and should be avoided any time there is a simpler solution.
For example here's is the ridiculous dependency tree for check-dependencies
└─┬ check-dependencies#1.1.0
├─┬ bower-config#1.4.3
│ ├── graceful-fs#4.2.4
│ ├── minimist#0.2.1 extraneous
│ ├── mout#1.2.2
│ ├─┬ osenv#0.1.5
│ │ ├── os-homedir#1.0.2
│ │ └── os-tmpdir#1.0.2
│ ├─┬ untildify#2.1.0
│ │ └── os-homedir#1.0.2 deduped
│ └── wordwrap#0.0.3
├─┬ chalk#2.4.2
│ ├─┬ ansi-styles#3.2.1
│ │ └─┬ color-convert#1.9.3
│ │ └── color-name#1.1.3
│ ├── escape-string-regexp#1.0.5
│ └─┬ supports-color#5.5.0
│ └── has-flag#3.0.0
├─┬ findup-sync#2.0.0
│ ├── detect-file#1.0.0
│ ├─┬ is-glob#3.1.0
│ │ └── is-extglob#2.1.1
│ ├─┬ micromatch#3.1.10
│ │ ├── arr-diff#4.0.0
│ │ ├── array-unique#0.3.2
│ │ ├─┬ braces#2.3.2
│ │ │ ├── arr-flatten#1.1.0
│ │ │ ├── array-unique#0.3.2 deduped
│ │ │ ├── extend-shallow#2.0.1 extraneous
│ │ │ ├─┬ fill-range#4.0.0
│ │ │ │ ├── extend-shallow#2.0.1 extraneous
│ │ │ │ ├─┬ is-number#3.0.0
│ │ │ │ │ └── kind-of#3.2.2 extraneous
│ │ │ │ ├── repeat-string#1.6.1
│ │ │ │ └─┬ to-regex-range#2.1.1
│ │ │ │ ├── is-number#3.0.0 deduped
│ │ │ │ └── repeat-string#1.6.1 deduped
│ │ │ ├── isobject#3.0.1
│ │ │ ├── repeat-element#1.1.3
│ │ │ ├── snapdragon#0.8.2 deduped
│ │ │ ├─┬ snapdragon-node#2.1.1
│ │ │ │ ├── define-property#1.0.0 extraneous
│ │ │ │ ├── isobject#3.0.1 deduped
│ │ │ │ └─┬ snapdragon-util#3.0.1
│ │ │ │ └── kind-of#3.2.2 extraneous
│ │ │ ├─┬ split-string#3.1.0
│ │ │ │ └── extend-shallow#3.0.2 deduped
│ │ │ └── to-regex#3.0.2 deduped
│ │ ├─┬ define-property#2.0.2
│ │ │ ├── is-descriptor#1.0.2 extraneous
│ │ │ └── isobject#3.0.1 deduped
│ │ ├─┬ extend-shallow#3.0.2
│ │ │ ├── assign-symbols#1.0.0
│ │ │ └── is-extendable#1.0.1 extraneous
│ │ ├─┬ extglob#2.0.4
│ │ │ ├── array-unique#0.3.2 deduped
│ │ │ ├── define-property#1.0.0 extraneous
│ │ │ ├─┬ expand-brackets#2.1.4
│ │ │ │ ├── debug#2.6.9 deduped
│ │ │ │ ├── define-property#0.2.5 extraneous
│ │ │ │ ├── extend-shallow#2.0.1 extraneous
│ │ │ │ ├── posix-character-classes#0.1.1
│ │ │ │ ├── regex-not#1.0.2 deduped
│ │ │ │ ├── snapdragon#0.8.2 deduped
│ │ │ │ └── to-regex#3.0.2 deduped
│ │ │ ├── extend-shallow#2.0.1 extraneous
│ │ │ ├── fragment-cache#0.2.1 deduped
│ │ │ ├── regex-not#1.0.2 deduped
│ │ │ ├── snapdragon#0.8.2 deduped
│ │ │ └── to-regex#3.0.2 deduped
│ │ ├─┬ fragment-cache#0.2.1
│ │ │ └── map-cache#0.2.2
│ │ ├── kind-of#6.0.3
│ │ ├─┬ nanomatch#1.2.13
│ │ │ ├── arr-diff#4.0.0 deduped
│ │ │ ├── array-unique#0.3.2 deduped
│ │ │ ├── define-property#2.0.2 deduped
│ │ │ ├── extend-shallow#3.0.2 deduped
│ │ │ ├── fragment-cache#0.2.1 deduped
│ │ │ ├── is-windows#1.0.2
│ │ │ ├── kind-of#6.0.3 deduped
│ │ │ ├── object.pick#1.3.0 deduped
│ │ │ ├── regex-not#1.0.2 deduped
│ │ │ ├── snapdragon#0.8.2 deduped
│ │ │ └── to-regex#3.0.2 deduped
│ │ ├─┬ object.pick#1.3.0
│ │ │ └── isobject#3.0.1 deduped
│ │ ├─┬ regex-not#1.0.2
│ │ │ ├── extend-shallow#3.0.2 deduped
│ │ │ └─┬ safe-regex#1.1.0
│ │ │ └── ret#0.1.15
│ │ ├─┬ snapdragon#0.8.2
│ │ │ ├─┬ base#0.11.2
│ │ │ │ ├─┬ cache-base#1.0.1
│ │ │ │ │ ├─┬ collection-visit#1.0.0
│ │ │ │ │ │ ├─┬ map-visit#1.0.0
│ │ │ │ │ │ │ └── object-visit#1.0.1 deduped
│ │ │ │ │ │ └─┬ object-visit#1.0.1
│ │ │ │ │ │ └── isobject#3.0.1 deduped
│ │ │ │ │ ├── component-emitter#1.3.0 deduped
│ │ │ │ │ ├── get-value#2.0.6
│ │ │ │ │ ├─┬ has-value#1.0.0
│ │ │ │ │ │ ├── get-value#2.0.6 deduped
│ │ │ │ │ │ ├─┬ has-values#1.0.0
│ │ │ │ │ │ │ ├── is-number#3.0.0 deduped
│ │ │ │ │ │ │ └── kind-of#4.0.0 extraneous
│ │ │ │ │ │ └── isobject#3.0.1 deduped
│ │ │ │ │ ├── isobject#3.0.1 deduped
│ │ │ │ │ ├─┬ set-value#2.0.1
│ │ │ │ │ │ ├── extend-shallow#2.0.1 extraneous
│ │ │ │ │ │ ├── is-extendable#0.1.1
│ │ │ │ │ │ ├─┬ is-plain-object#2.0.4
│ │ │ │ │ │ │ └── isobject#3.0.1 deduped
│ │ │ │ │ │ └── split-string#3.1.0 deduped
│ │ │ │ │ ├─┬ to-object-path#0.3.0
│ │ │ │ │ │ └── kind-of#3.2.2 extraneous
│ │ │ │ │ ├─┬ union-value#1.0.1
│ │ │ │ │ │ ├── arr-union#3.1.0 deduped
│ │ │ │ │ │ ├── get-value#2.0.6 deduped
│ │ │ │ │ │ ├── is-extendable#0.1.1 deduped
│ │ │ │ │ │ └── set-value#2.0.1 deduped
│ │ │ │ │ └─┬ unset-value#1.0.0
│ │ │ │ │ ├── has-value#0.3.1 extraneous
│ │ │ │ │ └── isobject#3.0.1 deduped
│ │ │ │ ├─┬ class-utils#0.3.6
│ │ │ │ │ ├── arr-union#3.1.0
│ │ │ │ │ ├── define-property#0.2.5 extraneous
│ │ │ │ │ ├── isobject#3.0.1 deduped
│ │ │ │ │ └─┬ static-extend#0.1.2
│ │ │ │ │ ├── define-property#0.2.5 extraneous
│ │ │ │ │ └─┬ object-copy#0.1.0
│ │ │ │ │ ├── copy-descriptor#0.1.1
│ │ │ │ │ ├── define-property#0.2.5 extraneous
│ │ │ │ │ └── kind-of#3.2.2 extraneous
│ │ │ │ ├── component-emitter#1.3.0
│ │ │ │ ├── define-property#1.0.0 extraneous
│ │ │ │ ├── isobject#3.0.1 deduped
│ │ │ │ ├─┬ mixin-deep#1.3.2
│ │ │ │ │ ├── for-in#1.0.2
│ │ │ │ │ └── is-extendable#1.0.1 extraneous
│ │ │ │ └── pascalcase#0.1.1
│ │ │ ├─┬ debug#2.6.9
│ │ │ │ └── ms#2.0.0
│ │ │ ├── define-property#0.2.5 extraneous
│ │ │ ├── extend-shallow#2.0.1 extraneous
│ │ │ ├── map-cache#0.2.2 deduped
│ │ │ ├── source-map#0.5.7
│ │ │ ├─┬ source-map-resolve#0.5.3
│ │ │ │ ├── atob#2.1.2
│ │ │ │ ├── decode-uri-component#0.2.0
│ │ │ │ ├── resolve-url#0.2.1
│ │ │ │ ├── source-map-url#0.4.0
│ │ │ │ └── urix#0.1.0
│ │ │ └── use#3.1.1
│ │ └─┬ to-regex#3.0.2
│ │ ├── define-property#2.0.2 deduped
│ │ ├── extend-shallow#3.0.2 deduped
│ │ ├── regex-not#1.0.2 deduped
│ │ └── safe-regex#1.1.0 deduped
│ └─┬ resolve-dir#1.0.1
│ ├─┬ expand-tilde#2.0.2
│ │ └─┬ homedir-polyfill#1.0.3
│ │ └── parse-passwd#1.0.0
│ └─┬ global-modules#1.0.0
│ ├─┬ global-prefix#1.0.2
│ │ ├── expand-tilde#2.0.2 deduped
│ │ ├── homedir-polyfill#1.0.3 deduped
│ │ ├── ini#1.3.5
│ │ ├── is-windows#1.0.2 deduped
│ │ └─┬ which#1.3.1
│ │ └── isexe#2.0.0
│ ├── is-windows#1.0.2 deduped
│ └── resolve-dir#1.0.1 deduped
├── lodash.camelcase#4.3.0
├── minimist#1.2.5
└── semver#5.7.1
788 js files and 48000 lines of code.
Everyone one of those dependencies is another chance for you to be told something is broke, deprecated, there's a new vulnerability, etc. Basically by using something with so many dependencies you're adding to your workload when you could instead be spending time shipping, dancing, playing with your kids, whatever. You thought you were saving time by using a dependency but if it's got so many dependencies than you're not saving time if you compare to a lower or simple no dependency option because you will be forever dealing with the issues of those dependencies.
Here's the dependency tree for deps-ok which is much more reasonable.
└─┬ deps-ok#1.4.1
├── check-more-types#2.24.0
├─┬ debug#3.1.0
│ └── ms#2.0.0
├── lazy-ass#1.6.0
├── lodash#4.17.10
├── minimist#1.2.0
├─┬ q#2.0.3
│ ├── asap#2.0.6
│ ├── pop-iterate#1.0.1
│ └── weak-map#1.0.5
├── quote#0.4.0
└── semver#5.5.0
But it still manages to demonstrate the problem with dependencies
❯ npm audit
=== npm audit security report ===
┌──────────────────────────────────────────────────────────────────────────────┐
│ Manual Review │
│ Some vulnerabilities require your attention to resolve │
│ │
│ Visit https://go.npm.me/audit-guide for additional guidance │
└──────────────────────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ High │ Prototype Pollution │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ lodash │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in │ >=4.17.11 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ deps-ok │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ deps-ok > lodash │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://npmjs.com/advisories/782 │
└───────────────┴──────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ High │ Prototype Pollution │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ lodash │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in │ >=4.17.12 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ deps-ok │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ deps-ok > lodash │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://npmjs.com/advisories/1065 │
└───────────────┴──────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low │ Prototype Pollution │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ lodash │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in │ >=4.17.19 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ deps-ok │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ deps-ok > lodash │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://npmjs.com/advisories/1523 │
└───────────────┴──────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low │ Prototype Pollution │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ minimist │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in │ >=0.2.1 <1.0.0 || >=1.2.3 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ deps-ok │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ deps-ok > minimist │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://npmjs.com/advisories/1179 │
└───────────────┴──────────────────────────────────────────────────────────────┘
found 4 vulnerabilities (2 low, 2 high) in 13 scanned packages
4 vulnerabilities require manual review. See the full report for details.
Now of course maybe you don't care about those vulnerabilities because this script is only used during building but now you have a new problem that any real vulnerabilities will be buried in these ones you don't care about.
I have no idea how robust or comprehensive the solution above is. The good thing is it relies on npm so whatever npm install is going to do it's doing that exact thing.
Not sure there is npm way to do it, but just found this seems helpful:
blog post: http://bahmutov.calepin.co/check-dependencies-in-grunt-by-default.html
project: https://github.com/bahmutov/deps-ok

Resources