Created
February 15, 2020 15:25
-
-
Save unitycoder/6fece768157c038fb4c28d4b59931625 to your computer and use it in GitHub Desktop.
Encode/Decode Audio to/from PNG Alpha Channel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://unitycoder.com/blog/2020/02/15/encode-decode-audio-to-from-png-alpha-channel | |
// Decode Raw audiodata from PNG alpha channel | |
// Usage: Assign source texture and audioclip | |
// Note: You need to use same seed and samplerate that was used in encoding | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.IO; | |
using UnityEngine; | |
namespace UnityLibrary | |
{ | |
public class DecodeAudioFromTexture : MonoBehaviour | |
{ | |
[Tooltip("Texture filename inside StreamingAssets/ folder")] | |
public string filename = "mytex.png"; | |
[Tooltip("Target MeshRenderer to draw decoded texture into")] | |
public MeshRenderer targetRenderer; | |
[Tooltip("Do you want to de-shuffle pixels?")] | |
public bool deShufflePixels = false; | |
[Tooltip("Use same seed as the encoding process")] | |
public int seed = 1; | |
[Tooltip("Enter SampleRate(hz) used in encoded data")] | |
public int sampleRate = 1000; | |
void Start() | |
{ | |
StartCoroutine(Decode()); | |
} | |
IEnumerator Decode() | |
{ | |
string path = Path.Combine(Application.streamingAssetsPath, filename); | |
Debug.Log("Reading image: " + path); | |
WWW www = new WWW(path); | |
yield return www; | |
if (!string.IsNullOrEmpty(www.error)) | |
{ | |
Debug.LogError("Cannot load texture " + path + " Error:" + www.error); | |
yield break; | |
} | |
Texture2D texTmp = new Texture2D(512, 512, TextureFormat.RGBA32, false, false); | |
www.LoadImageIntoTexture(texTmp); | |
// read colors from texture | |
var pixels = texTmp.GetPixels(0); | |
// unshuffle | |
if (deShufflePixels == true) DeShuffle(seed, ref pixels); | |
texTmp.SetPixels(pixels); | |
texTmp.Apply(false); | |
// show texture | |
if (targetRenderer == null) | |
{ | |
Debug.LogError("No targetRenderer assigned.. (ErrorCode: Zombie)", gameObject); | |
yield break; | |
} | |
targetRenderer.material.mainTexture = texTmp; | |
// get audio from alpha | |
var data = new List<float>(); | |
// NOTE there will be empty values in the end if texture is bigger than audio | |
for (int i = 0; i < pixels.Length; i++) | |
{ | |
float f = pixels[i].a * 2f - 1f; | |
data.Add(f); | |
} | |
Debug.Log("Decoded len: " + data.Count); | |
// create audioclip | |
var newclip = AudioClip.Create("DecodedAudio", data.Count, 1, sampleRate, false); | |
newclip.SetData(data.ToArray(), 0); | |
// play using audiosource | |
var au = targetRenderer.gameObject.GetComponent<AudioSource>(); | |
if (au == null) au = targetRenderer.gameObject.AddComponent<AudioSource>(); | |
au.clip = newclip; | |
au.Play(); | |
Debug.Log("Done decoding"); | |
} | |
// https://stackoverflow.com/a/3542000/5452781 | |
void DeShuffle<T>(int seed, ref T[] array) | |
{ | |
Random.InitState(seed); | |
int n = array.Length; | |
int nn = array.Length; | |
int l = n; | |
int rr = 0; | |
int[] keys = new int[nn - 1]; | |
int i = 0; | |
while (n > 1) | |
{ | |
int k = Random.Range(0, n--); | |
keys[i++] = k; | |
} | |
n = 1; | |
System.Array.Reverse(keys); | |
while (nn > 1) | |
{ | |
int k = keys[rr++]; | |
T temp = array[n]; | |
array[n] = array[k]; | |
array[k] = temp; | |
nn--; | |
n++; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://unitycoder.com/blog/2020/02/15/encode-decode-audio-to-from-png-alpha-channel/ | |
// Encode Raw audiodata into PNG alpha channel | |
// Usage: Assign texture to "tex" field (requires [x] read/write enabled and Texture format: RGBA32, in texture inspector settings) | |
using System.IO; | |
using UnityEngine; | |
public class EncodeAudioToTexture : MonoBehaviour | |
{ | |
[Tooltip("Source texture")] | |
public Texture2D tex; | |
[Tooltip("Source audioclip")] | |
public AudioClip clip; | |
[Tooltip("Shuffle pixels, so that it looks more mysterious")] | |
public bool shufflePixels = false; | |
[Tooltip("Random seed, you need to use same seed when deconding")] | |
public int seed = 1; | |
void Start() | |
{ | |
Encode(); | |
} | |
void Encode() | |
{ | |
// read audio data | |
float[] data = new float[clip.samples * clip.channels]; | |
var b = clip.GetData(data, 0); | |
if (b == false) | |
{ | |
Debug.LogError("Failed to get audio data.. (ErrorCode: Banana)"); | |
return; | |
} | |
// read texture data | |
var pixels = tex.GetPixels(0); | |
// check if can fit | |
Debug.Log("AudioLen = " + data.Length); | |
Debug.Log("TexLen = " + pixels.Length); | |
if (data.Length > pixels.Length) | |
{ | |
Debug.LogError("Audio cannot fit into this texture, try lowering the sample rate.. (ErrorCode: Mustard)"); | |
return; | |
} | |
//assign audio data into texture alpha | |
for (int i = 0; i < data.Length; i++) | |
{ | |
// remap -1 to 1 into 0 to 1 range | |
pixels[i].a = (data[i] + 1f) * 0.5f; | |
} | |
// "randomize" pixels, so cannot see image | |
if (shufflePixels == true) Shuffle(seed, ref pixels); | |
// save new texture | |
var outputTex = new Texture2D(tex.width, tex.height, tex.format, false); | |
outputTex.SetPixels(pixels, 0); | |
outputTex.Apply(false); | |
var bytes = outputTex.EncodeToPNG(); | |
string path = Path.Combine(Application.streamingAssetsPath, tex.name + ".png"); | |
File.WriteAllBytes(path, bytes); | |
Debug.Log("Done Encoding, saved png: " + path); | |
} | |
// https://stackoverflow.com/a/1262619/5452781 | |
void Shuffle<T>(int seed, ref T[] array) | |
{ | |
Random.InitState(seed); | |
int n = array.Length; | |
while (n > 1) | |
{ | |
int k = Random.Range(0, n--); | |
T temp = array[n]; | |
array[n] = array[k]; | |
array[k] = temp; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment