In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

[Prototyping] Microphone Study #03 リング変調

リング変調でボイスチェンジャーもどき

@author Yukiya Okuda
/**
 * Copyright alumican_net ( http://wonderfl.net/user/alumican_net )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/pBxX
 */

package
{
	import com.bit101.components.CheckBox;
	import com.bit101.components.HUISlider;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.SampleDataEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Microphone;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.utils.ByteArray;
	import flash.utils.getTimer;
	
	/**
	 * リング変調でボイスチェンジャーもどき
	 * 
	 * @author Yukiya Okuda
	 */
	public class Main extends Sprite
	{
		//----------------------------------------
		//CLASS CONSTANTS
		
		private const ZEROS:Point = new Point();
		
		
		
		
		//----------------------------------------
		//VARIABLES
		
		/**
		 * マイクロフォン
		 */
		private var _mic:Microphone;
		
		/**
		 * 録音用
		 */
		private var _records:Vector.<Number>;
		
		/**
		 * 再生用
		 */
		private var _sound:Sound;
		private var _soundChannel:SoundChannel;
		
		/**
		 * 録音、再生ヘッダ
		 */
		private var _position:int;
		
		/**
		 * 描画用
		 */
		private var _canvas:BitmapData;
		private var _slit:BitmapData;
		private var _over:Sprite;
		
		/**
		 * リング変調用
		 */
		private var _sample:int;
		private var _power:Number;
		private var _freq1:Number;
		private var _freq2:Number;
		private var _useDual:Boolean;
		
		
		
		//----------------------------------------
		//STAGE INSTANCES
		
		
		
		
		
		//----------------------------------------
		//METHODS
		
		/**
		 * コンストラクタ
		 */
		public function Main():void
		{
			Wonderfl.disable_capture();
			
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.quality = StageQuality.LOW;
			
			_mic = Microphone.getMicrophone();
			_mic.rate = 44;
			_mic.setSilenceLevel(0);
			_mic.setUseEchoSuppression(true);
			
			_sound = new Sound();
			
			_canvas = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0xffffff);
			_slit = new BitmapData(1, stage.stageHeight, false, 0xffffff);
			addChild( new Bitmap(_canvas) );
			
			_over = addChild( new Sprite() ) as Sprite;
			_over.graphics.beginFill(0x0);
			_over.graphics.drawRect(0, 0, 100, 100);
			_over.graphics.endFill();
			_over.blendMode = BlendMode.INVERT;
			
			var freq1Slider:HUISlider = new HUISlider(this, 10, 10, "FREQUENCY 1", function(e:Event):void
			{
				_freq1 = int( e.currentTarget.value );
			});
			freq1Slider.minimum = 100;
			freq1Slider.maximum = 500;
			freq1Slider.value   = _freq1 = 300;
			
			var freq2Slider:HUISlider = new HUISlider(this, 10, 30, "FREQUENCY 2", function(e:Event):void
			{
				_freq2 = int( e.currentTarget.value );
			});
			freq2Slider.minimum = 500;
			freq2Slider.maximum = 900;
			freq2Slider.value   = _freq2 = 700;
			
			var checkbox:CheckBox = new CheckBox(this, 200, 15, "USE ONLY FREQUENCY 1", function(e:Event):void
			{
				_useDual = !Boolean( e.currentTarget.selected );
			});
			_useDual = !(checkbox.selected = false);
			
			_startRecord();
		}
		
		/**
		 * 録音開始
		 */
		private function _startRecord():void
		{
			trace("_startRecord");
			
			_over.width = 0;
			
			_position = 0;
			_records = new Vector.<Number>(44100 * 5);
			_mic.addEventListener(SampleDataEvent.SAMPLE_DATA, _micSampleDataHandler);
		}
		
		private function _micSampleDataHandler(e:SampleDataEvent):void
		{
			var sw:int = stage.stageWidth;
			var sh:int = stage.stageHeight;
			
			var datas:ByteArray = e.data;
			
			while (datas.bytesAvailable)
			{
				var data:Number = _records[_position] = datas.readFloat();
				
				if (_position % 3 == 0)
				{
					var x:Number = _position / _records.length * sw;
					var y:Number = sh * 0.5 - data * 1000;
					_canvas.copyPixels(_slit, _slit.rect, new Point(x + 1, 0));
					_canvas.setPixel(x, y, 0x0);
				}
				
				if (++_position == _records.length)
				{
					_mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, _micSampleDataHandler);
					_startSound();
					return;
				}
			}
		}
		
		/**
		 * 再生開始
		 */
		private function _startSound():void
		{
			trace("_startSound");
			
			//リング変調
			_power = 6;
			_sample = 0;
			
			_position = 0;
			_sound.addEventListener(SampleDataEvent.SAMPLE_DATA, _soundSampleDataHandler);
			_soundChannel = _sound.play();
		}
		
		private function _soundSampleDataHandler(e:SampleDataEvent):void
		{
			_over.width = stage.stageWidth * (_position / _records.length);
			_over.height = stage.stageHeight;
			
			for (var i:int = 0; i < 2048; ++i)
			{
				var data:Number = _records[_position];
				
				var mod:Number = (_sample / 44100) * (2.0 * Math.PI);
				if (_useDual)
				{
					data *= _power * Math.cos(_freq1 * mod) * Math.cos(_freq2 * mod);
				}
				else
				{
					data *= _power * Math.cos(_freq1 * mod);
				}
				if (++_sample == 44100) _sample = 0;
				
				e.data.writeFloat(data);
				e.data.writeFloat(data);
				
				if (++_position == _records.length)
				{
					_sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, _soundSampleDataHandler);
					_startRecord();
					return;
				}
			}
		}
	}
}