I want to split a mp3 file into several pieces e.g. from 0:30 to 0:45 and from 0:45 to 1:00 in ActionScript.
The closest thing I've found is this short snippet from documentation. I cannot find any articles or libraries that would help me with this. Is it even possible to achieve?
Thanks in advance for any tips.
Example of what I want: mp3cut
I haven't done this myself, but the way I would go about it follows this process:
Load the MP3 file
Decode the MP3 file
Extract the bits you need
Encode the extracted data
All of the below is to be considered untested pseudo code.
Step 1: Load the MP3 file
I assume that you already have a way to load the compressed MP3 file into memory. If not, there is plenty of information about this step to be found.
Step 2: decode the MP3 file
The Sound class exposes the method loadCompressedDataFromByteArray (docs) which takes the compressed MP3 file as a ByteArray and decodes it to raw sound data.
Example:
var mySound:Sound = new Sound();
mySound.loadCompressedDataFromByteArray(myMP3Data, myMP3Data.length);
Step 3: Extract a part of the sound
Using the extract (docs) method (as described in the link in your question) you can extract raw sound data from a Sound object. You pass in a ByteArray object to be populated with the data, specify where you want the "cut" to be, and the length of the clip to extract.
The data is stored using 44100 samples per second (left + right channel), and the calculations below is based on this. I might be wrong about this, so if it doesn't work as expected, please look this up further.
Example:
var sampleFrequency:int = 44100;
var startTimeInSeconds:Number = 30.0;
var lengthInSeconds:Number = 15.0;
// Calculate the position to start the clip (in samples)
var startPosition:Number = Math.round(startTimeInSeconds * sampleFrequency);
// Calculate the number of samples to extract
var samplesLength:Number = Math.round(lengthInSeconds * sampleFrequency);
var extractedBytes:ByteArray = new ByteArray();
mySound.extract(extractedBytes, samplesLength, startPosition);
Step 4: Encode the extracted sound back to MP3
Now that you have the sound data as a ByteArray, theres a few way to encode it back to the MP3 format. This previous StackOverflow answer mentions this library, but there might be other libraries out there better suited for your tasks.
Related
I know it's 2016 and this is a question about Flash...
Sadly a lot of the Flash AS3 resources are no longer available as the format has fallen out of favour with web devs and the tutorials I have managed to find are all done on earlier versions of Flash - I have CS6, and some of the functions/commands don't seem to work the same way.
So my question for you S.O gurus...
How do I load any kind of data into a swf movie via a GET URL.
For example :
www.example.com/mymovie.swf?loadfile=myfile.mp3
I know I can do the following to load an external file :
var url:String = "http://example.com/myfile.mp3";
var soundFile:URLRequest = new URLRequest(url);
But instead of hard coding the url how do I tell it to look for the data in the loadfile variable delivered via the incoming request?
The answer in case anybody else stumbles across this :
loaderInfo.parameters['loadfile']
Gets the variable from the url
I created a batch of sounds assembled with this tool:
AudioSprite
https://github.com/tonistiigi/audiosprite
The output is generally used for JS libraries, such as Howler, Zynga Jukebox, or SoundJS - but I wanted to see if it's possible to implement in AS3.
I started creating a Sound player that can load, parse and play the sounds based on the JSON and MP3 file this tool generates.
So far so good! ... except for loops.
Now, the big question is - is there a way to play a Sound-loop seamlessly given that all music & sounds coexist in the same MP3 file, and it has a start & end range to play and stop it?
Example of how the sounds are placed in the file:
mygame_sounds.mp3 = [BUZZ + LASER + BOING ... + TRACKLOOP]
I'm looking for a solution that does not involve using the SAMPLE_DATA Event (given it eats up a lot of CPU usage). If there's no way around it, please explain why.
So far I've had mild success using flash.utils.Timer objects triggered after a given AudioSprite's duration, but it's not consistent.
To stop / dispose of a non-looping sound, I rely on a Master Timer (running at very short intervals) and that seems to "cut" the sample appropriately. But I already tried using this Master Timer to play a looped-sound over and over - same latency issues.
Is there any method to predict / measure how much latency is to be expected by the time the sound completes one pass?
In SoundJS we could not find a way to allow smooth looping of audio sprites in AS3 and went with a timer. We found Web Audio was the only api that allowed smooth looping, and therefore recommended staying away from audiosprites for sounds that needed to loop smoothly if any other plugin might be used.
Hope that helps.
The reason of why you can't get smooth loops of a track retrieved from a larger audio file is that you cannot check sound position faster than once per SWF frame, which length depends on stage.frameRate and total processing time of your application and is generally varied. So, if your looping sounds lasts say 5.123 seconds (I don't care how many samples, just that its length does not make a full number of frames regardless of stage.frameRate), your sound will attempt to play for either 5.125 seconds (205 frames at 40 fps, IMO best bet for this particular sound), 5.133 seconds (154 frames at 30 fps) or some weird number of frames if the SWF would experience lag. The excess milliseconds cannot be totally controlled by any means due to AS3/Flash engine optimization. So, consider not using audio sprites and shift into audio packs (several audio files in an SWF, or one sound in an MP3).
Although I'm still working on the perfect solution, this is the best I could come up with:
Load the JSON file / ByteArray.
Parse the JSON file to obtain each sprites' ID, start and end times.
Load the MP3 file / ByteArray (requires loadCompressedDataFromByteArray()) into a master Sound object.
Once loaded, check if any sprites are marked as "loops".
Create separate Sound objects for the above loops, and extract the portion from the master Sound via loadPCMFromByteArray() with some "magic-numbers" (details below).
To play a one-shot sound, call the master Sound's play(sprite.start * 1000) (depending on the format, usually the JSON's start values are in seconds, needs to be in milliseconds).
To play a seamless-loop sound, call the individual Sound object's (created in step #5) play(0, 9999) method.
I won't go too deep in details on how to stop the sounds (SoundChannel.stop(), bam!), but I'll explain the "magic-numbers" mentioned above. See this snippet:
var goldenOffset:UInt = (64 << 5);
var goldenDuration:UInt = (64 << 2);
var sampleRate:UInt = 44100;
for (id in loops) {
var sprite:AudioSpriteItem = _mapSprites.get(id);
var loop:Sound = _mapLoops.get(id);
var sampleBytes = new ByteArray();
var samplesTotal:UInt = cast(sprite.duration * sampleRate + goldenDuration);
var samplesStart:UInt = cast(sprite.start * sampleRate + goldenOffset);
_sound.extract(sampleBytes, samplesTotal, samplesStart);
sampleBytes.position = 0;
loop.loadPCMFromByteArray(sampleBytes, samplesTotal, "float", true);
}
Quite honestly, these magic goldenOffset and goldenDuration values were just found via Trial-and-Error. I could get close to a seamless loop without them by just calculating the start and duration with the sampleRate (assuming 44100 by default), but each endings had a bit of a hiccup to it.
After several adjustments, those couple "64 left bit-shifted" values made the loops sound smoother.
I posted the Haxe project on github (compiled SWC also available in /bin folder) if you wish to try it / read through the code.
FLAudioSprite
Github page: https://github.com/bigp/FLAudioSprite
SWF Demo (Download): bit.ly/FLAudioSpriteSWFDemo
I have an AS3 music player built into an app that I'm putting together. It works perfectly with almost every file I've used, but there is one file that it stops early on. The file is roughly 56 seconds long, the player stops at about 44 seconds. I'm using trace to show the length, and for every other song the length is correct. In this case, trace shows roughly 44 seconds instead of 56. Here's the code I use to load the file:
length = 0;
request = new URLRequest(fileAddress);
track = new Sound();
track.load(request);
track.addEventListener(Event.COMPLETE, TrackLoaded);
And here's the TrackLoaded function:
private function TrackLoaded(e:Event):void{
length = track.length;
if (playWhenLoaded == true){
trackChannel = track.play(0);
trackChannel.addEventListener(Event.SOUND_COMPLETE, TrackFinishedPlaying);
playWhenLoaded = false;
}
Works perfectly with every other file. What am I missing?
Are you willing to host this 56 sec MP3 somewhere for download & analysis? Or if you can yourself check the header info via a Hexeditor. I suspect either of two things:
1) Header has incorrect time length embedded and Flash takes that as final duration and stops there. After all why read anymore remaining bytes? they could be just metadata not audio samples Besides what encoder would lie about true duration? So its accepted as final duration even if your ears know that its incorrect.
2) MP3 samplerate /bitrate issue: Consider what sample rate is this one problem MP3? Check the sample rate against of a working MP3. Also are these various found sounds or you made each yourself? I ask to confirm you put the same settings for each file yet this one does not work?
In anycase I think you could solve this particular MP3 by re-encoding it. Maybe save as WAV or AIFF first then take that new uncompressed audio and convert back to a MP3 with samplerate of 44100 khz + Stereo sound + Constant Bitrate (avoid Variable B.Rate like hell if you dont want issues)
Checking and fixing either of the above should get you are correctly parsed MP3. Hope it helps
I'm working on a local application ( it's not a website or nothing related ) and I have various FLVs with a very simple encryptation method by now (just like adding 10 at each byte).
I can load/play them using NetStream.appendBytes() after decrypting, but that happens only after I read all video data it's not streamed.
What I really need is to stream those videos from a remote url, and decrypting while receiving data, using a OSMF based player that I already have built.
I'm lost on how OSMF deals with FLV, otherwise, I would try to create a plugin or something like.
I'd be very thankful if someone point me how to deal with that.
But I'd be happy if someone help me to find a way to load a local file using OSMF, passing a ByteArray value, instead of a url (below). Or even giving me directions to create a OSMF plugin to solve my problem.
videoElement.resource = "video_url/video.flv";
This is my current code just to play my decoded FLV byte array
private function playBytes(bytes:ByteArray):void
{
// detecting it's header
if (bytes.readUTFBytes(3) != "FLV")
{
_text.appendText("\nFile \""+ file +"\" is not a FLV")
return void;
}
bytes.position = 0;
netConnection.connect(null);
netStream = new NetStream(netConnection);
netStream.client = { onMetaData:function(obj:Object):void { } }
video.attachNetStream(netStream);
addChild(video);
// put the NetStream class into Data Generation mode
netStream.play(null);
// before appending new bytes, reset the position to the beginning
netStream.appendBytesAction(NetStreamAppendBytesAction.RESET_BEGIN);
// append the FLV video bytes
netStream.appendBytes(bytes);
}
Interesting post, I'd be interested to see the answer. Looking at something similar myself, though not with a stream, I came across the following.
http://ntt.cc/2008/07/15/bitsreader-read-bits-from-given-bytearray.html
After passing the byte array you can use bits.read(8) of a 10 bit array. Perhaps this would send you down the correct path? Otherwise, I'm thinking you'd need to break it apart and essentially do smaller sections to buffer in order to concatenate all the buffered data...
Just a thought,
Is there some cheap and reliable way to detect if an image file has EXIF data? Something like "read the first 100 bytes and search for EXIF substring" is highly preferable. I don't need to read and parse it - only to know if it is there.
The key is that it must be very fast. C++ is preferable.
You might look at the implementation of file (1).
You could just call it, of course, but you presumably don't need the rest of files functionality so...
You only need to check first 4 bytes of the stream:
bool IsExifStream(const char* pJpegBuffer)
{
static const char stream_prefix1[] = "\xff\xd8\xff\xe1";
return memcmp(pJpegBuffer, stream_prefix1, 4) == 0;
}
You could read the source of the PHP EXIF extension - by looking at how the exif_read_data is implemented, you might pick up some clues.
If you do not require massive performance, I would use some Exif library and let it try to get the Exif data (if present) for you. (pyexif, perl Image::exif, c# MetaDataExtractor etc)
Otherwise,
take a look at http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure
You need to create a simple binary parser do dig out the "segment codes", and to find the segment called APP1 (if I understand it correctly). The data should contain the letters "Exif"
e.g. in a random JPEG file on my PC, the bytes 7-10 said "Exif". I do not know if the location is the same in all JPEG files. The segments may be of variable length.