Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

save audio packets as Opus files #1034

Open
ChHa82 opened this issue May 4, 2023 · 1 comment
Open

save audio packets as Opus files #1034

ChHa82 opened this issue May 4, 2023 · 1 comment

Comments

@ChHa82
Copy link

ChHa82 commented May 4, 2023

Hello,

I have been trying to save audio packets as Opus files for several days now. The reason for this is that I would like to implement a wake word. Currently, my code looks like this:
using Org.BouncyCastle.Asn1.Cms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using TSLib.Audio;
using TSLib.Full;

namespace TSLib.Audio.OpusTranscript
{
public class OpusTranscript
{
private int fileCount = 1;
private readonly string outputDirectory = "D:\output";
private readonly int OpusHeaderSize = 19;
private readonly List<byte[]> audioPackets = new List<byte[]>();
public void Initialize()
{
}

	public void Dispose()
	{
	}

	public void Write(Span<byte> data)
	{
		// Fügen Sie die empfangenen Audiodaten in die Liste der Audio-Pakete ein
		//data = data.Slice(5);
		audioPackets.Add(data.ToArray());
		if (audioPackets.Count > 1) WriteOpusFileAsync();
	}

	// Rufen Sie diese Methode auf, um die Audio-Pakete zu einer Opus-Datei zusammenzufügen
	private async Task WriteOpusFileAsync()
	{
		string fileName = Path.Combine(outputDirectory, $"output_{fileCount}.opus");

		// Erhöhen Sie den Zähler für die Anzahl der geschriebenen Dateien
		fileCount++;

		using var fileStream = new FileStream(fileName, FileMode.Create);

		// Schreiben Sie den Opus-Header in die Datei
		//await fileStream.WriteAsync(OggOpusHeader, 0, OggOpusHeader.Length);

		// Schreiben Sie die Audiodatenpakete in die Datei
		foreach (byte[] packet in audioPackets)
		{
			await fileStream.WriteAsync(packet, 0, packet.Length);
		}

		// Löschen Sie die Audio-Pakete, um Speicherplatz freizugeben
		audioPackets.Clear();
	}
	private static readonly byte[] OggOpusHeader = new byte[]
	{
// OggS capture pattern
0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// OpusHead packet
0x01, // version
0x00, // channel count
0x00, 0x00, 0x00, 0x00, // pre-skip
0x00, 0x00, 0x00, 0x00, // input sample rate
 0x00, 0x00, // output gain
0x00, // channel mapping family
0x00, // padding length
// OpusTags packet
0x04, // packet type
0x00, 0x00, 0x00, 0x00, // packet length
// padding
0x00, 0x00, 0x00, 0x00
	};
}

}

and I am accessing the packets directly from the PacketEvent in TS3FullClient. There might be a better solution, but after spending countless hours trying to go through the code, I decided to stick with accessing them directly from there.

The problem is that the Opus files generated from my code cannot be played, and I suspect that the issue lies in the Opus header. Could you please help me figure out what I need to change in order to generate valid Opus files?

Thank you very much.

@ChHa82
Copy link
Author

ChHa82 commented May 7, 2023

dealt with it like this now, its not beautiful I now and Im not proud but it works for now^^:
using Org.BouncyCastle.Asn1.Cms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TSLib.Audio.Opus;
//Zukunftsich muss sich darum kümmern, dass die clients in einzelne Dateien gespeichert werden
namespace TSLib.Audio.OpusTranscript
{

public class OpusTranscript
{
	private int fileCount = 1;
	private readonly string outputDirectory = "D:\\output";
	private readonly int OpusHeaderSize = 19;
	private readonly List<byte[]> audioPackets = new List<byte[]>();
	private readonly byte[] decodedBuffer = new byte[4096 * 2];
	OpusDecoder decoder = OpusDecoder.Create(48000, 2);
	private readonly int maxPacketCount = 1500;
	private readonly int maxTimeInterval = 2000; // in Millisekunden
	private readonly System.Timers.Timer timer;
	private readonly object lockObj = new object();

	public OpusTranscript()
	{
		timer = new System.Timers.Timer(maxTimeInterval);
		timer.Elapsed += Timer_Elapsed;
	}
	public void Initialize()
	{
		
	}

	public void Dispose()
	{
	}
	private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
	{
			// Stoppen Sie den Timer, wenn er abläuft
			timer.Stop();
			if (audioPackets.Count > 0)
			{
				// Schreiben Sie die Audio-Pakete in die Datei, wenn der Timer abgelaufen ist
				WriteFileAsync();
			}
	}


	public void Write(Span<byte> data, Meta? meta)
	{
		// Decodieren Sie das empfangene Opus-Audio-Datenpaket
		var decodedData = decoder.Decode(data.Slice(5), decodedBuffer);
		timer.Stop();
		timer.Start();
		// Fügen Sie die dekodierten PCM-Audiodaten in die Liste der Audio-Pakete ein
		audioPackets.Add(decodedData.ToArray());

		if (audioPackets.Count >= maxPacketCount)
		{
			WriteFileAsync();
		}
	}
	private async Task WriteFileAsync()
	{
		string fileName = Path.Combine(outputDirectory, $"output_{fileCount}.wav");
		fileCount++;

		using var fileStream = new FileStream(fileName, FileMode.Create);

		// Schreiben Sie den WAV-Header in die Datei
		ushort channels = 2; // Anzahl der Kanäle
		ushort bitsPerSample = 16; // Bits pro Sample
		int sampleRate = 48000; // Sample-Rate
		int dataSize = audioPackets.Sum(x => x.Length);
		int chunkSize = 36 + dataSize;

		await fileStream.WriteAsync(Encoding.ASCII.GetBytes("RIFF"), 0, 4);
		await fileStream.WriteAsync(BitConverter.GetBytes(chunkSize), 0, 4);
		await fileStream.WriteAsync(Encoding.ASCII.GetBytes("WAVE"), 0, 4);
		await fileStream.WriteAsync(Encoding.ASCII.GetBytes("fmt "), 0, 4);
		await fileStream.WriteAsync(BitConverter.GetBytes(16), 0, 4); // Subchunk1Size
		await fileStream.WriteAsync(BitConverter.GetBytes((ushort)1), 0, 2); // AudioFormat (1 = PCM)
		await fileStream.WriteAsync(BitConverter.GetBytes(channels), 0, 2); // NumChannels
		await fileStream.WriteAsync(BitConverter.GetBytes(sampleRate), 0, 4); // SampleRate
		await fileStream.WriteAsync(BitConverter.GetBytes(sampleRate * channels * bitsPerSample / 8), 0, 4); // ByteRate
		await fileStream.WriteAsync(BitConverter.GetBytes((ushort)(channels * bitsPerSample / 8)), 0, 2); // BlockAlign
		await fileStream.WriteAsync(BitConverter.GetBytes(bitsPerSample), 0, 2); // BitsPerSample
		await fileStream.WriteAsync(Encoding.ASCII.GetBytes("data"), 0, 4);
		await fileStream.WriteAsync(BitConverter.GetBytes(dataSize), 0, 4);

		// Schreiben Sie die PCM-Daten in die Datei
		byte[] packet;
		lock (lockObj)
		{
			packet = audioPackets.SelectMany(x => x).ToArray();
			audioPackets.Clear();
		}

		await fileStream.WriteAsync(packet, 0, packet.Length);
	}
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant