diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml
index c1d5a737..5bbe35c9 100644
--- a/RMuseum/RMuseum.xml
+++ b/RMuseum/RMuseum.xml
@@ -4259,6 +4259,7 @@
+
@@ -4306,6 +4307,11 @@
equaivalnet for persian characters
+
+
+ ogg vorbis convertor
+
+
manipulating picture files
diff --git a/RMuseum/Services/Implementation/AudioNarrationService.cs b/RMuseum/Services/Implementation/AudioNarrationService.cs
index dbbb0bbf..4da951f7 100644
--- a/RMuseum/Services/Implementation/AudioNarrationService.cs
+++ b/RMuseum/Services/Implementation/AudioNarrationService.cs
@@ -351,7 +351,7 @@ namespace RMuseum.Services.Implementation
{
byte[] samples = new byte[mp3FileReader.Length];
await mp3FileReader.ReadAsync(samples, 0, (int)mp3FileReader.Length);
- var oggBytes = ConvertRawPCMFile(mp3FileReader.Mp3WaveFormat.SampleRate, mp3FileReader.Mp3WaveFormat.Channels, samples, PCMSample.EightBit, mp3FileReader.Mp3WaveFormat.SampleRate, mp3FileReader.Mp3WaveFormat.Channels);
+ var oggBytes = OggVorbisConvertor.ConvertRawPCMFile(mp3FileReader.Mp3WaveFormat.SampleRate, mp3FileReader.Mp3WaveFormat.Channels, samples, PCMSample.EightBit, mp3FileReader.Mp3WaveFormat.SampleRate, mp3FileReader.Mp3WaveFormat.Channels);
oggfileSize = oggBytes.Length;
File.WriteAllBytes(Path.Combine(targetPathForAudioFiles, $"{fileNameWithoutExtension}.ogg"), oggBytes);
}
@@ -424,135 +424,9 @@ namespace RMuseum.Services.Implementation
}
}
- enum PCMSample : int
- {
- EightBit = 1,
- SixteenBit = 2
- }
-
- private static byte[] ConvertRawPCMFile(int OutputSampleRate, int OutputChannels, byte[] PCMSamples, PCMSample PCMSampleSize, int PCMSampleRate, int PCMChannels)
- {
- int NumPCMSamples = (PCMSamples.Length / (int)PCMSampleSize / PCMChannels);
- float PCMDuraton = NumPCMSamples / (float)PCMSampleRate;
-
- int NumOutputSamples = (int)(PCMDuraton * OutputSampleRate);
- //Ensure that samble buffer is aligned to write chunk size
- NumOutputSamples = (NumOutputSamples / WriteBufferSize) * WriteBufferSize;
-
- float[][] OutSamples = new float[OutputChannels][];
-
- for (int ch = 0; ch < OutputChannels; ch++)
- OutSamples[ch] = new float[NumOutputSamples];
-
- for (int sampleNumber = 0; sampleNumber < NumOutputSamples; sampleNumber++)
- {
- float rawSample = 0.0f;
-
- for (int ch = 0; ch < OutputChannels; ch++)
- {
- int sampleIndex = (sampleNumber * PCMChannels) * (int)PCMSampleSize;
-
- if (ch < PCMChannels) sampleIndex += (ch * (int)PCMSampleSize);
-
- switch (PCMSampleSize)
- {
- case PCMSample.EightBit:
- rawSample = ByteToSample(PCMSamples[sampleIndex]);
- break;
- case PCMSample.SixteenBit:
- rawSample = ShortToSample((short)(PCMSamples[sampleIndex + 1] << 8 | PCMSamples[sampleIndex]));
- break;
- }
-
- OutSamples[ch][sampleNumber] = rawSample;
- }
- }
-
- return GenerateFile(OutSamples, OutputSampleRate, OutputChannels);
- }
-
- private static float ByteToSample(short pcmValue)
- {
- return pcmValue / 128f;
- }
-
- private static float ShortToSample(short pcmValue)
- {
- return pcmValue / 32768f;
- }
-
- private static byte[] GenerateFile(float[][] FloatSamples, int SampleRate, int Channels)
- {
- using (MemoryStream outputData = new MemoryStream())
- {
- // Stores all the static vorbis bitstream settings
- var info = VorbisInfo.InitVariableBitRate(Channels, SampleRate, 0.5f);
-
- // set up our packet->stream encoder
- var serial = new Random().Next();
- var oggStream = new OggStream(serial);
-
- // =========================================================
- // HEADER
- // =========================================================
- // Vorbis streams begin with three headers; the initial header (with
- // most of the codec setup parameters) which is mandated by the Ogg
- // bitstream spec. The second header holds any comment fields. The
- // third header holds the bitstream codebook.
- var headerBuilder = new HeaderPacketBuilder();
-
- var comments = new Comments();
- comments.AddTag("ARTIST", "TEST");
-
- var infoPacket = headerBuilder.BuildInfoPacket(info);
- var commentsPacket = headerBuilder.BuildCommentsPacket(comments);
- var booksPacket = headerBuilder.BuildBooksPacket(info);
-
- oggStream.PacketIn(infoPacket);
- oggStream.PacketIn(commentsPacket);
- oggStream.PacketIn(booksPacket);
-
- // Flush to force audio data onto its own page per the spec
- FlushPages(oggStream, outputData, true);
-
- // =========================================================
- // BODY (Audio Data)
- // =========================================================
- var processingState = ProcessingState.Create(info);
-
- processingState.WriteData(FloatSamples, FloatSamples[0].Length);
- for (int readIndex = 0; readIndex <= FloatSamples[0].Length; readIndex += WriteBufferSize)
- {
-
- OggPacket packet;
- while (!oggStream.Finished
- && processingState.PacketOut(out packet))
- {
- oggStream.PacketIn(packet);
-
- FlushPages(oggStream, outputData, false);
- }
- }
-
- FlushPages(oggStream, outputData, true);
-
- return outputData.ToArray();
- }
- }
-
- private static void FlushPages(OggStream oggStream, Stream Output, bool Force)
- {
- OggPage page;
- while (oggStream.PageOut(out page, Force))
- {
- Output.Write(page.Header, 0, page.Header.Length);
- Output.Write(page.Body, 0, page.Body.Length);
- }
- }
-
- private static int WriteBufferSize = 512;
- private static readonly int[] SampleRates = { 8000, 11025, 16000, 22050, 32000, 44100 };
+
+
///
@@ -597,6 +471,7 @@ namespace RMuseum.Services.Implementation
///
///
///
+ ///
public AudioNarrationService(RMuseumDbContext context, IConfiguration configuration, IBackgroundTaskQueue backgroundTaskQueue)
{
_context = context;
diff --git a/RMuseum/Services/Implementation/OggVorbisConvertor.cs b/RMuseum/Services/Implementation/OggVorbisConvertor.cs
new file mode 100644
index 00000000..0cca1602
--- /dev/null
+++ b/RMuseum/Services/Implementation/OggVorbisConvertor.cs
@@ -0,0 +1,141 @@
+using OggVorbisEncoder;
+using System;
+using System.IO;
+
+namespace RMuseum.Services.Implementation
+{
+ internal enum PCMSample : int
+ {
+ EightBit = 1,
+ SixteenBit = 2
+ }
+ ///
+ /// ogg vorbis convertor
+ ///
+ internal static class OggVorbisConvertor
+ {
+ public static byte[] ConvertRawPCMFile(int OutputSampleRate, int OutputChannels, byte[] PCMSamples, PCMSample PCMSampleSize, int PCMSampleRate, int PCMChannels)
+ {
+ int NumPCMSamples = (PCMSamples.Length / (int)PCMSampleSize / PCMChannels);
+ float PCMDuraton = NumPCMSamples / (float)PCMSampleRate;
+
+ int NumOutputSamples = (int)(PCMDuraton * OutputSampleRate);
+ //Ensure that samble buffer is aligned to write chunk size
+ NumOutputSamples = (NumOutputSamples / WriteBufferSize) * WriteBufferSize;
+
+ float[][] OutSamples = new float[OutputChannels][];
+
+ for (int ch = 0; ch < OutputChannels; ch++)
+ OutSamples[ch] = new float[NumOutputSamples];
+
+ for (int sampleNumber = 0; sampleNumber < NumOutputSamples; sampleNumber++)
+ {
+ float rawSample = 0.0f;
+
+ for (int ch = 0; ch < OutputChannels; ch++)
+ {
+ int sampleIndex = (sampleNumber * PCMChannels) * (int)PCMSampleSize;
+
+ if (ch < PCMChannels) sampleIndex += (ch * (int)PCMSampleSize);
+
+ switch (PCMSampleSize)
+ {
+ case PCMSample.EightBit:
+ rawSample = ByteToSample(PCMSamples[sampleIndex]);
+ break;
+ case PCMSample.SixteenBit:
+ rawSample = ShortToSample((short)(PCMSamples[sampleIndex + 1] << 8 | PCMSamples[sampleIndex]));
+ break;
+ }
+
+ OutSamples[ch][sampleNumber] = rawSample;
+ }
+ }
+
+ return GenerateFile(OutSamples, OutputSampleRate, OutputChannels);
+ }
+
+ private static float ByteToSample(short pcmValue)
+ {
+ return pcmValue / 128f;
+ }
+
+ private static float ShortToSample(short pcmValue)
+ {
+ return pcmValue / 32768f;
+ }
+
+ private static byte[] GenerateFile(float[][] FloatSamples, int SampleRate, int Channels)
+ {
+ using (MemoryStream outputData = new MemoryStream())
+ {
+ // Stores all the static vorbis bitstream settings
+ var info = VorbisInfo.InitVariableBitRate(Channels, SampleRate, 0.5f);
+
+ // set up our packet->stream encoder
+ var serial = new Random().Next();
+ var oggStream = new OggStream(serial);
+
+ // =========================================================
+ // HEADER
+ // =========================================================
+ // Vorbis streams begin with three headers; the initial header (with
+ // most of the codec setup parameters) which is mandated by the Ogg
+ // bitstream spec. The second header holds any comment fields. The
+ // third header holds the bitstream codebook.
+ var headerBuilder = new HeaderPacketBuilder();
+
+ var comments = new Comments();
+ comments.AddTag("ARTIST", "TEST");
+
+ var infoPacket = headerBuilder.BuildInfoPacket(info);
+ var commentsPacket = headerBuilder.BuildCommentsPacket(comments);
+ var booksPacket = headerBuilder.BuildBooksPacket(info);
+
+ oggStream.PacketIn(infoPacket);
+ oggStream.PacketIn(commentsPacket);
+ oggStream.PacketIn(booksPacket);
+
+ // Flush to force audio data onto its own page per the spec
+ FlushPages(oggStream, outputData, true);
+
+ // =========================================================
+ // BODY (Audio Data)
+ // =========================================================
+ var processingState = ProcessingState.Create(info);
+
+ processingState.WriteData(FloatSamples, FloatSamples[0].Length);
+ for (int readIndex = 0; readIndex <= FloatSamples[0].Length; readIndex += WriteBufferSize)
+ {
+
+ OggPacket packet;
+ while (!oggStream.Finished
+ && processingState.PacketOut(out packet))
+ {
+ oggStream.PacketIn(packet);
+
+ FlushPages(oggStream, outputData, false);
+ }
+ }
+
+ FlushPages(oggStream, outputData, true);
+
+ return outputData.ToArray();
+ }
+ }
+
+ private static void FlushPages(OggStream oggStream, Stream Output, bool Force)
+ {
+ OggPage page;
+ while (oggStream.PageOut(out page, Force))
+ {
+ Output.Write(page.Header, 0, page.Header.Length);
+ Output.Write(page.Body, 0, page.Body.Length);
+ }
+ }
+
+ private static int WriteBufferSize = 512;
+ private static readonly int[] SampleRates = { 8000, 11025, 16000, 22050, 32000, 44100 };
+
+ }
+}