feat: wip...got full transcription all in memory

This commit is contained in:
Stevan Freeborn
2025-07-21 01:01:09 -05:00
parent a8e4f669e5
commit 75bb571b6b
5 changed files with 114 additions and 5 deletions
+94 -2
View File
@@ -1,7 +1,99 @@
// See https://aka.ms/new-console-template for more information
using System.Globalization;
using System.Globalization;
using System.Resources;
using System.Text;
using FFMpegCore;
using FFMpegCore.Enums;
using FFMpegCore.Pipes;
using NAudio.Wave;
using Whisper.net;
using Whisper.net.Ggml;
var resourceManager = new ResourceManager("StreamShorts.Console.Resources.Resources", typeof(Program).Assembly);
Console.WriteLine(resourceManager.GetString("WelcomeMessage", CultureInfo.CurrentCulture));
// Step 1: Retrieve stream
if (args.Length is 0)
{
Console.WriteLine(resourceManager.GetString("StreamNotProvided", CultureInfo.CurrentCulture));
return;
}
using var mp3Stream = new MemoryStream();
using var mp4Stream = new FileStream(args[0], FileMode.Open, FileAccess.Read);
var wasExtracted = await FFMpegArguments
.FromPipeInput(new StreamPipeSource(mp4Stream))
.OutputToPipe(
new StreamPipeSink(mp3Stream),
o => o.DisableChannel(Channel.Video).ForceFormat("mp3")
)
.ProcessAsynchronously();
if (wasExtracted is false)
{
Console.WriteLine(resourceManager.GetString("Mp3ExtractionFailed", CultureInfo.CurrentCulture));
return;
}
// Step 2: Convert MP3 stream to 16khz wave format
mp3Stream.Position = 0;
using var reader = new Mp3FileReader(mp3Stream);
var outFormat = new WaveFormat(16000, reader.WaveFormat.Channels);
using var resampler = new MediaFoundationResampler(reader, outFormat);
using var waveStream = new MemoryStream();
WaveFileWriter.WriteWavFileToStream(waveStream, resampler);
// Step 3: Split the wave stream into 2 minute segments
waveStream.Position = 0;
var segmentDuration = TimeSpan.FromMinutes(2);
var segments = new List<MemoryStream>();
using var waveReader = new WaveFileReader(waveStream);
var segmentCount = (int)Math.Ceiling(waveReader.TotalTime.TotalMilliseconds / segmentDuration.TotalMilliseconds);
Directory.CreateDirectory("segments");
foreach (var i in Enumerable.Range(0, segmentCount))
{
waveStream.Position = 0;
using var segmentWaveReader = new WaveFileReader(waveStream);
var segment = segmentWaveReader.ToSampleProvider()
.Skip(i * segmentDuration)
.Take(segmentDuration);
var segmentProvider = segment.ToWaveProvider16();
var segmentStream = new MemoryStream();
WaveFileWriter.WriteWavFileToStream(segmentStream, segmentProvider);
segmentStream.Position = 0;
segments.Add(segmentStream);
}
// Step 4: Transcribe each segment using Whisper
using var modelMemoryStream = new MemoryStream();
var model = await WhisperGgmlDownloader.Default.GetGgmlModelAsync(GgmlType.TinyEn);
await model.CopyToAsync(modelMemoryStream);
var whisperFactory = WhisperFactory.FromBuffer(modelMemoryStream.ToArray());
using var whisperProcessor = whisperFactory.CreateBuilder()
.WithLanguage("en")
.Build();
foreach (var (i, segment) in segments.Select((s, index) => (index, s)))
{
var durationOffset = TimeSpan.FromMilliseconds(i * segmentDuration.TotalMilliseconds);
var segmentTranscription = new StringBuilder();
await foreach (var result in whisperProcessor.ProcessAsync(segment, CancellationToken.None))
{
var startTime = result.Start + durationOffset;
var endTime = result.End + durationOffset;
segmentTranscription.AppendLine(CultureInfo.CurrentCulture, $"[{startTime:hh\\:mm\\:ss} - {endTime:hh\\:mm\\:ss}] {result.Text}");
}
await File.AppendAllTextAsync("transcription.txt", segmentTranscription.ToString());
}
// Step 5: Send the transcription to LLM for analysis
// Step 6: Use analysis to generate a short video