mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
(MioInput): perform decoding in separate thread.
This commit is contained in:
@@ -24,10 +24,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
|
|
||||||
namespace GameRes.Formats.Entis
|
namespace GameRes.Formats.Entis
|
||||||
@@ -68,10 +71,10 @@ namespace GameRes.Formats.Entis
|
|||||||
public uint BitsPerSample { get { return m_info.BitsPerSample; } }
|
public uint BitsPerSample { get { return m_info.BitsPerSample; } }
|
||||||
|
|
||||||
public override int SourceBitrate { get { return m_bitrate; } }
|
public override int SourceBitrate { get { return m_bitrate; } }
|
||||||
public override string SourceFormat { get { return "raw"; } }
|
public override string SourceFormat { get { return "mio"; } }
|
||||||
|
|
||||||
#region Stream Members
|
#region Stream Members
|
||||||
public override bool CanSeek { get { return Source.CanSeek; } }
|
public override bool CanSeek { get { return m_decoded_stream.CanSeek; } }
|
||||||
|
|
||||||
public override long Position
|
public override long Position
|
||||||
{
|
{
|
||||||
@@ -81,7 +84,7 @@ namespace GameRes.Formats.Entis
|
|||||||
|
|
||||||
public override int Read (byte[] buffer, int offset, int count)
|
public override int Read (byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
return m_decoded_stream.Read (buffer, offset, count);
|
return Read_Threaded (buffer, offset, count);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -131,22 +134,22 @@ namespace GameRes.Formats.Entis
|
|||||||
if (EriCode.Nemesis != m_info.Architecture)
|
if (EriCode.Nemesis != m_info.Architecture)
|
||||||
m_pmioc = new HuffmanDecodeContext (0x10000);
|
m_pmioc = new HuffmanDecodeContext (0x10000);
|
||||||
else
|
else
|
||||||
throw new NotImplementedException ("Nemesis encoding not implemented");
|
throw new NotImplementedException ("MIO Nemesis encoding not implemented");
|
||||||
|
|
||||||
int pcm_bitrate = (int)m_info.SamplesPerSec * 16 * m_info.ChannelCount;
|
int pcm_bitrate = (int)m_info.SamplesPerSec * 16 * ChannelCount;
|
||||||
var format = new GameRes.WaveFormat();
|
var format = new GameRes.WaveFormat();
|
||||||
format.FormatTag = 1;
|
format.FormatTag = 1;
|
||||||
format.Channels = (ushort)m_info.ChannelCount;
|
format.Channels = (ushort)ChannelCount;
|
||||||
format.SamplesPerSecond = m_info.SamplesPerSec;
|
format.SamplesPerSecond = m_info.SamplesPerSec;
|
||||||
format.BitsPerSample = (ushort)m_info.BitsPerSample;
|
format.BitsPerSample = (ushort)BitsPerSample;
|
||||||
format.BlockAlign = (ushort)(m_info.BitsPerSample/8*format.Channels);
|
format.BlockAlign = (ushort)(BitsPerSample/8*format.Channels);
|
||||||
format.AverageBytesPerSecond = (uint)pcm_bitrate/8;
|
format.AverageBytesPerSecond = (uint)pcm_bitrate/8;
|
||||||
this.Format = format;
|
this.Format = format;
|
||||||
m_decoded_stream = LoadChunks();
|
m_decoded_stream = LoadChunks();
|
||||||
|
|
||||||
if (0 != m_total_samples)
|
if (0 != m_total_samples)
|
||||||
m_bitrate = (int)(stream_size * 8 * m_info.SamplesPerSec / m_total_samples);
|
m_bitrate = (int)(stream_size * 8 * m_info.SamplesPerSec / m_total_samples);
|
||||||
this.PcmSize = m_decoded_stream.Length;
|
this.PcmSize = m_total_samples * ChannelCount * BitsPerSample / 8;
|
||||||
m_decoded_stream.Position = 0;
|
m_decoded_stream.Position = 0;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -259,24 +262,107 @@ namespace GameRes.Formats.Entis
|
|||||||
catch (EndOfStreamException) { /* ignore EOF errors */ }
|
catch (EndOfStreamException) { /* ignore EOF errors */ }
|
||||||
m_total_samples = current_sample;
|
m_total_samples = current_sample;
|
||||||
if (0 == m_total_samples)
|
if (0 == m_total_samples)
|
||||||
|
{
|
||||||
|
m_decode_finished = true;
|
||||||
return Stream.Null;
|
return Stream.Null;
|
||||||
|
}
|
||||||
uint sample_bytes = (uint)ChannelCount * BitsPerSample / 8;
|
uint sample_bytes = (uint)ChannelCount * BitsPerSample / 8;
|
||||||
var total_bytes = m_total_samples * sample_bytes;
|
var total_bytes = m_total_samples * sample_bytes;
|
||||||
var wave_buf = new byte[total_bytes];
|
|
||||||
|
|
||||||
int current_pos = 0;
|
m_wait_handles = new WaitHandle[2] { m_available_chunk, m_decode_complete };
|
||||||
foreach (var chunk in chunks)
|
m_worker.WorkerSupportsCancellation = true;
|
||||||
|
m_worker.DoWork += DoWork_Decode;
|
||||||
|
m_worker.RunWorkerAsync (chunks);
|
||||||
|
return new MemoryStream ((int)total_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_decode_finished = false;
|
||||||
|
AutoResetEvent m_decode_complete = new AutoResetEvent (false);
|
||||||
|
AutoResetEvent m_available_chunk = new AutoResetEvent (false);
|
||||||
|
WaitHandle[] m_wait_handles;
|
||||||
|
|
||||||
|
ConcurrentQueue<byte[]> m_chunk_queue = new ConcurrentQueue<byte[]>();
|
||||||
|
BackgroundWorker m_worker = new BackgroundWorker();
|
||||||
|
Exception m_decode_error = null;
|
||||||
|
|
||||||
|
private void DoWork_Decode (object sender, DoWorkEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
using (var input = new ChunkStream (Source, chunk))
|
var worker = sender as BackgroundWorker;
|
||||||
|
var chunks = e.Argument as IEnumerable<MioChunk>;
|
||||||
|
uint sample_bytes = (uint)ChannelCount * BitsPerSample / 8;
|
||||||
|
foreach (var chunk in chunks)
|
||||||
{
|
{
|
||||||
m_pmioc.AttachInputFile (input);
|
if (worker.CancellationPending)
|
||||||
if (!m_pmiod.DecodeSound (m_pmioc, chunk, wave_buf, current_pos))
|
{
|
||||||
throw new InvalidFormatException();
|
e.Cancel = true;
|
||||||
current_pos += (int)(chunk.SampleCount * sample_bytes);
|
break;
|
||||||
|
}
|
||||||
|
using (var input = new ChunkStream (Source, chunk))
|
||||||
|
{
|
||||||
|
var wave_buf = new byte[chunk.SampleCount * sample_bytes];
|
||||||
|
m_pmioc.AttachInputFile (input);
|
||||||
|
if (!m_pmiod.DecodeSound (m_pmioc, chunk, wave_buf, 0))
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
m_chunk_queue.Enqueue (wave_buf);
|
||||||
|
m_available_chunk.Set();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new MemoryStream (wave_buf);
|
catch (Exception X)
|
||||||
|
{
|
||||||
|
Trace.WriteLine (X.Message, "[MIO]");
|
||||||
|
m_decode_error = X;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
m_decode_complete.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Read_Threaded (byte[] buf, int idx, int count)
|
||||||
|
{
|
||||||
|
var current_pos = Position;
|
||||||
|
int total_read = 0;
|
||||||
|
if (current_pos < m_decoded_stream.Length)
|
||||||
|
{
|
||||||
|
int available_bytes = (int)(m_decoded_stream.Length - current_pos);
|
||||||
|
int read = m_decoded_stream.Read (buf, idx, Math.Min (count, available_bytes));
|
||||||
|
idx += read;
|
||||||
|
count -= read;
|
||||||
|
total_read += read;
|
||||||
|
}
|
||||||
|
if (count > 0 && (!m_decode_finished || m_chunk_queue.Count > 0))
|
||||||
|
{
|
||||||
|
current_pos = Position;
|
||||||
|
m_decoded_stream.Seek (0, SeekOrigin.End);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
byte[] wave_buf = null;
|
||||||
|
while (m_chunk_queue.TryDequeue (out wave_buf))
|
||||||
|
{
|
||||||
|
m_decoded_stream.Write (wave_buf, 0, wave_buf.Length);
|
||||||
|
if (current_pos + count <= m_decoded_stream.Length)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (m_decode_finished || (current_pos + count <= m_decoded_stream.Length))
|
||||||
|
break;
|
||||||
|
int evt = WaitHandle.WaitAny (m_wait_handles);
|
||||||
|
if (1 == evt)
|
||||||
|
{
|
||||||
|
m_decode_finished = true;
|
||||||
|
if (m_decode_error != null)
|
||||||
|
{
|
||||||
|
m_decoded_stream.Position = current_pos;
|
||||||
|
throw m_decode_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_decoded_stream.Position = current_pos;
|
||||||
|
total_read += m_decoded_stream.Read (buf, idx, count);
|
||||||
|
}
|
||||||
|
return total_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable Members
|
#region IDisposable Members
|
||||||
@@ -286,9 +372,16 @@ namespace GameRes.Formats.Entis
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
if (!m_decode_finished)
|
||||||
|
{
|
||||||
|
m_worker.CancelAsync();
|
||||||
|
m_decode_complete.WaitOne();
|
||||||
|
}
|
||||||
m_erif.Dispose();
|
m_erif.Dispose();
|
||||||
if (m_decoded_stream != null)
|
if (m_decoded_stream != null)
|
||||||
m_decoded_stream.Dispose();
|
m_decoded_stream.Dispose();
|
||||||
|
m_decode_complete.Dispose();
|
||||||
|
m_available_chunk.Dispose();
|
||||||
}
|
}
|
||||||
m_erif = null;
|
m_erif = null;
|
||||||
base.Dispose (disposing);
|
base.Dispose (disposing);
|
||||||
|
|||||||
Reference in New Issue
Block a user