Skip to content

Instantly share code, notes, and snippets.

@Staars
Last active January 28, 2025 17:57
Show Gist options
  • Save Staars/6eeb027941341985c5a9ea2162a4a55e to your computer and use it in GitHub Desktop.
Save Staars/6eeb027941341985c5a9ea2162a4a55e to your computer and use it in GitHub Desktop.
unused i2s snippet
class AudioEncoderOpus : public AudioEncoder
{
public:
AudioEncoderOpus() {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: AudioEncoderOpus"));
headerBuffer = (uint8_t*)&combHeader;
packetno = 0;
};
virtual ~AudioEncoderOpus() {
if(inBuffer){ free(inBuffer); }
if(outFrame){ free(outFrame); }
opus_encoder_destroy(encoder);
};
virtual uint32_t begin(uint32_t samplingRate, uint32_t inputChannels) {
int error;
encoder = opus_encoder_create(samplingRate, inputChannels, OPUS_APPLICATION_AUDIO, &error);
if (error != 0) { return error; }
opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(complexity));
sigHead.samplingRate = samplingRate;
sigHead.inputChannels = inputChannels;
_makeHeader();
constexpr uint32_t frameDuration = 20; //ms
samplesPerPass = samplingRate/(1000/frameDuration);
byteSize = samplesPerPass * 2 * inputChannels;
inBuffer = (int16_t*)malloc(byteSize);
if (!inBuffer) {return 5; }
outFrame = (uint8_t*)malloc(maxBytes);
if (!outFrame) {return 5; }
return 0;
};
virtual size_t encode() {
opus_int32 i = opus_encode(encoder, inBuffer, samplesPerPass, outFrame, maxBytes);
return i;
}
virtual size_t getHeaderSize() {
return sizeof(combHeader);
}
protected:
OpusEncoder *encoder;
const uint32_t maxBytes = 1296;
const uint32_t complexity = 1; // 1-10 - low to high quality
struct __attribute__((packed)) {
ogg_packet sigHead;
ogg_packet sigTags;
} combHeader;
uint32_t packetno;
struct __attribute__((packed)) {
char signatureHead[8] = {'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'};
uint8_t version = 1;
uint8_t inputChannels = 0;
uint16_t preSkip = 3840;
uint32_t samplingRate = 0;
int16_t outputGain = 0;
uint8_t channelMappingFamily = 0;
} sigHead;
struct __attribute__((packed)) {
char signatureTags[8] = {'O', 'p', 'u', 's', 'T', 'a', 'g', 's'};
uint32_t vendorStringLength = 8;
char vendor[8] = "Tasmota";
uint32_t userCommentListLength = 0;
} sigTags;
void _makeHeader(){
combHeader.sigHead.packet = (uint8_t *)&sigHead;
combHeader.sigHead.bytes = sizeof(sigHead);
combHeader.sigHead.granulepos = 0;
combHeader.sigHead.packetno = packetno++;
combHeader.sigHead.b_o_s = true;
combHeader.sigHead.e_o_s = false;
combHeader.sigTags.packet = (uint8_t *)&sigTags;
combHeader.sigTags.bytes = sizeof(sigTags);
combHeader.sigTags.granulepos = 0;
combHeader.sigTags.packetno = packetno++;
combHeader.sigTags.b_o_s = true;
combHeader.sigTags.e_o_s = false;
}
};
#include "libopus/opus.h"
#include "libopusenc/include/opusenc.h"
class AudioEncoderOpusOPE;
typedef int (AudioEncoderOpusOPE::*packet_write)(void *userdata, const unsigned char *ptr, int len);
typedef int (AudioEncoderOpusOPE::*packet_close)(void *userdata);
class AudioEncoderOpusOPE : public AudioEncoder
{
public:
AudioEncoderOpusOPE(File *rec_file, WiFiClient *wifi) {
file = rec_file;
client = wifi;
comments = ope_comments_create();
receivedFrames = 0;
ope_comments_add(comments, "TASMOTA", "14.x");
};
virtual ~AudioEncoderOpusOPE() {
ope_comments_destroy(comments);
ope_encoder_destroy(enc);
if(file) {file->close();}
if(client) {client->stop();}
};
virtual uint32_t begin(uint32_t samplingRate, uint32_t inputChannels) {
if(callbacks.write == nullptr){
return 6;
}
int error;
enc = ope_encoder_create_callbacks(&callbacks, (void *)this, comments, samplingRate, inputChannels, 0, &error);
if(enc == nullptr) {return 3;}
ope_encoder_ctl(enc, OPUS_SET_COMPLEXITY_REQUEST, 0);
constexpr uint32_t frameDuration = 20; //ms
samplesPerPass = samplingRate/(1000/frameDuration);
byteSize = samplesPerPass * 2 * inputChannels;
inBuffer = (int16_t*)malloc(byteSize);
if (!inBuffer) {return 5; }
outFrame = (uint8_t*)malloc(maxBytes);
if (!outFrame) {return 5; }
return 0;
};
virtual int encode(size_t samples) {
receivedFrames += 1;
if( receivedFrames > 25){ //about 0.5 seconds for 20ms frames
ope_encoder_flush(enc);
receivedFrames = 0;
}
return ope_encoder_write(enc, inBuffer, samples);
}
virtual size_t stop() {
return ope_encoder_drain(enc);
}
virtual void setCB(void *cb){
OpusEncCallbacks * _cb = (OpusEncCallbacks *)cb;
callbacks.write = _cb->write;
callbacks.close = _cb->close;
}
static int packet_write(void *userdata, const unsigned char *ptr, opus_int32 len){
AudioEncoderOpusOPE *enc = (AudioEncoderOpusOPE*)userdata;
size_t written = 0;
if(enc->file != nullptr){
written = enc->file->write(ptr,len);
} else if (enc->client != nullptr){
written = enc->client->write(ptr,len);
}
return (written != (size_t)len);
}
static int packet_close(void *userdata){
(void)userdata;
return 0;
}
protected:
OpusEncCallbacks callbacks;
OggOpusEnc *enc;
OggOpusComments *comments;
size_t receivedFrames ;
const uint32_t maxBytes = 1296;
const uint32_t complexity = 1; // 1-10 - low to high quality
};
void resample16to32(){
// for(int32_t i = (samplesPerPass/2) - 1; i>=0; i--){
// inBuffer[(i*2) + 1] = inBuffer[i];
// if(i>0) inBuffer[(i*2)] = ((int32_t)inBuffer[i] + (int32_t)inBuffer[i-1]) / 2;
// }
int16_t* dst = inBuffer + (samplesPerPass - 1);
int16_t* src = inBuffer + ((samplesPerPass/2) - 1);
src[1] = 0; //guess the direction of the next frame heading zero
while (dst > inBuffer){
int32_t averaged = ((int32_t)*src + (int32_t)*(src + 1))/2;
*dst-- = averaged;
*dst-- = *src--;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment