PageRenderTime 30ms CodeModel.GetById 17ms app.highlight 9ms RepoModel.GetById 2ms app.codeStats 0ms

/flash/flash-audio/src/com/ryanberdeen/audio/DiscontinuousSampleSource.as

http://echo-nest-remix.googlecode.com/
ActionScript | 139 lines | 106 code | 24 blank | 9 comment | 17 complexity | a864c5b6f70c34cd62606bfe923018e5 MD5 | raw file
  1/*
  2 * Copyright 2009 Ryan Berdeen. All rights reserved.
  3 * Distributed under the terms of the MIT License.
  4 * See accompanying file LICENSE.txt
  5 */
  6
  7package com.ryanberdeen.audio {
  8  import flash.media.Sound;
  9  import flash.utils.ByteArray;
 10
 11  /**
 12  * Provides samples based on an arbitrary list of sample ranges.
 13  */
 14  public class DiscontinuousSampleSource implements ISampleSource {
 15    private var _sampleSource:ISampleSource;
 16    private var _sampleRanges:Array;
 17    private var _length:Number;
 18
 19    private var extractionRangeIndex:int;
 20    private var extractionRange:SampleRange;
 21    private var extractionPosition:Number;
 22    private var startPosition:Number;
 23    private var finished:Boolean;
 24
 25    private var positionRangeIndex:int;
 26    private var localPosition:Number;
 27
 28    private var linearPosition:Number;
 29
 30    public function set sampleSource(sampleSource:ISampleSource):void {
 31      _sampleSource = sampleSource;
 32    }
 33
 34    public function set sampleRanges(sampleRanges:Array):void {
 35      _sampleRanges = sampleRanges;
 36
 37      extractionRangeIndex = 0;
 38      extractionRange = _sampleRanges[extractionRangeIndex];
 39
 40      extractionPosition = extractionRange.start;
 41
 42      startPosition = 0;
 43
 44      linearPosition = 0;
 45      localPosition = extractionPosition;
 46      positionRangeIndex = 0;
 47
 48      _length = 0;
 49      for each (var sampleRange:SampleRange in _sampleRanges) {
 50        _length += sampleRange.length;
 51      }
 52    }
 53
 54    public function extract(target:ByteArray, length:Number, startPosition:Number = -1):Number {
 55      if (startPosition != -1) {
 56        if (startPosition != this.startPosition) {
 57          var newPosition:Array = seek(this.startPosition, startPosition, extractionPosition, extractionRangeIndex);
 58          extractionPosition = newPosition[0];
 59          extractionRangeIndex = newPosition[1];
 60          extractionRange = _sampleRanges[extractionRangeIndex];
 61        }
 62      }
 63
 64      var samplesRead:int = 0;
 65      while (!finished && samplesRead < length) {
 66        var samplesLeft:int = extractionRange.end - extractionPosition;
 67        var samplesToRead:int = Math.min(samplesLeft, length - samplesRead);
 68        _sampleSource.extract(target, samplesToRead, extractionPosition);
 69
 70        samplesRead += samplesToRead;
 71        extractionPosition += samplesToRead;
 72        if (extractionPosition == extractionRange.end) {
 73          extractionRangeIndex++;
 74
 75          if (extractionRangeIndex == _sampleRanges.length) {
 76            finished = true;
 77          }
 78          else {
 79            extractionRange = _sampleRanges[extractionRangeIndex];
 80            extractionPosition = extractionRange.start;
 81          }
 82        }
 83      }
 84
 85      this.startPosition = startPosition + samplesRead;
 86      return samplesRead;
 87    }
 88
 89    public function toSourcePosition(position:Number):Number {
 90      var result:Array = seek(linearPosition, position, localPosition, positionRangeIndex);
 91      linearPosition = position;
 92      localPosition = result[0];
 93      positionRangeIndex = result[1];
 94      return result[0];
 95    }
 96
 97    private function seek(linearStart:Number, linearEnd:Number, sourcePosition:Number, index:int):Array {
 98      if (linearEnd < linearStart) {
 99        // TODO might be faster to seek backwards and then seek forwards
100        linearStart = 0;
101        index = 0;
102        sourcePosition = _sampleRanges[index].start;
103      }
104
105      var distance:Number = linearEnd - linearStart;
106
107      var range:SampleRange;
108      var distanceLeft:Number;
109      var samplesLeftThisRange:Number;
110
111      var samplesAdvanced:Number = 0;
112      while (samplesAdvanced < distance) {
113        range = _sampleRanges[index];
114        samplesLeftThisRange = range.end - sourcePosition;
115        distanceLeft = distance - samplesAdvanced;
116        if (samplesLeftThisRange > distanceLeft) {
117          sourcePosition += distanceLeft;
118          samplesAdvanced += distanceLeft;
119        }
120        else {
121          index++;
122          if (index < _sampleRanges.length) {
123            sourcePosition = _sampleRanges[index].start;
124          }
125          samplesAdvanced += samplesLeftThisRange;
126        }
127      }
128      return [sourcePosition, index];
129    }
130
131    public function get length():Number {
132      return _length;
133    }
134
135    public function get rangeIndex():int {
136      return positionRangeIndex;
137    }
138  }
139}