How to import and utilize P5.Sound in Vue? - node.js

I have been trying to make a music visualizer app using Vue and P5, and after tinkering with P5 using this article as my guide (https://medium.com/js-dojo/experiment-with-p5-js-on-vue-7ebc05030d33), I managed to get a Canvas rendered with some cool looking graphics.
Now, I am trying to create a link between the waveform/amplitude of a given song and the visuals rendered in the canvas. I have been trying to get the constructors/functions from the P5.sound library to load a song from a file path, and then use the output from a FFT object to control the visuals rendering in the canvas.
Now, my research has indicated that the P5 library must be run in instance mode in order to function (https://github.com/processing/p5.js/wiki/Global-and-instance-mode), and I have done my very best to adhere to this approach in my Vue project. But although the visual rendering works, none of the P5.sound functionalities do.
Here is the code to my model which sets up the P5 objects:
import P5 from 'p5';
import P5sound from "p5/lib/addons/p5.sound";
let p5;
let fft;
let sound;
export function main(_p5) {
p5 = _p5;
p5.setup = () => {
p5.createCanvas(500, 500);
p5.background(100);
fft = new p5.FFT();
fft.setInput("../assets/sawtooth.mp3")
sound.amp(0.2);
};
p5.draw = () => {
p5.background(220);
let spectrum = fft.analyze();
noStroke();
fill(255, 0, 255);
for (let i = 0; i < spectrum.length; i++) {
let x = map(i, 0, spectrum.length, 0, width);
let h = -height + map(spectrum[i], 0, 255, height, 0);
rect(x, height, width / spectrum.length, h);
}
let waveform = fft.waveform();
noFill();
beginShape();
stroke(20);
for (let i = 0; i < waveform.length; i++){
let x = map(i, 0, waveform.length, 0, width);
let y = map( waveform[i], -1, 1, 0, height);
vertex(x,y);
}
endShape();
};
}
Then, in my MusicVisualization.vue component, I call this model in the mounted() section of the component:
import P5 from 'p5';
var musicVisualizerModel = require("../models/musicVisualizerModel.js");
export default {
name: "MusicVisualization",
data(){
return{
message: ""
}
},
mounted() {
new P5(musicVisualizerModel.main);
}
};
No matter how I try to import my P5.sound library, the line fft = new p5.FFT() always yields an error p5.FFT is not a constructor. I have checked the P5.js module in my node_modules, and I can clearly see the P5.sound.js library present in the p5/lib/addons/p5.sound file path, but I cannot utilize any of its functionality. I should note that when I remove all P5.sound functionality from the model's code, and only have a canvas with some simple shapes, it works fine.
I installed P5 using npm install --save p5, and have tried numerous approaches to getting P5.sound to work, such as the P5-manager package (https://www.npmjs.com/package/p5-manager/v/0.1.0) and the Vue-P5 wrapper (https://www.npmjs.com/package/vue-p5). I have also tried implementing some of the proposed solutions given in this similar question (Using p5.sound.js in instance mode: 'p5.Amplitude() not a constructor'). None of these work, although I may be implementing incorrectly. Does anybody know how I can import the P5.sound library into my Vue project?
UPDATES
Another error that I noticed if I did not run the fft= new p5.FFT() line was the error ReferenceError: p5 is not defined. This error occurred whenever I tried to import the P5.sound library (with either import P5sound from "p5/lib/addons/p5.sound"; or with import "p5/lib/addons/p5.sound";). I looked deeper into this problem and found others had experienced it as well (https://github.com/processing/p5.js-sound/issues/453). Apparently, one user resolved the issue by "downgrading to 0.9, copying the sound.js into project folder, boosted back into 1.0 and just linked imports to the local, 0.9 version of sound." Sadly, I am unsure how to carry out this solution. Would anybody know how?

Thanks to help I received on the Github issues page (https://github.com/processing/p5.js-sound/issues/453), I figured out how to get the P5.sound library to import.
First, I uninstalled the P5.js in the node_modules, then installed P5.js version 0.9.0:
npm uninstall p5
npm install p5#0.9.0
Next, after locating the P5 module in node_modules, I copied the P5 sound lib file and pasted it into my project's src directory, and then replaced the import P5sound from "p5/lib/addons/p5.sound" with import "../p5.sound.js" (which represented my relative file path). Then I installed the latest version of P5.js in the terminal with the following command:
npm install p5#1
And now I can import the sound library without getting the errors ReferenceError: p5 is not defined or p5.FFT is not a constructor.

The problem is that p5-sound relies on p5 globally available.
If you are using webpack, that can be solved easily by using webpack's ProvidePlugin, like so:
plugins: [
new webpack.ProvidePlugin({
p5: 'p5',
})
]
Then you should be able to import via:
import p5 from 'p5';
import 'p5/lib/addons/p5.sound';

I had a similar problem using p5 in react, but I was able to get this working (for the time being 😅) by including the p5 scripts in my html file.
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.1/lib/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.1/lib/addons/p5.sound.min.js"></script>
And then when creating my p5 instance in React I call:
new window.p5(sketch, node);
My sketch file looks something like:
const sketch = (p, props) => {
p.preload = () => {
...
};
p.setup = function () {
...
};
p.draw = () => {
...
};
};
export default sketch;
In my sketch logic, I use the instance p variable for the standard p5.js functionality like drawing shapes. Whenever I need to use a constructor of a p5 class like AudioIn, Amplitude, SoundFile, etc., I create it using the global p5 variable. For example:
new window.p5.AudioIn()
I believe this is necessary because the p5 sound addon library adds functions and classes onto the global p5 variable. When I was using es6 imports, there must have been some mess where the sound addon was not being added correctly to the imported p5 class.
One day there will hopefully be a better es6 module system for p5.js, but this seems to do the trick for me.

Related

How to make a ES6 class visible for both the server and client scripts in Node JS?

So, I have 2 classes for shapes in a script that's called shapes.js
class Shape {
constructor(x, y) {
this.x = x;
this.y = y
}
}
class Cube extends Shape {
constructor(x, y, t) {
super(x, y);
this.t = t;
}
}
How do I import both of these in my server.js or other js files? I know Shape is nothing more than an abstract class right now, but I want to test the functionality of importing multiple classes. I have tried doing it in the following ways:
var shape = require('/shapes');
var Shape = shape.Shape, Cube = shape.Cube;
or
import {Shape, Cube} from 'shapes'
import {Shape, Cube} from '/shapes'
I have also tried exporting them in shapes.js at the end of the file like this :
module.exports = {Shape, Cube}
or
export {Shape, Cube}
I've tried all the possibilities I've been provided within the basic tutorials, the result is either an error or a blank white screen with no errors. I have been really stuck on this one, would appreciate some help, thank you
I encourage you to use the ES Module syntax :
import {Shape, Cube} from 'shapes' to import a module
export {Shape, Cube} to export a module
Most browsers support this.Unfortunately! Node.js suppport ES6 but doesn't support ESModule syntax (or only experimental way)
So you need to transpile your code with babeljs and this plugin
Here the .babelrc file to configure babel:
{
"plugins": ["transform-es2015-modules-commonjs"]
}
If you use babel-register the transformation occurs when the file is required(imported)
The module.exports syntax is the best way to export your code. However, the best way to import them differs between Node and browsers.
module.exports = {Shape, Cube}; // in file with classes defined
// below are different ways to import
const { Shape, Cube } = require('./shapes'); // Node
import { Shape, Cube } from './shapes'; // modern browsers
The Node line has something called a destructuring statement. It just prevents you from having to define both shape and Shape/Cube.
You said in your question that you tried both module.exports and a require statement. My guess is that you either a) didn't try both at the same time or b) without the ./ in your require statement, the program couldn't find your file.
Note that the export/import keyword do not work in Node - they work only in modern browsers. As other answers have noted, there are ways to make those work. However, I would generally recommend against them for smaller scale projects, particularly if you're just getting familiar with this stuff.
In my last projects I used Typescript with node.js it's very powerful so I use namespace way :
shapes.ts :
export namespace Shapes {
export class A {
constructor(x, y) {
this.x = x;
this.y = y
}
}
export class B {
constructor(x, y) {
this.x = x;
this.y = y
}
}
}
usage :
import Shapes from 'shapes'
let shapeA = new Shapes.A();

Including d3 graph in Angular 4 application

I am building an Angular application with typescript, where I would like to display the force directed network graph from Mike Boston build with d3 shown here.
I have copied and translated most of the code to type script without trouble but the line
. force("link", d3.forceLink().id(function(d) { return d.id; }))
fails with the error {} has no property id.
Only the following lines referring to d.source.x works fine?
I have installed d3 with npm and also types/d3 but still no luck, even though the typings.d.ts has an interface defining the id.
Thank you for your help!
Maybe that is something related to versioning, if you are using TypeScript 2.4.1, try to downgrade to 2.3.4 and give it a go.
As a workaround you could use any:
const forceLink: any = d3.forceLink();
. force("link", forceLink.id(function(d) { return d.id; }))
The example given at http://plnkr.co/edit/qcESHb3cCwD6NZL1yuhX?p=preview helped me, with focus on code shown here:
this.simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
this.render(miserables);
This is the best rendition I have seen. Works for me.

Unexpected node type error SequenceExpression with jest

I was adding a snapshot test to a piece of React code, and I incurred in this error:
Unexpected node type: SequenceExpression (This is an error on an internal node. Probably an internal error. Location has been estimated.)
The code transpiles and works just fine, and the AST explorer doesn't warn me about anything.
Before this new test, no other test gave me any sort of similar error, and we have quite a few of them in our codebase.
I tried to reinstall jest, reinstall babel-jest, remove and reinstall all modules (using yarn --pure-lock), upgraded both jest and babel-jest to the latest version (20.0.1 for both), rinsed and repeated.
Nothing worked.
This occurs only when I try to collect the coverage (with --coverage), while the minimal snippet it occurs with is:
import { tint } from 'polished'
import styled from 'styled-components'
export default styled.label`
background: ${({ x, y }) => (x ? tint(0.3, y.a) : y.b)};
`
Here's what i've found:
This is an issue with jest code coverage being able to understand styled components and polished. I am using babel-plugin-polished with the following in my babelrc:
"plugins": [ "polished" ]
But still if you call export on a value, and do not also use that value in an object or exported object, it will fail.
Fails:
export const charcoalBlue = rgb(104, 131, 145);
Doesn't fail:
export const charcoalBlue = rgb(104, 131, 145);
const colors = { charcoalBlue }
So my solution has been to ignore my style files, or simply ensure I'm using the values I create and not just exporting them.
One way to ignore the style files, place this in your package.json:
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!**/*.styles.js",
]
}
And name your style files {ComponentName}.styles.js
Hope this helps!
I came across the same issue!
I fixed it by working around it:
import styled, {css} from 'styled-components';
styled.label`
${({x,y}) => x
? css`background: tint(0.3, y.a);`
: css`background: ${y.b};`
}
`;

I need something like this, but for Phaser

i need something for adding object in Phaser, this is something similar but in wade.
wade.addSceneObject(new SceneObject(dotSprite, 0, dotPosition.x, dotPosition.y));
Adding objects (usually called sprites) in phaser is super simple. Just load the image in the preloader function
function preload() {
game.load.image('mushroom', 'assets/sprites/mushroom2.png');
}
And then add the sprite in the create function
function create() {
// This simply creates a sprite using the mushroom image we loaded above and positions it at 200 x 200
var test = game.add.sprite(200, 200, 'mushroom');
}
Phaser has a ton of documentation on how to do things like this. I got the code from this example.
If you are completely new to phaser I highly recommend going through their tutorial

Can I use HaxeUI with HaxeFlixel?

I tried to use both HaxeUI and HaxeFlixel, but what I obtain is HaxeUI's interface over a white background, covering everything underneath. Moreover, even if it was possible to somewhat make HaxeUI and HaxeFlixel work together, it's not clear how to change the UI of HaxeUI when the state change in HaxeFlixel. Here is the code I used:
private function setupGame():Void {
Toolkit.theme = new GradientTheme();
Toolkit.init();
var stageWidth:Int = Lib.current.stage.stageWidth;
var stageHeight:Int = Lib.current.stage.stageHeight;
if (zoom == -1) {
var ratioX:Float = stageWidth / gameWidth;
var ratioY:Float = stageHeight / gameHeight;
zoom = Math.min(ratioX, ratioY);
gameWidth = Math.ceil(stageWidth / zoom);
gameHeight = Math.ceil(stageHeight / zoom);
}
trace('stage: ${stageWidth}x${stageHeight}, game: ${gameWidth}x${gameHeight}, zoom=$zoom');
addChild(new FlxGame(gameWidth, gameHeight, initialState, zoom, framerate, framerate, skipSplash, startFullscreen));
Toolkit.openFullscreen(function(root:Root) {
var view:IDisplayObject = Toolkit.processXmlResource("assets/xml/haxeui-resource.xml");
root.addChild(view);
});
}
I can guess that, probably, both HaxeUI and HaxeFlixel have their own main loop and that their event handling might not be compatible, but just in case, can someone have a more definitive answer?
Edit:
Actually, it's much better when using openPopup:
Toolkit.openPopup( { x:20, y:150, width:100, height:100 }, function(root:Root) {
var view:IDisplayObject = Toolkit.processXmlResource("assets/xml/haxeui-naming.xml");
root.addChild(view);
});
It's possible to interact with the rest of the screen (managed with HaxeFlixel), but the mouse pointer present in the part of the screen managed with HaxeFlixel remains under the HaxeUI user interface elements.
When using Flixel and HaxeUI together, its almost like running two applications at once. However, they both rely on OpenFL as a back-end and each attach themselves to its display tree.
One technique I'm experimenting with right now is to open a Flixel sub state, and within the sub state, call Toolkit.openFullscreen(). From inside of this, you can set the alpha of the root's background to 0, which allows you to see through it onto the underlying bitmap that Flixel uses to render.
Here is a minimal example of how you might "embed" an editor interface inside a Flixel sub state:
import haxe.ui.toolkit.core.Toolkit;
import haxe.ui.toolkit.core.RootManager;
import haxe.ui.toolkit.themes.DefaultTheme;
import flixel.FlxG;
import flixel.FlxSubState;
// This would typically be a Haxe UI XMLController
import app.MainEditor;
class HaxeUIState extends FlxSubState
{
override public function create()
{
super.create();
// Flixel uses a sprite-based cursor by default,
// so you need to enable the system cursor to be
// able to see what you're clicking.
FlxG.mouse.useSystemCursor = true;
Toolkit.theme = new DefaultTheme();
Toolkit.init();
Toolkit.openFullscreen(function (root) {
var editor = new MainEditor();
// Allows you to see what's going on in the sub state
root.style.backgroundAlpha = 0;
root.addChild(editor.view);
});
}
override public function destroy()
{
super.destroy();
// Switch back to Flixel's cursor
FlxG.mouse.useSystemCursor = true;
// Not sure if this is the "correct" way to close the UI,
// but it works for my purposes. Alternatively you could
// try opening the editor in advance, but hiding it
// until the sub-state opens.
RootManager.instance.destroyAllRoots();
}
// As far as I can tell, the update function continues to get
// called even while Haxe UI is open.
override public function update() {
super.update();
if (FlxG.keys.justPressed.ESCAPE) {
// This will implicitly trigger destroy().
close();
}
}
}
In this way, you can associate different Flixel states with different Haxe UI controllers. (NOTE: They don't strictly have to be sub-states, that's just what worked best in my case.)
When you open a fullscreen or popup with haxeui, the program flow will be blocked (your update() and draw() function won't be called). You should probably have a look at flixel-ui instead.
From my experience haxeflixel and haxeui work well together but they are totally independent projects, and as such, any coordination between flixel states and displayed UI must be added by the coder.
I don't recall having the white background problem you mention, it shouldn't happen unless haxeui root sprite has a solid background, in that case it should be addressed to haxeui project maintainer.

Resources