It seems maps in haxe are extremely slow vs dynamic objects
I would avoid them.
So using this code:
var nd=()->{
//var op:Dynamic = {x:100,y:1000};
//op.z = 22;
var op = {x:100,y:1000,z:22}
//var op = ['x'=>100,'y'=>1000];
//op['z'] = 22;
var i;
for(i in 0...1000000)
{
/*
op['x']++;
op['y']--;
op['z']++;
*/
op.x++;
op.y--;
op.z++;
}
trace('Line');
}
var j;
var q:Float = haxe.Timer.stamp();
for(j in 0...100) nd();
trace(haxe.Timer.stamp()-q);
Maps: 19 second
Dynamic Object: 9 seconds
Object: 0.6 seconds
It's amazing how slow maps are
It's not that maps are slow, it's that your test doesn't consider compiler optimizations. And it seems like it was ran in Debug mode?
Let's take a look at a slightly more verbose test (10x fewer iterations, shuffled order and averages):
import haxe.DynamicAccess;
class Main {
static inline var times = 10000;
static function testInline() {
var o = { x: 100, y: 1000, z: 22 };
for (_ in 0 ... times) {
o.x++;
o.y--;
o.z++;
}
}
static function getClass() {
return new Vector(100, 1000, 22);
}
static function testClass() {
var o = getClass();
for (_ in 0 ... times) {
o.x++;
o.y--;
o.z++;
}
}
static function testClassDynamic() {
var o:Dynamic = getClass();
for (_ in 0 ... times) {
o.x++;
o.y--;
o.z++;
}
}
static function getObj() {
return { x: 100, y: 1000, z: 22 };
}
static function testObj() {
var o = getObj();
for (_ in 0 ... times) {
o.x++;
o.y--;
o.z++;
}
}
static function testDynamic() {
var o:Dynamic = { x: 100, y: 1000, z: 22 };
for (_ in 0 ... times) {
o.x++;
o.y--;
o.z++;
}
}
static function testDynamicPlus() {
var o:Dynamic = { };
o.x = 100;
o.y = 1000;
o.z = 22;
for (_ in 0 ... times) {
o.x++;
o.y--;
o.z++;
}
}
static function testDynamicAccess() {
var o:DynamicAccess<Int> = getObj();
for (_ in 0 ... times) {
o["x"]++;
o["y"]--;
o["z"]++;
}
}
static function testMapString() {
var o = ["x" => 100, "y" => 1000, "z" => 22];
for (_ in 0 ... times) {
o["x"]++;
o["y"]--;
o["z"]++;
}
}
static function testMapInt() {
var o = [100 => 100, 200 => 1000, 300 => 22];
for (_ in 0 ... times) {
o[100]++;
o[200]--;
o[300]++;
}
}
static function shuffleSorter(a, b) {
return Math.random() > 0.5 ? 1 : -1;
}
static function main() {
var tests = [
new Test("inline", testInline),
new Test("class", testClass),
new Test("object", testObj),
new Test("object:Dynamic", testDynamic),
new Test("class:Dynamic", testClassDynamic),
new Test("object:Dynamic+", testDynamicPlus),
new Test("DynamicAccess", testDynamicAccess),
new Test("Map<String, Int>", testMapString),
new Test("Map<Int, Int>", testMapInt),
];
var shuffle = tests.copy();
var iterations = 0;
while (true) {
iterations += 1;
Sys.println("Step " + iterations);
for (i => v in shuffle) {
var k = Std.random(shuffle.length);
shuffle[i] = shuffle[k];
shuffle[k] = v;
}
for (test in shuffle) {
var t0 = haxe.Timer.stamp();
var fn = test.func;
for (_ in 0 ... 100) fn();
var t1 = haxe.Timer.stamp();
test.time += t1 - t0;
Sys.sleep(0.001);
}
for (test in tests) {
Sys.println('${test.name}: ${Math.ffloor(test.time / iterations * 10e6) / 1e3}ms avg');
}
Sys.sleep(1);
}
}
}
class Test {
public var time:Float = 0;
public var func:Void->Void;
public var name:String;
public function new(name:String, func:Void->Void) {
this.name = name;
this.func = func;
}
public function toString() return 'Test($name)';
}
class Vector {
public var x:Int;
public var y:Int;
public var z:Int;
public function new(x:Int, y:Int, z:Int) {
this.x = x;
this.y = y;
this.z = z;
}
}
And its output after a hundred or so "steps":
inline: 0.011ms avg
class: 15.737ms avg
object: 281.417ms avg
object:Dynamic: 275.509ms avg
class:Dynamic: 233.208ms avg
object:Dynamic+: 1208.83ms avg
DynamicAccess: 1021.248ms avg
Map<String, Int>: 1293.529ms avg
Map<Int, Int>: 916.552ms avg
Let's take a look at what each test compiles to.
Haxe-generated C++ code is formatted for readability
inline
This is what you were testing, although you have evidently suspected something judging by the commented out line.
If it seems suspiciously fast, that's because it is - the Haxe compiler noticed that your object is local and inlined it completely:
void Main_obj::testInline()
{
HX_STACKFRAME(&_hx_pos_e47a9afac0942eb9_5_testInline)
int o_x = 100;
int o_y = 1000;
int o_z = 22;
{
int _g = 0;
while ((_g < 10000))
{
_g = (_g + 1);
int _ = (_g - 1);
o_x = (o_x + 1);
o_y = (o_y - 1);
o_z = (o_z + 1);
}
}
}
Consequently, the C++ compiler might figure out that you aren't really doing anything in this function, at which point the contents are removed:
(whereas if you were to return o.z, the contents would be equivalent to return 10022 instead)
class
Let's talk about the things that you should be doing in a good case scenario.
Known field access on a class instance is very fast because it is compiled to a C++ class with direct field access:
::Vector Main_obj::getClass()
{
HX_GC_STACKFRAME(&_hx_pos_e47a9afac0942eb9_15_getClass)
return ::Vector_obj::__alloc(HX_CTX, 100, 1000, 22);
}
void Main_obj::testClass()
{
HX_STACKFRAME(&_hx_pos_e47a9afac0942eb9_17_testClass)
::Vector o = ::Main_obj::getClass();
{
int _g = 0;
while ((_g < 10000))
{
_g = (_g + 1);
int _ = (_g - 1);
o->x++;
o->y--;
o->z++;
}
}
}
Getting a class from a function call is required to prevent the Haxe compiler from inlining it; the C++ compiler may still collapse the for-loop.
object
Let's prevent the compiler from inlining the anonymous object by returning it from a function.
But it's still faster than a map.
Since dynamic objects are used pretty often (JSON and all), a few tricks are utilized - for instance, if you are creating an anonymous object with a set of predefined fields, additional work will be performed for these so that they can be accessed quicker (seen here as Create(n) and a subsequent chain of setFixed calls):
::Dynamic Main_obj::getObj()
{
HX_STACKFRAME(&_hx_pos_e47a9afac0942eb9_36_getObj)
return ::Dynamic(::hx::Anon_obj::Create(3)
->setFixed(0, HX_("x", 78, 00, 00, 00), 100)
->setFixed(1, HX_("y", 79, 00, 00, 00), 1000)
->setFixed(2, HX_("z", 7a, 00, 00, 00), 22));
}
void Main_obj::testObj()
{
HX_STACKFRAME(&_hx_pos_e47a9afac0942eb9_38_testObj)
::Dynamic o = ::Main_obj::getObj();
{
int _g = 0;
while ((_g < 10000))
{
_g = (_g + 1);
int _ = (_g - 1);
::hx::FieldRef((o).mPtr, HX_("x", 78, 00, 00, 00))++;
::hx::FieldRef((o).mPtr, HX_("y", 79, 00, 00, 00))--;
::hx::FieldRef((o).mPtr, HX_("z", 7a, 00, 00, 00))++;
}
}
}
You can see a handful of these tricks in Anon.cpp and Anon.h.
Dynamic
Same as above, but by typing the variable as Dynamic instead, and without an extra function call. Personally I wouldn't rely on this behaviour.
class:Dynamic
Although the code is effectively identical to above,
void Main_obj::testClassDynamic()
{
HX_STACKFRAME(&_hx_pos_e47a9afac0942eb9_26_testClassDynamic)
::Dynamic o = ::Main_obj::getClass();
{
int _g = 0;
while ((_g < 10000))
{
_g = (_g + 1);
int _ = (_g - 1);
::hx::FieldRef((o).mPtr, HX_("x", 78, 00, 00, 00))++;
::hx::FieldRef((o).mPtr, HX_("y", 79, 00, 00, 00))--;
::hx::FieldRef((o).mPtr, HX_("z", 7a, 00, 00, 00))++;
}
}
}
this runs a little faster. This is a accomplished by pre-generating functions for Reflection that will first check if the variable happens to be one of the predefined ones:
::hx::Val Vector_obj::__Field(const ::String &inName,::hx::PropertyAccess inCallProp)
{
switch(inName.length) {
case 1:
if (HX_FIELD_EQ(inName,"x") ) { return ::hx::Val( x ); }
if (HX_FIELD_EQ(inName,"y") ) { return ::hx::Val( y ); }
if (HX_FIELD_EQ(inName,"z") ) { return ::hx::Val( z ); }
}
return super::__Field(inName,inCallProp);
}
DynamicAccess
Same as Dynamic, but we're also forcing the runtime to jump through a few (unnecessary) hoops with Reflect functions.
void Main_obj::testDynamicAccess()
{
HX_STACKFRAME(&_hx_pos_e47a9afac0942eb9_66_testDynamicAccess)
::Dynamic o = ::Main_obj::getObj();
{
int _g = 0;
while ((_g < 10000))
{
_g = (_g + 1);
int _ = (_g - 1);
{
::String tmp = HX_("x", 78, 00, 00, 00);
{
int value = ((int)((::Reflect_obj::field(o, tmp) + 1)));
::Reflect_obj::setField(o, tmp, value);
}
}
// ...
}
}
}
object:Dynamic+
We can disregard the aforementioned predefined field optimization by creating an empty Dynamic object and then filling it up with fields. This puts us pretty close to Map's performance.
void Main_obj::testDynamicPlus()
{
HX_STACKFRAME(&_hx_pos_e47a9afac0942eb9_55_testDynamicPlus)
::Dynamic o = ::Dynamic(::hx::Anon_obj::Create(0));
o->__SetField(HX_("x", 78, 00, 00, 00), 100, ::hx::paccDynamic);
o->__SetField(HX_("y", 79, 00, 00, 00), 1000, ::hx::paccDynamic);
o->__SetField(HX_("z", 7a, 00, 00, 00), 22, ::hx::paccDynamic);
{
int _g = 0;
while ((_g < 10000))
{
_g = (_g + 1);
int _ = (_g - 1);
::hx::FieldRef((o).mPtr, HX_("x", 78, 00, 00, 00))++;
::hx::FieldRef((o).mPtr, HX_("y", 79, 00, 00, 00))--;
::hx::FieldRef((o).mPtr, HX_("z", 7a, 00, 00, 00))++;
}
}
}
Map<String, Int>
Given that Map is unable to benefit from most of the above contextual optimizations (in fact, many wouldn't make sense for normal use cases), its performance should not be particularly surprising.
void Main_obj::testMapString()
{
HX_GC_STACKFRAME(&_hx_pos_e47a9afac0942eb9_74_testMapString)
::haxe::ds::StringMap _g = ::haxe::ds::StringMap_obj::__alloc(HX_CTX);
_g->set(HX_("x", 78, 00, 00, 00), 100);
_g->set(HX_("y", 79, 00, 00, 00), 1000);
_g->set(HX_("z", 7a, 00, 00, 00), 22);
::haxe::ds::StringMap o = _g;
{
int _g1 = 0;
while ((_g1 < 10000))
{
_g1 = (_g1 + 1);
int _ = (_g1 - 1);
{
::String tmp = HX_("x", 78, 00, 00, 00);
{
int v = ((int)((o->get(tmp) + 1)));
o->set(tmp, v);
}
}
// ...
}
}
}
Map<String, Int>
A bonus: to any or no surprise, computing a hash of an integer is cheaper than doing so for a string.
Conclusions
Don't be hasteful to write your code one or other way based on what a microbenchmark suggests. For example, although this might seem like an in-depth breakdown, it does not account for garbage collection nor optimization differences between various C++ compilers.
Related
Question:
Given an array nums. We define a running sum of an array as runningSum[i] = sum(nums[0]…nums[i]).
Return the running sum of nums.
Code:
class Solution {
public int[] runningSum(int[] nums) {
int[] ans = new int[nums.length];
for(int i = 0; i < nums.length; i++) {
ans[i] = nums[i] + nums[i+1];
}
return ans;
}
}
error:
java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
at line 6, Solution.runningSum
at line 54, __DriverSolution__.__helper__
at line 84, __Driver__.main
Lot of code to post because I can't really tell where the issue is. I am trying to run this on an ESP32 chip and am writing the code in vscode with platformio.
The function
byte _previousPoint(){
for (byte point = 0; point < maxPoints; point ++){ //loop through active points.
if (_points[point + 1].getActive() != 1){
return point;
}
else {
long t1 = point_seconds(point);
long t2 = point_seconds(point + 1);
if (t1 <= currentTime){ //If current time is after this point.
if (t2 > currentTime){ //If current time is less than the next point (it is sandwiched).
return point;
}
}
}
}
return 0;
}
Is not behaving as I expect. And it seems the cause is that the function
bool getActive(){
return _active;
}
under the class point is returning not a bool but ascending numbers for each channel. point.getActive() should return a bool value, and that bool value should default to 0 (as it is set in the class definition), which is why this makes no sense. The point that is returning the erroneous values is the last one in the _points[maxPoints] array for each channel, and that value seems to match the channel numbers (for red it is 0, for green it is 1, for blue it is 2, etc.)
If I print the value _active upon running the point.setActive() function it comes out correctly. So something is wrong with "getting" it later.
Here is full code below. Let me know if you need some clarification because I know it's a lot. And thanks for anybody patient enough to help.
#include <Arduino.h>
long currentTime;
long lastUp;
byte totalChannels = 4;
const byte maxPoints = 3;
class point {
public:
point(){
}
void clear(){
_active = 0;
_day = 0;
_hour = 0;
_minute = 0;
_second = 0;
_intensity = 0;
_mode = 0;
}
bool getActive(){
return _active;
}
uint getDay(){
return _day;
}
byte getHour(){
return _hour;
}
byte getMinute(){
return _minute;
}
byte getSecond(){
return _second;
}
int getIntensity(){
return _intensity;
}
byte getMode(){
return _mode;
}
void setActive(bool active){
_active = active;
}
void setDay(uint day){
_day = day;
}
void setHour(byte hour){
_hour = hour;
}
void setMinute(byte minute){
_minute = minute;
}
void setSecond(byte second){
_second = second;
}
void setIntensity(byte intensity){
_intensity = intensity;
}
void setMode(byte mode){
_mode = mode;
}
private:
bool _active = 0;
uint _day = 0;
byte _hour = 0;
byte _minute = 0;
byte _second = 0;
int _intensity = 0;
byte _mode = 0;
};
class channel {
public:
channel(String color, byte pin){
this->_color = color;
this->_pin = pin;
init();
};
void setpoint(byte row, point &newPoint, uint day, byte hour, byte minute, byte second, byte intensity, byte mode){ //edits points (for debug)
newPoint.setDay(day);
newPoint.setHour(hour);
newPoint.setMinute(minute);
newPoint.setSecond(second);
newPoint.setIntensity(intensity);
newPoint.setMode(mode);
newPoint.setActive(1);
_points[row] = newPoint;
}
bool getPoint(byte point){
return _points[point + 1].getActive();
}
void clearAllPoints(){
for (byte point = 0; point < maxPoints; point ++ ){
_points[point].clear();
}
}
void setPin(byte pin){
_pin = pin;
}
byte getPin(){
return _pin;
}
uint getIntensity(){
byte point1 = _previousPoint();
byte point2 = _nextPoint(point1);
byte fade_mode = _points[point1].getMode();
uint intensity;
if (point2 != 0){
if (fade_mode == 0){
intensity = _interpolate_lin(point1, point2);
}
else if (fade_mode == 1){
intensity = _interpolate_sin(point1, point2);
}
}
else if (point2 != 1){
if (fade_mode == 0){
intensity = _interpolate_lin(point1, point2);
}
else if (fade_mode == 1){
intensity = _interpolate_sin(point1, point2);
}
}
return intensity;
}
private:
//class attributes
point _points[maxPoints]; //points maximum of 64 points per channel.
byte _pin; //PWM pin output for channel
String _color; //LED color
float _interpolate_lin(byte point1, byte point2){
float idiff = _points[point2].getIntensity() - _points[point1].getIntensity();
float tdiff = point_seconds(point2) - point_seconds(point1);
float m;
if (tdiff != 0){
m = idiff / tdiff;
}
else{
m = 0;
}
float t = currentTime - point_seconds(point1);
float b = _points[point1].getIntensity();
//linear equation result
float i = (m * t) + b;
return i;
}
float _interpolate_sin(byte point1, byte point2){
float amplitude = _points[point2].getIntensity() - _points[point1].getIntensity();
float tdiff = point_seconds(point2) - point_seconds(point1);
float a = (-0.5 * amplitude);
float b = (2 * PI) / (2 * tdiff);
float t = (currentTime - point_seconds(point1));
float d = 0.5 * abs(amplitude);
//cosine equation result
float i = (a * cos(b * t)) + d;
return i;
}
byte _previousPoint(){
for (byte point = 0; point < maxPoints; point ++){ //loop through active points.
if (_points[point + 1].getActive() != 1){
return point;
}
else {
long t1 = point_seconds(point);
long t2 = point_seconds(point + 1);
if (t1 <= currentTime){ //If current time is after this point.
if (t2 > currentTime){ //If current time is less than the next point (it is sandwiched).
return point;
}
}
}
}
return 0;
}
byte _nextPoint(byte point){
if (_points[point + 1].getActive() != 1){ //if next point is inactive, previous is last in cycle. Next point is 0.
return 0;
}
else if (_points[point + 1].getActive() == 1){ //if next point is active, return it as _nextPoint.
return point + 1;
}
return 0;
}
long point_seconds(byte point){
return ((_points[point].getHour() * 3600) + (_points[point].getMinute() * 60) + _points[point].getSecond());
}
};
//declaring channels and initializing channel array.
channel red("red", 0);
channel green("green", 1);
channel blue("blue", 2);
channel royal("royal blue", 3);
channel *channels[] = {
&red,
&green,
&blue,
&royal
};
void setIntensities(){
for (byte ch = 0; ch < totalChannels; ch ++){
channel ledChannel = *channels[ch];
byte intensity = ledChannel.getIntensity();
ledcWrite(ledChannel.getPin(), intensity);
}
}
void setup() {
Serial.begin(9600);
//set up LED outputs
ledcAttachPin(12, 0);
ledcAttachPin(13, 1);
ledcAttachPin(16, 2);
ledcSetup(0, 1000, 8);
ledcSetup(1, 1000, 8);
ledcSetup(2, 1000, 8);
//clear all points
for (byte ch = 0; ch < totalChannels; ch++){
channel ledChannel = *channels[ch];
ledChannel.clearAllPoints();
}
//Set points for testing purposes
point red1;
point red2;
point red3;
point green1;
point green2;
point green3;
point blue1;
point blue2;
point blue3;
point royal1;
point royal2;
point royal3;
red.setpoint(0, red1, 0, 0, 0, 0, 0, 0);
red.setpoint(1, red2, 0, 0, 0, 10, 255, 0);
red.setpoint(2, red3, 0, 0, 0, 20, 0, 0);
green.setpoint(0, green1, 0, 0, 0, 10, 0, 0);
green.setpoint(1, green2, 0, 0, 0, 20, 255, 0);
green.setpoint(2, green3, 0, 0, 0, 30, 0, 0);
blue.setpoint(0, blue1, 0, 0, 0, 20, 0, 0);
blue.setpoint(1, blue2, 0, 0, 0, 30, 255, 0);
blue.setpoint(2, blue3, 0, 0, 0, 40, 0, 0);
royal.setpoint(0, royal1, 0, 0, 0, 30, 0, 0);
royal.setpoint(1, royal2, 0, 0, 0, 40, 255, 0);
royal.setpoint(2, royal3, 0, 0, 0, 50, 0, 0);
Serial.print("red: "); // <----------These are to debug. The channels are returning ascending bool values for point 3 (which should all be 0)
for (byte point = 0; point < 3; point++){
Serial.print(red.getPoint(point));
}
Serial.print(" green: ");
for (byte point = 0; point < 3; point++){
Serial.print(green.getPoint(point));
}
Serial.print(" blue: ");
for (byte point = 0; point < 3; point++){
Serial.print(blue.getPoint(point));
}
Serial.print(" royal: ");
for (byte point = 0; point < 3; point++){
Serial.print(royal.getPoint(point));
}
}
void loop() {
currentTime = millis() / 1000;
if (currentTime > lastUp){
setIntensities();
lastUp = currentTime;
}
}
The problem is with your getPoint() method:
bool getPoint(byte point){
return _points[point + 1].getActive();
}
You're referencing the array with point + 1 instead of point so when you attempt to read the last item in the array you're actually reading off the end of the array and getting arbitary data.
Just change this to:
bool getPoint(byte point){
return _points[point].getActive();
}
You're also doing the same thing on your _previousPoint() method:
byte _previousPoint(){
for (byte point = 0; point < maxPoints; point ++){ //loop through active points.
if (_points[point + 1].getActive() != 1){
return point;
}
Again - I think you want to just replace [point + 1] with [point] to go through the list of points and prevent reading past the end of the array.
Is there any alternative to System.Web.Security.Membership.GeneratePassword in AspNetCore (netcoreapp1.0).
The easiest way would be to just use a Guid.NewGuid().ToString("n") which is long enough to be worthy of a password but it's not fully random.
Here's a class/method, based on the source of Membership.GeneratePassword of that works on .NET Core:
public static class Password
{
private static readonly char[] Punctuations = "!##$%^&*()_-+=[{]};:>|./?".ToCharArray();
public static string Generate(int length, int numberOfNonAlphanumericCharacters)
{
if (length < 1 || length > 128)
{
throw new ArgumentException(nameof(length));
}
if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
{
throw new ArgumentException(nameof(numberOfNonAlphanumericCharacters));
}
using (var rng = RandomNumberGenerator.Create())
{
var byteBuffer = new byte[length];
rng.GetBytes(byteBuffer);
var count = 0;
var characterBuffer = new char[length];
for (var iter = 0; iter < length; iter++)
{
var i = byteBuffer[iter] % 87;
if (i < 10)
{
characterBuffer[iter] = (char)('0' + i);
}
else if (i < 36)
{
characterBuffer[iter] = (char)('A' + i - 10);
}
else if (i < 62)
{
characterBuffer[iter] = (char)('a' + i - 36);
}
else
{
characterBuffer[iter] = Punctuations[i - 62];
count++;
}
}
if (count >= numberOfNonAlphanumericCharacters)
{
return new string(characterBuffer);
}
int j;
var rand = new Random();
for (j = 0; j < numberOfNonAlphanumericCharacters - count; j++)
{
int k;
do
{
k = rand.Next(0, length);
}
while (!char.IsLetterOrDigit(characterBuffer[k]));
characterBuffer[k] = Punctuations[rand.Next(0, Punctuations.Length)];
}
return new string(characterBuffer);
}
}
}
I've omitted the do...while loop over the CrossSiteScriptingValidation.IsDangerousString. You can add that back in yourself if you need it.
You use it like this:
var password = Password.Generate(32, 12);
Also, make sure you reference System.Security.Cryptography.Algorithms.
System.Random doesn't provide enough entropy when used for security reasons.
https://cwe.mitre.org/data/definitions/331.html
Why use the C# class System.Random at all instead of System.Security.Cryptography.RandomNumberGenerator?
Please see the example below for a more secure version of #khellang version
public static class Password
{
private static readonly char[] Punctuations = "!##$%^&*()_-+[{]}:>|/?".ToCharArray();
public static string Generate(int length, int numberOfNonAlphanumericCharacters)
{
if (length < 1 || length > 128)
{
throw new ArgumentException("length");
}
if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
{
throw new ArgumentException("numberOfNonAlphanumericCharacters");
}
using (var rng = RandomNumberGenerator.Create())
{
var byteBuffer = new byte[length];
rng.GetBytes(byteBuffer);
var count = 0;
var characterBuffer = new char[length];
for (var iter = 0; iter < length; iter++)
{
var i = byteBuffer[iter] % 87;
if (i < 10)
{
characterBuffer[iter] = (char)('0' + i);
}
else if (i < 36)
{
characterBuffer[iter] = (char)('A' + i - 10);
}
else if (i < 62)
{
characterBuffer[iter] = (char)('a' + i - 36);
}
else
{
characterBuffer[iter] = Punctuations[GetRandomInt(rng, Punctuations.Length)];
count++;
}
}
if (count >= numberOfNonAlphanumericCharacters)
{
return new string(characterBuffer);
}
int j;
for (j = 0; j < numberOfNonAlphanumericCharacters - count; j++)
{
int k;
do
{
k = GetRandomInt(rng, length);
}
while (!char.IsLetterOrDigit(characterBuffer[k]));
characterBuffer[k] = Punctuations[GetRandomInt(rng, Punctuations.Length)];
}
return new string(characterBuffer);
}
}
private static int GetRandomInt(RandomNumberGenerator randomGenerator)
{
var buffer = new byte[4];
randomGenerator.GetBytes(buffer);
return BitConverter.ToInt32(buffer);
}
private static int GetRandomInt(RandomNumberGenerator randomGenerator, int maxInput)
{
return Math.Abs(GetRandomInt(randomGenerator) % maxInput);
}
}
I am using recorderWorker.js and recorder.js
The final output which i get from this code is 44.1khz, 2-channel (stereo) wav file.But my requirement is 8khz , 1-channel wav file.
How can i achieve this with the help of recorderWorker.js
Kindly help me to resolve the issue.
The code for recorderWorker.js is
var recLength = 0,
recBuffers = [],
sampleRate;
this.onmessage = function(e){
switch(e.data.command){
case 'init':
init(e.data.config);
break;
case 'record':
record(e.data.buffer);
break;
case 'exportWAV':
exportWAV(e.data.type);
break;
case 'getBuffer':
getBuffer();
break;
case 'clear':
clear();
break;
}
};
function init(config){
sampleRate = config.sampleRate;
}
function record(inputBuffer){
var bufferL = inputBuffer[0];
var bufferR = inputBuffer[1];
var interleaved = interleave(bufferL, bufferR);
recBuffers.push(interleaved);
recLength += interleaved.length;
}
function exportWAV(type){
var buffer = mergeBuffers(recBuffers, recLength);
var dataview = encodeWAV(buffer);
var audioBlob = new Blob([dataview], { type: type });
this.postMessage(audioBlob);
}
function getBuffer() {
var buffer = mergeBuffers(recBuffers, recLength)
this.postMessage(buffer);
}
function clear(){
recLength = 0;
recBuffers = [];
}
function mergeBuffers(recBuffers, recLength){
var result = new Float32Array(recLength);
var offset = 0;
for (var i = 0; i < recBuffers.length; i++){
result.set(recBuffers[i], offset);
offset += recBuffers[i].length;
}
return result;
}
function interleave(inputL, inputR){
var length = inputL.length + inputR.length;
var result = new Float32Array(length);
var index = 0,
inputIndex = 0;
while (index < length){
result[index++] = inputL[inputIndex];
result[index++] = inputR[inputIndex];
inputIndex++;
}
return result;
}
function floatTo16BitPCM(output, offset, input){
for (var i = 0; i < input.length; i++, offset+=2){
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
function writeString(view, offset, string){
for (var i = 0; i < string.length; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
}
function encodeWAV(samples){
var buffer = new ArrayBuffer(44 + samples.length * 2);
var view = new DataView(buffer);
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* file length */
view.setUint32(4, 32 + samples.length * 2, true);
/* RIFF type */
writeString(view, 8, 'WAVE');
/* format chunk identifier */
writeString(view, 12, 'fmt ');
/* format chunk length */
view.setUint32(16, 16, true);
/* sample format (raw) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, 2, true);
/* sample rate */
view.setUint32(24, sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, sampleRate * 4, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, 4, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, samples.length * 2, true);
floatTo16BitPCM(view, 44, samples);
return view;
}
You don't need to modify the Recorder.JS library - this functionality is already provided. The constructor accepts a config object as the second parameter, as below:
var audioContext = new AudioContext();
var microphone = audioContext.createMediaStreamSource(stream);
recorder = new Recorder(microphone, { numChannels: 1, sampleRate: 8000 });
After some search and reading the Graphics class document, I can't find a way to specify the line style of a line. I mean whether the line is a solid one or a dotted one. Could anybody help me?
Thanks!
You can't, well, not just by using Flex library classes anyway. You can do it yourself though, of course. Here's a class that implements it (modified from code found here, thanks Ken Fox):
/*Copyright (c) 2006 Adobe Systems Incorporated
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.
*/
package com.some.package {
import mx.graphics.IStroke;
import flash.display.Graphics;
public class GraphicsUtils {
public static function _drawDashedLine( target:Graphics, strokes:Array, pattern:Array,
drawingState:DashStruct,
x0:Number, y0:Number, x1:Number, y1:Number):void {
var dX:Number = x1 - x0;
var dY:Number = y1 - y0;
var len:Number = Math.sqrt( dX * dX + dY * dY );
dX /= len;
dY /= len;
var tMax:Number = len;
var t:Number = -drawingState.offset;
var patternIndex:int = drawingState.patternIndex;
var strokeIndex:int = drawingState.strokeIndex;
var styleInited:Boolean = drawingState.styleInited;
while( t < tMax ) {
t += pattern[patternIndex];
if( t < 0 ) {
var x:int = 5;
}
if( t >= tMax ) {
drawingState.offset = pattern[patternIndex] - ( t - tMax );
drawingState.patternIndex = patternIndex;
drawingState.strokeIndex = strokeIndex;
drawingState.styleInited = true;
t = tMax;
}
if( styleInited == false ) {
(strokes[strokeIndex] as IStroke).apply( target );
}
else {
styleInited = false;
}
target.lineTo( x0 + t * dX, y0 + t * dY );
patternIndex = ( patternIndex + 1 ) % pattern.length;
strokeIndex = ( strokeIndex + 1 ) % strokes.length;
}
}
public static function drawDashedLine( target:Graphics, strokes:Array, pattern:Array,
x0:Number, y0:Number, x1:Number, y1:Number ):void {
target.moveTo( x0, y0 );
var struct:DashStruct = new DashStruct();
_drawDashedLine( target, strokes, pattern, struct, x0, y0, x1, y1 );
}
public static function drawDashedPolyLine( target:Graphics, strokes:Array, pattern:Array,
points:Array ):void {
if( points.length == 0 ) { return; }
var prev:Object = points[0];
var struct:DashStruct = new DashStruct();
target.moveTo( prev.x, prev.y );
for( var i:int = 1; i < points.length; i++ ) {
var current:Object = points[i];
_drawDashedLine( target, strokes, pattern, struct,
prev.x, prev.y, current.x, current.y );
prev = current;
}
}
}
}
class DashStruct {
public function init():void {
patternIndex = 0;
strokeIndex = 0;
offset = 0;
}
public var patternIndex:int = 0;
public var strokeIndex:int = 0;
public var offset:Number = 0;
public var styleInited:Boolean = false;
}
And you can use it like:
var points:Array = [];
points.push( { x: 0, y: 0 } );
points.push( { x: this.width, y: 0 } );
points.push( { x: this.width, y: this.height } );
points.push( { x: 0, y: this.height } );
points.push( { x: 0, y: 0 } );
var strokes:Array = [];
var transparentStroke:Stroke = new Stroke( 0x0, 0, 0 );
strokes.push( new Stroke( 0x999999, 2, 1, false, 'normal', CapsStyle.NONE ) );
strokes.push( transparentStroke );
strokes.push( new Stroke( 0x222222, 2, 1, false, 'normal', CapsStyle.NONE ) );
strokes.push( transparentStroke );
GraphicsUtils.drawDashedPolyLine( this.graphics, strokes, [ 16, 5, 16, 5 ], points );
Well there is no Dashed or Dotted line in flex.
However, you can create your own line or border:
http://cookbooks.adobe.com/post_Creating_a_dashed_line_custom_border_with_dash_and-13286.html
Try and enjoy!
I ran across this, today, which I like a lot. Provides for dashed/dotted lines and a few other things (curves, primarily).
http://flexdevtips.blogspot.com/2010/01/drawing-dashed-lines-and-cubic-curves.html
I wrote some simple DashedLine code -
import flash.display.Graphics;
import spark.primitives.Line;
public class DashedLine extends Line
{
public var dashLength:Number = 10;
override protected function draw(g:Graphics):void
{
// Our bounding box is (x1, y1, x2, y2)
var x1:Number = measuredX + drawX;
var y1:Number = measuredY + drawY;
var x2:Number = measuredX + drawX + width;
var y2:Number = measuredY + drawY + height;
var isGap:Boolean;
// Which way should we draw the line?
if ((xFrom <= xTo) == (yFrom <= yTo))
{
// top-left to bottom-right
g.moveTo(x1, y1);
x1 += dashLength;
for(x1; x1 < x2; x1 += dashLength){
if(isGap){
g.moveTo(x1, y1);
} else {
g.lineTo(x1, y1);
}
isGap = !isGap;
}
}
else
{
// bottom-left to top-right
g.moveTo(x1, y2);
x1 += dashLength;
for(x1; x1 < x2; y2 += dashLength){
if(isGap){
g.moveTo(x1, y2);
} else {
g.lineTo(x1, y2);
}
isGap = !isGap;
}
}
}
}
Then use it like this -
<comps:DashedLine top="{}" left="0" width="110%" >
<comps:stroke>
<s:SolidColorStroke color="0xff0000" weight="1"/>
</comps:stroke>
</comps:DashedLine>
I do it pretty simple:
public static function drawDottedLine(target:Graphics, xFrom:Number, yFrom:Number, xTo:Number, yTo:Number, dotLenth:Number = 10):void{
var isGap:Boolean;
target.moveTo(xFrom, yFrom);
xFrom += dotLenth;
for(xFrom; xFrom < xTo; xFrom += dotLenth){
if(isGap){
target.moveTo(xFrom, yFrom);
} else {
target.lineTo(xFrom, yFrom);
}
isGap = !isGap;
}
}
UPD: :) The same way as in the link provided by Ross.