Reading duration of MP3 file from App script - google-apps-script

I have a MP3 file in google drive. Is there a way I can get duraton of that MP3 file using google app script?

This page shows how to extract that information from an mp3 file by looking at the byte values.
So I wrote a short example and tested it. It seems to be working fine with all mp3 files I tried so far but there is no guarantee it will always work.
The getPlayTime function returns the play time in seconds.
function getPlayTime(file) {
var bitratesV1 = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320],
bitratesV2 = [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160];
var bytes = file.getBlob().getBytes();
for(var pos = 0;pos < bytes.length; pos++) {
if(bytes[pos] === -1 && pos < bytes.length - 3 && (bytes[pos+1]&0xF0) === 0xF0) {
var isMpegVersion2 = (bytes[pos+1]&8) !== 8,
isLayer3 = (bytes[pos+1]&6) === 2,
bitRate = ((bytes[pos+2]&0xF0) >>> 4)&0xF;
if(!isLayer3) continue;
if(isMpegVersion2) bitRate = bitratesV2[bitRate];
else bitRate = bitratesV1[bitRate];
var playTime = bytes.length*8/(1000 * bitRate);
return playTime;
}
}
}
function test() {
var file = DriveApp.getFilesByName("music.mp3").next();
var playTime = getPlayTime(file);
Logger.log(playTime);
}
edit:
Here is a hopefully more accurate but also much slower version
function getRunTime(file) {
var playTime = 0, numFrames = 0;
var bitratesV1 = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320],
bitratesV2 = [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160];
var bytes = file.getBlob().getBytes();
for(var pos = 0;pos < bytes.length; pos++) {
if(bytes[pos] === -1 && pos < bytes.length - 3 && (bytes[pos+1]&0xF0) === 0xF0) {
var isMpegVersion2 = (bytes[pos+1]&8) !== 8,
isLayer3 = (bytes[pos+1]&6) === 2,
bitRate = ((bytes[pos+2]&0xF0) >>> 4)&0xF;
if(!isLayer3) continue;
if(isMpegVersion2) bitRate = bitratesV2[bitRate];
else bitRate = bitratesV1[bitRate];
var pt = bytes.length*8/(1000 * bitRate);
if(!isNaN(pt) && isFinite(pt)) {
playTime += pt;
numFrames++;
}
}
}
return playTime/numFrames;
}

Related

Dynamic variables in actionscript 3

I am coding an ore-calculator, and for this I have repeating variables-names like:
tritaniumlogo:MineralContainer:new MineralContainer(values)
tritaniumtext:MinText=new MinText(values)
The Mineralcontainer and the MinText are some defined classes. Since I was repeating myself, I tried to re-factor the 80 lines of codes.
So I created an Array holding some data with the following code, but it does not work:
private var minsVar:Array = [[50, 98, "Tritanium", emptySpace], [50, 134, "Pyerite", emptySpace], [50, 170, "Mexallon", emptySpace], [50, 206, "Isogen", emptySpace], [230, 98, "Noxcium", emptySpace], [230, 134, "Zydrine", emptySpace], [230, 170, "Megacyte", emptySpace], [230, 206, "Morphite", emptySpace]];
for (var a:int = 0; a < minsVar.length; a++) {
private var [minsVar[a][2] + "logo"] = new MineralContainer(minsvar[a][0], minsvar[a][1], a + 1);
private var [minsVar[a][2] + "textinput"] = new MinText(minsvar[a][0], minsvar[a][1], minsvar[a][2], minsvar[a][3]);
}
If the loop is inside a class method, Then you may do the following:
for (var a:int = 0; a < minsVar.length; a++) {
this[minsVar[a][2] + "Logo"] = new MineralContainer(minsvar[a][0], minsvar[a][1], a + 1);
this[minsVar[a][2] + "Textinput"] = new MinText(minsvar[a][0], minsvar[a][1], minsvar[a][2], minsvar[a][3]);
}
Then, you can access them using this["TritaniumLogo"] or this["PyeriteTextinput"], for example. However, you are better to store your "new" objects inside some associative arrays:
private var minsVar:Array = [[50, 98, "Tritanium", emptySpace], [50, 134, "Pyerite", emptySpace], [50, 170, "Mexallon", emptySpace], [50, 206, "Isogen", emptySpace], [230, 98, "Noxcium", emptySpace], [230, 134, "Zydrine", emptySpace], [230, 170, "Megacyte", emptySpace], [230, 206, "Morphite", emptySpace]];
private var arrLogoes:Array = new Array();
private var arrTextInputs:Array = new Array();
// ...
for (var a:int = 0; a < minsVar.length; a++) {
arrLogoes[minsVar[a][2]] = new MineralContainer(minsvar[a][0], minsvar[a][1], a + 1);
arrTextInputs[minsVar[a][2]] = new MinText(minsvar[a][0], minsvar[a][1], minsvar[a][2], minsvar[a][3]);
}
So that you can access them later using arrLogoes["Tritanium"] or arrTextInputs["Pyerite"], for example.

ActionScript 3 - Collision detection of objects in an array issue

I'm working on a small flash game and am currently trying to implement multiplayer for up to 4 players. While the collision detection for 1 player works perfectly, when more players are added only the last player can collide with other objects.
Using trace statements I discovered that calling the x and y coordinates of the problematic players from the main class returns the initial x and y positions and not the current coordinates (trace(players[0].x + "/" + players[0].y);), while calling them from within the player class (trace(this.x + "/" + this.y);) always gives the correct values.
The last player will always return the correct coordinates in both classes. Below is a skeleton of the main class.
public class main extends MovieClip {
public var collisionObject: Array = new Array(14);
public var players: Array = new Array();
private var noOfPlayers = 2;
public function main() {
for (var i = 0; i < noOfPlayers; i++) {
setupPlayer(i);
stage.addChild(players[i]);
}
setupCollisionObject();
stage.addEventListener(Event.ENTER_FRAME, checkForCollision);
}
private function setupCollisionObject() {
/* Determines positions of and spawns objects */
}
private function setupPlayer(playerNo) {
switch (playerNo) {
case 3:
players[3] = new player(1000, 576, 180, 104, 100, 102);
case 2:
players[2] = new player(24, 576, 0, 73, 74, 76);
case 1:
players[1] = new player(1000, 384, 180, 38, 37, 39);
case 0:
players[0] = new player(24, 384, 0, 87, 65, 68);
}
}
public function checkForCollision(e: Event) {
trace("x: "+players[0].x+" y: "+players[0].y);
trace("x: "+players[1].x+" y: "+players[1].y);
for (var i in players) {
for (var j in collisionObject) {
if (players[i].hitTestObject(collisionObject[j])) {
//damage player
}
}
}
}
}
I'm at a loss of why this is happening.
You are missing break; in your switch-case in setupPlayer(), this results in all players but the last reinitialize during each call of setupPlayers(i).
private function setupPlayer(playerNo) {
switch (playerNo) {
case 3:
players[3] = new player(1000, 576, 180, 104, 100, 102);
break;
case 2:
players[2] = new player(24, 576, 0, 73, 74, 76);
break;
case 1:
players[1] = new player(1000, 384, 180, 38, 37, 39);
break;
case 0:
players[0] = new player(24, 384, 0, 87, 65, 68);
break;
}
}

AS3: Tile based movement system

very new to as3 and using a tutorial found here to try my hand at tile based movement. However, i cant seem to get the code to work. I keep getting the error code:
"1046: Type was not found or was not a compile-time constant:hero."
The line it is reference is:
var character: hero = new hero();
My full code is:
package {
import flash.display.MovieClip;
import flash.events.*;
public class main2 extends MovieClip {
var hero;
public function main2() {
// Create map
var mapWidth = 10;
var mapHeight = 10;
var tileSide = 32;
var totalTiles = mapWidth * mapHeight;
var myMap: Array = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
];
for (var i: int = 0; i < mapHeight; i++) {
for (var u: int = 0; u < mapWidth; u++) {
var cell: MovieClip = new tile();
cell.gotoAndStop(myMap[i][u] + 1);
cell.x = tileSide * u;
cell.y = tileSide * i;
addChild(cell);
}
}
// Hero management
var heroSpawnX = 4;
var heroSpawnY = 2;
var character: hero = new hero();
addChild(character);
character.x = heroSpawnX * tileSide;
character.y = heroSpawnY * tileSide;
var heroX = heroSpawnX;
var heroY = heroSpawnY;
// Basic movement
stage.addEventListener(KeyboardEvent.KEY_DOWN, movement);
function movement(event: KeyboardEvent):void {
if (event.keyCode == 40 && myMap[heroY + 1][heroX] == 0) {
character.gfx.rotation = 0;
character.y += tileSide;
heroY++;
}
if (event.keyCode == 38 && myMap[heroY - 1][heroX] == 0) {
character.gfx.rotation = 180;
character.y -= tileSide;
heroY--;
}
if (event.keyCode == 39 && myMap[heroY][heroX + 1] == 0) {
character.gfx.rotation = 270;
character.x += tileSide;
heroX++;
}
if (event.keyCode == 37 && myMap[heroY][heroX - 1] == 0) {
character.gfx.rotation = 90;
character.x -= tileSide;
heroX--;
}
}
}
}
}
Any help on this issue would be great, been at it for an hour now.
also if you have any recommendations on as3 resources please let me know...specifically tiled based systems.
thanks is advanced.
That error means your class Hero is not found. you should put the hero.as file at where your main2.as file is(at same location as main2.as). and then import it:
import Hero;
 
and my recommendation on as3 resources:
1.http://republicofcode.com:I think best site for starting as3.I started from it.
2.http://kirupa.com: a good site with a lot of articles
3.http://flashandmath.com: for professionals
and don't forget Adobe ActionScript-3 API reference:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/
I   H☺ P E   this helps !

Convert input text to 2d array

Im trying to make a level editor for the game.
Now I can create a new map (using mouse) and click "generate" button to trace map array
(string). After that I can simple copy the code from the output
and use it to create a new level.
Lets say I have a class called NewLevel.as
I create a new array and paste code from output window, so I have 2d array.
Then adding tiles to stage using for loops.
var array:Array =
// code below is what I get in output window
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[7, 7, 7, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7],
[7, 7, 7, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7]
];
for (var row:int = 0; row < array.length; row++)
{
for (var column:int = 0; column < array[row].length; column++)
{
var tile = new Tile();
addChild(tile);
tile.x = column * tile.width;
tile.y = row * tile.height;
tile.gotoAndStop(array[row][column] +1);
}
}
It works without problems,this gives me the map I created using level editor.
but what I want is that players input their "map code" and load the map they created.
I guess you have seen that in many games.
I have a textarea so users can input their string,
how can I convert their input to 2d array and load it (as you see in example)?
It should be 2d array.
I also added event listener to textarea
textarea.addEventListener(Event.CHANGE, changes);
function changes(e:Event):void
{
// convert input text to 2d array to build a new map
// Do not know how to get input to use with JSON
var myStr = levelTextarea.text;
var a2:Array = JSON.parse(myStr) as Array;
trace( a2 );
}
You can use JSON for this kind of job, this class is available in Flash Player 11.
package
{
import flash.display.Sprite;
import flash.events.Event;
/**
* ...
* #author
*/
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
var a:Array = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[7, 7, 7, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7],
[7, 7, 7, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7]
];
var txt:String = JSON.stringify( a )
trace( txt );
var a2:Array = JSON.parse( txt ) as Array;
trace( a2 );
}
}
}

Key generator method?

I really feel stupid for posting this but since i got no answers on my question and i am still junior programmer i will post this :
//List of keys
byte[] Key0 = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; //mode 10 = 0
byte[] Key1 = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 }; // i + 2
byte[] Key2 = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }; // i mode 2 = 0
byte[] Key3 = { 66, 77, 88, 99, 111, 222, 110, 112, 114, 115 }; // mode 11 = 0
byte[] Key4 = { 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121 }; //x^2
byte[] Key5 = { 6,17,34,57,86,121,162,209 }; //3x^2+2x+1
byte[] Key6 = { 77,78,79,80,81,82,83,84,85,86,87 }; // only in range
//Add all keys to the list
List<byte[]> oKeysList = new List<byte[]>();
oKeysList.Add(Key0);
oKeysList.Add(Key1);
oKeysList.Add(Key2);
oKeysList.Add(Key3);
oKeysList.Add(Key4);
oKeysList.Add(Key5);
oKeysList.Add(Key6);
Random oRandom = new Random();
//Generate random key index to be used in the encryption
int ListSelectedIndex = oRandom.Next(0, oKeysList.Count);
byte[] GeneratedKey = oKeysList[ListSelectedIndex];
//Generate 3 random number from the selected key and concate the key index to it
byte[] GeneratedBytes = new byte[4];
for (int i = 0; i < 3; i++)
{
GeneratedBytes[i] = GeneratedKey[oRandom.Next(0,GeneratedKey.Length)];
}
//Add the list of key index
GeneratedBytes[3] = (byte)ListSelectedIndex;
//Return the genreated bytes
return GeneratedBytes;
As you see i generate this 4 byte array along with a 8 bytes generated with from RNG Cryptography, and when i want to check my serial i take the last 4 bytes and use the mathematical relations between them, I want to generate many serials and be able to check if they are valid or not. I know its probably pretty old method with a very bad security so please if any one could help me or add anything to my code or suggest anything new i would be really thankful.
I am not sure that anyone will analyze your code here. I suggest the following: do not make software protection too complex. It won't make a significant affect on piracy, but 100% will affect people, who purchased your software. Keep your software protection mechanisms as simple as possible.