Xamarin.Android: на запись файла влияет другая обработка
Я разрабатываю приложение для Android Xamarin, позволяющее пользователю записывать звук, исходящий из микро AudioRecord
записать его в файл.wav и отобразить соответствующую спектрограмму. Спектрограмма отображается в библиотеке SciChart.
Когда я проверяю запись файла и отображение спектрограммы отдельно, все работает нормально.
Но если я пытаюсь использовать их одновременно, я сталкиваюсь с некоторыми проблемами: файл хорошо написан, но не "правильно". Файл обеспечивает звук, который кажется ускоренным, и где отсутствуют некоторые аудио буферы.
Запись начинается с кнопки на Activity
:
((Button)FindViewById(Resource.Id.btnStart)).Click += async delegate
{
isRecording = true;
EnableButtons(true);
audioService = new WavAudioService();
audioService.samplesUpdated += AudioService_samplesUpdated;
audioService.StartRecording();
};
в AudioService
это запускает AudioRecord
:
public async Task StartRecording()
{
audioRecord = new AudioRecord(AudioSource.Mic, 44100, ChannelIn.Mono, Android.Media.Encoding.Pcm16bit, buffer);
if (audioRecord.State == State.Initialized)
{
audioRecord.StartRecording();
}
isRecording = true;
recordingThread = new System.Threading.Thread(new ThreadStart(
WriteAudioDataToFile
));
recordingThread.Priority = System.Threading.ThreadPriority.Normal;
recordingThread.Start();
}
Файл написан пока AudioRecord
записывает:
private void WriteAudioDataToFile()
{
byte[] data = new byte[bufferSize];
string filename = GetTempFilename();
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(filename);
}
catch (Java.IO.FileNotFoundException e)
{
// ...
}
int read = 0;
if (null != fos)
{
while (isRecording)
{
read = audioRecord.Read(data, 0, bufferSize);
int[] bytesAsInts = Array.ConvertAll(data, c => (int)c);
// call to FFT
var res = FFT(bytesAsInts);
if ((int)RecordStatus.ErrorInvalidOperation != read)
{
try
{
fos.Write(data);
}
catch (Java.IO.IOException e)
{
// ...
}
}
}
try
{
fos.Close();
}
catch (Java.IO.IOException e)
{
// ...
}
}
}
byte[]
превращается в int[]
быть использованным FFT()
расчет:
public async Task<int[]> FFT(int[] y)
{
var input = new AForge.Math.Complex[y.Length];
for (int i = 0; i < y.Length; i++)
{
input[i] = new AForge.Math.Complex(y[i], 0);
}
FourierTransform.FFT(input, FourierTransform.Direction.Forward);
var result = new int[y.Length / 2];
// getting magnitude
for (int i = 0; i < y.Length / 2 - 1; i++)
{
var current = Math.Sqrt(input[i].Re * input[i].Re + input[i].Im * input[i].Im);
current = Math.Log10(current) * 10;
result[i] = (int)current;
}
samplesUpdated(this, new SamplesUpdatedEventArgs(result));
return result;
}
Результат отправляется в метод делегата на Activity
:
private async void AudioService_samplesUpdated(object sender, System.EventArgs e)
{
var audioService = (WavAudioService)sender;
if (token.IsCancellationRequested)
{
audioService.StopRecording();
return;
}
var arguments = e as SamplesUpdatedEventArgs;
if (arguments != null)
{
var samples = arguments.UpdatedSamples;
if (samples.Length < (samplesCount / 2))
{
return;
}
UpdateHeatmapDataSeries(samples);
}
}
И, наконец, UpdateHeatmapDataSeries()
обновляет значения, отображаемые спектрограммой:
public async void UpdateHeatmapDataSeries(int[] data)
{
var spectrogramSize = width * height;
var fftSize = data.Length;
var offset = spectrogramSize - fftSize;
try
{
Array.Copy(Data, fftSize, Data, 0, offset);
Array.Copy(data, 0, Data, offset, fftSize);
heatmapSeries.UpdateZValues(Data);
}
catch (System.Exception e)
{
// ...
}
}
Если я удалю звонок UpdateHeatmapDataSeries()
от AudioService_samplesUpdated()
Я больше не сталкиваюсь с проблемой: файл обеспечивает "правильный" звук.
Я пытался добавить немного Threads
управлять FFT()
или же UpdateHeatmapDataSeries()
, но это ничего не изменило:
private void WriteAudioDataToFile()
{
// ...
while (isRecording)
{
read = audioRecord.Read(data, 0, bufferSize);
int[] bytesAsInts = Array.ConvertAll(data, c => (int)c);
samplesUpdatedThread = new Thread(() => FFT(bytesAsInts));
samplesUpdatedThread.Start();
if ((int)RecordStatus.ErrorInvalidOperation != read)
{
try
{
fos.Write(data);
}
catch (Java.IO.IOException e)
{
// ...
}
}
}
// ...
}
private async void AudioService_samplesUpdated(object sender, System.EventArgs e)
{
// ...
var samples = arguments.UpdatedSamples;
if (samples.Length < (samplesCount / 2))
{
return;
}
System.Threading.Thread thread = new System.Threading.Thread(() => UpdateHeatmapDataSeries(samples));
thread.Start();
}
Я что-то забыл? Как я мог это исправить?