Zarion
28.06.2012, 22:34
Если бы музыкальный файл можно было бы проигрывать до конца, то проблемы бы практически не было. Достаточно выполнить команду вроде:
channel = sound.play(startPosition, countPlays);
и мы получили бы звук от startPosition до конца файла countPlays раз. Но проблема в том, что нужен звук не до конца файла, а до конкретной finishPosition.
Один раз это осуществить без проблем, с помощью таймера. Хотя надо сказать, что чувствительность у таймера не одна миллисекунда, а порядка 17-30 миллисекунд, что по само по себе не идеально, хотя терпимо. Вручную жать пимпочку, любое число раз, которая будет озвучивать данный музыкальный отрезок, тоже легко реализуемо. А вот программно запустить определенное число раз нужную пользовательскую функцию, типа:
playSound();
playSound();
не получается. Вместо того чтобы проигрывать с начала заданного интервала второй раз, мы имеем просто продолжение звучания с конца интервала, что не есть гуд.
Для решения данной задачи достаточно идей, а не кода. Если надо, я выложу свой образец программы.
Добавлено через 23 часа 37 минут
После некоторых экспериментов проблему удалось решить. С таймером идея оказалась не очень удачной, так как есть событие ENTER_FRAME, с помощью которого можно обрабатывать каждый фрейм звука, продолжительностью, по умолчанию, примерно от 20 до 35 миллисекунд (который, скорее всего, также привязан к системному таймеру), что соответствует от 50 до 30 кадрам в секунду. Пишем обработчик для этого события, который с одной стороны останавливает звук на нужной позиции, а с другой запускает повторное звучание фрагмента с заданной начальной позиции не более требуемого числа раз.
Пример теста программы, полностью удовлетворяющего меня, приведен ниже. С помощью кнопки "Browse mp3" загружаем произвольный музыкальный файл. После его загрузки в память становиться доступной кнопка "Play", нажав которую мы получим статическую информацию вроде:
startPosition = 10000
finishPosition = 50000
countPlays = 3
которая задается в программе непосредственно. Затем, после каждого проигрывания выводится сообщение типа:
2-я позиция звука: 50031 / 116767
Здесь мы видим слабую точность фреймового счетчика. В документации пишут о 16,7 миллисекундах. У нас где-то в два раза больше.
После третьего проигрывания (в данном случае) звук прекратится, что и требовалось получить. Раньше остановить все можно кнопкой "Stop". Не нажимайте только кнопку "Play" подряд несколько раз, а то мы получим какофонию нескольких звуков одновременно. Здесь в тесте нет особого желания обрабатывать подобную ситуацию.
Для любопытствующих, которые могут задаться вопросом, а зачем это нужно, скажу, что для озвучки коротких фрагментов аудиокниг, чтобы в промежутках между повторами можно было самому повторить текст диктора и набрать его на клавиатуре (русский перевод фрагмента текста также отображается), короче, для создания обучающей программы по собственному методу. Но это уже другая песня.
Пример кода:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
backgroundColor="#BBBBBB"
creationComplete="onCreationComplete()">
<fx:Declarations>
</fx:Declarations>
<fx:Script><![CDATA[
import flash.events.*;
private var file:File;
private var filter:FileFilter = new FileFilter("*.mp3", "*.mp3;");
private var sound:Sound = null;
private var soundChannel:SoundChannel = null;
private var soundLength:int = 0;
private var count:int = 1;
private var startPosition:int = 10000;
private var finishPosition:int = 50000;
private var countPlays:int = 3;
protected function onCreationComplete():void {
// Центрирование окна приложения
nativeWindow.x = (Capabilities.screenResolutionX - width) / 2;
nativeWindow.y = (Capabilities.screenResolutionY - height) / 2;
//loader = new URLLoader();
//loader.dataFormat = URLLoaderDataFormat.BINARY;
//loader.addEventListener(Event.COMPLETE, onURLLoader);
//loader.load(new URLRequest("../assets/Lolita_Foreword.mp3"));
} // onCreationComplete()
private function onBrowseFile(event:MouseEvent):void {
file = new File();
file.addEventListener(Event.SELECT, onFileSelect);
file.browseForOpen("Open",[filter]);
} // onBrowseFile()
protected function onFileSelect(event:Event):void{
file.removeEventListener(Event.SELECT, onFileSelect);
file = File(event.currentTarget);
sound = new Sound();
sound.load(new URLRequest(file.url));
sound.addEventListener(Event.COMPLETE, onSoundComplete);
} // onFileSelect()
protected function onSoundComplete(event:Event):void {
playButton.enabled = true;
soundLength = sound.length;
playInfo.text = "startPosition: " + startPosition + "\n" +
"finishPosition: " + finishPosition + "\n" + "countPlays: " + countPlays;
} // onSoundComplete()
private function onPlaySound():void {
count = 1;
soundChannel = sound.play(startPosition);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
} // onPlaySound()
protected function onEnterFrame(event:Event):void {
var position:int = soundChannel.position;
if(position >= finishPosition) {
playInfo.text = count + "-я позиция звука: " + position + " / " + soundLength;
soundChannel.stop();
if(count < countPlays) {
soundChannel = sound.play(startPosition);
count += 1;
}
}
} // onEnterFrame
protected function onStopSound():void {
count = 1;
if(soundChannel) {
playInfo.text = "Позиция звука: " + int(soundChannel.position) + " / " + soundLength;
soundChannel.stop();
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
} // onStopSound()
]]></fx:Script>
<s:Label id="playInfo" verticalCenter="1" horizontalCenter="1" fontSize="24">Привет, Zarion!</s:Label>
<mx:HBox>
<s:Button id="browseButton" label="Browse mp3" click="onBrowseFile(event)"/>
<mx:Button id="playButton" label="Play" enabled="false" click="onPlaySound();"></mx:Button>
<mx:Button id="stopButton" label="Stop" click="onStopSound();"></mx:Button>
</mx:HBox>
</s:WindowedApplication>
channel = sound.play(startPosition, countPlays);
и мы получили бы звук от startPosition до конца файла countPlays раз. Но проблема в том, что нужен звук не до конца файла, а до конкретной finishPosition.
Один раз это осуществить без проблем, с помощью таймера. Хотя надо сказать, что чувствительность у таймера не одна миллисекунда, а порядка 17-30 миллисекунд, что по само по себе не идеально, хотя терпимо. Вручную жать пимпочку, любое число раз, которая будет озвучивать данный музыкальный отрезок, тоже легко реализуемо. А вот программно запустить определенное число раз нужную пользовательскую функцию, типа:
playSound();
playSound();
не получается. Вместо того чтобы проигрывать с начала заданного интервала второй раз, мы имеем просто продолжение звучания с конца интервала, что не есть гуд.
Для решения данной задачи достаточно идей, а не кода. Если надо, я выложу свой образец программы.
Добавлено через 23 часа 37 минут
После некоторых экспериментов проблему удалось решить. С таймером идея оказалась не очень удачной, так как есть событие ENTER_FRAME, с помощью которого можно обрабатывать каждый фрейм звука, продолжительностью, по умолчанию, примерно от 20 до 35 миллисекунд (который, скорее всего, также привязан к системному таймеру), что соответствует от 50 до 30 кадрам в секунду. Пишем обработчик для этого события, который с одной стороны останавливает звук на нужной позиции, а с другой запускает повторное звучание фрагмента с заданной начальной позиции не более требуемого числа раз.
Пример теста программы, полностью удовлетворяющего меня, приведен ниже. С помощью кнопки "Browse mp3" загружаем произвольный музыкальный файл. После его загрузки в память становиться доступной кнопка "Play", нажав которую мы получим статическую информацию вроде:
startPosition = 10000
finishPosition = 50000
countPlays = 3
которая задается в программе непосредственно. Затем, после каждого проигрывания выводится сообщение типа:
2-я позиция звука: 50031 / 116767
Здесь мы видим слабую точность фреймового счетчика. В документации пишут о 16,7 миллисекундах. У нас где-то в два раза больше.
После третьего проигрывания (в данном случае) звук прекратится, что и требовалось получить. Раньше остановить все можно кнопкой "Stop". Не нажимайте только кнопку "Play" подряд несколько раз, а то мы получим какофонию нескольких звуков одновременно. Здесь в тесте нет особого желания обрабатывать подобную ситуацию.
Для любопытствующих, которые могут задаться вопросом, а зачем это нужно, скажу, что для озвучки коротких фрагментов аудиокниг, чтобы в промежутках между повторами можно было самому повторить текст диктора и набрать его на клавиатуре (русский перевод фрагмента текста также отображается), короче, для создания обучающей программы по собственному методу. Но это уже другая песня.
Пример кода:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
backgroundColor="#BBBBBB"
creationComplete="onCreationComplete()">
<fx:Declarations>
</fx:Declarations>
<fx:Script><![CDATA[
import flash.events.*;
private var file:File;
private var filter:FileFilter = new FileFilter("*.mp3", "*.mp3;");
private var sound:Sound = null;
private var soundChannel:SoundChannel = null;
private var soundLength:int = 0;
private var count:int = 1;
private var startPosition:int = 10000;
private var finishPosition:int = 50000;
private var countPlays:int = 3;
protected function onCreationComplete():void {
// Центрирование окна приложения
nativeWindow.x = (Capabilities.screenResolutionX - width) / 2;
nativeWindow.y = (Capabilities.screenResolutionY - height) / 2;
//loader = new URLLoader();
//loader.dataFormat = URLLoaderDataFormat.BINARY;
//loader.addEventListener(Event.COMPLETE, onURLLoader);
//loader.load(new URLRequest("../assets/Lolita_Foreword.mp3"));
} // onCreationComplete()
private function onBrowseFile(event:MouseEvent):void {
file = new File();
file.addEventListener(Event.SELECT, onFileSelect);
file.browseForOpen("Open",[filter]);
} // onBrowseFile()
protected function onFileSelect(event:Event):void{
file.removeEventListener(Event.SELECT, onFileSelect);
file = File(event.currentTarget);
sound = new Sound();
sound.load(new URLRequest(file.url));
sound.addEventListener(Event.COMPLETE, onSoundComplete);
} // onFileSelect()
protected function onSoundComplete(event:Event):void {
playButton.enabled = true;
soundLength = sound.length;
playInfo.text = "startPosition: " + startPosition + "\n" +
"finishPosition: " + finishPosition + "\n" + "countPlays: " + countPlays;
} // onSoundComplete()
private function onPlaySound():void {
count = 1;
soundChannel = sound.play(startPosition);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
} // onPlaySound()
protected function onEnterFrame(event:Event):void {
var position:int = soundChannel.position;
if(position >= finishPosition) {
playInfo.text = count + "-я позиция звука: " + position + " / " + soundLength;
soundChannel.stop();
if(count < countPlays) {
soundChannel = sound.play(startPosition);
count += 1;
}
}
} // onEnterFrame
protected function onStopSound():void {
count = 1;
if(soundChannel) {
playInfo.text = "Позиция звука: " + int(soundChannel.position) + " / " + soundLength;
soundChannel.stop();
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
} // onStopSound()
]]></fx:Script>
<s:Label id="playInfo" verticalCenter="1" horizontalCenter="1" fontSize="24">Привет, Zarion!</s:Label>
<mx:HBox>
<s:Button id="browseButton" label="Browse mp3" click="onBrowseFile(event)"/>
<mx:Button id="playButton" label="Play" enabled="false" click="onPlaySound();"></mx:Button>
<mx:Button id="stopButton" label="Stop" click="onStopSound();"></mx:Button>
</mx:HBox>
</s:WindowedApplication>