Adobe Director Online
 

Shrinidhi

Shrinidhi Karanth
Director Developer

Byte array as output to a sound object

 
 

Reference:  http://help.adobe.com/en_US/Director/11.5/UsingScripting/WSAC788789-A3A0-4993-BE09-C4E33D958427.html

Audio data from a sound object or a mixer can be accessed, processed and passed back to the audio engine and the audio engine will play the processed audio chunk. The sample program below just reads the PCM data from the sound object and find the peak value in each chunk and displays it to the user.

The steps to get the peak value from the sound object is as follows

  • Register a callback function with the sound object
  • Save the PCM data chunk obtained in each callback
  • Find the peak value in the last saved chunk in every exitFrame.

global gByteArray    -- Byte Array to save PCM data using which the peak is calculated

global gMix          -- Global Mixer

global gSO           -- Sound Object of which peak is calculated

global gValidLength  -- Length of valid data in the gByteArray

The below code creates a new byteArray, mixer and sound object. Registers a callback function with the sound object so that we get the PCM data of the sound object’s audio chunk. Note that the callback is registered as #readOnly. i.e. If the audio chunk that is passed in the callback function is modified that will have no impact in the audio playback. For the change in audio chunk to be reflected the callback has to be registered as #readWrite.

Also note that the bufferSize of the mixer is set to 50 ms. This mean that the callback will be called almost every 50 ms and also it will have 50 ms data. In some cases this might vary upto 2 buffersizes!

on startmovie

 

  -- Create a new ByteArray

  gByteArray = bytearray()

  gValidLength = 0

 

  -- Display the peak value

  member(1).text = string(0)

 

  -- Create a new Mixer

  gMix = new(#mixer)

 

  -- Add a sound object to it

  -- Let it loop infinitely

  gSO = gMix.createSoundObject("soundObj1", member(3), [#loopCount:0] )

 

  -- Register for byteArray output.

  gSO.registerByteArrayCallback( #preFilter, #getPeak, #readonly )

 

  -- Set the mixer's format to that of the sound object so that there is no reformatting required

  gMix.channelCount = gSO.channelCount

  gMix.bitDepth = gSO.bitDepth

  gMix.sampleRate = gSO.sampleRate

 

  -- Set the Mixer's bufferSize to 50.

  -- We get callback called almost at 50 ms intervals

  gMix.bufferSize = 50

 

  -- Finally, play the mixer!

  gMix.play()

end

 

on stopMovie

  -- Unregister the byteArray callback

  gSO.unregisterByteArrayCallback()

 

  -- Erase the Mixer

  if not voidp(gMix) then

    gMix.erase()

  end if

 

  -- Display the peak value

  member(1).text = string(0)

 

end

 

 

on getPeak pByteArray, bitDepth, sampRate, numChannel

  -- Copy the most recent PCM data into global byteArray

  gByteArray = pByteArray

 

  -- Update the length of the byteArray

  gValidLength = pByteArray.length

end

8 bit PCM values have values between 0 and 255. The below code calculates the peak value for the PCM data available in gByteArray.
This code works only for 8 bit, mono files!
For 16 bit-depth audio the PCM values can range from -32768 to +32767 and also the code should take care of endianess.

on exitFrame

 

  -- If there's not any data to process just return

  if gValidLength = 0 then

    return

  end if

 

  -- Set the initial peak value to 0

  maxValue = 0

 

  -- Iterate through each of the byte and get the peak value

  repeat with i = 1 to gValidLength

    if maxValue < gByteArray[i] then

      maxValue = gByteArray[i]

    end if

  end repeat

 

  -- Reset the valid data length

  gValidLength = 0

 

  -- Display the peak value

  member(1).text = string(maxValue)

 

end

The calculation of the peak value is done in exitFrame and not in the callback function for performance reasons.

In case the audio data need to be updated and it should get reflected in the audio playback (#readWrite) then the callback should save the most recent chunk into a global byteArray and pass the processed byteArray back. The saved byteArray is processed in the next exitFrame. This will ensure good performance at the cost of a bufferSize duration of delay.

For volume factor to be reflected in the peak Value we have to multiply the peak value with the scaled volume level.

Download the sample movie here

Feedback:
If you have any questions or comments concerning this article, please send a message to shrinidhi@gmail.com

 
spacer image
This is not an official site from Adobe. If you need any clarifications, please mail me at info@adobedirectoronline.com