#c# #timer
#c# #таймер
Вопрос:
Мне была представлена интригующая проблема в c #, которую я не уверен, как решить.
Мне нужно, чтобы две дорожки воспроизводились друг над другом. За заданный промежуток времени необходимо воспроизводить постоянное количество звуковых сигналов. Один из них будет иметь заданный интервал (например, метроном), но другой должен воспроизводиться через случайные промежутки времени.
Я не уверен, как решить эту вторую проблему, заданное количество звуковых сигналов, воспроизводимых со случайными интервалами в течение заданного промежутка времени.
Ответ №1:
Просто возьмите это заданное количество времени, Т. Представьте его как некоторую достаточно мелкозернистую структуру, скажем, миллисекунды. Если вам нужно издать N звуковых сигналов, вам нужно разделить интервал времени N раз. Итак, создайте цикл, который выполняется N раз и на каждой итерации выбирает случайное местоположение во временном интервале для звукового сигнала. В зависимости от того, что вы делаете с данными после этого, вам может потребоваться отсортировать точки звукового сигнала.
Комментарии:
1. Вы также можете захотеть убедиться, что вы не выбрали одну и ту же точку дважды.
2. Звуковые сигналы здесь являются точками разделения, а не фрагментами, поэтому для N звуковых сигналов вам понадобится N случайных местоположений в интервале времени. Конечно, это приведет к N 1 фрагментам.
3. Операционная система также может захотеть убедиться, что звуковые сигналы не ближе по времени, чем определенный минимальный интервал. Например, если для подачи самого звукового сигнала требуется 500 мс, может потребоваться, чтобы звуковые сигналы находились на расстоянии не менее половины секунды друг от друга.
4. @phoog: Да. Я отредактирую свой пост, чтобы исправить эту неясность.
Ответ №2:
Используйте генерацию случайных чисел для генерации даты и времени в пределах диапазона общего количества времени. Когда вы закончите подавать случайные звуковые сигналы, интервалы, конечно, будут случайными. Что-то вроде этого:
List<DateTime> randomBeeps = new List<DateTime>();
Random rand = new Random();
for( int j = 0; j < numberBeepsNeeded; j )
{
int randInt = rand.Next();
double percent = ((double)randInt) / Int32.MaxValue;
double randTicksOfTotal = ((double)setAmountOfTime.Ticks) * percent;
DateTime randomBeep = new DateTime((long)randTicksOfTotal);
randomBeeps.Add(randomBeep);
}
Возможно, вам придется использовать преобразование.ТоЛонг или что-то в этом роде. Не уверен, что это выдаст вам ошибку при преобразовании из double в long, поскольку оно округляет его, что здесь нормально.
Ответ №3:
Вы можете реализовать это как серию одноразовых таймеров. Когда каждый таймер истекает (или «тикает»), вы воспроизводите звуковой сигнал, а затем случайным образом определяете продолжительность, которую следует использовать для следующего одноразового таймера. Если выбранная вами длительность равна некоторому случайному числу от 1 до 1000 (миллисекунд), вы будете в среднем один «тик» каждые полсекунды.
Редактировать: просто для развлечения, я подумал, что упомяну, что это старая проблема для поведенческих психологов, проводящих эксперименты, вдохновленные Б.Ф. Скиннером. Иногда они используют график подкрепления, называемый «Переменным интервалом», в котором время между подкреплениями изменяется случайным образом вокруг некоторого заданного среднего интервала. См. http://www.ncbi.nlm.nih.gov/pmc/articles/PMC1404199/pdf/jeabehav00190-0145.pdf для тупоголового обсуждения соответствующих формул.
Ответ №4:
Что-то вроде этого должно сработать (этот код не тестировался…но он компилируется чисто)
using System;
using System.Security.Cryptography;
using System.Threading;
class BeatBox : IDisposable
{
private RandomNumberGenerator RNG;
private DateTime dtStarted;
private TimeSpan TimeElapsed { get { return DateTime.Now - dtStarted; } }
private TimeSpan Duration;
private TimeSpan BeatInterval;
private uint MinRandomInterval;
private uint MaxRandomInterval;
private uint RandomIntervalDomain;
private Timer RegularIntervalTimer;
private Timer RandomIntervalTimer;
public delegate void TickHandler( object sender , bool isRandom );
public event TickHandler TickEvent;
private EventWaitHandle CompletionEventWaitHandle;
public BeatBox( TimeSpan duration , TimeSpan beatInterval , uint minRandomInterval , uint maxRandomInterval )
{
this.RNG = RandomNumberGenerator.Create();
this.Duration = duration ;
this.BeatInterval = beatInterval ;
this.MinRandomInterval = minRandomInterval ;
this.MaxRandomInterval = maxRandomInterval ;
this.RandomIntervalDomain = ( maxRandomInterval - minRandomInterval ) 1 ;
this.dtStarted = DateTime.MinValue ;
this.RegularIntervalTimer = null ;
this.RandomIntervalTimer = null ;
return;
}
private long NextRandomInterval()
{
byte[] entropy = new byte[sizeof(long)] ;
RNG.GetBytes( entropy );
long randomValue = BitConverter.ToInt64( entropy , 0 ) amp; long.MaxValue; // ensure that its positive
long randomoffset = ( randomValue % this.RandomIntervalDomain );
long randomInterval = this.MinRandomInterval randomoffset;
return randomInterval;
}
public EventWaitHandle Start()
{
long randomInterval = NextRandomInterval();
this.CompletionEventWaitHandle = new ManualResetEvent( false );
this.RegularIntervalTimer = new Timer( RegularBeat , null , BeatInterval , BeatInterval );
this.RandomIntervalTimer = new Timer( RandomBeat , null , randomInterval , Timeout.Infinite );
return this.CompletionEventWaitHandle;
}
private void RegularBeat( object timer )
{
if ( this.TimeElapsed >= this.Duration )
{
MarkComplete();
}
else
{
this.TickEvent.Invoke( this , false );
}
return;
}
private void RandomBeat( object timer )
{
if ( this.TimeElapsed >= this.Duration )
{
MarkComplete();
}
else
{
this.TickEvent.Invoke( this , true );
long nextInterval = NextRandomInterval();
this.RandomIntervalTimer.Change( nextInterval , Timeout.Infinite );
}
return;
}
private void MarkComplete()
{
lock ( this.CompletionEventWaitHandle )
{
bool signaled = this.CompletionEventWaitHandle.WaitOne( 0 );
if ( !signaled )
{
this.RegularIntervalTimer.Change( Timeout.Infinite , Timeout.Infinite );
this.RandomIntervalTimer.Change( Timeout.Infinite , Timeout.Infinite );
this.CompletionEventWaitHandle.Set();
}
}
return;
}
public void Dispose()
{
if ( RegularIntervalTimer != null )
{
WaitHandle handle = new ManualResetEvent( false );
RegularIntervalTimer.Dispose( handle );
handle.WaitOne();
}
if ( RandomIntervalTimer != null )
{
WaitHandle handle = new ManualResetEvent( false );
RegularIntervalTimer.Dispose( handle );
handle.WaitOne();
}
return;
}
}
class Program
{
static void Main( string[] args )
{
TimeSpan duration = new TimeSpan( 0 , 5 , 0 ); // run for 5 minutes total
TimeSpan beatInterval = new TimeSpan( 0 , 0 , 1 ); // regular beats every 1 second
uint minRandomInterval = 5; // minimum random interval is 5ms
uint maxRandomInterval = 30; // maximum random interval is 30ms
using ( BeatBox beatBox = new BeatBox( duration , beatInterval , minRandomInterval , maxRandomInterval ) )
{
beatBox.TickEvent = TickHandler;
EventWaitHandle completionHandle = beatBox.Start();
completionHandle.WaitOne();
}
return;
}
static void TickHandler( object sender , bool isRandom )
{
Console.WriteLine( isRandom ? "Random Beep!" : "Beep!" );
return;
}
}